diff options
59 files changed, 2331 insertions, 985 deletions
@@ -108,6 +108,7 @@ under the License. <!-- OOXML support: --> <property name="ooxml.src" location="src/ooxml/java"/> + <property name="ooxml.resource1.dir" value="src/resources/ooxml"/> <property name="ooxml.src.test" location="src/ooxml/testcases"/> <property name="ooxml.reports.test" location="build/ooxml-test-results"/> <property name="ooxml.output.dir" location="build/ooxml-classes"/> @@ -553,6 +554,9 @@ under the License. <pathelement path="${main.output.test.dir}"/> </classpath> </javac> + <copy todir="${ooxml.output.dir}"> + <fileset dir="${ooxml.resource1.dir}"/> + </copy> </target> <target name="compile-excelant" depends="compile-main,compile-ooxml"> diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/tutorial/Step1.java b/src/examples/src/org/apache/poi/xslf/usermodel/tutorial/Step1.java new file mode 100644 index 0000000000..a83a17e4b8 --- /dev/null +++ b/src/examples/src/org/apache/poi/xslf/usermodel/tutorial/Step1.java @@ -0,0 +1,68 @@ +/* + * ==================================================================== + * 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.tutorial; + +import org.apache.poi.xslf.usermodel.XMLSlideShow; +import org.apache.poi.xslf.usermodel.XSLFShape; +import org.apache.poi.xslf.usermodel.XSLFSlide; +import org.apache.poi.xslf.usermodel.XSLFTextParagraph; +import org.apache.poi.xslf.usermodel.XSLFTextRun; +import org.apache.poi.xslf.usermodel.XSLFTextShape; + +import java.io.FileInputStream; + +/** + * Reading a .pptx presentation and printing basic shape properties + * + * @author Yegor Kozlov + */ +public class Step1 { + + public static void main(String[] args) throws Exception { + if(args.length == 0) { + System.out.println("Input file is required"); + return; + } + + XMLSlideShow ppt = new XMLSlideShow(new FileInputStream(args[0])); + + for(XSLFSlide slide : ppt.getSlides()){ + System.out.println("Title: " + slide.getTitle()); + + for(XSLFShape shape : slide.getShapes()){ + if(shape instanceof XSLFTextShape) { + XSLFTextShape tsh = (XSLFTextShape)shape; + for(XSLFTextParagraph p : tsh){ + System.out.println("Paragraph level: " + p.getLevel()); + for(XSLFTextRun r : p){ + System.out.println(r.getText()); + System.out.println(" bold: " + r.isBold()); + System.out.println(" italic: " + r.isItalic()); + System.out.println(" underline: " + r.isUnderline()); + System.out.println(" font.family: " + r.getFontFamily()); + System.out.println(" font.size: " + r.getFontSize()); + System.out.println(" font.color: " + r.getFontColor()); + } + } + } + } + } + } +} diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/tutorial/Step2.java b/src/examples/src/org/apache/poi/xslf/usermodel/tutorial/Step2.java new file mode 100644 index 0000000000..16b155d3f2 --- /dev/null +++ b/src/examples/src/org/apache/poi/xslf/usermodel/tutorial/Step2.java @@ -0,0 +1,80 @@ +/* + * ==================================================================== + * 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.tutorial; + +import org.apache.poi.xslf.usermodel.SlideLayout; +import org.apache.poi.xslf.usermodel.XMLSlideShow; +import org.apache.poi.xslf.usermodel.XSLFSlide; +import org.apache.poi.xslf.usermodel.XSLFSlideLayout; +import org.apache.poi.xslf.usermodel.XSLFSlideMaster; +import org.apache.poi.xslf.usermodel.XSLFTextShape; + +import java.io.FileOutputStream; + +/** + * Create slides from pre-defined slide layouts + * + * @author Yegor Kozlov + */ +public class Step2 { + public static void main(String[] args) throws Exception{ + XMLSlideShow ppt = new XMLSlideShow(); + + + // first see what slide layouts are available by default + System.out.println("Available slide layouts:"); + for(XSLFSlideMaster master : ppt.getSlideMasters()){ + for(XSLFSlideLayout layout : master.getSlideLayouts()){ + System.out.println(layout.getType()); + } + } + + // blank slide + XSLFSlide blankSlide = ppt.createSlide(); + + XSLFSlideMaster defaultMaster = ppt.getSlideMasters()[0]; + + // title slide + XSLFSlideLayout titleLayout = defaultMaster.getLayout(SlideLayout.TITLE); + XSLFSlide slide1 = ppt.createSlide(titleLayout); + XSLFTextShape title1 = slide1.getPlaceholder(0); + title1.setText("First Title"); + + // title and content + XSLFSlideLayout titleBodyLayout = defaultMaster.getLayout(SlideLayout.TITLE_AND_CONTENT); + XSLFSlide slide2 = ppt.createSlide(titleBodyLayout); + + XSLFTextShape title2 = slide2.getPlaceholder(0); + title2.setText("Second Title"); + + XSLFTextShape body2 = slide2.getPlaceholder(1); + body2.clearText(); // unset any existing text + body2.addNewTextParagraph().addNewTextRun().setText("First paragraph"); + body2.addNewTextParagraph().addNewTextRun().setText("Second paragraph"); + body2.addNewTextParagraph().addNewTextRun().setText("Third paragraph"); + + + + FileOutputStream out = new FileOutputStream("step2.pptx"); + ppt.write(out); + out.close(); + + } +} diff --git a/src/ooxml/java/org/apache/poi/xslf/extractor/XSLFPowerPointExtractor.java b/src/ooxml/java/org/apache/poi/xslf/extractor/XSLFPowerPointExtractor.java index 812c7ad0e6..0f9a26fe21 100644 --- a/src/ooxml/java/org/apache/poi/xslf/extractor/XSLFPowerPointExtractor.java +++ b/src/ooxml/java/org/apache/poi/xslf/extractor/XSLFPowerPointExtractor.java @@ -128,7 +128,7 @@ public class XSLFPowerPointExtractor extends POIXMLTextExtractor { XSLFNotes notes = slide.getNotes(); XSLFComments comments = slide.getComments(); XSLFSlideLayout layout = slide.getSlideLayout(); - XSLFSlideMaster master = slide.getMasterSheet(); + XSLFSlideMaster master = layout.getSlideMaster(); // TODO Do the slide's name // (Stored in docProps/app.xml) diff --git a/src/ooxml/java/org/apache/poi/xslf/model/geom/ArcToCommand.java b/src/ooxml/java/org/apache/poi/xslf/model/geom/ArcToCommand.java index 9b2e1bcf31..b1ea0defc8 100644 --- a/src/ooxml/java/org/apache/poi/xslf/model/geom/ArcToCommand.java +++ b/src/ooxml/java/org/apache/poi/xslf/model/geom/ArcToCommand.java @@ -57,15 +57,11 @@ public class ArcToCommand implements PathCommand { double x0 = pt.getX() - rx - rx * Math.cos(Math.toRadians(start)); double y0 = pt.getY() - ry - ry * Math.sin(Math.toRadians(start)); - if(start == 180 && extent == 180) { - x0 -= rx*2; //YK: TODO revisit the code and get rid of this hack - } - Arc2D arc = new Arc2D.Double( x0, y0, 2 * rx, 2 * ry, - -start, -extent, // negate angles because DrawingML rotates counter-clockwise + -start, -extent, Arc2D.OPEN); path.append(arc, true); } diff --git a/src/ooxml/java/org/apache/poi/xslf/model/geom/Context.java b/src/ooxml/java/org/apache/poi/xslf/model/geom/Context.java index 230524cfde..ea86c51002 100644 --- a/src/ooxml/java/org/apache/poi/xslf/model/geom/Context.java +++ b/src/ooxml/java/org/apache/poi/xslf/model/geom/Context.java @@ -29,17 +29,19 @@ import java.util.Map; * @author Yegor Kozlov */ public class Context { - Map<String, Double> _ctx = new HashMap<String, Double>(); - IAdjustableShape _props; - - public Context(CustomGeometry geom, IAdjustableShape props){ + final Map<String, Double> _ctx = new HashMap<String, Double>(); + final IAdjustableShape _props; + final Rectangle2D _anchor; + + public Context(CustomGeometry geom, Rectangle2D anchor, IAdjustableShape props){ _props = props; + _anchor = anchor; for(Guide gd : geom.adjusts) evaluate(gd); for(Guide gd : geom.guides) evaluate(gd); } public Rectangle2D getShapeAnchor(){ - return _props.getAnchor(); + return _anchor; } public Guide getAdjustValue(String name){ diff --git a/src/ooxml/java/org/apache/poi/xslf/model/geom/CustomGeometry.java b/src/ooxml/java/org/apache/poi/xslf/model/geom/CustomGeometry.java index 7507f55b21..6745ccd513 100644 --- a/src/ooxml/java/org/apache/poi/xslf/model/geom/CustomGeometry.java +++ b/src/ooxml/java/org/apache/poi/xslf/model/geom/CustomGeometry.java @@ -24,6 +24,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.*; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.awt.geom.Rectangle2D; /** * Definition of a custom geometric shape @@ -34,8 +35,9 @@ public class CustomGeometry implements Iterable<Path>{ List<Guide> adjusts = new ArrayList<Guide>(); List<Guide> guides = new ArrayList<Guide>(); List<Path> paths = new ArrayList<Path>(); + Path textBounds; - public CustomGeometry(CTCustomGeometry2D geom){ + public CustomGeometry(CTCustomGeometry2D geom) { CTGeomGuideList avLst = geom.getAvLst(); if(avLst != null) for(CTGeomGuide gd : avLst.getGdList()){ adjusts.add(new AdjustValue(gd)); @@ -50,6 +52,21 @@ public class CustomGeometry implements Iterable<Path>{ if(pathLst != null) for(CTPath2D spPath : pathLst.getPathList()){ paths.add(new Path(spPath)); } + + if(geom.isSetRect()) { + CTGeomRect rect = geom.getRect(); + textBounds = new Path(); + textBounds.addCommand( + new MoveToCommand(rect.getL().toString(), rect.getT().toString())); + textBounds.addCommand( + new LineToCommand(rect.getR().toString(), rect.getT().toString())); + textBounds.addCommand( + new LineToCommand(rect.getR().toString(), rect.getB().toString())); + textBounds.addCommand( + new LineToCommand(rect.getL().toString(), rect.getB().toString())); + textBounds.addCommand( + new ClosePathCommand()); + } } @@ -58,4 +75,7 @@ public class CustomGeometry implements Iterable<Path>{ return paths.iterator(); } + public Path getTextBounds(){ + return textBounds; + } } diff --git a/src/ooxml/java/org/apache/poi/xslf/model/geom/IAdjustableShape.java b/src/ooxml/java/org/apache/poi/xslf/model/geom/IAdjustableShape.java index 174af3d34c..44f5a562f7 100644 --- a/src/ooxml/java/org/apache/poi/xslf/model/geom/IAdjustableShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/model/geom/IAdjustableShape.java @@ -19,7 +19,6 @@ package org.apache.poi.xslf.model.geom; -import java.awt.geom.Rectangle2D; /** * A bridge to the consumer application. @@ -31,12 +30,6 @@ import java.awt.geom.Rectangle2D; public interface IAdjustableShape { /** * - * @return bounds of the shape - */ - Rectangle2D getAnchor(); - - /** - * * @param name name of a adjust value, e.g. adj1 * @return adjust guide defined in the shape or null */ diff --git a/src/ooxml/java/org/apache/poi/xslf/model/geom/LineToCommand.java b/src/ooxml/java/org/apache/poi/xslf/model/geom/LineToCommand.java index 3df2e499e1..5142dd234a 100644 --- a/src/ooxml/java/org/apache/poi/xslf/model/geom/LineToCommand.java +++ b/src/ooxml/java/org/apache/poi/xslf/model/geom/LineToCommand.java @@ -36,6 +36,11 @@ public class LineToCommand implements PathCommand { arg2 = pt.getY().toString(); } + LineToCommand(String s1, String s2){ + arg1 = s1; + arg2 = s2; + } + public void execute(GeneralPath path, Context ctx){ double x = ctx.getValue(arg1); double y = ctx.getValue(arg2); diff --git a/src/ooxml/java/org/apache/poi/xslf/model/geom/MoveToCommand.java b/src/ooxml/java/org/apache/poi/xslf/model/geom/MoveToCommand.java index a5a4b5ce69..9d9575ace1 100644 --- a/src/ooxml/java/org/apache/poi/xslf/model/geom/MoveToCommand.java +++ b/src/ooxml/java/org/apache/poi/xslf/model/geom/MoveToCommand.java @@ -36,6 +36,11 @@ public class MoveToCommand implements PathCommand { arg2 = pt.getY().toString(); } + MoveToCommand(String s1, String s2){ + arg1 = s1; + arg2 = s2; + } + public void execute(GeneralPath path, Context ctx){ double x = ctx.getValue(arg1); double y = ctx.getValue(arg2); diff --git a/src/ooxml/java/org/apache/poi/xslf/model/geom/Outline.java b/src/ooxml/java/org/apache/poi/xslf/model/geom/Outline.java new file mode 100644 index 0000000000..dbf9f1f9c9 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xslf/model/geom/Outline.java @@ -0,0 +1,45 @@ +/* + * ==================================================================== + * 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.model.geom; + +import java.awt.Shape; + +/** +* Date: 11/6/11 +* +* @author Yegor Kozlov +*/ +public class Outline { + private Shape shape; + private Path path; + + public Outline(Shape shape, Path path){ + this.shape = shape; + this.path = path; + } + + public Path getPath(){ + return path; + } + + public Shape getOutline(){ + return shape; + } +} diff --git a/src/ooxml/java/org/apache/poi/xslf/model/geom/Path.java b/src/ooxml/java/org/apache/poi/xslf/model/geom/Path.java index 9d5e9f71d3..94ab37662c 100644 --- a/src/ooxml/java/org/apache/poi/xslf/model/geom/Path.java +++ b/src/ooxml/java/org/apache/poi/xslf/model/geom/Path.java @@ -39,11 +39,26 @@ import java.util.List; public class Path { private final List<PathCommand> commands; boolean _fill, _stroke; + long _w, _h; + + public Path(){ + this(true, true); + } + + public Path(boolean fill, boolean stroke){ + commands = new ArrayList<PathCommand>(); + _w = -1; + _h = -1; + _fill = fill; + _stroke = stroke; + } public Path(CTPath2D spPath){ _fill = spPath.getFill() != STPathFillMode.NONE; _stroke = spPath.getStroke(); - + _w = spPath.isSetW() ? spPath.getW() : -1; + _h = spPath.isSetH() ? spPath.getH() : -1; + commands = new ArrayList<PathCommand>(); for(XmlObject ch : spPath.selectPath("*")){ if(ch instanceof CTPath2DMoveTo){ @@ -74,6 +89,13 @@ public class Path { } } + public void addCommand(PathCommand cmd){ + commands.add(cmd); + } + + /** + * Convert the internal represenation to java.awt.GeneralPath + */ public GeneralPath getPath(Context ctx) { GeneralPath path = new GeneralPath(); for(PathCommand cmd : commands) @@ -88,4 +110,12 @@ public class Path { public boolean isFilled(){ return _fill; } + + public long getW(){ + return _w; + } + + public long getH(){ + return _h; + } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/Placeholder.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/Placeholder.java index a96e88778d..087b28c054 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/Placeholder.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/Placeholder.java @@ -30,7 +30,7 @@ public enum Placeholder { SLIDE_NUMBER,
FOOTER,
HEADER,
- OBJECT,
+ CONTENT,
CHART,
TABLE,
CLIP_ART,
@@ -38,5 +38,4 @@ public enum Placeholder { MEDIA,
SLIDE_IMAGE,
PICTURE
-
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java index 393ba333e6..bbd0de9ea6 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java @@ -1,11 +1,563 @@ +/*
+ * ====================================================================
+ * 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.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.Units;
+import org.apache.poi.xslf.model.PropertyFetcher;
+import org.apache.poi.xslf.model.geom.Context;
+import org.apache.poi.xslf.model.geom.CustomGeometry;
+import org.apache.poi.xslf.model.geom.Guide;
+import org.apache.poi.xslf.model.geom.IAdjustableShape;
+import org.apache.poi.xslf.model.geom.Outline;
+import org.apache.poi.xslf.model.geom.Path;
+import org.apache.xmlbeans.XmlObject;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomGuide;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientFillProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTGradientStop;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTNoFillProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTPathShadeProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference;
+import org.openxmlformats.schemas.drawingml.x2006.main.STPathShadeType;
+
+import java.awt.AlphaComposite;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.GradientPaint;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.TexturePaint;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+
/**
- * Created by IntelliJ IDEA.
- * User: yegor
- * Date: Oct 27, 2011
- * Time: 4:50:08 PM
- * To change this template use File | Settings | File Templates.
+ * Encapsulates logic to translate DrawingML objects to Java2D
*/
+@Internal
class RenderableShape {
+ public final static Color NO_PAINT = new Color(0xFF, 0xFF, 0xFF, 0);
+
+ private XSLFSimpleShape _shape;
+
+ public RenderableShape(XSLFSimpleShape shape){
+ _shape = shape;
+ }
+
+ /**
+ * Convert shape fill into java.awt.Paint. The result is either Color or
+ * TexturePaint or GradientPaint or null
+ *
+ * @param graphics the target graphics
+ * @param obj the xml to read. Must contain elements from the EG_ColorChoice group:
+ * <code>
+ * a:scrgbClr RGB Color Model - Percentage Variant
+ * a:srgbClr RGB Color Model - Hex Variant
+ * a:hslClr Hue, Saturation, Luminance Color Model
+ * a:sysClr System Color
+ * a:schemeClr Scheme Color
+ * a:prstClr Preset Color
+ * </code>
+ *
+ * @param phClr context color
+ * @param parentPart the parent package part. Any external references (images, etc.) are resolved relative to it.
+ *
+ * @return the applied Paint or null if none was applied
+ */
+ public Paint selectPaint(Graphics2D graphics, XmlObject obj, CTSchemeColor phClr, PackagePart parentPart) {
+ XSLFTheme theme = _shape.getSheet().getTheme();
+ Rectangle2D anchor = _shape.getAnchor();
+
+ Paint paint = null;
+ if (obj instanceof CTNoFillProperties) {
+ paint = NO_PAINT;
+
+ }
+ else if (obj instanceof CTSolidColorFillProperties) {
+ CTSolidColorFillProperties solidFill = (CTSolidColorFillProperties) obj;
+ XSLFColor c = new XSLFColor(solidFill, theme, phClr);
+ paint = c.getColor();
+ }
+ else if (obj instanceof CTBlipFillProperties) {
+ CTBlipFillProperties blipFill = (CTBlipFillProperties)obj;
+ paint = createTexturePaint(blipFill, graphics, parentPart);
+ }
+ else if (obj instanceof CTGradientFillProperties) {
+ CTGradientFillProperties gradFill = (CTGradientFillProperties) obj;
+ if (gradFill.isSetLin()) {
+ paint = createLinearGradientPaint(gradFill, anchor, theme, phClr);
+ } else if (gradFill.isSetPath()){
+ CTPathShadeProperties ps = gradFill.getPath();
+ if(ps.getPath() == STPathShadeType.CIRCLE){
+ paint = createRadialGradientPaint(gradFill, anchor, theme, phClr);
+ }
+ }
+ }
+
+ return paint;
+ }
+
+ private Paint createTexturePaint(CTBlipFillProperties blipFill, Graphics2D graphics,
+ PackagePart parentPart){
+ Paint paint = null;
+ CTBlip blip = blipFill.getBlip();
+ String blipId = blip.getEmbed();
+ PackageRelationship rel = parentPart.getRelationship(blipId);
+ if (rel != null) {
+ XSLFImageRendener renderer = null;
+ if (graphics != null)
+ renderer = (XSLFImageRendener) graphics.getRenderingHint(XSLFRenderingHint.IMAGE_RENDERER);
+ if (renderer == null) renderer = new XSLFImageRendener();
+
+ try {
+ BufferedImage img = renderer.readImage(parentPart.getRelatedPart(rel));
+ if (blip.sizeOfAlphaModFixArray() > 0) {
+ float alpha = blip.getAlphaModFixArray(0).getAmt() / 100000.f;
+ AlphaComposite ac = AlphaComposite.getInstance(
+ AlphaComposite.SRC_OVER, alpha);
+ if (graphics != null) graphics.setComposite(ac);
+ }
+
+ if(img != null) {
+ paint = new TexturePaint(
+ img, new Rectangle2D.Double(0, 0, img.getWidth(), img.getHeight()));
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return paint;
+ }
+
+ private static Paint createLinearGradientPaint(
+ CTGradientFillProperties gradFill, Rectangle2D anchor,
+ XSLFTheme theme, CTSchemeColor phClr) {
+ double angle = gradFill.getLin().getAng() / 60000;
+ CTGradientStop[] gs = gradFill.getGsLst().getGsArray();
+
+ Arrays.sort(gs, new Comparator<CTGradientStop>() {
+ public int compare(CTGradientStop o1, CTGradientStop o2) {
+ Integer pos1 = o1.getPos();
+ Integer pos2 = o2.getPos();
+ return pos1.compareTo(pos2);
+ }
+ });
+
+ Color[] colors = new Color[gs.length];
+ float[] fractions = new float[gs.length];
+
+ AffineTransform at = AffineTransform.getRotateInstance(
+ Math.toRadians(angle),
+ anchor.getX() + anchor.getWidth() / 2,
+ anchor.getY() + anchor.getHeight() / 2);
+
+ double diagonal = Math.sqrt(anchor.getHeight() * anchor.getHeight() + anchor.getWidth() * anchor.getWidth());
+ Point2D p1 = new Point2D.Double(anchor.getX() + anchor.getWidth() / 2 - diagonal / 2,
+ anchor.getY() + anchor.getHeight() / 2);
+ p1 = at.transform(p1, null);
+
+ Point2D p2 = new Point2D.Double(anchor.getX() + anchor.getWidth(), anchor.getY() + anchor.getHeight() / 2);
+ p2 = at.transform(p2, null);
+
+ snapToAnchor(p1, anchor);
+ snapToAnchor(p2, anchor);
+
+ for (int i = 0; i < gs.length; i++) {
+ CTGradientStop stop = gs[i];
+ colors[i] = new XSLFColor(stop, theme, phClr).getColor();
+ fractions[i] = stop.getPos() / 100000.f;
+ }
+
+ // Trick to return GradientPaint on JDK 1.5 and LinearGradientPaint on JDK 1.6+
+ Paint paint;
+ try {
+ Class clz = Class.forName("java.awt.LinearGradientPaint");
+ Constructor c =
+ clz.getConstructor(Point2D.class, Point2D.class, float[].class, Color[].class);
+ paint = (Paint) c.newInstance(p1, p2, fractions, colors);
+ } catch (ClassNotFoundException e) {
+ paint = new GradientPaint(p1, colors[0], p2, colors[colors.length - 1]);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return paint;
+ }
+
+ private static Paint createRadialGradientPaint(
+ CTGradientFillProperties gradFill, Rectangle2D anchor,
+ XSLFTheme theme, CTSchemeColor phClr) {
+ CTGradientStop[] gs = gradFill.getGsLst().getGsArray();
+
+ Point2D pCenter = new Point2D.Double(anchor.getX() + anchor.getWidth()/2,
+ anchor.getY() + anchor.getHeight()/2);
+
+ float radius = (float)Math.max(anchor.getWidth(), anchor.getHeight());
+
+ Arrays.sort(gs, new Comparator<CTGradientStop>() {
+ public int compare(CTGradientStop o1, CTGradientStop o2) {
+ Integer pos1 = o1.getPos();
+ Integer pos2 = o2.getPos();
+ return pos1.compareTo(pos2);
+ }
+ });
+
+ Color[] colors = new Color[gs.length];
+ float[] fractions = new float[gs.length];
+
+
+ for (int i = 0; i < gs.length; i++) {
+ CTGradientStop stop = gs[i];
+ colors[i] = new XSLFColor(stop, theme, phClr).getColor();
+ fractions[i] = stop.getPos() / 100000.f;
+ }
+
+ // Trick to return GradientPaint on JDK 1.5 and RadialGradientPaint on JDK 1.6+
+ Paint paint;
+ try {
+ Class clz = Class.forName("java.awt.RadialGradientPaint");
+ Constructor c =
+ clz.getConstructor(Point2D.class, float.class,
+ float[].class, Color[].class);
+ paint = (Paint) c.newInstance(pCenter, radius, fractions, colors);
+ } catch (ClassNotFoundException e) {
+ // the result on JDK 1.5 is incorrect, but it is better than nothing
+ paint = new GradientPaint(
+ new Point2D.Double(anchor.getX(), anchor.getY()),
+ colors[0], pCenter, colors[colors.length - 1]);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return paint;
+ }
+
+ private static void snapToAnchor(Point2D p, Rectangle2D anchor) {
+ if (p.getX() < anchor.getX()) {
+ p.setLocation(anchor.getX(), p.getY());
+ } else if (p.getX() > (anchor.getX() + anchor.getWidth())) {
+ p.setLocation(anchor.getX() + anchor.getWidth(), p.getY());
+ }
+
+ if (p.getY() < anchor.getY()) {
+ p.setLocation(p.getX(), anchor.getY());
+ } else if (p.getY() > (anchor.getY() + anchor.getHeight())) {
+ p.setLocation(p.getX(), anchor.getY() + anchor.getHeight());
+ }
+ }
+
+
+ @SuppressWarnings("deprecation") // getXYZArray() array accessors are deprecated
+ Paint getPaint(Graphics2D graphics, XmlObject spPr, CTSchemeColor phClr) {
+
+ Paint paint = null;
+ for (XmlObject obj : spPr.selectPath("*")) {
+ paint = selectPaint(graphics, obj, phClr, _shape.getSheet().getPackagePart());
+ if(paint != null) break;
+ }
+ return paint == NO_PAINT ? null : paint;
+ }
+
+
+ /**
+ * fetch shape fill as a java.awt.Paint
+ *
+ * @return either Color or GradientPaint or TexturePaint or null
+ */
+ Paint getFillPaint(final Graphics2D graphics) {
+ PropertyFetcher<Paint> fetcher = new PropertyFetcher<Paint>() {
+ public boolean fetch(XSLFSimpleShape shape) {
+ CTShapeProperties spPr = shape.getSpPr();
+ if (spPr.isSetNoFill()) {
+ setValue(RenderableShape.NO_PAINT); // use it as 'nofill' value
+ return true;
+ }
+ Paint paint = getPaint(graphics, spPr, null);
+ if (paint != null) {
+ setValue(paint);
+ return true;
+ }
+ return false;
+ }
+ };
+ _shape.fetchShapeProperty(fetcher);
+
+ Paint paint = fetcher.getValue();
+ if (paint == null) {
+ // fill color was not found, check if it is defined in the theme
+ CTShapeStyle style = _shape.getSpStyle();
+ if (style != null) {
+ // get a reference to a fill style within the style matrix.
+ CTStyleMatrixReference fillRef = style.getFillRef();
+ // The idx attribute refers to the index of a fill style or
+ // background fill style within the presentation's style matrix, defined by the fmtScheme element.
+ // value of 0 or 1000 indicates no background,
+ // values 1-999 refer to the index of a fill style within the fillStyleLst element
+ // values 1001 and above refer to the index of a background fill style within the bgFillStyleLst element.
+ int idx = (int)fillRef.getIdx();
+ CTSchemeColor phClr = fillRef.getSchemeClr();
+ XSLFSheet sheet = _shape.getSheet();
+ XSLFTheme theme = sheet.getTheme();
+ XmlObject fillProps = null;
+ if(idx >= 1 && idx <= 999){
+ fillProps = theme.getXmlObject().
+ getThemeElements().getFmtScheme().getFillStyleLst().selectPath("*")[idx - 1];
+ } else if (idx >= 1001 ){
+ fillProps = theme.getXmlObject().
+ getThemeElements().getFmtScheme().getBgFillStyleLst().selectPath("*")[idx - 1001];
+ }
+ if(fillProps != null) {
+ paint = selectPaint(graphics, fillProps, phClr, sheet.getPackagePart());
+ }
+ }
+ }
+ return paint == RenderableShape.NO_PAINT ? null : paint;
+ }
+
+ public Paint getLinePaint(final Graphics2D graphics) {
+ PropertyFetcher<Paint> fetcher = new PropertyFetcher<Paint>() {
+ public boolean fetch(XSLFSimpleShape shape) {
+ CTLineProperties spPr = shape.getSpPr().getLn();
+ if (spPr != null) {
+ if (spPr.isSetNoFill()) {
+ setValue(NO_PAINT); // use it as 'nofill' value
+ return true;
+ }
+ Paint paint = getPaint(graphics, spPr, null);
+ if (paint != null) {
+ setValue(paint);
+ return true;
+ }
+ }
+ return false;
+
+ }
+ };
+ _shape.fetchShapeProperty(fetcher);
+
+ Paint paint = fetcher.getValue();
+ if (paint == null) {
+ // line color was not found, check if it is defined in the theme
+ CTShapeStyle style = _shape.getSpStyle();
+ if (style != null) {
+ // get a reference to a line style within the style matrix.
+ CTStyleMatrixReference lnRef = style.getLnRef();
+ int idx = (int)lnRef.getIdx();
+ CTSchemeColor phClr = lnRef.getSchemeClr();
+ if(idx > 0){
+ XSLFTheme theme = _shape.getSheet().getTheme();
+ XmlObject lnProps = theme.getXmlObject().
+ getThemeElements().getFmtScheme().getLnStyleLst().selectPath("*")[idx - 1];
+ paint = getPaint(graphics, lnProps, phClr);
+ }
+ }
+ }
+
+ return paint == NO_PAINT ? null : paint;
+ }
+
+ /**
+ * convert PPT dash into java.awt.BasicStroke
+ *
+ * The mapping is derived empirically on PowerPoint 2010
+ */
+ private static float[] getDashPattern(LineDash lineDash, float lineWidth) {
+ float[] dash = null;
+ switch (lineDash) {
+ case SYS_DOT:
+ dash = new float[]{lineWidth, lineWidth};
+ break;
+ case SYS_DASH:
+ dash = new float[]{2 * lineWidth, 2 * lineWidth};
+ break;
+ case DASH:
+ dash = new float[]{3 * lineWidth, 4 * lineWidth};
+ break;
+ case DASH_DOT:
+ dash = new float[]{4 * lineWidth, 3 * lineWidth, lineWidth,
+ 3 * lineWidth};
+ break;
+ case LG_DASH:
+ dash = new float[]{8 * lineWidth, 3 * lineWidth};
+ break;
+ case LG_DASH_DOT:
+ dash = new float[]{8 * lineWidth, 3 * lineWidth, lineWidth,
+ 3 * lineWidth};
+ break;
+ case LG_DASH_DOT_DOT:
+ dash = new float[]{8 * lineWidth, 3 * lineWidth, lineWidth,
+ 3 * lineWidth, lineWidth, 3 * lineWidth};
+ break;
+ }
+ return dash;
+ }
+
+
+ public Stroke applyStroke(Graphics2D graphics) {
+
+ float lineWidth = (float) _shape.getLineWidth();
+ LineDash lineDash = _shape.getLineDash();
+ float[] dash = null;
+ float dash_phase = 0;
+ if (lineDash != null) {
+ dash = getDashPattern(lineDash, lineWidth);
+ }
+
+ int cap = BasicStroke.CAP_BUTT;
+ LineCap lineCap = _shape.getLineCap();
+ if (lineCap != null) {
+ switch (lineCap) {
+ case ROUND:
+ cap = BasicStroke.CAP_ROUND;
+ break;
+ case SQUARE:
+ cap = BasicStroke.CAP_SQUARE;
+ break;
+ default:
+ cap = BasicStroke.CAP_BUTT;
+ break;
+ }
+ }
+
+ int meter = BasicStroke.JOIN_ROUND;
+
+ Stroke stroke = new BasicStroke(lineWidth, cap, meter, Math.max(1, lineWidth), dash,
+ dash_phase);
+ graphics.setStroke(stroke);
+ return stroke;
+ }
+
+ public void render(Graphics2D graphics){
+ Collection<Outline> elems = computeOutlines();
+
+ // shadow
+ XSLFShadow shadow = _shape.getShadow();
+
+ // first fill
+ Paint fill = getFillPaint(graphics);
+ if(fill != null) for(Outline o : elems){
+ if(o.getPath().isFilled()){
+ if(shadow != null) shadow.fill(graphics, o.getOutline());
+
+ graphics.setPaint(fill);
+ graphics.fill(o.getOutline());
+ }
+ }
+
+ // then draw any content within this shape (text, image, etc.)
+ _shape.drawContent(graphics);
+
+ // then stroke the shape outline
+ Paint line = getLinePaint(graphics);
+ if(line != null) for(Outline o : elems){
+ if(o.getPath().isStroked()){
+ applyStroke(graphics); // the stroke applies both to the shadow and the shape
+
+ if(shadow != null) shadow.draw(graphics, o.getOutline());
+
+ graphics.setPaint(line);
+ graphics.draw(o.getOutline());
+ }
+ }
+ }
+
+ private Collection<Outline> computeOutlines() {
+ CustomGeometry geom = _shape.getGeometry();
+
+ Collection<Outline> lst = new ArrayList<Outline>();
+
+ Rectangle2D anchor = _shape.getAnchor();
+ for (Path p : geom) {
+
+ double w = p.getW() == -1 ? anchor.getWidth() * Units.EMU_PER_POINT : p.getW();
+ double h = p.getH() == -1 ? anchor.getHeight() * Units.EMU_PER_POINT : p.getH();
+
+ // the guides in the shape definitions are all defined relative to each other,
+ // so we build the path starting from (0,0).
+ final Rectangle2D pathAnchor = new Rectangle2D.Double(
+ 0,
+ 0,
+ w,
+ h
+ );
+
+ Context ctx = new Context(geom, pathAnchor, new IAdjustableShape() {
+
+ public Guide getAdjustValue(String name) {
+ CTPresetGeometry2D prst = _shape.getSpPr().getPrstGeom();
+ if (prst.isSetAvLst()) {
+ for (CTGeomGuide g : prst.getAvLst().getGdList()) {
+ if (g.getName().equals(name)) {
+ return new Guide(g);
+ }
+ }
+ }
+ return null;
+ }
+ }) ;
+
+ Shape gp = p.getPath(ctx);
+
+ // translate the result to the canvas coordinates in points
+ AffineTransform at = new AffineTransform();
+ at.translate(anchor.getX(), anchor.getY());
+
+ double scaleX, scaleY;
+ if (p.getW() != -1) {
+ scaleX = anchor.getWidth() / p.getW();
+ } else {
+ scaleX = 1.0 / Units.EMU_PER_POINT;
+ }
+ if (p.getH() != -1) {
+ scaleY = anchor.getHeight() / p.getH();
+ } else {
+ scaleY = 1.0 / Units.EMU_PER_POINT;
+ }
+
+ at.scale(scaleX, scaleY);
+
+ Shape canvasShape = at.createTransformedShape(gp);
+
+ lst.add(new Outline(canvasShape, p));
+ }
+
+ // add any shape-specific stuff here (line decorations, etc.)
+ lst.addAll(_shape.getCustomOutlines());
+ return lst;
+ }
+
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/SlideLayout.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/SlideLayout.java new file mode 100644 index 0000000000..729057b13b --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/SlideLayout.java @@ -0,0 +1,99 @@ +/* + * ==================================================================== + * 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; + +/** + * Date: 11/5/11 + * + * @author Yegor Kozlov + */ +public enum SlideLayout { + /** + * Title layout with centered title and subtitle placeholders + */ + TITLE, + /** + * Title and text + */ + TEXT, + + TWO_COL_TX, + TBL, + TEXT_AND_CHART, + + /** + * Title, chart on left and text on right + */ + CHART_AND_TEXT, + + DGM, + + /** + * Title and chart + */ + CHART, + + TX_AND_CLIP_ART, + /** + * Title, clipart on left, text on right + */ + CLIP_ART_AND_TEXT, + + /** + * Title only + */ + TITLE_ONLY, + + /** + * Blank + */ + BLANK, + + TX_AND_OBJ, + OBJ_AND_TX, + OBJ_ONLY, + /** + * title and content + */ + TITLE_AND_CONTENT, + TX_AND_MEDIA, + MEDIA_AND_TX, + OBJ_OVER_TX, + TX_OVER_OBJ, + TX_AND_TWO_OBJ, + TWO_OBJ_AND_TX, + TWO_OBJ_OVER_TX, + FOUR_OBJ, + VERT_TX, + CLIP_ART_AND_VERT_TX, + VERT_TITLE_AND_TX, + VERT_TITLE_AND_TX_OVER_CHART, + TWO_OBJ, + OBJ_AND_TWO_OBJ, + TWO_OBJ_AND_OBJ, + CUST, + /** + * Section Header + */ + SECTION_HEADER, + TWO_TX_TWO_OBJ, + OBJ_TX, + PIC_TX, +} diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/TextAlign.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/TextAlign.java index c712dbacef..2570e6cfc9 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/TextAlign.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/TextAlign.java @@ -43,5 +43,8 @@ public enum TextAlign { * is smart in the sense that it will not justify sentences
* which are short
*/
- JUSTIFY
+ JUSTIFY,
+ JUSTIFY_LOW,
+ DIST,
+ THAI_DIST
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/TextCap.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/TextCap.java new file mode 100644 index 0000000000..2e998efcf0 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/TextCap.java @@ -0,0 +1,33 @@ +/*
+ * ====================================================================
+ * 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;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: yegor
+ * Date: 11/3/11
+ * Time: 5:07 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public enum TextCap {
+ NONE,
+ SMALL,
+ ALL
+}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java index 8f25beeede..efec667a93 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java @@ -40,7 +40,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdListEntry; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideSize; import org.openxmlformats.schemas.presentationml.x2006.main.PresentationDocument; -import java.awt.*; +import java.awt.Dimension; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -190,7 +190,13 @@ public class XMLSlideShow extends POIXMLDocument { return Collections.unmodifiableList(_pictures); } - public XSLFSlide createSlide() { + /** + * Create a slide and initialize it from the specified layout. + * + * @param layout + * @return created slide + */ + public XSLFSlide createSlide(XSLFSlideLayout layout) { int slideNumber = 256, cnt = 1; CTSlideIdList slideList; if (!_presentation.isSetSldIdLst()) slideList = _presentation.addNewSldIdLst(); @@ -209,12 +215,7 @@ public class XMLSlideShow extends POIXMLDocument { slideId.setId(slideNumber); slideId.setId2(slide.getPackageRelationship().getId()); - String masterId = _presentation.getSldMasterIdLst().getSldMasterIdArray(0).getId2(); - XSLFSlideMaster master = _masters.get(masterId); - - XSLFSlideLayout layout = master.getLayout("blank"); - if(layout == null) throw new IllegalArgumentException("Blank layout was not found"); - + layout.copyLayout(slide); slide.addRelation(layout.getPackageRelationship().getId(), layout); PackagePartName ppName = layout.getPackagePart().getPartName(); @@ -224,7 +225,20 @@ public class XMLSlideShow extends POIXMLDocument { _slides.add(slide); return slide; } - + + /** + * Create a blank slide. + */ + public XSLFSlide createSlide() { + String masterId = _presentation.getSldMasterIdLst().getSldMasterIdArray(0).getId2(); + XSLFSlideMaster master = _masters.get(masterId); + + XSLFSlideLayout layout = master.getLayout(SlideLayout.BLANK); + if(layout == null) throw new IllegalArgumentException("Blank layout was not found"); + + return createSlide(layout); + } + /** * Return the Notes Master, if there is one. * (May not be present if no notes exist) diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java index 2b8a1e08b8..5f4e838a0c 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java @@ -28,16 +28,14 @@ import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType; import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
import org.openxmlformats.schemas.presentationml.x2006.main.CTShapeNonVisual;
-import java.util.regex.Pattern;
/**
- * Represents a preset geometric shape.
+ * Represents a shape with a preset geometry.
*
* @author Yegor Kozlov
*/
@Beta
public class XSLFAutoShape extends XSLFTextShape {
- private static final Pattern adjPtrn = Pattern.compile("val\\s+(\\d+)");
/*package*/ XSLFAutoShape(CTShape shape, XSLFSheet sheet) {
super(shape, sheet);
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFBackground.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFBackground.java index cd82eabead..7d8cb061ef 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFBackground.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFBackground.java @@ -17,16 +17,16 @@ package org.apache.poi.xslf.usermodel;
-import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground;
-import org.openxmlformats.schemas.presentationml.x2006.main.CTBackgroundProperties;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrix;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTBackgroundFillStyleList;
import org.apache.xmlbeans.XmlObject;
-import org.apache.xmlbeans.XmlCursor;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTBackgroundFillStyleList;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference;
+import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground;
-import javax.xml.namespace.QName;
-import java.awt.*;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Paint;
import java.awt.geom.Rectangle2D;
/**
@@ -49,31 +49,46 @@ public class XSLFBackground extends XSLFSimpleShape { public void draw(Graphics2D graphics) {
Rectangle2D anchor = getAnchor();
- XmlObject spPr = null;
+ Paint fill = getPaint(graphics);
+ if(fill != null) {
+ graphics.setPaint(fill);
+ graphics.fill(anchor);
+ }
+ }
+
+ /**
+ * @return the Paint object to fill
+ */
+ Paint getPaint(Graphics2D graphics){
+ RenderableShape rShape = new RenderableShape(this);
+
+ Paint fill = null;
CTBackground bg = (CTBackground)getXmlObject();
if(bg.isSetBgPr()){
- spPr = bg.getBgPr();
+ XmlObject spPr = bg.getBgPr();
+ fill = rShape.getPaint(graphics, spPr, null);
} else if (bg.isSetBgRef()){
CTStyleMatrixReference bgRef= bg.getBgRef();
- int idx = (int)bgRef.getIdx() - 1000;
+ CTSchemeColor phClr = bgRef.getSchemeClr();
+
+ int idx = (int)bgRef.getIdx() - 1001;
XSLFTheme theme = getSheet().getTheme();
CTBackgroundFillStyleList bgStyles =
theme.getXmlObject().getThemeElements().getFmtScheme().getBgFillStyleLst();
- // TODO pass this to getPaint
XmlObject bgStyle = bgStyles.selectPath("*")[idx];
+ fill = rShape.selectPaint(graphics, bgStyle, phClr, theme.getPackagePart());
}
- if(spPr == null){
- return;
- }
+ return fill;
+ }
- Paint fill = getPaint(graphics, spPr);
- if(fill != null) {
- graphics.setPaint(fill);
- graphics.fill(anchor);
+ @Override
+ public Color getFillColor(){
+ Paint p = getPaint(null);
+ if(p instanceof Color){
+ return (Color)p;
}
+ return null;
}
-
-
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java index 8ca8dbde9f..c0f4762c7e 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java @@ -30,7 +30,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTSystemColor;
import org.w3c.dom.Node;
-import java.awt.*;
+import java.awt.Color;
import java.util.HashMap;
import java.util.Map;
@@ -40,12 +40,15 @@ import java.util.Map; * @author Yegor Kozlov
*/
@Beta
+@Internal
public class XSLFColor {
private XmlObject _xmlObject;
private Color _color;
+ private CTSchemeColor _phClr;
- XSLFColor(XmlObject obj, XSLFTheme theme) {
+ public XSLFColor(XmlObject obj, XSLFTheme theme, CTSchemeColor phClr) {
_xmlObject = obj;
+ _phClr = phClr;
_color = toColor(obj, theme);
}
@@ -94,7 +97,7 @@ public class XSLFColor { return result;
}
- static Color toColor(XmlObject obj, XSLFTheme theme) {
+ Color toColor(XmlObject obj, XSLFTheme theme) {
Color color = null;
for (XmlObject ch : obj.selectPath("*")) {
if (ch instanceof CTHslColor) {
@@ -102,7 +105,8 @@ public class XSLFColor { int h = hsl.getHue2();
int s = hsl.getSat2();
int l = hsl.getLum2();
- // is it correct ?
+ // This conversion is not correct and differs from PowerPoint.
+ // TODO: Revisit and improve.
color = Color.getHSBColor(h / 60000f, s / 100000f, l / 100000f);
} else if (ch instanceof CTPresetColor) {
CTPresetColor prst = (CTPresetColor)ch;
@@ -111,12 +115,13 @@ public class XSLFColor { } else if (ch instanceof CTSchemeColor) {
CTSchemeColor schemeColor = (CTSchemeColor)ch;
String colorRef = schemeColor.getVal().toString();
+ if(_phClr != null) {
+ // context color overrides the theme
+ colorRef = _phClr.getVal().toString();
+ }
// find referenced CTColor in the theme and convert it to java.awt.Color via a recursive call
CTColor ctColor = theme.getCTColor(colorRef);
if(ctColor != null) color = toColor(ctColor, null);
- else {
- color = Color.black;
- }
} else if (ch instanceof CTScRgbColor) {
// same as CTSRgbColor but with values expressed in percents
CTScRgbColor scrgb = (CTScRgbColor)ch;
@@ -145,21 +150,59 @@ public class XSLFColor { return color;
}
+ /**
+ * Read a perecentage value from the supplied xml bean.
+ * Example:
+ * <a:tint val="45000"/>
+ *
+ * the returned value is 45
+ *
+ * @return the percentage value in the range [0 .. 100]
+ */
private int getPercentageValue(String elem){
- XmlObject[] obj = _xmlObject.selectPath(
- "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' $this//a:" + elem);
+ String query = "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' $this//a:" + elem;
+
+ XmlObject[] obj;
+
+ // first ask the context color and if not found, ask the actual color bean
+ if(_phClr != null){
+ obj = _phClr.selectPath(query);
+ if(obj.length == 1){
+ Node attr = obj[0].getDomNode().getAttributes().getNamedItem("val");
+ if(attr != null) {
+ return Integer.parseInt(attr.getNodeValue()) / 1000;
+ }
+ }
+ }
+
+ obj = _xmlObject.selectPath(query);
if(obj.length == 1){
Node attr = obj[0].getDomNode().getAttributes().getNamedItem("val");
if(attr != null) {
return Integer.parseInt(attr.getNodeValue()) / 1000;
}
}
+
+
return -1;
}
private int getAngleValue(String elem){
- XmlObject[] obj = _xmlObject.selectPath(
- "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' $this//a:" + elem);
+ String color = "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' $this//a:" + elem;
+ XmlObject[] obj;
+
+ // first ask the context color and if not found, ask the actual color bean
+ if(_phClr != null){
+ obj = _xmlObject.selectPath( color );
+ if(obj.length == 1){
+ Node attr = obj[0].getDomNode().getAttributes().getNamedItem("val");
+ if(attr != null) {
+ return Integer.parseInt(attr.getNodeValue()) / 60000;
+ }
+ }
+ }
+
+ obj = _xmlObject.selectPath( color );
if(obj.length == 1){
Node attr = obj[0].getDomNode().getAttributes().getNamedItem("val");
if(attr != null) {
@@ -341,7 +384,7 @@ public class XSLFColor { * A 10% shade is 10% of the input color combined with 90% black.
*
* @return the value of the shade specified as a
- * percentage with 0% indicating minimal blue and 100% indicating maximum
+ * percentage with 0% indicating minimal shade and 100% indicating maximum
* or -1 if the value is not set
*/
int getShade(){
@@ -353,7 +396,7 @@ public class XSLFColor { * A 10% tint is 10% of the input color combined with 90% white.
*
* @return the value of the tint specified as a
- * percentage with 0% indicating minimal blue and 100% indicating maximum
+ * percentage with 0% indicating minimal tint and 100% indicating maximum
* or -1 if the value is not set
*/
int getTint(){
@@ -389,6 +432,10 @@ public class XSLFColor { return color;
}
+ /**
+ * This algorithm returns result different from PowerPoint.
+ * TODO: revisit and improve
+ */
private static Color shade(Color c, int shade) {
return new Color(
(int)(c.getRed() * shade * 0.01),
@@ -397,6 +444,10 @@ public class XSLFColor { c.getAlpha());
}
+ /**
+ * This algorithm returns result different from PowerPoint.
+ * TODO: revisit and improve
+ */
private static Color tint(Color c, int tint) {
int r = c.getRed();
int g = c.getGreen();
@@ -414,7 +465,7 @@ public class XSLFColor { /**
* Preset colors defined in DrawingML
*/
- static Map<String, Color> presetColors;
+ static final Map<String, Color> presetColors;
static {
presetColors = new HashMap<String, Color>();
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java index 2e28774a4c..647b414c53 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java @@ -20,6 +20,8 @@ package org.apache.poi.xslf.usermodel;
import org.apache.poi.util.Beta;
+import org.apache.poi.xslf.model.geom.Outline;
+import org.apache.poi.xslf.model.geom.Path;
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineEndProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
@@ -32,11 +34,14 @@ import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType; import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector;
import org.openxmlformats.schemas.presentationml.x2006.main.CTConnectorNonVisual;
-import java.awt.*;
+import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Specifies a connection shape.
@@ -197,36 +202,7 @@ public class XSLFConnectorShape extends XSLFSimpleShape { return len == null ? LineEndLength.MEDIUM : LineEndLength.values()[len.intValue() - 1];
}
- @Override
- public void draw(Graphics2D graphics) {
- java.awt.Shape outline = getOutline();
-
- // shadow
- XSLFShadow shadow = getShadow();
-
- //border
- Paint line = getLinePaint(graphics);
- if (line != null) {
- if (shadow != null) shadow.draw(graphics);
-
- graphics.setPaint(line);
- applyStroke(graphics);
- graphics.draw(outline);
-
- Shape tailDecoration = getTailDecoration();
- if (tailDecoration != null) {
- graphics.draw(tailDecoration);
- }
-
- Shape headDecoration = getHeadDecoration();
- if (headDecoration != null) {
- graphics.draw(headDecoration);
-
- }
- }
- }
-
- Shape getTailDecoration() {
+ Outline getTailDecoration() {
LineEndLength tailLength = getLineTailLength();
LineEndWidth tailWidth = getLineTailWidth();
@@ -239,17 +215,20 @@ public class XSLFConnectorShape extends XSLFSimpleShape { AffineTransform at = new AffineTransform();
Shape shape = null;
+ Path p = null;
Rectangle2D bounds;
double scaleY = Math.pow(2, tailWidth.ordinal());
double scaleX = Math.pow(2, tailLength.ordinal());
- switch (getLineHeadDecoration()) {
+ switch (getLineTailDecoration()) {
case OVAL:
+ p = new Path();
shape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
bounds = shape.getBounds2D();
at.translate(x2 - bounds.getWidth() / 2, y2 - bounds.getHeight() / 2);
at.rotate(alpha, bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
break;
case ARROW:
+ p = new Path();
GeneralPath arrow = new GeneralPath();
arrow.moveTo((float) (-lineWidth * 3), (float) (-lineWidth * 2));
arrow.lineTo(0, 0);
@@ -259,6 +238,7 @@ public class XSLFConnectorShape extends XSLFSimpleShape { at.rotate(alpha);
break;
case TRIANGLE:
+ p = new Path();
scaleY = tailWidth.ordinal() + 1;
scaleX = tailLength.ordinal() + 1;
GeneralPath triangle = new GeneralPath();
@@ -277,10 +257,10 @@ public class XSLFConnectorShape extends XSLFSimpleShape { if (shape != null) {
shape = at.createTransformedShape(shape);
}
- return shape;
+ return shape == null ? null : new Outline(shape, p);
}
- Shape getHeadDecoration() {
+ Outline getHeadDecoration() {
LineEndLength headLength = getLineHeadLength();
LineEndWidth headWidth = getLineHeadWidth();
@@ -293,11 +273,13 @@ public class XSLFConnectorShape extends XSLFSimpleShape { AffineTransform at = new AffineTransform();
Shape shape = null;
+ Path p = null;
Rectangle2D bounds;
double scaleY = 1;
double scaleX = 1;
switch (getLineHeadDecoration()) {
case OVAL:
+ p = new Path();
shape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
bounds = shape.getBounds2D();
at.translate(x1 - bounds.getWidth() / 2, y1 - bounds.getHeight() / 2);
@@ -305,6 +287,7 @@ public class XSLFConnectorShape extends XSLFSimpleShape { break;
case STEALTH:
case ARROW:
+ p = new Path();
GeneralPath arrow = new GeneralPath();
arrow.moveTo((float) (lineWidth * 3 * scaleX), (float) (-lineWidth * scaleY * 2));
arrow.lineTo(0, 0);
@@ -314,6 +297,7 @@ public class XSLFConnectorShape extends XSLFSimpleShape { at.rotate(alpha);
break;
case TRIANGLE:
+ p = new Path();
scaleY = headWidth.ordinal() + 1;
scaleX = headLength.ordinal() + 1;
GeneralPath triangle = new GeneralPath();
@@ -332,7 +316,19 @@ public class XSLFConnectorShape extends XSLFSimpleShape { if (shape != null) {
shape = at.createTransformedShape(shape);
}
- return shape;
+ return shape == null ? null : new Outline(shape, p);
+ }
+
+ @Override
+ List<Outline> getCustomOutlines(){
+ List<Outline> lst = new ArrayList<Outline>();
+
+ Outline head = getHeadDecoration();
+ if(head != null) lst.add(head);
+
+ Outline tail = getTailDecoration();
+ if(tail != null) lst.add(tail);
+ return lst;
}
}
\ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java index 6de6e4d775..c3b0b1a511 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java @@ -25,7 +25,8 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture;
import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
-import java.awt.*;
+import java.awt.Color;
+import java.awt.Rectangle;
/**
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java index 10490c0c9a..36afac5f8c 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java @@ -188,10 +188,4 @@ public class XSLFFreeformShape extends XSLFAutoShape { geom.addNewPathLst();
return ct;
}
-
- @Override
- protected java.awt.Shape getOutline(){
- return getPath();
- }
-
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java index 8e6809bba2..5beb53850c 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGraphicFrame.java @@ -26,7 +26,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame;
-import java.awt.*;
+import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
/**
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java index 95826af0e4..3159ee98a4 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java @@ -35,7 +35,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShapeNonVisual;
import org.openxmlformats.schemas.presentationml.x2006.main.CTShape;
-import java.awt.*;
+import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.List;
@@ -62,10 +62,12 @@ public class XSLFGroupShape extends XSLFShape { _spPr = shape.getGrpSpPr();
}
+ @Override
public CTGroupShape getXmlObject(){
return _shape;
}
+ @Override
public Rectangle2D getAnchor(){
CTGroupTransform2D xfrm = _spPr.getXfrm();
CTPoint2D off = xfrm.getOff();
@@ -79,6 +81,7 @@ public class XSLFGroupShape extends XSLFShape { Units.toPoints(cx), Units.toPoints(cy));
}
+ @Override
public void setAnchor(Rectangle2D anchor){
CTGroupTransform2D xfrm = _spPr.isSetXfrm() ? _spPr.getXfrm() : _spPr.addNewXfrm();
CTPoint2D off = xfrm.isSetOff() ? xfrm.getOff() : xfrm.addNewOff();
@@ -93,6 +96,12 @@ public class XSLFGroupShape extends XSLFShape { ext.setCy(cy);
}
+ /**
+ *
+ * @return the coordinates of the child extents rectangle
+ * used for calculations of grouping, scaling, and rotation
+ * behavior of shapes placed within a group.
+ */
public Rectangle2D getInteriorAnchor(){
CTGroupTransform2D xfrm = _spPr.getXfrm();
CTPoint2D off = xfrm.getChOff();
@@ -106,6 +115,12 @@ public class XSLFGroupShape extends XSLFShape { Units.toPoints(cx), Units.toPoints(cy));
}
+ /**
+ *
+ * @param anchor the coordinates of the child extents rectangle
+ * used for calculations of grouping, scaling, and rotation
+ * behavior of shapes placed within a group.
+ */
public void setInteriorAnchor(Rectangle2D anchor){
CTGroupTransform2D xfrm = _spPr.isSetXfrm() ? _spPr.getXfrm() : _spPr.addNewXfrm();
CTPoint2D off = xfrm.isSetChOff() ? xfrm.getChOff() : xfrm.addNewChOff();
@@ -120,10 +135,17 @@ public class XSLFGroupShape extends XSLFShape { ext.setCy(cy);
}
+ /**
+ *
+ * @return child shapes contained witin this group
+ */
public XSLFShape[] getShapes(){
return _shapes.toArray(new XSLFShape[_shapes.size()]);
}
+ /**
+ * Remove the specified shape from this group
+ */
public boolean removeShape(XSLFShape xShape) {
XmlObject obj = xShape.getXmlObject();
if(obj instanceof CTShape){
@@ -138,10 +160,12 @@ public class XSLFGroupShape extends XSLFShape { return _shapes.remove(xShape);
}
+ @Override
public String getShapeName(){
return _shape.getNvGrpSpPr().getCNvPr().getName();
}
+ @Override
public int getShapeId(){
return (int)_shape.getNvGrpSpPr().getCNvPr().getId();
}
@@ -216,53 +240,37 @@ public class XSLFGroupShape extends XSLFShape { return sh;
}
-
+ @Override
public void setFlipHorizontal(boolean flip){
_spPr.getXfrm().setFlipH(flip);
}
+ @Override
public void setFlipVertical(boolean flip){
_spPr.getXfrm().setFlipV(flip);
}
- /**
- * Whether the shape is horizontally flipped
- *
- * @return whether the shape is horizontally flipped
- */
+
+ @Override
public boolean getFlipHorizontal(){
return _spPr.getXfrm().getFlipH();
}
+ @Override
public boolean getFlipVertical(){
return _spPr.getXfrm().getFlipV();
}
- /**
- * Rotate this shape.
- * <p>
- * Positive angles are clockwise (i.e., towards the positive y axis);
- * negative angles are counter-clockwise (i.e., towards the negative y axis).
- * </p>
- *
- * @param theta the rotation angle in degrees.
- */
+ @Override
public void setRotation(double theta){
_spPr.getXfrm().setRot((int)(theta*60000));
}
- /**
- * Rotation angle in degrees
- * <p>
- * Positive angles are clockwise (i.e., towards the positive y axis);
- * negative angles are counter-clockwise (i.e., towards the negative y axis).
- * </p>
- *
- * @return rotation angle in degrees
- */
+ @Override
public double getRotation(){
return (double)_spPr.getXfrm().getRot()/60000;
}
+ @Override
public void draw(Graphics2D graphics){
// the coordinate system of this group of shape
@@ -278,16 +286,14 @@ public class XSLFGroupShape extends XSLFShape { for (XSLFShape shape : getShapes()) {
// remember the initial transform and restore it after we are done with the drawing
- AffineTransform at0 = graphics.getTransform();
+ AffineTransform at = graphics.getTransform();
graphics.setRenderingHint(XSLFRenderingHint.GSAVE, true);
- // apply rotation and flipping
- shape.applyTransform(graphics);
-
+ shape.applyTransform(graphics);
shape.draw(graphics);
// restore the coordinate system
- graphics.setTransform(at0);
+ graphics.setTransform(at);
graphics.setRenderingHint(XSLFRenderingHint.GRESTORE, true);
}
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java index 8f763622f4..a8bbe8b6a2 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java @@ -19,10 +19,11 @@ package org.apache.poi.xslf.usermodel;
+import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.util.Beta;
import javax.imageio.ImageIO;
-import java.awt.*;
+import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
@@ -76,11 +77,10 @@ public class XSLFImageRendener { public boolean drawImage(Graphics2D graphics, XSLFPictureData data,
Rectangle2D anchor) {
try {
- BufferedImage img = readImage(new ByteArrayInputStream(data.getData()));
- if (img != null){
- graphics.drawImage(img, (int) anchor.getX(), (int) anchor.getY(),
- (int) anchor.getWidth(), (int) anchor.getHeight(), null);
- }
+ BufferedImage img = ImageIO.read(data.getPackagePart().getInputStream());
+ graphics.drawImage(img,
+ (int) anchor.getX(), (int) anchor.getY(),
+ (int) anchor.getWidth(), (int) anchor.getHeight(), null);
return true;
} catch (Exception e) {
return false;
@@ -89,12 +89,13 @@ public class XSLFImageRendener { }
/**
- * create a buffered image from input stream
+ * Create a buffered image from the supplied package part.
+ * This method is called to create texture paints.
*
* @return a <code>BufferedImage</code> containing the decoded
* contents of the input, or <code>null</code>.
*/
- public BufferedImage readImage(InputStream is) throws IOException {
- return ImageIO.read(is);
+ public BufferedImage readImage(PackagePart packagePart) throws IOException {
+ return ImageIO.read(packagePart.getInputStream());
}
}
\ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java index 3025afcc40..ac4a4d2870 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java @@ -75,4 +75,9 @@ public final class XSLFNotes extends XSLFSheet { protected String getRootElementName(){ return "notes"; } + + @Override + public XSLFSheet getMasterSheet() { + return null; + } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotesMaster.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotesMaster.java index 05813bdb96..77ebf05956 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotesMaster.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotesMaster.java @@ -24,7 +24,6 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesMaster; import org.openxmlformats.schemas.presentationml.x2006.main.NotesMasterDocument; import java.io.IOException; -import java.util.Map; /** * Notes master object associated with this layout. @@ -45,8 +44,6 @@ import java.util.Map; @Beta public class XSLFNotesMaster extends XSLFSheet { private CTNotesMaster _slide; - private Map<String, XSLFSlideLayout> _layouts; - private XSLFTheme _theme; XSLFNotesMaster() { super(); @@ -70,4 +67,10 @@ import java.util.Map; protected String getRootElementName(){ return "notesMaster"; } + + @Override + public XSLFSheet getMasterSheet() { + return null; + } + }
\ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java index 0ceb5c5636..445c16e9b0 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java @@ -33,12 +33,14 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; import org.openxmlformats.schemas.presentationml.x2006.main.CTPictureNonVisual;
import javax.imageio.ImageIO;
-import java.awt.*;
+import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
/**
+ * Represents a picture shape
+ *
* @author Yegor Kozlov
*/
@Beta
@@ -114,37 +116,14 @@ public class XSLFPictureShape extends XSLFSimpleShape { }
@Override
- public void draw(Graphics2D graphics){
- java.awt.Shape outline = getOutline();
-
- // shadow
- XSLFShadow shadow = getShadow();
-
- Paint fill = getFill(graphics);
- Paint line = getLinePaint(graphics);
- if(shadow != null) {
- shadow.draw(graphics);
- }
-
- if(fill != null) {
- graphics.setPaint(fill);
- graphics.fill(outline);
- }
-
+ public void drawContent(Graphics2D graphics) {
XSLFPictureData data = getPictureData();
if(data == null) return;
-
+
XSLFImageRendener renderer = (XSLFImageRendener)graphics.getRenderingHint(XSLFRenderingHint.IMAGE_RENDERER);
if(renderer == null) renderer = new XSLFImageRendener();
renderer.drawImage(graphics, data, getAnchor());
-
- if (line != null){
- graphics.setPaint(line);
- applyStroke(graphics);
- graphics.draw(outline);
- }
}
-
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRenderingHint.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRenderingHint.java index cc0df0fbfd..345d57c4b3 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRenderingHint.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRenderingHint.java @@ -19,7 +19,7 @@ package org.apache.poi.xslf.usermodel;
-import java.awt.*;
+import java.awt.RenderingHints;
/**
*
@@ -38,5 +38,37 @@ public class XSLFRenderingHint extends RenderingHints.Key { public static final XSLFRenderingHint GSAVE = new XSLFRenderingHint(1);
public static final XSLFRenderingHint GRESTORE = new XSLFRenderingHint(2);
+
+ /**
+ * Use a custom image rendener
+ *
+ * @see XSLFImageRendener
+ */
public static final XSLFRenderingHint IMAGE_RENDERER = new XSLFRenderingHint(3);
+
+ /**
+ * how to render text:
+ *
+ * {@link #TEXT_MODE_CHARACTERS} (default) means to draw via
+ * {@link java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float)}.
+ * This mode draws text as characters. Use it if the target graphics writes the actual
+ * character codes instead of glyph outlines (PDFGraphics2D, SVGGraphics2D, etc.)
+ *
+ * {@link #TEXT_MODE_GLYPHS} means to render via
+ * {@link java.awt.font.TextLayout#draw(java.awt.Graphics2D, float, float)}.
+ * This mode draws glyphs as shapes and provides some advanced capabilities such as
+ * justification and font substitution. Use it if the target graphics is an image.
+ *
+ */
+ public static final XSLFRenderingHint TEXT_RENDERING_MODE = new XSLFRenderingHint(4);
+
+ /**
+ * draw text via {@link java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float)}
+ */
+ public static final int TEXT_MODE_CHARACTERS = 1;
+
+ /**
+ * draw text via {@link java.awt.font.TextLayout#draw(java.awt.Graphics2D, float, float)}
+ */
+ public static final int TEXT_MODE_GLYPHS = 2;
}
\ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java index 8838fa3e41..70d42ce5c3 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java @@ -19,8 +19,11 @@ package org.apache.poi.xslf.usermodel; import org.apache.poi.util.Units;
import org.openxmlformats.schemas.drawingml.x2006.main.CTOuterShadowEffect;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
-import java.awt.*;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Shape;
import java.awt.geom.Rectangle2D;
/**
@@ -37,36 +40,44 @@ public class XSLFShadow extends XSLFSimpleShape { _parent = parentShape;
}
- @Override
- public void draw(Graphics2D graphics) {
- Shape outline = _parent.getOutline();
- Paint parentFillColor = _parent.getFill(graphics);
- Paint parentLineColor = _parent.getLinePaint(graphics);
+ public void fill(Graphics2D graphics, Shape outline) {
double angle = getAngle();
double dist = getDistance();
- double dx = dist * Math.cos( Math.toRadians(angle));
- double dy = dist * Math.sin( Math.toRadians(angle));
+ double dx = dist * Math.cos(Math.toRadians(angle));
+ double dy = dist * Math.sin(Math.toRadians(angle));
graphics.translate(dx, dy);
Color fillColor = getFillColor();
if (fillColor != null) {
graphics.setColor(fillColor);
+ graphics.fill(outline);
}
- if(parentFillColor != null) {
- graphics.fill(outline);
- }
- if(parentLineColor != null) {
- _parent.applyStroke(graphics);
- graphics.draw(outline);
- }
+ graphics.translate(-dx, -dy);
+ }
+
+ public void draw(Graphics2D graphics, Shape outline) {
+
+ double angle = getAngle();
+ double dist = getDistance();
+ double dx = dist * Math.cos(Math.toRadians(angle));
+ double dy = dist * Math.sin(Math.toRadians(angle));
+
+ graphics.translate(dx, dy);
+
+ Color fillColor = getFillColor();
+ if (fillColor != null) {
+ graphics.setColor(fillColor);
+ graphics.draw(outline);
+ }
graphics.translate(-dx, -dy);
}
+
@Override
public Rectangle2D getAnchor(){
return _parent.getAnchor();
@@ -112,6 +123,11 @@ public class XSLFShadow extends XSLFSimpleShape { public Color getFillColor() {
XSLFTheme theme = getSheet().getTheme();
CTOuterShadowEffect ct = (CTOuterShadowEffect)getXmlObject();
- return ct == null ? null : new XSLFColor(ct, theme).getColor();
+ if(ct == null) {
+ return null;
+ } else {
+ CTSchemeColor phClr = ct.getSchemeClr();
+ return new XSLFColor(ct, theme, phClr).getColor();
+ }
}
}
\ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java index 992973c518..9ff619257c 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java @@ -22,24 +22,54 @@ package org.apache.poi.xslf.usermodel; import org.apache.poi.util.Beta;
import org.apache.xmlbeans.XmlObject;
-import java.awt.*;
+import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
/**
+ * Base super-class class for all shapes in PresentationML
+ *
* @author Yegor Kozlov
*/
@Beta
public abstract class XSLFShape {
-
+ /**
+ *
+ * @return the position of this shape within the drawing canvas.
+ * The coordinates are expressed in points
+ */
public abstract Rectangle2D getAnchor();
+ /**
+ *
+ * @param anchor the position of this shape within the drawing canvas.
+ * The coordinates are expressed in points
+ */
public abstract void setAnchor(Rectangle2D anchor);
+ /**
+ *
+ * @return the xml bean holding this shape's data
+ */
public abstract XmlObject getXmlObject();
+ /**
+ *
+ * @return human-readable name of this shape, e.g. "Rectange 3"
+ */
public abstract String getShapeName();
+ /**
+ * 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
+ */
public abstract int getShapeId();
/**
@@ -64,8 +94,16 @@ public abstract class XSLFShape { */
public abstract double getRotation();
+ /**
+ * @param flip whether the shape is horizontally flipped
+ */
public abstract void setFlipHorizontal(boolean flip);
+ /**
+ * Whether the shape is vertically flipped
+ *
+ * @param flip whether the shape is vertically flipped
+ */
public abstract void setFlipVertical(boolean flip);
/**
@@ -75,14 +113,25 @@ public abstract class XSLFShape { */
public abstract boolean getFlipHorizontal();
+ /**
+ * Whether the shape is vertically flipped
+ *
+ * @return whether the shape is vertically flipped
+ */
public abstract boolean getFlipVertical();
+ /**
+ * Draw this shape into the supplied canvas
+ *
+ * @param graphics the graphics to draw into
+ */
public abstract void draw(Graphics2D graphics);
- protected java.awt.Shape getOutline(){
- return getAnchor();
- }
-
+ /**
+ * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
+ *
+ * @param graphics the graphics whos transform matrix will be modified
+ */
protected void applyTransform(Graphics2D graphics){
Rectangle2D anchor = getAnchor();
@@ -112,5 +161,5 @@ public abstract class XSLFShape { graphics.translate(-anchor.getX(), -anchor.getY());
}
}
-
+
}
\ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java index 87da995d49..964df2df96 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java @@ -21,9 +21,9 @@ import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.openxml4j.opc.TargetMode; import org.apache.poi.util.Beta; +import org.apache.poi.util.Internal; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle; import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; import org.openxmlformats.schemas.presentationml.x2006.main.CTCommonSlideData; import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector; @@ -34,22 +34,25 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; import javax.xml.namespace.QName; -import java.awt.*; +import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Pattern; @Beta -public abstract class XSLFSheet extends POIXMLDocumentPart { +public abstract class XSLFSheet extends POIXMLDocumentPart implements Iterable<XSLFShape> { private XSLFCommonSlideData _commonSlideData; private XSLFDrawing _drawing; private List<XSLFShape> _shapes; private CTGroupShape _spTree; + + private List<XSLFTextShape>_placeholders; private Map<Integer, XSLFSimpleShape> _placeholderByIdMap; private Map<Integer, XSLFSimpleShape> _placeholderByTypeMap; @@ -61,6 +64,10 @@ public abstract class XSLFSheet extends POIXMLDocumentPart { super(part, rel); } + /** + * + * @return the XMLSlideShow this sheet belongs to + */ public XMLSlideShow getSlideShow() { POIXMLDocumentPart p = getParent(); while(p != null) { @@ -69,7 +76,7 @@ public abstract class XSLFSheet extends POIXMLDocumentPart { } p = p.getParent(); } - return null; + throw new IllegalStateException("SlideShow was not found"); } protected List<XSLFShape> buildShapes(CTGroupShape spTree){ @@ -92,11 +99,16 @@ public abstract class XSLFSheet extends POIXMLDocumentPart { return shapes; } + /** + * @return top-level Xml bean representing this sheet + */ public abstract XmlObject getXmlObject(); + @Internal public XSLFCommonSlideData getCommonSlideData() { return _commonSlideData; } + protected void setCommonSlideData(CTCommonSlideData data) { if(data == null) { _commonSlideData = null; @@ -180,10 +192,34 @@ public abstract class XSLFSheet extends POIXMLDocumentPart { return sh; } + /** + * Returns an array containing all of the shapes in this sheet + * + * @return an array of all shapes in this sheet + */ public XSLFShape[] getShapes(){ return getShapeList().toArray(new XSLFShape[_shapes.size()]); } + /** + * Returns an iterator over the shapes in this sheet + * + * @return an iterator over the shapes in this sheet + */ + public Iterator<XSLFShape> iterator(){ + return getShapeList().iterator(); + } + + /** + * Removes the specified shape from this sheet, if it is present + * (optional operation). If this sheet does not contain the element, + * it is unchanged. + * + * @param xShape shape to be removed from this sheet, if present + * @return <tt>true</tt> if this sheet contained the specified element + * @throws IllegalArgumentException if the type of the specified shape + * is incompatible with this sheet (optional) + */ public boolean removeShape(XSLFShape xShape) { XmlObject obj = xShape.getXmlObject(); CTGroupShape spTree = getSpTree(); @@ -199,10 +235,6 @@ public abstract class XSLFSheet extends POIXMLDocumentPart { return getShapeList().remove(xShape); } - public XSLFBackground getBackground(){ - return null; - } - protected abstract String getRootElementName(); protected CTGroupShape getSpTree(){ @@ -248,22 +280,22 @@ public abstract class XSLFSheet extends POIXMLDocumentPart { getXmlObject().set(src.getXmlObject()); } - public XSLFTheme getTheme(){ - return null; - } - - public XSLFSlideMaster getSlideMaster(){ + /** + * @return theme (shared styles) associated with this theme. + * By default returns <code>null</code> which means that this sheet is theme-less. + * Sheets that support the notion of themes (slides, masters, layouts, etc.) should override this + * method and return the corresposnding package part. + */ + XSLFTheme getTheme(){ return null; } - public XSLFSlideLayout getSlideLayout(){ - return null; - } + /** + * + * @return master of this sheet. + */ + public abstract XSLFSheet getMasterSheet(); - protected CTTextListStyle getTextProperties(Placeholder textType) { - return null; - } - protected XSLFTextShape getTextShapeByType(Placeholder type){ for(XSLFShape shape : this.getShapes()){ if(shape instanceof XSLFTextShape) { @@ -286,72 +318,107 @@ public abstract class XSLFSheet extends POIXMLDocumentPart { return shape; } - XSLFSimpleShape getPlaceholderById(int id) { - if(_placeholderByIdMap == null) { + void initPlaceholders() { + if(_placeholders == null) { + _placeholders = new ArrayList<XSLFTextShape>(); _placeholderByIdMap = new HashMap<Integer, XSLFSimpleShape>(); + _placeholderByTypeMap = new HashMap<Integer, XSLFSimpleShape>(); + for(XSLFShape sh : getShapes()){ - if(sh instanceof XSLFSimpleShape){ - XSLFSimpleShape sShape = (XSLFSimpleShape)sh; + if(sh instanceof XSLFTextShape){ + XSLFTextShape sShape = (XSLFTextShape)sh; CTPlaceholder ph = sShape.getCTPlaceholder(); - if(ph != null && ph.isSetIdx()){ - int idx = (int)ph.getIdx(); - _placeholderByIdMap.put(idx, sShape); + if(ph != null) { + _placeholders.add(sShape); + if(ph.isSetIdx()) { + int idx = (int)ph.getIdx(); + _placeholderByIdMap.put(idx, sShape); + } + if(ph.isSetType()){ + _placeholderByTypeMap.put(ph.getType().intValue(), sShape); + } } } } } + } + + XSLFSimpleShape getPlaceholderById(int id) { + initPlaceholders(); return _placeholderByIdMap.get(id); } XSLFSimpleShape getPlaceholderByType(int ordinal) { - if(_placeholderByTypeMap == null) { - _placeholderByTypeMap = new HashMap<Integer, XSLFSimpleShape>(); - for(XSLFShape sh : getShapes()){ - if(sh instanceof XSLFSimpleShape){ - XSLFSimpleShape sShape = (XSLFSimpleShape)sh; - CTPlaceholder ph = sShape.getCTPlaceholder(); - if(ph != null && ph.isSetType()){ - _placeholderByTypeMap.put(ph.getType().intValue(), sShape); - } - } - } - } + initPlaceholders(); return _placeholderByTypeMap.get(ordinal); } /** + * + * @param idx 0-based index of a placeholder in the sheet + * @return placeholder + */ + public XSLFTextShape getPlaceholder(int idx) { + initPlaceholders(); + return _placeholders.get(idx); + } + + /** + * + * @return all placeholder shapes in this sheet + */ + public XSLFTextShape[] getPlaceholders() { + initPlaceholders(); + return _placeholders.toArray(new XSLFTextShape[_placeholders.size()]); + } + + /** * Checks if this <code>sheet</code> displays the specified shape. * - * Subclasses can override it and skip certain shapes from drawings. + * Subclasses can override it and skip certain shapes from drawings, + * for instance, slide masters and layouts don't display placeholders */ protected boolean canDraw(XSLFShape shape){ return true; } /** + * + * @return whether shapes on the master sheet should be shown. By default master graphics is turned off. + * Sheets that support the notion of master (slide, slideLayout) should override it and + * check this setting in the sheet XML + */ + public boolean getFollowMasterGraphics(){ + return false; + } + + /** * Render this sheet into the supplied graphics object * * @param graphics */ public void draw(Graphics2D graphics){ - XSLFBackground bg = getBackground(); - if(bg != null) bg.draw(graphics); + XSLFSheet master = getMasterSheet(); + if(getFollowMasterGraphics() && master != null) master.draw(graphics); for(XSLFShape shape : getShapeList()) { if(!canDraw(shape)) continue; // remember the initial transform and restore it after we are done with drawing - AffineTransform at0 = graphics.getTransform(); + AffineTransform at = graphics.getTransform(); + // concrete implementations can make sense of this hint, + // for example PSGraphics2D or PDFGraphics2D would call gsave() / grestore graphics.setRenderingHint(XSLFRenderingHint.GSAVE, true); // apply rotation and flipping shape.applyTransform(graphics); - + // draw stuff shape.draw(graphics); // restore the coordinate system - graphics.setTransform(at0); + graphics.setTransform(at); + graphics.setRenderingHint(XSLFRenderingHint.GRESTORE, true); } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java index 10bad70d03..c67f39e793 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java @@ -19,8 +19,6 @@ package org.apache.poi.xslf.usermodel;
-import org.apache.poi.openxml4j.opc.PackagePart;
-import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.util.Beta;
import org.apache.poi.util.Units;
import org.apache.poi.xslf.model.PropertyFetcher;
@@ -28,36 +26,50 @@ import org.apache.poi.xslf.model.geom.Context; import org.apache.poi.xslf.model.geom.CustomGeometry;
import org.apache.poi.xslf.model.geom.Guide;
import org.apache.poi.xslf.model.geom.IAdjustableShape;
+import org.apache.poi.xslf.model.geom.Outline;
import org.apache.poi.xslf.model.geom.Path;
import org.apache.poi.xslf.model.geom.PresetGeometries;
import org.apache.xmlbeans.XmlObject;
-import org.openxmlformats.schemas.drawingml.x2006.main.*;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTEffectStyleItem;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomGuide;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTOuterShadowEffect;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetLineDashProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrix;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
+import org.openxmlformats.schemas.drawingml.x2006.main.STLineCap;
+import org.openxmlformats.schemas.drawingml.x2006.main.STPresetLineDashVal;
+import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
-import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.awt.Paint;
-import java.awt.Graphics2D;
-import java.awt.Color;
-import java.awt.TexturePaint;
-import java.awt.AlphaComposite;
-import java.awt.GradientPaint;
-import java.awt.BasicStroke;
-import java.awt.Stroke;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Constructor;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
/**
+ * Represents a single (non-group) shape in a .pptx slide show
+ *
* @author Yegor Kozlov
*/
@Beta
public abstract class XSLFSimpleShape extends XSLFShape {
+
private final XmlObject _shape;
private final XSLFSheet _sheet;
private CTShapeProperties _spPr;
@@ -70,10 +82,15 @@ public abstract class XSLFSimpleShape extends XSLFShape { _sheet = sheet;
}
+ @Override
public XmlObject getXmlObject() {
return _shape;
}
+ /**
+ *
+ * @return the sheet this shape belongs to
+ */
public XSLFSheet getSheet() {
return _sheet;
}
@@ -88,10 +105,12 @@ public abstract class XSLFSimpleShape extends XSLFShape { return stEnum == null ? 0 : stEnum.intValue();
}
+ @Override
public String getShapeName() {
return getNvPr().getName();
}
+ @Override
public int getShapeId() {
return (int) getNvPr().getId();
}
@@ -132,22 +151,22 @@ public abstract class XSLFSimpleShape extends XSLFShape { return _spStyle;
}
- protected CTPlaceholder getCTPlaceholder(){
- if(_ph == null){
+ protected CTPlaceholder getCTPlaceholder() {
+ if (_ph == null) {
XmlObject[] obj = _shape.selectPath(
"declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//*/p:nvPr/p:ph");
- if(obj.length == 1){
- _ph = (CTPlaceholder)obj[0];
+ if (obj.length == 1) {
+ _ph = (CTPlaceholder) obj[0];
}
}
return _ph;
}
- private CTTransform2D getXfrm(){
- PropertyFetcher<CTTransform2D> fetcher = new PropertyFetcher<CTTransform2D>(){
- public boolean fetch(XSLFSimpleShape shape){
+ private CTTransform2D getXfrm() {
+ PropertyFetcher<CTTransform2D> fetcher = new PropertyFetcher<CTTransform2D>() {
+ public boolean fetch(XSLFSimpleShape shape) {
CTShapeProperties pr = shape.getSpPr();
- if(pr.isSetXfrm()){
+ if (pr.isSetXfrm()) {
setValue(pr.getXfrm());
return true;
}
@@ -158,10 +177,11 @@ public abstract class XSLFSimpleShape extends XSLFShape { return fetcher.getValue();
}
+ @Override
public Rectangle2D getAnchor() {
CTTransform2D xfrm = getXfrm();
-
+
CTPoint2D off = xfrm.getOff();
long x = off.getX();
long y = off.getY();
@@ -173,6 +193,7 @@ public abstract class XSLFSimpleShape extends XSLFShape { Units.toPoints(cx), Units.toPoints(cy));
}
+ @Override
public void setAnchor(Rectangle2D anchor) {
CTShapeProperties spPr = getSpPr();
CTTransform2D xfrm = spPr.isSetXfrm() ? spPr.getXfrm() : spPr.addNewXfrm();
@@ -189,64 +210,46 @@ public abstract class XSLFSimpleShape extends XSLFShape { ext.setCy(cy);
}
- /**
- * Rotate this shape.
- * <p>
- * Positive angles are clockwise (i.e., towards the positive y axis);
- * negative angles are counter-clockwise (i.e., towards the negative y
- * axis).
- * </p>
- *
- * @param theta the rotation angle in degrees.
- */
+ @Override
public void setRotation(double theta) {
CTShapeProperties spPr = getSpPr();
CTTransform2D xfrm = spPr.isSetXfrm() ? spPr.getXfrm() : spPr.addNewXfrm();
xfrm.setRot((int) (theta * 60000));
}
- /**
- * Rotation angle in degrees
- * <p>
- * Positive angles are clockwise (i.e., towards the positive y axis);
- * negative angles are counter-clockwise (i.e., towards the negative y
- * axis).
- * </p>
- *
- * @return rotation angle in degrees
- */
+ @Override
public double getRotation() {
CTTransform2D xfrm = getXfrm();
return (double) xfrm.getRot() / 60000;
}
+ @Override
public void setFlipHorizontal(boolean flip) {
CTShapeProperties spPr = getSpPr();
CTTransform2D xfrm = spPr.isSetXfrm() ? spPr.getXfrm() : spPr.addNewXfrm();
xfrm.setFlipH(flip);
}
+ @Override
public void setFlipVertical(boolean flip) {
CTShapeProperties spPr = getSpPr();
CTTransform2D xfrm = spPr.isSetXfrm() ? spPr.getXfrm() : spPr.addNewXfrm();
xfrm.setFlipV(flip);
}
- /**
- * Whether the shape is horizontally flipped
- *
- * @return whether the shape is horizontally flipped
- */
+ @Override
public boolean getFlipHorizontal() {
return getXfrm().getFlipH();
}
+ @Override
public boolean getFlipVertical() {
return getXfrm().getFlipV();
}
/**
- * Get line properties defined in the theme (if any)
+ * Get default line properties defined in the theme (if any).
+ * Used internally to resolve shape properties.
*
* @return line propeties from the theme of null
*/
@@ -262,6 +265,10 @@ public abstract class XSLFSimpleShape extends XSLFShape { return ln;
}
+ /**
+ * @param color the color to paint the shape outline.
+ * A <code>null</code> value turns off the shape outline.
+ */
public void setLineColor(Color color) {
CTShapeProperties spPr = getSpPr();
if (color == null) {
@@ -281,48 +288,24 @@ public abstract class XSLFSimpleShape extends XSLFShape { }
}
+ /**
+ *
+ * @return the color of the shape outline or <code>null</code>
+ * if outline is turned off
+ */
public Color getLineColor() {
- Paint paint = getLinePaint(null);
- if(paint instanceof Color){
- return (Color)paint;
+ RenderableShape rShape = new RenderableShape(this);
+ Paint paint = rShape.getLinePaint(null);
+ if (paint instanceof Color) {
+ return (Color) paint;
}
return null;
}
- public Paint getLinePaint(final Graphics2D graphics) {
- final XSLFTheme theme = _sheet.getTheme();
- final Color nofill = new Color(0,0,0,0);
- PropertyFetcher<Paint> fetcher = new PropertyFetcher<Paint>(){
- public boolean fetch(XSLFSimpleShape shape){
- CTLineProperties spPr = shape.getSpPr().getLn();
- if (spPr != null) {
- if (spPr.isSetNoFill()) {
- setValue(nofill); // use it as 'nofill' value
- return true;
- }
- Paint paint = getPaint(graphics, spPr);
- if (paint != null) {
- setValue( paint );
- return true;
- }
- }
- return false;
-
- }
- };
- fetchShapeProperty(fetcher);
-
- Paint color = fetcher.getValue();
- if(color == null){
- // line color was not found, check if it is defined in the theme
- CTShapeStyle style = getSpStyle();
- if (style != null) {
- color = new XSLFColor(style.getLnRef(), theme).getColor();
- }
- }
- return color == nofill ? null : color;
- }
-
+ /**
+ *
+ * @param width line width in points. <code>0</code> means no line
+ */
public void setLineWidth(double width) {
CTShapeProperties spPr = getSpPr();
if (width == 0.) {
@@ -335,9 +318,13 @@ public abstract class XSLFSimpleShape extends XSLFShape { }
}
+ /**
+ *
+ * @return line width in points. <code>0</code> means no line.
+ */
public double getLineWidth() {
- PropertyFetcher<Double> fetcher = new PropertyFetcher<Double>(){
- public boolean fetch(XSLFSimpleShape shape){
+ PropertyFetcher<Double> fetcher = new PropertyFetcher<Double>() {
+ public boolean fetch(XSLFSimpleShape shape) {
CTShapeProperties spPr = shape.getSpPr();
CTLineProperties ln = spPr.getLn();
if (ln != null) {
@@ -347,7 +334,7 @@ public abstract class XSLFSimpleShape extends XSLFShape { }
if (ln.isSetW()) {
- setValue( Units.toPoints(ln.getW()) );
+ setValue(Units.toPoints(ln.getW()));
return true;
}
}
@@ -357,7 +344,7 @@ public abstract class XSLFSimpleShape extends XSLFShape { fetchShapeProperty(fetcher);
double lineWidth = 0;
- if(fetcher.getValue() == null) {
+ if (fetcher.getValue() == null) {
CTLineProperties defaultLn = getDefaultLineProperties();
if (defaultLn != null) {
if (defaultLn.isSetW()) lineWidth = Units.toPoints(defaultLn.getW());
@@ -369,6 +356,10 @@ public abstract class XSLFSimpleShape extends XSLFShape { return lineWidth;
}
+ /**
+ *
+ * @param dash a preset line dashing scheme to stroke thr shape outline
+ */
public void setLineDash(LineDash dash) {
CTShapeProperties spPr = getSpPr();
if (dash == null) {
@@ -384,16 +375,19 @@ public abstract class XSLFSimpleShape extends XSLFShape { }
}
+ /**
+ * @return a preset line dashing scheme to stroke thr shape outline
+ */
public LineDash getLineDash() {
- PropertyFetcher<LineDash> fetcher = new PropertyFetcher<LineDash>(){
- public boolean fetch(XSLFSimpleShape shape){
+ PropertyFetcher<LineDash> fetcher = new PropertyFetcher<LineDash>() {
+ public boolean fetch(XSLFSimpleShape shape) {
CTShapeProperties spPr = shape.getSpPr();
CTLineProperties ln = spPr.getLn();
if (ln != null) {
CTPresetLineDashProperties ctDash = ln.getPrstDash();
if (ctDash != null) {
- setValue( LineDash.values()[ctDash.getVal().intValue() - 1] );
+ setValue(LineDash.values()[ctDash.getVal().intValue() - 1]);
return true;
}
}
@@ -403,7 +397,7 @@ public abstract class XSLFSimpleShape extends XSLFShape { fetchShapeProperty(fetcher);
LineDash dash = fetcher.getValue();
- if(dash == null){
+ if (dash == null) {
CTLineProperties defaultLn = getDefaultLineProperties();
if (defaultLn != null) {
CTPresetLineDashProperties ctDash = defaultLn.getPrstDash();
@@ -415,6 +409,10 @@ public abstract class XSLFSimpleShape extends XSLFShape { return dash;
}
+ /**
+ *
+ * @param cap the line end cap style
+ */
public void setLineCap(LineCap cap) {
CTShapeProperties spPr = getSpPr();
if (cap == null) {
@@ -427,15 +425,19 @@ public abstract class XSLFSimpleShape extends XSLFShape { }
}
+ /**
+ *
+ * @return the line end cap style
+ */
public LineCap getLineCap() {
- PropertyFetcher<LineCap> fetcher = new PropertyFetcher<LineCap>(){
- public boolean fetch(XSLFSimpleShape shape){
+ PropertyFetcher<LineCap> fetcher = new PropertyFetcher<LineCap>() {
+ public boolean fetch(XSLFSimpleShape shape) {
CTShapeProperties spPr = shape.getSpPr();
CTLineProperties ln = spPr.getLn();
if (ln != null) {
STLineCap.Enum stCap = ln.getCap();
if (stCap != null) {
- setValue( LineCap.values()[stCap.intValue() - 1] );
+ setValue(LineCap.values()[stCap.intValue() - 1]);
return true;
}
}
@@ -445,7 +447,7 @@ public abstract class XSLFSimpleShape extends XSLFShape { fetchShapeProperty(fetcher);
LineCap cap = fetcher.getValue();
- if(cap == null){
+ if (cap == null) {
CTLineProperties defaultLn = getDefaultLineProperties();
if (defaultLn != null) {
STLineCap.Enum stCap = defaultLn.getCap();
@@ -469,10 +471,10 @@ public abstract class XSLFSimpleShape extends XSLFShape { if (color == null) {
if (spPr.isSetSolidFill()) spPr.unsetSolidFill();
- if(!spPr.isSetNoFill()) spPr.addNewNoFill();
+ if (!spPr.isSetNoFill()) spPr.addNewNoFill();
} else {
- if(spPr.isSetNoFill()) spPr.unsetNoFill();
-
+ if (spPr.isSetNoFill()) spPr.unsetNoFill();
+
CTSolidColorFillProperties fill = spPr.isSetSolidFill() ? spPr
.getSolidFill() : spPr.addNewSolidFill();
@@ -485,55 +487,24 @@ public abstract class XSLFSimpleShape extends XSLFShape { }
/**
- * @return solid fill color of null if not set
+ * @return solid fill color of null if not set or fill color
+ * is not solid (pattern or gradient)
*/
public Color getFillColor() {
- Paint paint = getFill(null);
- if(paint instanceof Color){
- return (Color)paint;
+ RenderableShape rShape = new RenderableShape(this);
+ Paint paint = rShape.getFillPaint(null);
+ if (paint instanceof Color) {
+ return (Color) paint;
}
return null;
}
/**
- * fetch shape fill as a java.awt.Paint
- *
- * @return either Color or GradientPaint or TexturePaint or null
+ * @return shadow of this shape or null if shadow is disabled
*/
- Paint getFill(final Graphics2D graphics) {
- final XSLFTheme theme = _sheet.getTheme();
- final Color nofill = new Color(0xFF,0xFF,0xFF, 0);
- PropertyFetcher<Paint> fetcher = new PropertyFetcher<Paint>(){
- public boolean fetch(XSLFSimpleShape shape){
- CTShapeProperties spPr = shape.getSpPr();
- if (spPr.isSetNoFill()) {
- setValue(nofill); // use it as 'nofill' value
- return true;
- }
- Paint paint = getPaint(graphics, spPr);
- if (paint != null) {
- setValue( paint );
- return true;
- }
- return false;
- }
- };
- fetchShapeProperty(fetcher);
-
- Paint paint = fetcher.getValue();
- if(paint == null){
- // fill color was not found, check if it is defined in the theme
- CTShapeStyle style = getSpStyle();
- if (style != null) {
- paint = new XSLFColor(style.getFillRef(), theme).getColor();
- }
- }
- return paint == nofill ? null : paint;
- }
-
- public XSLFShadow getShadow(){
- PropertyFetcher<CTOuterShadowEffect> fetcher = new PropertyFetcher<CTOuterShadowEffect>(){
- public boolean fetch(XSLFSimpleShape shape){
+ public XSLFShadow getShadow() {
+ PropertyFetcher<CTOuterShadowEffect> fetcher = new PropertyFetcher<CTOuterShadowEffect>() {
+ public boolean fetch(XSLFSimpleShape shape) {
CTShapeProperties spPr = shape.getSpPr();
if (spPr.isSetEffectLst()) {
CTOuterShadowEffect obj = spPr.getEffectLst().getOuterShdw();
@@ -546,233 +517,48 @@ public abstract class XSLFSimpleShape extends XSLFShape { fetchShapeProperty(fetcher);
CTOuterShadowEffect obj = fetcher.getValue();
- if(obj == null){
+ if (obj == null) {
// fill color was not found, check if it is defined in the theme
CTShapeStyle style = getSpStyle();
if (style != null) {
// 1-based index of a shadow style within the style matrix
int idx = (int) style.getEffectRef().getIdx();
-
- CTStyleMatrix styleMatrix = _sheet.getTheme().getXmlObject().getThemeElements().getFmtScheme();
- CTEffectStyleItem ef = styleMatrix.getEffectStyleLst().getEffectStyleArray(idx - 1);
- obj = ef.getEffectLst().getOuterShdw();
+ if(idx != 0) {
+ CTStyleMatrix styleMatrix = _sheet.getTheme().getXmlObject().getThemeElements().getFmtScheme();
+ CTEffectStyleItem ef = styleMatrix.getEffectStyleLst().getEffectStyleArray(idx - 1);
+ obj = ef.getEffectLst().getOuterShdw();
+ }
}
}
return obj == null ? null : new XSLFShadow(obj, this);
}
+ @Override
public void draw(Graphics2D graphics) {
-
- }
-
- @SuppressWarnings("deprecation") // getXYZArray() array accessors are deprecated
- protected Paint getPaint(Graphics2D graphics, XmlObject spPr) {
- XSLFTheme theme = getSheet().getTheme();
- Rectangle2D anchor = getAnchor();
-
- Paint paint = null;
- for(XmlObject obj : spPr.selectPath("*")){
- if(obj instanceof CTNoFillProperties){
- paint = null;
- break;
- }
- if(obj instanceof CTSolidColorFillProperties){
- CTSolidColorFillProperties solidFill = (CTSolidColorFillProperties)obj;
- XSLFColor c = new XSLFColor(solidFill, theme);
- paint = c.getColor();
- }
- if(obj instanceof CTBlipFillProperties){
- CTBlipFillProperties blipFill = (CTBlipFillProperties)obj;
- CTBlip blip = blipFill.getBlip();
- String blipId = blip.getEmbed();
- PackagePart p = getSheet().getPackagePart();
- PackageRelationship rel = p.getRelationship(blipId);
- if (rel != null) {
- XSLFImageRendener renderer = null;
- if(graphics != null) renderer = (XSLFImageRendener)graphics.getRenderingHint(XSLFRenderingHint.IMAGE_RENDERER);
- if(renderer == null) renderer = new XSLFImageRendener();
-
- try {
- BufferedImage img = renderer.readImage(p.getRelatedPart(rel).getInputStream());
- if(blip.sizeOfAlphaModFixArray() > 0){
- float alpha = blip.getAlphaModFixArray(0).getAmt()/100000.f;
- AlphaComposite ac = AlphaComposite.getInstance(
- AlphaComposite.SRC_OVER, alpha);
- if(graphics != null) graphics.setComposite(ac);
- }
-
- paint = new TexturePaint(
- img, new Rectangle2D.Double(0, 0, img.getWidth(), img.getHeight()));
- }
- catch (Exception e) {
- return null;
- }
- }
- }
- if(obj instanceof CTGradientFillProperties){
- CTGradientFillProperties gradFill = (CTGradientFillProperties)obj;
- double angle;
- if(gradFill.isSetLin()) {
- angle = gradFill.getLin().getAng() / 60000;
- } else {
- // XSLF only supports linear gradient fills. Other types are filled as liner with angle=90 degrees
- angle = 90;
- }
- CTGradientStop[] gs = gradFill.getGsLst().getGsArray();
-
- Arrays.sort(gs, new Comparator<CTGradientStop>(){
- public int compare(CTGradientStop o1, CTGradientStop o2){
- Integer pos1 = o1.getPos();
- Integer pos2 = o2.getPos();
- return pos1.compareTo(pos2);
- }
- });
-
- Color[] colors = new Color[gs.length];
- float[] fractions = new float[gs.length];
-
- AffineTransform at = AffineTransform.getRotateInstance(
- Math.toRadians(angle),
- anchor.getX() + anchor.getWidth()/2,
- anchor.getY() + anchor.getHeight()/2);
-
- double diagonal = Math.sqrt(anchor.getHeight()*anchor.getHeight() + anchor.getWidth()*anchor.getWidth());
- Point2D p1 = new Point2D.Double(anchor.getX() + anchor.getWidth()/2 - diagonal/2,
- anchor.getY() + anchor.getHeight()/2);
- p1 = at.transform(p1, null);
-
- Point2D p2 = new Point2D.Double(anchor.getX() + anchor.getWidth(), anchor.getY() + anchor.getHeight()/2);
- p2 = at.transform(p2, null);
-
- norm(p1, anchor);
- norm(p2, anchor);
-
- for(int i = 0; i < gs.length; i++){
- CTGradientStop stop = gs[i];
- colors[i] = new XSLFColor(stop, theme).getColor();
- fractions[i] = stop.getPos() / 100000.f;
- }
-
- paint = createGradientPaint(p1, p2, fractions, colors);
- }
- }
- return paint;
- }
-
- /**
- * Trick to return GradientPaint on JDK 1.5 and LinearGradientPaint on JDK 1.6+
- */
- private Paint createGradientPaint(Point2D p1, Point2D p2, float[] fractions, Color[] colors){
- Paint paint;
- try {
- Class clz = Class.forName("java.awt.LinearGradientPaint");
- Constructor c =
- clz.getConstructor(Point2D.class, Point2D.class, float[].class, Color[].class);
- paint = (Paint)c.newInstance(p1, p2, fractions, colors);
- } catch (ClassNotFoundException e){
- paint = new GradientPaint(p1, colors[0], p2, colors[colors.length - 1]);
- } catch (Exception e){
- throw new RuntimeException(e);
- }
- return paint;
- }
-
- void norm(Point2D p, Rectangle2D anchor){
- if(p.getX() < anchor.getX()){
- p.setLocation(anchor.getX(), p.getY());
- } else if(p.getX() > (anchor.getX() + anchor.getWidth())){
- p.setLocation(anchor.getX() + anchor.getWidth(), p.getY());
- }
-
- if(p.getY() < anchor.getY()){
- p.setLocation(p.getX(), anchor.getY());
- } else if (p.getY() > (anchor.getY() + anchor.getHeight())){
- p.setLocation(p.getX(), anchor.getY() + anchor.getHeight());
- }
- }
-
- protected float[] getDashPattern(LineDash lineDash, float lineWidth) {
- float[] dash = null;
- switch (lineDash) {
- case SYS_DOT:
- dash = new float[]{lineWidth, lineWidth};
- break;
- case SYS_DASH:
- dash = new float[]{2 * lineWidth, 2 * lineWidth};
- break;
- case DASH:
- dash = new float[]{3 * lineWidth, 4 * lineWidth};
- break;
- case DASH_DOT:
- dash = new float[]{4 * lineWidth, 3 * lineWidth, lineWidth,
- 3 * lineWidth};
- break;
- case LG_DASH:
- dash = new float[]{8 * lineWidth, 3 * lineWidth};
- break;
- case LG_DASH_DOT:
- dash = new float[]{8 * lineWidth, 3 * lineWidth, lineWidth,
- 3 * lineWidth};
- break;
- case LG_DASH_DOT_DOT:
- dash = new float[]{8 * lineWidth, 3 * lineWidth, lineWidth,
- 3 * lineWidth, lineWidth, 3 * lineWidth};
- break;
- }
- return dash;
+ RenderableShape rShape = new RenderableShape(this);
+ rShape.render(graphics);
}
- protected void applyStroke(Graphics2D graphics) {
-
- float lineWidth = (float) getLineWidth();
- LineDash lineDash = getLineDash();
- float[] dash = null;
- float dash_phase = 0;
- if (lineDash != null) {
- dash = getDashPattern(lineDash, lineWidth);
- }
-
- int cap = BasicStroke.CAP_BUTT;
- LineCap lineCap = getLineCap();
- if (lineCap != null) {
- switch (lineCap) {
- case ROUND:
- cap = BasicStroke.CAP_ROUND;
- break;
- case SQUARE:
- cap = BasicStroke.CAP_SQUARE;
- break;
- default:
- cap = BasicStroke.CAP_BUTT;
- break;
- }
- }
-
- int meter = BasicStroke.JOIN_ROUND;
-
- Stroke stroke = new BasicStroke(lineWidth, cap, meter, Math.max(1, lineWidth), dash,
- dash_phase);
- graphics.setStroke(stroke);
- }
/**
- * Walk up the inheritance tree and fetch properties.
- *
- * slide <-- slideLayout <-- slideMaster
+ * Walk up the inheritance tree and fetch shape properties.
*
+ * The following order of inheritance is assumed:
+ * <p>
+ * slide <-- slideLayout <-- slideMaster <-- default styles in the slideMaster
+ * </p>
*
- * @param visitor the object that collects the desired property
+ * @param visitor the object that collects the desired property
* @return true if the property was fetched
*/
- boolean fetchShapeProperty(PropertyFetcher visitor){
+ boolean fetchShapeProperty(PropertyFetcher visitor) {
boolean ok = visitor.fetch(this);
XSLFSimpleShape masterShape;
- if(!ok){
-
- // first try to fetch from the slide layout
- XSLFSlideLayout layout = getSheet().getSlideLayout();
- if(layout != null) {
+ XSLFSheet layout = getSheet().getMasterSheet();
+ if (layout != null) {
+ if (!ok) {
+ // first try to fetch from the slide layout
CTPlaceholder ph = getCTPlaceholder();
if (ph != null) {
masterShape = layout.getPlaceholder(ph);
@@ -781,96 +567,77 @@ public abstract class XSLFSimpleShape extends XSLFShape { }
}
}
- }
- // try slide master
- if (!ok) {
- int textType;
- CTPlaceholder ph = getCTPlaceholder();
- if(ph == null || !ph.isSetType()) textType = STPlaceholderType.INT_BODY;
- else {
- switch(ph.getType().intValue()){
- case STPlaceholderType.INT_TITLE:
- case STPlaceholderType.INT_CTR_TITLE:
- textType = STPlaceholderType.INT_TITLE;
- break;
- case STPlaceholderType.INT_FTR:
- case STPlaceholderType.INT_SLD_NUM:
- case STPlaceholderType.INT_DT:
- textType = ph.getType().intValue();
- break;
- default:
- textType = STPlaceholderType.INT_BODY;
- break;
+ // try slide master
+ if (!ok) {
+ int textType;
+ CTPlaceholder ph = getCTPlaceholder();
+ if (ph == null || !ph.isSetType()) textType = STPlaceholderType.INT_BODY;
+ else {
+ switch (ph.getType().intValue()) {
+ case STPlaceholderType.INT_TITLE:
+ case STPlaceholderType.INT_CTR_TITLE:
+ textType = STPlaceholderType.INT_TITLE;
+ break;
+ case STPlaceholderType.INT_FTR:
+ case STPlaceholderType.INT_SLD_NUM:
+ case STPlaceholderType.INT_DT:
+ textType = ph.getType().intValue();
+ break;
+ default:
+ textType = STPlaceholderType.INT_BODY;
+ break;
+ }
}
- }
- XSLFSlideMaster master = getSheet().getSlideMaster();
- if(master != null) {
- masterShape = master.getPlaceholderByType(textType);
- if (masterShape != null) {
- ok = visitor.fetch(masterShape);
+ XSLFSheet master = layout.getMasterSheet();
+ if (master != null) {
+ masterShape = master.getPlaceholderByType(textType);
+ if (masterShape != null) {
+ ok = visitor.fetch(masterShape);
+ }
}
}
}
-
return ok;
}
-
- @Override
- protected java.awt.Shape getOutline(){
- PresetGeometries dict = PresetGeometries.getInstance();
+ /**
+ *
+ * @return definition of the shape geometry
+ */
+ CustomGeometry getGeometry(){
CTShapeProperties spPr = getSpPr();
- String name;
- if(spPr.isSetPrstGeom()) {
- name = spPr.getPrstGeom().getPrst().toString();
+ CustomGeometry geom;
+ PresetGeometries dict = PresetGeometries.getInstance();
+ if(spPr.isSetPrstGeom()){
+ String name = spPr.getPrstGeom().getPrst().toString();
+ geom = dict.get(name);
+ if(geom == null) {
+ throw new IllegalStateException("Unknown shape geometry: " + name);
+ }
+ } else if (spPr.isSetCustGeom()){
+ geom = new CustomGeometry(spPr.getCustGeom());
} else {
- name = "rect";
+ geom = dict.get("rect");
}
- CustomGeometry geom = dict.get(name);
- Rectangle2D anchor = getAnchor();
- if(geom != null) {
- // the guides in the shape definitions are all defined relative to each other,
- // so we build the path starting from (0,0).
- final Rectangle2D anchorEmu = new Rectangle2D.Double(
- 0,
- 0,
- Units.toEMU(anchor.getWidth()),
- Units.toEMU(anchor.getHeight())
- );
-
- GeneralPath path = new GeneralPath();
- Context ctx = new Context(geom, new IAdjustableShape() {
- public Rectangle2D getAnchor() {
- return anchorEmu;
- }
+ return geom;
+ }
- public Guide getAdjustValue(String name) {
- CTPresetGeometry2D prst = getSpPr().getPrstGeom();
- if(prst.isSetAvLst()) {
- for(CTGeomGuide g : prst.getAvLst().getGdList()){
- if(g.getName().equals(name)) {
- return new Guide(g);
- }
- }
- }
- return null;
- }
- });
- for(Path p : geom){
- path.append( p.getPath(ctx) , false);
- }
+ /**
+ * @return any shape-specific geometry that is not included in presetShapeDefinitions.xml
+ * (line decorations, etc)
+ */
+ List<Outline> getCustomOutlines(){
+ return Collections.emptyList();
+ }
- // translate the result to the canvas coordinates in points
- AffineTransform at = new AffineTransform();
- at.scale(
- 1.0/Units.EMU_PER_POINT, 1.0/Units.EMU_PER_POINT);
- at.translate(Units.toEMU(anchor.getX()), Units.toEMU(anchor.getY()));
- return at.createTransformedShape(path);
- } else {
- return anchor;
- }
- }
+ /**
+ * draw any content within this shape (image, text, etc.).
+ *
+ * @param graphics the graphics to draw into
+ */
+ public void drawContent(Graphics2D graphics){
+ }
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java index f6f92d4f59..598baaeee4 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java @@ -32,7 +32,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShapeNonVisua import org.openxmlformats.schemas.presentationml.x2006.main.CTSlide; import org.openxmlformats.schemas.presentationml.x2006.main.SldDocument; -import java.awt.*; +import java.awt.Graphics2D; import java.io.IOException; @Beta @@ -109,11 +109,11 @@ public final class XSLFSlide extends XSLFSheet { return "sld"; } - public XSLFSlideMaster getMasterSheet(){ - return getSlideLayout().getSlideMaster(); + @Override + public XSLFSlideLayout getMasterSheet(){ + return getSlideLayout(); } - @Override public XSLFSlideLayout getSlideLayout(){ if(_layout == null){ for (POIXMLDocumentPart p : getRelations()) { @@ -128,7 +128,6 @@ public final class XSLFSlide extends XSLFSheet { return _layout; } - @Override public XSLFSlideMaster getSlideMaster(){ return getSlideLayout().getSlideMaster(); } @@ -159,20 +158,12 @@ public final class XSLFSlide extends XSLFSheet { } if(_notes == null) { // This slide lacks notes - // Not al have them, sorry... + // Not all have them, sorry... return null; } return _notes; } - public void setFollowMasterBackground(boolean value){ - _slide.setShowMasterSp(value); - } - - public boolean getFollowMasterBackground(){ - return !_slide.isSetShowMasterSp() || _slide.getShowMasterSp(); - } - /** * * @return title of this slide or empty string if title is not set @@ -182,27 +173,59 @@ public final class XSLFSlide extends XSLFSheet { return txt == null ? "" : txt.getText(); } + @Override public XSLFTheme getTheme(){ return getSlideLayout().getSlideMaster().getTheme(); } - @Override - public void draw(Graphics2D graphics){ - - if (getFollowMasterBackground()){ - XSLFSlideLayout layout = getSlideLayout(); - layout.draw(graphics); - } + /** + * + * @return the information about background appearance of this slide + */ + public XSLFBackground getBackground() { - super.draw(graphics); - } - @Override - public XSLFBackground getBackground(){ if(_slide.getCSld().isSetBg()) { return new XSLFBackground(_slide.getCSld().getBg(), this); } + + XSLFSlideLayout layout = getMasterSheet(); + if(layout.getXmlObject().getCSld().isSetBg()) { + return new XSLFBackground(layout.getXmlObject().getCSld().getBg(), this); + } + + XSLFSlideMaster master = layout.getMasterSheet(); + if(master.getXmlObject().getCSld().isSetBg()) { + return new XSLFBackground(master.getXmlObject().getCSld().getBg(), this); + } return null; } + /** + * + * @return whether shapes on the master slide should be shown or not. + */ + public boolean getFollowMasterGraphics(){ + return !_slide.isSetShowMasterSp() || _slide.getShowMasterSp(); + } + + /** + * + * @param value whether shapes on the master slide should be shown or not. + */ + public void setFollowMasterGraphics(boolean value){ + _slide.setShowMasterSp(value); + } + + + @Override + public void draw(Graphics2D graphics){ + + XSLFBackground bg = getBackground(); + if(bg != null) bg.draw(graphics); + + super.draw(graphics); + } + + } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java index 699d153345..4a505972dc 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java @@ -22,7 +22,6 @@ import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.util.Beta; import org.apache.poi.util.Internal; import org.apache.xmlbeans.XmlException; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideLayout; import org.openxmlformats.schemas.presentationml.x2006.main.SldLayoutDocument; @@ -42,13 +41,13 @@ public class XSLFSlideLayout extends XSLFSheet { public XSLFSlideLayout(PackagePart part, PackageRelationship rel) throws IOException, XmlException { super(part, rel); SldLayoutDocument doc = - SldLayoutDocument.Factory.parse(getPackagePart().getInputStream()); + SldLayoutDocument.Factory.parse(getPackagePart().getInputStream()); _layout = doc.getSldLayout(); setCommonSlideData(_layout.getCSld()); } - public String getName(){ + public String getName() { return _layout.getCSld().getName(); } @@ -61,79 +60,92 @@ public class XSLFSlideLayout extends XSLFSheet { } @Override - protected String getRootElementName(){ + protected String getRootElementName() { return "sldLayout"; } /** * Slide master object associated with this layout. - * <p> - * Within a slide master slide are contained all elements - * that describe the objects and their corresponding formatting - * for within a presentation slide. - * </p> - * <p> - * Within a slide master slide are two main elements. - * The cSld element specifies the common slide elements such as shapes and - * their attached text bodies. Then the txStyles element specifies the - * formatting for the text within each of these shapes. The other properties - * within a slide master slide specify other properties for within a presentation slide - * such as color information, headers and footers, as well as timing and - * transition information for all corresponding presentation slides. - * </p> * * @return slide master. Never null. * @throws IllegalStateException if slide master was not found */ - @Override - public XSLFSlideMaster getSlideMaster(){ - if(_master == null){ + public XSLFSlideMaster getSlideMaster() { + if (_master == null) { for (POIXMLDocumentPart p : getRelations()) { - if (p instanceof XSLFSlideMaster){ - _master = (XSLFSlideMaster)p; - } - } + if (p instanceof XSLFSlideMaster) { + _master = (XSLFSlideMaster) p; + } + } } - if(_master == null) { + if (_master == null) { throw new IllegalStateException("SlideMaster was not found for " + this.toString()); } return _master; } - - public XSLFTheme getTheme(){ - return getSlideMaster().getTheme(); - } + @Override + public XSLFSlideMaster getMasterSheet() { + return getSlideMaster(); + } + + @Override + public XSLFTheme getTheme() { + return getSlideMaster().getTheme(); + } @Override - protected CTTextListStyle getTextProperties(Placeholder textType) { - XSLFTextShape lp = getTextShapeByType(textType); - CTTextListStyle props = lp.getTextBody(false).getLstStyle(); - return props; + public boolean getFollowMasterGraphics() { + return !_layout.isSetShowMasterSp() || _layout.getShowMasterSp(); } /** * Render this sheet into the supplied graphics object - * */ @Override - protected boolean canDraw(XSLFShape shape){ - if(shape instanceof XSLFSimpleShape){ - XSLFSimpleShape txt = (XSLFSimpleShape)shape; + protected boolean canDraw(XSLFShape shape) { + if (shape instanceof XSLFSimpleShape) { + XSLFSimpleShape txt = (XSLFSimpleShape) shape; CTPlaceholder ph = txt.getCTPlaceholder(); - if(ph != null) { + if (ph != null) { return false; } } return true; } - @Override - public XSLFBackground getBackground(){ - if(_layout.getCSld().isSetBg()) { - return new XSLFBackground(_layout.getCSld().getBg(), this); + /** + * Copy placeholders from this layout to the destination slide + * + * @param slide destination slide + */ + public void copyLayout(XSLFSlide slide) { + for (XSLFShape sh : getShapes()) { + if (sh instanceof XSLFTextShape) { + XSLFTextShape tsh = (XSLFTextShape) sh; + Placeholder ph = tsh.getTextType(); + if (ph == null) continue; + + switch (ph) { + // these are special and not copied by default + case DATETIME: + case SLIDE_NUMBER: + case FOOTER: + break; + default: + slide.getSpTree().addNewSp().set(tsh.getXmlObject().copy()); + } + } } - return getSlideMaster().getBackground(); + } + + /** + * + * @return type of this layout + */ + public SlideLayout getType(){ + int ordinal = _layout.getType().intValue() - 1; + return SlideLayout.values()[ordinal]; } }
\ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideMaster.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideMaster.java index 2ec83609a8..e73ee7a4c1 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideMaster.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideMaster.java @@ -21,7 +21,9 @@ import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.util.Beta; import org.apache.xmlbeans.XmlException; +import org.openxmlformats.schemas.drawingml.x2006.main.CTColorMapping; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle; +import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMaster; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterTextStyles; import org.openxmlformats.schemas.presentationml.x2006.main.SldMasterDocument; @@ -78,7 +80,12 @@ import java.util.Map; return "sldMaster"; } - public XSLFSlideLayout getLayout(String name){ + @Override + public XSLFSheet getMasterSheet() { + return null; + } + + private Map<String, XSLFSlideLayout> getLayouts(){ if(_layouts == null){ _layouts = new HashMap<String, XSLFSlideLayout>(); for (POIXMLDocumentPart p : getRelations()) { @@ -88,14 +95,36 @@ import java.util.Map; } } } - return _layouts.get(name); + return _layouts; } + /** + * + * @return all slide layouts referencing this master + */ + public XSLFSlideLayout[] getSlideLayouts() { + return getLayouts().values().toArray(new XSLFSlideLayout[_layouts.size()]); + } + + public XSLFSlideLayout getLayout(SlideLayout type){ + for(XSLFSlideLayout layout : getLayouts().values()){ + if(layout.getType() == type) { + return layout; + } + } + return null; + } + + @Override public XSLFTheme getTheme(){ if(_theme == null){ for (POIXMLDocumentPart p : getRelations()) { if (p instanceof XSLFTheme){ _theme = (XSLFTheme)p; + CTColorMapping cmap = _slide.getClrMap(); + if(cmap != null){ + _theme.initColorMap(cmap); + } break; } } @@ -122,11 +151,20 @@ import java.util.Map; return props; } + /** + * Render this sheet into the supplied graphics object + * + */ @Override - public XSLFBackground getBackground(){ - if(_slide.getCSld().isSetBg()) { - return new XSLFBackground(_slide.getCSld().getBg(), this); + protected boolean canDraw(XSLFShape shape){ + if(shape instanceof XSLFSimpleShape){ + XSLFSimpleShape txt = (XSLFSimpleShape)shape; + CTPlaceholder ph = txt.getCTPlaceholder(); + if(ph != null) { + return false; + } } - return null; + return true; } + }
\ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java index 335e77d593..f3d4400aa0 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java @@ -35,7 +35,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndWidth; import org.openxmlformats.schemas.drawingml.x2006.main.STPenAlignment;
import org.openxmlformats.schemas.drawingml.x2006.main.STPresetLineDashVal;
-import java.awt.*;
+import java.awt.Color;
/**
* Represents a cell of a table in a .pptx presentation
@@ -77,7 +77,7 @@ public class XSLFTableCell extends XSLFTextShape { }
@Override
- public void setMarginLeft(double margin){
+ public void setLeftInset(double margin){
CTTableCellProperties pr = getXmlObject().getTcPr();
if(pr == null) pr = getXmlObject().addNewTcPr();
@@ -85,7 +85,7 @@ public class XSLFTableCell extends XSLFTextShape { }
@Override
- public void setMarginRight(double margin){
+ public void setRightInset(double margin){
CTTableCellProperties pr = getXmlObject().getTcPr();
if(pr == null) pr = getXmlObject().addNewTcPr();
@@ -93,7 +93,7 @@ public class XSLFTableCell extends XSLFTextShape { }
@Override
- public void setMarginTop(double margin){
+ public void setTopInset(double margin){
CTTableCellProperties pr = getXmlObject().getTcPr();
if(pr == null) pr = getXmlObject().addNewTcPr();
@@ -101,7 +101,7 @@ public class XSLFTableCell extends XSLFTextShape { }
@Override
- public void setMarginBottom(double margin){
+ public void setBottomInset(double margin){
CTTableCellProperties pr = getXmlObject().getTcPr();
if(pr == null) pr = getXmlObject().addNewTcPr();
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextBox.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextBox.java index 177e8099eb..ef523b4fc8 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextBox.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextBox.java @@ -63,23 +63,4 @@ public class XSLFTextBox extends XSLFAutoShape { return ct;
}
-
- /**
- * Specifies that the corresponding shape should be represented by the generating application
- * as a placeholder. When a shape is considered a placeholder by the generating application
- * it can have special properties to alert the user that they may enter content into the shape.
- * Different types of placeholders are allowed and can be specified by using the placeholder
- * type attribute for this element
- *
- * @param placeholder
- */
- public void setPlaceholder(Placeholder placeholder){
- CTShape sh = (CTShape)getXmlObject();
- CTApplicationNonVisualDrawingProps nv = sh.getNvSpPr().getNvPr();
- if(placeholder == null) {
- if(nv.isSetPh()) nv.unsetPh();
- } else {
- nv.addNewPh().setType(STPlaceholderType.Enum.forInt(placeholder.ordinal() + 1));
- }
- }
}
\ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java index a67dc0d34a..7134246bbb 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java @@ -30,7 +30,8 @@ import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;
-import java.awt.*;
+import java.awt.Color;
+import java.awt.Graphics2D;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
@@ -80,6 +81,23 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ return out.toString();
}
+ private String getVisibleText(){
+ StringBuilder out = new StringBuilder();
+ for (XSLFTextRun r : _runs) {
+ String txt = r.getText();
+ switch (r.getTextCap()){
+ case ALL:
+ txt = txt.toUpperCase();
+ break;
+ case SMALL:
+ txt = txt.toLowerCase();
+ break;
+ }
+ out.append(txt);
+ }
+ return out.toString();
+ }
+
@Internal
public CTTextParagraph getXmlObject(){
return _p;
@@ -89,6 +107,7 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ return _shape;
}
+
public List<XSLFTextRun> getTextRuns(){
return _runs;
}
@@ -186,7 +205,7 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ ParagraphPropertyFetcher<Color> fetcher = new ParagraphPropertyFetcher<Color>(getLevel()){
public boolean fetch(CTTextParagraphProperties props){
if(props.isSetBuClr()){
- XSLFColor c = new XSLFColor(props.getBuClr(), theme);
+ XSLFColor c = new XSLFColor(props.getBuClr(), theme, null);
setValue(c.getColor());
return true;
}
@@ -496,17 +515,18 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ return "[" + getClass() + "]" + getText();
}
- public List<TextFragment> getTextLines(){
+ List<TextFragment> getTextLines(){
return _lines;
}
public double draw(Graphics2D graphics, double x, double y){
- double marginLeft = _shape.getMarginLeft();
- double marginRight = _shape.getMarginRight();
+ double marginLeft = _shape.getLeftInset();
+ double marginRight = _shape.getRightInset();
Rectangle2D anchor = _shape.getAnchor();
double penY = y;
double textOffset = getLeftMargin();
+ boolean firstLine = true;
for(TextFragment line : _lines){
double penX = x;
switch (getTextAlign()) {
@@ -521,7 +541,7 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ break;
}
- if(_bullet != null){
+ if(_bullet != null && firstLine){
_bullet.draw(graphics, penX + getIndent(), penY);
}
line.draw(graphics, penX, penY);
@@ -535,6 +555,8 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ // positive value means absolute spacing in points
penY += -spacing;
}
+
+ firstLine = false;
}
return penY - y;
}
@@ -551,8 +573,12 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ void draw(Graphics2D graphics, double x, double y){
double yBaseline = y + _layout.getAscent();
- graphics.drawString(_str.getIterator(), (float)x, (float)yBaseline );
-
+ Integer textMode = (Integer)graphics.getRenderingHint(XSLFRenderingHint.TEXT_RENDERING_MODE);
+ if(textMode != null && textMode == XSLFRenderingHint.TEXT_MODE_GLYPHS){
+ _layout.draw(graphics, (float)x, (float)yBaseline);
+ } else {
+ graphics.drawString(_str.getIterator(), (float)x, (float)yBaseline );
+ }
}
public float getHeight(){
@@ -564,8 +590,8 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ }
- public AttributedString getAttributedString(){
- String text = getText();
+ AttributedString getAttributedString(Graphics2D graphics){
+ String text = getVisibleText();
AttributedString string = new AttributedString(text);
@@ -579,7 +605,10 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ int endIndex = startIndex + length;
string.addAttribute(TextAttribute.FOREGROUND, run.getFontColor(), startIndex, endIndex);
+
+ // user can pass an object to convert fonts via a rendering hint
string.addAttribute(TextAttribute.FAMILY, run.getFontFamily(), startIndex, endIndex);
+
string.addAttribute(TextAttribute.SIZE, (float)run.getFontSize(), startIndex, endIndex);
if(run.isBold()) {
string.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, startIndex, endIndex);
@@ -601,6 +630,7 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ string.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, startIndex, endIndex);
}
+
startIndex = endIndex;
}
@@ -610,7 +640,7 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ void breakText(Graphics2D graphics){
_lines = new ArrayList<TextFragment>();
- AttributedString at = getAttributedString();
+ AttributedString at = getAttributedString(graphics);
AttributedCharacterIterator it = at.getIterator();
if(it.getBeginIndex() == it.getEndIndex()) {
return;
@@ -628,7 +658,8 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ int endIndex = measurer.getPosition();
- if(getTextAlign() == TextAlign.JUSTIFY) {
+ TextAlign hAlign = getTextAlign();
+ if(hAlign == TextAlign.JUSTIFY || hAlign == TextAlign.JUSTIFY_LOW) {
layout = layout.getJustifiedLayout((float)wrappingWidth);
}
@@ -673,7 +704,7 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ width = _shape.getSheet().getSlideShow().getPageSize().getWidth();
} else {
width = _shape.getAnchor().getWidth() -
- _shape.getMarginLeft() - _shape.getMarginRight() - getLeftMargin();
+ _shape.getLeftInset() - _shape.getRightInset() - getLeftMargin();
}
return width;
}
@@ -700,7 +731,13 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ }
int level = getLevel();
- XmlObject[] o = _shape.getSheet().getSlideMaster().getXmlObject().selectPath(
+
+ XSLFSheet masterSheet = _shape.getSheet();
+ while (masterSheet.getMasterSheet() != null){
+ masterSheet = masterSheet.getMasterSheet();
+ }
+
+ XmlObject[] o = masterSheet.getXmlObject().selectPath(
"declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
"declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +
".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr");
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java index e4ebf9db58..b4aa3ca8bf 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java @@ -20,14 +20,17 @@ import org.apache.poi.util.Beta; import org.apache.poi.xslf.model.CharacterPropertyFetcher;
import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.STTextStrikeType;
import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType;
-import java.awt.*;
+import java.awt.Color;
/**
* Represents a run of text within the containing text body. The run element is the
@@ -70,12 +73,14 @@ public class XSLFTextRun { public Color getFontColor(){
final XSLFTheme theme = _p.getParentShape().getSheet().getTheme();
+ CTShapeStyle style = _p.getParentShape().getSpStyle();
+ final CTSchemeColor shapeStyle = style == null ? null : style.getFontRef().getSchemeClr();
CharacterPropertyFetcher<Color> fetcher = new CharacterPropertyFetcher<Color>(_p.getLevel()){
public boolean fetch(CTTextCharacterProperties props){
CTSolidColorFillProperties solidFill = props.getSolidFill();
- if(solidFill != null){
- Color c = new XSLFColor(solidFill, theme).getColor();
+ if(solidFill != null) {
+ Color c = new XSLFColor(solidFill, theme, shapeStyle).getColor();
setValue(c);
return true;
}
@@ -104,6 +109,10 @@ public class XSLFTextRun { * @return font size in points or -1 if font size is not set.
*/
public double getFontSize(){
+ double scale = 1;
+ CTTextNormalAutofit afit = getParentParagraph().getParentShape().getTextBodyPr().getNormAutofit();
+ if(afit != null) scale = (double)afit.getFontScale() / 100000;
+
CharacterPropertyFetcher<Double> fetcher = new CharacterPropertyFetcher<Double>(_p.getLevel()){
public boolean fetch(CTTextCharacterProperties props){
if(props.isSetSz()){
@@ -114,7 +123,27 @@ public class XSLFTextRun { }
};
fetchCharacterProperty(fetcher);
- return fetcher.getValue() == null ? -1 : fetcher.getValue();
+ return fetcher.getValue() == null ? -1 : fetcher.getValue()*scale;
+ }
+
+ /**
+ *
+ * @return the spacing between characters within a text run,
+ * If this attribute is omitted than a value of 0 or no adjustment is assumed.
+ */
+ public double getCharacterSpacing(){
+
+ CharacterPropertyFetcher<Double> fetcher = new CharacterPropertyFetcher<Double>(_p.getLevel()){
+ public boolean fetch(CTTextCharacterProperties props){
+ if(props.isSetSpc()){
+ setValue(props.getSpc()*0.01);
+ return true;
+ }
+ return false;
+ }
+ };
+ fetchCharacterProperty(fetcher);
+ return fetcher.getValue() == null ? 0 : fetcher.getValue();
}
/**
@@ -235,6 +264,24 @@ public class XSLFTextRun { }
/**
+ * @return whether a run of text will be formatted as a superscript text. Default is false.
+ */
+ public TextCap getTextCap() {
+ CharacterPropertyFetcher<TextCap> fetcher = new CharacterPropertyFetcher<TextCap>(_p.getLevel()){
+ public boolean fetch(CTTextCharacterProperties props){
+ if(props.isSetCap()){
+ int idx = props.getCap().intValue() - 1;
+ setValue(TextCap.values()[idx]);
+ return true;
+ }
+ return false;
+ }
+ };
+ fetchCharacterProperty(fetcher);
+ return fetcher.getValue() == null ? TextCap.NONE : fetcher.getValue();
+ }
+
+ /**
* Specifies whether this run of text will be formatted as bold text
*
* @param bold whether this run of text will be formatted as bold text
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java index 279cac9198..dd49c316a0 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java @@ -30,12 +30,16 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; import org.openxmlformats.schemas.drawingml.x2006.main.STTextAnchoringType; import org.openxmlformats.schemas.drawingml.x2006.main.STTextVerticalType; import org.openxmlformats.schemas.drawingml.x2006.main.STTextWrappingType; +import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; +import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; +import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; -import java.awt.*; +import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; /** @@ -44,7 +48,7 @@ import java.util.List; * @author Yegor Kozlov */ @Beta -public abstract class XSLFTextShape extends XSLFSimpleShape { +public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable<XSLFTextParagraph>{ private final List<XSLFTextParagraph> _paragraphs; /*package*/ XSLFTextShape(XmlObject shape, XSLFSheet sheet) { @@ -59,7 +63,14 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { } } - // textual properties + public Iterator<XSLFTextParagraph> iterator(){ + return _paragraphs.iterator(); + } + + /** + * + * @return text contained within this shape or empty string + */ public String getText() { StringBuilder out = new StringBuilder(); for (XSLFTextParagraph p : _paragraphs) { @@ -69,10 +80,34 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { return out.toString(); } + /** + * unset text from this shape + */ + public void clearText(){ + _paragraphs.clear(); + CTTextBody txBody = getTextBody(true); + txBody.setPArray(null); // remove any existing paragraphs + } + + public void setText(String text){ + clearText(); + + addNewTextParagraph().addNewTextRun().setText(text); + } + + /** + * + * @return text paragraphs in this shape + */ public List<XSLFTextParagraph> getTextParagraphs() { return _paragraphs; } + /** + * add a new paragraph run to this shape + * + * @return created paragraph run + */ public XSLFTextParagraph addNewTextParagraph() { CTTextBody txBody = getTextBody(true); CTTextParagraph p = txBody.addNewP(); @@ -84,9 +119,9 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { /** * Sets the type of vertical alignment for the text. - * One of the <code>Anchor*</code> constants defined in this class. * - * @param anchor - the type of alignment. Default is {@link org.apache.poi.xslf.usermodel.VerticalAlignment#TOP} + * @param anchor - the type of alignment. + * A <code>null</code> values unsets this property. */ public void setVerticalAlignment(VerticalAlignment anchor){ CTTextBodyProperties bodyPr = getTextBodyPr(); @@ -102,7 +137,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { /** * Returns the type of vertical alignment for the text. * - * @return the type of alignment + * @return the type of vertical alignment */ public VerticalAlignment getVerticalAlignment(){ PropertyFetcher<VerticalAlignment> fetcher = new TextBodyPropertyFetcher<VerticalAlignment>(){ @@ -147,13 +182,15 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { } return TextDirection.HORIZONTAL; } + + /** * Returns the distance (in points) between the bottom of the text frame * and the bottom of the inscribed rectangle of the shape that contains the text. * - * @return the bottom margin or -1 if not set + * @return the bottom inset in points */ - public double getMarginBottom(){ + public double getBottomInset(){ PropertyFetcher<Double> fetcher = new TextBodyPropertyFetcher<Double>(){ public boolean fetch(CTTextBodyProperties props){ if(props.isSetBIns()){ @@ -173,9 +210,9 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { * and the left edge of the inscribed rectangle of the shape that contains * the text. * - * @return the left margin + * @return the left inset in points */ - public double getMarginLeft(){ + public double getLeftInset(){ PropertyFetcher<Double> fetcher = new TextBodyPropertyFetcher<Double>(){ public boolean fetch(CTTextBodyProperties props){ if(props.isSetLIns()){ @@ -195,9 +232,9 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { * text frame and the right edge of the inscribed rectangle of the shape * that contains the text. * - * @return the right margin + * @return the right inset in points */ - public double getMarginRight(){ + public double getRightInset(){ PropertyFetcher<Double> fetcher = new TextBodyPropertyFetcher<Double>(){ public boolean fetch(CTTextBodyProperties props){ if(props.isSetRIns()){ @@ -216,9 +253,9 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { * Returns the distance (in points) between the top of the text frame * and the top of the inscribed rectangle of the shape that contains the text. * - * @return the top margin + * @return the top inset in points */ - public double getMarginTop(){ + public double getTopInset(){ PropertyFetcher<Double> fetcher = new TextBodyPropertyFetcher<Double>(){ public boolean fetch(CTTextBodyProperties props){ if(props.isSetTIns()){ @@ -235,11 +272,11 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { /** * Sets the botom margin. - * @see #getMarginBottom() + * @see #getBottomInset() * * @param margin the bottom margin */ - public void setMarginBottom(double margin){ + public void setBottomInset(double margin){ CTTextBodyProperties bodyPr = getTextBodyPr(); if (bodyPr != null) { if(margin == -1) bodyPr.unsetBIns(); @@ -249,11 +286,11 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { /** * Sets the left margin. - * @see #getMarginLeft() + * @see #getLeftInset() * * @param margin the left margin */ - public void setMarginLeft(double margin){ + public void setLeftInset(double margin){ CTTextBodyProperties bodyPr = getTextBodyPr(); if (bodyPr != null) { if(margin == -1) bodyPr.unsetLIns(); @@ -263,11 +300,11 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { /** * Sets the right margin. - * @see #getMarginRight() + * @see #getRightInset() * * @param margin the right margin */ - public void setMarginRight(double margin){ + public void setRightInset(double margin){ CTTextBodyProperties bodyPr = getTextBodyPr(); if (bodyPr != null) { if(margin == -1) bodyPr.unsetRIns(); @@ -277,11 +314,11 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { /** * Sets the top margin. - * @see #getMarginTop() + * @see #getTopInset() * * @param margin the top margin */ - public void setMarginTop(double margin){ + public void setTopInset(double margin){ CTTextBodyProperties bodyPr = getTextBodyPr(); if (bodyPr != null) { if(margin == -1) bodyPr.unsetTIns(); @@ -291,10 +328,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { /** - * Returns the value indicating word wrap. - * One of the <code>Wrap*</code> constants defined in this class. - * - * @return the value indicating word wrap + * @return whether to wrap words within the bounding rectangle */ public boolean getWordWrap(){ PropertyFetcher<Boolean> fetcher = new TextBodyPropertyFetcher<Boolean>(){ @@ -311,9 +345,8 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { } /** - * Specifies how the text should be wrapped * - * @param wrap the value indicating how the text should be wrapped + * @param wrap whether to wrap words within the bounding rectangle */ public void setWordWrap(boolean wrap){ CTTextBodyProperties bodyPr = getTextBodyPr(); @@ -381,33 +414,25 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { } } - @Override - public void draw(Graphics2D graphics){ - java.awt.Shape outline = getOutline(); - - // shadow - XSLFShadow shadow = getShadow(); - - Paint fill = getFill(graphics); - Paint line = getLinePaint(graphics); - if(shadow != null) { - shadow.draw(graphics); - } - - if(fill != null) { - graphics.setPaint(fill); - graphics.fill(outline); - } - if (line != null){ - graphics.setPaint(line); - applyStroke(graphics); - graphics.draw(outline); + /** + * Specifies that the corresponding shape should be represented by the generating application + * as a placeholder. When a shape is considered a placeholder by the generating application + * it can have special properties to alert the user that they may enter content into the shape. + * Different types of placeholders are allowed and can be specified by using the placeholder + * type attribute for this element + * + * @param placeholder + */ + public void setPlaceholder(Placeholder placeholder){ + CTShape sh = (CTShape)getXmlObject(); + CTApplicationNonVisualDrawingProps nv = sh.getNvSpPr().getNvPr(); + if(placeholder == null) { + if(nv.isSetPh()) nv.unsetPh(); + } else { + nv.addNewPh().setType(STPlaceholderType.Enum.forInt(placeholder.ordinal() + 1)); } - - // text - if(getText().length() > 0) drawText(graphics); - } + } /** * Compute the cumulative height occupied by the text @@ -418,15 +443,19 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { return drawParagraphs(img.createGraphics(), 0, 0); } - void breakText(Graphics2D graphics){ + /** + * break the contained text into lines + */ + private void breakText(Graphics2D graphics){ for(XSLFTextParagraph p : _paragraphs) p.breakText(graphics); } - public void drawText(Graphics2D graphics) { + @Override + public void drawContent(Graphics2D graphics) { breakText(graphics); Rectangle2D anchor = getAnchor(); - double x = anchor.getX() + getMarginLeft(); + double x = anchor.getX() + getLeftInset(); double y = anchor.getY(); // first dry-run to calculate the total height of the text @@ -434,21 +463,20 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { switch (getVerticalAlignment()){ case TOP: - y += getMarginTop(); + y += getTopInset(); break; case BOTTOM: - y += anchor.getHeight() - textHeight - getMarginBottom(); + y += anchor.getHeight() - textHeight - getBottomInset(); break; default: case MIDDLE: double delta = anchor.getHeight() - textHeight - - getMarginTop() - getMarginBottom(); - y += getMarginTop() + delta/2; + getTopInset() - getBottomInset(); + y += getTopInset() + delta/2; break; } drawParagraphs(graphics, x, y); - } @@ -461,7 +489,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape { double y0 = y; for(int i = 0; i < _paragraphs.size(); i++){ XSLFTextParagraph p = _paragraphs.get(i); - java.util.List<XSLFTextParagraph.TextFragment> lines = p.getTextLines(); + List<XSLFTextParagraph.TextFragment> lines = p.getTextLines(); if(i > 0 && lines.size() > 0) { // the amount of vertical white space before the paragraph diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java index 77dd0a8564..b996dd5fb4 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java @@ -26,6 +26,7 @@ import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.drawingml.x2006.main.CTBaseStyles; import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTColorMapping; import org.openxmlformats.schemas.drawingml.x2006.main.CTColorScheme; import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeStyleSheet; import org.openxmlformats.schemas.drawingml.x2006.main.ThemeDocument; @@ -36,6 +37,11 @@ import java.io.OutputStream; import java.util.HashMap; import java.util.Map; +/** + * A shared style sheet in a .pptx slide show + * + * @author Yegor Kozlov + */ @Beta public class XSLFTheme extends POIXMLDocumentPart { private CTOfficeStyleSheet _theme; @@ -64,17 +70,33 @@ public class XSLFTheme extends POIXMLDocumentPart { String name = c.getDomNode().getLocalName(); _schemeColors.put(name, c); } + } - _schemeColors.put("bg1", _schemeColors.get("lt1")); - _schemeColors.put("bg2", _schemeColors.get("lt2")); - _schemeColors.put("tx1", _schemeColors.get("dk1")); - _schemeColors.put("tx2", _schemeColors.get("dk2")); + /** + * re-map colors + * + * @param cmap color map defined in the master slide referencing this theme + */ + void initColorMap(CTColorMapping cmap) { + _schemeColors.put("bg1", _schemeColors.get(cmap.getBg1().toString())); + _schemeColors.put("bg2", _schemeColors.get(cmap.getBg2().toString())); + _schemeColors.put("tx1", _schemeColors.get(cmap.getTx1().toString())); + _schemeColors.put("tx2", _schemeColors.get(cmap.getTx2().toString())); } + /** + * + * @return name of this theme, e.g. "Office Theme" + */ public String getName(){ return _theme.getName(); } + /** + * Set name of this theme + * + * @param name name of this theme + */ public void setName(String name){ _theme.setName(name); } @@ -111,10 +133,20 @@ public class XSLFTheme extends POIXMLDocumentPart { out.close(); } + /** + * @return typeface of the major font to use in a document. + * Typically the major font is used for heading areas of a document. + * + */ public String getMajorFont(){ return _theme.getThemeElements().getFontScheme().getMajorFont().getLatin().getTypeface(); } + /** + * @return typeface of the minor font to use in a document. + * Typically the monor font is used for normal text or paragraph areas. + * + */ public String getMinorFont(){ return _theme.getThemeElements().getFontScheme().getMinorFont().getLatin().getTypeface(); } diff --git a/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java b/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java index f4e6955e6a..76bb08559f 100644 --- a/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java +++ b/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java @@ -28,21 +28,26 @@ import java.awt.Color; import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
-import java.io.File;
import java.io.FileOutputStream;
/**
- * Date: 10/11/11
+ * An utulity to convert slides of a .pptx slide show to a PNG image
*
* @author Yegor Kozlov
*/
public class PPTX2PNG {
+
+ static void usage(){
+ System.out.println("Usage: PPTX2PNG [options] <pptx file>");
+ System.out.println("Options:");
+ System.out.println(" -scale <float> scale factor");
+ System.out.println(" -slide <integer> 1-based index of a slide to render");
+ }
+
public static void main(String[] args) throws Exception {
if (args.length == 0) {
- System.out.println("Usage: PPTX2PNG [options] <pptx file>");
+ usage();
return;
}
@@ -62,6 +67,11 @@ public class PPTX2PNG { }
}
+ if(file == null){
+ usage();
+ return;
+ }
+
System.out.println("Processing " + file);
XMLSlideShow ppt = new XMLSlideShow(OPCPackage.open(file));
@@ -79,19 +89,23 @@ public class PPTX2PNG { BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
+ // default rendering options
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
- graphics.setPaint(Color.white);
- graphics.fill(new Rectangle2D.Float(0, 0, width, height));
+ graphics.setColor(Color.white);
+ graphics.clearRect(0, 0, width, height);
- graphics.scale((double) width / pgsize.width, (double) height / pgsize.height);
+ graphics.scale(scale, scale);
+ // draw stuff
slide[i].draw(graphics);
- String fname = file.replaceAll("\\.pptx", "-" + (i + 1) + ".png");
+ // save the result
+ int sep = file.lastIndexOf(".");
+ String fname = file.substring(0, sep == -1 ? file.length() : sep) + "-" + (i + 1) +".png";
FileOutputStream out = new FileOutputStream(fname);
ImageIO.write(img, "png", out);
out.close();
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/geom/TestFormulaParser.java b/src/ooxml/testcases/org/apache/poi/xslf/geom/TestFormulaParser.java index b9f15139d5..df3e2ec29d 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/geom/TestFormulaParser.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/geom/TestFormulaParser.java @@ -24,7 +24,7 @@ public class TestFormulaParser extends TestCase { }; CustomGeometry geom = new CustomGeometry(CTCustomGeometry2D.Factory.newInstance()); - Context ctx = new Context(geom, null); + Context ctx = new Context(geom, null, null); for(Formula fmla : ops) { ctx.evaluate(fmla); } diff --git a/src/ooxml/testcases/org/apache/poi/xslf/geom/TestPresetGeometries.java b/src/ooxml/testcases/org/apache/poi/xslf/geom/TestPresetGeometries.java index 5c19b4e53e..c71a065bb7 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/geom/TestPresetGeometries.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/geom/TestPresetGeometries.java @@ -21,11 +21,7 @@ public class TestPresetGeometries extends TestCase { for(String name : shapes.keySet()) { CustomGeometry geom = shapes.get(name); - Context ctx = new Context(geom, new IAdjustableShape() { - public Rectangle2D getAnchor() { - return new Rectangle2D.Double(0, 0, 100, 100); - } - + Context ctx = new Context(geom, new Rectangle2D.Double(0, 0, 100, 100), new IAdjustableShape() { public Guide getAdjustValue(String name) { return null; } diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java index 1f8826fdad..948b792b8b 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java @@ -34,7 +34,7 @@ import java.awt.image.BufferedImage; public class TestPPTX2PNG extends TestCase {
public void testRender(){
String[] testFiles = {"layouts.pptx", "sample.pptx", "shapes.pptx",
- "45541_Header.pptx", "backgrounds.pptx"};
+ "themes.pptx", "backgrounds.pptx"};
for(String sampleFile : testFiles){
XMLSlideShow pptx = XSLFTestDataSamples.openSampleDocument(sampleFile);
Dimension pg = pptx.getPageSize();
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java index 7cab8ea4e3..d29cb30662 100755 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java @@ -34,38 +34,38 @@ public class TestXSLFAutoShape extends TestCase { shape.addNewTextParagraph().addNewTextRun().setText("POI");
// default margins from slide master
- assertEquals(3.6, shape.getMarginBottom());
- assertEquals(3.6, shape.getMarginTop());
- assertEquals(7.2, shape.getMarginLeft());
- assertEquals(7.2, shape.getMarginRight());
-
- shape.setMarginBottom(1.0);
- assertEquals(1.0, shape.getMarginBottom());
- shape.setMarginTop(2.0);
- assertEquals(2.0, shape.getMarginTop());
- shape.setMarginLeft(3.0);
- assertEquals(3.0, shape.getMarginLeft());
- shape.setMarginRight(4.0);
- assertEquals(4.0, shape.getMarginRight());
-
- shape.setMarginBottom(0.0);
- assertEquals(0.0, shape.getMarginBottom());
- shape.setMarginTop(0.0);
- assertEquals(0.0, shape.getMarginTop());
- shape.setMarginLeft(0.0);
- assertEquals(0.0, shape.getMarginLeft());
- shape.setMarginRight(0.0);
- assertEquals(0.0, shape.getMarginRight());
+ assertEquals(3.6, shape.getBottomInset());
+ assertEquals(3.6, shape.getTopInset());
+ assertEquals(7.2, shape.getLeftInset());
+ assertEquals(7.2, shape.getRightInset());
+
+ shape.setBottomInset(1.0);
+ assertEquals(1.0, shape.getBottomInset());
+ shape.setTopInset(2.0);
+ assertEquals(2.0, shape.getTopInset());
+ shape.setLeftInset(3.0);
+ assertEquals(3.0, shape.getLeftInset());
+ shape.setRightInset(4.0);
+ assertEquals(4.0, shape.getRightInset());
+
+ shape.setBottomInset(0.0);
+ assertEquals(0.0, shape.getBottomInset());
+ shape.setTopInset(0.0);
+ assertEquals(0.0, shape.getTopInset());
+ shape.setLeftInset(0.0);
+ assertEquals(0.0, shape.getLeftInset());
+ shape.setRightInset(0.0);
+ assertEquals(0.0, shape.getRightInset());
// unset to defauls
- shape.setMarginBottom(-1);
- assertEquals(3.6, shape.getMarginBottom());
- shape.setMarginTop(-1);
- assertEquals(3.6, shape.getMarginTop());
- shape.setMarginLeft(-1);
- assertEquals(7.2, shape.getMarginLeft());
- shape.setMarginRight(-1);
- assertEquals(7.2, shape.getMarginRight());
+ shape.setBottomInset(-1);
+ assertEquals(3.6, shape.getBottomInset());
+ shape.setTopInset(-1);
+ assertEquals(3.6, shape.getTopInset());
+ shape.setLeftInset(-1);
+ assertEquals(7.2, shape.getLeftInset());
+ shape.setRightInset(-1);
+ assertEquals(7.2, shape.getRightInset());
// shape
assertTrue(shape.getWordWrap());
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFColor.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFColor.java index ffb25b37e1..108fca327b 100755 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFColor.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFColor.java @@ -37,7 +37,7 @@ public class TestXSLFColor extends TestCase { CTSRgbColor c = xml.addNewSrgbClr();
c.setVal(new byte[]{(byte)0xFF, 0, 0});
- XSLFColor color = new XSLFColor(xml, null);
+ XSLFColor color = new XSLFColor(xml, null, null);
assertEquals(-1, color.getAlpha());
c.addNewAlpha().setVal(50000);
@@ -99,7 +99,7 @@ public class TestXSLFColor extends TestCase { c.setSat2(100000);
c.setLum2(50000);
- XSLFColor color = new XSLFColor(xml, null);
+ XSLFColor color = new XSLFColor(xml, null, null);
assertEquals(new Color(128, 00, 00), color.getColor());
}
@@ -107,7 +107,7 @@ public class TestXSLFColor extends TestCase { CTColor xml = CTColor.Factory.newInstance();
xml.addNewSrgbClr().setVal(new byte[]{ (byte)0xFF, (byte)0xFF, 0});
- XSLFColor color = new XSLFColor(xml, null);
+ XSLFColor color = new XSLFColor(xml, null, null);
assertEquals(new Color(0xFF, 0xFF, 0), color.getColor());
}
@@ -118,19 +118,19 @@ public class TestXSLFColor extends TestCase { CTColor xml = CTColor.Factory.newInstance();
xml.addNewSchemeClr().setVal(STSchemeColorVal.ACCENT_2);
- XSLFColor color = new XSLFColor(xml, theme);
+ XSLFColor color = new XSLFColor(xml, theme, null);
// accent2 is theme1.xml is <a:srgbClr val="C0504D"/>
assertEquals(Color.decode("0xC0504D"), color.getColor());
xml = CTColor.Factory.newInstance();
xml.addNewSchemeClr().setVal(STSchemeColorVal.LT_1);
- color = new XSLFColor(xml, theme);
+ color = new XSLFColor(xml, theme, null);
// <a:sysClr val="window" lastClr="FFFFFF"/>
assertEquals(Color.decode("0xFFFFFF"), color.getColor());
xml = CTColor.Factory.newInstance();
xml.addNewSchemeClr().setVal(STSchemeColorVal.DK_1);
- color = new XSLFColor(xml, theme);
+ color = new XSLFColor(xml, theme, null);
// <a:sysClr val="windowText" lastClr="000000"/>
assertEquals(Color.decode("0x000000"), color.getColor());
}
@@ -138,7 +138,7 @@ public class TestXSLFColor extends TestCase { public void testPresetColor() {
CTColor xml = CTColor.Factory.newInstance();
xml.addNewPrstClr().setVal(STPresetColorVal.AQUAMARINE);
- XSLFColor color = new XSLFColor(xml, null);
+ XSLFColor color = new XSLFColor(xml, null, null);
assertEquals(new Color(127, 255, 212), color.getColor());
@@ -147,7 +147,7 @@ public class TestXSLFColor extends TestCase { STPresetColorVal.Enum val = STPresetColorVal.Enum.forString(colorName);
assertNotNull(colorName, val);
xml.addNewPrstClr().setVal(val);
- color = new XSLFColor(xml, null);
+ color = new XSLFColor(xml, null, null);
assertEquals(XSLFColor.presetColors.get(colorName), color.getColor());
}
}
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java index 9ffe69fe98..d8cb5783e4 100755 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java @@ -19,9 +19,6 @@ package org.apache.poi.xslf.usermodel; import junit.framework.TestCase;
import org.apache.poi.xslf.XSLFTestDataSamples;
-import org.apache.poi.POIXMLDocumentPart;
-
-import java.util.List;
/**
* @author Yegor Kozlov
@@ -99,11 +96,11 @@ public class TestXSLFSlide extends TestCase { assertEquals(0, ppt.getSlides().length);
XSLFSlide slide = ppt.createSlide();
- assertTrue(slide.getFollowMasterBackground());
- slide.setFollowMasterBackground(false);
- assertFalse(slide.getFollowMasterBackground());
- slide.setFollowMasterBackground(true);
- assertTrue(slide.getFollowMasterBackground());
+ assertTrue(slide.getFollowMasterGraphics());
+ slide.setFollowMasterGraphics(false);
+ assertFalse(slide.getFollowMasterGraphics());
+ slide.setFollowMasterGraphics(true);
+ assertTrue(slide.getFollowMasterGraphics());
}
}
\ No newline at end of file diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlideShow.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlideShow.java index c07493a3f9..46e10e45d8 100755 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlideShow.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlideShow.java @@ -38,7 +38,7 @@ public class TestXSLFSlideShow extends TestCase { List<POIXMLDocumentPart> rels = slide1.getRelations();
assertEquals(1, rels.size());
- assertEquals(slide1.getMasterSheet().getLayout("blank"), rels.get(0));
+ assertEquals(slide1.getSlideMaster().getLayout(SlideLayout.BLANK), rels.get(0));
XSLFSlide slide2 = ppt.createSlide();
assertEquals(2, ppt.getSlides().length);
@@ -91,7 +91,7 @@ public class TestXSLFSlideShow extends TestCase { assertEquals(1, masters.length);
XSLFSlide slide = ppt.createSlide();
- assertSame(masters[0], slide.getMasterSheet());
+ assertSame(masters[0], slide.getSlideMaster());
}
public void testSlideLayout(){
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java index 7524b25292..6347b63ccf 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java @@ -65,10 +65,10 @@ public class TestXSLFTextShape extends TestCase { !bodyPr1.isSetBIns() && !bodyPr1.isSetTIns() &&
!bodyPr1.isSetAnchor()
);
- assertEquals(7.2, shape1.getMarginLeft()); // 0.1"
- assertEquals(7.2, shape1.getMarginRight()); // 0.1"
- assertEquals(3.6, shape1.getMarginTop()); // 0.05"
- assertEquals(3.6, shape1.getMarginBottom()); // 0.05"
+ assertEquals(7.2, shape1.getLeftInset()); // 0.1"
+ assertEquals(7.2, shape1.getRightInset()); // 0.1"
+ assertEquals(3.6, shape1.getTopInset()); // 0.05"
+ assertEquals(3.6, shape1.getBottomInset()); // 0.05"
assertEquals(VerticalAlignment.MIDDLE, shape1.getVerticalAlignment());
// now check text properties
@@ -95,10 +95,10 @@ public class TestXSLFTextShape extends TestCase { !bodyPr2.isSetBIns() && !bodyPr2.isSetTIns() &&
!bodyPr2.isSetAnchor()
);
- assertEquals(7.2, shape2.getMarginLeft()); // 0.1"
- assertEquals(7.2, shape2.getMarginRight()); // 0.1"
- assertEquals(3.6, shape2.getMarginTop()); // 0.05"
- assertEquals(3.6, shape2.getMarginBottom()); // 0.05"
+ assertEquals(7.2, shape2.getLeftInset()); // 0.1"
+ assertEquals(7.2, shape2.getRightInset()); // 0.1"
+ assertEquals(3.6, shape2.getTopInset()); // 0.05"
+ assertEquals(3.6, shape2.getBottomInset()); // 0.05"
assertEquals(VerticalAlignment.TOP, shape2.getVerticalAlignment());
assertEquals("subtitle", shape2.getText());
@@ -134,10 +134,10 @@ public class TestXSLFTextShape extends TestCase { !bodyPr1.isSetBIns() && !bodyPr1.isSetTIns() &&
!bodyPr1.isSetAnchor()
);
- assertEquals(7.2, shape1.getMarginLeft()); // 0.1"
- assertEquals(7.2, shape1.getMarginRight()); // 0.1"
- assertEquals(3.6, shape1.getMarginTop()); // 0.05"
- assertEquals(3.6, shape1.getMarginBottom()); // 0.05"
+ assertEquals(7.2, shape1.getLeftInset()); // 0.1"
+ assertEquals(7.2, shape1.getRightInset()); // 0.1"
+ assertEquals(3.6, shape1.getTopInset()); // 0.05"
+ assertEquals(3.6, shape1.getBottomInset()); // 0.05"
assertEquals(VerticalAlignment.MIDDLE, shape1.getVerticalAlignment());
// now check text properties
@@ -169,10 +169,10 @@ public class TestXSLFTextShape extends TestCase { !bodyPr2.isSetBIns() && !bodyPr2.isSetTIns() &&
!bodyPr2.isSetAnchor()
);
- assertEquals(7.2, shape2.getMarginLeft()); // 0.1"
- assertEquals(7.2, shape2.getMarginRight()); // 0.1"
- assertEquals(3.6, shape2.getMarginTop()); // 0.05"
- assertEquals(3.6, shape2.getMarginBottom()); // 0.05"
+ assertEquals(7.2, shape2.getLeftInset()); // 0.1"
+ assertEquals(7.2, shape2.getRightInset()); // 0.1"
+ assertEquals(3.6, shape2.getTopInset()); // 0.05"
+ assertEquals(3.6, shape2.getBottomInset()); // 0.05"
assertEquals(VerticalAlignment.TOP, shape2.getVerticalAlignment());
XSLFTextRun pr1 = shape2.getTextParagraphs().get(0).getTextRuns().get(0);
@@ -244,10 +244,10 @@ public class TestXSLFTextShape extends TestCase { !bodyPr1.isSetBIns() && !bodyPr1.isSetTIns() &&
!bodyPr1.isSetAnchor()
);
- assertEquals(7.2, shape1.getMarginLeft()); // 0.1"
- assertEquals(7.2, shape1.getMarginRight()); // 0.1"
- assertEquals(3.6, shape1.getMarginTop()); // 0.05"
- assertEquals(3.6, shape1.getMarginBottom()); // 0.05"
+ assertEquals(7.2, shape1.getLeftInset()); // 0.1"
+ assertEquals(7.2, shape1.getRightInset()); // 0.1"
+ assertEquals(3.6, shape1.getTopInset()); // 0.05"
+ assertEquals(3.6, shape1.getBottomInset()); // 0.05"
assertEquals(VerticalAlignment.TOP, shape1.getVerticalAlignment());
// now check text properties
@@ -278,10 +278,10 @@ public class TestXSLFTextShape extends TestCase { !bodyPr2.isSetBIns() && !bodyPr2.isSetTIns() &&
!bodyPr2.isSetAnchor()
);
- assertEquals(7.2, shape2.getMarginLeft()); // 0.1"
- assertEquals(7.2, shape2.getMarginRight()); // 0.1"
- assertEquals(3.6, shape2.getMarginTop()); // 0.05"
- assertEquals(3.6, shape2.getMarginBottom()); // 0.05"
+ assertEquals(7.2, shape2.getLeftInset()); // 0.1"
+ assertEquals(7.2, shape2.getRightInset()); // 0.1"
+ assertEquals(3.6, shape2.getTopInset()); // 0.05"
+ assertEquals(3.6, shape2.getBottomInset()); // 0.05"
assertEquals(VerticalAlignment.BOTTOM, shape2.getVerticalAlignment());
assertEquals("Section Header", shape2.getText());
@@ -318,10 +318,10 @@ public class TestXSLFTextShape extends TestCase { !bodyPr1.isSetBIns() && !bodyPr1.isSetTIns() &&
!bodyPr1.isSetAnchor()
);
- assertEquals(7.2, shape1.getMarginLeft()); // 0.1"
- assertEquals(7.2, shape1.getMarginRight()); // 0.1"
- assertEquals(3.6, shape1.getMarginTop()); // 0.05"
- assertEquals(3.6, shape1.getMarginBottom()); // 0.05"
+ assertEquals(7.2, shape1.getLeftInset()); // 0.1"
+ assertEquals(7.2, shape1.getRightInset()); // 0.1"
+ assertEquals(3.6, shape1.getTopInset()); // 0.05"
+ assertEquals(3.6, shape1.getBottomInset()); // 0.05"
assertEquals(VerticalAlignment.MIDDLE, shape1.getVerticalAlignment());
// now check text properties
@@ -351,10 +351,10 @@ public class TestXSLFTextShape extends TestCase { !bodyPr2.isSetBIns() && !bodyPr2.isSetTIns() &&
!bodyPr2.isSetAnchor()
);
- assertEquals(7.2, shape2.getMarginLeft()); // 0.1"
- assertEquals(7.2, shape2.getMarginRight()); // 0.1"
- assertEquals(3.6, shape2.getMarginTop()); // 0.05"
- assertEquals(3.6, shape2.getMarginBottom()); // 0.05"
+ assertEquals(7.2, shape2.getLeftInset()); // 0.1"
+ assertEquals(7.2, shape2.getRightInset()); // 0.1"
+ assertEquals(3.6, shape2.getTopInset()); // 0.05"
+ assertEquals(3.6, shape2.getBottomInset()); // 0.05"
assertEquals(VerticalAlignment.TOP, shape2.getVerticalAlignment());
XSLFTextRun pr1 = shape2.getTextParagraphs().get(0).getTextRuns().get(0);
@@ -425,10 +425,10 @@ public class TestXSLFTextShape extends TestCase { !bodyPr1.isSetBIns() && !bodyPr1.isSetTIns() &&
!bodyPr1.isSetAnchor()
);
- assertEquals(7.2, shape1.getMarginLeft()); // 0.1"
- assertEquals(7.2, shape1.getMarginRight()); // 0.1"
- assertEquals(3.6, shape1.getMarginTop()); // 0.05"
- assertEquals(3.6, shape1.getMarginBottom()); // 0.05"
+ assertEquals(7.2, shape1.getLeftInset()); // 0.1"
+ assertEquals(7.2, shape1.getRightInset()); // 0.1"
+ assertEquals(3.6, shape1.getTopInset()); // 0.05"
+ assertEquals(3.6, shape1.getBottomInset()); // 0.05"
assertEquals(VerticalAlignment.MIDDLE, shape1.getVerticalAlignment());
// now check text properties
@@ -449,10 +449,10 @@ public class TestXSLFTextShape extends TestCase { !bodyPr2.isSetBIns() && !bodyPr2.isSetTIns() &&
!bodyPr2.isSetAnchor()
);
- assertEquals(7.2, shape2.getMarginLeft()); // 0.1"
- assertEquals(7.2, shape2.getMarginRight()); // 0.1"
- assertEquals(3.6, shape2.getMarginTop()); // 0.05"
- assertEquals(3.6, shape2.getMarginBottom()); // 0.05"
+ assertEquals(7.2, shape2.getLeftInset()); // 0.1"
+ assertEquals(7.2, shape2.getRightInset()); // 0.1"
+ assertEquals(3.6, shape2.getTopInset()); // 0.05"
+ assertEquals(3.6, shape2.getBottomInset()); // 0.05"
assertEquals(VerticalAlignment.TOP, shape2.getVerticalAlignment());
XSLFTextRun pr1 = shape2.getTextParagraphs().get(0).getTextRuns().get(0);
@@ -498,10 +498,10 @@ public class TestXSLFTextShape extends TestCase { !bodyPr1.isSetBIns() && !bodyPr1.isSetTIns() &&
!bodyPr1.isSetAnchor()
);
- assertEquals(7.2, shape1.getMarginLeft()); // 0.1"
- assertEquals(7.2, shape1.getMarginRight()); // 0.1"
- assertEquals(3.6, shape1.getMarginTop()); // 0.05"
- assertEquals(3.6, shape1.getMarginBottom()); // 0.05"
+ assertEquals(7.2, shape1.getLeftInset()); // 0.1"
+ assertEquals(7.2, shape1.getRightInset()); // 0.1"
+ assertEquals(3.6, shape1.getTopInset()); // 0.05"
+ assertEquals(3.6, shape1.getBottomInset()); // 0.05"
assertEquals(VerticalAlignment.BOTTOM, shape1.getVerticalAlignment());
// now check text properties
@@ -532,10 +532,10 @@ public class TestXSLFTextShape extends TestCase { !bodyPr2.isSetBIns() && !bodyPr2.isSetTIns() &&
!bodyPr2.isSetAnchor()
);
- assertEquals(7.2, shape2.getMarginLeft()); // 0.1"
- assertEquals(7.2, shape2.getMarginRight()); // 0.1"
- assertEquals(3.6, shape2.getMarginTop()); // 0.05"
- assertEquals(3.6, shape2.getMarginBottom()); // 0.05"
+ assertEquals(7.2, shape2.getLeftInset()); // 0.1"
+ assertEquals(7.2, shape2.getRightInset()); // 0.1"
+ assertEquals(3.6, shape2.getTopInset()); // 0.05"
+ assertEquals(3.6, shape2.getBottomInset()); // 0.05"
assertEquals(VerticalAlignment.TOP, shape2.getVerticalAlignment());
XSLFTextRun pr1 = shape2.getTextParagraphs().get(0).getTextRuns().get(0);
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java index 81f35d3250..b01c25e1d5 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java @@ -18,22 +18,141 @@ package org.apache.poi.xslf.usermodel; import junit.framework.TestCase;
+
import org.apache.poi.xslf.XSLFTestDataSamples;
+import java.awt.Color;
+import java.awt.TexturePaint;
+
/**
- * test common properties for sheets (slides, masters, layouts, etc.)
+ * test reading properties from a multi-theme and multi-master document
*
* @author Yegor Kozlov
*/
public class TestXSLFTheme extends TestCase {
public void testRead(){
- XMLSlideShow ppt = new XMLSlideShow();
- XSLFSlide slide = ppt.createSlide();
- XSLFTheme theme = slide.getSlideLayout().getSlideMaster().getTheme();
- assertNotNull(theme);
-
+ XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("themes.pptx");
+ XSLFSlide[] slides = ppt.getSlides();
+
+ slide1(slides[0]);
+ slide2(slides[1]);
+ slide3(slides[2]);
+ slide4(slides[3]);
+ slide5(slides[4]);
+ slide6(slides[5]);
+ slide7(slides[6]);
+ slide8(slides[7]);
+ slide9(slides[8]);
+ slide10(slides[9]);
+ }
+
+ private XSLFShape getShape(XSLFSheet sheet, String name){
+ for(XSLFShape sh : sheet.getShapes()){
+ if(sh.getShapeName().equals(name)) return sh;
+ }
+ throw new IllegalArgumentException("Shape not found: " + name);
+ }
+
+ void slide1(XSLFSlide slide){
+ assertEquals(Color.white, slide.getBackground().getFillColor());
+
+ XSLFTheme theme = slide.getTheme();
assertEquals("Office Theme", theme.getName());
- //XSLFColor accent1 = theme.getColor("accent1");
- //assertNotNull(accent1);
+
+ XSLFTextShape sh1 = (XSLFTextShape)getShape(slide, "Rectangle 3");
+ RenderableShape rsh1 = new RenderableShape(sh1);
+ XSLFTextRun run1 = sh1.getTextParagraphs().get(0).getTextRuns().get(0);
+ assertEquals(Color.white, run1.getFontColor());
+ assertEquals(new Color(79, 129, 189), sh1.getFillColor());
+ assertTrue(rsh1.getFillPaint(null) instanceof Color) ; // solid fill
+
+ }
+
+ void slide2(XSLFSlide slide){
+ // Background 2, darker 10%
+ // YK: PPT shows slightly different color: new Color(221, 217, 195)
+ assertEquals(new Color(214, 212, 203), slide.getBackground().getFillColor());
+ }
+
+ void slide3(XSLFSlide slide){
+ assertNull(slide.getBackground().getFillColor());
+ assertTrue(slide.getBackground().getPaint(null).getClass().getName().indexOf("Gradient") > 0);
+ }
+
+ void slide4(XSLFSlide slide){
+ assertNull(slide.getBackground().getFillColor());
+ assertTrue(slide.getBackground().getPaint(null).getClass().getName().indexOf("Gradient") > 0);
+
+ XSLFTextShape sh1 = (XSLFTextShape)getShape(slide, "Rectangle 4");
+ RenderableShape rsh1 = new RenderableShape(sh1);
+ XSLFTextRun run1 = sh1.getTextParagraphs().get(0).getTextRuns().get(0);
+ assertEquals(Color.white, run1.getFontColor());
+ assertEquals(new Color(148, 198, 0), sh1.getFillColor());
+ assertTrue(rsh1.getFillPaint(null) instanceof Color) ; // solid fill
+
+ XSLFTextShape sh2 = (XSLFTextShape)getShape(slide, "Title 3");
+ XSLFTextRun run2 = sh2.getTextParagraphs().get(0).getTextRuns().get(0);
+ assertEquals(new Color(148, 198, 0), run2.getFontColor());
+ assertNull(sh2.getFillColor()); // no fill
+
+ assertTrue(slide.getSlideLayout().getFollowMasterGraphics());
+ }
+
+ void slide5(XSLFSlide slide){
+ assertTrue(slide.getBackground().getPaint(null) instanceof TexturePaint);
+
+ XSLFTextShape sh2 = (XSLFTextShape)getShape(slide, "Title 1");
+ XSLFTextRun run2 = sh2.getTextParagraphs().get(0).getTextRuns().get(0);
+ assertEquals(new Color(148, 198, 0), run2.getFontColor());
+ assertNull(sh2.getFillColor()); // no fill
+ // font size is 40pt and scale factor is 90%
+ assertEquals(36.0, run2.getFontSize());
+
+ assertTrue(slide.getSlideLayout().getFollowMasterGraphics());
+ }
+
+ void slide6(XSLFSlide slide){
+
+ XSLFTextShape sh1 = (XSLFTextShape)getShape(slide, "Subtitle 3");
+ XSLFTextRun run1 = sh1.getTextParagraphs().get(0).getTextRuns().get(0);
+ assertEquals(new Color(66, 66, 66), run1.getFontColor());
+ assertNull(sh1.getFillColor()); // no fill
+
+ XSLFTextShape sh2 = (XSLFTextShape)getShape(slide, "Title 2");
+ XSLFTextRun run2 = sh2.getTextParagraphs().get(0).getTextRuns().get(0);
+ assertEquals(new Color(148, 198, 0), run2.getFontColor());
+ assertNull(sh2.getFillColor()); // no fill
+
+ assertFalse(slide.getSlideLayout().getFollowMasterGraphics());
+ }
+
+ void slide7(XSLFSlide slide){
+
+ //YK: PPT reports a slightly different color: r=189,g=239,b=87
+ assertEquals(new Color(182, 218, 108), slide.getBackground().getFillColor());
+
+ assertFalse(slide.getFollowMasterGraphics());
+ }
+
+ void slide8(XSLFSlide slide){
+ assertTrue(slide.getBackground().getPaint(null) instanceof TexturePaint);
+ }
+
+ void slide9(XSLFSlide slide){
+ assertTrue(slide.getBackground().getPaint(null) instanceof TexturePaint);
+ }
+
+ void slide10(XSLFSlide slide){
+ assertTrue(slide.getBackground().getPaint(null).getClass().getName().indexOf("Gradient") > 0);
+
+ XSLFTextShape sh1 = (XSLFTextShape)getShape(slide, "Title 3");
+ XSLFTextRun run1 = sh1.getTextParagraphs().get(0).getTextRuns().get(0);
+ assertEquals(Color.white, run1.getFontColor());
+ assertNull(sh1.getFillColor()); // no fill
+
+ XSLFTextShape sh2 = (XSLFTextShape)getShape(slide, "Subtitle 4");
+ XSLFTextRun run2 = sh2.getTextParagraphs().get(0).getTextRuns().get(0);
+ assertEquals(Color.white, run2.getFontColor());
+ assertNull(sh2.getFillColor()); // no fill
}
}
diff --git a/src/resources/scratchpad/org/apache/poi/xslf/usermodel/empty.pptx b/src/resources/ooxml/org/apache/poi/xslf/usermodel/empty.pptx Binary files differindex eea1e064e9..eea1e064e9 100755 --- a/src/resources/scratchpad/org/apache/poi/xslf/usermodel/empty.pptx +++ b/src/resources/ooxml/org/apache/poi/xslf/usermodel/empty.pptx diff --git a/src/resources/scratchpad/org/apache/poi/xslf/usermodel/presetShapeDefinitions.xml b/src/resources/ooxml/org/apache/poi/xslf/usermodel/presetShapeDefinitions.xml index f5fead717f..f5fead717f 100755 --- a/src/resources/scratchpad/org/apache/poi/xslf/usermodel/presetShapeDefinitions.xml +++ b/src/resources/ooxml/org/apache/poi/xslf/usermodel/presetShapeDefinitions.xml diff --git a/test-data/slideshow/pptx2svg.pptx b/test-data/slideshow/pptx2svg.pptx Binary files differnew file mode 100644 index 0000000000..2f9a3515cb --- /dev/null +++ b/test-data/slideshow/pptx2svg.pptx diff --git a/test-data/slideshow/pptx2svg.svg b/test-data/slideshow/pptx2svg.svg new file mode 100644 index 0000000000..9ea3636916 --- /dev/null +++ b/test-data/slideshow/pptx2svg.svg @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" stroke-dasharray="none" shape-rendering="auto" font-family="'Dialog'" width="720" text-rendering="auto" fill-opacity="1" contentScriptType="text/ecmascript" color-interpolation="auto" color-rendering="auto" preserveAspectRatio="xMidYMid meet" font-size="12" fill="black" stroke="black" image-rendering="auto" stroke-miterlimit="10" zoomAndPan="magnify" version="1.0" stroke-linecap="square" stroke-linejoin="miter" contentStyleType="text/css" font-style="normal" height="540" stroke-width="1" stroke-dashoffset="0" font-weight="normal" stroke-opacity="1"> +<!--Generated by the Batik Graphics2D SVG Generator--> +<defs id="genericDefs"/> +<g> +<defs id="defs1"> +<pattern x="0" y="0" width="128" xlink:type="simple" patternUnits="userSpaceOnUse" xlink:actuate="onLoad" id="pattern1" height="128" preserveAspectRatio="xMidYMid meet" xlink:show="other"> +<image x="0" y="0" width="128" xlink:href="data:image/png;base64,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" xlink:type="simple" xlink:actuate="onLoad" height="128" preserveAspectRatio="none" xlink:show="embed"/> +</pattern> +</defs> +<g fill="url(#pattern1)" stroke="url(#pattern1)"> +<rect x="0" width="720" height="540" y="0" stroke="none"/> +</g> +<g fill="rgb(144,60,58)" font-size="44" font-weight="bold" font-family="'Calibri'" stroke="rgb(144,60,58)"> +<text x="67.6621" xml:space="preserve" y="81.6641" stroke="none">Paragraphs and Rich Formatting</text> +</g> +<g fill="rgb(31,73,125)" font-size="17" font-family="'Wingdings'" stroke="rgb(31,73,125)"> +<text x="43.2" xml:space="preserve" y="144.8817" stroke="none">✭</text> +<text fill="black" x="70.2" xml:space="preserve" y="145.7865" font-family="'Calibri'" stroke="none">The Paragraphs and Rich Formatting portion of the DrawingML framework stores text </text> +<text fill="black" x="70.2" xml:space="preserve" y="165.7865" font-family="'Calibri'" stroke="none">and related formatting information for a text body contained within a shape. </text> +<text fill="black" x="70.2" xml:space="preserve" y="185.7865" font-family="'Calibri'" stroke="none">Formatting for text within a shape can be broken down into three levels of precision, </text> +<text fill="black" x="70.2" xml:space="preserve" y="205.7865" font-family="'Calibri'" stroke="none">namely body, paragraph, and run formatting properties.</text> +<text x="43.2" xml:space="preserve" y="248.8817" stroke="none">✭</text> +<text fill="black" x="70.2" xml:space="preserve" y="249.7865" font-family="'Calibri'" stroke="none">Body Formatting</text> +<text x="79.2" xml:space="preserve" font-size="13.6" y="299.8254" stroke="none">✭</text> +</g> +<g font-family="'Calibri'" font-size="13.6"> +<text x="101.7" xml:space="preserve" y="300.5492" stroke="none">Being the highest level of formatting available within a shape, the body properties allow for the </text> +<text x="101.7" xml:space="preserve" y="330.5492" stroke="none">manipulation of the text area as a whole. This means that all paragraphs and runs of text for the shape </text> +<text x="101.7" xml:space="preserve" y="360.5492" stroke="none">in question would be encompassed within here and, therefore, follow the text body style defined here.</text> +</g> +<g fill="rgb(31,73,125)" font-size="23.8" font-family="'Wingdings'" stroke="rgb(31,73,125)"> +<text x="43.2" xml:space="preserve" y="422.9944" stroke="none">✭</text> +</g> +<g fill="rgb(116,140,67)" stroke="rgb(116,140,67)"> +<path d="M86.1674 441.7646 L83.7153 441.7646 L82.0186 436.9418 L74.5347 436.9418 L72.838 441.7646 L70.5021 441.7646 L76.8008 424.4608 L79.8687 424.4608 ZM81.3098 434.9662 L78.2767 426.4712 L75.2319 434.9662 Z" stroke="none"/> +<path fill="black" d="M112.61 428.7838 L104.954 446.5525 L102.7693 446.5525 L105.4723 440.3003 Q104.2126 441.0789 103.1307 441.5496 Q102.0488 442.0203 100.7588 442.0203 Q98.9227 442.0203 98.0697 440.9104 Q97.2167 439.8006 97.6606 437.581 Q98.0348 435.71 98.9494 434.0424 Q99.864 432.3748 101.1144 431.1197 Q102.3509 429.8762 103.8442 429.1499 Q105.3375 428.4236 106.8483 428.4236 Q107.8826 428.4236 108.71 428.6444 Q109.5374 428.8652 110.2044 429.3068 L110.5647 428.7838 ZM109.4235 431.1197 Q108.7262 430.7129 108.0162 430.5154 Q107.3061 430.3178 106.4578 430.3178 Q105.2143 430.3178 104.1533 430.9163 Q103.0923 431.5148 102.2463 432.491 Q101.4468 433.409 100.8564 434.7048 Q100.2661 436.0005 100.0244 437.2091 Q99.7408 438.6269 100.2115 439.3242 Q100.6821 440.0214 102.0534 440.0214 Q103.0296 440.0214 104.1557 439.5333 Q105.2818 439.0453 106.2579 438.4642 ZM127.671 428.7838 L122.0766 441.7646 L119.8918 441.7646 L120.517 440.3236 Q119.1039 441.1719 117.9336 441.6484 Q116.7634 442.1248 115.5664 442.1248 Q113.893 442.1248 113.1202 441.2823 Q112.3474 440.4398 112.6704 438.8245 Q112.7495 438.4293 112.8761 438.0575 Q113.0028 437.6856 113.1911 437.2091 L116.8168 428.7838 L119.0016 428.7838 L115.8151 436.1748 Q115.6199 436.6281 115.4107 437.2091 Q115.2015 437.7902 115.1341 438.1272 Q114.9342 439.1266 115.3398 439.6263 Q115.7454 440.126 116.9656 440.126 Q117.8256 440.126 119.0353 439.6263 Q120.2451 439.1266 121.3165 438.4758 L125.4862 428.7838 ZM136.2287 424.3446 L135.2525 426.6107 L132.7889 426.6107 L133.765 424.3446 ZM134.1788 428.7838 L128.5844 441.7646 L126.3996 441.7646 L131.994 428.7838 ZM138.4739 442.0551 Q137.2421 442.0551 136.3531 441.7646 Q135.4641 441.4741 134.8807 440.8465 Q134.3159 440.2422 134.1276 439.3242 Q133.9394 438.4061 134.1834 437.1859 Q134.5483 435.3614 135.4338 433.7809 Q136.3194 432.2004 137.6047 431.0034 Q138.8481 429.8413 140.4716 429.1615 Q142.0951 428.4817 143.815 428.4817 Q144.9538 428.4817 145.8963 428.7954 Q146.8388 429.1092 147.5198 429.5392 L146.5715 431.8983 L146.4437 431.8983 Q146.2531 431.6891 145.9602 431.4102 Q145.6674 431.1313 145.2397 430.8872 Q144.8144 430.6316 144.267 430.4631 Q143.7197 430.2946 142.9759 430.2946 Q140.7214 430.2946 138.9167 432.2004 Q137.1119 434.1063 136.5657 436.8372 Q136.238 438.4758 136.9039 439.359 Q137.5698 440.2422 139.127 440.2422 Q139.8592 440.2422 140.6331 440.0331 Q141.4071 439.8239 141.9672 439.5798 Q142.5901 439.3125 143.1386 439.0104 Q143.6871 438.7083 143.9382 438.5572 L144.066 438.5572 L143.1247 440.9395 Q142.023 441.3927 140.8295 441.7239 Q139.636 442.0551 138.4739 442.0551 ZM157.2304 441.7646 L154.5807 441.7646 L151.5732 436.0586 L149.3861 437.581 L147.5965 441.7646 L145.3885 441.7646 L153.1886 423.6822 L155.3966 423.6822 L150.3763 435.3033 L159.2919 428.7838 L162.1624 428.7838 L153.5325 434.9198 Z" stroke="none"/> +<path fill="rgb(96,40,39)" d="M187.3732 435.0592 Q187.3732 438.1388 185.6707 440.0795 Q183.9682 442.0203 181.4232 442.0203 Q180.3308 442.0203 179.5057 441.7878 Q178.6806 441.5554 177.9368 441.1138 L177.7625 441.7646 L173.7532 441.7646 L173.7532 423.6822 L177.9368 423.6822 L177.9368 430.0738 Q178.9014 429.3068 179.9066 428.8303 Q180.9118 428.3539 182.225 428.3539 Q184.7003 428.3539 186.0367 430.1377 Q187.3732 431.9215 187.3732 435.0592 ZM183.0617 435.1406 Q183.0617 433.3974 182.4691 432.4561 Q181.8764 431.5148 180.354 431.5148 Q179.7613 431.5148 179.1338 431.6949 Q178.5063 431.875 177.9368 432.212 L177.9368 438.7664 Q178.39 438.929 178.7968 438.9872 Q179.2035 439.0453 179.773 439.0453 Q181.4348 439.0453 182.2483 438.0691 Q183.0617 437.0929 183.0617 435.1406 ZM199.8542 432.7001 L199.4823 432.7001 Q199.215 432.6072 198.6224 432.5607 Q198.0297 432.5142 197.6346 432.5142 Q196.7398 432.5142 196.0541 432.6304 Q195.3685 432.7466 194.5782 433.0255 L194.5782 441.7646 L190.3946 441.7646 L190.3946 428.7141 L194.5782 428.7141 L194.5782 430.6316 Q195.9611 429.4462 196.9838 429.0569 Q198.0065 428.6676 198.8664 428.6676 Q199.0872 428.6676 199.3661 428.6792 Q199.645 428.6909 199.8542 428.7141 ZM215.5659 435.2451 Q215.5659 438.4642 213.6891 440.3177 Q211.8123 442.1713 208.419 442.1713 Q205.0256 442.1713 203.1488 440.3177 Q201.272 438.4642 201.272 435.2451 Q201.272 432.0029 203.1604 430.1551 Q205.0488 428.3074 208.419 428.3074 Q211.8356 428.3074 213.7007 430.1667 Q215.5659 432.0261 215.5659 435.2451 ZM210.4178 438.4177 Q210.8245 437.918 211.0279 437.2149 Q211.2313 436.5118 211.2313 435.2684 Q211.2313 434.1179 211.0221 433.3393 Q210.8129 432.5607 210.441 432.0958 Q210.0692 431.6194 209.5462 431.4218 Q209.0233 431.2242 208.419 431.2242 Q207.8147 431.2242 207.344 431.387 Q206.8734 431.5497 206.4434 432.0377 Q206.0599 432.491 205.8333 433.2812 Q205.6067 434.0714 205.6067 435.2684 Q205.6067 436.3375 205.8042 437.1219 Q206.0018 437.9064 206.3853 438.3828 Q206.7571 438.8361 207.2743 439.0453 Q207.7914 439.2544 208.4538 439.2544 Q209.0233 439.2544 209.5404 439.0627 Q210.0575 438.8709 210.4178 438.4177 ZM239.3776 428.7141 L235.3334 441.7646 L230.8942 441.7646 L228.2329 432.9674 L225.6182 441.7646 L221.1208 441.7646 L217.1115 428.7141 L221.5043 428.7141 L223.7356 437.7088 L226.5246 428.7141 L230.2318 428.7141 L232.8814 437.7088 L235.0778 428.7141 ZM254.9847 441.7646 L250.7778 441.7646 L250.7778 435.2916 Q250.7778 434.5014 250.6965 433.717 Q250.6151 432.9326 250.4176 432.5607 Q250.1852 432.1307 249.7377 431.9331 Q249.2903 431.7356 248.4885 431.7356 Q247.9191 431.7356 247.3322 431.9215 Q246.7453 432.1075 246.0597 432.5142 L246.0597 441.7646 L241.8761 441.7646 L241.8761 428.7141 L246.0597 428.7141 L246.0597 430.1551 Q247.1753 429.2835 248.2038 428.8187 Q249.2322 428.3539 250.4873 428.3539 Q252.6024 428.3539 253.7935 429.5857 Q254.9847 430.8175 254.9847 433.2696 Z" stroke="none"/> +<path fill="black" d="M280.5552 414.9622 L280.3602 414.9622 Q279.7555 414.7866 278.7801 414.6013 Q277.8048 414.416 277.0635 414.416 Q274.7032 414.416 273.6401 415.4596 Q272.5769 416.5032 272.5769 419.2342 L272.5769 419.9755 L279.1898 419.9755 L279.1898 423.0575 L272.694 423.0575 L272.694 441.7646 L269.0267 441.7646 L269.0267 423.0575 L266.5493 423.0575 L266.5493 419.9755 L269.0267 419.9755 L269.0267 419.2537 Q269.0267 415.3718 270.9579 413.2944 Q272.8891 411.2169 276.5368 411.2169 Q277.7658 411.2169 278.7509 411.3339 Q279.736 411.451 280.5552 411.607 ZM301.4471 430.8798 Q301.4471 436.2051 298.7161 439.2872 Q295.9851 442.3693 291.401 442.3693 Q286.7779 442.3693 284.0567 439.2872 Q281.3355 436.2051 281.3355 430.8798 Q281.3355 425.5544 284.0567 422.4626 Q286.7779 419.3707 291.401 419.3707 Q295.9851 419.3707 298.7161 422.4626 Q301.4471 425.5544 301.4471 430.8798 ZM297.6628 430.8798 Q297.6628 426.6468 296.0047 424.5888 Q294.3466 422.5309 291.401 422.5309 Q288.4165 422.5309 286.7682 424.5888 Q285.1198 426.6468 285.1198 430.8798 Q285.1198 434.9762 286.7779 437.0927 Q288.436 439.2092 291.401 439.2092 Q294.3271 439.2092 295.9949 437.1122 Q297.6628 435.0152 297.6628 430.8798 ZM325.9867 441.7646 L321.3636 441.7646 L315.1799 433.3961 L308.9572 441.7646 L304.6852 441.7646 L313.1902 430.8993 L304.7632 419.9755 L309.3864 419.9755 L315.531 428.2073 L321.6952 419.9755 L325.9867 419.9755 L317.4232 430.7042 Z" stroke="none"/> +<path fill="black" d="M342.2429 424.1393 L338.5024 432.8086 Q337.8423 434.3271 336.6368 435.2219 Q335.4313 436.1168 333.8276 436.1168 Q333.0451 436.1168 332.4842 436.0548 Q331.9233 435.9928 331.6212 435.9153 L332.478 433.9165 L332.6949 433.9165 Q332.932 434.0095 333.1823 434.0559 Q333.4325 434.1024 333.7502 434.1024 Q334.4877 434.0947 334.9828 433.7112 Q335.4778 433.3277 335.8714 432.4057 L338.6186 426.0296 L336.9684 426.0296 L337.7881 424.1393 ZM343.6653 420.7847 L342.7542 422.8997 L339.7947 422.8997 L340.7057 420.7847 ZM353.5385 424.1393 L349.7841 432.8396 L346.9796 432.8396 L347.3964 431.8789 Q346.9238 432.1501 346.4776 432.3864 Q346.0313 432.6227 345.6471 432.7621 Q345.1605 432.9481 344.7755 433.0139 Q344.3904 433.0798 344.0263 433.0798 Q342.8797 433.0798 342.3273 432.5297 Q341.7749 431.9796 341.9903 430.9027 Q342.0492 430.6083 342.1305 430.3566 Q342.2119 430.1048 342.342 429.8026 L344.784 424.1393 L347.604 424.1393 L345.7416 428.4546 Q345.5262 428.9504 345.3968 429.3068 Q345.2674 429.6632 345.2178 429.9111 Q345.1156 430.4224 345.3178 430.651 Q345.52 430.8795 346.2095 430.8795 Q346.5504 430.8795 347.0253 430.7323 Q347.5002 430.5851 348.0751 430.3062 L350.734 424.1393 ZM369.4517 426.0761 Q369.4037 426.3163 369.313 426.5952 Q369.2224 426.8741 369.0922 427.1762 L366.6503 432.8396 L363.8302 432.8396 L365.7066 428.4933 Q365.8972 428.044 366.0607 427.6334 Q366.2242 427.2227 366.2753 426.9671 Q366.3651 426.5177 366.1591 426.3085 Q365.953 426.0994 365.3177 426.0994 Q365.0156 426.0994 364.6034 426.2427 Q364.1912 426.386 363.5916 426.6727 L360.9327 432.8396 L358.1126 432.8396 L359.989 428.4933 Q360.1502 428.1137 360.3237 427.6721 Q360.4973 427.2305 360.5438 426.9981 Q360.6383 426.5255 360.4446 426.3124 Q360.2509 426.0994 359.6079 426.0994 Q359.2747 426.0994 358.8486 426.2543 Q358.4225 426.4093 357.874 426.6727 L355.2151 432.8396 L352.4106 432.8396 L356.1649 424.1393 L358.9695 424.1393 L358.5527 425.0999 Q359.518 424.5344 360.2672 424.2167 Q361.0164 423.8991 361.8143 423.8991 Q362.6743 423.8991 363.1864 424.2826 Q363.6985 424.6661 363.7822 425.4099 Q364.9706 424.6584 365.8678 424.2787 Q366.7649 423.8991 367.5319 423.8991 Q368.6785 423.8991 369.1697 424.4647 Q369.6609 425.0302 369.4517 426.0761 ZM380.656 426.9206 Q380.4158 428.1214 379.7991 429.2487 Q379.1824 430.3759 378.3457 431.1894 Q377.4687 432.0494 376.4855 432.5297 Q375.5024 433.01 374.4023 433.01 Q373.705 433.01 373.1518 432.8512 Q372.5987 432.6924 372.1834 432.4057 L370.6216 436.0315 L367.817 436.0315 L372.9458 424.1393 L375.7503 424.1393 L375.3598 425.0457 Q376.2585 424.5034 376.9891 424.2013 Q377.7197 423.8991 378.5332 423.8991 Q379.8503 423.8991 380.3949 424.701 Q380.9395 425.5028 380.656 426.9206 ZM376.412 429.9731 Q376.8427 429.485 377.1518 428.8303 Q377.4609 428.1757 377.6143 427.4087 Q377.7553 426.7037 377.5206 426.3279 Q377.2859 425.9522 376.4879 425.9522 Q376.0772 425.9522 375.6535 426.0955 Q375.2297 426.2388 374.7416 426.4713 L372.859 430.8485 Q373.0852 430.957 373.4029 431.0151 Q373.7205 431.0732 374.0691 431.0732 Q374.7741 431.0732 375.3769 430.7711 Q375.9796 430.4689 376.412 429.9731 ZM388.4297 427.3699 Q388.4809 427.2305 388.5157 427.1143 Q388.5506 426.9981 388.5754 426.8741 Q388.6978 426.2621 388.3832 425.9367 Q388.0687 425.6113 387.356 425.6113 Q386.496 425.6113 385.8181 426.0761 Q385.1402 426.541 384.6722 427.3699 ZM385.1588 433.072 Q382.9353 433.072 381.9235 432.0881 Q380.9117 431.1042 381.2587 429.3688 Q381.4958 428.1834 382.0893 427.1917 Q382.6827 426.2001 383.5768 425.4486 Q384.4352 424.7203 385.5895 424.2942 Q386.7439 423.8681 388.0222 423.8681 Q389.9668 423.8681 390.7973 424.6545 Q391.6279 425.4408 391.321 426.9748 Q391.2157 427.5016 391.0104 428.0052 Q390.8051 428.5088 390.5355 429.0046 L384.0742 429.0046 Q384.0648 429.0511 384.0563 429.0937 Q384.0478 429.1364 384.0385 429.1828 Q383.8572 430.0893 384.3902 430.6006 Q384.9232 431.1119 386.2326 431.1119 Q387.1468 431.1119 388.0261 430.802 Q388.9054 430.4921 389.5856 430.1125 L389.8955 430.1125 L388.9286 432.3515 Q388.0439 432.7079 387.1088 432.89 Q386.1737 433.072 385.1588 433.072 ZM404.5056 420.7847 L399.3055 432.8396 L396.5009 432.8396 L396.8914 431.9332 Q396.4374 432.1888 396.0903 432.3941 Q395.7433 432.5994 395.3373 432.7699 Q394.9577 432.9248 394.6052 433.0023 Q394.2527 433.0798 393.7801 433.0798 Q392.5172 433.0798 391.9261 432.2585 Q391.335 431.4373 391.6371 429.9266 Q391.885 428.687 392.5017 427.5985 Q393.1184 426.51 393.983 425.6732 Q394.8275 424.8598 395.8533 424.3795 Q396.879 423.8991 397.8939 423.8991 Q398.6067 423.8991 399.1049 424.0424 Q399.603 424.1858 400.1004 424.4879 L401.701 420.7847 ZM399.3768 426.1691 Q399.1738 426.0606 398.8275 425.9715 Q398.4812 425.8824 398.2023 425.8824 Q397.536 425.8824 396.9425 426.1768 Q396.3491 426.4713 395.875 426.9826 Q395.4504 427.4397 395.1149 428.1486 Q394.7795 428.8575 394.6478 429.516 Q394.4913 430.2985 394.7841 430.6548 Q395.077 431.0112 395.8207 431.0112 Q396.2003 431.0112 396.6567 430.8408 Q397.113 430.6703 397.5391 430.4379 Z" stroke="none"/> +<path fill="black" d="M421.9023 429.3649 Q424.4008 429.3649 425.8767 431.108 Q427.3526 432.8512 427.3526 435.6984 Q427.3526 438.5688 425.8767 440.3236 Q424.4008 442.0784 421.9023 442.0784 Q419.3922 442.0784 417.9221 440.3294 Q416.452 438.5804 416.452 435.6984 Q416.452 432.8396 417.9279 431.1022 Q419.4038 429.3649 421.9023 429.3649 ZM421.9023 440.4979 Q423.5525 440.4979 424.5054 439.1034 Q425.4584 437.7088 425.4584 435.7216 Q425.4584 433.6298 424.5171 432.2818 Q423.5757 430.9337 421.9023 430.9337 Q420.2173 430.9337 419.2876 432.2643 Q418.3579 433.595 418.3579 435.7216 Q418.3579 437.7437 419.2992 439.1208 Q420.2405 440.4979 421.9023 440.4979 ZM441.4258 429.6554 L436.8354 441.7646 L435.1852 441.7646 L430.6181 429.6554 L432.5937 429.6554 L436.0219 439.2893 L439.4502 429.6554 ZM455.5221 435.7681 L447.213 435.7681 Q447.213 437.825 448.4042 439.1615 Q449.5954 440.4979 451.3269 440.4979 Q452.9771 440.4979 454.7319 439.9052 L455.0457 441.3578 Q453.442 442.0784 451.3037 442.0784 Q448.6076 442.0784 446.9574 440.3468 Q445.3072 438.6153 445.3072 435.6751 Q445.3072 432.7931 446.7889 431.079 Q448.2706 429.3649 450.618 429.3649 Q452.7098 429.3649 454.116 430.957 Q455.5221 432.5491 455.5221 435.1987 ZM453.5465 434.2922 Q453.5465 432.8745 452.6633 431.9041 Q451.7802 430.9337 450.6877 430.9337 Q449.2816 430.9337 448.3577 431.8634 Q447.4338 432.7931 447.3293 434.2922 ZM469.6302 429.9808 L468.898 431.724 Q467.701 431.201 466.8295 431.201 Q466.0276 431.201 465.2606 431.66 Q464.4937 432.1191 463.8487 432.9732 Q463.2037 433.8274 463.2037 433.932 L463.2037 441.7646 L461.4024 441.7646 L461.4024 429.6554 L463.2037 429.6554 L463.2037 432.1075 Q464.6563 429.3649 466.9689 429.3649 Q468.3402 429.3649 469.6302 429.9808 Z" stroke="none"/> +</g> +<g stroke-linecap="butt" stroke-width="1.9833"> +<line y2="443.0868" fill="none" x1="414.767" x2="471.8963" y1="443.0868"/> +<line y2="444.0868" fill="none" stroke-width="2" x1="414.767" x2="471.8963" y1="444.0868"/> +</g> +<g fill="rgb(102,0,102)" stroke="rgb(102,0,102)"> +<path d="M492.2913 435.6054 Q491.1571 435.6984 490.1553 435.8262 Q489.1536 435.954 488.4192 436.1981 Q487.6591 436.4537 487.1943 436.9186 Q486.7294 437.3834 486.576 438.1504 Q486.4412 438.8245 486.8805 439.0975 Q487.3198 439.3706 488.2611 439.3706 Q488.877 439.3706 489.637 439.0859 Q490.3971 438.8012 491.1013 438.3596 ZM490.232 440.3817 Q489.7858 440.6373 489.1559 440.9976 Q488.5261 441.3578 488.0078 441.567 Q487.3035 441.8343 486.7062 441.9738 Q486.1089 442.1132 485.0978 442.1132 Q483.4709 442.1132 482.6249 441.201 Q481.7789 440.2887 482.0717 438.8245 Q482.3808 437.2788 483.3279 436.2039 Q484.2751 435.1289 485.8439 434.4898 Q487.3128 433.8855 489.2315 433.6182 Q491.1501 433.3509 493.3488 433.2231 Q493.3744 433.1534 493.4406 432.9965 Q493.5069 432.8396 493.5464 432.642 Q493.7114 431.8169 493.0107 431.4857 Q492.3099 431.1545 490.7875 431.1545 Q489.7532 431.1545 488.4343 431.5032 Q487.1153 431.8518 486.4412 432.0842 L486.0577 432.0842 L487.2919 428.993 Q488.0612 428.8071 489.6417 428.5746 Q491.2222 428.3422 492.7445 428.3422 Q495.8125 428.3422 497.0873 429.1441 Q498.3622 429.9459 498.0251 431.631 Q497.9787 431.8634 497.8636 432.2353 Q497.7486 432.6072 497.623 432.8861 L493.802 441.7646 L489.6417 441.7646 ZM517.767 423.6822 L509.967 441.7646 L505.7601 441.7646 L513.5602 423.6822 ZM524.4585 435.6054 Q523.3243 435.6984 522.3225 435.8262 Q521.3208 435.954 520.5863 436.1981 Q519.8263 436.4537 519.3615 436.9186 Q518.8966 437.3834 518.7432 438.1504 Q518.6084 438.8245 519.0477 439.0975 Q519.4869 439.3706 520.4283 439.3706 Q521.0442 439.3706 521.8042 439.0859 Q522.5642 438.8012 523.2685 438.3596 ZM522.3992 440.3817 Q521.9529 440.6373 521.3231 440.9976 Q520.6932 441.3578 520.1749 441.567 Q519.4707 441.8343 518.8734 441.9738 Q518.2761 442.1132 517.265 442.1132 Q515.6381 442.1132 514.7921 441.201 Q513.946 440.2887 514.2389 438.8245 Q514.548 437.2788 515.4951 436.2039 Q516.4423 435.1289 518.0111 434.4898 Q519.48 433.8855 521.3986 433.6182 Q523.3173 433.3509 525.516 433.2231 Q525.5416 433.1534 525.6078 432.9965 Q525.674 432.8396 525.7136 432.642 Q525.8785 431.8169 525.1778 431.4857 Q524.4771 431.1545 522.9547 431.1545 Q521.9204 431.1545 520.6014 431.5032 Q519.2824 431.8518 518.6084 432.0842 L518.2249 432.0842 L519.4591 428.993 Q520.2284 428.8071 521.8088 428.5746 Q523.3893 428.3422 524.9117 428.3422 Q527.9797 428.3422 529.2545 429.1441 Q530.5294 429.9459 530.1923 431.631 Q530.1458 431.8634 530.0308 432.2353 Q529.9157 432.6072 529.7902 432.8861 L525.9692 441.7646 L521.8088 441.7646 ZM541.0069 441.7646 L528.7466 441.7646 L529.8646 439.1963 L539.9494 431.6542 L533.3835 431.6542 L534.6571 428.7141 L546.5106 428.7141 L545.4136 431.2359 L535.4357 438.7664 L542.3038 438.7664 ZM547.8471 446.5525 L543.2568 446.5525 L547.5891 441.5089 L547.9982 428.7141 L552.2747 428.7141 L551.6913 437.2672 L558.3758 428.7141 L562.7918 428.7141 Z" stroke="none"/> +<line stroke-linecap="butt" fill="none" x1="481.2629" x2="567.1544" y1="432.1935" y2="432.1935" stroke-width="1.9833"/> +<line stroke-linecap="butt" fill="none" x1="481.2629" x2="567.1544" y1="443.0868" y2="443.0868" stroke-width="1.9833"/> +<line stroke-linecap="butt" fill="none" x1="481.2629" x2="567.1544" y1="444.0868" y2="444.0868" stroke-width="2"/> +<path fill="black" d="M577.0121 450.6896 L575.5557 450.6896 L575.5557 449.7831 Q574.9281 450.3254 574.2463 450.6276 Q573.5646 450.9297 572.7666 450.9297 Q571.2171 450.9297 570.3068 449.7366 Q569.3965 448.5435 569.3965 446.4285 Q569.3965 445.3284 569.7103 444.4684 Q570.024 443.6085 570.5586 443.0042 Q571.0854 442.4153 571.7866 442.1055 Q572.4877 441.7956 573.2392 441.7956 Q573.921 441.7956 574.4478 441.9389 Q574.9746 442.0822 575.5557 442.3844 L575.5557 438.6346 L577.0121 438.6346 ZM575.5557 448.5591 L575.5557 443.593 Q574.9669 443.3296 574.502 443.2288 Q574.0372 443.1281 573.4871 443.1281 Q572.263 443.1281 571.5812 443.9803 Q570.8995 444.8326 570.8995 446.3975 Q570.8995 447.9392 571.4263 448.7411 Q571.9531 449.543 573.1152 449.543 Q573.735 449.543 574.3703 449.2679 Q575.0056 448.9929 575.5557 448.5591 ZM587.2542 446.3665 Q587.2542 448.4816 586.1696 449.7057 Q585.085 450.9297 583.2643 450.9297 Q581.4282 450.9297 580.3474 449.7057 Q579.2667 448.4816 579.2667 446.3665 Q579.2667 444.2515 580.3474 443.0235 Q581.4282 441.7956 583.2643 441.7956 Q585.085 441.7956 586.1696 443.0235 Q587.2542 444.2515 587.2542 446.3665 ZM585.7512 446.3665 Q585.7512 444.6853 585.0927 443.868 Q584.4341 443.0506 583.2643 443.0506 Q582.079 443.0506 581.4243 443.868 Q580.7697 444.6853 580.7697 446.3665 Q580.7697 447.9935 581.4282 448.8341 Q582.0867 449.6747 583.2643 449.6747 Q584.4265 449.6747 585.0888 448.8418 Q585.7512 448.009 585.7512 446.3665 ZM596.5278 449.7057 Q596.5278 451.9059 595.5284 452.9363 Q594.529 453.9667 592.4527 453.9667 Q591.7632 453.9667 591.1085 453.8699 Q590.4539 453.773 589.8186 453.5948 L589.8186 452.1074 L589.8961 452.1074 Q590.2524 452.2468 591.0272 452.4521 Q591.8019 452.6574 592.5767 452.6574 Q593.3204 452.6574 593.8085 452.4792 Q594.2966 452.301 594.5677 451.9834 Q594.8389 451.6812 594.9551 451.2551 Q595.0714 450.829 595.0714 450.3022 L595.0714 449.512 Q594.4128 450.0388 593.8124 450.2983 Q593.212 450.5579 592.2823 450.5579 Q590.7328 450.5579 589.8224 449.4384 Q588.9122 448.3189 588.9122 446.2813 Q588.9122 445.1657 589.226 444.3561 Q589.5397 443.5465 590.082 442.9577 Q590.5856 442.4076 591.3061 442.1016 Q592.0266 441.7956 592.7394 441.7956 Q593.4908 441.7956 593.9984 441.9466 Q594.5058 442.0977 595.0714 442.4076 L595.1643 442.0357 L596.5278 442.0357 ZM595.0714 448.3111 L595.0714 443.593 Q594.4903 443.3296 593.9906 443.2172 Q593.4908 443.1049 592.9951 443.1049 Q591.7942 443.1049 591.1047 443.9106 Q590.4152 444.7163 590.4152 446.2503 Q590.4152 447.7068 590.9265 448.4583 Q591.4378 449.2098 592.6232 449.2098 Q593.2584 449.2098 593.8976 448.9658 Q594.5367 448.7217 595.0714 448.3111 Z" stroke="none"/> +</g> +<g fill="rgb(0,0,0)" fill-opacity="0.349" transform="translate(0,1.811)" stroke-opacity="0.349" stroke="rgb(0,0,0)"> +<path d="M139.7672 494.3594 L224.4545 494.3594 L224.4545 484.8195 L243.5345 503.8994 L224.4545 522.9794 L224.4545 513.4395 L139.7672 513.4395 Z" stroke="none"/> +</g> +<g fill="rgb(185,113,53)" fill-opacity="0.502" stroke-opacity="0.502" stroke="rgb(185,113,53)"> +<path d="M139.7672 494.3594 L224.4545 494.3594 L224.4545 484.8195 L243.5345 503.8994 L224.4545 522.9794 L224.4545 513.4395 L139.7672 513.4395 Z" stroke="none"/> +</g> +<g stroke-linecap="butt" transform="translate(0,1.811)" fill-opacity="0.349" fill="rgb(0,0,0)" stroke-linejoin="round" stroke="rgb(0,0,0)" stroke-width="0.75" stroke-opacity="0.349" stroke-miterlimit="1"> +<path fill="none" d="M139.7672 494.3594 L224.4545 494.3594 L224.4545 484.8195 L243.5345 503.8994 L224.4545 522.9794 L224.4545 513.4395 L139.7672 513.4395 Z"/> +</g> +<g fill="rgb(75,122,179)" stroke-width="0.75" stroke-miterlimit="1" stroke-linejoin="round" stroke-linecap="butt" stroke="rgb(75,122,179)"> +<path fill="none" d="M139.7672 494.3594 L224.4545 494.3594 L224.4545 484.8195 L243.5345 503.8994 L224.4545 522.9794 L224.4545 513.4395 L139.7672 513.4395 Z"/> +</g> +<g stroke-linecap="butt" transform="translate(0,1.811)" fill-opacity="0.349" fill="rgb(0,0,0)" stroke-linejoin="round" stroke="rgb(0,0,0)" stroke-width="0.75" stroke-opacity="0.349" stroke-miterlimit="1"> +<path d="M280.6709 500.5583 C280.6709 488.1755 303.5645 478.1372 331.8052 478.1372 C360.0459 478.1372 382.9395 488.1755 382.9395 500.5583 C382.9395 512.9412 360.0459 522.9794 331.8052 522.9794 C303.5645 522.9794 280.6709 512.9412 280.6709 500.5583 Z" stroke="none"/> +</g> +<g stroke-linecap="butt" fill-opacity="0.502" fill="rgb(185,113,53)" stroke-linejoin="round" stroke="rgb(185,113,53)" stroke-width="0.75" stroke-opacity="0.502" stroke-miterlimit="1"> +<path d="M280.6709 500.5583 C280.6709 488.1755 303.5645 478.1372 331.8052 478.1372 C360.0459 478.1372 382.9395 488.1755 382.9395 500.5583 C382.9395 512.9412 360.0459 522.9794 331.8052 522.9794 C303.5645 522.9794 280.6709 512.9412 280.6709 500.5583 Z" stroke="none"/> +</g> +<g font-size="18" stroke-linecap="butt" fill="white" font-family="'Calibri'" stroke-linejoin="round" stroke="white" stroke-width="0.75" stroke-miterlimit="1"> +<text x="318.9292" xml:space="preserve" y="506.7107" stroke="none">POI</text> +</g> +<g stroke-linecap="butt" transform="translate(0,1.811)" fill-opacity="0.349" fill="rgb(0,0,0)" stroke-linejoin="round" stroke="rgb(0,0,0)" stroke-width="0.75" stroke-opacity="0.349" stroke-miterlimit="1"> +<path fill="none" d="M280.6709 500.5583 C280.6709 488.1755 303.5645 478.1372 331.8052 478.1372 C360.0459 478.1372 382.9395 488.1755 382.9395 500.5583 C382.9395 512.9412 360.0459 522.9794 331.8052 522.9794 C303.5645 522.9794 280.6709 512.9412 280.6709 500.5583 Z"/> +</g> +<g fill="rgb(75,122,179)" stroke-width="0.75" stroke-miterlimit="1" stroke-linejoin="round" stroke-linecap="butt" stroke="rgb(75,122,179)"> +<path fill="none" d="M280.6709 500.5583 C280.6709 488.1755 303.5645 478.1372 331.8052 478.1372 C360.0459 478.1372 382.9395 488.1755 382.9395 500.5583 C382.9395 512.9412 360.0459 522.9794 331.8052 522.9794 C303.5645 522.9794 280.6709 512.9412 280.6709 500.5583 Z"/> +</g> +<g stroke-linecap="butt" transform="translate(0,1.811)" fill-opacity="0.349" fill="rgb(0,0,0)" stroke-linejoin="round" stroke="rgb(0,0,0)" stroke-width="0.75" stroke-opacity="0.349" stroke-miterlimit="1"> +<path d="M417.029 503.8994 L436.109 484.8195 L436.109 494.3594 L520.434 494.3594 L520.434 513.4395 L436.109 513.4395 L436.109 522.9794 Z" stroke="none"/> +</g> +<g stroke-linecap="butt" fill-opacity="0.502" fill="rgb(185,113,53)" stroke-linejoin="round" stroke="rgb(185,113,53)" stroke-width="0.75" stroke-opacity="0.502" stroke-miterlimit="1"> +<path d="M417.029 503.8994 L436.109 484.8195 L436.109 494.3594 L520.434 494.3594 L520.434 513.4395 L436.109 513.4395 L436.109 522.9794 Z" stroke="none"/> +</g> +<g stroke-linecap="butt" transform="translate(0,1.811)" fill-opacity="0.349" fill="rgb(0,0,0)" stroke-linejoin="round" stroke="rgb(0,0,0)" stroke-width="0.75" stroke-opacity="0.349" stroke-miterlimit="1"> +<path fill="none" d="M417.029 503.8994 L436.109 484.8195 L436.109 494.3594 L520.434 494.3594 L520.434 513.4395 L436.109 513.4395 L436.109 522.9794 Z"/> +</g> +<g fill="rgb(75,122,179)" stroke-width="0.75" stroke-miterlimit="1" stroke-linejoin="round" stroke-linecap="butt" stroke="rgb(75,122,179)"> +<path fill="none" d="M417.029 503.8994 L436.109 484.8195 L436.109 494.3594 L520.434 494.3594 L520.434 513.4395 L436.109 513.4395 L436.109 522.9794 Z"/> +</g> +</g> +</svg> diff --git a/test-data/slideshow/themes.pptx b/test-data/slideshow/themes.pptx Binary files differnew file mode 100755 index 0000000000..ff39de3ed4 --- /dev/null +++ b/test-data/slideshow/themes.pptx |