]> source.dussan.org Git - poi.git/commitdiff
#60656 - Support export file that contains emf and render it correctly
authorAndreas Beeker <kiwiwings@apache.org>
Sun, 21 Oct 2018 20:49:03 +0000 (20:49 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Sun, 21 Oct 2018 20:49:03 +0000 (20:49 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hemf@1844522 13f79535-47bb-0310-9956-ffa450edef68

src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.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/HwmfMisc.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java
src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java

index 8b18be82f9134b49427668d4678f8f504e93a3aa..e32c451e2a653471b5f9be55ac0f0b9a0589d3f1 100644 (file)
@@ -53,6 +53,7 @@ public class HemfGraphics extends HwmfGraphics {
         super(graphicsCtx,bbox);
         // add dummy entry for object index 0, as emf is 1-based
         objectIndexes.set(0);
+        saveTransform();
     }
 
     @Override
@@ -82,10 +83,10 @@ public class HemfGraphics extends HwmfGraphics {
             if (tgt != null && !tgt.isEmpty()) {
                 final Rectangle2D src = bounded.getShapeBounds(this);
                 if (src != null && !src.isEmpty()) {
-                    graphicsCtx.translate(tgt.getCenterX() - src.getCenterX(), tgt.getCenterY() - src.getCenterY());
-                    graphicsCtx.translate(src.getCenterX(), src.getCenterY());
-                    graphicsCtx.scale(tgt.getWidth() / src.getWidth(), tgt.getHeight() / src.getHeight());
-                    graphicsCtx.translate(-src.getCenterX(), -src.getCenterY());
+//                    graphicsCtx.translate(tgt.getCenterX() - src.getCenterX(), tgt.getCenterY() - src.getCenterY());
+//                    graphicsCtx.translate(src.getCenterX(), src.getCenterY());
+//                    graphicsCtx.scale(tgt.getWidth() / src.getWidth(), tgt.getHeight() / src.getHeight());
+//                    graphicsCtx.translate(-src.getCenterX(), -src.getCenterY());
                 }
             }
         }
@@ -266,7 +267,27 @@ public class HemfGraphics extends HwmfGraphics {
         }
     }
 
+    /**
+     * @return the initial AffineTransform, when this graphics context was created
+     */
+    public AffineTransform getInitTransform() {
+        return new AffineTransform(transforms.peekFirst());
+    }
+
+    /**
+     * @return the current AffineTransform
+     */
+    public AffineTransform getTransform() {
+        return new AffineTransform(graphicsCtx.getTransform());
+    }
 
+    /**
+     * Set the current AffineTransform
+     * @param tx the current AffineTransform
+     */
+    public void setTransform(AffineTransform tx) {
+        graphicsCtx.setTransform(tx);
+    }
 
     /** saves the current affine transform on the stack */
     private void saveTransform() {
index 3596462364473738f1962cd718dcb177b1a56118..459a0b2ee3cc007bf2f129a2a04744240822ce3c 100644 (file)
@@ -860,6 +860,11 @@ public class HemfDraw {
             final HemfDrawProperties prop = ctx.getProperties();
             prop.setPath(new Path2D.Double());
         }
+
+        @Override
+        public String toString() {
+            return "{}";
+        }
     }
 
     /**
@@ -876,6 +881,11 @@ public class HemfDraw {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return 0;
         }
+
+        @Override
+        public String toString() {
+            return "{}";
+        }
     }
 
     /**
@@ -897,6 +907,11 @@ public class HemfDraw {
             final HemfDrawProperties prop = ctx.getProperties();
             prop.setPath(null);
         }
+
+        @Override
+        public String toString() {
+            return "{}";
+        }
     }
 
     /**
@@ -929,7 +944,6 @@ public class HemfDraw {
             return 0;
         }
 
-
         @Override
         public void draw(HemfGraphics ctx) {
             final HemfDrawProperties prop = ctx.getProperties();
@@ -940,6 +954,11 @@ public class HemfDraw {
             }
 
         }
+
+        @Override
+        public String toString() {
+            return "{}";
+        }
     }
 
     /**
@@ -972,12 +991,17 @@ public class HemfDraw {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return 0;
         }
+
+        @Override
+        public String toString() {
+            return "{}";
+        }
     }
 
     /**
      * The EMR_STROKEPATH record renders the specified path by using the current pen.
      */
-    public static class EmfStrokePath implements HemfRecord {
+    public static class EmfStrokePath implements HemfRecord, HemfBounded {
         protected final Rectangle2D bounds = new Rectangle2D.Double();
 
         @Override
@@ -990,6 +1014,28 @@ public class HemfDraw {
             // A 128-bit WMF RectL object, which specifies bounding rectangle, in device units
             return readRectL(leis, bounds);
         }
+
+        @Override
+        public void draw(HemfGraphics ctx) {
+            HemfDrawProperties props = ctx.getProperties();
+            ctx.draw(props.getPath());
+        }
+
+        @Override
+        public Rectangle2D getRecordBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Rectangle2D getShapeBounds(HemfGraphics ctx) {
+            HemfDrawProperties props = ctx.getProperties();
+            return props.getPath().getBounds2D();
+        }
+
+        @Override
+        public String toString() {
+            return "{ bounds: { x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+" }";
+        }
     }
 
     static long readRectL(LittleEndianInputStream leis, Rectangle2D bounds) {
index 2b814336f2e7cf87513a3bcabbcbbdc6fa786382..90a91a0bd16a0f27456d236a8f421a2811496a9a 100644 (file)
@@ -358,14 +358,12 @@ public class HemfFill {
         }
 
         protected Area getShape() {
-            final Area frame = new Area();
-            rgnRects.forEach((rct) -> frame.add(new Area(rct)));
-            return frame;
+            return getRgnShape(rgnRects);
         }
     }
 
     /** The EMR_INVERTRGN record inverts the colors in the specified region. */
-    public static class EmfInvertRgn implements HemfRecord {
+    public static class EmfInvertRgn implements HemfRecord, HemfBounded {
         protected final Rectangle2D bounds = new Rectangle2D.Double();
         protected final List<Rectangle2D> rgnRects = new ArrayList<>();
 
@@ -383,6 +381,20 @@ public class HemfFill {
             size += readRgnData(leis, rgnRects);
             return size;
         }
+
+        @Override
+        public Rectangle2D getRecordBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Rectangle2D getShapeBounds(HemfGraphics ctx) {
+            return getShape().getBounds2D();
+        }
+
+        protected Area getShape() {
+            return getRgnShape(rgnRects);
+        }
     }
 
     /**
@@ -397,7 +409,7 @@ public class HemfFill {
     }
 
     /** The EMR_FILLRGN record fills the specified region by using the specified brush. */
-    public static class EmfFillRgn extends HwmfFill.WmfFillRegion implements HemfRecord {
+    public static class EmfFillRgn extends HwmfFill.WmfFillRegion implements HemfRecord, HemfBounded {
         protected final Rectangle2D bounds = new Rectangle2D.Double();
         protected final List<Rectangle2D> rgnRects = new ArrayList<>();
 
@@ -416,6 +428,20 @@ public class HemfFill {
             size += readRgnData(leis, rgnRects);
             return size;
         }
+
+        @Override
+        public Rectangle2D getRecordBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Rectangle2D getShapeBounds(HemfGraphics ctx) {
+            return getShape().getBounds2D();
+        }
+
+        protected Area getShape() {
+            return getRgnShape(rgnRects);
+        }
     }
 
     public static class EmfExtSelectClipRgn implements HemfRecord {
@@ -442,9 +468,13 @@ public class HemfFill {
             }
             return size;
         }
+
+        protected Area getShape() {
+            return getRgnShape(rgnRects);
+        }
     }
 
-    public static class EmfAlphaBlend implements HemfRecord {
+    public static class EmfAlphaBlend implements HemfRecord, HemfBounded {
         /** the destination bounding rectangle in device units */
         protected final Rectangle2D bounds = new Rectangle2D.Double();
         /** the destination rectangle */
@@ -553,9 +583,24 @@ public class HemfFill {
 
             return size;
         }
+
+
+        @Override
+        public Rectangle2D getRecordBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Rectangle2D getShapeBounds(HemfGraphics ctx) {
+            return destRect;
+        }
     }
 
-    public static class EmfSetDiBitsToDevice implements HemfRecord {
+    /**
+     * The EMR_SETDIBITSTODEVICE record specifies a block transfer of pixels from specified scanlines of
+     * a source bitmap to a destination rectangle.
+     */
+    public static class EmfSetDiBitsToDevice implements HemfRecord, HemfBounded {
         protected final Rectangle2D bounds = new Rectangle2D.Double();
         protected final Point2D dest = new Point2D.Double();
         protected final Rectangle2D src = new Rectangle2D.Double();
@@ -600,6 +645,16 @@ public class HemfFill {
 
             return size;
         }
+
+        @Override
+        public Rectangle2D getRecordBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Rectangle2D getShapeBounds(HemfGraphics ctx) {
+            return new Rectangle2D.Double(dest.getX(), dest.getY(), src.getWidth(), src.getHeight());
+        }
     }
 
     static long readBitmap(final LittleEndianInputStream leis, final HwmfBitmapDib bitmap,
@@ -759,4 +814,10 @@ public class HemfFill {
             }
         }
     }
+
+    protected static Area getRgnShape(List<Rectangle2D> rgnRects) {
+        final Area frame = new Area();
+        rgnRects.forEach((rct) -> frame.add(new Area(rct)));
+        return frame;
+    }
 }
index ade921d2004971a6994e7ae5872ea659fcf2748b..e9ca805fd3118942d1094eb053e93c348e27a7a5 100644 (file)
@@ -29,6 +29,8 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.poi.hemf.draw.HemfGraphics;
+import org.apache.poi.hwmf.draw.HwmfDrawProperties;
+import org.apache.poi.hwmf.draw.HwmfGraphics;
 import org.apache.poi.hwmf.record.HwmfBinaryRasterOp;
 import org.apache.poi.hwmf.record.HwmfBitmapDib;
 import org.apache.poi.hwmf.record.HwmfBrushStyle;
@@ -45,6 +47,44 @@ import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
 public class HemfMisc {
+
+    public enum HemfModifyWorldTransformMode {
+        /**
+         * Reset the current transform using the identity matrix.
+         * In this mode, the specified transform data is ignored.
+         */
+        MWT_IDENTITY(1),
+        /**
+         * Multiply the current transform. In this mode, the specified transform data is the left multiplicand,
+         * and the transform that is currently defined in the playback device context is the right multiplicand.
+         */
+        MWT_LEFTMULTIPLY(2),
+        /**
+         * Multiply the current transform. In this mode, the specified transform data is the right multiplicand,
+         * and the transform that is currently defined in the playback device context is the left multiplicand.
+         */
+        MWT_RIGHTMULTIPLY(3),
+        /**
+         * Perform the function of an EMR_SETWORLDTRANSFORM record
+         */
+        MWT_SET(4)
+        ;
+
+        public final int id;
+
+        HemfModifyWorldTransformMode(int id) {
+            this.id = id;
+        }
+
+        public static HemfModifyWorldTransformMode valueOf(int id) {
+            for (HemfModifyWorldTransformMode wrt : values()) {
+                if (wrt.id == id) return wrt;
+            }
+            return null;
+        }
+    }
+
+
     public static class EmfEof implements HemfRecord {
         protected final List<PaletteEntry> palette = new ArrayList<>();
 
@@ -518,6 +558,11 @@ public class HemfMisc {
         public void draw(HemfGraphics ctx) {
             ctx.getProperties().setPenMiterLimit(miterLimit);
         }
+
+        @Override
+        public String toString() {
+            return "{ miterLimit: "+miterLimit+" }";
+        }
     }
 
 
@@ -552,11 +597,30 @@ public class HemfMisc {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return readXForm(leis, xForm);
         }
+
+        @Override
+        public void draw(HemfGraphics ctx) {
+            AffineTransform tx = ctx.getInitTransform();
+            tx.concatenate(xForm);
+            ctx.setTransform(tx);
+        }
+
+        @Override
+        public String toString() {
+            return
+                "{ xForm: " +
+                "{ scaleX: "+xForm.getScaleX()+
+                ", shearX: "+xForm.getShearX()+
+                ", transX: "+xForm.getTranslateX()+
+                ", scaleY: "+xForm.getScaleY()+
+                ", shearY: "+xForm.getShearY()+
+                ", transY: "+xForm.getTranslateY()+" } }";
+        }
     }
 
     public static class EmfModifyWorldTransform implements HemfRecord {
         protected final AffineTransform xForm = new AffineTransform();
-        protected int modifyWorldTransformMode;
+        protected HemfModifyWorldTransformMode modifyWorldTransformMode;
 
         @Override
         public HemfRecordType getEmfRecordType() {
@@ -572,16 +636,54 @@ public class HemfMisc {
 
             // A 32-bit unsigned integer that specifies how the transform specified in Xform is used.
             // This value MUST be in the ModifyWorldTransformMode enumeration
-            modifyWorldTransformMode = (int)leis.readUInt();
+            modifyWorldTransformMode = HemfModifyWorldTransformMode.valueOf((int)leis.readUInt());
 
             return size + LittleEndianConsts.INT_SIZE;
         }
 
+        @Override
+        public void draw(HemfGraphics ctx) {
+            if (modifyWorldTransformMode == null) {
+                return;
+            }
+
+            switch (modifyWorldTransformMode) {
+                case MWT_IDENTITY:
+                    ctx.setTransform(ctx.getInitTransform());
+                    break;
+                case MWT_LEFTMULTIPLY: {
+                    AffineTransform tx = new AffineTransform(xForm);
+                    tx.concatenate(ctx.getTransform());
+                    ctx.setTransform(tx);
+                    break;
+                }
+                case MWT_RIGHTMULTIPLY: {
+                    AffineTransform tx = new AffineTransform(xForm);
+                    tx.preConcatenate(ctx.getTransform());
+                    ctx.setTransform(tx);
+                    break;
+                }
+                default:
+                case MWT_SET: {
+                    AffineTransform tx = ctx.getInitTransform();
+                    tx.concatenate(xForm);
+                    ctx.setTransform(tx);
+                    break;
+                }
+            }
+        }
+
         @Override
         public String toString() {
             return
-                "{ xForm: { scaleX: "+xForm.getScaleX()+", shearX: "+xForm.getShearX()+", transX: "+xForm.getTranslateX()+", scaleY: "+xForm.getScaleY()+", shearY: "+xForm.getShearY()+", transY: "+xForm.getTranslateY()+" }"+
-                ", modifyWorldTransformMode: "+modifyWorldTransformMode+" }";
+                "{ xForm: " +
+                "{ scaleX: "+xForm.getScaleX()+
+                ", shearX: "+xForm.getShearX()+
+                ", transX: "+xForm.getTranslateX()+
+                ", scaleY: "+xForm.getScaleY()+
+                ", shearY: "+xForm.getShearY()+
+                ", transY: "+xForm.getTranslateY()+" }"+
+                ", modifyWorldTransformMode: '"+modifyWorldTransformMode+"' }";
         }
     }
 
@@ -626,5 +728,13 @@ public class HemfMisc {
 
             return size;
         }
+
+        @Override
+        public void applyObject(HwmfGraphics ctx) {
+            super.applyObject(ctx);
+            HwmfDrawProperties props = ctx.getProperties();
+            props.setBrushStyle(HwmfBrushStyle.BS_PATTERN);
+            props.setBrushBitmap(bitmap.getImage());
+        }
     }
 }
index 6b2f9a6d26e796ca24059444d4ac039d266d059e..97a81d910201230205ec1f5cd990696f7410b090 100644 (file)
@@ -57,6 +57,7 @@ public class HemfText {
 
     public static class EmfExtTextOutA extends HwmfText.WmfExtTextOut implements HemfRecord {
 
+        protected Rectangle2D boundsIgnored = new Rectangle2D.Double();
         protected EmfGraphicsMode graphicsMode;
 
         /**
@@ -81,7 +82,7 @@ public class HemfText {
             }
 
             // A WMF RectL object. It is not used and MUST be ignored on receipt.
-            long size = readRectL(leis, bounds);
+            long size = readRectL(leis, boundsIgnored);
 
             // A 32-bit unsigned integer that specifies the graphics mode from the GraphicsMode enumeration
             graphicsMode = EmfGraphicsMode.values()[leis.readInt()-1];
@@ -192,8 +193,7 @@ public class HemfText {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            Rectangle2D bounds = new Rectangle2D.Double(reference.getX(), reference.getY(), 0, 0);
-            ctx.drawString(rawTextBytes, bounds, dx, isUnicode());
+            ctx.drawString(rawTextBytes, reference, bounds, dx, isUnicode());
         }
 
         @Override
index dff46ce863c8f2e16e5d093857685e9c659c70b1..7a325dc0a0e0aceed48b16976b1f991f1473c9f8 100644 (file)
@@ -25,8 +25,11 @@ import java.awt.Paint;
 import java.awt.Rectangle;
 import java.awt.Shape;
 import java.awt.TexturePaint;
+import java.awt.font.FontRenderContext;
 import java.awt.font.TextAttribute;
+import java.awt.font.TextLayout;
 import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.nio.charset.Charset;
@@ -47,6 +50,7 @@ import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
 import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
 import org.apache.poi.hwmf.record.HwmfPenStyle;
 import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;
+import org.apache.poi.hwmf.record.HwmfText;
 import org.apache.poi.sl.draw.DrawFactory;
 import org.apache.poi.sl.draw.DrawFontManager;
 import org.apache.poi.util.LocaleUtil;
@@ -327,11 +331,11 @@ public class HwmfGraphics {
         }
     }
 
-    public void drawString(byte[] text, Rectangle2D bounds) {
-        drawString(text, bounds, null, false);
+    public void drawString(byte[] text, Point2D reference, Rectangle2D clip) {
+        drawString(text, reference, clip, null, false);
     }
 
-    public void drawString(byte[] text, Rectangle2D bounds, List<Integer> dx, boolean isUnicode) {
+    public void drawString(byte[] text, Point2D reference, Rectangle2D clip, List<Integer> dx, boolean isUnicode) {
 
         HwmfFont font = getProperties().getFont();
         if (font == null || text == null || text.length == 0) {
@@ -396,18 +400,55 @@ public class HwmfGraphics {
             }
         }
         */
-        
+
         double angle = Math.toRadians(-font.getEscapement()/10.);
 
+        final HwmfText.HwmfTextAlignment align = prop.getTextAlignLatin();
+        final HwmfText.HwmfTextVerticalAlignment valign = prop.getTextVAlignLatin();
+        final FontRenderContext frc = graphicsCtx.getFontRenderContext();
+        final TextLayout layout = new TextLayout(as.getIterator(), frc);
+
+        final Rectangle2D pixelBounds = layout.getBounds();
+
+        AffineTransform tx = new AffineTransform();
+        switch (align) {
+            default:
+            case LEFT:
+                break;
+            case CENTER:
+                tx.translate(-pixelBounds.getWidth() / 2., 0);
+                break;
+            case RIGHT:
+                tx.translate(-pixelBounds.getWidth(), 0);
+                break;
+        }
+
+        // TODO: check min/max orientation
+        switch (valign) {
+            case TOP:
+                tx.translate(0, layout.getAscent());
+            default:
+            case BASELINE:
+                break;
+            case BOTTOM:
+                tx.translate(0, pixelBounds.getHeight());
+                break;
+        }
+        tx.rotate(angle);
+        Point2D src = new Point2D.Double();
+        Point2D dst = new Point2D.Double();
+        tx.transform(src, dst);
+
+        // TODO: implement clipping on bounds
         final AffineTransform at = graphicsCtx.getTransform();
         try {
-            graphicsCtx.translate(bounds.getX(), bounds.getY());
+            graphicsCtx.translate(reference.getX(), reference.getY());
             graphicsCtx.rotate(angle);
-            graphicsCtx.translate(0, fontH);
-            if (getProperties().getBkMode() == HwmfBkMode.OPAQUE) {
+            graphicsCtx.translate(dst.getX(), dst.getY());
+            if (getProperties().getBkMode() == HwmfBkMode.OPAQUE && clip != null) {
                 // TODO: validate bounds
                 graphicsCtx.setBackground(getProperties().getBackgroundColor().getColor());
-                graphicsCtx.fill(new Rectangle2D.Double(0, 0, bounds.getWidth(), bounds.getHeight()));
+                graphicsCtx.fill(new Rectangle2D.Double(0, 0, clip.getWidth(), clip.getHeight()));
             }
             graphicsCtx.setColor(getProperties().getTextColor().getColor());
             graphicsCtx.drawString(as.getIterator(), 0, 0);
@@ -415,11 +456,11 @@ public class HwmfGraphics {
             graphicsCtx.setTransform(at);
         }
     }
-    
+
     private void addAttributes(AttributedString as, HwmfFont font) {
         DrawFontManager fontHandler = DrawFactory.getInstance(graphicsCtx).getFontManager(graphicsCtx);
         FontInfo fontInfo = fontHandler.getMappedFont(graphicsCtx, font);
-        
+
         as.addAttribute(TextAttribute.FAMILY, fontInfo.getTypeface());
         as.addAttribute(TextAttribute.SIZE, getFontHeight(font));
         if (font.isStrikeOut()) {
index 64f2dae8c98677e57a128798e4cabf8f984e6e79..b4e7ba73ed4837f77c9102451b80640a29c96592 100644 (file)
@@ -374,6 +374,16 @@ public class HwmfDraw {
         public void draw(HwmfGraphics ctx) {
             ctx.fill(bounds);
         }
+
+        @Override
+        public String toString() {
+            return
+                "{ bounds: " +
+                "{ x: "+bounds.getX()+
+                ", y: "+bounds.getY()+
+                ", w: "+bounds.getWidth()+
+                ", h: "+bounds.getHeight()+" } }";
+        }
     }
 
     /**
index 7bc46795bb950fa8de278051938478043df3c692..7197674ffa7730c795ed310f7b79410d2a498711 100644 (file)
@@ -302,6 +302,11 @@ public class HwmfMisc {
         public void draw(HwmfGraphics ctx) {
 
         }
+
+        @Override
+        public String toString() {
+            return "{ drawMode: '"+drawMode+"' }";
+        }
     }
 
     /**
index fb45170d22249756aa5f5c836f1d6236de08603f..a0b240d048c069385044d60e068b965034f39d05 100644 (file)
@@ -164,17 +164,9 @@ public class HwmfText {
          * The string is written at the location specified by the XStart and YStart fields.
          */
         private byte[] rawTextBytes;
-        /**
-         * A 16-bit signed integer that defines the vertical (y-axis) coordinate, in logical
-         * units, of the point where drawing is to start.
-         */
-        private int yStart;
-        /**
-         * A 16-bit signed integer that defines the horizontal (x-axis) coordinate, in
-         * logical units, of the point where drawing is to start.
-         */
-        private int xStart;  
-        
+
+        protected Point2D reference = new Point2D.Double();
+
         @Override
         public HwmfRecordType getWmfRecordType() {
             return HwmfRecordType.textOut;
@@ -185,15 +177,19 @@ public class HwmfText {
             stringLength = leis.readShort();
             rawTextBytes = IOUtils.safelyAllocate(stringLength+(stringLength&1), MAX_RECORD_LENGTH);
             leis.readFully(rawTextBytes);
-            yStart = leis.readShort();
-            xStart = leis.readShort();
+            // A 16-bit signed integer that defines the vertical (y-axis) coordinate, in logical
+            // units, of the point where drawing is to start.
+            int yStart = leis.readShort();
+            // A 16-bit signed integer that defines the horizontal (x-axis) coordinate, in
+            // logical units, of the point where drawing is to start.
+            int xStart = leis.readShort();
+            reference.setLocation(xStart, yStart);
             return 3*LittleEndianConsts.SHORT_SIZE+rawTextBytes.length;
         }
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            Rectangle2D bounds = new Rectangle2D.Double(xStart, yStart, 0, 0);
-            ctx.drawString(getTextBytes(), bounds);
+            ctx.drawString(getTextBytes(), reference, null);
         }
 
         public String getText(Charset charset) {
@@ -398,8 +394,7 @@ public class HwmfText {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            Rectangle2D bounds = new Rectangle2D.Double(reference.getX(), reference.getY(), 0, 0);
-            ctx.drawString(rawTextBytes, bounds, dx, false);
+            ctx.drawString(rawTextBytes, reference, bounds, dx, false);
         }
 
         public String getText(Charset charset) throws IOException {
index 94be4712e5c2f48090045015a4a9129231adf066..cabe627f6671a76b7fb5622dfb1e00e4a2e3af73 100644 (file)
@@ -63,7 +63,8 @@ public class HemfPictureTest {
     public void paint() throws IOException {
         byte buf[] = new byte[50_000_000];
 
-        final boolean writeLog = true;
+        final boolean writeLog = false;
+        final boolean dumpRecords = false;
         final boolean savePng = true;
 
         Set<String> passed = new HashSet<>();
@@ -101,6 +102,10 @@ public class HemfPictureTest {
                     }
                 }
 
+                if (dumpRecords) {
+                    dumpRecords(emf);
+                }
+
                 Graphics2D g = null;
                 try {
                     Dimension2D dim = emf.getSize();
@@ -112,17 +117,23 @@ public class HemfPictureTest {
                         width *= 1500. / max;
                         height *= 1500. / max;
                     }
+                    width = Math.ceil(width);
+                    height = Math.ceil(height);
 
-                    BufferedImage bufImg = new BufferedImage((int)Math.ceil(width), (int)Math.ceil(height), BufferedImage.TYPE_INT_ARGB);
+                    BufferedImage bufImg = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_ARGB);
                     g = bufImg.createGraphics();
                     g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                     g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
                     g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
                     g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
 
+                    g.setComposite(AlphaComposite.Clear);
+                    g.fillRect(0, 0, (int)width, (int)height);
+                    g.setComposite(AlphaComposite.Src);
+
                     emf.draw(g, new Rectangle2D.Double(0, 0, width, height));
 
-                    final File pngName = new File("build/tmp", etName.replaceFirst(".*"+"/", "").replace(".emf", ".png"));
+                    final File pngName = new File("build/tmp", etName.replaceFirst(".+/", "").replace(".emf", ".png"));
                     if (savePng) {
                         ImageIO.write(bufImg, "PNG", pngName);
                     }