如何复制形状 apache POI?

问题描述 投票:0回答:2

我需要使用 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
2个回答
1
投票

XSLFShape
s 没有任何克隆方法。即使可以,也没有任何方法可以将克隆的
XSLFShape
添加到
XSLFSheet
(幻灯片)。有 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();
 }
}

0
投票

如果克隆后您收到警告

shape id X has been already used
, 在调用之前尝试清除 shapeIds
initDrawingAndShapes
:

static void clearShapeIds(XSLFSheet sheet) {
        Field _shapeIds = XSLFSheet.class.getDeclaredField("shapeIds");
        _shapeIds.setAccessible(true);
        ((SparseBitSet) _shapeIds.get(sheet)).clear();
}
© www.soinside.com 2019 - 2024. All rights reserved.