* if null, the bounds of the shape are used.
*/
void draw(Graphics2D graphics, Rectangle2D bounds);
+
+
+ /**
+ * Returns a unique identifier for this shape within the current slide.
+ * This ID may be used to assist in uniquely identifying this object so that it can
+ * be referred to by other parts of the document.
+ * <p>
+ * If multiple objects within the same slide share the same id attribute value,
+ * then the document shall be considered non-conformant.
+ * </p>
+ *
+ * @return unique id of this shape
+ */
+ int getShapeId();
}
CTShapeNonVisual nvSpPr = ct.addNewNvSpPr();
CTNonVisualDrawingProps cnv = nvSpPr.addNewCNvPr();
cnv.setName("AutoShape " + shapeId);
- cnv.setId(shapeId + 1);
+ cnv.setId(shapeId);
nvSpPr.addNewCNvSpPr();
nvSpPr.addNewNvPr();
CTShapeProperties spPr = ct.addNewSpPr();
CTConnectorNonVisual nvSpPr = ct.addNewNvCxnSpPr();
CTNonVisualDrawingProps cnv = nvSpPr.addNewCNvPr();
cnv.setName("Connector " + shapeId);
- cnv.setId(shapeId + 1);
+ cnv.setId(shapeId);
nvSpPr.addNewCNvCxnSpPr();
nvSpPr.addNewNvPr();
CTShapeProperties spPr = ct.addNewSpPr();
import java.awt.Color;
import java.awt.geom.Rectangle2D;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import org.apache.poi.POIXMLDocumentPart.RelationPart;
-import org.apache.poi.hpsf.ClassID;
-import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
-import org.apache.poi.openxml4j.opc.OPCPackage;
-import org.apache.poi.openxml4j.opc.PackagePart;
-import org.apache.poi.openxml4j.opc.PackagePartName;
-import org.apache.poi.openxml4j.opc.PackageRelationship;
-import org.apache.poi.openxml4j.opc.PackagingURIHelper;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.Beta;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape;
-import org.openxmlformats.schemas.presentationml.x2006.main.CTOleObject;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture;
import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
@Beta
public class XSLFDrawing {
private XSLFSheet _sheet;
- private int _shapeId = 1;
private CTGroupShape _spTree;
/*package*/ XSLFDrawing(XSLFSheet sheet, CTGroupShape spTree){
// ignore them for now
if (o instanceof CTNonVisualDrawingProps) {
CTNonVisualDrawingProps p = (CTNonVisualDrawingProps)o;
- _shapeId = (int)Math.max(_shapeId, p.getId());
+ sheet.registerShapeId((int)p.getId());
}
}
}
public XSLFAutoShape createAutoShape(){
CTShape sp = _spTree.addNewSp();
- sp.set(XSLFAutoShape.prototype(_shapeId++));
+ sp.set(XSLFAutoShape.prototype(_sheet.allocateShapeId()));
XSLFAutoShape shape = new XSLFAutoShape(sp, _sheet);
shape.setAnchor(new Rectangle2D.Double());
return shape;
public XSLFFreeformShape createFreeform(){
CTShape sp = _spTree.addNewSp();
- sp.set(XSLFFreeformShape.prototype(_shapeId++));
+ sp.set(XSLFFreeformShape.prototype(_sheet.allocateShapeId()));
XSLFFreeformShape shape = new XSLFFreeformShape(sp, _sheet);
shape.setAnchor(new Rectangle2D.Double());
return shape;
public XSLFTextBox createTextBox(){
CTShape sp = _spTree.addNewSp();
- sp.set(XSLFTextBox.prototype(_shapeId++));
+ sp.set(XSLFTextBox.prototype(_sheet.allocateShapeId()));
XSLFTextBox shape = new XSLFTextBox(sp, _sheet);
shape.setAnchor(new Rectangle2D.Double());
return shape;
public XSLFConnectorShape createConnector(){
CTConnector sp = _spTree.addNewCxnSp();
- sp.set(XSLFConnectorShape.prototype(_shapeId++));
+ sp.set(XSLFConnectorShape.prototype(_sheet.allocateShapeId()));
XSLFConnectorShape shape = new XSLFConnectorShape(sp, _sheet);
shape.setAnchor(new Rectangle2D.Double());
shape.setLineColor(Color.black);
}
public XSLFGroupShape createGroup(){
- CTGroupShape obj = _spTree.addNewGrpSp();
- obj.set(XSLFGroupShape.prototype(_shapeId++));
- XSLFGroupShape shape = new XSLFGroupShape(obj, _sheet);
+ CTGroupShape sp = _spTree.addNewGrpSp();
+ sp.set(XSLFGroupShape.prototype(_sheet.allocateShapeId()));
+ XSLFGroupShape shape = new XSLFGroupShape(sp, _sheet);
shape.setAnchor(new Rectangle2D.Double());
return shape;
}
public XSLFPictureShape createPicture(String rel){
- CTPicture obj = _spTree.addNewPic();
- obj.set(XSLFPictureShape.prototype(_shapeId++, rel));
- XSLFPictureShape shape = new XSLFPictureShape(obj, _sheet);
+ CTPicture sp = _spTree.addNewPic();
+ sp.set(XSLFPictureShape.prototype(_sheet.allocateShapeId(), rel));
+ XSLFPictureShape shape = new XSLFPictureShape(sp, _sheet);
shape.setAnchor(new Rectangle2D.Double());
return shape;
}
public XSLFTable createTable(){
- CTGraphicalObjectFrame obj = _spTree.addNewGraphicFrame();
- obj.set(XSLFTable.prototype(_shapeId++));
- XSLFTable shape = new XSLFTable(obj, _sheet);
+ CTGraphicalObjectFrame sp = _spTree.addNewGraphicFrame();
+ sp.set(XSLFTable.prototype(_sheet.allocateShapeId()));
+ XSLFTable shape = new XSLFTable(sp, _sheet);
shape.setAnchor(new Rectangle2D.Double());
return shape;
}
public XSLFObjectShape createOleShape(String pictureRel) {
- CTGraphicalObjectFrame obj = _spTree.addNewGraphicFrame();
- obj.set(XSLFObjectShape.prototype(_shapeId++, pictureRel));
- XSLFObjectShape shape = new XSLFObjectShape(obj, _sheet);
+ CTGraphicalObjectFrame sp = _spTree.addNewGraphicFrame();
+ sp.set(XSLFObjectShape.prototype(_sheet.allocateShapeId(), pictureRel));
+ XSLFObjectShape shape = new XSLFObjectShape(sp, _sheet);
shape.setAnchor(new Rectangle2D.Double());
return shape;
}
CTShapeNonVisual nvSpPr = ct.addNewNvSpPr();
CTNonVisualDrawingProps cnv = nvSpPr.addNewCNvPr();
cnv.setName("Freeform " + shapeId);
- cnv.setId(shapeId + 1);
+ cnv.setId(shapeId);
nvSpPr.addNewCNvSpPr();
nvSpPr.addNewNvPr();
CTShapeProperties spPr = ct.addNewSpPr();
public boolean removeShape(XSLFShape xShape) {
XmlObject obj = xShape.getXmlObject();
CTGroupShape grpSp = (CTGroupShape)getXmlObject();
+ getSheet().deregisterShapeId(xShape.getShapeId());
if(obj instanceof CTShape){
grpSp.getSpList().remove(obj);
} else if (obj instanceof CTGroupShape){
+ XSLFGroupShape gs = (XSLFGroupShape)xShape;
+ new ArrayList<>(gs.getShapes()).forEach(gs::removeShape);
grpSp.getGrpSpList().remove(obj);
} else if (obj instanceof CTConnector){
grpSp.getCxnSpList().remove(obj);
CTGroupShapeNonVisual nvSpPr = ct.addNewNvGrpSpPr();
CTNonVisualDrawingProps cnv = nvSpPr.addNewCNvPr();
cnv.setName("Group " + shapeId);
- cnv.setId(shapeId + 1);
+ cnv.setId(shapeId);
nvSpPr.addNewCNvGrpSpPr();
nvSpPr.addNewNvPr();
CTNonVisualDrawingProps cnv = nvGr.addNewCNvPr();
// usually the shape name has its index based on the n-th embeding, but having
// the prototype separate from the actual updating of the object, we use the shape id
- cnv.setName("Object " + (shapeId + 1));
- cnv.setId(shapeId + 1);
+ cnv.setName("Object " + shapeId);
+ cnv.setId(shapeId);
// add empty property elements otherwise Powerpoint doesn't load the file ...
nvGr.addNewCNvGraphicFramePr();
CTPictureNonVisual nvSpPr = ct.addNewNvPicPr();
CTNonVisualDrawingProps cnv = nvSpPr.addNewCNvPr();
cnv.setName("Picture " + shapeId);
- cnv.setId(shapeId + 1);
+ cnv.setId(shapeId);
nvSpPr.addNewCNvPicPr().addNewPicLocks().setNoChangeAspect(true);
nvSpPr.addNewNvPr();
return getCNvPr().getName();
}
- /**
- * Returns a unique identifier for this shape within the current document.
- * This ID may be used to assist in uniquely identifying this object so that it can
- * be referred to by other parts of the document.
- * <p>
- * If multiple objects within the same document share the same id attribute value,
- * then the document shall be considered non-conformant.
- * </p>
- *
- * @return unique id of this shape
- */
+ @Override
public int getShapeId() {
return (int)getCNvPr().getId();
}
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.BitSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
private Map<Integer, XSLFSimpleShape> _placeholderByIdMap;
private Map<Integer, XSLFSimpleShape> _placeholderByTypeMap;
+ private final BitSet shapeIds = new BitSet();
+
public XSLFSheet() {
super();
}
throw new IllegalStateException("SlideShow was not found");
}
+ protected int allocateShapeId() {
+ final int nextId = shapeIds.nextClearBit(1);
+ shapeIds.set(nextId);
+ return nextId;
+ }
+
+ protected void registerShapeId(final int shapeId) {
+ if (shapeIds.get(shapeId)) {
+ LOG.log(POILogger.WARN, "shape id "+shapeId+" has been already used.");
+ }
+ shapeIds.set(shapeId);
+ }
+
+ protected void deregisterShapeId(final int shapeId) {
+ if (!shapeIds.get(shapeId)) {
+ LOG.log(POILogger.WARN, "shape id "+shapeId+" hasn't been registered.");
+ }
+ shapeIds.clear(shapeId);
+ }
+
protected static List<XSLFShape> buildShapes(CTGroupShape spTree, XSLFShapeContainer parent){
final XSLFSheet sheet = (parent instanceof XSLFSheet) ? (XSLFSheet)parent : ((XSLFShape)parent).getSheet();
public boolean removeShape(XSLFShape xShape) {
XmlObject obj = xShape.getXmlObject();
CTGroupShape spTree = getSpTree();
+ deregisterShapeId(xShape.getShapeId());
if(obj instanceof CTShape){
spTree.getSpList().remove(obj);
} else if (obj instanceof CTGroupShape) {
+ XSLFGroupShape gs = (XSLFGroupShape)xShape;
+ new ArrayList<>(gs.getShapes()).forEach(gs::removeShape);
spTree.getGrpSpList().remove(obj);
} else if (obj instanceof CTConnector) {
spTree.getCxnSpList().remove(obj);
CTNonVisualDrawingProps cnv = nvGr.addNewCNvPr();
cnv.setName("Table " + shapeId);
- cnv.setId(shapeId + 1);
+ cnv.setId(shapeId);
nvGr.addNewCNvGraphicFramePr().addNewGraphicFrameLocks().setNoGrp(true);
nvGr.addNewNvPr();
CTShapeNonVisual nvSpPr = ct.addNewNvSpPr();
CTNonVisualDrawingProps cnv = nvSpPr.addNewCNvPr();
cnv.setName("TextBox " + shapeId);
- cnv.setId(shapeId + 1);
+ cnv.setId(shapeId);
nvSpPr.addNewCNvSpPr().setTxBox(true);
nvSpPr.addNewNvPr();
CTShapeProperties spPr = ct.addNewSpPr();
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.Optional;
+import java.util.function.Function;
import java.util.stream.Collectors;
import javax.imageio.ImageIO;
import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
+import org.apache.poi.sl.usermodel.Shape;
import org.apache.poi.sl.usermodel.ShapeType;
import org.apache.poi.sl.usermodel.VerticalAlignment;
import org.apache.poi.util.IOUtils;
-import org.apache.poi.xslf.extractor.XSLFPowerPointExtractor;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFAutoShape;
import org.apache.poi.xslf.usermodel.XSLFGroupShape;
assertNotNull(notesSlide);
}
- /*OutputStream stream = new FileOutputStream("/tmp/test.pptx");
- try {
- ppt.write(stream);
- } finally {
- stream.close();
- }*/
-
ppt.close();
}
}
ppt.close();
}
+
+ @Test
+ public void bug62051() throws IOException {
+ final Function<List<XSLFShape>, int[]> ids = (shapes) ->
+ shapes.stream().mapToInt(Shape::getShapeId).toArray();
+
+ try (final XMLSlideShow ppt = new XMLSlideShow()) {
+ final XSLFSlide slide = ppt.createSlide();
+ final List<XSLFShape> shapes = new ArrayList<>();
+ shapes.add(slide.createAutoShape());
+ final XSLFGroupShape g1 = slide.createGroup();
+ shapes.add(g1);
+ final XSLFGroupShape g2 = g1.createGroup();
+ shapes.add(g2);
+ shapes.add(g2.createAutoShape());
+ shapes.add(slide.createAutoShape());
+ shapes.add(g2.createAutoShape());
+ shapes.add(g1.createAutoShape());
+ assertArrayEquals(new int[]{ 2,3,4,5,6,7,8 }, ids.apply(shapes));
+ g1.removeShape(g2);
+ shapes.remove(5);
+ shapes.remove(3);
+ shapes.remove(2);
+ shapes.add(g1.createAutoShape());
+ assertArrayEquals(new int[]{ 2,3,6,8,4 }, ids.apply(shapes));
+
+ }
+ }
}
assertNotNull(tbl.getCTTable().getTblGrid());
assertNotNull(tbl.getCTTable().getTblPr());
assertTrue(tbl.getXmlObject() instanceof CTGraphicalObjectFrame);
- assertEquals("Table 1", tbl.getShapeName());
+ assertEquals("Table 2", tbl.getShapeName());
assertEquals(2, tbl.getShapeId());
assertEquals(0, tbl.getRows().size());
assertEquals(0, tbl.getCTTable().sizeOfTrArray());
return new Color(r, g, b);
}
- /**
- * @return id for the shape.
- */
+ @Override
public int getShapeId(){
EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID);
return spRecord == null ? 0 : spRecord.getShapeId();