aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java21
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java106
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfBounded.java45
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java227
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java150
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java4
-rw-r--r--src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java72
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java11
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java27
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java177
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java5
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java15
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java6
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java306
-rw-r--r--src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java12
15 files changed, 604 insertions, 580 deletions
diff --git a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java
index b12a4caa6c..f22f5a460f 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java
@@ -17,6 +17,7 @@
package org.apache.poi.hemf.draw;
+import java.awt.Shape;
import java.awt.geom.Path2D;
import org.apache.poi.hwmf.draw.HwmfDrawProperties;
@@ -25,6 +26,8 @@ public class HemfDrawProperties extends HwmfDrawProperties {
/** Path for path bracket operations */
protected Path2D path = null;
+ protected Shape clip = null;
+ protected boolean usePathBracket = false;
public HemfDrawProperties() {
@@ -33,6 +36,8 @@ public class HemfDrawProperties extends HwmfDrawProperties {
public HemfDrawProperties(HemfDrawProperties other) {
super(other);
path = (other.path != null) ? (Path2D)other.path.clone() : null;
+ // TODO: check how to clone
+ clip = other.clip;
}
/**
@@ -55,7 +60,19 @@ public class HemfDrawProperties extends HwmfDrawProperties {
* @return {@code true}, if the drawing should go to the path bracket,
* if {@code false} draw directly to the graphics context
*/
- public boolean usePathBracket() {
- return path != null;
+ public boolean getUsePathBracket() {
+ return usePathBracket;
+ }
+
+ public void setUsePathBracket(boolean usePathBracket) {
+ this.usePathBracket = usePathBracket;
+ }
+
+ public Shape getClip() {
+ return clip;
+ }
+
+ public void setClip(Shape shape) {
+ clip = shape;
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java
index 011cb56ff7..1d947f24ff 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java
@@ -22,15 +22,15 @@ import static org.apache.poi.hwmf.record.HwmfBrushStyle.BS_SOLID;
import java.awt.Color;
import java.awt.Graphics2D;
+import java.awt.Shape;
import java.awt.geom.AffineTransform;
+import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
-import java.util.ArrayDeque;
-import java.util.Deque;
import java.util.function.Consumer;
-import org.apache.poi.hemf.record.emf.HemfBounded;
+import org.apache.poi.hemf.record.emf.HemfFill;
import org.apache.poi.hemf.record.emf.HemfRecord;
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.hwmf.record.HwmfColorRef;
@@ -47,13 +47,13 @@ public class HemfGraphics extends HwmfGraphics {
private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK);
- private final Deque<AffineTransform> transforms = new ArrayDeque<>();
+ private final AffineTransform initTrans;
public HemfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) {
super(graphicsCtx,bbox);
- // add dummy entry for object index 0, as emf is 1-based
+ // add dummy entry for object ind ex 0, as emf is 1-based
objectIndexes.set(0);
- saveTransform();
+ initTrans = new AffineTransform(graphicsCtx.getTransform());
}
@Override
@@ -66,48 +66,34 @@ public class HemfGraphics extends HwmfGraphics {
@Override
public void saveProperties() {
- propStack.add(getProperties());
- prop = new HemfDrawProperties((HemfDrawProperties)prop);
+ final HemfDrawProperties oldProp = getProperties();
+ oldProp.setClip(graphicsCtx.getClip());
+ propStack.add(oldProp);
+ prop = new HemfDrawProperties(oldProp);
}
@Override
- public void updateWindowMapMode() {
- // ignore window settings
+ public void restoreProperties(int index) {
+ super.restoreProperties(index);
+ HemfDrawProperties newProp = getProperties();
+ graphicsCtx.setClip(newProp.getClip());
}
public void draw(HemfRecord r) {
- if (r instanceof HemfBounded) {
- saveTransform();
- final HemfBounded bounded = (HemfBounded)r;
- final Rectangle2D tgt = bounded.getRecordBounds();
- if (tgt != null && !tgt.isEmpty()) {
- final Rectangle2D src = bounded.getShapeBounds(this);
- if (src != null && !src.isEmpty()) {
-// graphicsCtx.translate(tgt.getCenterX() - src.getCenterX(), tgt.getCenterY() - src.getCenterY());
-// graphicsCtx.translate(src.getCenterX(), src.getCenterY());
-// graphicsCtx.scale(tgt.getWidth() / src.getWidth(), tgt.getHeight() / src.getHeight());
-// graphicsCtx.translate(-src.getCenterX(), -src.getCenterY());
- }
- }
- }
-
r.draw(this);
-
- if (r instanceof HemfBounded) {
- restoreTransform();
- }
}
@Internal
- public void draw(Consumer<Path2D> pathConsumer) {
+ public void draw(Consumer<Path2D> pathConsumer, FillDrawStyle fillDraw) {
final HemfDrawProperties prop = getProperties();
- final boolean useBracket = prop.usePathBracket();
+ final boolean useBracket = prop.getUsePathBracket();
final Path2D path;
if (useBracket) {
path = prop.getPath();
} else {
path = new Path2D.Double();
+ path.setWindingRule(prop.getWindingRule());
Point2D pnt = prop.getLocation();
path.moveTo(pnt.getX(),pnt.getY());
}
@@ -124,8 +110,18 @@ public class HemfGraphics extends HwmfGraphics {
prop.setLocation(path.getCurrentPoint());
if (!useBracket) {
- // TODO: when to use draw vs. fill?
- super.draw(path);
+ switch (fillDraw) {
+ case FILL:
+ super.fill(path);
+ break;
+ case DRAW:
+ super.draw(path);
+ break;
+ case FILL_DRAW:
+ super.fill(path);
+ super.draw(path);
+ break;
+ }
}
}
@@ -273,7 +269,7 @@ public class HemfGraphics extends HwmfGraphics {
* @return the initial AffineTransform, when this graphics context was created
*/
public AffineTransform getInitTransform() {
- return new AffineTransform(transforms.peekFirst());
+ return new AffineTransform(initTrans);
}
/**
@@ -291,13 +287,41 @@ public class HemfGraphics extends HwmfGraphics {
graphicsCtx.setTransform(tx);
}
- /** saves the current affine transform on the stack */
- private void saveTransform() {
- transforms.push(graphicsCtx.getTransform());
- }
+ public void setClip(Shape clip, HemfFill.HemfRegionMode regionMode) {
+ Shape oldClip = graphicsCtx.getClip();
- /** restore the last saved affine transform */
- private void restoreTransform() {
- graphicsCtx.setTransform(transforms.pop());
+ switch (regionMode) {
+ case RGN_AND:
+ graphicsCtx.clip(clip);
+ break;
+ case RGN_OR:
+ if (oldClip == null) {
+ graphicsCtx.setClip(clip);
+ } else {
+ Area area = new Area(oldClip);
+ area.add(new Area(clip));
+ graphicsCtx.setClip(area);
+ }
+ break;
+ case RGN_XOR:
+ if (oldClip == null) {
+ graphicsCtx.setClip(clip);
+ } else {
+ Area area = new Area(oldClip);
+ area.exclusiveOr(new Area(clip));
+ graphicsCtx.setClip(area);
+ }
+ break;
+ case RGN_DIFF:
+ if (oldClip != null) {
+ Area area = new Area(oldClip);
+ area.subtract(new Area(clip));
+ graphicsCtx.setClip(area);
+ }
+ break;
+ case RGN_COPY:
+ graphicsCtx.setClip(clip);
+ break;
+ }
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfBounded.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfBounded.java
deleted file mode 100644
index dee8f94ffa..0000000000
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfBounded.java
+++ /dev/null
@@ -1,45 +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.hemf.record.emf;
-
-import java.awt.geom.Rectangle2D;
-
-import org.apache.poi.hemf.draw.HemfGraphics;
-
-/**
- * In EMF, shape records bring their own bounding.
- * The record bounding is in the same space as the global drawing context,
- * but the specified shape points can have a different space and therefore
- * need to be translated/normalized
- */
-public interface HemfBounded {
- /**
- * Getter for the outer bounds which are given in the record
- *
- * @return the bounds specified in the record
- */
- Rectangle2D getRecordBounds();
-
- /**
- * Getter for the inner bounds which are calculated by the shape points
- *
- * @param ctx the graphics context
- * @return the bounds of the shape points
- */
- Rectangle2D getShapeBounds(HemfGraphics ctx);
-}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java
index 0eb6522e44..103f90a052 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java
@@ -17,8 +17,10 @@
package org.apache.poi.hemf.record.emf;
+import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
+
+import java.awt.Shape;
import java.awt.geom.Arc2D;
-import java.awt.geom.Area;
import java.awt.geom.Dimension2D;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
@@ -28,6 +30,7 @@ import java.io.IOException;
import org.apache.poi.hemf.draw.HemfDrawProperties;
import org.apache.poi.hemf.draw.HemfGraphics;
+import org.apache.poi.hwmf.draw.HwmfGraphics.FillDrawStyle;
import org.apache.poi.hwmf.record.HwmfDraw;
import org.apache.poi.hwmf.record.HwmfDraw.WmfSelectObject;
import org.apache.poi.util.LittleEndianConsts;
@@ -88,7 +91,7 @@ public class HemfDraw {
/** The EMR_POLYBEZIER record specifies one or more Bezier curves. */
- public static class EmfPolyBezier extends HwmfDraw.WmfPolygon implements HemfRecord, HemfBounded {
+ public static class EmfPolyBezier extends HwmfDraw.WmfPolygon implements HemfRecord {
private final Rectangle2D bounds = new Rectangle2D.Double();
@Override
@@ -120,7 +123,7 @@ public class HemfDraw {
final int points = Math.min(count, 16384);
size += LittleEndianConsts.INT_SIZE;
- poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, points);
+ poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, points+2);
/* Cubic Bezier curves are defined using the endpoints and control points
* specified by the points field. The first curve is drawn from the first
@@ -135,8 +138,8 @@ public class HemfDraw {
Point2D pnt[] = { new Point2D.Double(), new Point2D.Double(), new Point2D.Double() };
// points-1 because of the first point
- final int pointCnt = hasStartPoint() ? points-1 : points;
- for (int i=0; i+3<pointCnt; i+=3) {
+ final int pointCnt = hasStartPoint() ? points-2 : points;
+ for (int i=0; i+2<pointCnt; i+=3) {
// x (4 bytes): A 32-bit signed integer that defines the horizontal (x) coordinate of the point.
// y (4 bytes): A 32-bit signed integer that defines the vertical (y) coordinate of the point.
if (i==0) {
@@ -162,25 +165,23 @@ public class HemfDraw {
return size;
}
- @Override
- public Rectangle2D getRecordBounds() {
- return bounds;
- }
-
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- if (!hasStartPoint()) {
- throw new IllegalStateException("shape bounds not valid for path bracket based record: "+getClass().getName());
- }
- return poly.getBounds2D();
- }
-
/**
* @return true, if start point is in the list of points. false, if start point is taken from the context
*/
protected boolean hasStartPoint() {
return true;
}
+
+ @Override
+ protected FillDrawStyle getFillDrawStyle() {
+ // The cubic Bezier curves SHOULD be drawn using the current pen.
+ return FillDrawStyle.DRAW;
+ }
+
+ @Override
+ public void draw(HemfGraphics ctx) {
+ ctx.draw(path -> path.append(poly, !hasStartPoint()), getFillDrawStyle());
+ }
}
/**
@@ -203,7 +204,7 @@ public class HemfDraw {
* The EMR_POLYGON record specifies a polygon consisting of two or more vertexes connected by
* straight lines.
*/
- public static class EmfPolygon extends HwmfDraw.WmfPolygon implements HemfRecord, HemfBounded {
+ public static class EmfPolygon extends HwmfDraw.WmfPolygon implements HemfRecord {
private final Rectangle2D bounds = new Rectangle2D.Double();
@Override
@@ -246,25 +247,25 @@ public class HemfDraw {
return size;
}
- @Override
- public Rectangle2D getRecordBounds() {
- return bounds;
- }
-
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- if (!hasStartPoint()) {
- throw new IllegalStateException("shape bounds not valid for path bracket based record: "+getClass().getName());
- }
- return poly.getBounds2D();
- }
-
/**
* @return true, if start point is in the list of points. false, if start point is taken from the context
*/
protected boolean hasStartPoint() {
return true;
}
+
+ @Override
+ protected FillDrawStyle getFillDrawStyle() {
+ // The polygon SHOULD be outlined using the current pen and filled using the current brush and
+ // polygon fill mode. The polygon SHOULD be closed automatically by drawing a line from the last
+ // vertex to the first.
+ return FillDrawStyle.FILL_DRAW;
+ }
+
+ @Override
+ public void draw(HemfGraphics ctx) {
+ ctx.draw(path -> path.append(poly, false), getFillDrawStyle());
+ }
}
/**
@@ -295,8 +296,9 @@ public class HemfDraw {
}
@Override
- protected boolean isFill() {
- return false;
+ protected FillDrawStyle getFillDrawStyle() {
+ // The line segments SHOULD be drawn using the current pen.
+ return FillDrawStyle.DRAW;
}
}
@@ -333,14 +335,7 @@ public class HemfDraw {
@Override
public void draw(HemfGraphics ctx) {
- polyTo(ctx, poly);
- }
-
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- // should be called in a beginPath/endPath bracket, so the shape bounds
- // of this path segment are irrelevant
- return null;
+ polyTo(ctx, poly, getFillDrawStyle());
}
}
@@ -374,14 +369,7 @@ public class HemfDraw {
@Override
public void draw(HemfGraphics ctx) {
- polyTo(ctx, poly);
- }
-
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- // should be called in a beginPath/endPath bracket, so the shape bounds
- // of this path segment are irrelevant
- return null;
+ polyTo(ctx, poly, getFillDrawStyle());
}
}
@@ -406,7 +394,7 @@ public class HemfDraw {
/**
* The EMR_POLYPOLYGON record specifies a series of closed polygons.
*/
- public static class EmfPolyPolygon extends HwmfDraw.WmfPolyPolygon implements HemfRecord, HemfBounded {
+ public static class EmfPolyPolygon extends HwmfDraw.WmfPolyPolygon implements HemfRecord {
private final Rectangle2D bounds = new Rectangle2D.Double();
@Override
@@ -461,22 +449,15 @@ public class HemfDraw {
return size;
}
- /**
- * @return true, if a polyline should be closed, i.e. is a polygon
- */
- protected boolean isClosed() {
- return true;
- }
@Override
- public Rectangle2D getRecordBounds() {
- return bounds;
- }
+ public void draw(HemfGraphics ctx) {
+ Shape shape = getShape(ctx);
+ if (shape == null) {
+ return;
+ }
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- Area area = getShape(ctx);
- return area == null ? bounds : area.getBounds2D();
+ ctx.draw(path -> path.append(shape, false), getFillDrawStyle());
}
}
@@ -512,8 +493,8 @@ public class HemfDraw {
}
@Override
- protected boolean isFill() {
- return false;
+ protected FillDrawStyle getFillDrawStyle() {
+ return FillDrawStyle.DRAW;
}
}
@@ -563,12 +544,12 @@ public class HemfDraw {
@Override
public void draw(final HemfGraphics ctx) {
- ctx.draw((path) -> path.moveTo(point.getX(), point.getY()));
+ ctx.draw((path) -> path.moveTo(point.getX(), point.getY()), FillDrawStyle.NONE);
}
}
/**
- * The EMR_ARCTO record specifies an elliptical arc.
+ * The EMR_ARC record specifies an elliptical arc.
* It resets the current position to the end point of the arc.
*/
public static class EmfArc extends HwmfDraw.WmfArc implements HemfRecord {
@@ -587,8 +568,7 @@ public class HemfDraw {
@Override
public void draw(HemfGraphics ctx) {
- super.draw(ctx);
- ctx.getProperties().setLocation(endPoint);
+ ctx.draw(path -> path.append(getShape(), false), getFillDrawStyle());
}
}
@@ -610,6 +590,11 @@ public class HemfDraw {
size += readPointL(leis, endPoint);
return size;
}
+
+ @Override
+ public void draw(HemfGraphics ctx) {
+ ctx.draw(path -> path.append(getShape(), false), getFillDrawStyle());
+ }
}
/**
@@ -629,6 +614,11 @@ public class HemfDraw {
size += readPointL(leis, endPoint);
return size;
}
+
+ @Override
+ public void draw(HemfGraphics ctx) {
+ ctx.draw(path -> path.append(getShape(), false), getFillDrawStyle());
+ }
}
/**
@@ -646,6 +636,11 @@ public class HemfDraw {
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
return readRectL(leis, bounds);
}
+
+ @Override
+ public void draw(HemfGraphics ctx) {
+ ctx.draw(path -> path.append(getShape(), false), FillDrawStyle.FILL_DRAW);
+ }
}
/**
@@ -662,6 +657,11 @@ public class HemfDraw {
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
return readRectL(leis, bounds);
}
+
+ @Override
+ public void draw(HemfGraphics ctx) {
+ ctx.draw(path -> path.append(bounds, false), FillDrawStyle.FILL_DRAW);
+ }
}
/**
@@ -684,6 +684,11 @@ public class HemfDraw {
return size + 2*LittleEndianConsts.INT_SIZE;
}
+
+ @Override
+ public void draw(HemfGraphics ctx) {
+ ctx.draw(path -> path.append(getShape(), false), FillDrawStyle.FILL_DRAW);
+ }
}
/**
@@ -703,7 +708,7 @@ public class HemfDraw {
@Override
public void draw(final HemfGraphics ctx) {
- ctx.draw((path) -> path.lineTo(point.getX(), point.getY()));
+ ctx.draw((path) -> path.lineTo(point.getX(), point.getY()), FillDrawStyle.DRAW);
}
}
@@ -728,12 +733,12 @@ public class HemfDraw {
@Override
public void draw(final HemfGraphics ctx) {
final Arc2D arc = getShape();
- ctx.draw((path) -> path.append(arc, true));
+ ctx.draw((path) -> path.append(arc, true), getFillDrawStyle());
}
}
/** The EMR_POLYDRAW record specifies a set of line segments and Bezier curves. */
- public static class EmfPolyDraw extends HwmfDraw.WmfPolygon implements HemfRecord, HemfBounded {
+ public static class EmfPolyDraw extends HwmfDraw.WmfPolygon implements HemfRecord {
private final Rectangle2D bounds = new Rectangle2D.Double();
@Override
@@ -808,13 +813,14 @@ public class HemfDraw {
}
@Override
- public Rectangle2D getRecordBounds() {
- return bounds;
+ protected FillDrawStyle getFillDrawStyle() {
+ // Draws a set of line segments and Bezier curves.
+ return FillDrawStyle.DRAW;
}
@Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- return poly.getBounds2D();
+ public void draw(HemfGraphics ctx) {
+ ctx.draw(path -> path.append(poly, false), getFillDrawStyle());
}
}
@@ -854,6 +860,7 @@ public class HemfDraw {
public void draw(HemfGraphics ctx) {
final HemfDrawProperties prop = ctx.getProperties();
prop.setPath(new Path2D.Double());
+ prop.setUsePathBracket(true);
}
@Override
@@ -878,6 +885,12 @@ public class HemfDraw {
}
@Override
+ public void draw(HemfGraphics ctx) {
+ final HemfDrawProperties prop = ctx.getProperties();
+ prop.setUsePathBracket(false);
+ }
+
+ @Override
public String toString() {
return "{}";
}
@@ -901,6 +914,7 @@ public class HemfDraw {
public void draw(HemfGraphics ctx) {
final HemfDrawProperties prop = ctx.getProperties();
prop.setPath(null);
+ prop.setUsePathBracket(false);
}
@Override
@@ -995,7 +1009,7 @@ public class HemfDraw {
/**
* The EMR_STROKEPATH record renders the specified path by using the current pen.
*/
- public static class EmfStrokePath implements HemfRecord, HemfBounded {
+ public static class EmfStrokePath implements HemfRecord {
protected final Rectangle2D bounds = new Rectangle2D.Double();
@Override
@@ -1006,29 +1020,64 @@ public class HemfDraw {
@Override
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
// A 128-bit WMF RectL object, which specifies bounding rectangle, in device units
- return readRectL(leis, bounds);
+ return (recordSize == 0) ? 0 : readRectL(leis, bounds);
}
@Override
public void draw(HemfGraphics ctx) {
HemfDrawProperties props = ctx.getProperties();
- ctx.draw(props.getPath());
+ Path2D path = props.getPath();
+ path.setWindingRule(ctx.getProperties().getWindingRule());
+ ctx.draw(path);
}
@Override
- public Rectangle2D getRecordBounds() {
- return bounds;
+ public String toString() {
+ return boundsToString(bounds);
}
+ }
+
+ /**
+ * The EMR_FILLPATH record closes any open figures in the current path and fills the path's interior by
+ * using the current brush and polygon-filling mode.
+ */
+ public static class EmfFillPath extends EmfStrokePath {
@Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- HemfDrawProperties props = ctx.getProperties();
- return props.getPath().getBounds2D();
+ public HemfRecordType getEmfRecordType() {
+ return HemfRecordType.fillPath;
}
@Override
- public String toString() {
- return "{ bounds: { x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+" }";
+ public void draw(HemfGraphics ctx) {
+ final HemfDrawProperties prop = ctx.getProperties();
+ final Path2D path = (Path2D)prop.getPath().clone();
+ path.closePath();
+ path.setWindingRule(ctx.getProperties().getWindingRule());
+ ctx.fill(path);
+ }
+ }
+
+ /**
+ * The EMR_STROKEANDFILLPATH record closes any open figures in a path, strokes the outline of the
+ * path by using the current pen, and fills its interior by using the current brush.
+ */
+ public static class EmfStrokeAndFillPath extends EmfStrokePath {
+ protected final Rectangle2D bounds = new Rectangle2D.Double();
+
+ @Override
+ public HemfRecordType getEmfRecordType() {
+ return HemfRecordType.strokeAndFillPath;
+ }
+
+ @Override
+ public void draw(HemfGraphics ctx) {
+ HemfDrawProperties props = ctx.getProperties();
+ Path2D path = props.getPath();
+ path.closePath();
+ path.setWindingRule(ctx.getProperties().getWindingRule());
+ ctx.fill(path);
+ ctx.draw(path);
}
}
@@ -1080,7 +1129,7 @@ public class HemfDraw {
return 2*LittleEndianConsts.INT_SIZE;
}
- private static void polyTo(final HemfGraphics ctx, final Path2D poly) {
+ private static void polyTo(final HemfGraphics ctx, final Path2D poly, FillDrawStyle fillDrawStyle) {
if (poly.getCurrentPoint() == null) {
return;
}
@@ -1092,6 +1141,6 @@ public class HemfDraw {
return;
}
- ctx.draw((path) -> path.append(pi, true));
+ ctx.draw((path) -> path.append(pi, true), fillDrawStyle);
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java
index 90a91a0bd1..7b172d5506 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java
@@ -51,10 +51,29 @@ public class HemfFill {
private static final int MAX_RECORD_LENGTH = 10_000_000;
public enum HemfRegionMode {
+ /**
+ * The new clipping region includes the intersection (overlapping areas)
+ * of the current clipping region and the current path (or new region).
+ */
RGN_AND(0x01),
+ /**
+ * The new clipping region includes the union (combined areas)
+ * of the current clipping region and the current path (or new region).
+ */
RGN_OR(0x02),
+ /**
+ * The new clipping region includes the union of the current clipping region
+ * and the current path (or new region) but without the overlapping areas
+ */
RGN_XOR(0x03),
+ /**
+ * The new clipping region includes the areas of the current clipping region
+ * with those of the current path (or new region) excluded.
+ */
RGN_DIFF(0x04),
+ /**
+ * The new clipping region is the current path (or the new region).
+ */
RGN_COPY(0x05);
int flag;
@@ -114,7 +133,7 @@ public class HemfFill {
* optionally in combination with a brush pattern, according to a specified raster operation, stretching or
* compressing the output to fit the dimensions of the destination, if necessary.
*/
- public static class EmfStretchBlt extends HwmfFill.WmfBitBlt implements HemfRecord, HemfBounded {
+ public static class EmfStretchBlt extends HwmfFill.WmfBitBlt implements HemfRecord {
protected final Rectangle2D bounds = new Rectangle2D.Double();
/** An XForm object that specifies a world-space to page-space transform to apply to the source bitmap. */
@@ -196,16 +215,6 @@ public class HemfFill {
return size;
}
- @Override
- public Rectangle2D getRecordBounds() {
- return bounds;
- }
-
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- return dstBounds;
- }
-
protected boolean srcEqualsDstDimension() {
return false;
}
@@ -226,7 +235,7 @@ public class HemfFill {
* destination rectangle, optionally in combination with a brush pattern, according to a specified raster
* operation, stretching or compressing the output to fit the dimensions of the destination, if necessary.
*/
- public static class EmfStretchDiBits extends HwmfFill.WmfStretchDib implements HemfRecord, HemfBounded {
+ public static class EmfStretchDiBits extends HwmfFill.WmfStretchDib implements HemfRecord {
protected final Rectangle2D bounds = new Rectangle2D.Double();
@Override
@@ -286,16 +295,6 @@ public class HemfFill {
return size;
}
-
- @Override
- public Rectangle2D getRecordBounds() {
- return bounds;
- }
-
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- return dstBounds;
- }
}
/**
@@ -316,7 +315,7 @@ public class HemfFill {
/** The EMR_FRAMERGN record draws a border around the specified region using the specified brush. */
- public static class EmfFrameRgn extends HwmfDraw.WmfFrameRegion implements HemfRecord, HemfBounded {
+ public static class EmfFrameRgn extends HwmfDraw.WmfFrameRegion implements HemfRecord {
private final Rectangle2D bounds = new Rectangle2D.Double();
private final List<Rectangle2D> rgnRects = new ArrayList<>();
@@ -347,23 +346,13 @@ public class HemfFill {
ctx.fill(getShape());
}
- @Override
- public Rectangle2D getRecordBounds() {
- return bounds;
- }
-
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- return getShape().getBounds2D();
- }
-
protected Area getShape() {
return getRgnShape(rgnRects);
}
}
/** The EMR_INVERTRGN record inverts the colors in the specified region. */
- public static class EmfInvertRgn implements HemfRecord, HemfBounded {
+ public static class EmfInvertRgn implements HemfRecord {
protected final Rectangle2D bounds = new Rectangle2D.Double();
protected final List<Rectangle2D> rgnRects = new ArrayList<>();
@@ -382,16 +371,6 @@ public class HemfFill {
return size;
}
- @Override
- public Rectangle2D getRecordBounds() {
- return bounds;
- }
-
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- return getShape().getBounds2D();
- }
-
protected Area getShape() {
return getRgnShape(rgnRects);
}
@@ -409,7 +388,7 @@ public class HemfFill {
}
/** The EMR_FILLRGN record fills the specified region by using the specified brush. */
- public static class EmfFillRgn extends HwmfFill.WmfFillRegion implements HemfRecord, HemfBounded {
+ public static class EmfFillRgn extends HwmfFill.WmfFillRegion implements HemfRecord {
protected final Rectangle2D bounds = new Rectangle2D.Double();
protected final List<Rectangle2D> rgnRects = new ArrayList<>();
@@ -429,16 +408,6 @@ public class HemfFill {
return size;
}
- @Override
- public Rectangle2D getRecordBounds() {
- return bounds;
- }
-
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- return getShape().getBounds2D();
- }
-
protected Area getShape() {
return getRgnShape(rgnRects);
}
@@ -474,7 +443,7 @@ public class HemfFill {
}
}
- public static class EmfAlphaBlend implements HemfRecord, HemfBounded {
+ public static class EmfAlphaBlend implements HemfRecord {
/** the destination bounding rectangle in device units */
protected final Rectangle2D bounds = new Rectangle2D.Double();
/** the destination rectangle */
@@ -583,24 +552,13 @@ public class HemfFill {
return size;
}
-
-
- @Override
- public Rectangle2D getRecordBounds() {
- return bounds;
- }
-
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- return destRect;
- }
}
/**
* The EMR_SETDIBITSTODEVICE record specifies a block transfer of pixels from specified scanlines of
* a source bitmap to a destination rectangle.
*/
- public static class EmfSetDiBitsToDevice implements HemfRecord, HemfBounded {
+ public static class EmfSetDiBitsToDevice implements HemfRecord {
protected final Rectangle2D bounds = new Rectangle2D.Double();
protected final Point2D dest = new Point2D.Double();
protected final Rectangle2D src = new Rectangle2D.Double();
@@ -645,16 +603,6 @@ public class HemfFill {
return size;
}
-
- @Override
- public Rectangle2D getRecordBounds() {
- return bounds;
- }
-
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- return new Rectangle2D.Double(dest.getX(), dest.getY(), src.getWidth(), src.getHeight());
- }
}
static long readBitmap(final LittleEndianInputStream leis, final HwmfBitmapDib bitmap,
@@ -769,52 +717,6 @@ public class HemfFill {
return 6 * LittleEndian.INT_SIZE;
}
- /**
- * The EMR_FILLPATH record closes any open figures in the current path and fills the path's interior by
- * using the current brush and polygon-filling mode.
- */
- public static class EmfFillPath implements HemfRecord, HemfBounded {
- protected final Rectangle2D bounds = new Rectangle2D.Double();
-
- @Override
- public HemfRecordType getEmfRecordType() {
- return HemfRecordType.fillPath;
- }
-
- @Override
- public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
- // A 128-bit WMF RectL object, which specifies bounding rectangle, in device units
- return (recordSize == 0) ? 0 : readRectL(leis, bounds);
- }
-
- @Override
- public Rectangle2D getRecordBounds() {
- return bounds;
- }
-
- @Override
- public Rectangle2D getShapeBounds(HemfGraphics ctx) {
- final HemfDrawProperties prop = ctx.getProperties();
- final Path2D path = prop.getPath();
- return path.getBounds2D();
- }
-
- @Override
- public void draw(HemfGraphics ctx) {
- final HemfDrawProperties prop = ctx.getProperties();
- if (!prop.usePathBracket()) {
- return;
- }
- final Path2D path = (Path2D)prop.getPath().clone();
- path.setWindingRule(ctx.getProperties().getWindingRule());
- if (prop.getBrushStyle() == HwmfBrushStyle.BS_NULL) {
- ctx.draw(path);
- } else {
- ctx.fill(path);
- }
- }
- }
-
protected static Area getRgnShape(List<Rectangle2D> rgnRects) {
final Area frame = new Area();
rgnRects.forEach((rct) -> frame.add(new Area(rct)));
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java
index 5b19f9d128..25e7149251 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java
@@ -85,8 +85,8 @@ public enum HemfRecordType {
beginPath(0x0000003B, HemfDraw.EmfBeginPath::new),
endPath(0x0000003C, HemfDraw.EmfEndPath::new),
closeFigure(0x0000003D, HemfDraw.EmfCloseFigure::new),
- fillPath(0x0000003E, HemfFill.EmfFillPath::new),
- strokeandfillpath(0x0000003F, UnimplementedHemfRecord::new),
+ fillPath(0x0000003E, HemfDraw.EmfFillPath::new),
+ strokeAndFillPath(0x0000003F, HemfDraw.EmfStrokeAndFillPath::new),
strokePath(0x00000040, HemfDraw.EmfStrokePath::new),
flattenPath(0x00000041, HemfDraw.EmfFlattenPath::new),
widenPath(0x00000042, HemfDraw.EmfWidenPath::new),
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java
index 03c033b763..294bfdcb99 100644
--- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java
+++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java
@@ -17,8 +17,13 @@
package org.apache.poi.hemf.record.emf;
+import static org.apache.poi.hemf.record.emf.HemfDraw.readDimensionInt;
+import static org.apache.poi.hemf.record.emf.HemfDraw.readPointL;
+
import java.io.IOException;
+import org.apache.poi.hemf.draw.HemfDrawProperties;
+import org.apache.poi.hemf.draw.HemfGraphics;
import org.apache.poi.hemf.record.emf.HemfFill.HemfRegionMode;
import org.apache.poi.hwmf.record.HwmfWindowing;
import org.apache.poi.util.LittleEndianConsts;
@@ -37,13 +42,7 @@ public class HemfWindowing {
@Override
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
- // cx (4 bytes): A 32-bit unsigned integer that defines the x-coordinate of the point.
- int width = (int)leis.readUInt();
- // cy (4 bytes): A 32-bit unsigned integer that defines the y-coordinate of the point.
- int height = (int)leis.readUInt();
- size.setSize(width, height);
-
- return 2*LittleEndianConsts.INT_SIZE;
+ return readDimensionInt(leis, size);
}
}
@@ -58,12 +57,7 @@ public class HemfWindowing {
@Override
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
- // x (4 bytes): A 32-bit signed integer that defines the horizontal (x) coordinate of the point.
- x = leis.readInt();
- // y (4 bytes): A 32-bit signed integer that defines the vertical (y) coordinate of the point.
- y = leis.readInt();
-
- return 2*LittleEndianConsts.INT_SIZE;
+ return readPointL(leis, origin);
}
}
@@ -78,12 +72,7 @@ public class HemfWindowing {
@Override
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
- // cx (4 bytes): A 32-bit unsigned integer that defines the x-coordinate of the point.
- width = (int)leis.readUInt();
- // cy (4 bytes): A 32-bit unsigned integer that defines the y-coordinate of the point.
- height = (int)leis.readUInt();
-
- return 2*LittleEndianConsts.INT_SIZE;
+ return readDimensionInt(leis, extents);
}
}
@@ -98,12 +87,7 @@ public class HemfWindowing {
@Override
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
- // x (4 bytes): A 32-bit signed integer that defines the horizontal (x) coordinate of the point.
- x = leis.readInt();
- // y (4 bytes): A 32-bit signed integer that defines the vertical (y) coordinate of the point.
- y = leis.readInt();
-
- return 2*LittleEndianConsts.INT_SIZE;
+ return readPointL(leis, origin);
}
}
@@ -119,12 +103,7 @@ public class HemfWindowing {
@Override
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
- // x (4 bytes): A 32-bit signed integer that defines the horizontal (x) coordinate of the point.
- xOffset = leis.readInt();
- // y (4 bytes): A 32-bit signed integer that defines the vertical (y) coordinate of the point.
- yOffset = leis.readInt();
-
- return 2*LittleEndianConsts.INT_SIZE;
+ return readPointL(leis, offset);
}
}
@@ -172,10 +151,11 @@ public class HemfWindowing {
@Override
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
- xNum = leis.readInt();
- xDenom = leis.readInt();
- yNum = leis.readInt();
- yDenom = leis.readInt();
+ double xNum = leis.readInt();
+ double xDenom = leis.readInt();
+ double yNum = leis.readInt();
+ double yDenom = leis.readInt();
+ scale.setSize(xNum / xDenom, yNum / yDenom);
return 4*LittleEndianConsts.INT_SIZE;
}
}
@@ -192,10 +172,13 @@ public class HemfWindowing {
@Override
public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
- xNum = leis.readInt();
- xDenom = leis.readInt();
- yNum = leis.readInt();
- yDenom = leis.readInt();
+ double xNum = leis.readInt();
+ double xDenom = leis.readInt();
+ double yNum = leis.readInt();
+ double yDenom = leis.readInt();
+
+ scale.setSize(xNum / xDenom, yNum / yDenom);
+
return 4*LittleEndianConsts.INT_SIZE;
}
}
@@ -220,6 +203,17 @@ public class HemfWindowing {
return LittleEndianConsts.INT_SIZE;
}
+
+ @Override
+ public void draw(HemfGraphics ctx) {
+ HemfDrawProperties props = ctx.getProperties();
+ ctx.setClip(props.getPath(), regionMode);
+ }
+
+ @Override
+ public String toString() {
+ return "{ regionMode: '"+regionMode+"' }";
+ }
}
} \ No newline at end of file
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
index 7a325dc0a0..0038298946 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
@@ -57,6 +57,10 @@ import org.apache.poi.util.LocaleUtil;
public class HwmfGraphics {
+ public enum FillDrawStyle {
+ NONE, FILL, DRAW, FILL_DRAW
+ }
+
protected final List<HwmfDrawProperties> propStack = new LinkedList<>();
protected HwmfDrawProperties prop;
protected final Graphics2D graphicsCtx;
@@ -297,6 +301,7 @@ public class HwmfGraphics {
*/
public void updateWindowMapMode() {
Rectangle2D win = getProperties().getWindow();
+ Rectangle2D view = getProperties().getViewport();
HwmfMapMode mapMode = getProperties().getMapMode();
graphicsCtx.setTransform(initialAT);
@@ -304,8 +309,10 @@ public class HwmfGraphics {
default:
case MM_ANISOTROPIC:
// scale window bounds to output bounds
- graphicsCtx.scale(bbox.getWidth()/win.getWidth(), bbox.getHeight()/win.getHeight());
- graphicsCtx.translate(-win.getX(), -win.getY());
+ if (view != null) {
+ graphicsCtx.translate(view.getX() - win.getX(), view.getY() - win.getY());
+ graphicsCtx.scale(view.getWidth() / win.getWidth(), view.getHeight() / win.getHeight());
+ }
break;
case MM_ISOTROPIC:
// TODO: to be validated ...
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java
index a6f378512e..e6a8e302a3 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java
@@ -234,20 +234,33 @@ public class HwmfBitmapDib {
leis.reset();
- assert( headerSize != 0x0C || ((((headerWidth * headerPlanes * headerBitCount.flag + 31) & ~31) / 8) * Math.abs(headerHeight)) == headerImageSize);
+ // The size and format of this data is determined by information in the DIBHeaderInfo field. If
+ // it is a BitmapCoreHeader, the size in bytes MUST be calculated as follows:
- if (headerImageSize < headerSize) {
- imageData = IOUtils.safelyAllocate(recordSize, MAX_RECORD_LENGTH);
- leis.readFully(imageData);
- return recordSize;
- } else {
- int fileSize = (int)Math.min(introSize+headerImageSize,recordSize);
+ int bodySize = ((((headerWidth * headerPlanes * headerBitCount.flag + 31) & ~31) / 8) * Math.abs(headerHeight));
+
+ // This formula SHOULD also be used to calculate the size of aData when DIBHeaderInfo is a
+ // BitmapInfoHeader Object, using values from that object, but only if its Compression value is
+ // BI_RGB, BI_BITFIELDS, or BI_CMYK.
+ // Otherwise, the size of aData MUST be the BitmapInfoHeader Object value ImageSize.
+
+ assert( headerSize != 0x0C || bodySize == headerImageSize);
+
+ if (headerSize == 0x0C ||
+ headerCompression == Compression.BI_RGB ||
+ headerCompression == Compression.BI_BITFIELDS ||
+ headerCompression == Compression.BI_CMYK) {
+ int fileSize = (int)Math.min(introSize+bodySize,recordSize);
imageData = IOUtils.safelyAllocate(fileSize, MAX_RECORD_LENGTH);
leis.readFully(imageData, 0, introSize);
leis.skipFully(recordSize-fileSize);
// emfs are sometimes truncated, read as much as possible
int readBytes = leis.read(imageData, introSize, fileSize-introSize);
return introSize+(recordSize-fileSize)+readBytes;
+ } else {
+ imageData = IOUtils.safelyAllocate(recordSize, MAX_RECORD_LENGTH);
+ leis.readFully(imageData);
+ return recordSize;
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java
index b4e7ba73ed..107d440499 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java
@@ -32,6 +32,8 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.poi.hwmf.draw.HwmfGraphics;
+import org.apache.poi.hwmf.draw.HwmfGraphics.FillDrawStyle;
+import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
@@ -61,7 +63,7 @@ public class HwmfDraw {
@Override
public String toString() {
- return "{ x: "+point.getX()+", y: "+point.getY()+" }";
+ return pointToString(point);
}
}
@@ -93,7 +95,7 @@ public class HwmfDraw {
@Override
public String toString() {
- return "{ x: "+point.getX()+", y: "+point.getY()+" }";
+ return pointToString(point);
}
}
@@ -139,10 +141,17 @@ public class HwmfDraw {
Path2D p = (Path2D)poly.clone();
// don't close the path
p.setWindingRule(ctx.getProperties().getWindingRule());
- if (isFill()) {
- ctx.fill(p);
- } else {
- ctx.draw(p);
+ switch (getFillDrawStyle()) {
+ case FILL:
+ ctx.fill(p);
+ break;
+ case DRAW:
+ ctx.draw(p);
+ break;
+ case FILL_DRAW:
+ ctx.fill(p);
+ ctx.draw(p);
+ break;
}
}
@@ -154,8 +163,8 @@ public class HwmfDraw {
/**
* @return true, if the shape should be filled
*/
- protected boolean isFill() {
- return true;
+ protected FillDrawStyle getFillDrawStyle() {
+ return FillDrawStyle.FILL;
}
}
@@ -171,8 +180,8 @@ public class HwmfDraw {
}
@Override
- protected boolean isFill() {
- return false;
+ protected FillDrawStyle getFillDrawStyle() {
+ return FillDrawStyle.DRAW;
}
}
@@ -196,8 +205,16 @@ public class HwmfDraw {
@Override
public void draw(HwmfGraphics ctx) {
- Shape s = new Ellipse2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight());
- ctx.fill(s);
+ ctx.fill(getShape());
+ }
+
+ protected Ellipse2D getShape() {
+ return new Ellipse2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight());
+ }
+
+ @Override
+ public String toString() {
+ return boundsToString(bounds);
}
}
@@ -264,7 +281,7 @@ public class HwmfDraw {
*/
public static class WmfPolyPolygon implements HwmfRecord {
- protected List<Path2D> polyList = new ArrayList<>();
+ protected final List<Path2D> polyList = new ArrayList<>();
@Override
public HwmfRecordType getWmfRecordType() {
@@ -316,41 +333,82 @@ public class HwmfDraw {
@Override
public void draw(HwmfGraphics ctx) {
- Area area = getShape(ctx);
- if (area == null) {
+ Shape shape = getShape(ctx);
+ if (shape == null) {
return;
}
-
- if (isFill()) {
- ctx.fill(area);
- } else {
- ctx.draw(area);
- }
- }
- protected Area getShape(HwmfGraphics ctx) {
- int windingRule = ctx.getProperties().getWindingRule();
- Area area = null;
- for (Path2D poly : polyList) {
- Path2D p = (Path2D)poly.clone();
- p.setWindingRule(windingRule);
- Area newArea = new Area(p);
- if (area == null) {
- area = newArea;
- } else {
- area.exclusiveOr(newArea);
- }
+ switch (getFillDrawStyle()) {
+ case DRAW:
+ ctx.draw(shape);
+ break;
+ case FILL:
+ ctx.fill(shape);
+ break;
+ case FILL_DRAW:
+ ctx.fill(shape);
+ ctx.draw(shape);
+ break;
}
+ }
- return area;
+ protected FillDrawStyle getFillDrawStyle() {
+ // Each polygon SHOULD be outlined using the current pen, and filled using the current brush and
+ // polygon fill mode that are defined in the playback device context. The polygons defined by this
+ // record can overlap.
+ return FillDrawStyle.FILL_DRAW;
}
/**
- * @return true, if the shape should be filled
+ * @return true, if a polyline should be closed, i.e. is a polygon
*/
- protected boolean isFill() {
+ protected boolean isClosed() {
return true;
}
+
+ protected Shape getShape(HwmfGraphics ctx) {
+ int windingRule = ctx.getProperties().getWindingRule();
+
+ if (isClosed()) {
+ Area area = null;
+ for (Path2D poly : polyList) {
+ Path2D p = (Path2D)poly.clone();
+ p.setWindingRule(windingRule);
+ Area newArea = new Area(p);
+ if (area == null) {
+ area = newArea;
+ } else {
+ area.exclusiveOr(newArea);
+ }
+ }
+ return area;
+ } else {
+ Path2D path = new Path2D.Double();
+ path.setWindingRule(windingRule);
+ for (Path2D poly : polyList) {
+ path.append(poly, false);
+ }
+ return path;
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("{ polyList: [");
+ boolean isFirst = true;
+ for (Path2D p : polyList) {
+ if (!isFirst) {
+ sb.append(",");
+ }
+ isFirst = false;
+ sb.append("{ points: ");
+ sb.append(polyToString(p));
+ sb.append(" }");
+ }
+ sb.append(" }");
+ return sb.toString();
+ }
}
/**
@@ -377,12 +435,7 @@ public class HwmfDraw {
@Override
public String toString() {
- return
- "{ bounds: " +
- "{ x: "+bounds.getX()+
- ", y: "+bounds.getY()+
- ", w: "+bounds.getWidth()+
- ", h: "+bounds.getHeight()+" } }";
+ return boundsToString(bounds);
}
}
@@ -450,8 +503,11 @@ public class HwmfDraw {
@Override
public void draw(HwmfGraphics ctx) {
- Shape s = new RoundRectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), width, height);
- ctx.fill(s);
+ ctx.fill(getShape());
+ }
+
+ protected RoundRectangle2D getShape() {
+ return new RoundRectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), width, height);
}
}
@@ -487,15 +543,28 @@ public class HwmfDraw {
@Override
public void draw(HwmfGraphics ctx) {
Shape s = getShape();
+ switch (getFillDrawStyle()) {
+ case FILL:
+ ctx.fill(s);
+ break;
+ case DRAW:
+ ctx.draw(s);
+ break;
+ case FILL_DRAW:
+ ctx.fill(s);
+ ctx.draw(s);
+ break;
+ }
+ }
+
+ protected FillDrawStyle getFillDrawStyle() {
switch (getWmfRecordType()) {
default:
case arc:
- ctx.draw(s);
- break;
+ return FillDrawStyle.DRAW;
case chord:
case pie:
- ctx.fill(s);
- break;
+ return FillDrawStyle.FILL_DRAW;
}
}
@@ -665,4 +734,14 @@ public class HwmfDraw {
sb.append("]");
return sb.toString();
}
+
+ @Internal
+ public static String pointToString(Point2D point) {
+ return "{ x: "+point.getX()+", y: "+point.getY()+" }";
+ }
+
+ @Internal
+ public static String boundsToString(Rectangle2D bounds) {
+ return "{ x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+" }";
+ }
}
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java
index 4f15272e73..e335db4577 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java
@@ -17,6 +17,7 @@
package org.apache.poi.hwmf.record;
+import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
import static org.apache.poi.hwmf.record.HwmfDraw.readPointS;
import java.awt.Shape;
@@ -420,8 +421,8 @@ public class HwmfFill {
public String toString() {
return
"{ rasterOperation: '"+rasterOperation+"'"+
- ", srcBounds: { x: "+srcBounds.getX()+", y: "+srcBounds.getY()+", w: "+srcBounds.getWidth()+", h: "+srcBounds.getHeight()+" }"+
- ", dstBounds: { x: "+dstBounds.getX()+", y: "+dstBounds.getY()+", w: "+dstBounds.getWidth()+", h: "+dstBounds.getHeight()+" }"+
+ ", srcBounds: "+boundsToString(srcBounds)+
+ ", dstBounds: "+boundsToString(dstBounds)+
"}";
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java
index 7197674ffa..7303d2c6fe 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java
@@ -244,6 +244,11 @@ public class HwmfMisc {
ctx.getProperties().setMapMode(mapMode);
ctx.updateWindowMapMode();
}
+
+ @Override
+ public String toString() {
+ return "{ mapMode: '"+mapMode+"' }";
+ }
}
/**
@@ -275,6 +280,11 @@ public class HwmfMisc {
public void draw(HwmfGraphics ctx) {
}
+
+ @Override
+ public String toString() {
+ return "{ mapperValues: "+mapperValues+" }";
+ }
}
/**
@@ -379,6 +389,11 @@ public class HwmfMisc {
public void draw(HwmfGraphics ctx) {
}
+
+ @Override
+ public String toString() {
+ return "{ stretchBltMode: '"+stretchBltMode+"' }";
+ }
}
/**
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java
index a0b240d048..024e1dedf2 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java
@@ -17,6 +17,8 @@
package org.apache.poi.hwmf.record;
+import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
+import static org.apache.poi.hwmf.record.HwmfDraw.pointToString;
import static org.apache.poi.hwmf.record.HwmfDraw.readPointS;
import static org.apache.poi.hwmf.record.HwmfDraw.readRectS;
@@ -422,8 +424,8 @@ public class HwmfText {
}
return
- "{ reference: { x: "+reference.getX()+", y: "+reference.getY()+" }"+
- ", bounds: { x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+"}"+
+ "{ reference: " + pointToString(reference) +
+ ", bounds: " + boundsToString(bounds) +
", text: '"+text.replaceAll("\\p{Cntrl}",".")+"'"+
"}";
}
diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java
index 6e17b574fd..88915f8ad2 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java
@@ -17,11 +17,15 @@
package org.apache.poi.hwmf.record;
+import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
+import static org.apache.poi.hwmf.record.HwmfDraw.pointToString;
import static org.apache.poi.hwmf.record.HwmfDraw.readBounds;
+import static org.apache.poi.hwmf.record.HwmfDraw.readPointS;
import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.Dimension2D;
+import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
@@ -37,11 +41,7 @@ public class HwmfWindowing {
*/
public static class WmfSetViewportOrg implements HwmfRecord {
- /** A signed integer that defines the vertical offset, in device units. */
- protected int y;
-
- /** A signed integer that defines the horizontal offset, in device units. */
- protected int x;
+ protected final Point2D origin = new Point2D.Double();
@Override
public HwmfRecordType getWmfRecordType() {
@@ -50,14 +50,18 @@ public class HwmfWindowing {
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
- y = leis.readShort();
- x = leis.readShort();
- return 2*LittleEndianConsts.SHORT_SIZE;
+ return readPointS(leis, origin);
}
@Override
public void draw(HwmfGraphics ctx) {
- ctx.getProperties().setViewportOrg(x, y);
+ ctx.getProperties().setViewportOrg(origin.getX(), origin.getY());
+ ctx.updateWindowMapMode();
+ }
+
+ @Override
+ public String toString() {
+ return pointToString(origin);
}
}
@@ -67,11 +71,7 @@ public class HwmfWindowing {
*/
public static class WmfSetViewportExt implements HwmfRecord {
- /** A signed integer that defines the vertical extent of the viewport in device units. */
- protected int height;
-
- /** A signed integer that defines the horizontal extent of the viewport in device units. */
- protected int width;
+ protected final Dimension2D extents = new Dimension2DDouble();
@Override
public HwmfRecordType getWmfRecordType() {
@@ -80,14 +80,23 @@ public class HwmfWindowing {
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
- height = leis.readShort();
- width = leis.readShort();
+ // A signed integer that defines the vertical extent of the viewport in device units.
+ int height = leis.readShort();
+ // A signed integer that defines the horizontal extent of the viewport in device units.
+ int width = leis.readShort();
+ extents.setSize(width, height);
return 2*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
- ctx.getProperties().setViewportExt(width, height);
+ ctx.getProperties().setViewportExt(extents.getWidth(), extents.getHeight());
+ ctx.updateWindowMapMode();
+ }
+
+ @Override
+ public String toString() {
+ return "{ width: "+extents.getWidth()+", height: "+extents.getHeight()+" }";
}
}
@@ -97,15 +106,7 @@ public class HwmfWindowing {
*/
public static class WmfOffsetViewportOrg implements HwmfRecord {
- /**
- * A 16-bit signed integer that defines the vertical offset, in device units.
- */
- private int yOffset;
-
- /**
- * A 16-bit signed integer that defines the horizontal offset, in device units.
- */
- private int xOffset;
+ protected final Point2D offset = new Point2D.Double();
@Override
public HwmfRecordType getWmfRecordType() {
@@ -114,9 +115,7 @@ public class HwmfWindowing {
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
- yOffset = leis.readShort();
- xOffset = leis.readShort();
- return 2*LittleEndianConsts.SHORT_SIZE;
+ return readPointS(leis, offset);
}
@Override
@@ -124,7 +123,12 @@ public class HwmfWindowing {
Rectangle2D viewport = ctx.getProperties().getViewport();
double x = (viewport == null) ? 0 : viewport.getX();
double y = (viewport == null) ? 0 : viewport.getY();
- ctx.getProperties().setViewportOrg(x+xOffset, y+yOffset);
+ ctx.getProperties().setViewportOrg(x+offset.getX(), y+offset.getY());
+ }
+
+ @Override
+ public String toString() {
+ return pointToString(offset);
}
}
@@ -133,11 +137,7 @@ public class HwmfWindowing {
*/
public static class WmfSetWindowOrg implements HwmfRecord {
- /** A signed integer that defines the y-coordinate, in logical units. */
- protected int y;
-
- /** A signed integer that defines the x-coordinate, in logical units. */
- protected int x;
+ protected final Point2D origin = new Point2D.Double();
@Override
public HwmfRecordType getWmfRecordType() {
@@ -146,23 +146,26 @@ public class HwmfWindowing {
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
- y = leis.readShort();
- x = leis.readShort();
- return 2*LittleEndianConsts.SHORT_SIZE;
+ return readPointS(leis, origin);
}
@Override
public void draw(HwmfGraphics ctx) {
- ctx.getProperties().setWindowOrg(x, y);
+ ctx.getProperties().setWindowOrg(getX(), getY());
ctx.updateWindowMapMode();
}
- public int getY() {
- return y;
+ public double getY() {
+ return origin.getY();
}
- public int getX() {
- return x;
+ public double getX() {
+ return origin.getX();
+ }
+
+ @Override
+ public String toString() {
+ return pointToString(origin);
}
}
@@ -198,6 +201,11 @@ public class HwmfWindowing {
public Dimension2D getSize() {
return size;
}
+
+ @Override
+ public String toString() {
+ return "{ width: "+size.getWidth()+", height: "+size.getHeight()+" }";
+ }
}
/**
@@ -206,15 +214,7 @@ public class HwmfWindowing {
*/
public static class WmfOffsetWindowOrg implements HwmfRecord {
- /**
- * A 16-bit signed integer that defines the vertical offset, in device units.
- */
- private int yOffset;
-
- /**
- * A 16-bit signed integer that defines the horizontal offset, in device units.
- */
- private int xOffset;
+ protected final Point2D offset = new Point2D.Double();
@Override
public HwmfRecordType getWmfRecordType() {
@@ -223,17 +223,20 @@ public class HwmfWindowing {
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
- yOffset = leis.readShort();
- xOffset = leis.readShort();
- return 2*LittleEndianConsts.SHORT_SIZE;
+ return readPointS(leis, offset);
}
@Override
public void draw(HwmfGraphics ctx) {
Rectangle2D window = ctx.getProperties().getWindow();
- ctx.getProperties().setWindowOrg(window.getX()+xOffset, window.getY()+yOffset);
+ ctx.getProperties().setWindowOrg(window.getX()+offset.getX(), window.getY()+offset.getY());
ctx.updateWindowMapMode();
}
+
+ @Override
+ public String toString() {
+ return pointToString(offset);
+ }
}
/**
@@ -242,29 +245,7 @@ public class HwmfWindowing {
*/
public static class WmfScaleWindowExt implements HwmfRecord {
- /**
- * A signed integer that defines the amount by which to divide the
- * result of multiplying the current y-extent by the value of the yNum member.
- */
- protected int yDenom;
-
- /**
- * A signed integer that defines the amount by which to multiply the
- * current y-extent.
- */
- protected int yNum;
-
- /**
- * A signed integer that defines the amount by which to divide the
- * result of multiplying the current x-extent by the value of the xNum member.
- */
- protected int xDenom;
-
- /**
- * A signed integer that defines the amount by which to multiply the
- * current x-extent.
- */
- protected int xNum;
+ protected final Dimension2D scale = new Dimension2DDouble();
@Override
public HwmfRecordType getWmfRecordType() {
@@ -273,21 +254,37 @@ public class HwmfWindowing {
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
- yDenom = leis.readShort();
- yNum = leis.readShort();
- xDenom = leis.readShort();
- xNum = leis.readShort();
+ // A signed integer that defines the amount by which to divide the
+ // result of multiplying the current y-extent by the value of the yNum member.
+ double yDenom = leis.readShort();
+ // A signed integer that defines the amount by which to multiply the
+ // current y-extent.
+ double yNum = leis.readShort();
+ // A signed integer that defines the amount by which to divide the
+ // result of multiplying the current x-extent by the value of the xNum member.
+ double xDenom = leis.readShort();
+ // A signed integer that defines the amount by which to multiply the
+ // current x-extent.
+ double xNum = leis.readShort();
+
+ scale.setSize(xNum / xDenom, yNum / yDenom);
+
return 4*LittleEndianConsts.SHORT_SIZE;
}
@Override
public void draw(HwmfGraphics ctx) {
Rectangle2D window = ctx.getProperties().getWindow();
- double width = window.getWidth() * xNum / xDenom;
- double height = window.getHeight() * yNum / yDenom;
+ double width = window.getWidth() * scale.getWidth();
+ double height = window.getHeight() * scale.getHeight();
ctx.getProperties().setWindowExt(width, height);
ctx.updateWindowMapMode();
}
+
+ @Override
+ public String toString() {
+ return "{ scaleX: "+scale.getWidth()+", scaleY: "+scale.getHeight()+" }";
+ }
}
@@ -298,29 +295,7 @@ public class HwmfWindowing {
*/
public static class WmfScaleViewportExt implements HwmfRecord {
- /**
- * A signed integer that defines the amount by which to divide the
- * result of multiplying the current y-extent by the value of the yNum member.
- */
- protected int yDenom;
-
- /**
- * A signed integer that defines the amount by which to multiply the
- * current y-extent.
- */
- protected int yNum;
-
- /**
- * A signed integer that defines the amount by which to divide the
- * result of multiplying the current x-extent by the value of the xNum member.
- */
- protected int xDenom;
-
- /**
- * A signed integer that defines the amount by which to multiply the
- * current x-extent.
- */
- protected int xNum;
+ protected final Dimension2D scale = new Dimension2DDouble();
@Override
public HwmfRecordType getWmfRecordType() {
@@ -329,10 +304,21 @@ public class HwmfWindowing {
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
- yDenom = leis.readShort();
- yNum = leis.readShort();
- xDenom = leis.readShort();
- xNum = leis.readShort();
+ // A signed integer that defines the amount by which to divide the
+ // result of multiplying the current y-extent by the value of the yNum member.
+ double yDenom = leis.readShort();
+ // A signed integer that defines the amount by which to multiply the
+ // current y-extent.
+ double yNum = leis.readShort();
+ // A signed integer that defines the amount by which to divide the
+ // result of multiplying the current x-extent by the value of the xNum member.
+ double xDenom = leis.readShort();
+ // A signed integer that defines the amount by which to multiply the
+ // current x-extent.
+ double xNum = leis.readShort();
+
+ scale.setSize(xNum / xDenom, yNum / yDenom);
+
return 4*LittleEndianConsts.SHORT_SIZE;
}
@@ -342,10 +328,15 @@ public class HwmfWindowing {
if (viewport == null) {
viewport = ctx.getProperties().getWindow();
}
- double width = viewport.getWidth() * xNum / xDenom;
- double height = viewport.getHeight() * yNum / yDenom;
+ double width = viewport.getWidth() * scale.getWidth();
+ double height = viewport.getHeight() * scale.getHeight();
ctx.getProperties().setViewportExt(width, height);
}
+
+ @Override
+ public String toString() {
+ return "{ scaleX: "+scale.getWidth()+", scaleY: "+scale.getHeight()+" }";
+ }
}
/**
@@ -354,15 +345,7 @@ public class HwmfWindowing {
*/
public static class WmfOffsetClipRgn implements HwmfRecord, HwmfObjectTableEntry {
- /**
- * A signed integer that defines the number of logical units to move up or down.
- */
- protected int yOffset;
-
- /**
- * A signed integer that defines the number of logical units to move left or right.
- */
- protected int xOffset;
+ protected final Point2D offset = new Point2D.Double();
@Override
public HwmfRecordType getWmfRecordType() {
@@ -371,9 +354,7 @@ public class HwmfWindowing {
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
- yOffset = leis.readShort();
- xOffset = leis.readShort();
- return 2*LittleEndianConsts.SHORT_SIZE;
+ return readPointS(leis, offset);
}
@Override
@@ -384,6 +365,11 @@ public class HwmfWindowing {
@Override
public void applyObject(HwmfGraphics ctx) {
}
+
+ @Override
+ public String toString() {
+ return pointToString(offset);
+ }
}
/**
@@ -402,20 +388,7 @@ public class HwmfWindowing {
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
- // A 16-bit signed integer that defines the y-coordinate, in logical units, of the
- // lower-right corner of the rectangle.
- final int bottom = leis.readShort();
- // A 16-bit signed integer that defines the x-coordinate, in logical units, of the
- // lower-right corner of the rectangle.
- final int right = leis.readShort();
- // A 16-bit signed integer that defines the y-coordinate, in logical units, of the
- // upper-left corner of the rectangle.
- final int top = leis.readShort();
- // A 16-bit signed integer that defines the x-coordinate, in logical units, of the
- // upper-left corner of the rectangle.
- final int left = leis.readShort();
- bounds.setRect(left, top, right-left, bottom-top);
- return 4*LittleEndianConsts.SHORT_SIZE;
+ return readBounds(leis, bounds);
}
@Override
@@ -426,6 +399,11 @@ public class HwmfWindowing {
@Override
public void applyObject(HwmfGraphics ctx) {
}
+
+ @Override
+ public String toString() {
+ return boundsToString(bounds);
+ }
}
@@ -459,12 +437,7 @@ public class HwmfWindowing {
@Override
public String toString() {
- return
- "{ x: "+bounds.getX()+
- ", y: "+bounds.getY()+
- ", w: "+bounds.getWidth()+
- ", h: "+bounds.getHeight()+
- "}";
+ return boundsToString(bounds);
}
}
@@ -572,29 +545,7 @@ public class HwmfWindowing {
*/
private int maxScan;
- /**
- * A 16-bit signed integer that defines the y-coordinate, in logical units, of the
- * lower-right corner of the rectangle.
- */
- private int bottom;
-
- /**
- * A 16-bit signed integer that defines the x-coordinate, in logical units, of the
- * lower-right corner of the rectangle.
- */
- private int right;
-
- /**
- * A 16-bit signed integer that defines the y-coordinate, in logical units, of the
- * upper-left corner of the rectangle.
- */
- private int top;
-
- /**
- * A 16-bit signed integer that defines the x-coordinate, in logical units, of the
- * upper-left corner of the rectangle.
- */
- private int left;
+ private Rectangle2D bounds = new Rectangle2D.Double();
/**
* An array of Scan objects that define the scanlines in the region.
@@ -614,10 +565,19 @@ public class HwmfWindowing {
regionSize = leis.readShort();
scanCount = leis.readShort();
maxScan = leis.readShort();
- left = leis.readShort();
- top = leis.readShort();
- right = leis.readShort();
- bottom = leis.readShort();
+ // A 16-bit signed integer that defines the x-coordinate, in logical units, of the
+ // upper-left corner of the rectangle.
+ double left = leis.readShort();
+ // A 16-bit signed integer that defines the y-coordinate, in logical units, of the
+ // upper-left corner of the rectangle.
+ double top = leis.readShort();
+ // A 16-bit signed integer that defines the x-coordinate, in logical units, of the
+ // lower-right corner of the rectangle.
+ double right = leis.readShort();
+ // A 16-bit signed integer that defines the y-coordinate, in logical units, of the
+ // lower-right corner of the rectangle.
+ double bottom = leis.readShort();
+ bounds.setRect(left, top, right-left, bottom-top);
int size = 9*LittleEndianConsts.SHORT_SIZE+LittleEndianConsts.INT_SIZE;
diff --git a/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java b/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java
index cabe627f66..8ff4742be6 100644
--- a/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java
+++ b/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java
@@ -63,7 +63,13 @@ public class HemfPictureTest {
public void paint() throws IOException {
byte buf[] = new byte[50_000_000];
- final boolean writeLog = false;
+ // good test samples to validate rendering:
+ // emfs/commoncrawl2/NB/NBWN2YH5VFCLZRFDQU7PB7IDD4UKY7DN_2.emf
+ // emfs/govdocs1/777/777525.ppt_0.emf
+ // emfs/govdocs1/844/844795.ppt_2.emf
+ // emfs/commoncrawl2/TO/TOYZSTNUSW5OFCFUQ6T5FBLIDLCRF3NH_0.emf
+
+ final boolean writeLog = true;
final boolean dumpRecords = false;
final boolean savePng = true;
@@ -257,7 +263,7 @@ public class HemfPictureTest {
long fudgeFactorX = 1000;
StringBuilder sb = new StringBuilder();
for (HemfRecord record : pic) {
- if (record.getEmfRecordType().equals(HemfRecordType.exttextoutw)) {
+ if (record.getEmfRecordType().equals(HemfRecordType.extTextOutW)) {
HemfText.EmfExtTextOutW extTextOutW = (HemfText.EmfExtTextOutW) record;
Point2D reference = extTextOutW.getReference();
if (lastY > -1 && lastY != reference.getY()) {
@@ -291,7 +297,7 @@ public class HemfPictureTest {
expectedParts.add("testPDF.pdf");
int foundExpected = 0;
for (HemfRecord record : pic) {
- if (record.getEmfRecordType().equals(HemfRecordType.exttextoutw)) {
+ if (record.getEmfRecordType().equals(HemfRecordType.extTextOutW)) {
HemfText.EmfExtTextOutW extTextOutW = (HemfText.EmfExtTextOutW) record;
Point2D reference = extTextOutW.getReference();
if (lastY > -1 && lastY != reference.getY()) {