【问题标题】:How to duplicate a shape apache POI?如何复制形状 apache POI?
【发布时间】:2022-08-23 20:01:13
【问题描述】:

我需要使用 Apache POI (XSLF) ppt 在幻灯片中复制相同的形状。

我可以在下面的代码中做这样的事情吗?

static void cloneShape(XMLSlideShow slideShow, int slideNumber, String textBoxId) {
    Optional<XSLFShape> textBoxopt = getShapesByName(slideShow, slideNumber, textBoxId).stream().findFirst();
    XSLFAutoShape shapeToBeCloned = (XSLFAutoShape) textBoxopt.get();
    XSLFShapeContainer slide = slideShow.getSlides().get(slideNumber);
    XSLFAutoShape shape1 = slide.createAutoShape(***shapeToBeCloned***);

    标签: java apache-poi powerpoint xslf


    【解决方案1】:

    XSLFShapes 没有任何克隆方法。即使可以,也没有任何方法可以将克隆的XSLFShape 添加到XSSFSheet(幻灯片)。有XSSFSheet.addShape(XSLFShape shape),但这只会抛出UnsupportedOperationException。我喜欢apache poi 开发人员的幽默感。

    因此,如果要复制幻灯片的形状,则只能使用底层对象。 org.apache.xmlbeans.XmlObject 类提供了一个 copy 方法,该方法制作 XML 的深层副本。然后需要将该副本添加到幻灯片的形状树中。然后需要重新初始化幻灯片的形状树。之后可以从XSSFSheet.getShapes() 获取形状的高级对象。不幸的是,大多数需要的方法都不是公开的。所以需要使用反射。

    以下代码显示了执行此操作的一种方法。它只是在给定PPTIn.pptx 的每张幻灯片中克隆除组形状和图形对象框架形状之外的所有形状。

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    
    import org.apache.poi.sl.usermodel.*;
    import org.apache.poi.xslf.usermodel.*;
    import java.util.List;
    import java.util.ArrayList;
    
    public class PowerPointCloneShape {
        
     static List<XSLFShape> getShapesByName(XMLSlideShow slideShow, String shapeName) {
      List<XSLFShape> shapes = new ArrayList<XSLFShape>();
      for (XSLFSlide slide : slideShow.getSlides()) {
       for (XSLFShape shape : slide.getShapes()) {
        //System.out.println(shape.getShapeName());
        if (shapeName.equals(shape.getShapeName())) {
         shapes.add(shape);
        }
       }
      }
      return shapes;
     }
     
     static List<XSLFShape> getShapes(XMLSlideShow slideShow) {
      List<XSLFShape> shapes = new ArrayList<XSLFShape>();
      for (XSLFSlide slide : slideShow.getSlides()) {
       for (XSLFShape shape : slide.getShapes()) {
        shapes.add(shape);
       }
      }
      return shapes;
     }
     
     // method to new initialize drawing and shapes in sheet from updated shape tree
     static void initDrawingAndShapes(XSLFSheet sheet) throws Exception {
      java.lang.reflect.Field _drawing  = XSLFSheet.class.getDeclaredField("_drawing");
      _drawing.setAccessible(true);
      _drawing.set(sheet, null);
      java.lang.reflect.Field _shapes = XSLFSheet.class.getDeclaredField("_shapes");
      _shapes.setAccessible(true);
      _shapes.set(sheet, null);
      java.lang.reflect.Method initDrawingAndShapes = XSLFSheet.class.getDeclaredMethod("initDrawingAndShapes");
      initDrawingAndShapes.setAccessible(true);
      initDrawingAndShapes.invoke(sheet); 
     }
     
     // method to allocate the next shape ID in sheet
     static int allocateShapeId(XSLFSheet sheet) throws Exception {
      java.lang.reflect.Method allocateShapeId = XSLFSheet.class.getDeclaredMethod("allocateShapeId");
      allocateShapeId.setAccessible(true);
      int nextId = (int)allocateShapeId.invoke(sheet); 
      return nextId;
     }
     
     // method to get the shape tree of sheet
     static org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape getSpTree(XSLFSheet sheet) throws Exception {
      java.lang.reflect.Field _spTree = XSLFSheet.class.getDeclaredField("_spTree");
      _spTree.setAccessible(true);
      org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape spTree = (org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape)_spTree.get(sheet);
      return spTree;     
     }
     
     // method to clone a shape contained in a sheet
     static XSLFShape cloneShape(XSLFShape shape) throws Exception {
      // first clone low level XML   
      org.apache.xmlbeans.XmlObject xmlObject = shape.getXmlObject();
      org.apache.xmlbeans.XmlObject xmlClone =  xmlObject.copy();
      //System.out.println(xmlClone.getClass().getName());
      // then create high level clone shapes
      if (xmlClone instanceof org.openxmlformats.schemas.presentationml.x2006.main.CTShape) { // simple shape
       org.openxmlformats.schemas.presentationml.x2006.main.CTShape ctShapeClone = (org.openxmlformats.schemas.presentationml.x2006.main.CTShape)xmlClone;
       // get sheet
       XSLFSheet sheet = shape.getSheet();
       // set new ID
       int nextId = allocateShapeId(sheet);
       ctShapeClone.getNvSpPr().getCNvPr().setId(nextId);
       // add into the shape tree of sheet
       org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape spTree = getSpTree(sheet);
       spTree.addNewSp();
       spTree.setSpArray​(spTree.sizeOfSpArray()-1, ctShapeClone);
       // new initialize drawing and shapes in sheet
       initDrawingAndShapes(sheet);
       // get clone
       XSLFShape clone = sheet.getShapes().get(sheet.getShapes().size()-1);
       return clone;
      } else if (xmlClone instanceof org.openxmlformats.schemas.presentationml.x2006.main.CTPicture) { // picture shape
       org.openxmlformats.schemas.presentationml.x2006.main.CTPicture ctPictureClone = (org.openxmlformats.schemas.presentationml.x2006.main.CTPicture)xmlClone;   
     
       XSLFSheet sheet = shape.getSheet();
    
       int nextId = allocateShapeId(sheet);
       ctPictureClone.getNvPicPr().getCNvPr().setId(nextId);
       
       org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape spTree = getSpTree(sheet);
       spTree.addNewPic();
       spTree.setPicArray​(spTree.sizeOfPicArray()-1, ctPictureClone);
    
       initDrawingAndShapes(sheet);
       
       XSLFShape clone = sheet.getShapes().get(sheet.getShapes().size()-1);
       return clone;
      } else if (xmlClone instanceof org.openxmlformats.schemas.presentationml.x2006.main.CTConnector) { // connector shape
       org.openxmlformats.schemas.presentationml.x2006.main.CTConnector ctConnectorClone = (org.openxmlformats.schemas.presentationml.x2006.main.CTConnector)xmlClone;   
    
       XSLFSheet sheet = shape.getSheet();
    
       int nextId = allocateShapeId(sheet);
       ctConnectorClone.getNvCxnSpPr().getCNvPr().setId(nextId);
       
       org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape spTree = getSpTree(sheet);
       spTree.addNewCxnSp();
       spTree.setCxnSpArray​(spTree.sizeOfCxnSpArray()-1, ctConnectorClone);
    
       initDrawingAndShapes(sheet);
       
       XSLFShape clone = sheet.getShapes().get(sheet.getShapes().size()-1);
       // connector has connecting points which also simple are cloned but would must be new adjusted
       return clone;
      } else if (xmlClone instanceof org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape) { // group shape
       // cloning is not that simple
      } else if (xmlClone instanceof org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame) { // graphical object frame shape (table, chart, diagram, ...)
       // cloning is not that simple
      }
      return null;
     }
    
     public static void main(String args[]) throws Exception {
    
      XMLSlideShow slideShow = new XMLSlideShow(new FileInputStream("./PPTIn.pptx"));
      //List<XSLFShape> shapes = getShapesByName(slideShow, "Textbox 1");
      List<XSLFShape> shapes = getShapes(slideShow);
      System.out.println(shapes);
      //if (shapes.size() > 0 ) {
      //XSLFShape shape = shapes.get(0);  
      for (XSLFShape shape : shapes) {
       System.out.println("source: " + shape);
       XSLFShape clone = cloneShape(shape);
       System.out.println("clone: " + clone);
       if (clone instanceof PlaceableShape) {
        if (!clone.isPlaceholder() || clone.getPlaceholder() == Placeholder.CONTENT) { // do not change anchor of placeholders except content
         PlaceableShape placeableShape = (PlaceableShape)clone;   
         java.awt.geom.Rectangle2D anchor = shape.getAnchor();
         placeableShape.setAnchor(new java.awt.geom.Rectangle2D.Double(anchor.getX()+100, anchor.getY()+100, anchor.getWidth(), anchor.getHeight()));
         //System.out.println(clone.getAnchor());
        }
       }
       if (clone instanceof XSLFTextShape) {
        XSLFTextShape textShape = (XSLFTextShape)clone;
        if (textShape.getTextParagraphs().size() > 0 && textShape.getTextParagraphs().get(0).getTextRuns().size() > 0) {
         textShape.getTextParagraphs().get(0).getTextRuns().get(0).setText("new text");
        } else {
         textShape.setText("new text");     
        }
        //System.out.println(textShape.getText());
       }
      }
    
      FileOutputStream out = new FileOutputStream("./PPTOut.pptx");
      slideShow.write(out);
      out.close();
      slideShow.close();
     }
    }
    

    【讨论】:

      【解决方案2】:

      如果克隆后收到警告shape id X has been already used, 在调用 initDrawingAndShapes 之前尝试清除 shapeIds:

      static void clearShapeIds(XSLFSheet sheet) {
              Field _shapeIds = XSLFSheet.class.getDeclaredField("shapeIds");
              _shapeIds.setAccessible(true);
              ((SparseBitSet) _shapeIds.get(sheet)).clear();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多