package org.apache.poi.hwmf.draw;\r
\r
import java.awt.Color;\r
+import java.awt.Shape;\r
+import java.awt.geom.Area;\r
import java.awt.geom.Point2D;\r
import java.awt.geom.Rectangle2D;\r
import java.awt.image.BufferedImage;\r
+import java.awt.image.ColorModel;\r
+import java.awt.image.WritableRaster;\r
\r
import org.apache.poi.hwmf.record.HwmfBrushStyle;\r
import org.apache.poi.hwmf.record.HwmfColorRef;\r
import org.apache.poi.hwmf.record.HwmfPenStyle;\r
\r
public class HwmfDrawProperties {\r
- private Rectangle2D window = new Rectangle2D.Double(0, 0, 1, 1);\r
- private Rectangle2D viewport = new Rectangle2D.Double(0, 0, 1, 1);\r
- private Point2D location = new Point2D.Double(0,0);\r
+ private final Rectangle2D window;\r
+ private Rectangle2D viewport = null;\r
+ private final Point2D location;\r
private HwmfMapMode mapMode = HwmfMapMode.MM_ANISOTROPIC;\r
private HwmfColorRef backgroundColor = new HwmfColorRef(Color.BLACK);\r
private HwmfBrushStyle brushStyle = HwmfBrushStyle.BS_SOLID;\r
private double penMiterLimit = 10;\r
private HwmfBkMode bkMode = HwmfBkMode.OPAQUE;\r
private HwmfPolyfillMode polyfillMode = HwmfPolyfillMode.WINDING;\r
-\r
+ private Shape region = null;\r
+\r
+ public HwmfDrawProperties() {\r
+ window = new Rectangle2D.Double(0, 0, 1, 1);\r
+ viewport = null;\r
+ location = new Point2D.Double(0,0);\r
+ }\r
+ \r
+ public HwmfDrawProperties(HwmfDrawProperties other) {\r
+ this.window = (Rectangle2D)other.window.clone();\r
+ this.viewport = (other.viewport == null) ? null : (Rectangle2D)other.viewport.clone();\r
+ this.location = (Point2D)other.location.clone();\r
+ this.mapMode = other.mapMode;\r
+ this.backgroundColor = (other.backgroundColor == null) ? null : other.backgroundColor.clone();\r
+ this.brushStyle = other.brushStyle;\r
+ this.brushColor = other.brushColor.clone();\r
+ this.brushHatch = other.brushHatch;\r
+ if (other.brushBitmap != null) {\r
+ ColorModel cm = other.brushBitmap.getColorModel();\r
+ boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();\r
+ WritableRaster raster = other.brushBitmap.copyData(null);\r
+ this.brushBitmap = new BufferedImage(cm, raster, isAlphaPremultiplied, null); \r
+ }\r
+ this.penWidth = 1;\r
+ this.penStyle = (other.penStyle == null) ? null : other.penStyle.clone();\r
+ this.penColor = (other.penColor == null) ? null : other.penColor.clone();\r
+ this.penMiterLimit = other.penMiterLimit;\r
+ this.bkMode = other.bkMode;\r
+ this.polyfillMode = other.polyfillMode;\r
+ if (other.region instanceof Rectangle2D) {\r
+ this.region = ((Rectangle2D)other.region).getBounds2D();\r
+ } else if (other.region instanceof Area) {\r
+ this.region = new Area(other.region);\r
+ }\r
+ }\r
+ \r
public void setViewportExt(double width, double height) {\r
double x = viewport.getX();\r
double y = viewport.getY();\r
}\r
\r
public Rectangle2D getViewport() {\r
- return (Rectangle2D)viewport.clone();\r
+ return (viewport == null) ? null : (Rectangle2D)viewport.clone();\r
}\r
\r
public void setWindowExt(double width, double height) {\r
public void setBrushBitmap(BufferedImage brushBitmap) {\r
this.brushBitmap = brushBitmap;\r
}\r
+\r
+\r
+ /**\r
+ * Gets the last stored region\r
+ *\r
+ * @return the last stored region\r
+ */\r
+ public Shape getRegion() {\r
+ return region;\r
+ }\r
+\r
+ /**\r
+ * Sets a region for further usage\r
+ *\r
+ * @param region a region object which is usually a rectangle\r
+ */\r
+ public void setRegion(Shape region) {\r
+ this.region = region;\r
+ }\r
}\r
import java.awt.geom.GeneralPath;\r
import java.awt.geom.Rectangle2D;\r
import java.awt.image.BufferedImage;\r
-import java.util.ArrayDeque;\r
-import java.util.Deque;\r
+import java.util.ArrayList;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.ListIterator;\r
+import java.util.NoSuchElementException;\r
\r
import org.apache.poi.hwmf.record.HwmfBrushStyle;\r
import org.apache.poi.hwmf.record.HwmfHatchStyle;\r
import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;\r
+import org.apache.poi.hwmf.record.HwmfObjectTableEntry;\r
import org.apache.poi.hwmf.record.HwmfPenStyle;\r
import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;\r
import org.apache.poi.util.Units;\r
\r
public class HwmfGraphics {\r
private final Graphics2D graphicsCtx;\r
- private final Deque<HwmfDrawProperties> propStack = new ArrayDeque<HwmfDrawProperties>();\r
- HwmfDrawProperties prop;\r
+ private final List<HwmfDrawProperties> propStack = new LinkedList<HwmfDrawProperties>();\r
+ private HwmfDrawProperties prop = new HwmfDrawProperties();\r
+ private List<HwmfObjectTableEntry> objectTable = new ArrayList<HwmfObjectTableEntry>();\r
+ /** Bounding box from the placeable header */ \r
+ private final Rectangle2D bbox;\r
\r
- public HwmfGraphics(Graphics2D graphicsCtx) {\r
+ /**\r
+ * Initialize a graphics context for wmf rendering\r
+ *\r
+ * @param graphicsCtx the graphics context to delegate drawing calls\r
+ * @param bbox the bounding box of the wmf (taken from the placeable header)\r
+ */\r
+ public HwmfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) {\r
this.graphicsCtx = graphicsCtx;\r
- prop = new HwmfDrawProperties();\r
- propStack.push(prop);\r
+ this.bbox = (Rectangle2D)bbox.clone();\r
}\r
\r
public HwmfDrawProperties getProperties() {\r
\r
protected Shape fitShapeToView(Shape shape) {\r
int scaleUnits = prop.getMapMode().scale;\r
- Rectangle2D view = prop.getViewport(), win = prop.getWindow();\r
+ Rectangle2D view = prop.getViewport();\r
+ Rectangle2D win = prop.getWindow();\r
+ if (view == null) {\r
+ view = win;\r
+ }\r
double scaleX, scaleY;\r
switch (scaleUnits) {\r
case -1:\r
}\r
\r
AffineTransform at = new AffineTransform();\r
- at.translate(view.getX(), view.getY());\r
at.scale(scaleX, scaleY);\r
- at.translate(-win.getX(), -win.getY());\r
- at.translate(-view.getX(), -view.getY());\r
+// at.translate(-view.getX(), -view.getY());\r
+ at.translate(bbox.getWidth()/win.getWidth(), bbox.getHeight()/win.getHeight());\r
\r
- return at.createTransformedShape(shape);\r
+ Shape tshape = at.createTransformedShape(shape);\r
+ return tshape;\r
}\r
\r
protected BasicStroke getStroke() {\r
- Rectangle2D view = prop.getViewport(), win = prop.getWindow();\r
+ Rectangle2D view = prop.getViewport();\r
+ Rectangle2D win = prop.getWindow();\r
+ if (view == null) {\r
+ view = win;\r
+ }\r
float width = (float)(prop.getPenWidth() * view.getWidth() / win.getWidth());\r
HwmfPenStyle ps = prop.getPenStyle();\r
int cap = ps.getLineCap().awtFlag;\r
return (bi == null) ? null\r
: new TexturePaint(bi, new Rectangle(0,0,bi.getWidth(),bi.getHeight()));\r
}\r
+\r
+ /**\r
+ * Adds an record of type {@link HwmfObjectTableEntry} to the object table.\r
+ *\r
+ * Every object is assigned the lowest available index-that is, the smallest\r
+ * numerical value-in the WMF Object Table. This binding happens at object creation,\r
+ * not when the object is used.\r
+ * Moreover, each object table index uniquely refers to an object.\r
+ * Indexes in the WMF Object Table always start at 0.\r
+ *\r
+ * @param entry\r
+ */\r
+ public void addObjectTableEntry(HwmfObjectTableEntry entry) {\r
+ ListIterator<HwmfObjectTableEntry> oIter = objectTable.listIterator();\r
+ while (oIter.hasNext()) {\r
+ HwmfObjectTableEntry tableEntry = oIter.next();\r
+ if (tableEntry == null) {\r
+ oIter.set(entry);\r
+ return;\r
+ }\r
+ }\r
+ objectTable.add(entry);\r
+ }\r
+\r
+ /**\r
+ * Applies the object table entry\r
+ *\r
+ * @param index the index of the object table entry (0-based)\r
+ * \r
+ * @throws IndexOutOfBoundsException if the index is out of range\r
+ * @throws NoSuchElementException if the entry was deleted before\r
+ */\r
+ public void applyObjectTableEntry(int index) {\r
+ HwmfObjectTableEntry ote = objectTable.get(index);\r
+ if (ote == null) {\r
+ throw new NoSuchElementException("WMF reference exception - object table entry on index "+index+" was deleted before.");\r
+ }\r
+ ote.applyObject(this);\r
+ }\r
+ \r
+ /**\r
+ * Unsets (deletes) the object table entry for further usage\r
+ * \r
+ * When a META_DELETEOBJECT record (section 2.3.4.7) is received that specifies this\r
+ * object's particular index, the object's resources are released, the binding to its\r
+ * WMF Object Table index is ended, and the index value is returned to the pool of\r
+ * available indexes. The index will be reused, if needed, by a subsequent object\r
+ * created by another Object Record Type record.\r
+ *\r
+ * @param index the index (0-based)\r
+ * \r
+ * @throws IndexOutOfBoundsException if the index is out of range\r
+ */\r
+ public void unsetObjectTableEntry(int index) {\r
+ objectTable.set(index, null);\r
+ }\r
+ \r
+ /**\r
+ * Saves the current properties to the stack\r
+ */\r
+ public void saveProperties() {\r
+ propStack.add(prop);\r
+ prop = new HwmfDrawProperties(prop); \r
+ }\r
+ \r
+ /**\r
+ * Restores the properties from the stack\r
+ *\r
+ * @param index if the index is positive, the n-th element from the start is removed and activated.\r
+ * If the index is negative, the n-th previous element relative to the current properties element is removed and activated.\r
+ */\r
+ public void restoreProperties(int index) {\r
+ if (index == 0) {\r
+ return;\r
+ }\r
+ int stackIndex = index;\r
+ if (stackIndex < 0) {\r
+ int curIdx = propStack.indexOf(prop);\r
+ assert (curIdx != -1);\r
+ stackIndex = curIdx + index;\r
+ }\r
+ prop = propStack.remove(stackIndex);\r
+ }\r
}\r
* Blue (1 byte): An 8-bit unsigned integer that defines the relative intensity of blue.\r
* Reserved (1 byte): An 8-bit unsigned integer that MUST be 0x00.\r
*/\r
-public class HwmfColorRef {\r
+public class HwmfColorRef implements Cloneable {\r
private Color colorRef = Color.BLACK;\r
\r
public HwmfColorRef() {}\r
public Color getColor() {\r
return colorRef;\r
}\r
+\r
+ /**\r
+ * Creates a new object of the same class and with the\r
+ * same contents as this object.\r
+ * @return a clone of this instance.\r
+ * @exception OutOfMemoryError if there is not enough memory.\r
+ * @see java.lang.Cloneable\r
+ */\r
+ @Override\r
+ public HwmfColorRef clone() {\r
+ try {\r
+ return (HwmfColorRef)super.clone();\r
+ } catch (CloneNotSupportedException e) {\r
+ // this shouldn't happen, since we are Cloneable\r
+ throw new InternalError();\r
+ }\r
+ }\r
}\r
package org.apache.poi.hwmf.record;\r
\r
import java.awt.Polygon;\r
+import java.awt.Rectangle;\r
+import java.awt.Shape;\r
+import java.awt.geom.Arc2D;\r
+import java.awt.geom.Ellipse2D;\r
import java.awt.geom.GeneralPath;\r
import java.awt.geom.Line2D;\r
import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+import java.awt.geom.RoundRectangle2D;\r
import java.io.IOException;\r
\r
import org.apache.poi.hwmf.draw.HwmfGraphics;\r
x = leis.readShort();\r
return 2*LittleEndianConsts.SHORT_SIZE;\r
}\r
- \r
+\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
ctx.getProperties().setLocation(x, y);\r
x = leis.readShort();\r
return 2*LittleEndianConsts.SHORT_SIZE;\r
}\r
- \r
+\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
Point2D start = ctx.getProperties().getLocation();\r
\r
return LittleEndianConsts.SHORT_SIZE+numberofPoints*LittleEndianConsts.INT_SIZE;\r
}\r
- \r
+\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
ctx.fill(getShape());\r
}\r
- \r
+\r
protected Polygon getShape() {\r
Polygon polygon = new Polygon();\r
for(int i = 0; i < numberofPoints; i++) {\r
leftRect = leis.readShort();\r
return 4*LittleEndianConsts.SHORT_SIZE;\r
}\r
- \r
+\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
- \r
+ int x = Math.min(leftRect, rightRect);\r
+ int y = Math.min(topRect, bottomRect);\r
+ int w = Math.abs(leftRect - rightRect - 1);\r
+ int h = Math.abs(topRect - bottomRect - 1);\r
+ Shape s = new Ellipse2D.Double(x, y, w, h);\r
+ ctx.fill(s);\r
}\r
}\r
\r
width = leis.readShort();\r
return 4*LittleEndianConsts.SHORT_SIZE;\r
}\r
- \r
+\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
- \r
+\r
}\r
}\r
\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ int x = Math.min(leftRect, rightRect);\r
+ int y = Math.min(topRect, bottomRect);\r
+ int w = Math.abs(leftRect - rightRect - 1);\r
+ int h = Math.abs(topRect - bottomRect - 1);\r
+ Shape s = new Rectangle2D.Double(x, y, w, h);\r
+ ctx.fill(s);\r
}\r
}\r
\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ Shape s = new Rectangle2D.Double(x, y, 1, 1);\r
+ ctx.fill(s);\r
}\r
}\r
\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ int x = Math.min(leftRect, rightRect);\r
+ int y = Math.min(topRect, bottomRect);\r
+ int w = Math.abs(leftRect - rightRect - 1);\r
+ int h = Math.abs(topRect - bottomRect - 1);\r
+ Shape s = new RoundRectangle2D.Double(x, y, w, h, width, height);\r
+ ctx.fill(s);\r
}\r
}\r
\r
\r
-\r
- /**\r
- * The META_PIE record draws a pie-shaped wedge bounded by the intersection of an ellipse and two\r
- * radials. The pie is outlined by using the pen and filled by using the brush that are defined in the\r
- * playback device context.\r
- */\r
- public static class WmfPie implements HwmfRecord {\r
- /**\r
- * A 16-bit signed integer that defines the y-coordinate, in logical\r
- * coordinates, of the endpoint of the second radial.\r
- */\r
- private int yRadial2;\r
- /**\r
- * A 16-bit signed integer that defines the x-coordinate, in logical\r
- * coordinates, of the endpoint of the second radial.\r
- */\r
- private int xRadial2;\r
- /**\r
- * A 16-bit signed integer that defines the y-coordinate, in logical\r
- * coordinates, of the endpoint of the first radial.\r
- */\r
- private int yRadial1;\r
- /**\r
- * A 16-bit signed integer that defines the x-coordinate, in logical\r
- * coordinates, of the endpoint of the first radial.\r
- */\r
- private int xRadial1;\r
- /**\r
- * A 16-bit signed integer that defines the y-coordinate, in logical units, of\r
- * the lower-right corner of the bounding rectangle.\r
- */\r
- private int bottomRect;\r
- /**\r
- * A 16-bit signed integer that defines the x-coordinate, in logical units, of\r
- * the lower-right corner of the bounding rectangle.\r
- */\r
- private int rightRect;\r
- /**\r
- * A 16-bit signed integer that defines the y-coordinate, in logical units, of the\r
- * upper-left corner of the bounding rectangle.\r
- */\r
- private int topRect;\r
- /**\r
- * A 16-bit signed integer that defines the x-coordinate, in logical units, of\r
- * the upper-left corner of the bounding rectangle.\r
- */\r
- private int leftRect;\r
-\r
- @Override\r
- public HwmfRecordType getRecordType() {\r
- return HwmfRecordType.pie;\r
- }\r
-\r
- @Override\r
- public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
- yRadial2 = leis.readShort();\r
- xRadial2 = leis.readShort();\r
- yRadial1 = leis.readShort();\r
- xRadial1 = leis.readShort();\r
- bottomRect = leis.readShort();\r
- rightRect = leis.readShort();\r
- topRect = leis.readShort();\r
- leftRect = leis.readShort();\r
- return 8*LittleEndianConsts.SHORT_SIZE;\r
- }\r
-\r
-\r
- @Override\r
- public void draw(HwmfGraphics ctx) {\r
-\r
- }\r
- }\r
-\r
/**\r
* The META_ARC record draws an elliptical arc.\r
*/\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
+ int x = Math.min(leftRect, rightRect);\r
+ int y = Math.min(topRect, bottomRect);\r
+ int w = Math.abs(leftRect - rightRect - 1);\r
+ int h = Math.abs(topRect - bottomRect - 1);\r
+ double startAngle = Math.toDegrees(Math.atan2(-(yStartArc - (topRect + h / 2.)), xStartArc - (leftRect + w / 2.)));\r
+ double endAngle = Math.toDegrees(Math.atan2(-(yEndArc - (topRect + h / 2.)), xEndArc - (leftRect + w / 2.)));\r
+ double arcAngle = (endAngle - startAngle) + (endAngle - startAngle > 0 ? 0 : 360);\r
+ if (startAngle < 0) {\r
+ startAngle += 360;\r
+ }\r
\r
+ boolean fillShape;\r
+ int arcClosure;\r
+ switch (getRecordType()) {\r
+ default:\r
+ case arc:\r
+ arcClosure = Arc2D.OPEN;\r
+ fillShape = false;\r
+ break;\r
+ case chord:\r
+ arcClosure = Arc2D.CHORD;\r
+ fillShape = true;\r
+ break;\r
+ case pie:\r
+ arcClosure = Arc2D.PIE;\r
+ fillShape = true;\r
+ break;\r
+ }\r
+ \r
+ Shape s = new Arc2D.Double(x, y, w, h, startAngle, arcAngle, arcClosure);\r
+ if (fillShape) {\r
+ ctx.fill(s);\r
+ } else {\r
+ ctx.draw(s);\r
+ }\r
}\r
}\r
\r
/**\r
- * The META_CHORD record draws a chord, which is defined by a region bounded by the intersection of\r
- * an ellipse with a line segment. The chord is outlined using the pen and filled using the brush\r
- * that are defined in the playback device context.\r
+ * The META_PIE record draws a pie-shaped wedge bounded by the intersection of an ellipse and two\r
+ * radials. The pie is outlined by using the pen and filled by using the brush that are defined in the\r
+ * playback device context.\r
*/\r
- public static class WmfChord implements HwmfRecord {\r
- /**\r
- * A 16-bit signed integer that defines the y-coordinate, in logical\r
- * coordinates, of the endpoint of the second radial.\r
- */\r
- private int yRadial2;\r
- /**\r
- * A 16-bit signed integer that defines the x-coordinate, in logical\r
- * coordinates, of the endpoint of the second radial.\r
- */\r
- private int xRadial2;\r
- /**\r
- * A 16-bit signed integer that defines the y-coordinate, in logical\r
- * coordinates, of the endpoint of the first radial.\r
- */\r
- private int yRadial1;\r
- /**\r
- * A 16-bit signed integer that defines the x-coordinate, in logical\r
- * coordinates, of the endpoint of the first radial.\r
- */\r
- private int xRadial1;\r
- /**\r
- * A 16-bit signed integer that defines the y-coordinate, in logical units, of\r
- * the lower-right corner of the bounding rectangle.\r
- */\r
- private int bottomRect;\r
- /**\r
- * A 16-bit signed integer that defines the x-coordinate, in logical units, of\r
- * the lower-right corner of the bounding rectangle.\r
- */\r
- private int rightRect;\r
- /**\r
- * A 16-bit signed integer that defines the y-coordinate, in logical units, of the\r
- * upper-left corner of the bounding rectangle.\r
- */\r
- private int topRect;\r
- /**\r
- * A 16-bit signed integer that defines the x-coordinate, in logical units, of\r
- * the upper-left corner of the bounding rectangle.\r
- */\r
- private int leftRect;\r
-\r
+ public static class WmfPie extends WmfArc {\r
\r
@Override\r
public HwmfRecordType getRecordType() {\r
- return HwmfRecordType.chord;\r
+ return HwmfRecordType.pie;\r
}\r
+ }\r
\r
- @Override\r
- public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
- yRadial2 = leis.readShort();\r
- xRadial2 = leis.readShort();\r
- yRadial1 = leis.readShort();\r
- xRadial1 = leis.readShort();\r
- bottomRect = leis.readShort();\r
- rightRect = leis.readShort();\r
- topRect = leis.readShort();\r
- leftRect = leis.readShort();\r
- return 8*LittleEndianConsts.SHORT_SIZE;\r
- }\r
+ /**\r
+ * The META_CHORD record draws a chord, which is defined by a region bounded by the intersection of\r
+ * an ellipse with a line segment. The chord is outlined using the pen and filled using the brush\r
+ * that are defined in the playback device context.\r
+ */\r
+ public static class WmfChord extends WmfArc {\r
\r
@Override\r
- public void draw(HwmfGraphics ctx) {\r
-\r
+ public HwmfRecordType getRecordType() {\r
+ return HwmfRecordType.chord;\r
}\r
-}\r
+ }\r
\r
\r
/**\r
\r
package org.apache.poi.hwmf.record;\r
\r
+import java.awt.Shape;\r
import java.awt.geom.Path2D;\r
import java.awt.image.BufferedImage;\r
import java.io.File;\r
import javax.imageio.ImageIO;\r
\r
import org.apache.poi.hwmf.draw.HwmfGraphics;\r
+import org.apache.poi.hwmf.record.HwmfWindowing.WmfCreateRegion;\r
import org.apache.poi.util.LittleEndianConsts;\r
import org.apache.poi.util.LittleEndianInputStream;\r
\r
* A 16-bit unsigned integer used to index into the WMF Object Table to get\r
* the region to be filled.\r
*/\r
- private int region;\r
+ private int regionIndex;\r
\r
/**\r
* A 16-bit unsigned integer used to index into the WMF Object Table to get the\r
* brush to use for filling the region.\r
*/\r
- private int brush;\r
+ private int brushIndex;\r
\r
@Override\r
public HwmfRecordType getRecordType() {\r
\r
@Override\r
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
- region = leis.readUShort();\r
- brush = leis.readUShort();\r
+ regionIndex = leis.readUShort();\r
+ brushIndex = leis.readUShort();\r
return 2*LittleEndianConsts.SHORT_SIZE;\r
}\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
+ ctx.applyObjectTableEntry(regionIndex);\r
+ ctx.applyObjectTableEntry(brushIndex);\r
+ \r
+ Shape region = ctx.getProperties().getRegion();\r
+ if (region != null) {\r
+ ctx.fill(region);\r
+ }\r
\r
}\r
}\r
* A 16-bit unsigned integer used to index into the WMF Object Table to get\r
* the region to be painted.\r
*/\r
- int region;\r
+ int regionIndex;\r
\r
public HwmfRecordType getRecordType() {\r
return HwmfRecordType.paintRegion;\r
}\r
\r
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
- region = leis.readUShort();\r
+ regionIndex = leis.readUShort();\r
return LittleEndianConsts.SHORT_SIZE;\r
}\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
- \r
+ ctx.applyObjectTableEntry(regionIndex);\r
+\r
+ Shape region = ctx.getProperties().getRegion();\r
+ if (region != null) {\r
+ ctx.fill(region);\r
+ } \r
}\r
}\r
\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ ctx.saveProperties();\r
}\r
}\r
\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ ctx.restoreProperties(nSavedDC);\r
}\r
}\r
\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ ctx.unsetObjectTableEntry(objectIndex);\r
}\r
}\r
\r
- public static class WmfCreatePatternBrush implements HwmfRecord {\r
+ public static class WmfCreatePatternBrush implements HwmfRecord, HwmfObjectTableEntry {\r
\r
private HwmfBitmap16 pattern;\r
\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
-\r
+ ctx.addObjectTableEntry(this);\r
+ }\r
+ \r
+ @Override\r
+ public void applyObject(HwmfGraphics ctx) {\r
+ HwmfDrawProperties dp = ctx.getProperties();\r
+ dp.setBrushBitmap(pattern.getImage());\r
+ dp.setBrushStyle(HwmfBrushStyle.BS_PATTERN);\r
}\r
}\r
\r
- public static class WmfCreatePenIndirect implements HwmfRecord {\r
+ public static class WmfCreatePenIndirect implements HwmfRecord, HwmfObjectTableEntry {\r
\r
private HwmfPenStyle penStyle;\r
/**\r
* A 32-bit PointS Object that specifies a point for the object dimensions.\r
- * The xcoordinate is the pen width. The y-coordinate is ignored.\r
+ * The x-coordinate is the pen width. The y-coordinate is ignored.\r
*/\r
private int xWidth;\r
@SuppressWarnings("unused")\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
+ ctx.addObjectTableEntry(this);\r
+ }\r
+ \r
+ @Override\r
+ public void applyObject(HwmfGraphics ctx) {\r
HwmfDrawProperties p = ctx.getProperties();\r
p.setPenStyle(penStyle);\r
p.setPenColor(colorRef);\r
* </tr>\r
* </table>\r
*/\r
- public static class WmfCreateBrushIndirect implements HwmfRecord {\r
+ public static class WmfCreateBrushIndirect implements HwmfRecord, HwmfObjectTableEntry {\r
private HwmfBrushStyle brushStyle;\r
\r
private HwmfColorRef colorRef;\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
+ ctx.addObjectTableEntry(this);\r
+ }\r
+ \r
+ @Override\r
+ public void applyObject(HwmfGraphics ctx) {\r
HwmfDrawProperties p = ctx.getProperties();\r
p.setBrushStyle(brushStyle);\r
p.setBrushColor(colorRef);\r
--- /dev/null
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hwmf.record;\r
+\r
+import org.apache.poi.hwmf.draw.HwmfGraphics;\r
+\r
+/**\r
+ * Marker interface for Records, which should be added to the\r
+ * WMF object table for further selection \r
+ */\r
+public interface HwmfObjectTableEntry {\r
+ public void applyObject(HwmfGraphics ctx);\r
+}\r
/**\r
* The META_CREATEPALETTE record creates a Palette Object\r
*/\r
- public static class WmfCreatePalette extends WmfPaletteParent {\r
+ public static class WmfCreatePalette extends WmfPaletteParent implements HwmfObjectTableEntry {\r
@Override\r
public HwmfRecordType getRecordType() {\r
return HwmfRecordType.createPalette;\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
+ ctx.addObjectTableEntry(this);\r
+ }\r
+ \r
+ @Override\r
+ public void applyObject(HwmfGraphics ctx) {\r
\r
}\r
}\r
* The defaults in case the other values of the subsection aren't set are\r
* solid, round end caps, round joins and cosmetic type.\r
*/\r
-public class HwmfPenStyle {\r
+public class HwmfPenStyle implements Cloneable {\r
public enum HwmfLineCap {\r
/** Rounded ends */\r
ROUND(0, BasicStroke.CAP_ROUND),\r
public boolean isAlternateDash() {\r
return SUBSECTION_ALTERNATE.isSet(flag);\r
}\r
+\r
+\r
+ /**\r
+ * Creates a new object of the same class and with the\r
+ * same contents as this object.\r
+ * @return a clone of this instance.\r
+ * @exception OutOfMemoryError if there is not enough memory.\r
+ * @see java.lang.Cloneable\r
+ */\r
+ @Override\r
+ public HwmfPenStyle clone() {\r
+ try {\r
+ return (HwmfPenStyle)super.clone();\r
+ } catch (CloneNotSupportedException e) {\r
+ // this shouldn't happen, since we are Cloneable\r
+ throw new InternalError();\r
+ }\r
+ }\r
}\r
\r
package org.apache.poi.hwmf.record;\r
\r
+import java.awt.geom.Rectangle2D;\r
import java.io.IOException;\r
\r
import org.apache.poi.util.LittleEndianConsts;\r
public class HwmfPlaceableHeader {\r
public static int WMF_HEADER_MAGIC = 0x9AC6CDD7;\r
\r
+ final Rectangle2D bounds;\r
+ \r
protected HwmfPlaceableHeader(LittleEndianInputStream leis) throws IOException {\r
/*\r
* HWmf (2 bytes): The resource handle to the metafile, when the metafile is in memory. When\r
int y1 = leis.readShort();\r
int x2 = leis.readShort();\r
int y2 = leis.readShort();\r
+ bounds = new Rectangle2D.Double(x1, y1, x2-x1, y2-y1);\r
\r
/*\r
* Inch (2 bytes): The number of logical units per inch used to represent the image.\r
return null;\r
}\r
}\r
+\r
+ public Rectangle2D getBounds() {\r
+ return bounds;\r
+ }\r
}\r
* @see <a href="http://www.symantec.com/avcenter/reference/inside.the.windows.meta.file.format.pdf">Inside the Windows Meta File Format</a>\r
*/\r
public enum HwmfRecordType {\r
- eof(0x0000, null),\r
- realizePalette(0x0035, HwmfPalette.WmfRealizePalette.class),\r
- setPalEntries(0x0037, HwmfPalette.WmfSetPaletteEntries.class),\r
- setBkMode(0x0102, HwmfMisc.WmfSetBkMode.class),\r
- setMapMode(0x0103, HwmfMisc.WmfSetMapMode.class),\r
- setRop2(0x0104, HwmfMisc.WmfSetRop2.class),\r
- setRelabs(0x0105, HwmfMisc.WmfSetRelabs.class),\r
- setPolyFillMode(0x0106, HwmfFill.WmfSetPolyfillMode.class),\r
- setStretchBltMode(0x0107, HwmfMisc.WmfSetStretchBltMode.class),\r
- setTextCharExtra(0x0108, HwmfText.WmfSetTextCharExtra.class),\r
- restoreDc(0x0127, HwmfMisc.WmfRestoreDc.class),\r
- resizePalette(0x0139, HwmfPalette.WmfResizePalette.class),\r
- dibCreatePatternBrush(0x0142, HwmfMisc.WmfDibCreatePatternBrush.class),\r
- setLayout(0x0149, HwmfMisc.WmfSetLayout.class),\r
- setBkColor(0x0201, HwmfMisc.WmfSetBkColor.class),\r
- setTextColor(0x0209, HwmfText.WmfSetTextColor.class),\r
- offsetViewportOrg(0x0211, HwmfWindowing.WmfOffsetViewportOrg.class),\r
- lineTo(0x0213, HwmfDraw.WmfLineTo.class),\r
- moveTo(0x0214, HwmfDraw.WmfMoveTo.class),\r
- offsetClipRgn(0x0220, HwmfWindowing.WmfOffsetClipRgn.class),\r
- fillRegion(0x0228, HwmfFill.WmfFillRegion.class),\r
- setMapperFlags(0x0231, HwmfMisc.WmfSetMapperFlags.class),\r
- selectPalette(0x0234, HwmfPalette.WmfSelectPalette.class),\r
- polygon(0x0324, HwmfDraw.WmfPolygon.class),\r
- polyline(0x0325, HwmfDraw.WmfPolyline.class),\r
- setTextJustification(0x020a, HwmfText.WmfSetTextJustification.class),\r
- setWindowOrg(0x020b, HwmfWindowing.WmfSetWindowOrg.class),\r
- setWindowExt(0x020c, HwmfWindowing.WmfSetWindowExt.class),\r
- setViewportOrg(0x020d, HwmfWindowing.WmfSetViewportOrg.class),\r
- setViewportExt(0x020e, HwmfWindowing.WmfSetViewportExt.class),\r
- offsetWindowOrg(0x020f, HwmfWindowing.WmfOffsetWindowOrg.class),\r
- scaleWindowExt(0x0410, HwmfWindowing.WmfScaleWindowExt.class),\r
- scaleViewportExt(0x0412, HwmfWindowing.WmfScaleViewportExt.class), \r
- excludeClipRect(0x0415, HwmfWindowing.WmfExcludeClipRect.class),\r
- intersectClipRect(0x0416, HwmfWindowing.WmfIntersectClipRect.class),\r
- ellipse(0x0418, HwmfDraw.WmfEllipse.class),\r
- floodFill(0x0419, HwmfFill.WmfFloodFill.class),\r
- frameRegion(0x0429, HwmfDraw.WmfFrameRegion.class),\r
- animatePalette(0x0436, HwmfPalette.WmfAnimatePalette.class),\r
- textOut(0x0521, HwmfText.WmfTextOut.class),\r
- polyPolygon(0x0538, HwmfDraw.WmfPolyPolygon.class),\r
- extFloodFill(0x0548, HwmfFill.WmfExtFloodFill.class),\r
- rectangle(0x041b, HwmfDraw.WmfRectangle.class),\r
- setPixel(0x041f, HwmfDraw.WmfSetPixel.class),\r
- roundRect(0x061c, HwmfDraw.WmfRoundRect.class),\r
- patBlt(0x061d, HwmfFill.WmfPatBlt.class),\r
- saveDc(0x001e, HwmfMisc.WmfSaveDc.class),\r
- pie(0x081a, HwmfDraw.WmfPie.class),\r
- stretchBlt(0x0b23, HwmfFill.WmfStretchBlt.class),\r
- escape(0x0626, HwmfEscape.class),\r
- invertRegion(0x012a, HwmfFill.WmfInvertRegion.class),\r
- paintRegion(0x012b, HwmfFill.WmfPaintRegion.class),\r
- selectClipRegion(0x012c, HwmfWindowing.WmfSelectClipRegion.class),\r
- selectObject(0x012d, HwmfDraw.WmfSelectObject.class),\r
- setTextAlign(0x012e, HwmfText.WmfSetTextAlign.class),\r
- arc(0x0817, HwmfDraw.WmfArc.class),\r
- chord(0x0830, HwmfDraw.WmfChord.class),\r
- bitBlt(0x0922, HwmfFill.WmfBitBlt.class),\r
- extTextOut(0x0a32, HwmfText.WmfExtTextOut.class),\r
- setDibToDev(0x0d33, HwmfFill.WmfSetDibToDev.class),\r
- dibBitBlt(0x0940, HwmfFill.WmfDibBitBlt.class),\r
- dibStretchBlt(0x0b41, HwmfFill.WmfDibStretchBlt.class),\r
- stretchDib(0x0f43, HwmfFill.WmfStretchDib.class),\r
- deleteObject(0x01f0, HwmfMisc.WmfDeleteObject.class),\r
- createPalette(0x00f7, HwmfPalette.WmfCreatePalette.class),\r
- createPatternBrush(0x01f9, HwmfMisc.WmfCreatePatternBrush.class),\r
- createPenIndirect(0x02fa, HwmfMisc.WmfCreatePenIndirect.class),\r
- createFontIndirect(0x02fb, HwmfText.WmfCreateFontIndirect.class),\r
- createBrushIndirect(0x02fc, HwmfMisc.WmfCreateBrushIndirect.class),\r
- createRegion(0x06ff, HwmfWindowing.WmfCreateRegion.class);\r
+ eof(0x0000, null)\r
+ ,animatePalette(0x0436, HwmfPalette.WmfAnimatePalette.class)\r
+ ,arc(0x0817, HwmfDraw.WmfArc.class)\r
+ ,bitBlt(0x0922, HwmfFill.WmfBitBlt.class)\r
+ ,chord(0x0830, HwmfDraw.WmfChord.class)\r
+ ,createBrushIndirect(0x02fc, HwmfMisc.WmfCreateBrushIndirect.class)\r
+ ,createFontIndirect(0x02fb, HwmfText.WmfCreateFontIndirect.class)\r
+ ,createPalette(0x00f7, HwmfPalette.WmfCreatePalette.class)\r
+ ,createPatternBrush(0x01f9, HwmfMisc.WmfCreatePatternBrush.class)\r
+ ,createPenIndirect(0x02fa, HwmfMisc.WmfCreatePenIndirect.class)\r
+ ,createRegion(0x06ff, HwmfWindowing.WmfCreateRegion.class)\r
+ ,deleteObject(0x01f0, HwmfMisc.WmfDeleteObject.class)\r
+ ,dibBitBlt(0x0940, HwmfFill.WmfDibBitBlt.class)\r
+ ,dibCreatePatternBrush(0x0142, HwmfMisc.WmfDibCreatePatternBrush.class)\r
+ ,dibStretchBlt(0x0b41, HwmfFill.WmfDibStretchBlt.class)\r
+ ,ellipse(0x0418, HwmfDraw.WmfEllipse.class)\r
+ ,escape(0x0626, HwmfEscape.class)\r
+ ,excludeClipRect(0x0415, HwmfWindowing.WmfExcludeClipRect.class)\r
+ ,extFloodFill(0x0548, HwmfFill.WmfExtFloodFill.class)\r
+ ,extTextOut(0x0a32, HwmfText.WmfExtTextOut.class)\r
+ ,fillRegion(0x0228, HwmfFill.WmfFillRegion.class)\r
+ ,floodFill(0x0419, HwmfFill.WmfFloodFill.class)\r
+ ,frameRegion(0x0429, HwmfDraw.WmfFrameRegion.class)\r
+ ,intersectClipRect(0x0416, HwmfWindowing.WmfIntersectClipRect.class)\r
+ ,invertRegion(0x012a, HwmfFill.WmfInvertRegion.class)\r
+ ,lineTo(0x0213, HwmfDraw.WmfLineTo.class)\r
+ ,moveTo(0x0214, HwmfDraw.WmfMoveTo.class)\r
+ ,offsetClipRgn(0x0220, HwmfWindowing.WmfOffsetClipRgn.class)\r
+ ,offsetViewportOrg(0x0211, HwmfWindowing.WmfOffsetViewportOrg.class)\r
+ ,offsetWindowOrg(0x020f, HwmfWindowing.WmfOffsetWindowOrg.class)\r
+ ,paintRegion(0x012b, HwmfFill.WmfPaintRegion.class)\r
+ ,patBlt(0x061d, HwmfFill.WmfPatBlt.class)\r
+ ,pie(0x081a, HwmfDraw.WmfPie.class)\r
+ ,polygon(0x0324, HwmfDraw.WmfPolygon.class)\r
+ ,polyline(0x0325, HwmfDraw.WmfPolyline.class)\r
+ ,polyPolygon(0x0538, HwmfDraw.WmfPolyPolygon.class)\r
+ ,realizePalette(0x0035, HwmfPalette.WmfRealizePalette.class)\r
+ ,rectangle(0x041b, HwmfDraw.WmfRectangle.class)\r
+ ,resizePalette(0x0139, HwmfPalette.WmfResizePalette.class)\r
+ ,restoreDc(0x0127, HwmfMisc.WmfRestoreDc.class)\r
+ ,roundRect(0x061c, HwmfDraw.WmfRoundRect.class)\r
+ ,saveDc(0x001e, HwmfMisc.WmfSaveDc.class)\r
+ ,scaleViewportExt(0x0412, HwmfWindowing.WmfScaleViewportExt.class) \r
+ ,scaleWindowExt(0x0410, HwmfWindowing.WmfScaleWindowExt.class)\r
+ ,selectClipRegion(0x012c, HwmfWindowing.WmfSelectClipRegion.class)\r
+ ,selectObject(0x012d, HwmfDraw.WmfSelectObject.class)\r
+ ,selectPalette(0x0234, HwmfPalette.WmfSelectPalette.class)\r
+ ,setBkColor(0x0201, HwmfMisc.WmfSetBkColor.class)\r
+ ,setBkMode(0x0102, HwmfMisc.WmfSetBkMode.class)\r
+ ,setDibToDev(0x0d33, HwmfFill.WmfSetDibToDev.class)\r
+ ,setLayout(0x0149, HwmfMisc.WmfSetLayout.class)\r
+ ,setMapMode(0x0103, HwmfMisc.WmfSetMapMode.class)\r
+ ,setMapperFlags(0x0231, HwmfMisc.WmfSetMapperFlags.class)\r
+ ,setPalEntries(0x0037, HwmfPalette.WmfSetPaletteEntries.class)\r
+ ,setPixel(0x041f, HwmfDraw.WmfSetPixel.class)\r
+ ,setPolyFillMode(0x0106, HwmfFill.WmfSetPolyfillMode.class)\r
+ ,setRelabs(0x0105, HwmfMisc.WmfSetRelabs.class)\r
+ ,setRop2(0x0104, HwmfMisc.WmfSetRop2.class)\r
+ ,setStretchBltMode(0x0107, HwmfMisc.WmfSetStretchBltMode.class)\r
+ ,setTextAlign(0x012e, HwmfText.WmfSetTextAlign.class)\r
+ ,setTextCharExtra(0x0108, HwmfText.WmfSetTextCharExtra.class)\r
+ ,setTextColor(0x0209, HwmfText.WmfSetTextColor.class)\r
+ ,setTextJustification(0x020a, HwmfText.WmfSetTextJustification.class)\r
+ ,setViewportExt(0x020e, HwmfWindowing.WmfSetViewportExt.class)\r
+ ,setViewportOrg(0x020d, HwmfWindowing.WmfSetViewportOrg.class)\r
+ ,setWindowExt(0x020c, HwmfWindowing.WmfSetWindowExt.class)\r
+ ,setWindowOrg(0x020b, HwmfWindowing.WmfSetWindowOrg.class)\r
+ ,stretchBlt(0x0b23, HwmfFill.WmfStretchBlt.class)\r
+ ,stretchDib(0x0f43, HwmfFill.WmfStretchDib.class)\r
+ ,textOut(0x0521, HwmfText.WmfTextOut.class)\r
+ ;\r
\r
public int id;\r
public Class<? extends HwmfRecord> clazz;\r
}\r
}\r
\r
- public static class WmfCreateFontIndirect implements HwmfRecord {\r
+ public static class WmfCreateFontIndirect implements HwmfRecord, HwmfObjectTableEntry {\r
private HwmfFont font;\r
\r
@Override\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
+ ctx.addObjectTableEntry(this);\r
+ }\r
+ \r
+ @Override\r
+ public void applyObject(HwmfGraphics ctx) {\r
\r
}\r
}\r
\r
package org.apache.poi.hwmf.record;\r
\r
+import java.awt.Shape;\r
+import java.awt.geom.Area;\r
import java.awt.geom.Rectangle2D;\r
+import java.awt.geom.Rectangle2D.Double;\r
import java.io.IOException;\r
import java.util.ArrayList;\r
import java.util.List;\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
Rectangle2D viewport = ctx.getProperties().getViewport();\r
- ctx.getProperties().setViewportOrg(viewport.getX()+xOffset, viewport.getY()+yOffset);\r
+ double x = (viewport == null) ? 0 : viewport.getX();\r
+ double y = (viewport == null) ? 0 : viewport.getY();\r
+ ctx.getProperties().setViewportOrg(x+xOffset, y+yOffset);\r
}\r
}\r
\r
public void draw(HwmfGraphics ctx) {\r
ctx.getProperties().setWindowOrg(x, y);\r
}\r
+\r
+ public int getY() {\r
+ return y;\r
+ }\r
+\r
+ public int getX() {\r
+ return x;\r
+ }\r
}\r
\r
/**\r
public void draw(HwmfGraphics ctx) {\r
ctx.getProperties().setWindowExt(width, height);\r
}\r
+\r
+ public int getHeight() {\r
+ return height;\r
+ }\r
+\r
+ public int getWidth() {\r
+ return width;\r
+ }\r
}\r
\r
/**\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
Rectangle2D viewport = ctx.getProperties().getViewport();\r
+ if (viewport == null) {\r
+ viewport = ctx.getProperties().getWindow();\r
+ }\r
double width = viewport.getWidth() * xNum / xDenom;\r
double height = viewport.getHeight() * yNum / yDenom;\r
ctx.getProperties().setViewportExt(width, height);\r
}\r
}\r
\r
- public static class WmfCreateRegion implements HwmfRecord {\r
+ public static class WmfCreateRegion implements HwmfRecord, HwmfObjectTableEntry {\r
/**\r
* A 16-bit signed integer. A value that MUST be ignored.\r
*/\r
\r
@Override\r
public void draw(HwmfGraphics ctx) {\r
+ ctx.addObjectTableEntry(this);\r
+ }\r
+ \r
+ @Override\r
+ public void applyObject(HwmfGraphics ctx) {\r
+ Rectangle2D lastRect = null;\r
+ Area scanLines = new Area();\r
+ int count = 0;\r
+ for (WmfScanObject so : scanObjects) {\r
+ int y = Math.min(so.top, so.bottom);\r
+ int h = Math.abs(so.top - so.bottom - 1);\r
+ for (int i=0; i<so.count/2; i++) {\r
+ int x = Math.min(so.left_scanline[i], so.right_scanline[i]);\r
+ int w = Math.abs(so.right_scanline[i] - so.left_scanline[i] - 1);\r
+ lastRect = new Rectangle2D.Double(x,y,w,h);\r
+ scanLines.add(new Area(lastRect));\r
+ count++;\r
+ }\r
+ }\r
+ \r
+ Shape region = null;\r
+ if (count > 0) {\r
+ region = (count == 1) ? lastRect : scanLines;\r
+ }\r
\r
+ ctx.getProperties().setRegion(region);\r
}\r
}\r
}\r
\r
package org.apache.poi.hwmf.usermodel;\r
\r
+import java.awt.Graphics2D;\r
+import java.awt.geom.Rectangle2D;\r
import java.io.BufferedInputStream;\r
import java.io.IOException;\r
import java.io.InputStream;\r
import java.util.Collections;\r
import java.util.List;\r
\r
+import org.apache.poi.hwmf.draw.HwmfGraphics;\r
import org.apache.poi.hwmf.record.HwmfHeader;\r
import org.apache.poi.hwmf.record.HwmfPlaceableHeader;\r
import org.apache.poi.hwmf.record.HwmfRecord;\r
import org.apache.poi.hwmf.record.HwmfRecordType;\r
+import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowExt;\r
+import org.apache.poi.hwmf.record.HwmfWindowing.WmfSetWindowOrg;\r
import org.apache.poi.util.LittleEndianInputStream;\r
\r
public class HwmfPicture {\r
- List<HwmfRecord> records = new ArrayList<HwmfRecord>();\r
- \r
- public List<HwmfRecord> getRecords() {\r
- return Collections.unmodifiableList(records);\r
- }\r
+ final List<HwmfRecord> records = new ArrayList<HwmfRecord>();\r
+ final HwmfPlaceableHeader placeableHeader;\r
+ final HwmfHeader header;\r
\r
public HwmfPicture(InputStream inputStream) throws IOException {\r
BufferedInputStream bis = new BufferedInputStream(inputStream, 10000);\r
LittleEndianInputStream leis = new LittleEndianInputStream(bis);\r
- HwmfPlaceableHeader placeableHeader = HwmfPlaceableHeader.readHeader(leis);\r
- HwmfHeader header = new HwmfHeader(leis);\r
+ placeableHeader = HwmfPlaceableHeader.readHeader(leis);\r
+ header = new HwmfHeader(leis);\r
\r
for (;;) {\r
// recordSize in DWORDs\r
}\r
}\r
\r
+ public List<HwmfRecord> getRecords() {\r
+ return Collections.unmodifiableList(records);\r
+ }\r
\r
+ public void draw(Graphics2D ctx) {\r
+ HwmfGraphics g = new HwmfGraphics(ctx, getBounds());\r
+ for (HwmfRecord r : records) {\r
+ r.draw(g);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Returns the bounding box in device-independent units. Usually this is taken from the placeable header.\r
+ * \r
+ * @return the bounding box\r
+ */\r
+ public Rectangle2D getBounds() {\r
+ if (placeableHeader != null) {\r
+ return placeableHeader.getBounds();\r
+ } else {\r
+ WmfSetWindowOrg wOrg = null;\r
+ WmfSetWindowExt wExt = null;\r
+ for (HwmfRecord r : getRecords()) {\r
+ if (wOrg != null && wExt != null) {\r
+ break;\r
+ }\r
+ if (r instanceof WmfSetWindowOrg) {\r
+ wOrg = (WmfSetWindowOrg)r;\r
+ } else if (r instanceof WmfSetWindowExt) {\r
+ wExt = (WmfSetWindowExt)r;\r
+ }\r
+ }\r
+ if (wOrg == null || wExt == null) {\r
+ throw new RuntimeException("invalid wmf file - window records are incomplete.");\r
+ }\r
+ return new Rectangle2D.Double(wOrg.getX(), wOrg.getY(), wExt.getWidth(), wExt.getHeight());\r
+ } \r
+ }\r
+ \r
+ public HwmfPlaceableHeader getPlaceableHeader() {\r
+ return placeableHeader;\r
+ }\r
+\r
+ public HwmfHeader getHeader() {\r
+ return header;\r
+ }\r
}\r
\r
import static org.junit.Assert.assertEquals;\r
\r
+import java.awt.Graphics2D;\r
+import java.awt.RenderingHints;\r
+import java.awt.geom.Rectangle2D;\r
import java.awt.image.BufferedImage;\r
import java.io.File;\r
import java.io.FileFilter;\r
List<HwmfRecord> records = wmf.getRecords();\r
assertEquals(581, records.size());\r
}\r
+ \r
+ @Test\r
+ @Ignore\r
+ public void paint() throws IOException {\r
+ File f = POIDataSamples.getSlideShowInstance().getFile("santa.wmf");\r
+ FileInputStream fis = new FileInputStream(f);\r
+ HwmfPicture wmf = new HwmfPicture(fis);\r
+ fis.close();\r
+ \r
+ Rectangle2D bounds = wmf.getBounds();\r
+ int width = 300;\r
+ // keep aspect ratio for height\r
+ int height = (int)(width*bounds.getHeight()/bounds.getWidth());\r
+ \r
+ BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);\r
+ Graphics2D g = bufImg.createGraphics();\r
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);\r
+ g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);\r
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);\r
+ g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);\r
+ \r
+ g.scale(width/bounds.getWidth(), height/bounds.getHeight());\r
+ g.translate(-bounds.getX(), -bounds.getY());\r
+ \r
+ wmf.draw(g);\r
+\r
+ g.dispose();\r
+ \r
+ ImageIO.write(bufImg, "PNG", new File("bla.png"));\r
+ }\r
\r
@Test\r
@Ignore\r