git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hemf@1844931 13f79535-47bb-0310-9956-ffa450edef68tags/REL_4_1_0
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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 | |||
@@ -877,6 +884,12 @@ public class HemfDraw { | |||
return 0; | |||
} | |||
@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); | |||
} | |||
} |
@@ -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))); |
@@ -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), |
@@ -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+"' }"; | |||
} | |||
} | |||
} |
@@ -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 ... |
@@ -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; | |||
} | |||
} | |||
@@ -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()+" }"; | |||
} | |||
} |
@@ -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)+ | |||
"}"; | |||
} | |||
} |
@@ -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+"' }"; | |||
} | |||
} | |||
/** |
@@ -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}",".")+"'"+ | |||
"}"; | |||
} |
@@ -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; | |||
@@ -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()) { |