From: Andreas Beeker Date: Sun, 9 Dec 2018 01:06:15 +0000 (+0000) Subject: #62953 - Rendering of FreeformShapes with formula fails X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=79addd3048a120b360c6a33bea5aafd42de7c17a;p=poi.git #62953 - Rendering of FreeformShapes with formula fails git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1848492 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/ddf/EscherProperties.java b/src/java/org/apache/poi/ddf/EscherProperties.java index acff6241c2..482bce95c5 100644 --- a/src/java/org/apache/poi/ddf/EscherProperties.java +++ b/src/java/org/apache/poi/ddf/EscherProperties.java @@ -26,6 +26,7 @@ import java.util.Map; * * @author Glen Stampoultzis (glens at apache.org) */ +@SuppressWarnings("WeakerAccess") public final class EscherProperties { // Property constants @@ -117,6 +118,15 @@ public final class EscherProperties { public static final short GEOMETRY__ADJUST8VALUE = 334; public static final short GEOMETRY__ADJUST9VALUE = 335; public static final short GEOMETRY__ADJUST10VALUE = 336; + public static final short GEOMETRY__PCONNECTIONSITES = 337; + public static final short GEOMETRY__PCONNECTIONSITESDIR = 338; + public static final short GEOMETRY__XLIMO = 339; + public static final short GEOMETRY__YLIMO = 340; + public static final short GEOMETRY__PADJUSTHANDLES = 341; + public static final short GEOMETRY__PGUIDES = 342; + public static final short GEOMETRY__PINSCRIBE = 343; + public static final short GEOMETRY__CXK = 344; + public static final short GEOMETRY__PFRAGMENTS = 345; public static final short GEOMETRY__SHADOWok = 378; public static final short GEOMETRY__3DOK = 379; public static final short GEOMETRY__LINEOK = 380; @@ -333,6 +343,9 @@ public final class EscherProperties { private static final Map properties = initProps(); + private EscherProperties() { + } + private static Map initProps() { Map m = new HashMap<>(); addProp(m, TRANSFORM__ROTATION, "transform.rotation"); @@ -423,6 +436,15 @@ public final class EscherProperties { addProp(m, GEOMETRY__ADJUST8VALUE, "geometry.adjust8value"); addProp(m, GEOMETRY__ADJUST9VALUE, "geometry.adjust9value"); addProp(m, GEOMETRY__ADJUST10VALUE, "geometry.adjust10value"); + addProp(m, GEOMETRY__PCONNECTIONSITES, "geometry.pConnectionSites"); + addProp(m, GEOMETRY__PCONNECTIONSITESDIR, "geometry.pConnectionSitesDir"); + addProp(m, GEOMETRY__XLIMO, "geometry.xLimo"); + addProp(m, GEOMETRY__YLIMO, "geometry.yLimo"); + addProp(m, GEOMETRY__PADJUSTHANDLES, "geometry.pAdjustHandles"); + addProp(m, GEOMETRY__PGUIDES, "geometry.pGuides"); + addProp(m, GEOMETRY__PINSCRIBE, "geometry.pInscribe"); + addProp(m, GEOMETRY__CXK, "geometry.cxk"); + addProp(m, GEOMETRY__PFRAGMENTS, "geometry.pFragments"); addProp(m, GEOMETRY__SHADOWok, "geometry.shadowOK"); addProp(m, GEOMETRY__3DOK, "geometry.3dok"); addProp(m, GEOMETRY__LINEOK, "geometry.lineok"); @@ -641,20 +663,20 @@ public final class EscherProperties { } private static void addProp(Map m, int s, String propName) { - m.put(Short.valueOf((short) s), new EscherPropertyMetaData(propName)); + m.put((short) s, new EscherPropertyMetaData(propName)); } private static void addProp(Map m, int s, String propName, byte type) { - m.put(Short.valueOf((short) s), new EscherPropertyMetaData(propName, type)); + m.put((short) s, new EscherPropertyMetaData(propName, type)); } public static String getPropertyName(short propertyId) { - EscherPropertyMetaData o = properties.get(Short.valueOf(propertyId)); + EscherPropertyMetaData o = properties.get(propertyId); return o == null ? "unknown" : o.getDescription(); } public static byte getPropertyType(short propertyId) { - EscherPropertyMetaData escherPropertyMetaData = properties.get(Short.valueOf(propertyId)); + EscherPropertyMetaData escherPropertyMetaData = properties.get(propertyId); return escherPropertyMetaData == null ? 0 : escherPropertyMetaData.getType(); } } diff --git a/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java b/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java index 6eb60dfb52..be18f037ab 100644 --- a/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java @@ -17,43 +17,11 @@ package org.apache.poi.sl.draw; -import java.awt.Graphics2D; -import java.awt.geom.AffineTransform; -import java.awt.geom.Path2D; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.apache.poi.sl.draw.geom.Outline; -import org.apache.poi.sl.draw.geom.Path; -import org.apache.poi.sl.usermodel.*; +import org.apache.poi.sl.usermodel.FreeformShape; +@SuppressWarnings("WeakerAccess") public class DrawFreeformShape extends DrawAutoShape { public DrawFreeformShape(FreeformShape shape) { super(shape); } - - protected Collection computeOutlines(Graphics2D graphics) { - List lst = new ArrayList<>(); - FreeformShape fsh = (FreeformShape) getShape(); - Path2D sh = fsh.getPath(); - - AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM); - if (tx == null) { - tx = new AffineTransform(); - } - - java.awt.Shape canvasShape = tx.createTransformedShape(sh); - - FillStyle fs = fsh.getFillStyle(); - StrokeStyle ss = fsh.getStrokeStyle(); - Path path = new Path(fs != null, ss != null); - lst.add(new Outline(canvasShape, path)); - return lst; - } - - @Override - protected TextShape> getShape() { - return (TextShape>)shape; - } } diff --git a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java index 7ed1d35da2..35c3aa1207 100644 --- a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java @@ -410,14 +410,20 @@ public class DrawSimpleShape extends DrawShape { } for (Path p : geom) { - double w = p.getW(), h = p.getH(), scaleX = Units.toPoints(1), scaleY = scaleX; + double w = p.getW(), h = p.getH(), scaleX, scaleY; if (w == -1) { w = Units.toEMU(anchor.getWidth()); + scaleX = Units.toPoints(1); + } else if (anchor.getWidth() == 0) { + scaleX = 1; } else { scaleX = anchor.getWidth() / w; } if (h == -1) { h = Units.toEMU(anchor.getHeight()); + scaleY = Units.toPoints(1); + } else if (anchor.getHeight() == 0) { + scaleY = 1; } else { scaleY = anchor.getHeight() / h; } diff --git a/src/java/org/apache/poi/sl/draw/geom/Context.java b/src/java/org/apache/poi/sl/draw/geom/Context.java index 31847ceeff..3ed84b3d18 100644 --- a/src/java/org/apache/poi/sl/draw/geom/Context.java +++ b/src/java/org/apache/poi/sl/draw/geom/Context.java @@ -22,11 +22,18 @@ package org.apache.poi.sl.draw.geom; import java.awt.geom.Rectangle2D; import java.util.HashMap; import java.util.Map; +import java.util.regex.Pattern; public class Context { - final Map _ctx = new HashMap<>(); - final IAdjustableShape _props; - final Rectangle2D _anchor; + private static final Pattern DOUBLE_PATTERN = Pattern.compile( + "[\\x00-\\x20]*[+-]?(NaN|Infinity|((((\\p{Digit}+)(\\.)?((\\p{Digit}+)?)" + + "([eE][+-]?(\\p{Digit}+))?)|(\\.(\\p{Digit}+)([eE][+-]?(\\p{Digit}+))?)|" + + "(((0[xX](\\p{XDigit}+)(\\.)?)|(0[xX](\\p{XDigit}+)?(\\.)(\\p{XDigit}+)))" + + "[pP][+-]?(\\p{Digit}+)))[fFdD]?))[\\x00-\\x20]*"); + + private final Map _ctx = new HashMap<>(); + private final IAdjustableShape _props; + private final Rectangle2D _anchor; public Context(CustomGeometry geom, Rectangle2D anchor, IAdjustableShape props){ _props = props; @@ -39,23 +46,22 @@ public class Context { } } - public Rectangle2D getShapeAnchor(){ + Rectangle2D getShapeAnchor(){ return _anchor; } - public Guide getAdjustValue(String name){ + Guide getAdjustValue(String name){ // ignore HSLF props for now ... the results with default value are usually better - see #59004 return (_props.getClass().getName().contains("hslf")) ? null : _props.getAdjustValue(name); } public double getValue(String key){ - if(key.matches("(\\+|-)?\\d+")){ + if(DOUBLE_PATTERN.matcher(key).matches()){ return Double.parseDouble(key); } - Double val = _ctx.get(key); // BuiltInGuide throws IllegalArgumentException if key is not defined - return (val != null) ? val : evaluate(BuiltInGuide.valueOf("_"+key)); + return _ctx.containsKey(key) ? _ctx.get(key) : evaluate(BuiltInGuide.valueOf("_"+key)); } public double evaluate(Formula fmla){ diff --git a/src/java/org/apache/poi/sl/usermodel/FreeformShape.java b/src/java/org/apache/poi/sl/usermodel/FreeformShape.java index 2a1580a7da..42ae10ae9a 100644 --- a/src/java/org/apache/poi/sl/usermodel/FreeformShape.java +++ b/src/java/org/apache/poi/sl/usermodel/FreeformShape.java @@ -24,16 +24,15 @@ public interface FreeformShape< P extends TextParagraph > extends AutoShape { /** - * Gets the shape path. - *

- * The path is translated in the shape's coordinate system, i.e. - * freeform.getPath().getBounds2D() equals to freeform.getAnchor() - * (small discrepancies are possible due to rounding errors) - *

+ * Gets the shape path.

+ * + * The path is translated in the shape's coordinate system, i.e. + * freeform.getPath2D().getBounds2D() equals to freeform.getAnchor() + * (small discrepancies are possible due to rounding errors) * * @return the path */ - Path2D.Double getPath(); + Path2D getPath(); /** * Set the shape path @@ -41,5 +40,5 @@ public interface FreeformShape< * @param path shape outline * @return the number of points written */ - int setPath(Path2D.Double path); + int setPath(Path2D path); } diff --git a/src/java/org/apache/poi/util/Units.java b/src/java/org/apache/poi/util/Units.java index da0a877cf4..709e3f10cc 100644 --- a/src/java/org/apache/poi/util/Units.java +++ b/src/java/org/apache/poi/util/Units.java @@ -127,7 +127,7 @@ public class Units { points /= MASTER_DPI; return points; } - + public static int pointsToMaster(double points) { points *= MASTER_DPI; points /= POINT_DPI; 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 54acd2b7ba..8c41fe1b93 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java @@ -24,6 +24,12 @@ import java.awt.geom.Path2D; import java.awt.geom.PathIterator; import java.awt.geom.Rectangle2D; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.apache.poi.ooxml.POIXMLTypeLoader; +import org.apache.poi.sl.draw.geom.CustomGeometry; +import org.apache.poi.sl.draw.geom.PresetGeometries; import org.apache.poi.sl.usermodel.FreeformShape; import org.apache.poi.util.Beta; import org.apache.poi.util.POILogFactory; @@ -31,6 +37,7 @@ import org.apache.poi.util.POILogger; import org.apache.poi.util.Units; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.drawingml.x2006.main.CTAdjPoint2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTCustomGeometry2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomRect; @@ -61,7 +68,7 @@ public class XSLFFreeformShape extends XSLFAutoShape } @Override - public int setPath(final Path2D.Double path) { + public int setPath(final Path2D path) { final CTPath2D ctPath = CTPath2D.Factory.newInstance(); final Rectangle2D bounds = path.getBounds2D(); @@ -117,6 +124,30 @@ public class XSLFFreeformShape extends XSLFAutoShape return numPoints; } + /** + * @return definition of the shape geometry + */ + @Override + public CustomGeometry getGeometry() { + final XmlObject xo = getShapeProperties(); + if (!(xo instanceof CTShapeProperties)) { + return null; + } + + XmlOptions xop = new XmlOptions(POIXMLTypeLoader.DEFAULT_XML_OPTIONS); + xop.setSaveOuter(); + + XMLStreamReader staxReader = ((CTShapeProperties)xo).getCustGeom().newXMLStreamReader(xop); + CustomGeometry custGeo = PresetGeometries.convertCustomGeometry(staxReader); + try { + staxReader.close(); + } catch (XMLStreamException e) { + LOG.log(POILogger.WARN, + "An error occurred while closing a Custom Geometry XML Stream Reader: " + e.getMessage()); + } + + return custGeo; + } @Override public Path2D.Double getPath() { 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 4e54712a77..89f312327b 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java @@ -716,7 +716,6 @@ public abstract class XSLFSimpleShape extends XSLFShape } /** - * * @return definition of the shape geometry */ @Override 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 199a9f6286..9a206a48ca 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java @@ -47,7 +47,9 @@ public class TestPPTX2PNG { private static final POIDataSamples samples = POIDataSamples.getSlideShowInstance(); private static final File basedir = null; private static final String files = - "53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt, keyframes.pptx"; + "53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, " + + "backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt, keyframes.pptx," + + "customGeo.pptx, customGeo.ppt"; diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFAutoShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFAutoShape.java index 910c8a0adc..3a3fa7afb3 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFAutoShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFAutoShape.java @@ -17,21 +17,122 @@ package org.apache.poi.hslf.usermodel; +import java.awt.geom.Arc2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Iterator; +import java.util.List; + +import org.apache.poi.ddf.AbstractEscherOptRecord; +import org.apache.poi.ddf.EscherArrayProperty; import org.apache.poi.ddf.EscherContainerRecord; import org.apache.poi.ddf.EscherProperties; -import org.apache.poi.sl.usermodel.*; +import org.apache.poi.ddf.EscherProperty; +import org.apache.poi.ddf.EscherSimpleProperty; +import org.apache.poi.sl.draw.binding.CTAdjPoint2D; +import org.apache.poi.sl.draw.binding.CTCustomGeometry2D; +import org.apache.poi.sl.draw.binding.CTPath2D; +import org.apache.poi.sl.draw.binding.CTPath2DArcTo; +import org.apache.poi.sl.draw.binding.CTPath2DCubicBezierTo; +import org.apache.poi.sl.draw.binding.CTPath2DLineTo; +import org.apache.poi.sl.draw.binding.CTPath2DList; +import org.apache.poi.sl.draw.binding.CTPath2DMoveTo; +import org.apache.poi.sl.draw.binding.ObjectFactory; +import org.apache.poi.sl.draw.geom.CustomGeometry; +import org.apache.poi.sl.usermodel.AutoShape; +import org.apache.poi.sl.usermodel.ShapeContainer; +import org.apache.poi.sl.usermodel.ShapeType; +import org.apache.poi.sl.usermodel.VerticalAlignment; import org.apache.poi.ss.usermodel.ShapeTypes; +import org.apache.poi.util.BitField; +import org.apache.poi.util.BitFieldFactory; +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; /** - * Represents an AutoShape. - *

+ * Represents an AutoShape.

+ * * AutoShapes are drawing objects with a particular shape that may be customized through smart resizing and adjustments. * See {@link ShapeTypes} - *

- * - * @author Yegor Kozlov */ public class HSLFAutoShape extends HSLFTextShape implements AutoShape { + private static final POILogger LOG = POILogFactory.getLogger(HSLFAutoShape.class); + + static final byte[] SEGMENTINFO_MOVETO = new byte[]{0x00, 0x40}; + static final byte[] SEGMENTINFO_LINETO = new byte[]{0x00, (byte)0xAC}; + static final byte[] SEGMENTINFO_ESCAPE = new byte[]{0x01, 0x00}; + static final byte[] SEGMENTINFO_ESCAPE2 = new byte[]{0x01, 0x20}; + static final byte[] SEGMENTINFO_CUBICTO = new byte[]{0x00, (byte)0xAD}; + // OpenOffice inserts 0xB3 instead of 0xAD. + // protected static final byte[] SEGMENTINFO_CUBICTO2 = new byte[]{0x00, (byte)0xB3}; + static final byte[] SEGMENTINFO_CLOSE = new byte[]{0x01, (byte)0x60}; + static final byte[] SEGMENTINFO_END = new byte[]{0x00, (byte)0x80}; + + private static final BitField PATH_INFO = BitFieldFactory.getInstance(0xE000); + private static final BitField ESCAPE_INFO = BitFieldFactory.getInstance(0x1F00); + + enum PathInfo { + lineTo(0),curveTo(1),moveTo(2),close(3),end(4),escape(5),clientEscape(6); + private final int flag; + PathInfo(int flag) { + this.flag = flag; + } + public int getFlag() { + return flag; + } + static PathInfo valueOf(int flag) { + for (PathInfo v : values()) { + if (v.flag == flag) { + return v; + } + } + return null; + } + } + + enum EscapeInfo { + EXTENSION(0x0000), + ANGLE_ELLIPSE_TO(0x0001), + ANGLE_ELLIPSE(0x0002), + ARC_TO(0x0003), + ARC(0x0004), + CLOCKWISE_ARC_TO(0x0005), + CLOCKWISE_ARC(0x0006), + ELLIPTICAL_QUADRANT_X(0x0007), + ELLIPTICAL_QUADRANT_Y(0x0008), + QUADRATIC_BEZIER(0x0009), + NO_FILL(0X000A), + NO_LINE(0X000B), + AUTO_LINE(0X000C), + AUTO_CURVE(0X000D), + CORNER_LINE(0X000E), + CORNER_CURVE(0X000F), + SMOOTH_LINE(0X0010), + SMOOTH_CURVE(0X0011), + SYMMETRIC_LINE(0X0012), + SYMMETRIC_CURVE(0X0013), + FREEFORM(0X0014), + FILL_COLOR(0X0015), + LINE_COLOR(0X0016); + + private final int flag; + EscapeInfo(int flag) { + this.flag = flag; + } + public int getFlag() { + return flag; + } + static EscapeInfo valueOf(int flag) { + for (EscapeInfo v : values()) { + if (v.flag == flag) { + return v; + } + } + return null; + } + } protected HSLFAutoShape(EscherContainerRecord escherRecord, ShapeContainer parent){ super(escherRecord, parent); @@ -72,13 +173,11 @@ public class HSLFAutoShape extends HSLFTextShape implements AutoShape * - *

* The adjustment values are given in shape coordinates: * the origin is at the top-left, positive-x is to the right, positive-y is down. * The region from (0,0) to (S,S) maps to the geometry box of the shape (S=21600 is a constant). - *

* * @param idx the adjust index in the [0, 9] range * @return the adjustment value @@ -90,13 +189,11 @@ public class HSLFAutoShape extends HSLFTextShape implements AutoShape * - *

* The adjustment values are given in shape coordinates: * the origin is at the top-left, positive-x is to the right, positive-y is down. * The region from (0,0) to (S,S) maps to the geometry box of the shape (S=21600 is a constant). - *

* * @param idx the adjust index in the [0, 9] range * @param val the adjustment value @@ -106,4 +203,278 @@ public class HSLFAutoShape extends HSLFTextShape implements AutoShape vertIter = verticesProp.iterator(); + final Iterator segIter = segmentsProp.iterator(); + final int[] xyPoints = new int[2]; + boolean isClosed = false; + + final CTPath2DList pathLst = of.createCTPath2DList(); + final CTPath2D pathCT = of.createCTPath2D(); + final List moveLst = pathCT.getCloseOrMoveToOrLnTo(); + pathLst.getPath().add(pathCT); + cusGeo.setPathLst(pathLst); + + while (segIter.hasNext()) { + byte[] segElem = segIter.next(); + HSLFFreeformShape.PathInfo pi = getPathInfo(segElem); + if (pi == null) { + continue; + } + switch (pi) { + case escape: { + handleEscapeInfo(pathCT, path2D, segElem, vertIter); + break; + } + case moveTo: + if (vertIter.hasNext()) { + final CTPath2DMoveTo m = of.createCTPath2DMoveTo(); + m.setPt(fillPoint(vertIter.next(), xyPoints)); + moveLst.add(m); + path2D.moveTo(xyPoints[0], xyPoints[1]); + } + break; + case lineTo: + if (vertIter.hasNext()) { + final CTPath2DLineTo m = of.createCTPath2DLineTo(); + m.setPt(fillPoint(vertIter.next(), xyPoints)); + moveLst.add(m); + path2D.lineTo(xyPoints[0], xyPoints[1]); + } + break; + case curveTo: { + final CTPath2DCubicBezierTo m = of.createCTPath2DCubicBezierTo(); + List mLst = m.getPt(); + + int[] pts = new int[6]; + + for (int i=0; vertIter.hasNext() && i<3; i++) { + mLst.add(fillPoint(vertIter.next(), xyPoints)); + pts[i*2] = xyPoints[0]; + pts[i*2+1] = xyPoints[1]; + if (i == 2) { + moveLst.add(m); + path2D.curveTo(pts[0], pts[1], pts[2], pts[3], pts[4], pts[5]); + } + } + break; + } + case close: + moveLst.add(of.createCTPath2DClose()); + path2D.closePath(); + isClosed = true; + break; + default: + break; + } + } + + EscherSimpleProperty shapePath = getShapeProp(opt, EscherProperties.GEOMETRY__SHAPEPATH); + HSLFFreeformShape.ShapePath sp = HSLFFreeformShape.ShapePath.valueOf(shapePath == null ? 1 : shapePath.getPropertyValue()); + if ((sp == HSLFFreeformShape.ShapePath.LINES_CLOSED || sp == HSLFFreeformShape.ShapePath.CURVES_CLOSED) && !isClosed) { + moveLst.add(of.createCTPath2DClose()); + path2D.closePath(); + } + + EscherSimpleProperty geoLeft = getShapeProp(opt, EscherProperties.GEOMETRY__LEFT); + EscherSimpleProperty geoRight = getShapeProp(opt, EscherProperties.GEOMETRY__RIGHT); + EscherSimpleProperty geoTop = getShapeProp(opt, EscherProperties.GEOMETRY__TOP); + EscherSimpleProperty geoBottom = getShapeProp(opt, EscherProperties.GEOMETRY__BOTTOM); + + final Rectangle2D bounds; + if (geoLeft != null && geoRight != null && geoTop != null && geoBottom != null) { + bounds = new Rectangle2D.Double(); + bounds.setFrameFromDiagonal( + new Point2D.Double(geoLeft.getPropertyValue(), geoTop.getPropertyValue()), + new Point2D.Double(geoRight.getPropertyValue(), geoBottom.getPropertyValue()) + ); + } else { + bounds = path2D.getBounds2D(); + } + + pathCT.setW((int)Math.rint(bounds.getWidth())); + pathCT.setH((int)Math.rint(bounds.getHeight())); + + return new CustomGeometry(cusGeo); + } + + private void handleEscapeInfo(CTPath2D pathCT, Path2D path2D, byte[] segElem, Iterator vertIter) { + final ObjectFactory of = new ObjectFactory(); + HSLFFreeformShape.EscapeInfo ei = getEscapeInfo(segElem); + switch (ei) { + case EXTENSION: + break; + case ANGLE_ELLIPSE_TO: + break; + case ANGLE_ELLIPSE: + break; + case ARC_TO: { + // The first two POINT values specify the bounding rectangle of the ellipse. + // The second two POINT values specify the radial vectors for the ellipse. + // The radial vectors are cast from the center of the bounding rectangle. + // The path starts at the POINT where the first radial vector intersects the + // bounding rectangle and goes to the POINT where the second radial vector + // intersects the bounding rectangle. The drawing direction is always counterclockwise. + // If the path has already been started, a line is drawn from the last POINT to + // the starting POINT of the arc; otherwise, a new path is started. + // The number of arc segments drawn equals the number of segments divided by four. + + int[] r1 = new int[2], r2 = new int[2], start = new int[2], end = new int[2]; + fillPoint(vertIter.next(), r1); + fillPoint(vertIter.next(), r2); + fillPoint(vertIter.next(), start); + fillPoint(vertIter.next(), end); + + Arc2D arc2D = new Arc2D.Double(); + Rectangle2D.Double bounds = new Rectangle2D.Double(); + bounds.setFrameFromDiagonal(xy2p(r1), xy2p(r2)); + arc2D.setFrame(bounds); + arc2D.setAngles(xy2p(start), xy2p(end)); + path2D.append(arc2D, true); + + + CTPath2DArcTo arcTo = of.createCTPath2DArcTo(); + arcTo.setHR(d2s(bounds.getHeight()/2.0)); + arcTo.setWR(d2s(bounds.getWidth()/2.0)); + + arcTo.setStAng(d2s(-arc2D.getAngleStart()*60000.)); + arcTo.setSwAng(d2s(-arc2D.getAngleExtent()*60000.)); + + pathCT.getCloseOrMoveToOrLnTo().add(arcTo); + + break; + } + case ARC: + break; + case CLOCKWISE_ARC_TO: + break; + case CLOCKWISE_ARC: + break; + case ELLIPTICAL_QUADRANT_X: + break; + case ELLIPTICAL_QUADRANT_Y: + break; + case QUADRATIC_BEZIER: + break; + case NO_FILL: + break; + case NO_LINE: + break; + case AUTO_LINE: + break; + case AUTO_CURVE: + break; + case CORNER_LINE: + break; + case CORNER_CURVE: + break; + case SMOOTH_LINE: + break; + case SMOOTH_CURVE: + break; + case SYMMETRIC_LINE: + break; + case SYMMETRIC_CURVE: + break; + case FREEFORM: + break; + case FILL_COLOR: + break; + case LINE_COLOR: + break; + default: + break; + } + } + + private static String d2s(double d) { + return Integer.toString((int)Math.rint(d)); + } + + private static Point2D xy2p(int[] xyPoints) { + return new Point2D.Double(xyPoints[0],xyPoints[1]); + } + + private static HSLFFreeformShape.PathInfo getPathInfo(byte[] elem) { + int elemUS = LittleEndian.getUShort(elem, 0); + int pathInfo = PATH_INFO.getValue(elemUS); + return HSLFFreeformShape.PathInfo.valueOf(pathInfo); + } + + private static HSLFFreeformShape.EscapeInfo getEscapeInfo(byte[] elem) { + int elemUS = LittleEndian.getUShort(elem, 0); + int escInfo = ESCAPE_INFO.getValue(elemUS); + return HSLFFreeformShape.EscapeInfo.valueOf(escInfo); + } + + + private static T getShapeProp(AbstractEscherOptRecord opt, int propId) { + T prop = getEscherProperty(opt, (short)(propId + 0x4000)); + if (prop == null) { + prop = getEscherProperty(opt, propId); + } + return prop; + } + + private CTAdjPoint2D fillPoint(byte[] xyMaster, int[] xyPoints) { + if (xyMaster == null || xyPoints == null) { + LOG.log(POILogger.WARN, "Master bytes or points not set - ignore point"); + return null; + } + if ((xyMaster.length != 4 && xyMaster.length != 8) || xyPoints.length != 2) { + LOG.log(POILogger.WARN, "Invalid number of master bytes for a single point - ignore point"); + return null; + } + + int x, y; + if (xyMaster.length == 4) { + x = LittleEndian.getShort(xyMaster, 0); + y = LittleEndian.getShort(xyMaster, 2); + } else { + x = LittleEndian.getInt(xyMaster, 0); + y = LittleEndian.getInt(xyMaster, 4); + } + + xyPoints[0] = x; + xyPoints[1] = y; + + return toPoint(xyPoints); + } + + private static CTAdjPoint2D toPoint(int[] xyPoints) { + CTAdjPoint2D pt = new CTAdjPoint2D(); + pt.setX(Integer.toString(xyPoints[0])); + pt.setY(Integer.toString(xyPoints[1])); + return pt; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java index ee0a2d0731..63885af3f3 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java @@ -49,60 +49,61 @@ import org.apache.poi.util.Units; /** * Represents functionality provided by the 'Fill Effects' dialog in PowerPoint. */ +@SuppressWarnings("WeakerAccess") public final class HSLFFill { private static final POILogger LOG = POILogFactory.getLogger(HSLFFill.class); /** * Fill with a solid color */ - public static final int FILL_SOLID = 0; + static final int FILL_SOLID = 0; /** * Fill with a pattern (bitmap) */ - public static final int FILL_PATTERN = 1; + static final int FILL_PATTERN = 1; /** * A texture (pattern with its own color map) */ - public static final int FILL_TEXTURE = 2; + static final int FILL_TEXTURE = 2; /** * Center a picture in the shape */ - public static final int FILL_PICTURE = 3; + static final int FILL_PICTURE = 3; /** * Shade from start to end points */ - public static final int FILL_SHADE = 4; + static final int FILL_SHADE = 4; /** * Shade from bounding rectangle to end point */ - public static final int FILL_SHADE_CENTER = 5; + static final int FILL_SHADE_CENTER = 5; /** * Shade from shape outline to end point */ - public static final int FILL_SHADE_SHAPE = 6; + static final int FILL_SHADE_SHAPE = 6; /** * Similar to FILL_SHADE, but the fill angle * is additionally scaled by the aspect ratio of * the shape. If shape is square, it is the same as FILL_SHADE */ - public static final int FILL_SHADE_SCALE = 7; + static final int FILL_SHADE_SCALE = 7; /** * shade to title */ - public static final int FILL_SHADE_TITLE = 8; + static final int FILL_SHADE_TITLE = 8; /** * Use the background fill color/pattern */ - public static final int FILL_BACKGROUND = 9; + static final int FILL_BACKGROUND = 9; /** * A bit that specifies whether the RecolorFillAsPicture bit is set. @@ -214,7 +215,7 @@ public final class HSLFFill { private HSLFShape shape; /** - * Construct a Fill object for a shape. + * Construct a {@code Fill} object for a shape. * Fill information will be read from shape's escher properties. * * @param shape the shape this background applies to @@ -279,7 +280,7 @@ public final class HSLFFill { @Override public ColorStyle[] getGradientColors() { - ColorStyle cs[]; + ColorStyle[] cs; if (colorCnt == 0) { cs = new ColorStyle[2]; cs[0] = wrapColor(getBackgroundColor()); @@ -288,7 +289,7 @@ public final class HSLFFill { cs = new ColorStyle[colorCnt]; int idx = 0; // TODO: handle palette colors and alpha(?) value - for (byte data[] : ep) { + for (byte[] data : ep) { EscherColorRef ecr = new EscherColorRef(data, 0, 4); cs[idx++] = wrapColor(shape.getColor(ecr)); } @@ -302,13 +303,13 @@ public final class HSLFFill { @Override public float[] getGradientFractions() { - float frc[]; + float[] frc; if (colorCnt == 0) { frc = new float[]{0, 1}; } else { frc = new float[colorCnt]; int idx = 0; - for (byte data[] : ep) { + for (byte[] data : ep) { double pos = Units.fixedPointToDouble(LittleEndian.getInt(data, 4)); frc[idx++] = (float)pos; } @@ -354,7 +355,7 @@ public final class HSLFFill { /** * Returns fill type. - * Must be one of the FILL_* constants defined in this class. + * Must be one of the {@code FILL_*} constants defined in this class. * * @return type of fill */ @@ -364,9 +365,7 @@ public final class HSLFFill { return prop == null ? FILL_SOLID : prop.getPropertyValue(); } - /** - */ - protected void afterInsert(HSLFSheet sh){ + void afterInsert(HSLFSheet sh){ AbstractEscherOptRecord opt = shape.getEscherOptRecord(); EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE); if(p != null) { @@ -379,7 +378,7 @@ public final class HSLFFill { } @SuppressWarnings("resource") - protected EscherBSERecord getEscherBSERecord(int idx){ + EscherBSERecord getEscherBSERecord(int idx){ HSLFSheet sheet = shape.getSheet(); if(sheet == null) { LOG.log(POILogger.DEBUG, "Fill has not yet been assigned to a sheet"); @@ -399,7 +398,7 @@ public final class HSLFFill { /** * Sets fill type. - * Must be one of the FILL_* constants defined in this class. + * Must be one of the {@code FILL_*} constants defined in this class. * * @param type type of the fill */ @@ -415,10 +414,10 @@ public final class HSLFFill { AbstractEscherOptRecord opt = shape.getEscherOptRecord(); EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); int propVal = (p == null) ? 0 : p.getPropertyValue(); - + return (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal)) ? null - : shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY, -1); + : shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY); } /** @@ -462,7 +461,7 @@ public final class HSLFFill { return (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal)) ? null - : shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY, -1); + : shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY); } /** @@ -480,7 +479,7 @@ public final class HSLFFill { } /** - * PictureData object used in a texture, pattern of picture fill. + * {@code PictureData} object used in a texture, pattern of picture fill. */ @SuppressWarnings("resource") public HSLFPictureData getPictureData(){ diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java index 0b2d322b63..10eeb7b19c 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java @@ -23,20 +23,16 @@ import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import org.apache.poi.ddf.AbstractEscherOptRecord; import org.apache.poi.ddf.EscherArrayProperty; import org.apache.poi.ddf.EscherContainerRecord; import org.apache.poi.ddf.EscherProperties; -import org.apache.poi.ddf.EscherProperty; import org.apache.poi.ddf.EscherSimpleProperty; import org.apache.poi.sl.usermodel.FreeformShape; import org.apache.poi.sl.usermodel.ShapeContainer; import org.apache.poi.sl.usermodel.ShapeType; -import org.apache.poi.util.BitField; -import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -53,79 +49,6 @@ import org.apache.poi.util.Units; public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformShape { private static final POILogger LOG = POILogFactory.getLogger(HSLFFreeformShape.class); - private static final byte[] SEGMENTINFO_MOVETO = new byte[]{0x00, 0x40}; - private static final byte[] SEGMENTINFO_LINETO = new byte[]{0x00, (byte)0xAC}; - private static final byte[] SEGMENTINFO_ESCAPE = new byte[]{0x01, 0x00}; - private static final byte[] SEGMENTINFO_ESCAPE2 = new byte[]{0x01, 0x20}; - private static final byte[] SEGMENTINFO_CUBICTO = new byte[]{0x00, (byte)0xAD}; - // OpenOffice inserts 0xB3 instead of 0xAD. - // private static final byte[] SEGMENTINFO_CUBICTO2 = new byte[]{0x00, (byte)0xB3}; - private static final byte[] SEGMENTINFO_CLOSE = new byte[]{0x01, (byte)0x60}; - private static final byte[] SEGMENTINFO_END = new byte[]{0x00, (byte)0x80}; - - private static final BitField PATH_INFO = BitFieldFactory.getInstance(0xE000); - // private static final BitField ESCAPE_INFO = BitFieldFactory.getInstance(0x1F00); - - enum PathInfo { - lineTo(0),curveTo(1),moveTo(2),close(3),end(4),escape(5),clientEscape(6); - private final int flag; - PathInfo(int flag) { - this.flag = flag; - } - public int getFlag() { - return flag; - } - static PathInfo valueOf(int flag) { - for (PathInfo v : values()) { - if (v.flag == flag) { - return v; - } - } - return null; - } - } - - enum EscapeInfo { - EXTENSION(0x0000), - ANGLE_ELLIPSE_TO(0x0001), - ANGLE_ELLIPSE(0x0002), - ARC_TO(0x0003), - ARC(0x0004), - CLOCKWISE_ARC_TO(0x0005), - CLOCKWISE_ARC(0x0006), - ELLIPTICAL_QUADRANT_X(0x0007), - ELLIPTICAL_QUADRANT_Y(0x0008), - QUADRATIC_BEZIER(0x0009), - NO_FILL(0X000A), - NO_LINE(0X000B), - AUTO_LINE(0X000C), - AUTO_CURVE(0X000D), - CORNER_LINE(0X000E), - CORNER_CURVE(0X000F), - SMOOTH_LINE(0X0010), - SMOOTH_CURVE(0X0011), - SYMMETRIC_LINE(0X0012), - SYMMETRIC_CURVE(0X0013), - FREEFORM(0X0014), - FILL_COLOR(0X0015), - LINE_COLOR(0X0016); - - private final int flag; - EscapeInfo(int flag) { - this.flag = flag; - } - public int getFlag() { - return flag; - } - static EscapeInfo valueOf(int flag) { - for (EscapeInfo v : values()) { - if (v.flag == flag) { - return v; - } - } - return null; - } - } enum ShapePath { LINES(0), @@ -182,9 +105,9 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh } @Override - public int setPath(Path2D.Double path) { + public int setPath(Path2D path) { Rectangle2D bounds = path.getBounds2D(); - PathIterator it = path.getPathIterator(new AffineTransform()); + PathIterator it = path.getPathIterator(null); List segInfo = new ArrayList<>(); List pntInfo = new ArrayList<>(); @@ -275,187 +198,24 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh } @Override - public Path2D.Double getPath(){ - AbstractEscherOptRecord opt = getEscherOptRecord(); - - EscherArrayProperty verticesProp = getShapeProp(opt, EscherProperties.GEOMETRY__VERTICES); - EscherArrayProperty segmentsProp = getShapeProp(opt, EscherProperties.GEOMETRY__SEGMENTINFO); - - // return empty path if either GEOMETRY__VERTICES or GEOMETRY__SEGMENTINFO is missing, see Bugzilla 54188 - Path2D.Double path = new Path2D.Double(); + public Path2D getPath(){ + Path2D path2D = new Path2D.Double(); + getGeometry(path2D); - //sanity check - if(verticesProp == null) { - LOG.log(POILogger.WARN, "Freeform is missing GEOMETRY__VERTICES "); - return path; - } - if(segmentsProp == null) { - LOG.log(POILogger.WARN, "Freeform is missing GEOMETRY__SEGMENTINFO "); - return path; - } - - Iterator vertIter = verticesProp.iterator(); - Iterator segIter = segmentsProp.iterator(); - double xyPoints[] = new double[2]; - - while (vertIter.hasNext() && segIter.hasNext()) { - byte[] segElem = segIter.next(); - PathInfo pi = getPathInfo(segElem); - if (pi != null) { - switch (pi) { - case escape: { - // handleEscapeInfo(path, segElem, vertIter); - break; - } - case moveTo: { - fillPoint(vertIter.next(), xyPoints); - double x = xyPoints[0]; - double y = xyPoints[1]; - path.moveTo(x, y); - break; - } - case curveTo: { - fillPoint(vertIter.next(), xyPoints); - double x1 = xyPoints[0]; - double y1 = xyPoints[1]; - fillPoint(vertIter.next(), xyPoints); - double x2 = xyPoints[0]; - double y2 = xyPoints[1]; - fillPoint(vertIter.next(), xyPoints); - double x3 = xyPoints[0]; - double y3 = xyPoints[1]; - path.curveTo(x1, y1, x2, y2, x3, y3); - break; - } - case lineTo: - if (vertIter.hasNext()) { - fillPoint(vertIter.next(), xyPoints); - double x = xyPoints[0]; - double y = xyPoints[1]; - path.lineTo(x, y); - } - break; - case close: - path.closePath(); - break; - default: - break; - } - } - } - - EscherSimpleProperty shapePath = getShapeProp(opt, EscherProperties.GEOMETRY__SHAPEPATH); - ShapePath sp = ShapePath.valueOf(shapePath == null ? 1 : shapePath.getPropertyValue()); - if (sp == ShapePath.LINES_CLOSED || sp == ShapePath.CURVES_CLOSED) { - path.closePath(); - } - + Rectangle2D bounds = path2D.getBounds2D(); Rectangle2D anchor = getAnchor(); - Rectangle2D bounds = path.getBounds2D(); AffineTransform at = new AffineTransform(); at.translate(anchor.getX(), anchor.getY()); at.scale( anchor.getWidth()/bounds.getWidth(), anchor.getHeight()/bounds.getHeight() ); - return new Path2D.Double(at.createTransformedShape(path)); - } - - private void fillPoint(byte xyMaster[], double xyPoints[]) { - if (xyMaster == null || xyPoints == null) { - LOG.log(POILogger.WARN, "Master bytes or points not set - ignore point"); - return; - } - if ((xyMaster.length != 4 && xyMaster.length != 8) || xyPoints.length != 2) { - LOG.log(POILogger.WARN, "Invalid number of master bytes for a single point - ignore point"); - return; - } - - int x, y; - if (xyMaster.length == 4) { - x = LittleEndian.getShort(xyMaster, 0); - y = LittleEndian.getShort(xyMaster, 2); - } else { - x = LittleEndian.getInt(xyMaster, 0); - y = LittleEndian.getInt(xyMaster, 4); - } - - xyPoints[0] = Units.masterToPoints(x); - xyPoints[1] = Units.masterToPoints(y); - } - - private static T getShapeProp(AbstractEscherOptRecord opt, int propId) { - T prop = getEscherProperty(opt, (short)(propId + 0x4000)); - if (prop == null) { - prop = getEscherProperty(opt, propId); - } - return prop; - } - -// private void handleEscapeInfo(Path2D path, byte segElem[], Iterator vertIter) { -// EscapeInfo ei = getEscapeInfo(segElem); -// switch (ei) { -// case EXTENSION: -// break; -// case ANGLE_ELLIPSE_TO: -// break; -// case ANGLE_ELLIPSE: -// break; -// case ARC_TO: -// break; -// case ARC: -// break; -// case CLOCKWISE_ARC_TO: -// break; -// case CLOCKWISE_ARC: -// break; -// case ELLIPTICAL_QUADRANT_X: -// break; -// case ELLIPTICAL_QUADRANT_Y: -// break; -// case QUADRATIC_BEZIER: -// break; -// case NO_FILL: -// break; -// case NO_LINE: -// break; -// case AUTO_LINE: -// break; -// case AUTO_CURVE: -// break; -// case CORNER_LINE: -// break; -// case CORNER_CURVE: -// break; -// case SMOOTH_LINE: -// break; -// case SMOOTH_CURVE: -// break; -// case SYMMETRIC_LINE: -// break; -// case SYMMETRIC_CURVE: -// break; -// case FREEFORM: -// break; -// case FILL_COLOR: -// break; -// case LINE_COLOR: -// break; -// default: -// break; -// } -// } - - private static PathInfo getPathInfo(byte elem[]) { - int elemUS = LittleEndian.getUShort(elem, 0); - int pathInfo = PATH_INFO.getValue(elemUS); - return PathInfo.valueOf(pathInfo); + path2D.transform(at); + + + return path2D; } - -// private static EscapeInfo getEscapeInfo(byte elem[]) { -// int elemUS = LittleEndian.getUShort(elem, 0); -// int escInfo = ESCAPE_INFO.getValue(elemUS); -// return EscapeInfo.valueOf(escInfo); -// } + + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java index 085d617eb8..f1f2c22406 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java @@ -358,17 +358,18 @@ public abstract class HSLFShape implements Shape { _sheet = sheet; } - Color getColor(short colorProperty, short opacityProperty, int defaultColor){ - AbstractEscherOptRecord opt = getEscherOptRecord(); - EscherSimpleProperty p = getEscherProperty(opt, colorProperty); - if(p == null && defaultColor == -1) return null; - - int val = (p == null) ? defaultColor : p.getPropertyValue(); - - EscherColorRef ecr = new EscherColorRef(val); - Color col = getColor(ecr); - if (col == null) { - return null; + Color getColor(short colorProperty, short opacityProperty){ + final AbstractEscherOptRecord opt = getEscherOptRecord(); + final EscherSimpleProperty colProp = getEscherProperty(opt, colorProperty); + final Color col; + if (colProp == null) { + col = Color.WHITE; + } else { + EscherColorRef ecr = new EscherColorRef(colProp.getPropertyValue()); + col = getColor(ecr); + if (col == null) { + return null; + } } double alpha = getAlpha(opacityProperty); diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java index f9cf74c263..acabc68715 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java @@ -164,7 +164,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShapejava.awt.Color.black */ public Color getShadowColor(){ - Color clr = getColor(EscherProperties.SHADOWSTYLE__COLOR, EscherProperties.SHADOWSTYLE__OPACITY, -1); + Color clr = getColor(EscherProperties.SHADOWSTYLE__COLOR, EscherProperties.SHADOWSTYLE__OPACITY); return clr == null ? Color.black : clr; } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java index 14301af2ba..26b30ea53c 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java @@ -25,7 +25,6 @@ import org.junit.runners.Suite; */ @RunWith(Suite.class) @Suite.SuiteClasses({ - TestBackground.class, TestFreeform.class, TestHeadersFooters.class, TestHyperlink.class, diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestBackground.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestBackground.java deleted file mode 100644 index faf61fbc52..0000000000 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestBackground.java +++ /dev/null @@ -1,227 +0,0 @@ -/* ==================================================================== - 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.hslf.model; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.awt.Color; -import java.io.IOException; -import java.util.List; - -import org.apache.poi.POIDataSamples; -import org.apache.poi.ddf.AbstractEscherOptRecord; -import org.apache.poi.ddf.EscherBSERecord; -import org.apache.poi.ddf.EscherContainerRecord; -import org.apache.poi.ddf.EscherProperties; -import org.apache.poi.ddf.EscherRecord; -import org.apache.poi.ddf.EscherSimpleProperty; -import org.apache.poi.hslf.HSLFTestDataSamples; -import org.apache.poi.hslf.record.Document; -import org.apache.poi.hslf.usermodel.HSLFAutoShape; -import org.apache.poi.hslf.usermodel.HSLFFill; -import org.apache.poi.hslf.usermodel.HSLFPictureData; -import org.apache.poi.hslf.usermodel.HSLFShape; -import org.apache.poi.hslf.usermodel.HSLFSheet; -import org.apache.poi.hslf.usermodel.HSLFSlide; -import org.apache.poi.hslf.usermodel.HSLFSlideShow; -import org.apache.poi.sl.usermodel.PictureData.PictureType; -import org.apache.poi.sl.usermodel.ShapeType; -import org.junit.Test; - - -/** - * Test Fill object. - * - * @author Yegor Kozlov - */ -public final class TestBackground { - private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance(); - - /** - * Default background for slide, shape and slide master. - */ - @Test - public void defaults() throws IOException { - HSLFSlideShow ppt = new HSLFSlideShow(); - - assertEquals(HSLFFill.FILL_SOLID, ppt.getSlideMasters().get(0).getBackground().getFill().getFillType()); - - HSLFSlide slide = ppt.createSlide(); - assertTrue(slide.getFollowMasterBackground()); - assertEquals(HSLFFill.FILL_SOLID, slide.getBackground().getFill().getFillType()); - - HSLFShape shape = new HSLFAutoShape(ShapeType.RECT); - assertEquals(HSLFFill.FILL_SOLID, shape.getFill().getFillType()); - ppt.close(); - } - - /** - * Read fill information from an reference ppt file - */ - @Test - public void readBackground() throws IOException { - HSLFSlideShow ppt = HSLFTestDataSamples.getSlideShow("backgrounds.ppt"); - HSLFFill fill; - HSLFShape shape; - - List slide = ppt.getSlides(); - - fill = slide.get(0).getBackground().getFill(); - assertEquals(HSLFFill.FILL_PICTURE, fill.getFillType()); - shape = slide.get(0).getShapes().get(0); - assertEquals(HSLFFill.FILL_SOLID, shape.getFill().getFillType()); - - fill = slide.get(1).getBackground().getFill(); - assertEquals(HSLFFill.FILL_PATTERN, fill.getFillType()); - shape = slide.get(1).getShapes().get(0); - assertEquals(HSLFFill.FILL_BACKGROUND, shape.getFill().getFillType()); - - fill = slide.get(2).getBackground().getFill(); - assertEquals(HSLFFill.FILL_TEXTURE, fill.getFillType()); - shape = slide.get(2).getShapes().get(0); - assertEquals(HSLFFill.FILL_PICTURE, shape.getFill().getFillType()); - - fill = slide.get(3).getBackground().getFill(); - assertEquals(HSLFFill.FILL_SHADE_CENTER, fill.getFillType()); - shape = slide.get(3).getShapes().get(0); - assertEquals(HSLFFill.FILL_SHADE, shape.getFill().getFillType()); - ppt.close(); - } - - /** - * Create a ppt with various fill effects - */ - @Test - public void backgroundPicture() throws IOException { - HSLFSlideShow ppt1 = new HSLFSlideShow(); - HSLFSlide slide; - HSLFFill fill; - HSLFShape shape; - HSLFPictureData data; - - //slide 1 - slide = ppt1.createSlide(); - slide.setFollowMasterBackground(false); - fill = slide.getBackground().getFill(); - data = ppt1.addPicture(_slTests.readFile("tomcat.png"), PictureType.PNG); - fill.setFillType(HSLFFill.FILL_PICTURE); - fill.setPictureData(data); - - shape = new HSLFAutoShape(ShapeType.RECT); - shape.setAnchor(new java.awt.Rectangle(100, 100, 200, 200)); - fill = shape.getFill(); - fill.setFillType(HSLFFill.FILL_SOLID); - slide.addShape(shape); - - //slide 2 - slide = ppt1.createSlide(); - slide.setFollowMasterBackground(false); - fill = slide.getBackground().getFill(); - data = ppt1.addPicture(_slTests.readFile("tomcat.png"), PictureType.PNG); - fill.setFillType(HSLFFill.FILL_PATTERN); - fill.setPictureData(data); - fill.setBackgroundColor(Color.green); - fill.setForegroundColor(Color.red); - - shape = new HSLFAutoShape(ShapeType.RECT); - shape.setAnchor(new java.awt.Rectangle(100, 100, 200, 200)); - fill = shape.getFill(); - fill.setFillType(HSLFFill.FILL_BACKGROUND); - slide.addShape(shape); - - //slide 3 - slide = ppt1.createSlide(); - slide.setFollowMasterBackground(false); - fill = slide.getBackground().getFill(); - data = ppt1.addPicture(_slTests.readFile("tomcat.png"), PictureType.PNG); - fill.setFillType(HSLFFill.FILL_TEXTURE); - fill.setPictureData(data); - - shape = new HSLFAutoShape(ShapeType.RECT); - shape.setAnchor(new java.awt.Rectangle(100, 100, 200, 200)); - fill = shape.getFill(); - fill.setFillType(HSLFFill.FILL_PICTURE); - data = ppt1.addPicture(_slTests.readFile("clock.jpg"), PictureType.JPEG); - fill.setPictureData(data); - slide.addShape(shape); - - // slide 4 - slide = ppt1.createSlide(); - slide.setFollowMasterBackground(false); - fill = slide.getBackground().getFill(); - fill.setFillType(HSLFFill.FILL_SHADE_CENTER); - fill.setBackgroundColor(Color.white); - fill.setForegroundColor(Color.darkGray); - - shape = new HSLFAutoShape(ShapeType.RECT); - shape.setAnchor(new java.awt.Rectangle(100, 100, 200, 200)); - fill = shape.getFill(); - fill.setFillType(HSLFFill.FILL_SHADE); - fill.setBackgroundColor(Color.red); - fill.setForegroundColor(Color.green); - slide.addShape(shape); - - //serialize and read again - HSLFSlideShow ppt2 = HSLFTestDataSamples.writeOutAndReadBack(ppt1); - List slides = ppt2.getSlides(); - - fill = slides.get(0).getBackground().getFill(); - assertEquals(HSLFFill.FILL_PICTURE, fill.getFillType()); - assertEquals(3, getFillPictureRefCount(slides.get(0).getBackground(), fill)); - shape = slides.get(0).getShapes().get(0); - assertEquals(HSLFFill.FILL_SOLID, shape.getFill().getFillType()); - - fill = slides.get(1).getBackground().getFill(); - assertEquals(HSLFFill.FILL_PATTERN, fill.getFillType()); - shape = slides.get(1).getShapes().get(0); - assertEquals(HSLFFill.FILL_BACKGROUND, shape.getFill().getFillType()); - - fill = slides.get(2).getBackground().getFill(); - assertEquals(HSLFFill.FILL_TEXTURE, fill.getFillType()); - assertEquals(3, getFillPictureRefCount(slides.get(2).getBackground(), fill)); - shape = slides.get(2).getShapes().get(0); - assertEquals(HSLFFill.FILL_PICTURE, shape.getFill().getFillType()); - assertEquals(1, getFillPictureRefCount(shape, fill)); - - fill = slides.get(3).getBackground().getFill(); - assertEquals(HSLFFill.FILL_SHADE_CENTER, fill.getFillType()); - shape = slides.get(3).getShapes().get(0); - assertEquals(HSLFFill.FILL_SHADE, shape.getFill().getFillType()); - ppt2.close(); - ppt1.close(); - } - - private int getFillPictureRefCount(HSLFShape shape, HSLFFill fill) { - AbstractEscherOptRecord opt = shape.getEscherOptRecord(); - EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE); - if(p != null) { - int idx = p.getPropertyValue(); - - HSLFSheet sheet = shape.getSheet(); - HSLFSlideShow ppt = sheet.getSlideShow(); - Document doc = ppt.getDocumentRecord(); - EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer(); - EscherContainerRecord bstore = HSLFShape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER); - List lst = bstore.getChildRecords(); - return ((EscherBSERecord)lst.get(idx-1)).getRef(); - } - return 0; - } - -} diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java index 91d23664d8..1e3c52ddee 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java @@ -87,7 +87,7 @@ public final class TestFreeform { public void test54188() { HSLFFreeformShape p = new HSLFFreeformShape(); - Path2D.Double path = p.getPath(); + Path2D path = p.getPath(); Path2D.Double emptyPath = new Path2D.Double(); assertEquals(emptyPath.getBounds2D(), path.getBounds2D()); } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java index 0354d9b46c..cf8dc7658d 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java @@ -26,6 +26,7 @@ import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ TestAddingSlides.class, + TestBackground.class, TestBugs.class, TestCounts.class, TestMostRecentRecords.class, diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBackground.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBackground.java new file mode 100644 index 0000000000..2732d16183 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBackground.java @@ -0,0 +1,227 @@ +/* ==================================================================== + 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.hslf.usermodel; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.awt.Color; +import java.io.IOException; +import java.util.List; + +import org.apache.poi.POIDataSamples; +import org.apache.poi.ddf.AbstractEscherOptRecord; +import org.apache.poi.ddf.EscherBSERecord; +import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.ddf.EscherProperties; +import org.apache.poi.ddf.EscherRecord; +import org.apache.poi.ddf.EscherSimpleProperty; +import org.apache.poi.hslf.HSLFTestDataSamples; +import org.apache.poi.hslf.record.Document; +import org.apache.poi.hslf.usermodel.HSLFAutoShape; +import org.apache.poi.hslf.usermodel.HSLFFill; +import org.apache.poi.hslf.usermodel.HSLFPictureData; +import org.apache.poi.hslf.usermodel.HSLFShape; +import org.apache.poi.hslf.usermodel.HSLFSheet; +import org.apache.poi.hslf.usermodel.HSLFSlide; +import org.apache.poi.hslf.usermodel.HSLFSlideShow; +import org.apache.poi.sl.usermodel.PictureData.PictureType; +import org.apache.poi.sl.usermodel.ShapeType; +import org.junit.Test; + + +/** + * Test Fill object. + * + * @author Yegor Kozlov + */ +public final class TestBackground { + private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance(); + + /** + * Default background for slide, shape and slide master. + */ + @Test + public void defaults() throws IOException { + HSLFSlideShow ppt = new HSLFSlideShow(); + + assertEquals(HSLFFill.FILL_SOLID, ppt.getSlideMasters().get(0).getBackground().getFill().getFillType()); + + HSLFSlide slide = ppt.createSlide(); + assertTrue(slide.getFollowMasterBackground()); + assertEquals(HSLFFill.FILL_SOLID, slide.getBackground().getFill().getFillType()); + + HSLFShape shape = new HSLFAutoShape(ShapeType.RECT); + assertEquals(HSLFFill.FILL_SOLID, shape.getFill().getFillType()); + ppt.close(); + } + + /** + * Read fill information from an reference ppt file + */ + @Test + public void readBackground() throws IOException { + HSLFSlideShow ppt = HSLFTestDataSamples.getSlideShow("backgrounds.ppt"); + HSLFFill fill; + HSLFShape shape; + + List slide = ppt.getSlides(); + + fill = slide.get(0).getBackground().getFill(); + assertEquals(HSLFFill.FILL_PICTURE, fill.getFillType()); + shape = slide.get(0).getShapes().get(0); + assertEquals(HSLFFill.FILL_SOLID, shape.getFill().getFillType()); + + fill = slide.get(1).getBackground().getFill(); + assertEquals(HSLFFill.FILL_PATTERN, fill.getFillType()); + shape = slide.get(1).getShapes().get(0); + assertEquals(HSLFFill.FILL_BACKGROUND, shape.getFill().getFillType()); + + fill = slide.get(2).getBackground().getFill(); + assertEquals(HSLFFill.FILL_TEXTURE, fill.getFillType()); + shape = slide.get(2).getShapes().get(0); + assertEquals(HSLFFill.FILL_PICTURE, shape.getFill().getFillType()); + + fill = slide.get(3).getBackground().getFill(); + assertEquals(HSLFFill.FILL_SHADE_CENTER, fill.getFillType()); + shape = slide.get(3).getShapes().get(0); + assertEquals(HSLFFill.FILL_SHADE, shape.getFill().getFillType()); + ppt.close(); + } + + /** + * Create a ppt with various fill effects + */ + @Test + public void backgroundPicture() throws IOException { + HSLFSlideShow ppt1 = new HSLFSlideShow(); + HSLFSlide slide; + HSLFFill fill; + HSLFShape shape; + HSLFPictureData data; + + //slide 1 + slide = ppt1.createSlide(); + slide.setFollowMasterBackground(false); + fill = slide.getBackground().getFill(); + data = ppt1.addPicture(_slTests.readFile("tomcat.png"), PictureType.PNG); + fill.setFillType(HSLFFill.FILL_PICTURE); + fill.setPictureData(data); + + shape = new HSLFAutoShape(ShapeType.RECT); + shape.setAnchor(new java.awt.Rectangle(100, 100, 200, 200)); + fill = shape.getFill(); + fill.setFillType(HSLFFill.FILL_SOLID); + slide.addShape(shape); + + //slide 2 + slide = ppt1.createSlide(); + slide.setFollowMasterBackground(false); + fill = slide.getBackground().getFill(); + data = ppt1.addPicture(_slTests.readFile("tomcat.png"), PictureType.PNG); + fill.setFillType(HSLFFill.FILL_PATTERN); + fill.setPictureData(data); + fill.setBackgroundColor(Color.green); + fill.setForegroundColor(Color.red); + + shape = new HSLFAutoShape(ShapeType.RECT); + shape.setAnchor(new java.awt.Rectangle(100, 100, 200, 200)); + fill = shape.getFill(); + fill.setFillType(HSLFFill.FILL_BACKGROUND); + slide.addShape(shape); + + //slide 3 + slide = ppt1.createSlide(); + slide.setFollowMasterBackground(false); + fill = slide.getBackground().getFill(); + data = ppt1.addPicture(_slTests.readFile("tomcat.png"), PictureType.PNG); + fill.setFillType(HSLFFill.FILL_TEXTURE); + fill.setPictureData(data); + + shape = new HSLFAutoShape(ShapeType.RECT); + shape.setAnchor(new java.awt.Rectangle(100, 100, 200, 200)); + fill = shape.getFill(); + fill.setFillType(HSLFFill.FILL_PICTURE); + data = ppt1.addPicture(_slTests.readFile("clock.jpg"), PictureType.JPEG); + fill.setPictureData(data); + slide.addShape(shape); + + // slide 4 + slide = ppt1.createSlide(); + slide.setFollowMasterBackground(false); + fill = slide.getBackground().getFill(); + fill.setFillType(HSLFFill.FILL_SHADE_CENTER); + fill.setBackgroundColor(Color.white); + fill.setForegroundColor(Color.darkGray); + + shape = new HSLFAutoShape(ShapeType.RECT); + shape.setAnchor(new java.awt.Rectangle(100, 100, 200, 200)); + fill = shape.getFill(); + fill.setFillType(HSLFFill.FILL_SHADE); + fill.setBackgroundColor(Color.red); + fill.setForegroundColor(Color.green); + slide.addShape(shape); + + //serialize and read again + HSLFSlideShow ppt2 = HSLFTestDataSamples.writeOutAndReadBack(ppt1); + List slides = ppt2.getSlides(); + + fill = slides.get(0).getBackground().getFill(); + assertEquals(HSLFFill.FILL_PICTURE, fill.getFillType()); + assertEquals(3, getFillPictureRefCount(slides.get(0).getBackground(), fill)); + shape = slides.get(0).getShapes().get(0); + assertEquals(HSLFFill.FILL_SOLID, shape.getFill().getFillType()); + + fill = slides.get(1).getBackground().getFill(); + assertEquals(HSLFFill.FILL_PATTERN, fill.getFillType()); + shape = slides.get(1).getShapes().get(0); + assertEquals(HSLFFill.FILL_BACKGROUND, shape.getFill().getFillType()); + + fill = slides.get(2).getBackground().getFill(); + assertEquals(HSLFFill.FILL_TEXTURE, fill.getFillType()); + assertEquals(3, getFillPictureRefCount(slides.get(2).getBackground(), fill)); + shape = slides.get(2).getShapes().get(0); + assertEquals(HSLFFill.FILL_PICTURE, shape.getFill().getFillType()); + assertEquals(1, getFillPictureRefCount(shape, fill)); + + fill = slides.get(3).getBackground().getFill(); + assertEquals(HSLFFill.FILL_SHADE_CENTER, fill.getFillType()); + shape = slides.get(3).getShapes().get(0); + assertEquals(HSLFFill.FILL_SHADE, shape.getFill().getFillType()); + ppt2.close(); + ppt1.close(); + } + + private int getFillPictureRefCount(HSLFShape shape, HSLFFill fill) { + AbstractEscherOptRecord opt = shape.getEscherOptRecord(); + EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE); + if(p != null) { + int idx = p.getPropertyValue(); + + HSLFSheet sheet = shape.getSheet(); + HSLFSlideShow ppt = sheet.getSlideShow(); + Document doc = ppt.getDocumentRecord(); + EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer(); + EscherContainerRecord bstore = HSLFShape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER); + List lst = bstore.getChildRecords(); + return ((EscherBSERecord)lst.get(idx-1)).getRef(); + } + return 0; + } + +} diff --git a/test-data/slideshow/customGeo.ppt b/test-data/slideshow/customGeo.ppt new file mode 100644 index 0000000000..fdb80edcbc Binary files /dev/null and b/test-data/slideshow/customGeo.ppt differ diff --git a/test-data/slideshow/customGeo.pptx b/test-data/slideshow/customGeo.pptx new file mode 100644 index 0000000000..c30e33140b Binary files /dev/null and b/test-data/slideshow/customGeo.pptx differ