diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 12a3c9a4e..f90dec601 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 52268 - support cloning sheets with drawings in XSSF 52285 - Support XWPF smart tags text in Paragraphs 51875 - More XSSF new-line in formula support POIFS EntryUtils.copyNodes(POFS,POIFS) now uses FilteringDirectoryNode, so can exclude from copying nodes not just directly under the root diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java index 0c13db8ff..72794b6ef 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java @@ -36,15 +36,10 @@ import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.util.Internal; import org.apache.poi.xssf.model.CommentsTable; import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; -import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTConnector; -import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTDrawing; -import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTGraphicalObjectFrame; -import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTGroupShape; -import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTPicture; -import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape; -import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor; -import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.STEditAs; +import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.*; import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; /** @@ -57,7 +52,6 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing { * Root element of the SpreadsheetML Drawing part */ private CTDrawing drawing; - private boolean isNew; private long numOfGraphicFrames = 0L; protected static final String NAMESPACE_A = "http://schemas.openxmlformats.org/drawingml/2006/main"; @@ -71,7 +65,6 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing { protected XSSFDrawing() { super(); drawing = newDrawing(); - isNew = true; } /** @@ -84,8 +77,10 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing { */ protected XSSFDrawing(PackagePart part, PackageRelationship rel) throws IOException, XmlException { super(part, rel); - drawing = CTDrawing.Factory.parse(part.getInputStream()); - isNew = false; + XmlOptions options = new XmlOptions(DEFAULT_XML_OPTIONS); + //Removing root element + options.setLoadReplaceDocumentElement(null); + drawing = CTDrawing.Factory.parse(part.getInputStream(),options); } /** @@ -117,13 +112,9 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing { xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"> */ - if(isNew) { - // Have it wrapped in a tag - xmlOptions.setSaveSyntheticDocumentElement( - new QName(CTDrawing.type.getName().getNamespaceURI(), "wsDr", "xdr") - ); - isNew = false; - } + xmlOptions.setSaveSyntheticDocumentElement( + new QName(CTDrawing.type.getName().getNamespaceURI(), "wsDr", "xdr") + ); Map map = new HashMap(); map.put(NAMESPACE_A, "a"); map.put(STRelationshipId.type.getName().getNamespaceURI(), "r"); @@ -372,4 +363,20 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing { private long newShapeId(){ return drawing.sizeOfTwoCellAnchorArray() + 1; } + + /** + * + * @return list of shapes in this drawing + */ + public List getShapes(){ + List lst = new ArrayList(); + for(XmlObject obj : drawing.selectPath("./*/*")) { + if(obj instanceof CTPicture) lst.add(new XSSFPicture(this, (CTPicture)obj)) ; + else if(obj instanceof CTConnector) lst.add(new XSSFConnector(this, (CTConnector)obj)) ; + else if(obj instanceof CTShape) lst.add(new XSSFSimpleShape(this, (CTShape)obj)) ; + else if(obj instanceof CTGraphicalObjectFrame) lst.add(new XSSFGraphicFrame(this, (CTGraphicalObjectFrame)obj)) ; + else if(obj instanceof CTGroupShape) lst.add(new XSSFShapeGroup(this, (CTGroupShape)obj)) ; + } + return lst; + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFGraphicFrame.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFGraphicFrame.java index 3abc774f8..d22802ab2 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFGraphicFrame.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFGraphicFrame.java @@ -25,14 +25,9 @@ import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.util.Internal; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlCursor; +import org.openxmlformats.schemas.drawingml.x2006.main.*; import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTGraphicalObjectFrame; import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTGraphicalObjectFrameNonVisual; -import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject; -import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData; -import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; -import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; /** @@ -40,7 +35,7 @@ import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelations * * @author Roman Kashitsyn */ -public final class XSSFGraphicFrame { +public final class XSSFGraphicFrame extends XSSFShape { private static CTGraphicalObjectFrame prototype = null; @@ -186,4 +181,8 @@ public final class XSSFGraphicFrame { data.setUri(c_namespaceUri); } + @Override + protected CTShapeProperties getShapeProperties(){ + return null; + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index 801128178..896be63bd 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -375,25 +375,56 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable rels = srcSheet.getRelations(); + // if the sheet being cloned has a drawing then rememebr it and re-create tpoo + XSSFDrawing dg = null; for(POIXMLDocumentPart r : rels) { + // do not copy the drawing relationship, it will be re-created + if(r instanceof XSSFDrawing) { + dg = (XSSFDrawing)r; + continue; + } + PackageRelationship rel = r.getPackageRelationship(); - clonedSheet.getPackagePart().addRelationship(rel.getTargetURI(), rel.getTargetMode(),rel.getRelationshipType()); + clonedSheet.getPackagePart().addRelationship( + rel.getTargetURI(), rel.getTargetMode(),rel.getRelationshipType()); clonedSheet.addRelation(rel.getId(), r); } + // clone the sheet drawing alongs with its relationships + if (dg != null) { + if(ct.isSetDrawing()) { + // unset the existing reference to the drawing, + // so that subsequent call of clonedSheet.createDrawingPatriarch() will create a new one + ct.unsetDrawing(); + } + XSSFDrawing clonedDg = clonedSheet.createDrawingPatriarch(); + // copy drawing contents + clonedDg.getCTDrawing().set(dg.getCTDrawing()); + + // Clone drawing relations + List srcRels = srcSheet.createDrawingPatriarch().getRelations(); + for (POIXMLDocumentPart rel : srcRels) { + PackageRelationship relation = rel.getPackageRelationship(); + clonedSheet + .createDrawingPatriarch() + .getPackagePart() + .addRelationship(relation.getTargetURI(), relation.getTargetMode(), + relation.getRelationshipType(), relation.getId()); + } + } return clonedSheet; } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java index 2ce1bf279..ced478e3e 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java @@ -23,6 +23,7 @@ import junit.framework.TestCase; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.xssf.XSSFTestDataSamples; +import org.apache.poi.xssf.dev.XSSFDump; import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTDrawing; /** @@ -45,9 +46,20 @@ public class TestXSSFDrawing extends TestCase { //there should be a relation to this drawing in the worksheet assertTrue(sheet.getCTWorksheet().isSetDrawing()); assertEquals(drawingId, sheet.getCTWorksheet().getDrawing().getId()); + + List shapes = drawing.getShapes(); + assertEquals(6, shapes.size()); + + assertTrue(shapes.get(0) instanceof XSSFPicture); + assertTrue(shapes.get(1) instanceof XSSFPicture); + assertTrue(shapes.get(2) instanceof XSSFPicture); + assertTrue(shapes.get(3) instanceof XSSFPicture); + assertTrue(shapes.get(4) instanceof XSSFSimpleShape); + assertTrue(shapes.get(5) instanceof XSSFPicture); + } - public void testNew(){ + public void testNew() throws Exception { XSSFWorkbook wb = new XSSFWorkbook(); XSSFSheet sheet = wb.createSheet(); //multiple calls of createDrawingPatriarch should return the same instance of XSSFDrawing @@ -66,31 +78,36 @@ public class TestXSSFDrawing extends TestCase { assertTrue(sheet.getCTWorksheet().isSetDrawing()); assertEquals(drawingId, sheet.getCTWorksheet().getDrawing().getId()); - XSSFClientAnchor anchor = new XSSFClientAnchor(); - - XSSFConnector c1= drawing.createConnector(anchor); + XSSFConnector c1= drawing.createConnector(new XSSFClientAnchor(0,0,0,0,0,0,2,2)); c1.setLineWidth(2.5); c1.setLineStyle(1); - XSSFShapeGroup c2 = drawing.createGroup(anchor); + XSSFShapeGroup c2 = drawing.createGroup(new XSSFClientAnchor(0,0,0,0,0,0,5,5)); - XSSFSimpleShape c3 = drawing.createSimpleShape(anchor); + XSSFSimpleShape c3 = drawing.createSimpleShape(new XSSFClientAnchor(0,0,0,0,2,2,3,4)); c3.setText(new XSSFRichTextString("Test String")); c3.setFillColor(128, 128, 128); - XSSFTextBox c4 = drawing.createTextbox(anchor); + XSSFTextBox c4 = drawing.createTextbox(new XSSFClientAnchor(0,0,0,0,4,4,5,6)); XSSFRichTextString rt = new XSSFRichTextString("Test String"); rt.applyFont(0, 5, wb.createFont()); rt.applyFont(5, 6, wb.createFont()); c4.setText(rt); c4.setNoFill(true); - - + assertEquals(4, drawing.getCTDrawing().sizeOfTwoCellAnchorArray()); + + List shapes = drawing.getShapes(); + assertEquals(4, shapes.size()); + assertTrue(shapes.get(0) instanceof XSSFConnector); + assertTrue(shapes.get(1) instanceof XSSFShapeGroup); + assertTrue(shapes.get(2) instanceof XSSFSimpleShape); + assertTrue(shapes.get(3) instanceof XSSFSimpleShape); // + // Save and re-load it wb = XSSFTestDataSamples.writeOutAndReadBack(wb); sheet = wb.getSheetAt(0); - + // Check dr1 = sheet.createDrawingPatriarch(); CTDrawing ctDrawing = dr1.getCTDrawing(); @@ -98,12 +115,19 @@ public class TestXSSFDrawing extends TestCase { // Connector, shapes and text boxes are all two cell anchors assertEquals(0, ctDrawing.sizeOfAbsoluteAnchorArray()); assertEquals(0, ctDrawing.sizeOfOneCellAnchorArray()); - // TODO Fix this! -// assertEquals(4, ctDrawing.sizeOfTwoCellAnchorArray()); - + assertEquals(4, ctDrawing.sizeOfTwoCellAnchorArray()); + + shapes = dr1.getShapes(); + assertEquals(4, shapes.size()); + assertTrue(shapes.get(0) instanceof XSSFConnector); + assertTrue(shapes.get(1) instanceof XSSFShapeGroup); + assertTrue(shapes.get(2) instanceof XSSFSimpleShape); + assertTrue(shapes.get(3) instanceof XSSFSimpleShape); // + // Ensure it got the right namespaces String xml = ctDrawing.toString(); - assertEquals(" rels1 = sheet1.getRelations(); + assertEquals(1, rels1.size()); + assertTrue(rels1.get(0) instanceof XSSFDrawing); + + List rels2 = sheet2.getRelations(); + assertEquals(1, rels2.size()); + assertTrue(rels2.get(0) instanceof XSSFDrawing); + + XSSFDrawing drawing1 = (XSSFDrawing)rels1.get(0); + XSSFDrawing drawing2 = (XSSFDrawing)rels2.get(0); + assertNotSame(drawing1, drawing2); // drawing2 is a clone of drawing1 + + List shapes1 = drawing1.getShapes(); + List shapes2 = drawing2.getShapes(); + assertEquals(shapes1.size(), shapes2.size()); + + for(int i = 0; i < shapes1.size(); i++){ + XSSFShape sh1 = (XSSFShape)shapes1.get(i); + XSSFShape sh2 = (XSSFShape)shapes2.get(i); + + assertTrue(sh1.getClass() == sh2.getClass()); + assertEquals(sh1.getShapeProperties().toString(), sh2.getShapeProperties().toString()); + } + } }