From 01039a9d79ed6fc5ff6a3cebb8be78572cfd5eaf Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Mon, 21 Nov 2011 13:10:14 +0000 Subject: [PATCH] added missing definition of the upArrow shape, moved support for line decorations to XSLFSimpleShape git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1204477 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/xslf/model/geom/PresetGeometries.java | 3 + .../poi/xslf/usermodel/RenderableShape.java | 29 +- .../poi/xslf/usermodel/XSLFAutoShape.java | 5 + .../xslf/usermodel/XSLFConnectorShape.java | 274 ---------------- .../poi/xslf/usermodel/XSLFSimpleShape.java | 294 ++++++++++++++++-- .../poi/xslf/geom/TestPresetGeometries.java | 2 +- .../xslf/usermodel/presetShapeDefinitions.xml | 17 +- 7 files changed, 313 insertions(+), 311 deletions(-) diff --git a/src/ooxml/java/org/apache/poi/xslf/model/geom/PresetGeometries.java b/src/ooxml/java/org/apache/poi/xslf/model/geom/PresetGeometries.java index 433c0cbe2f..62accc42f3 100644 --- a/src/ooxml/java/org/apache/poi/xslf/model/geom/PresetGeometries.java +++ b/src/ooxml/java/org/apache/poi/xslf/model/geom/PresetGeometries.java @@ -51,6 +51,9 @@ public class PresetGeometries extends LinkedHashMap { String name = def.getDomNode().getLocalName(); CTCustomGeometry2D geom = CTCustomGeometry2D.Factory.parse(def.toString()); + if(containsKey(name)) { + System.out.println("Duplicate definoition of " + name) ; + } put(name, new CustomGeometry(geom)); } } 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 c59ea6d1dd..21ab67124f 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java @@ -120,7 +120,7 @@ class RenderableShape { else if (obj instanceof CTGradientFillProperties) { CTGradientFillProperties gradFill = (CTGradientFillProperties) obj; if (gradFill.isSetLin()) { - paint = createLinearGradientPaint(gradFill, anchor, theme, phClr); + paint = createLinearGradientPaint(graphics, gradFill, anchor, theme, phClr); } else if (gradFill.isSetPath()){ CTPathShadeProperties ps = gradFill.getPath(); if(ps.getPath() == STPathShadeType.CIRCLE){ @@ -166,6 +166,7 @@ class RenderableShape { } private static Paint createLinearGradientPaint( + Graphics2D graphics, CTGradientFillProperties gradFill, Rectangle2D anchor, XSLFTheme theme, CTSchemeColor phClr) { double angle = gradFill.getLin().getAng() / 60000; @@ -204,13 +205,30 @@ class RenderableShape { fractions[i] = stop.getPos() / 100000.f; } + AffineTransform grAt; + if(gradFill.getRotWithShape()) grAt = new AffineTransform(); + else { + // gradient fill is not rotated with the shape + try { + grAt = graphics.getTransform().createInverse(); + } catch (Exception e){ + // should not happen. + grAt = new AffineTransform(); + } + } + // Trick to return GradientPaint on JDK 1.5 and LinearGradientPaint on JDK 1.6+ Paint paint; try { Class clz = Class.forName("java.awt.LinearGradientPaint"); + Class clzCycleMethod = Class.forName("java.awt.MultipleGradientPaint$CycleMethod"); + Class clzColorSpaceType = Class.forName("java.awt.MultipleGradientPaint$ColorSpaceType"); Constructor c = - clz.getConstructor(Point2D.class, Point2D.class, float[].class, Color[].class); - paint = (Paint) c.newInstance(p1, p2, fractions, colors); + clz.getConstructor(Point2D.class, Point2D.class, float[].class, Color[].class, + clzCycleMethod, clzColorSpaceType, AffineTransform.class); + paint = (Paint) c.newInstance(p1, p2, fractions, colors, + Enum.valueOf(clzCycleMethod, "NO_CYCLE"), + Enum.valueOf(clzColorSpaceType, "SRGB"), grAt); } catch (ClassNotFoundException e) { paint = new GradientPaint(p1, colors[0], p2, colors[colors.length - 1]); } catch (Exception e) { @@ -504,9 +522,12 @@ class RenderableShape { } private Collection computeOutlines() { - CustomGeometry geom = _shape.getGeometry(); Collection lst = new ArrayList(); + CustomGeometry geom = _shape.getGeometry(); + if(geom == null) { + return lst; + } Rectangle2D anchor = _shape.getAnchor(); for (Path p : geom) { 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 5f4e838a0c..b880e5988d 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFAutoShape.java @@ -80,4 +80,9 @@ public class XSLFAutoShape extends XSLFTextShape { } return txBody; } + + @Override + public String toString(){ + return "[" + getClass().getSimpleName() + "] " + getShapeName(); + } } 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 cd3a925d54..3f7d88b870 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java @@ -75,262 +75,6 @@ public class XSLFConnectorShape extends XSLFSimpleShape { return ct; } - /** - * Specifies the line end decoration, such as a triangle or arrowhead. - */ - public void setLineHeadDecoration(LineDecoration style) { - CTLineProperties ln = getSpPr().getLn(); - CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd(); - if (style == null) { - if (lnEnd.isSetType()) lnEnd.unsetType(); - } else { - lnEnd.setType(STLineEndType.Enum.forInt(style.ordinal() + 1)); - } - } - - public LineDecoration getLineHeadDecoration() { - CTLineProperties ln = getSpPr().getLn(); - if (ln == null || !ln.isSetHeadEnd()) return LineDecoration.NONE; - - STLineEndType.Enum end = ln.getHeadEnd().getType(); - return end == null ? LineDecoration.NONE : LineDecoration.values()[end.intValue() - 1]; - } - - /** - * specifies decorations which can be added to the head of a line. - */ - public void setLineHeadWidth(LineEndWidth style) { - CTLineProperties ln = getSpPr().getLn(); - CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd(); - if (style == null) { - if (lnEnd.isSetW()) lnEnd.unsetW(); - } else { - lnEnd.setW(STLineEndWidth.Enum.forInt(style.ordinal() + 1)); - } - } - - public LineEndWidth getLineHeadWidth() { - CTLineProperties ln = getSpPr().getLn(); - if (ln == null || !ln.isSetHeadEnd()) return LineEndWidth.MEDIUM; - - STLineEndWidth.Enum w = ln.getHeadEnd().getW(); - return w == null ? LineEndWidth.MEDIUM : LineEndWidth.values()[w.intValue() - 1]; - } - - /** - * Specifies the line end width in relation to the line width. - */ - public void setLineHeadLength(LineEndLength style) { - CTLineProperties ln = getSpPr().getLn(); - CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd(); - - if (style == null) { - if (lnEnd.isSetLen()) lnEnd.unsetLen(); - } else { - lnEnd.setLen(STLineEndLength.Enum.forInt(style.ordinal() + 1)); - } - } - - public LineEndLength getLineHeadLength() { - CTLineProperties ln = getSpPr().getLn(); - if (ln == null || !ln.isSetHeadEnd()) return LineEndLength.MEDIUM; - - STLineEndLength.Enum len = ln.getHeadEnd().getLen(); - return len == null ? LineEndLength.MEDIUM : LineEndLength.values()[len.intValue() - 1]; - } - - /** - * Specifies the line end decoration, such as a triangle or arrowhead. - */ - public void setLineTailDecoration(LineDecoration style) { - CTLineProperties ln = getSpPr().getLn(); - CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd(); - if (style == null) { - if (lnEnd.isSetType()) lnEnd.unsetType(); - } else { - lnEnd.setType(STLineEndType.Enum.forInt(style.ordinal() + 1)); - } - } - - public LineDecoration getLineTailDecoration() { - CTLineProperties ln = getSpPr().getLn(); - if (ln == null || !ln.isSetTailEnd()) return LineDecoration.NONE; - - STLineEndType.Enum end = ln.getTailEnd().getType(); - return end == null ? LineDecoration.NONE : LineDecoration.values()[end.intValue() - 1]; - } - - /** - * specifies decorations which can be added to the tail of a line. - */ - public void setLineTailWidth(LineEndWidth style) { - CTLineProperties ln = getSpPr().getLn(); - CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd(); - if (style == null) { - if (lnEnd.isSetW()) lnEnd.unsetW(); - } else { - lnEnd.setW(STLineEndWidth.Enum.forInt(style.ordinal() + 1)); - } - } - - public LineEndWidth getLineTailWidth() { - CTLineProperties ln = getSpPr().getLn(); - if (ln == null || !ln.isSetTailEnd()) return LineEndWidth.MEDIUM; - - STLineEndWidth.Enum w = ln.getTailEnd().getW(); - return w == null ? LineEndWidth.MEDIUM : LineEndWidth.values()[w.intValue() - 1]; - } - - /** - * Specifies the line end width in relation to the line width. - */ - public void setLineTailLength(LineEndLength style) { - CTLineProperties ln = getSpPr().getLn(); - CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd(); - - if (style == null) { - if (lnEnd.isSetLen()) lnEnd.unsetLen(); - } else { - lnEnd.setLen(STLineEndLength.Enum.forInt(style.ordinal() + 1)); - } - } - - public LineEndLength getLineTailLength() { - CTLineProperties ln = getSpPr().getLn(); - if (ln == null || !ln.isSetTailEnd()) return LineEndLength.MEDIUM; - - STLineEndLength.Enum len = ln.getTailEnd().getLen(); - return len == null ? LineEndLength.MEDIUM : LineEndLength.values()[len.intValue() - 1]; - } - - Outline getTailDecoration() { - LineEndLength tailLength = getLineTailLength(); - LineEndWidth tailWidth = getLineTailWidth(); - - double lineWidth = Math.max(2.5, getLineWidth()); - - Rectangle2D anchor = getAnchor(); - double x2 = anchor.getX() + anchor.getWidth(), - y2 = anchor.getY() + anchor.getHeight(); - - double alpha = Math.atan(anchor.getHeight() / anchor.getWidth()); - - 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 (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); - arrow.lineTo((float) (-lineWidth * 3), (float) (lineWidth * 2)); - shape = arrow; - at.translate(x2, y2); - at.rotate(alpha); - break; - case TRIANGLE: - p = new Path(); - scaleY = tailWidth.ordinal() + 1; - scaleX = tailLength.ordinal() + 1; - GeneralPath triangle = new GeneralPath(); - triangle.moveTo((float) (-lineWidth * scaleX), (float) (-lineWidth * scaleY / 2)); - triangle.lineTo(0, 0); - triangle.lineTo((float) (-lineWidth * scaleX), (float) (lineWidth * scaleY / 2)); - triangle.closePath(); - shape = triangle; - at.translate(x2, y2); - at.rotate(alpha); - break; - default: - break; - } - - if (shape != null) { - shape = at.createTransformedShape(shape); - } - return shape == null ? null : new Outline(shape, p); - } - - Outline getHeadDecoration() { - LineEndLength headLength = getLineHeadLength(); - LineEndWidth headWidth = getLineHeadWidth(); - - double lineWidth = Math.max(2.5, getLineWidth()); - Rectangle2D anchor = getAnchor(); - double x1 = anchor.getX(), - y1 = anchor.getY(); - - double alpha = Math.atan(anchor.getHeight() / anchor.getWidth()); - - 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); - at.rotate(alpha, bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2); - break; - case STEALTH: - case ARROW: - p = new Path(false, true); - GeneralPath arrow = new GeneralPath(); - arrow.moveTo((float) (lineWidth * 3 * scaleX), (float) (-lineWidth * scaleY * 2)); - arrow.lineTo(0, 0); - arrow.lineTo((float) (lineWidth * 3 * scaleX), (float) (lineWidth * scaleY * 2)); - shape = arrow; - at.translate(x1, y1); - at.rotate(alpha); - break; - case TRIANGLE: - p = new Path(); - scaleY = headWidth.ordinal() + 1; - scaleX = headLength.ordinal() + 1; - GeneralPath triangle = new GeneralPath(); - triangle.moveTo((float) (lineWidth * scaleX), (float) (-lineWidth * scaleY / 2)); - triangle.lineTo(0, 0); - triangle.lineTo((float) (lineWidth * scaleX), (float) (lineWidth * scaleY / 2)); - triangle.closePath(); - shape = triangle; - at.translate(x1, y1); - at.rotate(alpha); - break; - default: - break; - } - - if (shape != null) { - shape = at.createTransformedShape(shape); - } - return shape == null ? null : new Outline(shape, p); - } - - private List getDecorationOutlines(){ - List lst = new ArrayList(); - - Outline head = getHeadDecoration(); - if(head != null) lst.add(head); - - Outline tail = getTailDecoration(); - if(tail != null) lst.add(tail); - return lst; - } /** * YK: dashing of lines is suppressed for now. @@ -341,22 +85,4 @@ public class XSLFConnectorShape extends XSLFSimpleShape { return null; } - @Override - public void draw(Graphics2D graphics){ - super.draw(graphics); - - // draw line decorations - Color lineColor = getLineColor(); - if(lineColor != null) { - graphics.setPaint(lineColor); - for(Outline o : getDecorationOutlines()){ - if(o.getPath().isFilled()){ - graphics.fill(o.getOutline()); - } - if(o.getPath().isStroked()){ - graphics.draw(o.getOutline()); - } - } - } - } } \ No newline at end of file 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 92adf446e0..16ecef8623 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java @@ -35,25 +35,7 @@ import org.apache.poi.openxml4j.opc.TargetMode; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.POIXMLException; import org.apache.xmlbeans.XmlObject; -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.drawingml.x2006.main.CTBlip; +import org.openxmlformats.schemas.drawingml.x2006.main.*; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; @@ -63,8 +45,10 @@ import java.awt.Graphics2D; import java.awt.Paint; 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.Collection; import java.util.Collections; import java.util.List; @@ -555,6 +539,20 @@ public abstract class XSLFSimpleShape extends XSLFShape { public void draw(Graphics2D graphics) { RenderableShape rShape = new RenderableShape(this); rShape.render(graphics); + + // draw line decorations + Color lineColor = getLineColor(); + if(lineColor != null) { + graphics.setPaint(lineColor); + for(Outline o : getDecorationOutlines()){ + if(o.getPath().isFilled()){ + graphics.fill(o.getOutline()); + } + if(o.getPath().isStroked()){ + graphics.draw(o.getOutline()); + } + } + } } @@ -696,4 +694,262 @@ public abstract class XSLFSimpleShape extends XSLFShape { } } + + /** + * Specifies the line end decoration, such as a triangle or arrowhead. + */ + public void setLineHeadDecoration(LineDecoration style) { + CTLineProperties ln = getSpPr().getLn(); + CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd(); + if (style == null) { + if (lnEnd.isSetType()) lnEnd.unsetType(); + } else { + lnEnd.setType(STLineEndType.Enum.forInt(style.ordinal() + 1)); + } + } + + public LineDecoration getLineHeadDecoration() { + CTLineProperties ln = getSpPr().getLn(); + if (ln == null || !ln.isSetHeadEnd()) return LineDecoration.NONE; + + STLineEndType.Enum end = ln.getHeadEnd().getType(); + return end == null ? LineDecoration.NONE : LineDecoration.values()[end.intValue() - 1]; + } + + /** + * specifies decorations which can be added to the head of a line. + */ + public void setLineHeadWidth(LineEndWidth style) { + CTLineProperties ln = getSpPr().getLn(); + CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd(); + if (style == null) { + if (lnEnd.isSetW()) lnEnd.unsetW(); + } else { + lnEnd.setW(STLineEndWidth.Enum.forInt(style.ordinal() + 1)); + } + } + + public LineEndWidth getLineHeadWidth() { + CTLineProperties ln = getSpPr().getLn(); + if (ln == null || !ln.isSetHeadEnd()) return LineEndWidth.MEDIUM; + + STLineEndWidth.Enum w = ln.getHeadEnd().getW(); + return w == null ? LineEndWidth.MEDIUM : LineEndWidth.values()[w.intValue() - 1]; + } + + /** + * Specifies the line end width in relation to the line width. + */ + public void setLineHeadLength(LineEndLength style) { + CTLineProperties ln = getSpPr().getLn(); + CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd(); + + if (style == null) { + if (lnEnd.isSetLen()) lnEnd.unsetLen(); + } else { + lnEnd.setLen(STLineEndLength.Enum.forInt(style.ordinal() + 1)); + } + } + + public LineEndLength getLineHeadLength() { + CTLineProperties ln = getSpPr().getLn(); + if (ln == null || !ln.isSetHeadEnd()) return LineEndLength.MEDIUM; + + STLineEndLength.Enum len = ln.getHeadEnd().getLen(); + return len == null ? LineEndLength.MEDIUM : LineEndLength.values()[len.intValue() - 1]; + } + + /** + * Specifies the line end decoration, such as a triangle or arrowhead. + */ + public void setLineTailDecoration(LineDecoration style) { + CTLineProperties ln = getSpPr().getLn(); + CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd(); + if (style == null) { + if (lnEnd.isSetType()) lnEnd.unsetType(); + } else { + lnEnd.setType(STLineEndType.Enum.forInt(style.ordinal() + 1)); + } + } + + public LineDecoration getLineTailDecoration() { + CTLineProperties ln = getSpPr().getLn(); + if (ln == null || !ln.isSetTailEnd()) return LineDecoration.NONE; + + STLineEndType.Enum end = ln.getTailEnd().getType(); + return end == null ? LineDecoration.NONE : LineDecoration.values()[end.intValue() - 1]; + } + + /** + * specifies decorations which can be added to the tail of a line. + */ + public void setLineTailWidth(LineEndWidth style) { + CTLineProperties ln = getSpPr().getLn(); + CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd(); + if (style == null) { + if (lnEnd.isSetW()) lnEnd.unsetW(); + } else { + lnEnd.setW(STLineEndWidth.Enum.forInt(style.ordinal() + 1)); + } + } + + public LineEndWidth getLineTailWidth() { + CTLineProperties ln = getSpPr().getLn(); + if (ln == null || !ln.isSetTailEnd()) return LineEndWidth.MEDIUM; + + STLineEndWidth.Enum w = ln.getTailEnd().getW(); + return w == null ? LineEndWidth.MEDIUM : LineEndWidth.values()[w.intValue() - 1]; + } + + /** + * Specifies the line end width in relation to the line width. + */ + public void setLineTailLength(LineEndLength style) { + CTLineProperties ln = getSpPr().getLn(); + CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd(); + + if (style == null) { + if (lnEnd.isSetLen()) lnEnd.unsetLen(); + } else { + lnEnd.setLen(STLineEndLength.Enum.forInt(style.ordinal() + 1)); + } + } + + public LineEndLength getLineTailLength() { + CTLineProperties ln = getSpPr().getLn(); + if (ln == null || !ln.isSetTailEnd()) return LineEndLength.MEDIUM; + + STLineEndLength.Enum len = ln.getTailEnd().getLen(); + return len == null ? LineEndLength.MEDIUM : LineEndLength.values()[len.intValue() - 1]; + } + + Outline getTailDecoration() { + LineEndLength tailLength = getLineTailLength(); + LineEndWidth tailWidth = getLineTailWidth(); + + double lineWidth = Math.max(2.5, getLineWidth()); + + Rectangle2D anchor = getAnchor(); + double x2 = anchor.getX() + anchor.getWidth(), + y2 = anchor.getY() + anchor.getHeight(); + + double alpha = Math.atan(anchor.getHeight() / anchor.getWidth()); + + 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 (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); + arrow.lineTo((float) (-lineWidth * 3), (float) (lineWidth * 2)); + shape = arrow; + at.translate(x2, y2); + at.rotate(alpha); + break; + case TRIANGLE: + p = new Path(); + scaleY = tailWidth.ordinal() + 1; + scaleX = tailLength.ordinal() + 1; + GeneralPath triangle = new GeneralPath(); + triangle.moveTo((float) (-lineWidth * scaleX), (float) (-lineWidth * scaleY / 2)); + triangle.lineTo(0, 0); + triangle.lineTo((float) (-lineWidth * scaleX), (float) (lineWidth * scaleY / 2)); + triangle.closePath(); + shape = triangle; + at.translate(x2, y2); + at.rotate(alpha); + break; + default: + break; + } + + if (shape != null) { + shape = at.createTransformedShape(shape); + } + return shape == null ? null : new Outline(shape, p); + } + + Outline getHeadDecoration() { + LineEndLength headLength = getLineHeadLength(); + LineEndWidth headWidth = getLineHeadWidth(); + + double lineWidth = Math.max(2.5, getLineWidth()); + Rectangle2D anchor = getAnchor(); + double x1 = anchor.getX(), + y1 = anchor.getY(); + + double alpha = Math.atan(anchor.getHeight() / anchor.getWidth()); + + 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); + at.rotate(alpha, bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2); + break; + case STEALTH: + case ARROW: + p = new Path(false, true); + GeneralPath arrow = new GeneralPath(); + arrow.moveTo((float) (lineWidth * 3 * scaleX), (float) (-lineWidth * scaleY * 2)); + arrow.lineTo(0, 0); + arrow.lineTo((float) (lineWidth * 3 * scaleX), (float) (lineWidth * scaleY * 2)); + shape = arrow; + at.translate(x1, y1); + at.rotate(alpha); + break; + case TRIANGLE: + p = new Path(); + scaleY = headWidth.ordinal() + 1; + scaleX = headLength.ordinal() + 1; + GeneralPath triangle = new GeneralPath(); + triangle.moveTo((float) (lineWidth * scaleX), (float) (-lineWidth * scaleY / 2)); + triangle.lineTo(0, 0); + triangle.lineTo((float) (lineWidth * scaleX), (float) (lineWidth * scaleY / 2)); + triangle.closePath(); + shape = triangle; + at.translate(x1, y1); + at.rotate(alpha); + break; + default: + break; + } + + if (shape != null) { + shape = at.createTransformedShape(shape); + } + return shape == null ? null : new Outline(shape, p); + } + + private List getDecorationOutlines(){ + List lst = new ArrayList(); + + Outline head = getHeadDecoration(); + if(head != null) lst.add(head); + + Outline tail = getTailDecoration(); + if(tail != null) lst.add(tail); + return lst; + } + } 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 7f5e967ccc..50a66667ab 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/geom/TestPresetGeometries.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/geom/TestPresetGeometries.java @@ -39,7 +39,7 @@ public class TestPresetGeometries extends TestCase { public void testRead(){ Map shapes = PresetGeometries.getInstance(); - assertEquals(186, shapes.size()); + assertEquals(187, shapes.size()); for(String name : shapes.keySet()) { diff --git a/src/resources/ooxml/org/apache/poi/xslf/usermodel/presetShapeDefinitions.xml b/src/resources/ooxml/org/apache/poi/xslf/usermodel/presetShapeDefinitions.xml index f5fead717f..4a3a0701d3 100755 --- a/src/resources/ooxml/org/apache/poi/xslf/usermodel/presetShapeDefinitions.xml +++ b/src/resources/ooxml/org/apache/poi/xslf/usermodel/presetShapeDefinitions.xml @@ -18822,7 +18822,7 @@ - + @@ -18890,19 +18890,10 @@ - - - - - - - - - - + - + @@ -18910,7 +18901,7 @@ - + -- 2.39.5