git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1848492 13f79535-47bb-0310-9956-ffa450edef68pull/133/head
@@ -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<Short, EscherPropertyMetaData> properties = initProps(); | |||
private EscherProperties() { | |||
} | |||
private static Map<Short, EscherPropertyMetaData> initProps() { | |||
Map<Short, EscherPropertyMetaData> 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<Short, EscherPropertyMetaData> 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<Short, EscherPropertyMetaData> 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(); | |||
} | |||
} |
@@ -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<Outline> computeOutlines(Graphics2D graphics) { | |||
List<Outline> 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<?,? extends TextParagraph<?,?,? extends TextRun>> getShape() { | |||
return (TextShape<?,? extends TextParagraph<?,?,? extends TextRun>>)shape; | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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<String, Double> _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<String, Double> _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){ |
@@ -24,16 +24,15 @@ public interface FreeformShape< | |||
P extends TextParagraph<S,P,? extends TextRun> | |||
> extends AutoShape<S,P> { | |||
/** | |||
* Gets the shape path. | |||
* <p> | |||
* 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) | |||
* </p> | |||
* Gets the shape path.<p> | |||
* | |||
* 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); | |||
} |
@@ -127,7 +127,7 @@ public class Units { | |||
points /= MASTER_DPI; | |||
return points; | |||
} | |||
public static int pointsToMaster(double points) { | |||
points *= MASTER_DPI; | |||
points /= POINT_DPI; |
@@ -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() { |
@@ -716,7 +716,6 @@ public abstract class XSLFSimpleShape extends XSLFShape | |||
} | |||
/** | |||
* | |||
* @return definition of the shape geometry | |||
*/ | |||
@Override |
@@ -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"; | |||
@@ -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. | |||
* <p> | |||
* Represents an AutoShape.<p> | |||
* | |||
* AutoShapes are drawing objects with a particular shape that may be customized through smart resizing and adjustments. | |||
* See {@link ShapeTypes} | |||
* </p> | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class HSLFAutoShape extends HSLFTextShape implements AutoShape<HSLFShape,HSLFTextParagraph> { | |||
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<HSLFShape,HSLFTextParagraph> parent){ | |||
super(escherRecord, parent); | |||
@@ -72,13 +173,11 @@ public class HSLFAutoShape extends HSLFTextShape implements AutoShape<HSLFShape, | |||
} | |||
/** | |||
* Gets adjust value which controls smart resizing of the auto-shape. | |||
* Gets adjust value which controls smart resizing of the auto-shape.<p> | |||
* | |||
* <p> | |||
* 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). | |||
* </p> | |||
* | |||
* @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<HSLFShape, | |||
} | |||
/** | |||
* Sets adjust value which controls smart resizing of the auto-shape. | |||
* Sets adjust value which controls smart resizing of the auto-shape.<p> | |||
* | |||
* <p> | |||
* 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). | |||
* </p> | |||
* | |||
* @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<HSLFShape, | |||
setEscherProperty((short)(EscherProperties.GEOMETRY__ADJUSTVALUE + idx), val); | |||
} | |||
@Override | |||
public CustomGeometry getGeometry() { | |||
return getGeometry(new Path2D.Double()); | |||
} | |||
CustomGeometry getGeometry(Path2D path2D) { | |||
final ObjectFactory of = new ObjectFactory(); | |||
final CTCustomGeometry2D cusGeo = of.createCTCustomGeometry2D(); | |||
cusGeo.setAvLst(of.createCTGeomGuideList()); | |||
cusGeo.setGdLst(of.createCTGeomGuideList()); | |||
cusGeo.setAhLst(of.createCTAdjustHandleList()); | |||
cusGeo.setCxnLst(of.createCTConnectionSiteList()); | |||
final 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 | |||
//sanity check | |||
if(verticesProp == null) { | |||
LOG.log(POILogger.WARN, "Freeform is missing GEOMETRY__VERTICES "); | |||
return super.getGeometry(); | |||
} | |||
if(segmentsProp == null) { | |||
LOG.log(POILogger.WARN, "Freeform is missing GEOMETRY__SEGMENTINFO "); | |||
return super.getGeometry(); | |||
} | |||
final Iterator<byte[]> vertIter = verticesProp.iterator(); | |||
final Iterator<byte[]> segIter = segmentsProp.iterator(); | |||
final int[] xyPoints = new int[2]; | |||
boolean isClosed = false; | |||
final CTPath2DList pathLst = of.createCTPath2DList(); | |||
final CTPath2D pathCT = of.createCTPath2D(); | |||
final List<Object> 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<CTAdjPoint2D> 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<byte[]> 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 extends EscherProperty> 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; | |||
} | |||
} |
@@ -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 <code>Fill</code> 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 <code>FILL_*</code> 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 <code>FILL_*</code> 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 { | |||
} | |||
/** | |||
* <code>PictureData</code> 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(){ |
@@ -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<HSLFShape,HSLFTextParagraph> { | |||
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<byte[]> segInfo = new ArrayList<>(); | |||
List<Point2D.Double> 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<byte[]> vertIter = verticesProp.iterator(); | |||
Iterator<byte[]> 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 extends EscherProperty> 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<byte[]> 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); | |||
// } | |||
} |
@@ -358,17 +358,18 @@ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> { | |||
_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); |
@@ -164,7 +164,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H | |||
return null; | |||
} | |||
Color clr = getColor(EscherProperties.LINESTYLE__COLOR, EscherProperties.LINESTYLE__OPACITY, -1); | |||
Color clr = getColor(EscherProperties.LINESTYLE__COLOR, EscherProperties.LINESTYLE__OPACITY); | |||
return clr == null ? null : clr; | |||
} | |||
@@ -179,7 +179,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H | |||
return null; | |||
} | |||
Color clr = getColor(EscherProperties.LINESTYLE__BACKCOLOR, EscherProperties.LINESTYLE__OPACITY, -1); | |||
Color clr = getColor(EscherProperties.LINESTYLE__BACKCOLOR, EscherProperties.LINESTYLE__OPACITY); | |||
return clr == null ? null : clr; | |||
} | |||
@@ -385,7 +385,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H | |||
* @return color of the line. If color is not set returns <code>java.awt.Color.black</code> | |||
*/ | |||
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; | |||
} | |||
@@ -25,7 +25,6 @@ import org.junit.runners.Suite; | |||
*/ | |||
@RunWith(Suite.class) | |||
@Suite.SuiteClasses({ | |||
TestBackground.class, | |||
TestFreeform.class, | |||
TestHeadersFooters.class, | |||
TestHyperlink.class, |
@@ -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()); | |||
} |
@@ -26,6 +26,7 @@ import org.junit.runners.Suite; | |||
@RunWith(Suite.class) | |||
@Suite.SuiteClasses({ | |||
TestAddingSlides.class, | |||
TestBackground.class, | |||
TestBugs.class, | |||
TestCounts.class, | |||
TestMostRecentRecords.class, |
@@ -15,7 +15,7 @@ | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hslf.model; | |||
package org.apache.poi.hslf.usermodel; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertTrue; |