Java tutorial
/* * Copyright 2017-2018 Micro Focus International plc. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. */ package com.hp.autonomy.frontend.reports.powerpoint; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.InputStream; import java.util.List; import javax.xml.namespace.QName; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.xslf.usermodel.XMLSlideShow; import org.apache.poi.xslf.usermodel.XSLFChart; import org.apache.poi.xslf.usermodel.XSLFGraphicFrame; import org.apache.poi.xslf.usermodel.XSLFShape; import org.apache.poi.xslf.usermodel.XSLFSlide; import org.apache.poi.xssf.usermodel.XSSFRelation; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; import static org.apache.poi.util.Units.EMU_PER_POINT; /** * Internal implementation class to keep track of required elements from the template. */ class SlideShowTemplate { private static final String RELATION_NAMESPACE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; /** Parsed PowerPoint file from the template. */ private final XMLSlideShow pptx; /** Doughnut chart XML object, cached so we can clone it. */ private final ImmutablePair<XSLFChart, CTGraphicalObjectFrame> doughnutChart; /** An xy scatterplot chart XML object, cached so we can clone it. */ private final ImmutablePair<XSLFChart, CTGraphicalObjectFrame> graphChart; SlideShowTemplate(final InputStream inputStream) throws TemplateLoadException { try { // There should be a doughnut chart in slide 1 and a scatterplot chart in slide 2 pptx = new XMLSlideShow(inputStream); final List<XSLFSlide> slides = pptx.getSlides(); if (slides.size() != 2) { throw new TemplateLoadException( "Template powerpoint should have two slides, doughnut chart on slide 1 and time-axis xy scatterplot chart on slide 2"); } XSLFSlide slide = slides.get(0); doughnutChart = getChart(slide, "First slide should have a doughnut chart"); if (ArrayUtils.isEmpty(doughnutChart.getLeft().getCTChart().getPlotArea().getDoughnutChartArray())) { throw new TemplateLoadException( "First slide has the wrong chart type, should have a doughnut chart"); } graphChart = getChart(slides.get(1), "Second slide should have a time-axis xy scatterplot chart"); if (ArrayUtils.isEmpty(graphChart.getLeft().getCTChart().getPlotArea().getScatterChartArray())) { throw new TemplateLoadException( "Second slide has the wrong chart type, should have a time-axis xy scatterplot chart"); } // Remove the slides afterwards pptx.removeSlide(1); pptx.removeSlide(0); } catch (IOException e) { throw new TemplateLoadException("Error while loading slide show", e); } } /** * Get the doughnut chart from the first slide. Do not modify this object. * @return the doughnut chart from the first slide */ XSLFChart getDoughnutChart() { return doughnutChart.getLeft(); } /** * Creates a new clone of the doughnut chart XML from the first slide, for inclusion into a slide's shapes. * @param relId the relation id to the chart. * @param shapeId the shape id of the new shape. * @param shapeName the name of your choice for the shape. * @param anchor where the shape should be positioned on screen, or null to use the same position as the cloned chart. * @return a new clone of the doughnut chart XML. */ CTGraphicalObjectFrame getDoughnutChartShapeXML(final String relId, final int shapeId, final String shapeName, final Rectangle2D.Double anchor) { return cloneShapeXML(doughnutChart.getRight(), relId, shapeId, shapeName, anchor); } /** * Get the graph xy scatterplot chart from the second slide. Do not modify this object. * @return the graph xy scatterplot chart from the second slide */ XSLFChart getGraphChart() { return graphChart.getLeft(); } /** * Creates a new clone of the scatterplot chart XML from the second slide, for inclusion into a slide's shapes. * @param relId the relation id to the chart. * @param shapeId the shape id of the new shape. * @param shapeName the name of your choice for the shape. * @param anchor where the shape should be positioned on screen, or null to use the same position as the cloned chart. * @return a new clone of the scatterplot chart XML. */ CTGraphicalObjectFrame getGraphChartShapeXML(final String relId, final int shapeId, final String shapeName, final Rectangle2D.Double anchor) { return cloneShapeXML(graphChart.getRight(), relId, shapeId, shapeName, anchor); } /** * Get the template presentation with all slides removed. * @return the template presentation without any slides. */ XMLSlideShow getSlideShow() { return pptx; } /** * Given an existing slide, search its relations to find a chart object. * @param slide a slide from the template. * @param error if we can't find a slide, this error message will be returned as the exception. * @return a pair containing the chart.xml data and the graphical object which represented it on the slide. * @throws TemplateLoadException if we can't find a chart object. */ private ImmutablePair<XSLFChart, CTGraphicalObjectFrame> getChart(final XSLFSlide slide, final String error) throws TemplateLoadException { for (POIXMLDocumentPart.RelationPart part : slide.getRelationParts()) { if (part.getDocumentPart() instanceof XSLFChart) { final String relId = part.getRelationship().getId(); for (XSLFShape shape : slide.getShapes()) { if (shape instanceof XSLFGraphicFrame) { final CTGraphicalObjectFrame frameXML = (CTGraphicalObjectFrame) shape.getXmlObject(); final XmlObject[] children = frameXML.getGraphic().getGraphicData() .selectChildren(new QName(XSSFRelation.NS_CHART, "chart")); for (final XmlObject child : children) { final String imageRel = child.getDomNode().getAttributes() .getNamedItemNS(RELATION_NAMESPACE, "id").getNodeValue(); if (relId.equals(imageRel)) { return new ImmutablePair<>(part.getDocumentPart(), frameXML); } } } } } } throw new TemplateLoadException(error); } /** * Utility function to clone the graphical object which represents a chart on a slide. * @param base the object to clone. * @param relId the new relation ID we should insert. * @param shapeId the new shape ID we should insert. * @param shapeName the new shape name we should insert. * @param anchor the bounds of the new shape object in PowerPoint coordinates, if set, or null to use the existing clone's bounds. * @return a new clone object with the desired properties. */ private CTGraphicalObjectFrame cloneShapeXML(final CTGraphicalObjectFrame base, final String relId, final int shapeId, final String shapeName, final Rectangle2D.Double anchor) { /* Based on viewing the raw chart. <p:graphicFrame> <p:nvGraphicFramePr> <p:cNvPr id="4" name="Chart 3"/> <p:cNvGraphicFramePr/> <p:nvPr> <p:extLst> <p:ext uri="{D42A27DB-BD31-4B8C-83A1-F6EECF244321}"> <p14:modId xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main" val="866141002"/> </p:ext> </p:extLst> </p:nvPr> </p:nvGraphicFramePr> <p:xfrm> <a:off x="0" y="0"/> <a:ext cx="12192000" cy="6858000"/> </p:xfrm> <a:graphic> <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart"> <c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="rId2"/> </a:graphicData> </a:graphic> </p:graphicFrame> */ final CTGraphicalObjectFrame copy = (CTGraphicalObjectFrame) base.copy(); final CTNonVisualDrawingProps cNvPr = copy.getNvGraphicFramePr().getCNvPr(); cNvPr.setName(shapeName); cNvPr.setId(shapeId); final XmlObject[] children = copy.getGraphic().getGraphicData() .selectChildren(new QName(XSSFRelation.NS_CHART, "chart")); if (anchor != null) { final CTPoint2D off = copy.getXfrm().getOff(); off.setX((int) (anchor.getX() * EMU_PER_POINT)); off.setY((int) (anchor.getY() * EMU_PER_POINT)); final CTPositiveSize2D ext = copy.getXfrm().getExt(); ext.setCx((int) (anchor.getWidth() * EMU_PER_POINT)); ext.setCy((int) (anchor.getHeight() * EMU_PER_POINT)); } for (final XmlObject child : children) { child.getDomNode().getAttributes().getNamedItemNS(RELATION_NAMESPACE, "id").setNodeValue(relId); } return copy; } }