[bug-66176] Integrate SmartArt diagrams from powerpoint presentations. Thanks to Yaseen.

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1902934 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
PJ Fanning 2022-07-22 09:55:10 +00:00
parent aa7eee178c
commit cc7fcdfab3
7 changed files with 578 additions and 4 deletions

View File

@ -0,0 +1,137 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.examples.xslf;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.xslf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
/**
* Converts SmartArt to openxml shapes and saves the result to the specified output path.
*/
public class SmartArtConversionDemo {
private final XMLSlideShow inputPptx;
private final XMLSlideShow outputPptx;
SmartArtConversionDemo(XMLSlideShow inputPptx, XMLSlideShow outputPptx) {
this.inputPptx = inputPptx;
this.outputPptx = outputPptx;
}
public static void main(String[] args) throws IOException {
if (args.length != 2) {
System.out.println("Expected arguments: <inputPath> <outputPath>");
System.exit(1);
}
File inputFile = new File(args[0]);
if (!inputFile.exists()) {
System.out.printf("Unable to find input file at path: %s", args[0]);
System.exit(1);
}
try (
FileInputStream inputPptxStream = new FileInputStream(inputFile);
FileOutputStream outputPptxStream = new FileOutputStream(args[1])
) {
XMLSlideShow inputPptx = new XMLSlideShow(inputPptxStream);
XMLSlideShow outputPptx = new XMLSlideShow();
SmartArtConversionDemo demo = new SmartArtConversionDemo(inputPptx, outputPptx);
demo.convertSmartArt();
outputPptx.write(outputPptxStream);
}
}
private static void copyAndUpdateImageRelations(XSLFDiagram diagram, XSLFSlide outputSlide) throws IOException {
XSLFGroupShape inputGroupShape = diagram.getGroupShape();
for (XSLFShape shape : inputGroupShape.getShapes()) {
org.openxmlformats.schemas.presentationml.x2006.main.CTShape ctShape
= (org.openxmlformats.schemas.presentationml.x2006.main.CTShape) shape.getXmlObject();
if (ctShape.getSpPr().getBlipFill() == null) {
continue;
}
CTBlipFillProperties blipFillProps = ctShape.getSpPr().getBlipFill();
CTBlip blip = blipFillProps.getBlip();
// In SmartArt diagrams, the references to images/embeds are stored in drawing#.xml.rels. When read by
// POI it copies this relationship to the parent slide to allow POI to correctly resolve the images.
POIXMLDocumentPart inputPicturePart = diagram.getSheet().getRelationById(blip.getEmbed());
if (inputPicturePart == null || inputPicturePart.getPackagePart() == null) {
continue;
}
XSLFPictureData inputPictureData = new XSLFPictureData(inputPicturePart.getPackagePart());
// Copy the input image to the output slides and update the shape to reference the copied image
XMLSlideShow outputPptx = outputSlide.getSlideShow();
XSLFPictureData outputPictureData = outputPptx.addPicture(
inputPicturePart.getPackagePart().getInputStream(), inputPictureData.getType());
POIXMLDocumentPart.RelationPart outputRelation = outputSlide.addRelation(null, XSLFRelation.IMAGES, outputPictureData);
ctShape.getSpPr().getBlipFill().getBlip().setEmbed(outputRelation.getRelationship().getId());
}
}
private static XSLFTheme extractTheme(XMLSlideShow slideShow) {
if (!slideShow.getSlideMasters().isEmpty()) {
return slideShow.getSlideMasters().get(0).getTheme();
}
return null;
}
private void convertSmartArt() throws IOException {
// Copy page size and theme
outputPptx.setPageSize(inputPptx.getPageSize());
XSLFTheme theme = extractTheme(inputPptx);
if (theme != null) {
outputPptx.getSlideMasters().get(0).getTheme().getXmlObject().set(theme.getXmlObject());
}
for (XSLFSlide inputSlide : inputPptx.getSlides()) {
XSLFSlide outputSlide = outputPptx.createSlide();
List<XSLFShape> inputShapes = inputSlide.getShapes();
for (XSLFShape shape : inputShapes) {
if (shape instanceof XSLFDiagram) {
copyDiagramToOutput((XSLFDiagram) shape, outputSlide);
} else {
XSLFAutoShape autoShape = outputSlide.createAutoShape();
// Hacky hack. Reassign xml to copy the content over.
autoShape.getXmlObject().set(shape.getXmlObject());
}
}
}
}
private void copyDiagramToOutput(XSLFDiagram inputDiagram, XSLFSlide outputSlide) throws IOException {
// This method modifies the underlying xml of the input shapes. We modify the xml structure first, then
// assign that to our output shape.
copyAndUpdateImageRelations(inputDiagram, outputSlide);
XSLFGroupShape inputGroupShape = inputDiagram.getGroupShape();
XSLFGroupShape outputGroupShape = outputSlide.createGroup();
outputGroupShape.getXmlObject().set(inputGroupShape.getXmlObject());
}
}

View File

@ -0,0 +1,243 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.xslf.usermodel;
import com.microsoft.schemas.office.drawing.x2008.diagram.CTGroupShape;
import com.microsoft.schemas.office.drawing.x2008.diagram.CTShape;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.util.Beta;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.drawingml.x2006.diagram.CTRelIds;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupShapeProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShapeNonVisual;
import org.openxmlformats.schemas.presentationml.x2006.main.CTShapeNonVisual;
import javax.xml.namespace.QName;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
/**
* Representation of a DrawingML Diagram
* <p>
* This class converts the diagram to an {@link XSLFGroupShape} accessible via {@link #getGroupShape()}. The underlying
* {@link XSLFDiagramDrawing} used to create the group shape is accessible via {@link #getDiagramDrawing()}.
* <p>
* In pptx files, these diagrams are generated by creating SmartArt. When a pptx has SmartArt, a directory with the
* following structure is created:
*
* <pre>
* ppt/
* diagrams/
* data#.xml
* drawing#.xml^
* colors#.xml
* quickStyle#.xml
* layout#.xml
* rels/
* data#.xml.rels
* drawing#.xml.rels
* </pre>
* <p>
* ^The `drawing#.xml` file is not in the OpenXML spec. It was added as an extension by Microsoft, namespace:
* http://schemas.microsoft.com/office/drawing/2008/diagram
* <p>
* The drawing#.xml file contains the rendered output of the diagram. This class reads the underlying drawing#.xml and
* converts it to a {@link XSLFGroupShape}.
* <p>
* The data, drawing, colors, and quickStyle files are in the OpenXML spec. These contain the instructions that define
* how to render the diagram. Rendering diagrams from these files is not trivial, they support for loops, if/elses, etc.
* Integrating such a change into POI would be quite sophisticated and challenging.
*
* @since POI 5.2.3
*/
@Beta
public class XSLFDiagram extends XSLFGraphicFrame {
public static final String DRAWINGML_DIAGRAM_URI = "http://schemas.openxmlformats.org/drawingml/2006/diagram";
private final XSLFDiagramDrawing _drawing;
private final XSLFGroupShape _groupShape;
/* package protected */ XSLFDiagram(CTGraphicalObjectFrame shape, XSLFSheet sheet) {
super(shape, sheet);
_drawing = readDiagramDrawing(shape, sheet);
_groupShape = initGroupShape(sheet);
}
private static boolean hasTextContent(CTShape msShapeCt) {
if (msShapeCt.getTxBody() == null || msShapeCt.getTxXfrm() == null) {
return false;
}
// A shape has text content when there is at least 1 paragraph with 1 paragraph run list
List<CTTextParagraph> paragraphs = msShapeCt.getTxBody().getPList();
return paragraphs.stream()
.flatMap(p -> p.getRList().stream())
.anyMatch(run -> run.getT() != null && !run.getT().trim().isEmpty());
}
private static boolean hasBlipEmbed(CTShape msShapeCt) {
return msShapeCt != null
&& msShapeCt.getSpPr() != null
&& msShapeCt.getSpPr().getBlipFill() != null
&& msShapeCt.getSpPr().getBlipFill().getBlip() != null
&& msShapeCt.getSpPr().getBlipFill().getBlip().getEmbed() != null;
}
private static XSLFDiagramDrawing readDiagramDrawing(CTGraphicalObjectFrame shape, XSLFSheet sheet) {
CTGraphicalObjectData graphicData = shape.getGraphic().getGraphicData();
XmlObject[] children = graphicData.selectChildren(new QName(DRAWINGML_DIAGRAM_URI, "relIds"));
if (children.length == 0) {
return null;
}
// CTRelIds doesn't contain a relationship to the drawing#.xml
// But it has the same name as the other data#.xml, layout#.xml, etc.
CTRelIds relIds = (CTRelIds) children[0];
POIXMLDocumentPart dataModelPart = sheet.getRelationById(relIds.getDm());
if (dataModelPart == null) {
return null;
}
String dataPartName = dataModelPart.getPackagePart().getPartName().getName();
String drawingPartName = dataPartName.replace("data", "drawing");
for (POIXMLDocumentPart.RelationPart rp : sheet.getRelationParts()) {
if (drawingPartName.equals(rp.getDocumentPart().getPackagePart().getPartName().getName())) {
if (rp.getDocumentPart() instanceof XSLFDiagramDrawing) {
return rp.getDocumentPart();
}
}
}
return null;
}
// If the shape has text, two XSLFShapes are created. One shape element and one textbox element.
public List<org.openxmlformats.schemas.presentationml.x2006.main.CTShape> convertShape(CTShape msShapeCt, XSLFSheet sheet) {
org.openxmlformats.schemas.presentationml.x2006.main.CTShape shapeCt
= org.openxmlformats.schemas.presentationml.x2006.main.CTShape.Factory.newInstance();
// The fields on MS CTShape largely re-use the underlying openxml classes.
// We just copy the fields from the MS CTShape to the openxml CTShape
shapeCt.setStyle(msShapeCt.getStyle());
shapeCt.setSpPr(msShapeCt.getSpPr());
CTShapeNonVisual nonVisualCt = shapeCt.addNewNvSpPr();
nonVisualCt.setCNvPr(msShapeCt.getNvSpPr().getCNvPr());
nonVisualCt.setCNvSpPr(msShapeCt.getNvSpPr().getCNvSpPr());
nonVisualCt.setNvPr(CTApplicationNonVisualDrawingProps.Factory.newInstance());
shapeCt.setNvSpPr(nonVisualCt);
ArrayList<org.openxmlformats.schemas.presentationml.x2006.main.CTShape> shapes = new ArrayList<>();
shapes.add(shapeCt);
if (hasTextContent(msShapeCt)) {
org.openxmlformats.schemas.presentationml.x2006.main.CTShape textShapeCT = convertText(msShapeCt, nonVisualCt);
shapes.add(textShapeCT);
}
if (hasBlipEmbed(msShapeCt)) {
String embedId = msShapeCt.getSpPr().getBlipFill().getBlip().getEmbed();
POIXMLDocumentPart part = _drawing.getRelationById(embedId);
if (part != null) {
// When reading the blip, POI looks into the `slide#.xml.rels` file. However, the blip relationship is
// defined inside `drawing#.xml.rels`. Copy this relationship to the parent.
POIXMLDocumentPart.RelationPart updatedRelation = sheet.addRelation(null, XSLFRelation.IMAGES, part);
shapeCt.getSpPr().getBlipFill().getBlip().setEmbed(updatedRelation.getRelationship().getId());
}
}
return shapes;
}
private org.openxmlformats.schemas.presentationml.x2006.main.CTShape convertText(CTShape msShapeCt, CTShapeNonVisual nonVisualCt) {
org.openxmlformats.schemas.presentationml.x2006.main.CTShape textShapeCT
= org.openxmlformats.schemas.presentationml.x2006.main.CTShape.Factory.newInstance();
// SmartArt shapes define a separate `txXfrm` property for the placement of text inside the shape
// We can't easily (is it even possible?) set a separate xfrm for the text on the openxml CTShape.
// Instead, we create a separate textbox shape with the same xfrm.
org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties textShapeProps = textShapeCT.addNewSpPr();
textShapeProps.setXfrm(msShapeCt.getTxXfrm());
textShapeCT.setTxBody(msShapeCt.getTxBody());
textShapeCT.setStyle(msShapeCt.getStyle());
// Create a copy of the nonVisualCt when setting it for the text box.
// If we shared the one object, a consumer may be surprised that updating the text shape properties
// also updates the parent shape.
textShapeCT.setNvSpPr((CTShapeNonVisual) nonVisualCt.copy());
return textShapeCT;
}
/**
* Returns the underlying {@link XSLFDiagramDrawing} used to create this diagram.
* <p>
* NOTE: Modifying this drawing will not update the groupShape returned from {@link #getGroupShape()}.
*/
public XSLFDiagramDrawing getDiagramDrawing() {
return _drawing;
}
private XSLFGroupShape initGroupShape(XSLFSheet sheet) {
XSLFDiagramDrawing drawing = getDiagramDrawing();
if (drawing == null || drawing.getDrawingDocument() == null) {
return null;
}
CTGroupShape msGroupShapeCt = drawing.getDrawingDocument().getDrawing().getSpTree();
if (msGroupShapeCt == null || msGroupShapeCt.getSpList().isEmpty()) {
return null;
}
return convertMsGroupToGroupShape(msGroupShapeCt, sheet);
}
/**
* Returns the diagram represented as a grouped shape.
*/
public XSLFGroupShape getGroupShape() {
return _groupShape;
}
private XSLFGroupShape convertMsGroupToGroupShape(CTGroupShape msGroupShapeCt, XSLFSheet sheet) {
org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape groupShapeCt
= org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape.Factory.newInstance();
CTGroupShapeProperties groupShapePropsCt = groupShapeCt.addNewGrpSpPr();
CTGroupShapeNonVisual groupShapeNonVisualCt = groupShapeCt.addNewNvGrpSpPr();
groupShapeNonVisualCt.setCNvPr(msGroupShapeCt.getNvGrpSpPr().getCNvPr());
groupShapeNonVisualCt.setCNvGrpSpPr(msGroupShapeCt.getNvGrpSpPr().getCNvGrpSpPr());
groupShapeNonVisualCt.setNvPr(CTApplicationNonVisualDrawingProps.Factory.newInstance());
for (CTShape msShapeCt : msGroupShapeCt.getSpList()) {
List<org.openxmlformats.schemas.presentationml.x2006.main.CTShape> shapes = convertShape(msShapeCt, sheet);
groupShapeCt.getSpList().addAll(shapes);
}
Rectangle2D anchor = super.getAnchor();
Rectangle2D interiorAnchor = new Rectangle2D.Double(0, 0, anchor.getWidth(), anchor.getHeight());
XSLFGroupShape groupShape = new XSLFGroupShape(groupShapeCt, getSheet());
groupShape.setAnchor(anchor);
groupShape.setInteriorAnchor(interiorAnchor);
groupShape.setRotation(super.getRotation());
return groupShape;
}
}

View File

@ -0,0 +1,55 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.xslf.usermodel;
import com.microsoft.schemas.office.drawing.x2008.diagram.DrawingDocument;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.util.Beta;
import org.apache.xmlbeans.XmlException;
import java.io.IOException;
import java.io.InputStream;
/**
* Drawing representation of a SmartArt diagram.
*/
@Beta
public class XSLFDiagramDrawing extends POIXMLDocumentPart {
private final DrawingDocument _drawingDoc;
/* package protected */ XSLFDiagramDrawing() {
super();
_drawingDoc = DrawingDocument.Factory.newInstance();
}
/* package protected */ XSLFDiagramDrawing(PackagePart part) throws XmlException, IOException {
super(part);
_drawingDoc = readPackagePart(part);
}
private static DrawingDocument readPackagePart(PackagePart part) throws IOException, XmlException {
try (InputStream is = part.getInputStream()) {
return DrawingDocument.Factory.parse(is);
}
}
public DrawingDocument getDrawingDocument() {
return _drawingDoc;
}
}

View File

@ -53,7 +53,6 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape;
@Beta
public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame<XSLFShape, XSLFTextParagraph> {
private static final String DRAWINGML_CHART_URI = "http://schemas.openxmlformats.org/drawingml/2006/chart";
private static final String DRAWINGML_DIAGRAM_URI = "http://schemas.openxmlformats.org/drawingml/2006/diagram";
private static final Logger LOG = LogManager.getLogger(XSLFGraphicFrame.class);
/*package*/ XSLFGraphicFrame(CTGraphicalObjectFrame shape, XSLFSheet sheet){
@ -100,6 +99,8 @@ public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame<XSLFSh
return new XSLFTable(shape, sheet);
case XSLFObjectShape.OLE_URI:
return new XSLFObjectShape(shape, sheet);
case XSLFDiagram.DRAWINGML_DIAGRAM_URI:
return new XSLFDiagram(shape, sheet);
default:
return new XSLFGraphicFrame(shape, sheet);
}
@ -177,7 +178,7 @@ public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame<XSLFSh
*/
public boolean hasDiagram() {
String uri = getGraphicalData().getUri();
return uri.equals(DRAWINGML_DIAGRAM_URI);
return uri.equals(XSLFDiagram.DRAWINGML_DIAGRAM_URI);
}
private CTGraphicalObjectData getGraphicalData() {
@ -211,7 +212,7 @@ public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame<XSLFSh
CTGraphicalObjectData data = getGraphicalData();
String uri = data.getUri();
if(uri.equals(DRAWINGML_DIAGRAM_URI)){
if(uri.equals(XSLFDiagram.DRAWINGML_DIAGRAM_URI)){
copyDiagram(data, (XSLFGraphicFrame)sh);
} if(uri.equals(DRAWINGML_CHART_URI)){
copyChart(data, (XSLFGraphicFrame)sh);
@ -256,7 +257,7 @@ public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame<XSLFSh
// TODO should be moved to a sub-class
private void copyDiagram(CTGraphicalObjectData objData, XSLFGraphicFrame srcShape){
String xpath = "declare namespace dgm='" + DRAWINGML_DIAGRAM_URI + "' $this//dgm:relIds";
String xpath = "declare namespace dgm='" + XSLFDiagram.DRAWINGML_DIAGRAM_URI + "' $this//dgm:relIds";
XmlObject[] obj = objData.selectPath(xpath);
if(obj != null && obj.length == 1) {
XSLFSheet sheet = srcShape.getSheet();

View File

@ -150,6 +150,13 @@ public final class XSLFRelation extends POIXMLRelation {
XSLFChart::new, XSLFChart::new
);
public static final XSLFRelation DIAGRAM_DRAWING = new XSLFRelation(
"application/vnd.ms-office.drawingml.diagramDrawing+xml",
"http://schemas.microsoft.com/office/2007/relationships/diagramDrawing",
"/ppt/diagrams/drawing#.xml",
XSLFDiagramDrawing::new, XSLFDiagramDrawing::new
);
public static final XSLFRelation IMAGE_EMF = new XSLFRelation(
PictureType.EMF.contentType,
IMAGE_PART,

View File

@ -0,0 +1,131 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.xslf.usermodel;
import org.apache.poi.openxml4j.opc.ContentTypes;
import org.apache.poi.sl.usermodel.ColorStyle;
import org.apache.poi.sl.usermodel.PaintStyle;
import org.apache.poi.sl.usermodel.TextParagraph.TextAlign;
import org.apache.poi.xslf.XSLFTestDataSamples;
import org.junit.jupiter.api.Test;
import java.awt.Color;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.*;
public class TestXSLFDiagram {
private static final String SIMPLE_DIAGRAM = "smartart-simple.pptx";
private static List<XSLFDiagram> extractDiagrams(XMLSlideShow slideShow) {
return slideShow.getSlides()
.stream()
.flatMap(s -> s.getShapes().stream())
.filter(s -> s instanceof XSLFDiagram)
.map(s -> (XSLFDiagram) s)
.collect(Collectors.toList());
}
// https://stackoverflow.com/a/3607942
private static String colorToHex(Color color) {
return String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue());
}
private static Color hexToColor(String hex) {
return Color.decode(hex);
}
@Test
public void testHasDiagram() {
XMLSlideShow inputPptx = XSLFTestDataSamples.openSampleDocument(SIMPLE_DIAGRAM);
List<XSLFDiagram> diagrams = extractDiagrams(inputPptx);
assertEquals(1, diagrams.size());
XSLFDiagram diagram = diagrams.get(0);
assertTrue(diagram.hasDiagram());
}
@Test
public void testDiagramContainsShapes() {
XMLSlideShow inputPptx = XSLFTestDataSamples.openSampleDocument(SIMPLE_DIAGRAM);
List<XSLFDiagram> diagrams = extractDiagrams(inputPptx);
assertEquals(1, diagrams.size());
XSLFDiagram diagram = diagrams.get(0);
XSLFGroupShape groupShape = diagram.getGroupShape();
assertNotNull(groupShape);
// The Group gets the same positioning as the SmartArt. This can be much wider/taller than the content inside.
assertEquals(groupShape.getAnchor().getWidth(), 113.375, 1E-4);
assertEquals(groupShape.getAnchor().getHeight(), 74, 1E-4);
assertEquals(groupShape.getAnchor().getX(), -16.75, 1E-4);
assertEquals(groupShape.getAnchor().getY(), 5.5, 1E-4);
List<XSLFShape> shapes = groupShape.getShapes();
// 4 shapes, 3 text boxes, one shape does not have any text inside it
assertEquals(7, shapes.size());
// Shape 1 - Yellow Circle - "abc" center aligned
String accent4Hex = "#ffc000"; // yellow
XSLFAutoShape yellowCircle = (XSLFAutoShape) shapes.get(0);
assertTrue(yellowCircle.getText().isEmpty());
assertEquals(accent4Hex, colorToHex(yellowCircle.getFillColor()));
XSLFAutoShape yellowCircleText = (XSLFAutoShape) shapes.get(1);
assertEquals(yellowCircleText.getText(), "abc");
assertEquals(TextAlign.CENTER, yellowCircleText.getTextParagraphs().get(0).getTextAlign());
// Shape 2 - Gradient Blue & Purple - "def" left aligned
XSLFAutoShape gradientCircle = (XSLFAutoShape) shapes.get(2);
assertTrue(gradientCircle.getFillPaint() instanceof PaintStyle.GradientPaint);
assertTrue(gradientCircle.getText().isEmpty());
XSLFAutoShape gradientCircleText = (XSLFAutoShape) shapes.get(3);
assertEquals(gradientCircleText.getText(), "def");
// Even with left justification, the text is rendered on the right side of the circle because SmartArt defines
// a better visual placement for the textbox inside the txXfrm property.
assertEquals(1, gradientCircleText.getTextParagraphs().size());
XSLFTextParagraph paragraph = gradientCircleText.getTextParagraphs().get(0);
assertEquals(TextAlign.LEFT, paragraph.getTextAlign());
assertEquals(1, paragraph.getTextRuns().size());
XSLFTextRun textRun = paragraph.getTextRuns().get(0);
assertTrue(textRun.isBold());
assertTrue(textRun.isItalic());
// Shape 3 - Green Circle with theme color - "ghi" right aligned
XSLFAutoShape greenCircle = (XSLFAutoShape) shapes.get(4);
ColorStyle greenCircleColorStyle = ((PaintStyle.SolidPaint) greenCircle.getFillPaint()).getSolidColor();
// The circle uses the yellow accent4 color but has HSL adjustments that make it green
assertEquals(hexToColor(accent4Hex), greenCircleColorStyle.getColor());
assertEquals(50004, greenCircleColorStyle.getAlpha()); // 50% transparency
assertEquals(6533927, greenCircleColorStyle.getHueOff());
assertEquals(6405, greenCircleColorStyle.getLumOff());
assertEquals(-27185, greenCircleColorStyle.getSatOff());
XSLFAutoShape greenCircleText = (XSLFAutoShape) shapes.get(5);
assertEquals(greenCircleText.getText(), "ghi");
assertEquals(TextAlign.RIGHT, greenCircleText.getTextParagraphs().get(0).getTextAlign());
// Shape 4 - Circle with Picture Fill - no text
XSLFAutoShape pictureShape = (XSLFAutoShape) shapes.get(6);
assertTrue(pictureShape.getText().isEmpty());
XSLFTexturePaint texturePaint = (XSLFTexturePaint) pictureShape.getFillPaint();
assertEquals(ContentTypes.IMAGE_JPEG, texturePaint.getContentType());
}
}

Binary file not shown.