import org.openxmlformats.schemas.presentationml.x2006.main.CTConnectorNonVisual;\r
\r
import java.awt.*;\r
-import java.awt.geom.GeneralPath;\r
-import java.awt.geom.Rectangle2D;\r
+import java.awt.geom.*;\r
\r
/**\r
- *\r
- * Specifies a connection shape. \r
+ * Specifies a connection shape.\r
*\r
* @author Yegor Kozlov\r
*/\r
@Beta\r
public class XSLFConnectorShape extends XSLFSimpleShape {\r
\r
- /*package*/ XSLFConnectorShape(CTConnector shape, XSLFSheet sheet){\r
+ /*package*/ XSLFConnectorShape(CTConnector shape, XSLFSheet sheet) {\r
super(shape, sheet);\r
}\r
\r
/**\r
* Specifies the line end decoration, such as a triangle or arrowhead.\r
*/\r
- public void setLineHeadDecoration(LineDecoration style){\r
+ public void setLineHeadDecoration(LineDecoration style) {\r
CTLineProperties ln = getSpPr().getLn();\r
CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd();\r
- if(style == null){\r
- if(lnEnd.isSetType()) lnEnd.unsetType();\r
+ if (style == null) {\r
+ if (lnEnd.isSetType()) lnEnd.unsetType();\r
} else {\r
lnEnd.setType(STLineEndType.Enum.forInt(style.ordinal() + 1));\r
}\r
}\r
\r
- public LineDecoration getLineHeadDecoration(){\r
+ public LineDecoration getLineHeadDecoration() {\r
CTLineProperties ln = getSpPr().getLn();\r
- if(!ln.isSetHeadEnd()) return LineDecoration.NONE;\r
+ if (ln == null || !ln.isSetHeadEnd()) return LineDecoration.NONE;\r
\r
STLineEndType.Enum end = ln.getHeadEnd().getType();\r
return end == null ? LineDecoration.NONE : LineDecoration.values()[end.intValue() - 1];\r
/**\r
* specifies decorations which can be added to the head of a line.\r
*/\r
- public void setLineHeadWidth(LineEndWidth style){\r
+ public void setLineHeadWidth(LineEndWidth style) {\r
CTLineProperties ln = getSpPr().getLn();\r
CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd();\r
- if(style == null){\r
- if(lnEnd.isSetW()) lnEnd.unsetW();\r
+ if (style == null) {\r
+ if (lnEnd.isSetW()) lnEnd.unsetW();\r
} else {\r
lnEnd.setW(STLineEndWidth.Enum.forInt(style.ordinal() + 1));\r
}\r
}\r
\r
- public LineEndWidth getLineHeadWidth(){\r
+ public LineEndWidth getLineHeadWidth() {\r
CTLineProperties ln = getSpPr().getLn();\r
- if(!ln.isSetHeadEnd()) return null;\r
+ if (ln == null || !ln.isSetHeadEnd()) return LineEndWidth.MEDIUM;\r
\r
STLineEndWidth.Enum w = ln.getHeadEnd().getW();\r
- return w == null ? null : LineEndWidth.values()[w.intValue() - 1];\r
+ return w == null ? LineEndWidth.MEDIUM : LineEndWidth.values()[w.intValue() - 1];\r
}\r
\r
/**\r
* Specifies the line end width in relation to the line width.\r
*/\r
- public void setLineHeadLength(LineEndLength style){\r
+ public void setLineHeadLength(LineEndLength style) {\r
CTLineProperties ln = getSpPr().getLn();\r
CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd();\r
\r
- if(style == null){\r
- if(lnEnd.isSetLen()) lnEnd.unsetLen();\r
+ if (style == null) {\r
+ if (lnEnd.isSetLen()) lnEnd.unsetLen();\r
} else {\r
lnEnd.setLen(STLineEndLength.Enum.forInt(style.ordinal() + 1));\r
}\r
}\r
\r
- public LineEndLength getLineHeadLength(){\r
+ public LineEndLength getLineHeadLength() {\r
CTLineProperties ln = getSpPr().getLn();\r
- if(!ln.isSetHeadEnd()) return null;\r
+ if (ln == null || !ln.isSetHeadEnd()) return LineEndLength.MEDIUM;\r
\r
STLineEndLength.Enum len = ln.getHeadEnd().getLen();\r
- return len == null ? null : LineEndLength.values()[len.intValue() - 1];\r
+ return len == null ? LineEndLength.MEDIUM : LineEndLength.values()[len.intValue() - 1];\r
}\r
\r
/**\r
* Specifies the line end decoration, such as a triangle or arrowhead.\r
*/\r
- public void setLineTailDecoration(LineDecoration style){\r
+ public void setLineTailDecoration(LineDecoration style) {\r
CTLineProperties ln = getSpPr().getLn();\r
CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd();\r
- if(style == null){\r
- if(lnEnd.isSetType()) lnEnd.unsetType();\r
+ if (style == null) {\r
+ if (lnEnd.isSetType()) lnEnd.unsetType();\r
} else {\r
lnEnd.setType(STLineEndType.Enum.forInt(style.ordinal() + 1));\r
}\r
}\r
\r
- public LineDecoration getLineTailDecoration(){\r
+ public LineDecoration getLineTailDecoration() {\r
CTLineProperties ln = getSpPr().getLn();\r
- if(!ln.isSetTailEnd()) return LineDecoration.NONE;\r
+ if (ln == null || !ln.isSetTailEnd()) return LineDecoration.NONE;\r
\r
STLineEndType.Enum end = ln.getTailEnd().getType();\r
return end == null ? LineDecoration.NONE : LineDecoration.values()[end.intValue() - 1];\r
/**\r
* specifies decorations which can be added to the tail of a line.\r
*/\r
- public void setLineTailWidth(LineEndWidth style){\r
+ public void setLineTailWidth(LineEndWidth style) {\r
CTLineProperties ln = getSpPr().getLn();\r
CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd();\r
- if(style == null){\r
- if(lnEnd.isSetW()) lnEnd.unsetW();\r
+ if (style == null) {\r
+ if (lnEnd.isSetW()) lnEnd.unsetW();\r
} else {\r
lnEnd.setW(STLineEndWidth.Enum.forInt(style.ordinal() + 1));\r
}\r
}\r
\r
- public LineEndWidth getLineTailWidth(){\r
+ public LineEndWidth getLineTailWidth() {\r
CTLineProperties ln = getSpPr().getLn();\r
- if(!ln.isSetTailEnd()) return null;\r
+ if (ln == null || !ln.isSetTailEnd()) return LineEndWidth.MEDIUM;\r
\r
STLineEndWidth.Enum w = ln.getTailEnd().getW();\r
- return w == null ? null : LineEndWidth.values()[w.intValue() - 1];\r
+ return w == null ? LineEndWidth.MEDIUM : LineEndWidth.values()[w.intValue() - 1];\r
}\r
\r
/**\r
* Specifies the line end width in relation to the line width.\r
*/\r
- public void setLineTailLength(LineEndLength style){\r
+ public void setLineTailLength(LineEndLength style) {\r
CTLineProperties ln = getSpPr().getLn();\r
CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd();\r
\r
- if(style == null){\r
- if(lnEnd.isSetLen()) lnEnd.unsetLen();\r
+ if (style == null) {\r
+ if (lnEnd.isSetLen()) lnEnd.unsetLen();\r
} else {\r
lnEnd.setLen(STLineEndLength.Enum.forInt(style.ordinal() + 1));\r
}\r
}\r
\r
- public LineEndLength getLineTailLength(){\r
+ public LineEndLength getLineTailLength() {\r
CTLineProperties ln = getSpPr().getLn();\r
- if(!ln.isSetTailEnd()) return null;\r
+ if (ln == null || !ln.isSetTailEnd()) return LineEndLength.MEDIUM;\r
\r
STLineEndLength.Enum len = ln.getTailEnd().getLen();\r
- return len == null ? null : LineEndLength.values()[len.intValue() - 1];\r
+ return len == null ? LineEndLength.MEDIUM : LineEndLength.values()[len.intValue() - 1];\r
}\r
\r
@Override\r
- public void draw(Graphics2D graphics){\r
+ public void draw(Graphics2D graphics) {\r
java.awt.Shape outline = getOutline();\r
\r
// shadow\r
XSLFShadow shadow = getShadow();\r
- if(shadow != null) shadow.draw(graphics);\r
\r
//border\r
Color lineColor = getLineColor();\r
- if (lineColor != null){\r
+ if (lineColor != null) {\r
+ if (shadow != null) shadow.draw(graphics);\r
+\r
graphics.setColor(lineColor);\r
applyStroke(graphics);\r
graphics.draw(outline);\r
+\r
+ Shape tailDecoration = getTailDecoration();\r
+ if (tailDecoration != null) {\r
+ graphics.draw(tailDecoration);\r
+ }\r
+\r
+ Shape headDecoration = getHeadDecoration();\r
+ if (headDecoration != null) {\r
+ graphics.draw(headDecoration);\r
+\r
+ }\r
}\r
}\r
\r
@Override\r
- protected java.awt.Shape getOutline(){\r
+ protected java.awt.Shape getOutline() {\r
Rectangle2D anchor = getAnchor();\r
- double x1 = anchor.getX(),\r
+ double x1 = anchor.getX(),\r
y1 = anchor.getY(),\r
x2 = anchor.getX() + anchor.getWidth(),\r
y2 = anchor.getY() + anchor.getHeight();\r
\r
- GeneralPath line = new GeneralPath();\r
- line.moveTo((float)x1, (float)y1);\r
- line.lineTo((float)x2, (float)y2);\r
\r
- return line;\r
+ return new Line2D.Double(x1, y1, x2, y2);\r
+ }\r
+\r
+ Shape getTailDecoration() {\r
+ LineEndLength tailLength = getLineTailLength();\r
+ LineEndWidth tailWidth = getLineTailWidth();\r
+\r
+ double lineWidth = getLineWidth();\r
+ Rectangle2D anchor = getAnchor();\r
+ double x2 = anchor.getX() + anchor.getWidth(),\r
+ y2 = anchor.getY() + anchor.getHeight();\r
+\r
+ double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());\r
+\r
+ AffineTransform at = new AffineTransform();\r
+ Shape shape = null;\r
+ Rectangle2D bounds;\r
+ double scaleY = Math.pow(2, tailWidth.ordinal());\r
+ double scaleX = Math.pow(2, tailLength.ordinal());\r
+ switch (getLineHeadDecoration()) {\r
+ case OVAL:\r
+ shape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);\r
+ bounds = shape.getBounds2D();\r
+ at.translate(x2 - bounds.getWidth() / 2, y2 - bounds.getHeight() / 2);\r
+ at.rotate(alpha, bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);\r
+ break;\r
+ case ARROW:\r
+ GeneralPath arrow = new GeneralPath();\r
+ arrow.moveTo(-lineWidth * 3, -lineWidth * 2);\r
+ arrow.lineTo(0, 0);\r
+ arrow.lineTo(-lineWidth * 3, lineWidth * 2);\r
+ shape = arrow;\r
+ at.translate(x2, y2);\r
+ at.rotate(alpha);\r
+ break;\r
+ case TRIANGLE:\r
+ scaleY = tailWidth.ordinal() + 1;\r
+ scaleX = tailLength.ordinal() + 1;\r
+ GeneralPath triangle = new GeneralPath();\r
+ triangle.moveTo(-lineWidth * scaleX, -lineWidth * scaleY/2);\r
+ triangle.lineTo(0, 0);\r
+ triangle.lineTo(-lineWidth * scaleX, lineWidth * scaleY/2);\r
+ triangle.closePath();\r
+ shape = triangle;\r
+ at.translate(x2, y2);\r
+ at.rotate(alpha);\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ if (shape != null) {\r
+ shape = at.createTransformedShape(shape);\r
+ }\r
+ return shape;\r
+ }\r
+\r
+ Shape getHeadDecoration() {\r
+ LineEndLength headLength = getLineHeadLength();\r
+ LineEndWidth headWidth = getLineHeadWidth();\r
+\r
+ double lineWidth = getLineWidth();\r
+ Rectangle2D anchor = getAnchor();\r
+ double x1 = anchor.getX(),\r
+ y1 = anchor.getY();\r
+\r
+ double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());\r
+\r
+ AffineTransform at = new AffineTransform();\r
+ Shape shape = null;\r
+ Rectangle2D bounds;\r
+ double scaleY = 1;\r
+ double scaleX = 1;\r
+ switch (getLineHeadDecoration()) {\r
+ case OVAL:\r
+ scaleY = Math.pow(2, headWidth.ordinal());\r
+ scaleX = Math.pow(2, headLength.ordinal());\r
+ shape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);\r
+ break;\r
+ case STEALTH:\r
+ case ARROW:\r
+ GeneralPath arrow = new GeneralPath();\r
+ arrow.moveTo(lineWidth * 3 * scaleX, -lineWidth * scaleY * 2);\r
+ arrow.lineTo(0, 0);\r
+ arrow.lineTo(lineWidth * 3 * scaleX, lineWidth * scaleY * 2);\r
+ shape = arrow;\r
+ at.translate(x1, y1);\r
+ at.rotate(alpha);\r
+ break;\r
+ case TRIANGLE:\r
+ scaleY = headWidth.ordinal() + 1;\r
+ scaleX = headLength.ordinal() + 1;\r
+ GeneralPath triangle = new GeneralPath();\r
+ triangle.moveTo(lineWidth * scaleX, -lineWidth * scaleY/2);\r
+ triangle.lineTo(0, 0);\r
+ triangle.lineTo(lineWidth * scaleX, lineWidth * scaleY/2);\r
+ triangle.closePath();\r
+ shape = triangle;\r
+ at.translate(x1, y1);\r
+ at.rotate(alpha);\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+\r
+ if (shape != null) {\r
+ shape = at.createTransformedShape(shape);\r
+ }\r
+ return shape;\r
}\r
\r
}
\ No newline at end of file
--- /dev/null
+/*\r
+ * ====================================================================\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ====================================================================\r
+ */\r
+\r
+package org.apache.poi.xslf.usermodel;\r
+\r
+import org.apache.poi.util.Beta;\r
+\r
+import javax.imageio.ImageIO;\r
+import java.awt.Graphics2D;\r
+import java.awt.geom.Rectangle2D;\r
+import java.awt.image.BufferedImage;\r
+import java.io.ByteArrayInputStream;\r
+\r
+/**\r
+ * For now this class renders only images supported by the javax.imageio.ImageIO\r
+ * framework. Subclasses can override this class to support other formats, for\r
+ * example, Use Apache batik to render WMF:\r
+ * \r
+ * <pre>\r
+ * <code>\r
+ * @Override\r
+ * public class MyImageRendener extends XSLFImageRendener{\r
+ * public boolean drawImage(Graphics2D graphics, XSLFPictureData data, Rectangle2D anchor){\r
+ * boolean ok = super.drawImage(graphics, data, anchor);\r
+ * if(!ok){\r
+ * // see what type of image we are\r
+ * String contentType = data.getPackagePart().getContentType();\r
+ * if(contentType.equals("image/wmf")){\r
+ * // use Apache Batik to handle WMF\r
+ * // see http://xmlgraphics.apache.org/batik/\r
+ * }\r
+ * \r
+ * }\r
+ * return ok;\r
+ * }\r
+ * }\r
+ * </code>\r
+ * </pre>\r
+ * \r
+ * and then pass this class to your instance of java.awt.Graphics2D:\r
+ * \r
+ * <pre>\r
+ * <code>\r
+ * graphics.setRenderingHint(XSLFRenderingHint.IMAGE_RENDERER, new MyImageRendener());\r
+ * </code>\r
+ * </pre>\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+@Beta\r
+public class XSLFImageRendener {\r
+\r
+ /**\r
+ * Render picture data into the supplied graphics\r
+ * \r
+ * @return true if the picture data was succesfully renderered\r
+ */\r
+ public boolean drawImage(Graphics2D graphics, XSLFPictureData data,\r
+ Rectangle2D anchor) {\r
+ try {\r
+ BufferedImage img = ImageIO.read(new ByteArrayInputStream(data\r
+ .getData()));\r
+ graphics.drawImage(img, (int) anchor.getX(), (int) anchor.getY(),\r
+ (int) anchor.getWidth(), (int) anchor.getHeight(), null);\r
+ return true;\r
+ } catch (Exception e) {\r
+ return false;\r
+ }\r
+\r
+ }\r
+}
\ No newline at end of file
_parent = parentShape;\r
}\r
\r
+ @Override\r
public void draw(Graphics2D graphics) {\r
Shape outline = _parent.getOutline();\r
\r
+ Color parentFillColor = _parent.getFillColor();\r
+ Color parentLineColor = _parent.getLineColor();\r
+\r
double angle = getAngle();\r
double dist = getDistance();\r
double dx = dist * Math.cos( Math.toRadians(angle));\r
\r
graphics.translate(dx, dy);\r
\r
- //fill\r
Color fillColor = getFillColor();\r
if (fillColor != null) {\r
graphics.setColor(fillColor);\r
- graphics.fill(outline);\r
}\r
\r
+ if(parentFillColor != null) {\r
+ graphics.fill(outline);\r
+ }\r
+ if(parentLineColor != null) {\r
+ _parent.applyStroke(graphics);\r
+ graphics.draw(outline);\r
+ }\r
+\r
graphics.translate(-dx, -dy);\r
}\r
\r
throw new IllegalStateException("You can't set anchor of a shadow");\r
}\r
\r
+ /**\r
+ * @return the offset of this shadow in points\r
+ */\r
public double getDistance(){\r
CTOuterShadowEffect ct = (CTOuterShadowEffect)getXmlObject();\r
return ct.isSetDist() ? Units.toPoints(ct.getDist()) : 0; \r
}\r
\r
+ /**\r
+ * \r
+ * @return the direction to offset the shadow in angles\r
+ */\r
public double getAngle(){\r
CTOuterShadowEffect ct = (CTOuterShadowEffect)getXmlObject();\r
return ct.isSetDir() ? (double)ct.getDir() / 60000 : 0;\r
}\r
\r
+ /**\r
+ * \r
+ * @return the blur radius of the shadow\r
+ * TODO: figure out how to make sense of this property when rendering shadows \r
+ */\r
public double getBlur(){\r
CTOuterShadowEffect ct = (CTOuterShadowEffect)getXmlObject();\r
return ct.isSetBlurRad() ? Units.toPoints(ct.getBlurRad()) : 0;\r
}\r
\r
+ /**\r
+ * @return the color of this shadow. \r
+ * Depending whether the parent shape is filled or stroked, this color is used to fill or stroke this shadow\r
+ */\r
@Override\r
public Color getFillColor() {\r
XSLFTheme theme = getSheet().getTheme();\r
else if (ct.isSetPrstClr()) {\r
return theme.getPresetColor(ct.getPrstClr());\r
}\r
+ else if (ct.isSetSrgbClr()) {\r
+ return theme.getSrgbColor(ct.getSrgbClr());\r
+ }\r
\r
- return Color.black;\r
+ return null;\r
}\r
}
\ No newline at end of file