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 6eb49b4b9..bdaa58620 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java @@ -33,19 +33,17 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector; import org.openxmlformats.schemas.presentationml.x2006.main.CTConnectorNonVisual; import java.awt.*; -import java.awt.geom.GeneralPath; -import java.awt.geom.Rectangle2D; +import java.awt.geom.*; /** - * - * Specifies a connection shape. + * Specifies a connection shape. * * @author Yegor Kozlov */ @Beta public class XSLFConnectorShape extends XSLFSimpleShape { - /*package*/ XSLFConnectorShape(CTConnector shape, XSLFSheet sheet){ + /*package*/ XSLFConnectorShape(CTConnector shape, XSLFSheet sheet) { super(shape, sheet); } @@ -71,19 +69,19 @@ public class XSLFConnectorShape extends XSLFSimpleShape { /** * Specifies the line end decoration, such as a triangle or arrowhead. */ - public void setLineHeadDecoration(LineDecoration style){ + 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(); + if (style == null) { + if (lnEnd.isSetType()) lnEnd.unsetType(); } else { lnEnd.setType(STLineEndType.Enum.forInt(style.ordinal() + 1)); } } - public LineDecoration getLineHeadDecoration(){ + public LineDecoration getLineHeadDecoration() { CTLineProperties ln = getSpPr().getLn(); - if(!ln.isSetHeadEnd()) return LineDecoration.NONE; + if (ln == null || !ln.isSetHeadEnd()) return LineDecoration.NONE; STLineEndType.Enum end = ln.getHeadEnd().getType(); return end == null ? LineDecoration.NONE : LineDecoration.values()[end.intValue() - 1]; @@ -92,62 +90,62 @@ public class XSLFConnectorShape extends XSLFSimpleShape { /** * specifies decorations which can be added to the head of a line. */ - public void setLineHeadWidth(LineEndWidth style){ + 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(); + if (style == null) { + if (lnEnd.isSetW()) lnEnd.unsetW(); } else { lnEnd.setW(STLineEndWidth.Enum.forInt(style.ordinal() + 1)); } } - public LineEndWidth getLineHeadWidth(){ + public LineEndWidth getLineHeadWidth() { CTLineProperties ln = getSpPr().getLn(); - if(!ln.isSetHeadEnd()) return null; + if (ln == null || !ln.isSetHeadEnd()) return LineEndWidth.MEDIUM; STLineEndWidth.Enum w = ln.getHeadEnd().getW(); - return w == null ? null : LineEndWidth.values()[w.intValue() - 1]; + 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){ + 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(); + if (style == null) { + if (lnEnd.isSetLen()) lnEnd.unsetLen(); } else { lnEnd.setLen(STLineEndLength.Enum.forInt(style.ordinal() + 1)); } } - public LineEndLength getLineHeadLength(){ + public LineEndLength getLineHeadLength() { CTLineProperties ln = getSpPr().getLn(); - if(!ln.isSetHeadEnd()) return null; + if (ln == null || !ln.isSetHeadEnd()) return LineEndLength.MEDIUM; STLineEndLength.Enum len = ln.getHeadEnd().getLen(); - return len == null ? null : LineEndLength.values()[len.intValue() - 1]; + 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){ + 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(); + if (style == null) { + if (lnEnd.isSetType()) lnEnd.unsetType(); } else { lnEnd.setType(STLineEndType.Enum.forInt(style.ordinal() + 1)); } } - public LineDecoration getLineTailDecoration(){ + public LineDecoration getLineTailDecoration() { CTLineProperties ln = getSpPr().getLn(); - if(!ln.isSetTailEnd()) return LineDecoration.NONE; + if (ln == null || !ln.isSetTailEnd()) return LineDecoration.NONE; STLineEndType.Enum end = ln.getTailEnd().getType(); return end == null ? LineDecoration.NONE : LineDecoration.values()[end.intValue() - 1]; @@ -156,76 +154,193 @@ public class XSLFConnectorShape extends XSLFSimpleShape { /** * specifies decorations which can be added to the tail of a line. */ - public void setLineTailWidth(LineEndWidth style){ + 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(); + if (style == null) { + if (lnEnd.isSetW()) lnEnd.unsetW(); } else { lnEnd.setW(STLineEndWidth.Enum.forInt(style.ordinal() + 1)); } } - public LineEndWidth getLineTailWidth(){ + public LineEndWidth getLineTailWidth() { CTLineProperties ln = getSpPr().getLn(); - if(!ln.isSetTailEnd()) return null; + if (ln == null || !ln.isSetTailEnd()) return LineEndWidth.MEDIUM; STLineEndWidth.Enum w = ln.getTailEnd().getW(); - return w == null ? null : LineEndWidth.values()[w.intValue() - 1]; + 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){ + 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(); + if (style == null) { + if (lnEnd.isSetLen()) lnEnd.unsetLen(); } else { lnEnd.setLen(STLineEndLength.Enum.forInt(style.ordinal() + 1)); } } - public LineEndLength getLineTailLength(){ + public LineEndLength getLineTailLength() { CTLineProperties ln = getSpPr().getLn(); - if(!ln.isSetTailEnd()) return null; + if (ln == null || !ln.isSetTailEnd()) return LineEndLength.MEDIUM; STLineEndLength.Enum len = ln.getTailEnd().getLen(); - return len == null ? null : LineEndLength.values()[len.intValue() - 1]; + return len == null ? LineEndLength.MEDIUM : LineEndLength.values()[len.intValue() - 1]; } @Override - public void draw(Graphics2D graphics){ + public void draw(Graphics2D graphics) { java.awt.Shape outline = getOutline(); // shadow XSLFShadow shadow = getShadow(); - if(shadow != null) shadow.draw(graphics); //border Color lineColor = getLineColor(); - if (lineColor != null){ + if (lineColor != null) { + if (shadow != null) shadow.draw(graphics); + graphics.setColor(lineColor); applyStroke(graphics); graphics.draw(outline); + + Shape tailDecoration = getTailDecoration(); + if (tailDecoration != null) { + graphics.draw(tailDecoration); + } + + Shape headDecoration = getHeadDecoration(); + if (headDecoration != null) { + graphics.draw(headDecoration); + + } } } @Override - protected java.awt.Shape getOutline(){ + protected java.awt.Shape getOutline() { Rectangle2D anchor = getAnchor(); - double x1 = anchor.getX(), + double x1 = anchor.getX(), y1 = anchor.getY(), x2 = anchor.getX() + anchor.getWidth(), y2 = anchor.getY() + anchor.getHeight(); - GeneralPath line = new GeneralPath(); - line.moveTo((float)x1, (float)y1); - line.lineTo((float)x2, (float)y2); - return line; + return new Line2D.Double(x1, y1, x2, y2); + } + + Shape getTailDecoration() { + LineEndLength tailLength = getLineTailLength(); + LineEndWidth tailWidth = getLineTailWidth(); + + double lineWidth = 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; + Rectangle2D bounds; + double scaleY = Math.pow(2, tailWidth.ordinal()); + double scaleX = Math.pow(2, tailLength.ordinal()); + switch (getLineHeadDecoration()) { + case OVAL: + 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: + GeneralPath arrow = new GeneralPath(); + arrow.moveTo(-lineWidth * 3, -lineWidth * 2); + arrow.lineTo(0, 0); + arrow.lineTo(-lineWidth * 3, lineWidth * 2); + shape = arrow; + at.translate(x2, y2); + at.rotate(alpha); + break; + case TRIANGLE: + scaleY = tailWidth.ordinal() + 1; + scaleX = tailLength.ordinal() + 1; + GeneralPath triangle = new GeneralPath(); + triangle.moveTo(-lineWidth * scaleX, -lineWidth * scaleY/2); + triangle.lineTo(0, 0); + triangle.lineTo(-lineWidth * scaleX, 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; + } + + Shape getHeadDecoration() { + LineEndLength headLength = getLineHeadLength(); + LineEndWidth headWidth = getLineHeadWidth(); + + double lineWidth = 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; + Rectangle2D bounds; + double scaleY = 1; + double scaleX = 1; + switch (getLineHeadDecoration()) { + case OVAL: + scaleY = Math.pow(2, headWidth.ordinal()); + scaleX = Math.pow(2, headLength.ordinal()); + shape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY); + break; + case STEALTH: + case ARROW: + GeneralPath arrow = new GeneralPath(); + arrow.moveTo(lineWidth * 3 * scaleX, -lineWidth * scaleY * 2); + arrow.lineTo(0, 0); + arrow.lineTo(lineWidth * 3 * scaleX, lineWidth * scaleY * 2); + shape = arrow; + at.translate(x1, y1); + at.rotate(alpha); + break; + case TRIANGLE: + scaleY = headWidth.ordinal() + 1; + scaleX = headLength.ordinal() + 1; + GeneralPath triangle = new GeneralPath(); + triangle.moveTo(lineWidth * scaleX, -lineWidth * scaleY/2); + triangle.lineTo(0, 0); + triangle.lineTo(lineWidth * scaleX, 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; } } \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java new file mode 100644 index 000000000..46fa1bf17 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java @@ -0,0 +1,87 @@ +/* + * ==================================================================== + * 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.util.Beta; + +import javax.imageio.ImageIO; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; + +/** + * For now this class renders only images supported by the javax.imageio.ImageIO + * framework. Subclasses can override this class to support other formats, for + * example, Use Apache batik to render WMF: + * + *
+ *
+ * @Override
+ * public class MyImageRendener extends XSLFImageRendener{
+ * public boolean drawImage(Graphics2D graphics, XSLFPictureData data, Rectangle2D anchor){
+ * boolean ok = super.drawImage(graphics, data, anchor);
+ * if(!ok){
+ * // see what type of image we are
+ * String contentType = data.getPackagePart().getContentType();
+ * if(contentType.equals("image/wmf")){
+ * // use Apache Batik to handle WMF
+ * // see http://xmlgraphics.apache.org/batik/
+ * }
+ *
+ * }
+ * return ok;
+ * }
+ * }
+ *
+ *
+ *
+ * and then pass this class to your instance of java.awt.Graphics2D:
+ *
+ *
+ *
+ * graphics.setRenderingHint(XSLFRenderingHint.IMAGE_RENDERER, new MyImageRendener());
+ *
+ *
+ *
+ * @author Yegor Kozlov
+ */
+@Beta
+public class XSLFImageRendener {
+
+ /**
+ * Render picture data into the supplied graphics
+ *
+ * @return true if the picture data was succesfully renderered
+ */
+ public boolean drawImage(Graphics2D graphics, XSLFPictureData data,
+ Rectangle2D anchor) {
+ try {
+ BufferedImage img = ImageIO.read(new ByteArrayInputStream(data
+ .getData()));
+ graphics.drawImage(img, (int) anchor.getX(), (int) anchor.getY(),
+ (int) anchor.getWidth(), (int) anchor.getHeight(), null);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+
+ }
+}
\ 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 4e17d62c0..c5c92b5dc 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java
@@ -117,30 +117,26 @@ public class XSLFPictureShape extends XSLFSimpleShape {
public void draw(Graphics2D graphics){
java.awt.Shape outline = getOutline();
+ // shadow
+ XSLFShadow shadow = getShadow();
+
//fill
Color fillColor = getFillColor();
if (fillColor != null) {
- graphics.setColor(fillColor);
+ if(shadow != null) shadow.draw(graphics);
+
+ graphics.setColor(fillColor);
applyFill(graphics);
graphics.fill(outline);
}
-
- // text
-
- XSLFPictureData data = getPictureData();
+
+ XSLFPictureData data = getPictureData();
if(data == null) return;
- BufferedImage img;
- try {
- img = ImageIO.read(new ByteArrayInputStream(data.getData()));
- }
- catch (Exception e){
- return;
- }
- Rectangle2D anchor = getAnchor();
- graphics.drawImage(img, (int)anchor.getX(), (int)anchor.getY(),
- (int)anchor.getWidth(), (int)anchor.getHeight(), null);
-
+ XSLFImageRendener renderer = (XSLFImageRendener)graphics.getRenderingHint(XSLFRenderingHint.IMAGE_RENDERER);
+ if(renderer == null) renderer = new XSLFImageRendener();
+
+ renderer.drawImage(graphics, data, getAnchor());
//border overlays the image
Color lineColor = getLineColor();
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 aff8c90c4..cc0df0fbf 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRenderingHint.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRenderingHint.java
@@ -38,5 +38,5 @@ public class XSLFRenderingHint extends RenderingHints.Key {
public static final XSLFRenderingHint GSAVE = new XSLFRenderingHint(1);
public static final XSLFRenderingHint GRESTORE = new XSLFRenderingHint(2);
- public static final XSLFRenderingHint SKIP_PLACEHOLDERS = new XSLFRenderingHint(3);
+ public static final XSLFRenderingHint IMAGE_RENDERER = new XSLFRenderingHint(3);
}
\ 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 e897e29fb..837529a90 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java
@@ -45,9 +45,13 @@ public class XSLFShadow extends XSLFSimpleShape {
_parent = parentShape;
}
+ @Override
public void draw(Graphics2D graphics) {
Shape outline = _parent.getOutline();
+ Color parentFillColor = _parent.getFillColor();
+ Color parentLineColor = _parent.getLineColor();
+
double angle = getAngle();
double dist = getDistance();
double dx = dist * Math.cos( Math.toRadians(angle));
@@ -55,13 +59,19 @@ public class XSLFShadow extends XSLFSimpleShape {
graphics.translate(dx, dy);
- //fill
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);
}
@@ -75,21 +85,37 @@ public class XSLFShadow extends XSLFSimpleShape {
throw new IllegalStateException("You can't set anchor of a shadow");
}
+ /**
+ * @return the offset of this shadow in points
+ */
public double getDistance(){
CTOuterShadowEffect ct = (CTOuterShadowEffect)getXmlObject();
return ct.isSetDist() ? Units.toPoints(ct.getDist()) : 0;
}
+ /**
+ *
+ * @return the direction to offset the shadow in angles
+ */
public double getAngle(){
CTOuterShadowEffect ct = (CTOuterShadowEffect)getXmlObject();
return ct.isSetDir() ? (double)ct.getDir() / 60000 : 0;
}
+ /**
+ *
+ * @return the blur radius of the shadow
+ * TODO: figure out how to make sense of this property when rendering shadows
+ */
public double getBlur(){
CTOuterShadowEffect ct = (CTOuterShadowEffect)getXmlObject();
return ct.isSetBlurRad() ? Units.toPoints(ct.getBlurRad()) : 0;
}
+ /**
+ * @return the color of this shadow.
+ * Depending whether the parent shape is filled or stroked, this color is used to fill or stroke this shadow
+ */
@Override
public Color getFillColor() {
XSLFTheme theme = getSheet().getTheme();
@@ -100,7 +126,10 @@ public class XSLFShadow extends XSLFSimpleShape {
else if (ct.isSetPrstClr()) {
return theme.getPresetColor(ct.getPrstClr());
}
+ else if (ct.isSetSrgbClr()) {
+ return theme.getSrgbColor(ct.getSrgbClr());
+ }
- return Color.black;
+ return null;
}
}
\ 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 8e3b38a22..c7efb5ff5 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java
@@ -279,14 +279,14 @@ public abstract class XSLFSimpleShape extends XSLFShape {
public Color getLineColor() {
final XSLFTheme theme = _sheet.getTheme();
-
+ final Color noline = new Color(0,0,0,0);
PropertyFetcher