]> source.dussan.org Git - poi.git/commitdiff
WMF fixes
authorAndreas Beeker <kiwiwings@apache.org>
Sun, 3 Jan 2016 22:46:57 +0000 (22:46 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Sun, 3 Jan 2016 22:46:57 +0000 (22:46 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1722771 13f79535-47bb-0310-9956-ffa450edef68

15 files changed:
src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java
src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfObjectTableEntry.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPlaceableHeader.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecordType.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java
src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java
src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java

index 64ee4cc346320ee399d68754c77a7f0819effd0e..aa8a2f3cbdbc45ec1b873a8d15db318a34710b44 100644 (file)
 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
@@ -31,9 +35,9 @@ import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
 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
@@ -46,7 +50,42 @@ public class HwmfDrawProperties {
     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
@@ -62,7 +101,7 @@ public class HwmfDrawProperties {
     }\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
@@ -186,4 +225,23 @@ public class HwmfDrawProperties {
     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
index 4a07cc33047b1a444e976e9d5553041ad19def2b..2ef64a8ed9ee7130e1c48f64387c10bfef37ca0b 100644 (file)
@@ -28,25 +28,37 @@ import java.awt.geom.AffineTransform;
 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
@@ -91,7 +103,11 @@ public class HwmfGraphics {
 \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
@@ -106,16 +122,20 @@ public class HwmfGraphics {
         }\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
@@ -182,4 +202,87 @@ public class HwmfGraphics {
         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
index ff11d486291a59c89635309e3d1d3d331643f2d4..541b98219b7b6cb65726f09bcabb869f6b4072b3 100644 (file)
@@ -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.\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
@@ -53,4 +53,21 @@ public class HwmfColorRef {
     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
index a65b7b45bf50aaf54276118cce8b9e7844348a13..06f9b03af060406a77ca46bfb02d133eb745a5a3 100644 (file)
 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
@@ -55,7 +61,7 @@ public class HwmfDraw {
             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
@@ -91,7 +97,7 @@ public class HwmfDraw {
             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
@@ -135,12 +141,12 @@ public class HwmfDraw {
 \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
@@ -207,10 +213,15 @@ public class HwmfDraw {
             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
@@ -253,10 +264,10 @@ public class HwmfDraw {
             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
@@ -372,7 +383,12 @@ public class HwmfDraw {
 \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
@@ -415,7 +431,8 @@ public class HwmfDraw {
 \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
@@ -479,84 +496,16 @@ public class HwmfDraw {
 \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
@@ -622,81 +571,69 @@ public class HwmfDraw {
 \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
index 4b01deb07d74365f5ba5f8be07637a2fd8c435bb..32d34d76aae0fb575c30d117ed0a3a502f21ef95 100644 (file)
@@ -17,6 +17,7 @@
 \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
@@ -25,6 +26,7 @@ import java.io.IOException;
 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
@@ -46,13 +48,13 @@ public class HwmfFill {
          * 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
@@ -61,13 +63,20 @@ public class HwmfFill {
         \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
@@ -82,20 +91,25 @@ public class HwmfFill {
          * 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
index be59c2dd80268a0c662964a2a57d07394473a40f..adaf91cc44c1f0ff0f21c27c148774cec304d0e9 100644 (file)
@@ -44,7 +44,7 @@ public class HwmfMisc {
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
-\r
+            ctx.saveProperties();\r
         }\r
     }\r
 \r
@@ -92,7 +92,7 @@ public class HwmfMisc {
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
-\r
+            ctx.restoreProperties(nSavedDC);\r
         }\r
     }\r
 \r
@@ -431,11 +431,11 @@ public class HwmfMisc {
 \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
@@ -452,16 +452,23 @@ public class HwmfMisc {
 \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
@@ -488,6 +495,11 @@ public class HwmfMisc {
 \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
@@ -540,7 +552,7 @@ public class HwmfMisc {
      * </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
@@ -568,6 +580,11 @@ public class HwmfMisc {
 \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
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 (file)
index 0000000..f05e690
--- /dev/null
@@ -0,0 +1,28 @@
+/* ====================================================================\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
index d55f9e454e2564ddab0aee9527deb3be0c1206aa..0823e807c6647b236522d1ae139865b454d0dcc6 100644 (file)
@@ -81,7 +81,7 @@ public class HwmfPalette {
     /**\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
@@ -89,6 +89,11 @@ public class HwmfPalette {
 \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
index 4e908ac399ba79c312f99d49b7d2466c0c553b41..075a0a3c91cde9843755577915b0f0262816f917 100644 (file)
@@ -32,7 +32,7 @@ import org.apache.poi.util.BitFieldFactory;
  * 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
@@ -168,4 +168,22 @@ public class HwmfPenStyle {
     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
index edff44dffb41c452d8cfc49510929ea567f46514..451892132070ac0e2ad91c9ee4352fdbd7d2a1c9 100644 (file)
@@ -17,6 +17,7 @@
 \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
@@ -25,6 +26,8 @@ import org.apache.poi.util.LittleEndianInputStream;
 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
@@ -41,6 +44,7 @@ public class HwmfPlaceableHeader {
         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
@@ -74,4 +78,8 @@ public class HwmfPlaceableHeader {
             return null;\r
         }\r
     }\r
+\r
+    public Rectangle2D getBounds() {\r
+        return bounds;\r
+    }\r
 }\r
index b5b93097a366922fb4b3b7038ad4f9cd8b6bc561..db40bb6f8c29803dcf12bc20c36724f845e0635e 100644 (file)
@@ -23,76 +23,77 @@ package org.apache.poi.hwmf.record;
  * @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
index 5e6dde83a6cc06a59b73709cf68ead316ea7a319..167e446bef9644d0cf19418e76e54baa9cede874 100644 (file)
@@ -391,7 +391,7 @@ public class HwmfText {
         }\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
@@ -407,6 +407,11 @@ public class HwmfText {
 \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
index a6d7204583313d66ade35e36518a4620d8bece13..5af1ab826c33c7c159fc528a65bbc3814e54a034 100644 (file)
 \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
@@ -128,7 +131,9 @@ public class HwmfWindowing {
         @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
@@ -163,6 +168,14 @@ public class HwmfWindowing {
         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
@@ -199,6 +212,14 @@ public class HwmfWindowing {
         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
@@ -338,6 +359,9 @@ public class HwmfWindowing {
         @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
@@ -556,7 +580,7 @@ public class HwmfWindowing {
         }\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
@@ -642,7 +666,32 @@ public class HwmfWindowing {
 \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
index 3bea50eaf69922a05e5e1b43bc9ea1b29833a17e..7d953aa03a893303595bfb93937d2353022896c8 100644 (file)
@@ -17,6 +17,8 @@
 \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
@@ -24,24 +26,25 @@ import java.util.ArrayList;
 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
@@ -76,5 +79,50 @@ public class HwmfPicture {
         }\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
index ad585eccbd2218b5c661246d4d54287096a81909..1c9f4c1a12858473e9ee5184d4676992d5a9c4f5 100644 (file)
@@ -19,6 +19,9 @@ package org.apache.poi.hwmf;
 \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
@@ -55,6 +58,36 @@ public class TestHwmfParsing {
         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