From: Andreas Beeker Date: Sun, 3 Jan 2016 22:46:57 +0000 (+0000) Subject: WMF fixes X-Git-Tag: REL_3_14_FINAL~97 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=6d6cea92e26eabda5e529a1a00e64ab448120d39;p=poi.git WMF fixes git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1722771 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java index 64ee4cc346..aa8a2f3cbd 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java @@ -18,9 +18,13 @@ package org.apache.poi.hwmf.draw; import java.awt.Color; +import java.awt.Shape; +import java.awt.geom.Area; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.WritableRaster; import org.apache.poi.hwmf.record.HwmfBrushStyle; import org.apache.poi.hwmf.record.HwmfColorRef; @@ -31,9 +35,9 @@ import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode; import org.apache.poi.hwmf.record.HwmfPenStyle; public class HwmfDrawProperties { - private Rectangle2D window = new Rectangle2D.Double(0, 0, 1, 1); - private Rectangle2D viewport = new Rectangle2D.Double(0, 0, 1, 1); - private Point2D location = new Point2D.Double(0,0); + private final Rectangle2D window; + private Rectangle2D viewport = null; + private final Point2D location; private HwmfMapMode mapMode = HwmfMapMode.MM_ANISOTROPIC; private HwmfColorRef backgroundColor = new HwmfColorRef(Color.BLACK); private HwmfBrushStyle brushStyle = HwmfBrushStyle.BS_SOLID; @@ -46,7 +50,42 @@ public class HwmfDrawProperties { private double penMiterLimit = 10; private HwmfBkMode bkMode = HwmfBkMode.OPAQUE; private HwmfPolyfillMode polyfillMode = HwmfPolyfillMode.WINDING; - + private Shape region = null; + + public HwmfDrawProperties() { + window = new Rectangle2D.Double(0, 0, 1, 1); + viewport = null; + location = new Point2D.Double(0,0); + } + + public HwmfDrawProperties(HwmfDrawProperties other) { + this.window = (Rectangle2D)other.window.clone(); + this.viewport = (other.viewport == null) ? null : (Rectangle2D)other.viewport.clone(); + this.location = (Point2D)other.location.clone(); + this.mapMode = other.mapMode; + this.backgroundColor = (other.backgroundColor == null) ? null : other.backgroundColor.clone(); + this.brushStyle = other.brushStyle; + this.brushColor = other.brushColor.clone(); + this.brushHatch = other.brushHatch; + if (other.brushBitmap != null) { + ColorModel cm = other.brushBitmap.getColorModel(); + boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); + WritableRaster raster = other.brushBitmap.copyData(null); + this.brushBitmap = new BufferedImage(cm, raster, isAlphaPremultiplied, null); + } + this.penWidth = 1; + this.penStyle = (other.penStyle == null) ? null : other.penStyle.clone(); + this.penColor = (other.penColor == null) ? null : other.penColor.clone(); + this.penMiterLimit = other.penMiterLimit; + this.bkMode = other.bkMode; + this.polyfillMode = other.polyfillMode; + if (other.region instanceof Rectangle2D) { + this.region = ((Rectangle2D)other.region).getBounds2D(); + } else if (other.region instanceof Area) { + this.region = new Area(other.region); + } + } + public void setViewportExt(double width, double height) { double x = viewport.getX(); double y = viewport.getY(); @@ -62,7 +101,7 @@ public class HwmfDrawProperties { } public Rectangle2D getViewport() { - return (Rectangle2D)viewport.clone(); + return (viewport == null) ? null : (Rectangle2D)viewport.clone(); } public void setWindowExt(double width, double height) { @@ -186,4 +225,23 @@ public class HwmfDrawProperties { public void setBrushBitmap(BufferedImage brushBitmap) { this.brushBitmap = brushBitmap; } + + + /** + * Gets the last stored region + * + * @return the last stored region + */ + public Shape getRegion() { + return region; + } + + /** + * Sets a region for further usage + * + * @param region a region object which is usually a rectangle + */ + public void setRegion(Shape region) { + this.region = region; + } } 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 4a07cc3304..2ef64a8ed9 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java @@ -28,25 +28,37 @@ import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; -import java.util.ArrayDeque; -import java.util.Deque; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; import org.apache.poi.hwmf.record.HwmfBrushStyle; import org.apache.poi.hwmf.record.HwmfHatchStyle; import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode; +import org.apache.poi.hwmf.record.HwmfObjectTableEntry; import org.apache.poi.hwmf.record.HwmfPenStyle; import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash; import org.apache.poi.util.Units; public class HwmfGraphics { private final Graphics2D graphicsCtx; - private final Deque propStack = new ArrayDeque(); - HwmfDrawProperties prop; + private final List propStack = new LinkedList(); + private HwmfDrawProperties prop = new HwmfDrawProperties(); + private List objectTable = new ArrayList(); + /** Bounding box from the placeable header */ + private final Rectangle2D bbox; - public HwmfGraphics(Graphics2D graphicsCtx) { + /** + * Initialize a graphics context for wmf rendering + * + * @param graphicsCtx the graphics context to delegate drawing calls + * @param bbox the bounding box of the wmf (taken from the placeable header) + */ + public HwmfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) { this.graphicsCtx = graphicsCtx; - prop = new HwmfDrawProperties(); - propStack.push(prop); + this.bbox = (Rectangle2D)bbox.clone(); } public HwmfDrawProperties getProperties() { @@ -91,7 +103,11 @@ public class HwmfGraphics { protected Shape fitShapeToView(Shape shape) { int scaleUnits = prop.getMapMode().scale; - Rectangle2D view = prop.getViewport(), win = prop.getWindow(); + Rectangle2D view = prop.getViewport(); + Rectangle2D win = prop.getWindow(); + if (view == null) { + view = win; + } double scaleX, scaleY; switch (scaleUnits) { case -1: @@ -106,16 +122,20 @@ public class HwmfGraphics { } AffineTransform at = new AffineTransform(); - at.translate(view.getX(), view.getY()); at.scale(scaleX, scaleY); - at.translate(-win.getX(), -win.getY()); - at.translate(-view.getX(), -view.getY()); +// at.translate(-view.getX(), -view.getY()); + at.translate(bbox.getWidth()/win.getWidth(), bbox.getHeight()/win.getHeight()); - return at.createTransformedShape(shape); + Shape tshape = at.createTransformedShape(shape); + return tshape; } protected BasicStroke getStroke() { - Rectangle2D view = prop.getViewport(), win = prop.getWindow(); + Rectangle2D view = prop.getViewport(); + Rectangle2D win = prop.getWindow(); + if (view == null) { + view = win; + } float width = (float)(prop.getPenWidth() * view.getWidth() / win.getWidth()); HwmfPenStyle ps = prop.getPenStyle(); int cap = ps.getLineCap().awtFlag; @@ -182,4 +202,87 @@ public class HwmfGraphics { return (bi == null) ? null : new TexturePaint(bi, new Rectangle(0,0,bi.getWidth(),bi.getHeight())); } + + /** + * Adds an record of type {@link HwmfObjectTableEntry} to the object table. + * + * Every object is assigned the lowest available index-that is, the smallest + * numerical value-in the WMF Object Table. This binding happens at object creation, + * not when the object is used. + * Moreover, each object table index uniquely refers to an object. + * Indexes in the WMF Object Table always start at 0. + * + * @param entry + */ + public void addObjectTableEntry(HwmfObjectTableEntry entry) { + ListIterator oIter = objectTable.listIterator(); + while (oIter.hasNext()) { + HwmfObjectTableEntry tableEntry = oIter.next(); + if (tableEntry == null) { + oIter.set(entry); + return; + } + } + objectTable.add(entry); + } + + /** + * Applies the object table entry + * + * @param index the index of the object table entry (0-based) + * + * @throws IndexOutOfBoundsException if the index is out of range + * @throws NoSuchElementException if the entry was deleted before + */ + public void applyObjectTableEntry(int index) { + HwmfObjectTableEntry ote = objectTable.get(index); + if (ote == null) { + throw new NoSuchElementException("WMF reference exception - object table entry on index "+index+" was deleted before."); + } + ote.applyObject(this); + } + + /** + * Unsets (deletes) the object table entry for further usage + * + * When a META_DELETEOBJECT record (section 2.3.4.7) is received that specifies this + * object's particular index, the object's resources are released, the binding to its + * WMF Object Table index is ended, and the index value is returned to the pool of + * available indexes. The index will be reused, if needed, by a subsequent object + * created by another Object Record Type record. + * + * @param index the index (0-based) + * + * @throws IndexOutOfBoundsException if the index is out of range + */ + public void unsetObjectTableEntry(int index) { + objectTable.set(index, null); + } + + /** + * Saves the current properties to the stack + */ + public void saveProperties() { + propStack.add(prop); + prop = new HwmfDrawProperties(prop); + } + + /** + * Restores the properties from the stack + * + * @param index if the index is positive, the n-th element from the start is removed and activated. + * If the index is negative, the n-th previous element relative to the current properties element is removed and activated. + */ + public void restoreProperties(int index) { + if (index == 0) { + return; + } + int stackIndex = index; + if (stackIndex < 0) { + int curIdx = propStack.indexOf(prop); + assert (curIdx != -1); + stackIndex = curIdx + index; + } + prop = propStack.remove(stackIndex); + } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java index ff11d48629..541b98219b 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java @@ -30,7 +30,7 @@ import org.apache.poi.util.LittleEndianInputStream; * Blue (1 byte): An 8-bit unsigned integer that defines the relative intensity of blue. * Reserved (1 byte): An 8-bit unsigned integer that MUST be 0x00. */ -public class HwmfColorRef { +public class HwmfColorRef implements Cloneable { private Color colorRef = Color.BLACK; public HwmfColorRef() {} @@ -53,4 +53,21 @@ public class HwmfColorRef { public Color getColor() { return colorRef; } + + /** + * Creates a new object of the same class and with the + * same contents as this object. + * @return a clone of this instance. + * @exception OutOfMemoryError if there is not enough memory. + * @see java.lang.Cloneable + */ + @Override + public HwmfColorRef clone() { + try { + return (HwmfColorRef)super.clone(); + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(); + } + } } 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 a65b7b45bf..06f9b03af0 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java @@ -18,9 +18,15 @@ package org.apache.poi.hwmf.record; import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.Arc2D; +import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; import java.io.IOException; import org.apache.poi.hwmf.draw.HwmfGraphics; @@ -55,7 +61,7 @@ public class HwmfDraw { x = leis.readShort(); return 2*LittleEndianConsts.SHORT_SIZE; } - + @Override public void draw(HwmfGraphics ctx) { ctx.getProperties().setLocation(x, y); @@ -91,7 +97,7 @@ public class HwmfDraw { x = leis.readShort(); return 2*LittleEndianConsts.SHORT_SIZE; } - + @Override public void draw(HwmfGraphics ctx) { Point2D start = ctx.getProperties().getLocation(); @@ -135,12 +141,12 @@ public class HwmfDraw { return LittleEndianConsts.SHORT_SIZE+numberofPoints*LittleEndianConsts.INT_SIZE; } - + @Override public void draw(HwmfGraphics ctx) { ctx.fill(getShape()); } - + protected Polygon getShape() { Polygon polygon = new Polygon(); for(int i = 0; i < numberofPoints; i++) { @@ -207,10 +213,15 @@ public class HwmfDraw { leftRect = leis.readShort(); return 4*LittleEndianConsts.SHORT_SIZE; } - + @Override public void draw(HwmfGraphics ctx) { - + int x = Math.min(leftRect, rightRect); + int y = Math.min(topRect, bottomRect); + int w = Math.abs(leftRect - rightRect - 1); + int h = Math.abs(topRect - bottomRect - 1); + Shape s = new Ellipse2D.Double(x, y, w, h); + ctx.fill(s); } } @@ -253,10 +264,10 @@ public class HwmfDraw { width = leis.readShort(); return 4*LittleEndianConsts.SHORT_SIZE; } - + @Override public void draw(HwmfGraphics ctx) { - + } } @@ -372,7 +383,12 @@ public class HwmfDraw { @Override public void draw(HwmfGraphics ctx) { - + int x = Math.min(leftRect, rightRect); + int y = Math.min(topRect, bottomRect); + int w = Math.abs(leftRect - rightRect - 1); + int h = Math.abs(topRect - bottomRect - 1); + Shape s = new Rectangle2D.Double(x, y, w, h); + ctx.fill(s); } } @@ -415,7 +431,8 @@ public class HwmfDraw { @Override public void draw(HwmfGraphics ctx) { - + Shape s = new Rectangle2D.Double(x, y, 1, 1); + ctx.fill(s); } } @@ -479,84 +496,16 @@ public class HwmfDraw { @Override public void draw(HwmfGraphics ctx) { - + int x = Math.min(leftRect, rightRect); + int y = Math.min(topRect, bottomRect); + int w = Math.abs(leftRect - rightRect - 1); + int h = Math.abs(topRect - bottomRect - 1); + Shape s = new RoundRectangle2D.Double(x, y, w, h, width, height); + ctx.fill(s); } } - - /** - * The META_PIE record draws a pie-shaped wedge bounded by the intersection of an ellipse and two - * radials. The pie is outlined by using the pen and filled by using the brush that are defined in the - * playback device context. - */ - public static class WmfPie implements HwmfRecord { - /** - * A 16-bit signed integer that defines the y-coordinate, in logical - * coordinates, of the endpoint of the second radial. - */ - private int yRadial2; - /** - * A 16-bit signed integer that defines the x-coordinate, in logical - * coordinates, of the endpoint of the second radial. - */ - private int xRadial2; - /** - * A 16-bit signed integer that defines the y-coordinate, in logical - * coordinates, of the endpoint of the first radial. - */ - private int yRadial1; - /** - * A 16-bit signed integer that defines the x-coordinate, in logical - * coordinates, of the endpoint of the first radial. - */ - private int xRadial1; - /** - * A 16-bit signed integer that defines the y-coordinate, in logical units, of - * the lower-right corner of the bounding rectangle. - */ - private int bottomRect; - /** - * A 16-bit signed integer that defines the x-coordinate, in logical units, of - * the lower-right corner of the bounding rectangle. - */ - private int rightRect; - /** - * A 16-bit signed integer that defines the y-coordinate, in logical units, of the - * upper-left corner of the bounding rectangle. - */ - private int topRect; - /** - * A 16-bit signed integer that defines the x-coordinate, in logical units, of - * the upper-left corner of the bounding rectangle. - */ - private int leftRect; - - @Override - public HwmfRecordType getRecordType() { - return HwmfRecordType.pie; - } - - @Override - public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { - yRadial2 = leis.readShort(); - xRadial2 = leis.readShort(); - yRadial1 = leis.readShort(); - xRadial1 = leis.readShort(); - bottomRect = leis.readShort(); - rightRect = leis.readShort(); - topRect = leis.readShort(); - leftRect = leis.readShort(); - return 8*LittleEndianConsts.SHORT_SIZE; - } - - - @Override - public void draw(HwmfGraphics ctx) { - - } - } - /** * The META_ARC record draws an elliptical arc. */ @@ -622,81 +571,69 @@ public class HwmfDraw { @Override public void draw(HwmfGraphics ctx) { + int x = Math.min(leftRect, rightRect); + int y = Math.min(topRect, bottomRect); + int w = Math.abs(leftRect - rightRect - 1); + int h = Math.abs(topRect - bottomRect - 1); + double startAngle = Math.toDegrees(Math.atan2(-(yStartArc - (topRect + h / 2.)), xStartArc - (leftRect + w / 2.))); + double endAngle = Math.toDegrees(Math.atan2(-(yEndArc - (topRect + h / 2.)), xEndArc - (leftRect + w / 2.))); + double arcAngle = (endAngle - startAngle) + (endAngle - startAngle > 0 ? 0 : 360); + if (startAngle < 0) { + startAngle += 360; + } + boolean fillShape; + int arcClosure; + switch (getRecordType()) { + default: + case arc: + arcClosure = Arc2D.OPEN; + fillShape = false; + break; + case chord: + arcClosure = Arc2D.CHORD; + fillShape = true; + break; + case pie: + arcClosure = Arc2D.PIE; + fillShape = true; + break; + } + + Shape s = new Arc2D.Double(x, y, w, h, startAngle, arcAngle, arcClosure); + if (fillShape) { + ctx.fill(s); + } else { + ctx.draw(s); + } } } /** - * The META_CHORD record draws a chord, which is defined by a region bounded by the intersection of - * an ellipse with a line segment. The chord is outlined using the pen and filled using the brush - * that are defined in the playback device context. + * The META_PIE record draws a pie-shaped wedge bounded by the intersection of an ellipse and two + * radials. The pie is outlined by using the pen and filled by using the brush that are defined in the + * playback device context. */ - public static class WmfChord implements HwmfRecord { - /** - * A 16-bit signed integer that defines the y-coordinate, in logical - * coordinates, of the endpoint of the second radial. - */ - private int yRadial2; - /** - * A 16-bit signed integer that defines the x-coordinate, in logical - * coordinates, of the endpoint of the second radial. - */ - private int xRadial2; - /** - * A 16-bit signed integer that defines the y-coordinate, in logical - * coordinates, of the endpoint of the first radial. - */ - private int yRadial1; - /** - * A 16-bit signed integer that defines the x-coordinate, in logical - * coordinates, of the endpoint of the first radial. - */ - private int xRadial1; - /** - * A 16-bit signed integer that defines the y-coordinate, in logical units, of - * the lower-right corner of the bounding rectangle. - */ - private int bottomRect; - /** - * A 16-bit signed integer that defines the x-coordinate, in logical units, of - * the lower-right corner of the bounding rectangle. - */ - private int rightRect; - /** - * A 16-bit signed integer that defines the y-coordinate, in logical units, of the - * upper-left corner of the bounding rectangle. - */ - private int topRect; - /** - * A 16-bit signed integer that defines the x-coordinate, in logical units, of - * the upper-left corner of the bounding rectangle. - */ - private int leftRect; - + public static class WmfPie extends WmfArc { @Override public HwmfRecordType getRecordType() { - return HwmfRecordType.chord; + return HwmfRecordType.pie; } + } - @Override - public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { - yRadial2 = leis.readShort(); - xRadial2 = leis.readShort(); - yRadial1 = leis.readShort(); - xRadial1 = leis.readShort(); - bottomRect = leis.readShort(); - rightRect = leis.readShort(); - topRect = leis.readShort(); - leftRect = leis.readShort(); - return 8*LittleEndianConsts.SHORT_SIZE; - } + /** + * The META_CHORD record draws a chord, which is defined by a region bounded by the intersection of + * an ellipse with a line segment. The chord is outlined using the pen and filled using the brush + * that are defined in the playback device context. + */ + public static class WmfChord extends WmfArc { @Override - public void draw(HwmfGraphics ctx) { - + public HwmfRecordType getRecordType() { + return HwmfRecordType.chord; } -} + } /** 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 4b01deb07d..32d34d76aa 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 java.awt.Shape; import java.awt.geom.Path2D; import java.awt.image.BufferedImage; import java.io.File; @@ -25,6 +26,7 @@ import java.io.IOException; import javax.imageio.ImageIO; import org.apache.poi.hwmf.draw.HwmfGraphics; +import org.apache.poi.hwmf.record.HwmfWindowing.WmfCreateRegion; import org.apache.poi.util.LittleEndianConsts; import org.apache.poi.util.LittleEndianInputStream; @@ -46,13 +48,13 @@ public class HwmfFill { * A 16-bit unsigned integer used to index into the WMF Object Table to get * the region to be filled. */ - private int region; + private int regionIndex; /** * A 16-bit unsigned integer used to index into the WMF Object Table to get the * brush to use for filling the region. */ - private int brush; + private int brushIndex; @Override public HwmfRecordType getRecordType() { @@ -61,13 +63,20 @@ public class HwmfFill { @Override public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { - region = leis.readUShort(); - brush = leis.readUShort(); + regionIndex = leis.readUShort(); + brushIndex = leis.readUShort(); return 2*LittleEndianConsts.SHORT_SIZE; } @Override public void draw(HwmfGraphics ctx) { + ctx.applyObjectTableEntry(regionIndex); + ctx.applyObjectTableEntry(brushIndex); + + Shape region = ctx.getProperties().getRegion(); + if (region != null) { + ctx.fill(region); + } } } @@ -82,20 +91,25 @@ public class HwmfFill { * A 16-bit unsigned integer used to index into the WMF Object Table to get * the region to be painted. */ - int region; + int regionIndex; public HwmfRecordType getRecordType() { return HwmfRecordType.paintRegion; } public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { - region = leis.readUShort(); + regionIndex = leis.readUShort(); return LittleEndianConsts.SHORT_SIZE; } @Override public void draw(HwmfGraphics ctx) { - + ctx.applyObjectTableEntry(regionIndex); + + Shape region = ctx.getProperties().getRegion(); + if (region != null) { + ctx.fill(region); + } } } 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 be59c2dd80..adaf91cc44 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java @@ -44,7 +44,7 @@ public class HwmfMisc { @Override public void draw(HwmfGraphics ctx) { - + ctx.saveProperties(); } } @@ -92,7 +92,7 @@ public class HwmfMisc { @Override public void draw(HwmfGraphics ctx) { - + ctx.restoreProperties(nSavedDC); } } @@ -431,11 +431,11 @@ public class HwmfMisc { @Override public void draw(HwmfGraphics ctx) { - + ctx.unsetObjectTableEntry(objectIndex); } } - public static class WmfCreatePatternBrush implements HwmfRecord { + public static class WmfCreatePatternBrush implements HwmfRecord, HwmfObjectTableEntry { private HwmfBitmap16 pattern; @@ -452,16 +452,23 @@ public class HwmfMisc { @Override public void draw(HwmfGraphics ctx) { - + ctx.addObjectTableEntry(this); + } + + @Override + public void applyObject(HwmfGraphics ctx) { + HwmfDrawProperties dp = ctx.getProperties(); + dp.setBrushBitmap(pattern.getImage()); + dp.setBrushStyle(HwmfBrushStyle.BS_PATTERN); } } - public static class WmfCreatePenIndirect implements HwmfRecord { + public static class WmfCreatePenIndirect implements HwmfRecord, HwmfObjectTableEntry { private HwmfPenStyle penStyle; /** * A 32-bit PointS Object that specifies a point for the object dimensions. - * The xcoordinate is the pen width. The y-coordinate is ignored. + * The x-coordinate is the pen width. The y-coordinate is ignored. */ private int xWidth; @SuppressWarnings("unused") @@ -488,6 +495,11 @@ public class HwmfMisc { @Override public void draw(HwmfGraphics ctx) { + ctx.addObjectTableEntry(this); + } + + @Override + public void applyObject(HwmfGraphics ctx) { HwmfDrawProperties p = ctx.getProperties(); p.setPenStyle(penStyle); p.setPenColor(colorRef); @@ -540,7 +552,7 @@ public class HwmfMisc { * * */ - public static class WmfCreateBrushIndirect implements HwmfRecord { + public static class WmfCreateBrushIndirect implements HwmfRecord, HwmfObjectTableEntry { private HwmfBrushStyle brushStyle; private HwmfColorRef colorRef; @@ -568,6 +580,11 @@ public class HwmfMisc { @Override public void draw(HwmfGraphics ctx) { + ctx.addObjectTableEntry(this); + } + + @Override + public void applyObject(HwmfGraphics ctx) { HwmfDrawProperties p = ctx.getProperties(); p.setBrushStyle(brushStyle); p.setBrushColor(colorRef); diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfObjectTableEntry.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfObjectTableEntry.java new file mode 100644 index 0000000000..f05e6907c7 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfObjectTableEntry.java @@ -0,0 +1,28 @@ +/* ==================================================================== + 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.hwmf.record; + +import org.apache.poi.hwmf.draw.HwmfGraphics; + +/** + * Marker interface for Records, which should be added to the + * WMF object table for further selection + */ +public interface HwmfObjectTableEntry { + public void applyObject(HwmfGraphics ctx); +} diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java index d55f9e454e..0823e807c6 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java @@ -81,7 +81,7 @@ public class HwmfPalette { /** * The META_CREATEPALETTE record creates a Palette Object */ - public static class WmfCreatePalette extends WmfPaletteParent { + public static class WmfCreatePalette extends WmfPaletteParent implements HwmfObjectTableEntry { @Override public HwmfRecordType getRecordType() { return HwmfRecordType.createPalette; @@ -89,6 +89,11 @@ public class HwmfPalette { @Override public void draw(HwmfGraphics ctx) { + ctx.addObjectTableEntry(this); + } + + @Override + public void applyObject(HwmfGraphics ctx) { } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java index 4e908ac399..075a0a3c91 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java @@ -32,7 +32,7 @@ import org.apache.poi.util.BitFieldFactory; * The defaults in case the other values of the subsection aren't set are * solid, round end caps, round joins and cosmetic type. */ -public class HwmfPenStyle { +public class HwmfPenStyle implements Cloneable { public enum HwmfLineCap { /** Rounded ends */ ROUND(0, BasicStroke.CAP_ROUND), @@ -168,4 +168,22 @@ public class HwmfPenStyle { public boolean isAlternateDash() { return SUBSECTION_ALTERNATE.isSet(flag); } + + + /** + * Creates a new object of the same class and with the + * same contents as this object. + * @return a clone of this instance. + * @exception OutOfMemoryError if there is not enough memory. + * @see java.lang.Cloneable + */ + @Override + public HwmfPenStyle clone() { + try { + return (HwmfPenStyle)super.clone(); + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(); + } + } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPlaceableHeader.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPlaceableHeader.java index edff44dffb..4518921320 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPlaceableHeader.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPlaceableHeader.java @@ -17,6 +17,7 @@ package org.apache.poi.hwmf.record; +import java.awt.geom.Rectangle2D; import java.io.IOException; import org.apache.poi.util.LittleEndianConsts; @@ -25,6 +26,8 @@ import org.apache.poi.util.LittleEndianInputStream; public class HwmfPlaceableHeader { public static int WMF_HEADER_MAGIC = 0x9AC6CDD7; + final Rectangle2D bounds; + protected HwmfPlaceableHeader(LittleEndianInputStream leis) throws IOException { /* * HWmf (2 bytes): The resource handle to the metafile, when the metafile is in memory. When @@ -41,6 +44,7 @@ public class HwmfPlaceableHeader { int y1 = leis.readShort(); int x2 = leis.readShort(); int y2 = leis.readShort(); + bounds = new Rectangle2D.Double(x1, y1, x2-x1, y2-y1); /* * Inch (2 bytes): The number of logical units per inch used to represent the image. @@ -74,4 +78,8 @@ public class HwmfPlaceableHeader { return null; } } + + public Rectangle2D getBounds() { + return bounds; + } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecordType.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecordType.java index b5b93097a3..db40bb6f8c 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecordType.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecordType.java @@ -23,76 +23,77 @@ package org.apache.poi.hwmf.record; * @see Inside the Windows Meta File Format */ public enum HwmfRecordType { - eof(0x0000, null), - realizePalette(0x0035, HwmfPalette.WmfRealizePalette.class), - setPalEntries(0x0037, HwmfPalette.WmfSetPaletteEntries.class), - setBkMode(0x0102, HwmfMisc.WmfSetBkMode.class), - setMapMode(0x0103, HwmfMisc.WmfSetMapMode.class), - setRop2(0x0104, HwmfMisc.WmfSetRop2.class), - setRelabs(0x0105, HwmfMisc.WmfSetRelabs.class), - setPolyFillMode(0x0106, HwmfFill.WmfSetPolyfillMode.class), - setStretchBltMode(0x0107, HwmfMisc.WmfSetStretchBltMode.class), - setTextCharExtra(0x0108, HwmfText.WmfSetTextCharExtra.class), - restoreDc(0x0127, HwmfMisc.WmfRestoreDc.class), - resizePalette(0x0139, HwmfPalette.WmfResizePalette.class), - dibCreatePatternBrush(0x0142, HwmfMisc.WmfDibCreatePatternBrush.class), - setLayout(0x0149, HwmfMisc.WmfSetLayout.class), - setBkColor(0x0201, HwmfMisc.WmfSetBkColor.class), - setTextColor(0x0209, HwmfText.WmfSetTextColor.class), - offsetViewportOrg(0x0211, HwmfWindowing.WmfOffsetViewportOrg.class), - lineTo(0x0213, HwmfDraw.WmfLineTo.class), - moveTo(0x0214, HwmfDraw.WmfMoveTo.class), - offsetClipRgn(0x0220, HwmfWindowing.WmfOffsetClipRgn.class), - fillRegion(0x0228, HwmfFill.WmfFillRegion.class), - setMapperFlags(0x0231, HwmfMisc.WmfSetMapperFlags.class), - selectPalette(0x0234, HwmfPalette.WmfSelectPalette.class), - polygon(0x0324, HwmfDraw.WmfPolygon.class), - polyline(0x0325, HwmfDraw.WmfPolyline.class), - setTextJustification(0x020a, HwmfText.WmfSetTextJustification.class), - setWindowOrg(0x020b, HwmfWindowing.WmfSetWindowOrg.class), - setWindowExt(0x020c, HwmfWindowing.WmfSetWindowExt.class), - setViewportOrg(0x020d, HwmfWindowing.WmfSetViewportOrg.class), - setViewportExt(0x020e, HwmfWindowing.WmfSetViewportExt.class), - offsetWindowOrg(0x020f, HwmfWindowing.WmfOffsetWindowOrg.class), - scaleWindowExt(0x0410, HwmfWindowing.WmfScaleWindowExt.class), - scaleViewportExt(0x0412, HwmfWindowing.WmfScaleViewportExt.class), - excludeClipRect(0x0415, HwmfWindowing.WmfExcludeClipRect.class), - intersectClipRect(0x0416, HwmfWindowing.WmfIntersectClipRect.class), - ellipse(0x0418, HwmfDraw.WmfEllipse.class), - floodFill(0x0419, HwmfFill.WmfFloodFill.class), - frameRegion(0x0429, HwmfDraw.WmfFrameRegion.class), - animatePalette(0x0436, HwmfPalette.WmfAnimatePalette.class), - textOut(0x0521, HwmfText.WmfTextOut.class), - polyPolygon(0x0538, HwmfDraw.WmfPolyPolygon.class), - extFloodFill(0x0548, HwmfFill.WmfExtFloodFill.class), - rectangle(0x041b, HwmfDraw.WmfRectangle.class), - setPixel(0x041f, HwmfDraw.WmfSetPixel.class), - roundRect(0x061c, HwmfDraw.WmfRoundRect.class), - patBlt(0x061d, HwmfFill.WmfPatBlt.class), - saveDc(0x001e, HwmfMisc.WmfSaveDc.class), - pie(0x081a, HwmfDraw.WmfPie.class), - stretchBlt(0x0b23, HwmfFill.WmfStretchBlt.class), - escape(0x0626, HwmfEscape.class), - invertRegion(0x012a, HwmfFill.WmfInvertRegion.class), - paintRegion(0x012b, HwmfFill.WmfPaintRegion.class), - selectClipRegion(0x012c, HwmfWindowing.WmfSelectClipRegion.class), - selectObject(0x012d, HwmfDraw.WmfSelectObject.class), - setTextAlign(0x012e, HwmfText.WmfSetTextAlign.class), - arc(0x0817, HwmfDraw.WmfArc.class), - chord(0x0830, HwmfDraw.WmfChord.class), - bitBlt(0x0922, HwmfFill.WmfBitBlt.class), - extTextOut(0x0a32, HwmfText.WmfExtTextOut.class), - setDibToDev(0x0d33, HwmfFill.WmfSetDibToDev.class), - dibBitBlt(0x0940, HwmfFill.WmfDibBitBlt.class), - dibStretchBlt(0x0b41, HwmfFill.WmfDibStretchBlt.class), - stretchDib(0x0f43, HwmfFill.WmfStretchDib.class), - deleteObject(0x01f0, HwmfMisc.WmfDeleteObject.class), - createPalette(0x00f7, HwmfPalette.WmfCreatePalette.class), - createPatternBrush(0x01f9, HwmfMisc.WmfCreatePatternBrush.class), - createPenIndirect(0x02fa, HwmfMisc.WmfCreatePenIndirect.class), - createFontIndirect(0x02fb, HwmfText.WmfCreateFontIndirect.class), - createBrushIndirect(0x02fc, HwmfMisc.WmfCreateBrushIndirect.class), - createRegion(0x06ff, HwmfWindowing.WmfCreateRegion.class); + eof(0x0000, null) + ,animatePalette(0x0436, HwmfPalette.WmfAnimatePalette.class) + ,arc(0x0817, HwmfDraw.WmfArc.class) + ,bitBlt(0x0922, HwmfFill.WmfBitBlt.class) + ,chord(0x0830, HwmfDraw.WmfChord.class) + ,createBrushIndirect(0x02fc, HwmfMisc.WmfCreateBrushIndirect.class) + ,createFontIndirect(0x02fb, HwmfText.WmfCreateFontIndirect.class) + ,createPalette(0x00f7, HwmfPalette.WmfCreatePalette.class) + ,createPatternBrush(0x01f9, HwmfMisc.WmfCreatePatternBrush.class) + ,createPenIndirect(0x02fa, HwmfMisc.WmfCreatePenIndirect.class) + ,createRegion(0x06ff, HwmfWindowing.WmfCreateRegion.class) + ,deleteObject(0x01f0, HwmfMisc.WmfDeleteObject.class) + ,dibBitBlt(0x0940, HwmfFill.WmfDibBitBlt.class) + ,dibCreatePatternBrush(0x0142, HwmfMisc.WmfDibCreatePatternBrush.class) + ,dibStretchBlt(0x0b41, HwmfFill.WmfDibStretchBlt.class) + ,ellipse(0x0418, HwmfDraw.WmfEllipse.class) + ,escape(0x0626, HwmfEscape.class) + ,excludeClipRect(0x0415, HwmfWindowing.WmfExcludeClipRect.class) + ,extFloodFill(0x0548, HwmfFill.WmfExtFloodFill.class) + ,extTextOut(0x0a32, HwmfText.WmfExtTextOut.class) + ,fillRegion(0x0228, HwmfFill.WmfFillRegion.class) + ,floodFill(0x0419, HwmfFill.WmfFloodFill.class) + ,frameRegion(0x0429, HwmfDraw.WmfFrameRegion.class) + ,intersectClipRect(0x0416, HwmfWindowing.WmfIntersectClipRect.class) + ,invertRegion(0x012a, HwmfFill.WmfInvertRegion.class) + ,lineTo(0x0213, HwmfDraw.WmfLineTo.class) + ,moveTo(0x0214, HwmfDraw.WmfMoveTo.class) + ,offsetClipRgn(0x0220, HwmfWindowing.WmfOffsetClipRgn.class) + ,offsetViewportOrg(0x0211, HwmfWindowing.WmfOffsetViewportOrg.class) + ,offsetWindowOrg(0x020f, HwmfWindowing.WmfOffsetWindowOrg.class) + ,paintRegion(0x012b, HwmfFill.WmfPaintRegion.class) + ,patBlt(0x061d, HwmfFill.WmfPatBlt.class) + ,pie(0x081a, HwmfDraw.WmfPie.class) + ,polygon(0x0324, HwmfDraw.WmfPolygon.class) + ,polyline(0x0325, HwmfDraw.WmfPolyline.class) + ,polyPolygon(0x0538, HwmfDraw.WmfPolyPolygon.class) + ,realizePalette(0x0035, HwmfPalette.WmfRealizePalette.class) + ,rectangle(0x041b, HwmfDraw.WmfRectangle.class) + ,resizePalette(0x0139, HwmfPalette.WmfResizePalette.class) + ,restoreDc(0x0127, HwmfMisc.WmfRestoreDc.class) + ,roundRect(0x061c, HwmfDraw.WmfRoundRect.class) + ,saveDc(0x001e, HwmfMisc.WmfSaveDc.class) + ,scaleViewportExt(0x0412, HwmfWindowing.WmfScaleViewportExt.class) + ,scaleWindowExt(0x0410, HwmfWindowing.WmfScaleWindowExt.class) + ,selectClipRegion(0x012c, HwmfWindowing.WmfSelectClipRegion.class) + ,selectObject(0x012d, HwmfDraw.WmfSelectObject.class) + ,selectPalette(0x0234, HwmfPalette.WmfSelectPalette.class) + ,setBkColor(0x0201, HwmfMisc.WmfSetBkColor.class) + ,setBkMode(0x0102, HwmfMisc.WmfSetBkMode.class) + ,setDibToDev(0x0d33, HwmfFill.WmfSetDibToDev.class) + ,setLayout(0x0149, HwmfMisc.WmfSetLayout.class) + ,setMapMode(0x0103, HwmfMisc.WmfSetMapMode.class) + ,setMapperFlags(0x0231, HwmfMisc.WmfSetMapperFlags.class) + ,setPalEntries(0x0037, HwmfPalette.WmfSetPaletteEntries.class) + ,setPixel(0x041f, HwmfDraw.WmfSetPixel.class) + ,setPolyFillMode(0x0106, HwmfFill.WmfSetPolyfillMode.class) + ,setRelabs(0x0105, HwmfMisc.WmfSetRelabs.class) + ,setRop2(0x0104, HwmfMisc.WmfSetRop2.class) + ,setStretchBltMode(0x0107, HwmfMisc.WmfSetStretchBltMode.class) + ,setTextAlign(0x012e, HwmfText.WmfSetTextAlign.class) + ,setTextCharExtra(0x0108, HwmfText.WmfSetTextCharExtra.class) + ,setTextColor(0x0209, HwmfText.WmfSetTextColor.class) + ,setTextJustification(0x020a, HwmfText.WmfSetTextJustification.class) + ,setViewportExt(0x020e, HwmfWindowing.WmfSetViewportExt.class) + ,setViewportOrg(0x020d, HwmfWindowing.WmfSetViewportOrg.class) + ,setWindowExt(0x020c, HwmfWindowing.WmfSetWindowExt.class) + ,setWindowOrg(0x020b, HwmfWindowing.WmfSetWindowOrg.class) + ,stretchBlt(0x0b23, HwmfFill.WmfStretchBlt.class) + ,stretchDib(0x0f43, HwmfFill.WmfStretchDib.class) + ,textOut(0x0521, HwmfText.WmfTextOut.class) + ; public int id; public Class clazz; 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 5e6dde83a6..167e446bef 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java @@ -391,7 +391,7 @@ public class HwmfText { } } - public static class WmfCreateFontIndirect implements HwmfRecord { + public static class WmfCreateFontIndirect implements HwmfRecord, HwmfObjectTableEntry { private HwmfFont font; @Override @@ -407,6 +407,11 @@ public class HwmfText { @Override public void draw(HwmfGraphics ctx) { + ctx.addObjectTableEntry(this); + } + + @Override + public void applyObject(HwmfGraphics ctx) { } } 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 a6d7204583..5af1ab826c 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java @@ -17,7 +17,10 @@ package org.apache.poi.hwmf.record; +import java.awt.Shape; +import java.awt.geom.Area; import java.awt.geom.Rectangle2D; +import java.awt.geom.Rectangle2D.Double; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -128,7 +131,9 @@ public class HwmfWindowing { @Override public void draw(HwmfGraphics ctx) { Rectangle2D viewport = ctx.getProperties().getViewport(); - ctx.getProperties().setViewportOrg(viewport.getX()+xOffset, viewport.getY()+yOffset); + double x = (viewport == null) ? 0 : viewport.getX(); + double y = (viewport == null) ? 0 : viewport.getY(); + ctx.getProperties().setViewportOrg(x+xOffset, y+yOffset); } } @@ -163,6 +168,14 @@ public class HwmfWindowing { public void draw(HwmfGraphics ctx) { ctx.getProperties().setWindowOrg(x, y); } + + public int getY() { + return y; + } + + public int getX() { + return x; + } } /** @@ -199,6 +212,14 @@ public class HwmfWindowing { public void draw(HwmfGraphics ctx) { ctx.getProperties().setWindowExt(width, height); } + + public int getHeight() { + return height; + } + + public int getWidth() { + return width; + } } /** @@ -338,6 +359,9 @@ public class HwmfWindowing { @Override public void draw(HwmfGraphics ctx) { Rectangle2D viewport = ctx.getProperties().getViewport(); + if (viewport == null) { + viewport = ctx.getProperties().getWindow(); + } double width = viewport.getWidth() * xNum / xDenom; double height = viewport.getHeight() * yNum / yDenom; ctx.getProperties().setViewportExt(width, height); @@ -556,7 +580,7 @@ public class HwmfWindowing { } } - public static class WmfCreateRegion implements HwmfRecord { + public static class WmfCreateRegion implements HwmfRecord, HwmfObjectTableEntry { /** * A 16-bit signed integer. A value that MUST be ignored. */ @@ -642,7 +666,32 @@ public class HwmfWindowing { @Override public void draw(HwmfGraphics ctx) { + ctx.addObjectTableEntry(this); + } + + @Override + public void applyObject(HwmfGraphics ctx) { + Rectangle2D lastRect = null; + Area scanLines = new Area(); + int count = 0; + for (WmfScanObject so : scanObjects) { + int y = Math.min(so.top, so.bottom); + int h = Math.abs(so.top - so.bottom - 1); + for (int i=0; i 0) { + region = (count == 1) ? lastRect : scanLines; + } + ctx.getProperties().setRegion(region); } } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java b/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java index 3bea50eaf6..7d953aa03a 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java @@ -17,6 +17,8 @@ package org.apache.poi.hwmf.usermodel; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -24,24 +26,25 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.apache.poi.hwmf.draw.HwmfGraphics; import org.apache.poi.hwmf.record.HwmfHeader; import org.apache.poi.hwmf.record.HwmfPlaceableHeader; import org.apache.poi.hwmf.record.HwmfRecord; import org.apache.poi.hwmf.record.HwmfRecordType; +import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowExt; +import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowOrg; import org.apache.poi.util.LittleEndianInputStream; public class HwmfPicture { - List records = new ArrayList(); - - public List getRecords() { - return Collections.unmodifiableList(records); - } + final List records = new ArrayList(); + final HwmfPlaceableHeader placeableHeader; + final HwmfHeader header; public HwmfPicture(InputStream inputStream) throws IOException { BufferedInputStream bis = new BufferedInputStream(inputStream, 10000); LittleEndianInputStream leis = new LittleEndianInputStream(bis); - HwmfPlaceableHeader placeableHeader = HwmfPlaceableHeader.readHeader(leis); - HwmfHeader header = new HwmfHeader(leis); + placeableHeader = HwmfPlaceableHeader.readHeader(leis); + header = new HwmfHeader(leis); for (;;) { // recordSize in DWORDs @@ -76,5 +79,50 @@ public class HwmfPicture { } } + public List getRecords() { + return Collections.unmodifiableList(records); + } + public void draw(Graphics2D ctx) { + HwmfGraphics g = new HwmfGraphics(ctx, getBounds()); + for (HwmfRecord r : records) { + r.draw(g); + } + } + + /** + * Returns the bounding box in device-independent units. Usually this is taken from the placeable header. + * + * @return the bounding box + */ + public Rectangle2D getBounds() { + if (placeableHeader != null) { + return placeableHeader.getBounds(); + } else { + WmfSetWindowOrg wOrg = null; + WmfSetWindowExt wExt = null; + for (HwmfRecord r : getRecords()) { + if (wOrg != null && wExt != null) { + break; + } + if (r instanceof WmfSetWindowOrg) { + wOrg = (WmfSetWindowOrg)r; + } else if (r instanceof WmfSetWindowExt) { + wExt = (WmfSetWindowExt)r; + } + } + if (wOrg == null || wExt == null) { + throw new RuntimeException("invalid wmf file - window records are incomplete."); + } + return new Rectangle2D.Double(wOrg.getX(), wOrg.getY(), wExt.getWidth(), wExt.getHeight()); + } + } + + public HwmfPlaceableHeader getPlaceableHeader() { + return placeableHeader; + } + + public HwmfHeader getHeader() { + return header; + } } diff --git a/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java b/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java index ad585eccbd..1c9f4c1a12 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java +++ b/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java @@ -19,6 +19,9 @@ package org.apache.poi.hwmf; import static org.junit.Assert.assertEquals; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileFilter; @@ -55,6 +58,36 @@ public class TestHwmfParsing { List records = wmf.getRecords(); assertEquals(581, records.size()); } + + @Test + @Ignore + public void paint() throws IOException { + File f = POIDataSamples.getSlideShowInstance().getFile("santa.wmf"); + FileInputStream fis = new FileInputStream(f); + HwmfPicture wmf = new HwmfPicture(fis); + fis.close(); + + Rectangle2D bounds = wmf.getBounds(); + int width = 300; + // keep aspect ratio for height + int height = (int)(width*bounds.getHeight()/bounds.getWidth()); + + BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = bufImg.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + + g.scale(width/bounds.getWidth(), height/bounds.getHeight()); + g.translate(-bounds.getX(), -bounds.getY()); + + wmf.draw(g); + + g.dispose(); + + ImageIO.write(bufImg, "PNG", new File("bla.png")); + } @Test @Ignore