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

12 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/HwmfDraw.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java
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/HwmfText.java
src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java
src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java

index aa8a2f3cbdbc45ec1b873a8d15db318a34710b44..8c112169f1cf44d22fde40995046a8a86d577897 100644 (file)
@@ -25,14 +25,19 @@ import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;\r
 import java.awt.image.ColorModel;\r
 import java.awt.image.WritableRaster;\r
+import java.util.List;\r
 \r
 import org.apache.poi.hwmf.record.HwmfBrushStyle;\r
 import org.apache.poi.hwmf.record.HwmfColorRef;\r
+import org.apache.poi.hwmf.record.HwmfFont;\r
 import org.apache.poi.hwmf.record.HwmfFill.WmfSetPolyfillMode.HwmfPolyfillMode;\r
 import org.apache.poi.hwmf.record.HwmfHatchStyle;\r
 import org.apache.poi.hwmf.record.HwmfMapMode;\r
 import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;\r
+import org.apache.poi.hwmf.record.HwmfPalette.PaletteEntry;\r
 import org.apache.poi.hwmf.record.HwmfPenStyle;\r
+import org.apache.poi.hwmf.record.HwmfText.HwmfTextAlignment;\r
+import org.apache.poi.hwmf.record.HwmfText.HwmfTextVerticalAlignment;\r
 \r
 public class HwmfDrawProperties {\r
     private final Rectangle2D window;\r
@@ -51,6 +56,14 @@ public class HwmfDrawProperties {
     private HwmfBkMode bkMode = HwmfBkMode.OPAQUE;\r
     private HwmfPolyfillMode polyfillMode = HwmfPolyfillMode.WINDING;\r
     private Shape region = null;\r
+    private List<PaletteEntry> palette = null;\r
+    private int paletteOffset = 0;\r
+    private HwmfFont font = null;\r
+    private HwmfColorRef textColor = new HwmfColorRef(Color.BLACK);\r
+    private HwmfTextAlignment textAlignLatin = HwmfTextAlignment.LEFT;\r
+    private HwmfTextVerticalAlignment textVAlignLatin = HwmfTextVerticalAlignment.TOP;\r
+    private HwmfTextAlignment textAlignAsian = HwmfTextAlignment.RIGHT;\r
+    private HwmfTextVerticalAlignment textVAlignAsian = HwmfTextVerticalAlignment.TOP;\r
 \r
     public HwmfDrawProperties() {\r
         window = new Rectangle2D.Double(0, 0, 1, 1);\r
@@ -84,9 +97,16 @@ public class HwmfDrawProperties {
         } else if (other.region instanceof Area) {\r
             this.region = new Area(other.region);\r
         }\r
+        this.palette = other.palette;\r
+        this.paletteOffset = other.paletteOffset;\r
+        this.font = other.font;\r
+        this.textColor = (other.textColor == null) ? null : other.textColor.clone();\r
     }\r
     \r
     public void setViewportExt(double width, double height) {\r
+        if (viewport == null) {\r
+            viewport = (Rectangle2D)window.clone();\r
+        }\r
         double x = viewport.getX();\r
         double y = viewport.getY();\r
         double w = (width != 0) ? width : viewport.getWidth();\r
@@ -244,4 +264,80 @@ public class HwmfDrawProperties {
     public void setRegion(Shape region) {\r
         this.region = region;\r
     }\r
+\r
+    /**\r
+     * Returns the current palette.\r
+     * Callers may modify the palette.\r
+     * \r
+     * @return the current palette or null, if it hasn't been set\r
+     */\r
+    public List<PaletteEntry> getPalette() {\r
+        return palette;\r
+    }\r
+\r
+    /**\r
+     * Sets the current palette.\r
+     * It's the callers duty to set a modifiable copy of the palette.\r
+     *\r
+     * @param palette\r
+     */\r
+    public void setPalette(List<PaletteEntry> palette) {\r
+        this.palette = palette;\r
+    }\r
+\r
+    public int getPaletteOffset() {\r
+        return paletteOffset;\r
+    }\r
+\r
+    public void setPaletteOffset(int paletteOffset) {\r
+        this.paletteOffset = paletteOffset;\r
+    }\r
+\r
+    public HwmfColorRef getTextColor() {\r
+        return textColor;\r
+    }\r
+\r
+    public void setTextColor(HwmfColorRef textColor) {\r
+        this.textColor = textColor;\r
+    }\r
+\r
+    public HwmfFont getFont() {\r
+        return font;\r
+    }\r
+\r
+    public void setFont(HwmfFont font) {\r
+        this.font = font;\r
+    }\r
+\r
+    public HwmfTextAlignment getTextAlignLatin() {\r
+        return textAlignLatin;\r
+    }\r
+\r
+    public void setTextAlignLatin(HwmfTextAlignment textAlignLatin) {\r
+        this.textAlignLatin = textAlignLatin;\r
+    }\r
+\r
+    public HwmfTextVerticalAlignment getTextVAlignLatin() {\r
+        return textVAlignLatin;\r
+    }\r
+\r
+    public void setTextVAlignLatin(HwmfTextVerticalAlignment textVAlignLatin) {\r
+        this.textVAlignLatin = textVAlignLatin;\r
+    }\r
+\r
+    public HwmfTextAlignment getTextAlignAsian() {\r
+        return textAlignAsian;\r
+    }\r
+\r
+    public void setTextAlignAsian(HwmfTextAlignment textAlignAsian) {\r
+        this.textAlignAsian = textAlignAsian;\r
+    }\r
+\r
+    public HwmfTextVerticalAlignment getTextVAlignAsian() {\r
+        return textVAlignAsian;\r
+    }\r
+\r
+    public void setTextVAlignAsian(HwmfTextVerticalAlignment textVAlignAsian) {\r
+        this.textVAlignAsian = textVAlignAsian;\r
+    }\r
 }\r
index a61390704ac571fda4d16b806f9a0212625a4d21..75e815f70b61af4124c2cc0fc2b1b85d207efd64 100644 (file)
@@ -25,10 +25,12 @@ import java.awt.Paint;
 import java.awt.Rectangle;\r
 import java.awt.Shape;\r
 import java.awt.TexturePaint;\r
+import java.awt.font.TextAttribute;\r
 import java.awt.geom.AffineTransform;\r
 import java.awt.geom.GeneralPath;\r
 import java.awt.geom.Rectangle2D;\r
 import java.awt.image.BufferedImage;\r
+import java.text.AttributedString;\r
 import java.util.ArrayList;\r
 import java.util.LinkedList;\r
 import java.util.List;\r
@@ -36,13 +38,13 @@ import java.util.ListIterator;
 import java.util.NoSuchElementException;\r
 \r
 import org.apache.poi.hwmf.record.HwmfBrushStyle;\r
+import org.apache.poi.hwmf.record.HwmfFont;\r
 import org.apache.poi.hwmf.record.HwmfHatchStyle;\r
 import org.apache.poi.hwmf.record.HwmfMapMode;\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
@@ -118,7 +120,7 @@ public class HwmfGraphics {
         boolean dashAlt = ps.isAlternateDash();\r
         // This value is not an integer index into the dash pattern array.\r
         // Instead, it is a floating-point value that specifies a linear distance.\r
-        float dashStart = (dashAlt && dashes.length > 1) ? dashes[0] : 0;\r
+        float dashStart = (dashAlt && dashes != null && dashes.length > 1) ? dashes[0] : 0;\r
 \r
         return new BasicStroke(width, cap, join, miterLimit, dashes, dashStart);\r
     }\r
@@ -243,8 +245,8 @@ public class HwmfGraphics {
     /**\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
+     * @param index if the index is positive, the n-th element from the start is activated.\r
+     *      If the index is negative, the n-th previous element relative to the current properties element is activated.\r
      */\r
     public void restoreProperties(int index) {\r
         if (index == 0) {\r
@@ -253,12 +255,20 @@ public class HwmfGraphics {
         int stackIndex = index;\r
         if (stackIndex < 0) {\r
             int curIdx = propStack.indexOf(prop);\r
-            assert (curIdx != -1);\r
+            if (curIdx == -1) {\r
+                // the current element is not pushed to the stacked, i.e. it's the last\r
+                curIdx = propStack.size();\r
+            }\r
             stackIndex = curIdx + index;\r
         }\r
-        prop = propStack.remove(stackIndex);\r
+        prop = propStack.get(stackIndex);\r
     }\r
 \r
+    /**\r
+     * After setting various window and viewport related properties,\r
+     * the underlying graphics context needs to be adapted.\r
+     * This methods gathers and sets the corresponding graphics transformations.\r
+     */\r
     public void updateWindowMapMode() {\r
         GraphicsConfiguration gc = graphicsCtx.getDeviceConfiguration();\r
         Rectangle2D win = prop.getWindow();\r
@@ -268,23 +278,15 @@ public class HwmfGraphics {
         switch (mapMode) {\r
         default:\r
         case MM_ANISOTROPIC:\r
-            // scale output bounds to image bounds\r
-            graphicsCtx.scale(gc.getBounds().getWidth()/bbox.getWidth(), gc.getBounds().getHeight()/bbox.getHeight());\r
-            graphicsCtx.translate(-bbox.getX(), -bbox.getY());\r
-\r
             // scale window bounds to output bounds\r
-            graphicsCtx.translate(win.getCenterX(), win.getCenterY());\r
             graphicsCtx.scale(bbox.getWidth()/win.getWidth(), bbox.getHeight()/win.getHeight());\r
-            graphicsCtx.translate(-win.getCenterX(), -win.getCenterY());\r
+            graphicsCtx.translate(-win.getX(), -win.getY());\r
             break;\r
         case MM_ISOTROPIC:\r
             // TODO: to be validated ...\r
             // like anisotropic, but use x-axis as reference\r
-            graphicsCtx.scale(gc.getBounds().getWidth()/bbox.getWidth(), gc.getBounds().getWidth()/bbox.getWidth());\r
-            graphicsCtx.translate(-bbox.getX(), -bbox.getY());\r
-            graphicsCtx.translate(win.getCenterX(), win.getCenterY());\r
             graphicsCtx.scale(bbox.getWidth()/win.getWidth(), bbox.getWidth()/win.getWidth());\r
-            graphicsCtx.translate(-win.getCenterX(), -win.getCenterY());\r
+            graphicsCtx.translate(-win.getX(), -win.getY());\r
             break;\r
         case MM_LOMETRIC:\r
         case MM_HIMETRIC:\r
@@ -294,11 +296,48 @@ public class HwmfGraphics {
             // TODO: to be validated ...\r
             graphicsCtx.transform(gc.getNormalizingTransform());\r
             graphicsCtx.scale(1./mapMode.scale, -1./mapMode.scale);\r
-            graphicsCtx.translate(-bbox.getX(), -bbox.getY());\r
+            graphicsCtx.translate(-win.getX(), -win.getY());\r
             break;\r
         case MM_TEXT:\r
             // TODO: to be validated ...\r
             break;\r
         }\r
     }\r
+    \r
+    public void drawString(String text, Rectangle2D bounds) {\r
+        HwmfFont font = prop.getFont();\r
+        if (font == null) {\r
+            return;\r
+        }\r
+        AttributedString as = new AttributedString(text);\r
+        as.addAttribute(TextAttribute.FAMILY, font.getFacename());\r
+        // TODO: fix font height calculation\r
+        as.addAttribute(TextAttribute.SIZE, Math.abs(font.getHeight()));\r
+        as.addAttribute(TextAttribute.STRIKETHROUGH, font.isStrikeOut());\r
+        if (font.isUnderline()) {\r
+            as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);\r
+        }\r
+        if (font.isItalic()) {\r
+            as.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);\r
+        }\r
+        as.addAttribute(TextAttribute.WEIGHT, font.getWeight());\r
+        \r
+        double angle = Math.toRadians(-font.getEscapement()/10.);\r
+        \r
+        \r
+        final AffineTransform at = graphicsCtx.getTransform();\r
+        try {\r
+            graphicsCtx.translate(bounds.getX(), bounds.getY());\r
+            graphicsCtx.rotate(angle);\r
+            if (prop.getBkMode() == HwmfBkMode.OPAQUE) {\r
+                // TODO: validate bounds\r
+                graphicsCtx.setBackground(prop.getBackgroundColor().getColor());\r
+                graphicsCtx.fill(bounds);\r
+            }\r
+            graphicsCtx.setColor(prop.getTextColor().getColor());\r
+            graphicsCtx.drawString(as.getIterator(), 0, 0); // (float)bounds.getX(), (float)bounds.getY());\r
+        } finally {\r
+            graphicsCtx.setTransform(at);\r
+        }\r
+    }\r
 }\r
index 4ed79ceb49a912a2cbd4966ed9ca4abc62d96181..b167d0236a70516069babe8efa908b1253ef763f 100644 (file)
 \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.Area;\r
 import java.awt.geom.Ellipse2D;\r
-import java.awt.geom.GeneralPath;\r
 import java.awt.geom.Line2D;\r
+import java.awt.geom.Path2D;\r
 import java.awt.geom.Point2D;\r
 import java.awt.geom.Rectangle2D;\r
 import java.awt.geom.RoundRectangle2D;\r
 import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
 \r
 import org.apache.poi.hwmf.draw.HwmfGraphics;\r
 import org.apache.poi.util.LittleEndianConsts;\r
@@ -114,13 +115,8 @@ public class HwmfDraw {
      */\r
     public static class WmfPolygon implements HwmfRecord {\r
 \r
-        /**\r
-         * A 16-bit signed integer that defines the number of points in the array.\r
-         */\r
-        private int numberofPoints;\r
-\r
-        short xPoints[], yPoints[];\r
-\r
+        private Path2D poly = new Path2D.Double();\r
+        \r
         @Override\r
         public HwmfRecordType getRecordType() {\r
             return HwmfRecordType.polygon;\r
@@ -128,15 +124,21 @@ public class HwmfDraw {
 \r
         @Override\r
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
-            numberofPoints = leis.readShort();\r
-            xPoints = new short[numberofPoints];\r
-            yPoints = new short[numberofPoints];\r
+            /**\r
+             * A 16-bit signed integer that defines the number of points in the array.\r
+             */\r
+            int numberofPoints = leis.readShort();\r
 \r
             for (int i=0; i<numberofPoints; i++) {\r
                 // A 16-bit signed integer that defines the horizontal (x) coordinate of the point.\r
-                xPoints[i] = leis.readShort();\r
+                int x = leis.readShort();\r
                 // A 16-bit signed integer that defines the vertical (y) coordinate of the point.\r
-                yPoints[i] = leis.readShort();\r
+                int y = leis.readShort();\r
+                if (i==0) {\r
+                    poly.moveTo(x, y);\r
+                } else {\r
+                    poly.lineTo(x, y);\r
+                }\r
             }\r
 \r
             return LittleEndianConsts.SHORT_SIZE+numberofPoints*LittleEndianConsts.INT_SIZE;\r
@@ -144,15 +146,14 @@ public class HwmfDraw {
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
-            ctx.fill(getShape());\r
+            Path2D p = getShape();\r
+            p.closePath();\r
+            p.setWindingRule(ctx.getProperties().getPolyfillMode().awtFlag);\r
+            ctx.fill(p);\r
         }\r
 \r
-        protected Polygon getShape() {\r
-            Polygon polygon = new Polygon();\r
-            for(int i = 0; i < numberofPoints; i++) {\r
-                polygon.addPoint(xPoints[i], yPoints[i]);\r
-            }\r
-            return polygon;\r
+        protected Path2D getShape() {\r
+            return (Path2D)poly.clone();\r
         }\r
     }\r
 \r
@@ -169,7 +170,9 @@ public class HwmfDraw {
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
-            ctx.draw(getShape());\r
+            Path2D p = getShape();\r
+            p.setWindingRule(ctx.getProperties().getPolyfillMode().awtFlag);\r
+            ctx.draw(p);\r
         }\r
     }\r
 \r
@@ -234,12 +237,12 @@ public class HwmfDraw {
          * A 16-bit unsigned integer used to index into the WMF Object Table to get\r
          * the region to be framed.\r
          */\r
-        private int region;\r
+        private int regionIndex;\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
          * A 16-bit signed integer that defines the height, in logical units, of the\r
          * region frame.\r
@@ -258,8 +261,8 @@ public class HwmfDraw {
 \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
             height = leis.readShort();\r
             width = leis.readShort();\r
             return 4*LittleEndianConsts.SHORT_SIZE;\r
@@ -267,7 +270,17 @@ public class HwmfDraw {
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
-\r
+            ctx.applyObjectTableEntry(brushIndex);\r
+            ctx.applyObjectTableEntry(regionIndex);\r
+            Rectangle2D inner = ctx.getProperties().getRegion().getBounds();\r
+            double x = inner.getX()-width;\r
+            double y = inner.getY()-height;\r
+            double w = inner.getWidth()+2*width;\r
+            double h = inner.getHeight()+2*height;\r
+            Rectangle2D outer = new Rectangle2D.Double(x,y,w,h);\r
+            Area frame = new Area(outer);\r
+            frame.subtract(new Area(inner));\r
+            ctx.fill(frame);\r
         }\r
     }\r
 \r
@@ -278,28 +291,8 @@ public class HwmfDraw {
      */\r
     public static class WmfPolyPolygon implements HwmfRecord {\r
 \r
-        /**\r
-         * A 16-bit unsigned integer that defines the number of polygons in the object.\r
-         */\r
-        private int numberOfPolygons;\r
-\r
-        /**\r
-         * A NumberOfPolygons array of 16-bit unsigned integers that define the number of\r
-         * points for each polygon in the object.\r
-         */\r
-        private int pointsPerPolygon[];\r
-\r
-        /**\r
-         * An array of 16-bit unsigned integers that define the coordinates of the polygons.\r
-         */\r
-        private int xPoints[][];\r
-\r
-        /**\r
-         * An array of 16-bit unsigned integers that define the coordinates of the polygons.\r
-         */\r
-        private int yPoints[][];\r
-\r
-\r
+        private List<Path2D> polyList = new ArrayList<Path2D>();\r
+        \r
         @Override\r
         public HwmfRecordType getRecordType() {\r
             return HwmfRecordType.polyPolygon;\r
@@ -308,10 +301,15 @@ public class HwmfDraw {
         @Override\r
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
             // see http://secunia.com/gfx/pdf/SA31675_BA.pdf ;)\r
-            numberOfPolygons = leis.readUShort();\r
-            pointsPerPolygon = new int[numberOfPolygons];\r
-            xPoints = new int[numberOfPolygons][];\r
-            yPoints = new int[numberOfPolygons][];\r
+            /**\r
+             * A 16-bit unsigned integer that defines the number of polygons in the object.\r
+             */\r
+            int numberOfPolygons = leis.readUShort();\r
+            /**\r
+             * A NumberOfPolygons array of 16-bit unsigned integers that define the number of\r
+             * points for each polygon in the object.\r
+             */\r
+            int[] pointsPerPolygon = new int[numberOfPolygons];\r
 \r
             int size = LittleEndianConsts.SHORT_SIZE;\r
 \r
@@ -320,16 +318,23 @@ public class HwmfDraw {
                 size += LittleEndianConsts.SHORT_SIZE;\r
             }\r
 \r
-            for (int i=0; i<numberOfPolygons; i++) {\r
-\r
-                xPoints[i] = new int[pointsPerPolygon[i]];\r
-                yPoints[i] = new int[pointsPerPolygon[i]];\r
-\r
-                for (int j=0; j<pointsPerPolygon[i]; j++) {\r
-                    xPoints[i][j] = leis.readUShort();\r
-                    yPoints[i][j] = leis.readUShort();\r
+            for (int nPoints : pointsPerPolygon) {\r
+                /**\r
+                 * An array of 16-bit unsigned integers that define the coordinates of the polygons.\r
+                 */\r
+                Path2D poly = new Path2D.Double();\r
+                for (int i=0; i<nPoints; i++) {\r
+                    int x = leis.readUShort();\r
+                    int y = leis.readUShort();\r
                     size += 2*LittleEndianConsts.SHORT_SIZE;\r
+                    if (i == 0) {\r
+                        poly.moveTo(x, y);\r
+                    } else {\r
+                        poly.lineTo(x, y);\r
+                    }\r
                 }\r
+                poly.closePath();\r
+                polyList.add(poly);\r
             }\r
 \r
             return size;\r
@@ -337,7 +342,14 @@ public class HwmfDraw {
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
-\r
+            int windingRule = ctx.getProperties().getPolyfillMode().awtFlag;\r
+            Area area = new Area();\r
+            for (Path2D poly : polyList) {\r
+                Path2D p = (Path2D)poly.clone();\r
+                p.setWindingRule(windingRule);\r
+                area.add(new Area(p));\r
+            }\r
+            ctx.draw(area);\r
         }\r
     }\r
 \r
index 32d34d76aae0fb575c30d117ed0a3a502f21ef95..d77aa49323c23fa9a4a1c75e0a8b89f1c2ac9809 100644 (file)
@@ -38,6 +38,42 @@ public class HwmfFill {
         BufferedImage getImage();\r
     }\r
     \r
+    /**\r
+     * The ColorUsage Enumeration (a 16-bit unsigned integer) specifies whether a color table\r
+     * exists in a device-independent bitmap (DIB) and how to interpret its values,\r
+     * i.e. if contains explicit RGB values or indexes into a palette.\r
+     */\r
+    public enum ColorUsage {\r
+        /**\r
+         * The color table contains RGB values\r
+         */\r
+        DIB_RGB_COLORS(0x0000),\r
+        /**\r
+         * The color table contains 16-bit indices into the current logical palette in\r
+         * the playback device context.\r
+         */\r
+        DIB_PAL_COLORS(0x0001),\r
+        /**\r
+         * No color table exists. The pixels in the DIB are indices into the current\r
+         * logical palette in the playback device context.\r
+         */\r
+        DIB_PAL_INDICES(0x0002)\r
+        ;\r
+\r
+\r
+        int flag;\r
+        ColorUsage(int flag) {\r
+            this.flag = flag;\r
+        }\r
+\r
+        static ColorUsage valueOf(int flag) {\r
+            for (ColorUsage bs : values()) {\r
+                if (bs.flag == flag) return bs;\r
+            }\r
+            return null;\r
+        }\r
+    }\r
+    \r
     \r
     /**\r
      * The META_FILLREGION record fills a region using a specified brush.\r
@@ -482,12 +518,8 @@ public class HwmfFill {
         /**\r
          * A 16-bit unsigned integer that defines whether the Colors field of the\r
          * DIB contains explicit RGB values or indexes into a palette.\r
-         * This value MUST be in the ColorUsage Enumeration:\r
-         * DIB_RGB_COLORS = 0x0000,\r
-         * DIB_PAL_COLORS = 0x0001,\r
-         * DIB_PAL_INDICES = 0x0002\r
          */\r
-        private int colorUsage;\r
+        private ColorUsage colorUsage;\r
         /**\r
          * A 16-bit signed integer that defines the height, in logical units, of the\r
          * source rectangle.\r
@@ -548,7 +580,7 @@ public class HwmfFill {
             rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);\r
             assert(rasterOpCode == rasterOperation.opCode);\r
 \r
-            colorUsage = leis.readUShort();\r
+            colorUsage = ColorUsage.valueOf(leis.readUShort());\r
             srcHeight = leis.readShort();\r
             srcWidth = leis.readShort();\r
             ySrc = leis.readShort();\r
@@ -679,12 +711,8 @@ public class HwmfFill {
         /**\r
          * A 16-bit unsigned integer that defines whether the Colors field of the\r
          * DIB contains explicit RGB values or indexes into a palette.\r
-         * This MUST be one of the values in the ColorUsage Enumeration:\r
-         * DIB_RGB_COLORS = 0x0000,\r
-         * DIB_PAL_COLORS = 0x0001,\r
-         * DIB_PAL_INDICES = 0x0002\r
          */\r
-        private int colorUsage;  \r
+        private ColorUsage colorUsage;  \r
         /**\r
          * A 16-bit unsigned integer that defines the number of scan lines in the source.\r
          */\r
@@ -736,7 +764,7 @@ public class HwmfFill {
         \r
         @Override\r
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
-            colorUsage = leis.readUShort();\r
+            colorUsage = ColorUsage.valueOf(leis.readUShort());\r
             scanCount = leis.readUShort();\r
             startScan = leis.readUShort();\r
             yDib = leis.readUShort();\r
index d1a99e641a3ad49d50c0487e77222accaad40424..d23b2381b6e8d2e227556dcbe56f54a43f0752eb 100644 (file)
@@ -497,4 +497,64 @@ public class HwmfFont {
         \r
         return 5*LittleEndianConsts.SHORT_SIZE+8*LittleEndianConsts.BYTE_SIZE+readBytes;\r
     }\r
+\r
+    public int getHeight() {\r
+        return height;\r
+    }\r
+\r
+    public int getWidth() {\r
+        return width;\r
+    }\r
+\r
+    public int getEscapement() {\r
+        return escapement;\r
+    }\r
+\r
+    public int getOrientation() {\r
+        return orientation;\r
+    }\r
+\r
+    public int getWeight() {\r
+        return weight;\r
+    }\r
+\r
+    public boolean isItalic() {\r
+        return italic;\r
+    }\r
+\r
+    public boolean isUnderline() {\r
+        return underline;\r
+    }\r
+\r
+    public boolean isStrikeOut() {\r
+        return strikeOut;\r
+    }\r
+\r
+    public WmfCharset getCharSet() {\r
+        return charSet;\r
+    }\r
+\r
+    public WmfOutPrecision getOutPrecision() {\r
+        return outPrecision;\r
+    }\r
+\r
+    public WmfClipPrecision getClipPrecision() {\r
+        return clipPrecision;\r
+    }\r
+\r
+    public WmfFontQuality getQuality() {\r
+        return quality;\r
+    }\r
+\r
+    public WmfFontFamilyClass getFamily() {\r
+        return family;\r
+    }\r
+\r
+    public WmfFontPitch getPitch() {\r
+        return pitch;\r
+    }\r
+\r
+    public String getFacename() {\r
+        return facename;\r
+    }\r
 }\r
index 749faa29bd87c5a24a6b155b662b7d4cff3a401a..90fd70dced07c0d27806145ee04322df6257da09 100644 (file)
@@ -22,6 +22,7 @@ import java.io.IOException;
 \r
 import org.apache.poi.hwmf.draw.HwmfDrawProperties;\r
 import org.apache.poi.hwmf.draw.HwmfGraphics;\r
+import org.apache.poi.hwmf.record.HwmfFill.ColorUsage;\r
 import org.apache.poi.hwmf.record.HwmfFill.HwmfImageRecord;\r
 import org.apache.poi.util.LittleEndianConsts;\r
 import org.apache.poi.util.LittleEndianInputStream;\r
@@ -349,12 +350,9 @@ public class HwmfMisc {
          * If the Style field specifies BS_PATTERN, a ColorUsage value of DIB_RGB_COLORS MUST be\r
          * used regardless of the contents of this field.\r
          *\r
-         * If the Style field specified anything but BS_PATTERN, this field MUST be one of the values:\r
-         * DIB_RGB_COLORS = 0x0000,\r
-         * DIB_PAL_COLORS = 0x0001,\r
-         * DIB_PAL_INDICES = 0x0002\r
+         * If the Style field specified anything but BS_PATTERN, this field MUST be one of the ColorUsage values.\r
          */\r
-        private int colorUsage;\r
+        private ColorUsage colorUsage;\r
 \r
         private HwmfBitmapDib patternDib;\r
         private HwmfBitmap16 pattern16;\r
@@ -367,7 +365,7 @@ public class HwmfMisc {
         @Override\r
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
             style = HwmfBrushStyle.valueOf(leis.readUShort());\r
-            colorUsage = leis.readUShort();\r
+            colorUsage = ColorUsage.valueOf(leis.readUShort());\r
             int size = 2*LittleEndianConsts.SHORT_SIZE;\r
             switch (style) {\r
             case BS_SOLID:\r
index ae403d5f13bb2aa28d1af4c6737c06f5105edd59..42e661b17eebd5c8e7a393800e6a04cd951bb2d9 100644 (file)
@@ -19,106 +19,121 @@ package org.apache.poi.hwmf.record;
 \r
 import java.awt.Color;\r
 import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
 \r
+import org.apache.poi.hwmf.draw.HwmfDrawProperties;\r
 import org.apache.poi.hwmf.draw.HwmfGraphics;\r
+import org.apache.poi.util.BitField;\r
+import org.apache.poi.util.BitFieldFactory;\r
 import org.apache.poi.util.LittleEndianConsts;\r
 import org.apache.poi.util.LittleEndianInputStream;\r
 \r
 public class HwmfPalette {\r
-    \r
+\r
     public static class PaletteEntry {\r
-        enum PaletteEntryFlag {\r
-            /**\r
-             * Specifies that the logical palette entry be used for palette animation. This value\r
-             * prevents other windows from matching colors to the palette entry because the color frequently\r
-             * changes. If an unused system-palette entry is available, the color is placed in that entry.\r
-             * Otherwise, the color is not available for animation.\r
-             */\r
-            PC_RESERVED(0x01),\r
-            /**\r
-             * Specifies that the low-order word of the logical palette entry designates a hardware\r
-             * palette index. This value allows the application to show the contents of the display device palette.\r
-             */\r
-            PC_EXPLICIT(0x02),\r
-            /**\r
-             * Specifies that the color be placed in an unused entry in the system palette\r
-             * instead of being matched to an existing color in the system palette. If there are no unused entries\r
-             * in the system palette, the color is matched normally. Once this color is in the system palette,\r
-             * colors in other logical palettes can be matched to this color.\r
-             */\r
-            PC_NOCOLLAPSE(0x04)\r
-            ;\r
-            \r
-            int flag;\r
-            \r
-            PaletteEntryFlag(int flag) {\r
-                this.flag = flag;\r
-            }\r
+        private static final BitField PC_RESERVED   = BitFieldFactory.getInstance(0x01);\r
+        private static final BitField PC_EXPLICIT   = BitFieldFactory.getInstance(0x02);\r
+        private static final BitField PC_NOCOLLAPSE = BitFieldFactory.getInstance(0x04);\r
 \r
-            static PaletteEntryFlag valueOf(int flag) {\r
-                for (PaletteEntryFlag pef : values()) {\r
-                    if (pef.flag == flag) return pef;\r
-                }\r
-                return null;\r
-            }        \r
+        private int values;\r
+        private Color colorRef;\r
 \r
+        private PaletteEntry() {\r
+            this.values = PC_RESERVED.set(0);\r
+            this.colorRef = Color.BLACK;\r
         }\r
-        \r
-        // Values (1 byte):  An 8-bit unsigned integer that defines how the palette entry is to be used. \r
-        // The Values field MUST be 0x00 or one of the values in the PaletteEntryFlag Enumeration table.\r
-        // Blue (1 byte): An 8-bit unsigned integer that defines the blue intensity value for the palette entry.\r
-        // Green (1 byte): An 8-bit unsigned integer that defines the green intensity value for the palette entry.\r
-        // Red (1 byte): An 8-bit unsigned integer that defines the red intensity value for the palette entry.\r
-        private PaletteEntryFlag values;\r
-        private Color colorRef;\r
-        \r
+\r
+        private PaletteEntry(PaletteEntry other) {\r
+            this.values = other.values;\r
+            this.colorRef = other.colorRef;\r
+        }\r
+\r
         public int init(LittleEndianInputStream leis) throws IOException {\r
-            values = PaletteEntryFlag.valueOf(leis.readUByte());\r
+            // Values (1 byte):  An 8-bit unsigned integer that defines how the palette entry is to be used.\r
+            // The Values field MUST be 0x00 or one of the values in the PaletteEntryFlag Enumeration table.\r
+            values = leis.readUByte();\r
+            // Blue (1 byte): An 8-bit unsigned integer that defines the blue intensity value for the palette entry.\r
             int blue = leis.readUByte();\r
+            // Green (1 byte): An 8-bit unsigned integer that defines the green intensity value for the palette entry.\r
             int green = leis.readUByte();\r
+            // Red (1 byte): An 8-bit unsigned integer that defines the red intensity value for the palette entry.\r
             int red = leis.readUByte();\r
             colorRef = new Color(red, green, blue);\r
-            \r
+\r
             return 4*LittleEndianConsts.BYTE_SIZE;\r
         }\r
+\r
+        /**\r
+         * Specifies that the logical palette entry be used for palette animation. This value\r
+         * prevents other windows from matching colors to the palette entry because the color frequently\r
+         * changes. If an unused system-palette entry is available, the color is placed in that entry.\r
+         * Otherwise, the color is not available for animation.\r
+         */\r
+        public boolean isReserved() {\r
+            return PC_RESERVED.isSet(values);\r
+        }\r
+\r
+        /**\r
+         * Specifies that the low-order word of the logical palette entry designates a hardware\r
+         * palette index. This value allows the application to show the contents of the display device palette.\r
+         */\r
+        public boolean isExplicit() {\r
+            return PC_EXPLICIT.isSet(values);\r
+        }\r
+\r
+        /**\r
+         * Specifies that the color be placed in an unused entry in the system palette\r
+         * instead of being matched to an existing color in the system palette. If there are no unused entries\r
+         * in the system palette, the color is matched normally. Once this color is in the system palette,\r
+         * colors in other logical palettes can be matched to this color.\r
+         */\r
+        public boolean isNoCollapse() {\r
+            return PC_NOCOLLAPSE.isSet(values);\r
+        }\r
     }\r
-    \r
+\r
     public static abstract class WmfPaletteParent implements HwmfRecord  {\r
-    \r
+\r
         /**\r
          * Start (2 bytes):  A 16-bit unsigned integer that defines the offset into the Palette Object when\r
          * used with the META_SETPALENTRIES and META_ANIMATEPALETTE record types.\r
          * When used with META_CREATEPALETTE, it MUST be 0x0300\r
          */\r
         private int start;\r
-        \r
-        /**\r
-         * NumberOfEntries (2 bytes):  A 16-bit unsigned integer that defines the number of objects in\r
-         * aPaletteEntries.  \r
-         */\r
-        private int numberOfEntries;\r
-        \r
-        private PaletteEntry entries[];\r
-        \r
+\r
+        private List<PaletteEntry> palette = new ArrayList<PaletteEntry>();\r
+\r
         @Override\r
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
             start = leis.readUShort();\r
-            numberOfEntries = leis.readUShort();\r
+            /**\r
+             * NumberOfEntries (2 bytes):  A 16-bit unsigned integer that defines the number of objects in\r
+             * aPaletteEntries.\r
+             */\r
+            int numberOfEntries = leis.readUShort();\r
             int size = 2*LittleEndianConsts.SHORT_SIZE;\r
-            entries = new PaletteEntry[numberOfEntries];\r
             for (int i=0; i<numberOfEntries; i++) {\r
-                entries[i] = new PaletteEntry();\r
-                size += entries[i].init(leis);\r
+                PaletteEntry pe = new PaletteEntry();\r
+                size += pe.init(leis);\r
+                palette.add(pe);\r
             }\r
             return size;\r
         }\r
 \r
-        @Override\r
-        public void draw(HwmfGraphics ctx) {\r
+        protected List<PaletteEntry> getPaletteCopy() {\r
+            List<PaletteEntry> newPalette = new ArrayList<PaletteEntry>();\r
+            for (PaletteEntry et : palette) {\r
+                newPalette.add(new PaletteEntry(et));\r
+            }\r
+            return newPalette;\r
+        }\r
 \r
+        protected int getPaletteStart() {\r
+            return start;\r
         }\r
     }\r
-    \r
+\r
     /**\r
      * The META_CREATEPALETTE record creates a Palette Object\r
      */\r
@@ -132,10 +147,10 @@ public class HwmfPalette {
         public void draw(HwmfGraphics ctx) {\r
             ctx.addObjectTableEntry(this);\r
         }\r
-        \r
+\r
         @Override\r
         public void applyObject(HwmfGraphics ctx) {\r
-\r
+            ctx.getProperties().setPalette(getPaletteCopy());\r
         }\r
     }\r
 \r
@@ -151,35 +166,62 @@ public class HwmfPalette {
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
-\r
+            HwmfDrawProperties props = ctx.getProperties();\r
+            List<PaletteEntry> palette = props.getPalette();\r
+            if (palette == null) {\r
+                palette = new ArrayList<PaletteEntry>();\r
+            }\r
+            int start = getPaletteStart();\r
+            for (int i=palette.size(); i<start; i++) {\r
+                palette.add(new PaletteEntry());\r
+            }\r
+            int index = start;\r
+            for (PaletteEntry palCopy : getPaletteCopy()) {\r
+                if (palette.size() <= index) {\r
+                    palette.add(palCopy);\r
+                } else {\r
+                    palette.set(index, palCopy);\r
+                }\r
+                index++;\r
+            }\r
+            props.setPalette(palette);\r
         }\r
     }\r
-    \r
+\r
     /**\r
      * The META_RESIZEPALETTE record redefines the size of the logical palette that is defined in the\r
      * playback device context.\r
      */\r
     public static class WmfResizePalette implements HwmfRecord {\r
         /**\r
-         * A 16-bit unsigned integer that defines the number of entries in \r
+         * A 16-bit unsigned integer that defines the number of entries in\r
          * the logical palette.\r
          */\r
         int numberOfEntries;\r
-        \r
+\r
         @Override\r
         public HwmfRecordType getRecordType() {\r
             return HwmfRecordType.resizePalette;\r
         }\r
-        \r
+\r
         @Override\r
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
             numberOfEntries = leis.readUShort();\r
             return LittleEndianConsts.SHORT_SIZE;\r
-        }        \r
+        }\r
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
-\r
+            HwmfDrawProperties props = ctx.getProperties();\r
+            List<PaletteEntry> palette = props.getPalette();\r
+            if (palette == null) {\r
+                palette = new ArrayList<PaletteEntry>();\r
+            }\r
+            for (int i=palette.size(); i<numberOfEntries; i++) {\r
+                palette.add(new PaletteEntry());\r
+            }\r
+            palette = palette.subList(0, numberOfEntries);\r
+            props.setPalette(palette);\r
         }\r
     }\r
 \r
@@ -191,22 +233,22 @@ public class HwmfPalette {
          * A 16-bit unsigned integer used to index into the WMF Object Table to get\r
          * the Palette Object to be selected.\r
          */\r
-        private int palette;\r
+        private int paletteIndex;\r
 \r
         @Override\r
         public HwmfRecordType getRecordType() {\r
             return HwmfRecordType.selectPalette;\r
         }\r
-        \r
+\r
         @Override\r
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {\r
-            palette = leis.readUShort();\r
+            paletteIndex = leis.readUShort();\r
             return LittleEndianConsts.SHORT_SIZE;\r
-        }        \r
+        }\r
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
-\r
+            ctx.applyObjectTableEntry(paletteIndex);\r
         }\r
     }\r
 \r
@@ -234,7 +276,7 @@ public class HwmfPalette {
     /**\r
      * The META_ANIMATEPALETTE record redefines entries in the logical palette that\r
      * is defined in the playback device context with the specified Palette object\r
-     * \r
+     *\r
      * The logical palette that is specified by the Palette object in this record is the\r
      * source of the palette changes, and the logical palette that is currently selected\r
      * into the playback device context is the destination. Entries in the destination\r
@@ -251,7 +293,28 @@ public class HwmfPalette {
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
-\r
+            HwmfDrawProperties props = ctx.getProperties();\r
+            List<PaletteEntry> dest = props.getPalette();\r
+            List<PaletteEntry> src = getPaletteCopy();\r
+            int start = getPaletteStart();\r
+            if (dest == null) {\r
+                dest = new ArrayList<PaletteEntry>();\r
+            }\r
+            for (int i=dest.size(); i<start; i++) {\r
+                dest.add(new PaletteEntry());\r
+            }\r
+            for (int i=0; i<src.size(); i++) {\r
+                PaletteEntry pe = src.get(i);\r
+                if (dest.size() <= start+i) {\r
+                    dest.add(pe);\r
+                } else {\r
+                    PaletteEntry peDst = dest.get(start+i);\r
+                    if (peDst.isReserved()) {\r
+                        dest.set(start+i, pe);\r
+                    }\r
+                }\r
+            }\r
+            props.setPalette(dest);\r
         }\r
     }\r
 }\r
index 075a0a3c91cde9843755577915b0f0262816f917..fb2ce122e138ab4a365792b5860da6a477816230 100644 (file)
@@ -86,7 +86,7 @@ public class HwmfPenStyle implements Cloneable {
         /**\r
          * The pen is solid.\r
          */\r
-        SOLID(0x0000, 10),\r
+        SOLID(0x0000, null),\r
         /**\r
          * The pen is dashed. (-----) \r
          */\r
@@ -106,19 +106,19 @@ public class HwmfPenStyle implements Cloneable {
         /**\r
          * The pen is invisible.\r
          */\r
-        NULL(0x0005),\r
+        NULL(0x0005, null),\r
         /**\r
          * The pen is solid. When this pen is used in any drawing record that takes a\r
          * bounding rectangle, the dimensions of the figure are shrunk so that it fits\r
          * entirely in the bounding rectangle, taking into account the width of the pen.\r
          */\r
-        INSIDEFRAME(0x0006, 10),\r
+        INSIDEFRAME(0x0006, null),\r
         /**\r
          * The pen uses a styling array supplied by the user.\r
          * (this is currently not supported and drawn as solid ... no idea where the user\r
          * styling is supposed to come from ...)\r
          */\r
-        USERSTYLE(0x0007, 10);\r
+        USERSTYLE(0x0007, null);\r
         \r
 \r
         public int wmfFlag;\r
index 451892132070ac0e2ad91c9ee4352fdbd7d2a1c9..ea223501356fc1d46fe1ef6ff8670f0cb49be8cd 100644 (file)
@@ -27,6 +27,7 @@ public class HwmfPlaceableHeader {
     public static int WMF_HEADER_MAGIC = 0x9AC6CDD7;\r
     \r
     final Rectangle2D bounds;\r
+    final int unitsPerInch;\r
     \r
     protected HwmfPlaceableHeader(LittleEndianInputStream leis) throws IOException {\r
         /*\r
@@ -53,7 +54,7 @@ public class HwmfPlaceableHeader {
          * Thus, a value of 720 specifies that the image SHOULD be rendered at twice its normal size,\r
          * and a value of 2880 specifies that the image SHOULD be rendered at half its normal size.\r
          */\r
-        int inch = leis.readShort();\r
+        unitsPerInch = leis.readShort();\r
         \r
         /*\r
          * Reserved (4 bytes):  A field that is not used and MUST be set to 0x00000000.\r
@@ -82,4 +83,8 @@ public class HwmfPlaceableHeader {
     public Rectangle2D getBounds() {\r
         return bounds;\r
     }\r
+\r
+    public int getUnitsPerInch() {\r
+        return unitsPerInch;\r
+    }\r
 }\r
index 167e446bef9644d0cf19418e76e54baa9cede874..8c6c41549b5e262face5b1340c02d35a7085530d 100644 (file)
 \r
 package org.apache.poi.hwmf.record;\r
 \r
+import java.awt.geom.Rectangle2D;\r
 import java.io.IOException;\r
+import java.text.AttributedString;\r
 \r
+import org.apache.poi.hwmf.draw.HwmfDrawProperties;\r
 import org.apache.poi.hwmf.draw.HwmfGraphics;\r
 import org.apache.poi.hwmf.record.HwmfMisc.WmfSetMapMode;\r
+import org.apache.poi.util.BitField;\r
+import org.apache.poi.util.BitFieldFactory;\r
 import org.apache.poi.util.LittleEndianConsts;\r
 import org.apache.poi.util.LittleEndianInputStream;\r
 import org.apache.poi.util.LocaleUtil;\r
@@ -80,7 +85,7 @@ public class HwmfText {
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
-\r
+            ctx.getProperties().setTextColor(colorRef);\r
         }\r
     }\r
     \r
@@ -168,7 +173,8 @@ public class HwmfText {
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
-\r
+            Rectangle2D bounds = new Rectangle2D.Double(xStart, yStart, 0, 0);\r
+            ctx.drawString(text, bounds);\r
         }\r
     }\r
     \r
@@ -304,74 +310,125 @@ public class HwmfText {
         }\r
     }\r
     \r
-\r
+    public enum HwmfTextAlignment {\r
+        LEFT,\r
+        RIGHT,\r
+        CENTER;\r
+    }\r
     \r
+    public enum HwmfTextVerticalAlignment {\r
+        TOP,\r
+        BOTTOM,\r
+        BASELINE;\r
+    }\r
     \r
     /**\r
      * The META_SETTEXTALIGN record defines text-alignment values in the playback device context.\r
      */\r
     public static class WmfSetTextAlign implements HwmfRecord {\r
         \r
-        /**\r
-         * A 16-bit unsigned integer that defines text alignment.\r
-         * This value MUST be a combination of one or more TextAlignmentMode Flags\r
-         * for text with a horizontal baseline, and VerticalTextAlignmentMode Flags\r
-         * for text with a vertical baseline.\r
-         * \r
-         * TextAlignmentMode Flags:\r
-         * TA_NOUPDATECP (0x0000):\r
+        // ***********************************************************************************\r
+        // TextAlignmentMode Flags:\r
+        // ***********************************************************************************\r
+        \r
+        /** \r
          * The drawing position in the playback device context MUST NOT be updated after each\r
          * text output call. The reference point MUST be passed to the text output function.\r
-         * \r
-         * TA_LEFT (0x0000):\r
+         */\r
+        @SuppressWarnings("unused")\r
+        private static final BitField TA_NOUPDATECP = BitFieldFactory.getInstance(0x0000);\r
+        \r
+        /**\r
          * The reference point MUST be on the left edge of the bounding rectangle.\r
-         * \r
-         * TA_TOP (0x0000):\r
+         */\r
+        @SuppressWarnings("unused")\r
+        private static final BitField TA_LEFT = BitFieldFactory.getInstance(0x0000);\r
+        \r
+        /**\r
          * The reference point MUST be on the top edge of the bounding rectangle.\r
-         * \r
-         * TA_UPDATECP (0x0001):\r
+         */\r
+        @SuppressWarnings("unused")\r
+        private static final BitField TA_TOP = BitFieldFactory.getInstance(0x0000);\r
+        \r
+        /**\r
          * The drawing position in the playback device context MUST be updated after each text\r
          * output call. It MUST be used as the reference point.\r
-         * \r
-         * TA_RIGHT (0x0002):\r
+         */\r
+        @SuppressWarnings("unused")\r
+        private static final BitField TA_UPDATECP = BitFieldFactory.getInstance(0x0001);\r
+        \r
+        /**\r
          * The reference point MUST be on the right edge of the bounding rectangle.\r
-         * \r
-         * TA_CENTER (0x0006):\r
+         */\r
+        private static final BitField TA_RIGHT = BitFieldFactory.getInstance(0x0002);\r
+        \r
+        /**\r
          * The reference point MUST be aligned horizontally with the center of the bounding\r
          * rectangle.\r
-         * \r
-         * TA_BOTTOM (0x0008):\r
+         */\r
+        private static final BitField TA_CENTER = BitFieldFactory.getInstance(0x0006);\r
+        \r
+        /**\r
          * The reference point MUST be on the bottom edge of the bounding rectangle.\r
-         * \r
-         * TA_BASELINE (0x0018):\r
+         */\r
+        private static final BitField TA_BOTTOM = BitFieldFactory.getInstance(0x0008);\r
+        \r
+        /**\r
          * The reference point MUST be on the baseline of the text.\r
-         * \r
-         * TA_RTLREADING (0x0100):\r
+         */\r
+        private static final BitField TA_BASELINE = BitFieldFactory.getInstance(0x0018);\r
+        \r
+        /**\r
          * The text MUST be laid out in right-to-left reading order, instead of the default\r
-         * left-toright order. This SHOULD be applied only when the font that is defined in the\r
+         * left-to-right order. This SHOULD be applied only when the font that is defined in the\r
          * playback device context is either Hebrew or Arabic.\r
-         * \r
-         * \r
-         * VerticalTextAlignmentMode Flags (e.g. for Kanji fonts)\r
-         * VTA_TOP (0x0000):\r
+         */\r
+        @SuppressWarnings("unused")\r
+        private static final BitField TA_RTLREADING = BitFieldFactory.getInstance(0x0100);\r
+        \r
+        // ***********************************************************************************\r
+        // VerticalTextAlignmentMode Flags (e.g. for Kanji fonts)\r
+        // ***********************************************************************************\r
+        \r
+        /**\r
          * The reference point MUST be on the top edge of the bounding rectangle.\r
-         * \r
-         * VTA_RIGHT (0x0000):\r
+         */\r
+        @SuppressWarnings("unused")\r
+        private static final BitField VTA_TOP = BitFieldFactory.getInstance(0x0000);\r
+        \r
+        /**\r
          * The reference point MUST be on the right edge of the bounding rectangle.\r
-         * \r
-         * VTA_BOTTOM (0x0002):\r
+         */\r
+        @SuppressWarnings("unused")\r
+        private static final BitField VTA_RIGHT = BitFieldFactory.getInstance(0x0000);\r
+        \r
+        /**\r
          * The reference point MUST be on the bottom edge of the bounding rectangle.\r
-         * \r
-         * VTA_CENTER (0x0006):\r
+         */\r
+        private static final BitField VTA_BOTTOM = BitFieldFactory.getInstance(0x0002);\r
+        \r
+        /**\r
          * The reference point MUST be aligned vertically with the center of the bounding\r
          * rectangle.\r
-         * \r
-         * VTA_LEFT (0x0008):\r
+         */\r
+        private static final BitField VTA_CENTER = BitFieldFactory.getInstance(0x0006);\r
+        \r
+        /**\r
          * The reference point MUST be on the left edge of the bounding rectangle.\r
-         * \r
-         * VTA_BASELINE (0x0018):\r
+         */\r
+        private static final BitField VTA_LEFT = BitFieldFactory.getInstance(0x0008);\r
+        \r
+        /**\r
          * The reference point MUST be on the baseline of the text.\r
          */\r
+        private static final BitField VTA_BASELINE = BitFieldFactory.getInstance(0x0018);\r
+        \r
+        /**\r
+         * A 16-bit unsigned integer that defines text alignment.\r
+         * This value MUST be a combination of one or more TextAlignmentMode Flags\r
+         * for text with a horizontal baseline, and VerticalTextAlignmentMode Flags\r
+         * for text with a vertical baseline.\r
+         */\r
         private int textAlignmentMode;\r
         \r
         @Override\r
@@ -387,7 +444,38 @@ public class HwmfText {
 \r
         @Override\r
         public void draw(HwmfGraphics ctx) {\r
+            HwmfDrawProperties props = ctx.getProperties();\r
+            if (TA_CENTER.isSet(textAlignmentMode)) {\r
+                props.setTextAlignLatin(HwmfTextAlignment.CENTER);\r
+            } else if (TA_RIGHT.isSet(textAlignmentMode)) {\r
+                props.setTextAlignLatin(HwmfTextAlignment.RIGHT);\r
+            } else {\r
+                props.setTextAlignLatin(HwmfTextAlignment.LEFT);\r
+            }\r
+\r
+            if (VTA_CENTER.isSet(textAlignmentMode)) {\r
+                props.setTextAlignAsian(HwmfTextAlignment.CENTER);\r
+            } else if (VTA_LEFT.isSet(textAlignmentMode)) {\r
+                props.setTextAlignAsian(HwmfTextAlignment.LEFT);\r
+            } else {\r
+                props.setTextAlignAsian(HwmfTextAlignment.RIGHT);\r
+            }\r
+\r
+            if (TA_BASELINE.isSet(textAlignmentMode)) {\r
+                props.setTextVAlignLatin(HwmfTextVerticalAlignment.BASELINE);\r
+            } else if (TA_BOTTOM.isSet(textAlignmentMode)) {\r
+                props.setTextVAlignLatin(HwmfTextVerticalAlignment.BOTTOM);\r
+            } else {\r
+                props.setTextVAlignLatin(HwmfTextVerticalAlignment.TOP);\r
+            }\r
 \r
+            if (VTA_BASELINE.isSet(textAlignmentMode)) {\r
+                props.setTextVAlignAsian(HwmfTextVerticalAlignment.BASELINE);\r
+            } else if (VTA_BOTTOM.isSet(textAlignmentMode)) {\r
+                props.setTextVAlignAsian(HwmfTextVerticalAlignment.BOTTOM);\r
+            } else {\r
+                props.setTextVAlignAsian(HwmfTextVerticalAlignment.TOP);\r
+            }\r
         }\r
     }\r
     \r
@@ -412,7 +500,7 @@ public class HwmfText {
         \r
         @Override\r
         public void applyObject(HwmfGraphics ctx) {\r
-\r
+            ctx.getProperties().setFont(font);\r
         }\r
     }\r
 }\r
index 19385d4fee9a8c0b043f9cf6b5054ec3c829a799..39a05e66008951b36f2c56e4bace1460d697b2c6 100644 (file)
 \r
 package org.apache.poi.hwmf.usermodel;\r
 \r
+import java.awt.Dimension;\r
 import java.awt.Graphics2D;\r
+import java.awt.GraphicsConfiguration;\r
 import java.awt.geom.AffineTransform;\r
 import java.awt.geom.Rectangle2D;\r
+import java.awt.geom.Rectangle2D.Double;\r
 import java.io.BufferedInputStream;\r
 import java.io.IOException;\r
 import java.io.InputStream;\r
@@ -35,6 +38,7 @@ import org.apache.poi.hwmf.record.HwmfRecordType;
 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
+import org.apache.poi.util.Units;\r
 \r
 public class HwmfPicture {\r
     final List<HwmfRecord> records = new ArrayList<HwmfRecord>();\r
@@ -85,9 +89,23 @@ public class HwmfPicture {
     }\r
 \r
     public void draw(Graphics2D ctx) {\r
+        Dimension dim = getSize();\r
+        int width = Units.pointsToPixel(dim.getWidth());\r
+        // keep aspect ratio for height\r
+        int height = Units.pointsToPixel(dim.getHeight());\r
+        Rectangle2D bounds = new Rectangle2D.Double(0,0,width,height);\r
+        draw(ctx, bounds);\r
+    }\r
+    \r
+    public void draw(Graphics2D ctx, Rectangle2D graphicsBounds) {\r
         AffineTransform at = ctx.getTransform();\r
         try {\r
-            HwmfGraphics g = new HwmfGraphics(ctx, getBounds());\r
+            Rectangle2D wmfBounds = getBounds();\r
+            // scale output bounds to image bounds\r
+            ctx.scale(graphicsBounds.getWidth()/wmfBounds.getWidth(), graphicsBounds.getHeight()/wmfBounds.getHeight());\r
+            ctx.translate(-wmfBounds.getX(), -wmfBounds.getY());\r
+            \r
+            HwmfGraphics g = new HwmfGraphics(ctx, wmfBounds);\r
             for (HwmfRecord r : records)  {\r
                 r.draw(g);\r
             }\r
@@ -131,4 +149,18 @@ public class HwmfPicture {
     public HwmfHeader getHeader() {\r
         return header;\r
     }\r
+    \r
+    /**\r
+     * Return the image size in points\r
+     *\r
+     * @return the image size in points\r
+     */\r
+    public Dimension getSize() {\r
+        double inch = (placeableHeader == null) ? 1440 : placeableHeader.getUnitsPerInch();\r
+        Rectangle2D bounds = getBounds();\r
+        \r
+        //coefficient to translate from WMF dpi to 72dpi\r
+        double coeff = Units.POINT_DPI/inch;\r
+        return new Dimension((int)Math.round(bounds.getWidth()*coeff), (int)Math.round(bounds.getHeight()*coeff));\r
+    }\r
 }\r
index 8d14f0c7dce7107aac64263929dddc8f3b1e86ae..7ddacf475a97d2c16da5bb301d42b00d323173f2 100644 (file)
@@ -19,9 +19,9 @@ package org.apache.poi.hwmf;
 \r
 import static org.junit.Assert.assertEquals;\r
 \r
+import java.awt.Dimension;\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
@@ -45,6 +45,7 @@ import org.apache.poi.sl.usermodel.PictureData;
 import org.apache.poi.sl.usermodel.PictureData.PictureType;\r
 import org.apache.poi.sl.usermodel.SlideShow;\r
 import org.apache.poi.sl.usermodel.SlideShowFactory;\r
+import org.apache.poi.util.Units;\r
 import org.junit.Ignore;\r
 import org.junit.Test;\r
 \r
@@ -60,17 +61,18 @@ public class TestHwmfParsing {
     }\r
     \r
     @Test\r
-    @Ignore\r
+    @Ignore("This is work-in-progress and not a real unit test ...")\r
     public void paint() throws IOException {\r
         File f = POIDataSamples.getSlideShowInstance().getFile("santa.wmf");\r
+//        File f = new File("E:\\project\\poi\\misc\\govdocs-ppt", "000133-0001.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
+        Dimension dim = wmf.getSize();\r
+        int width = Units.pointsToPixel(dim.getWidth());\r
         // keep aspect ratio for height\r
-        int height = (int)(width*bounds.getHeight()/bounds.getWidth());\r
+        int height = Units.pointsToPixel(dim.getHeight());\r
         \r
         BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);\r
         Graphics2D g = bufImg.createGraphics();\r
@@ -87,7 +89,7 @@ public class TestHwmfParsing {
     }\r
 \r
     @Test\r
-    @Ignore\r
+    @Ignore("This is work-in-progress and not a real unit test ...")\r
     public void fetchWmfFromGovdocs() throws IOException {\r
         URL url = new URL("http://digitalcorpora.org/corpora/files/govdocs1/by_type/ppt.zip");\r
         File outdir = new File("build/ppt");\r
@@ -117,12 +119,16 @@ public class TestHwmfParsing {
             }\r
         }\r
     }\r
-    \r
+\r
     @Test\r
-    @Ignore\r
+    @Ignore("This is work-in-progress and not a real unit test ...")\r
     public void parseWmfs() throws IOException {\r
+        // parse and render the extracted wmfs from the fetchWmfFromGovdocs step\r
         boolean outputFiles = false;\r
-        File indir = new File("build/ppt"), outdir = indir;\r
+        boolean renderWmf = true;\r
+        File indir = new File("E:\\project\\poi\\misc\\govdocs-ppt");\r
+        File outdir = new File("build/wmf");\r
+        outdir.mkdirs();\r
         final String startFile = "";\r
         File files[] = indir.listFiles(new FileFilter() {\r
             boolean foundStartFile = false;\r
@@ -149,6 +155,26 @@ public class TestHwmfParsing {
                         bmpIndex++;\r
                     }\r
                 }\r
+\r
+                if (renderWmf) {\r
+                    Dimension dim = wmf.getSize();\r
+                    int width = Units.pointsToPixel(dim.getWidth());\r
+                    // keep aspect ratio for height\r
+                    int height = Units.pointsToPixel(dim.getHeight());\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
+                    wmf.draw(g);\r
+\r
+                    g.dispose();\r
+                    \r
+                    ImageIO.write(bufImg, "PNG", new File(outdir, basename+".png"));\r
+                }\r
             } catch (Exception e) {\r
                 System.out.println(f.getName()+" ignored.");                \r
             }\r