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

13 files changed:
src/java/org/apache/poi/sl/draw/DrawFontManagerDefault.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/HemfFont.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPenStyle.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordIterator.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/HwmfFill.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java

index c439fc926f2256022b1024cfbe3cdb2827f2c57a..a4289436998c16d0629c9f5b51c17f08c4fbbd0e 100644 (file)
@@ -22,6 +22,8 @@ package org.apache.poi.sl.draw;
 import java.awt.Font;
 import java.awt.Graphics2D;
 import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
 
 import org.apache.poi.common.usermodel.fonts.FontInfo;
 import org.apache.poi.sl.draw.Drawable.DrawableHint;
@@ -33,6 +35,13 @@ import org.apache.poi.sl.draw.Drawable.DrawableHint;
  */
 public class DrawFontManagerDefault implements DrawFontManager {
 
+    protected final Set<String> knownSymbolFonts = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+
+    public DrawFontManagerDefault() {
+        knownSymbolFonts.add("Wingdings");
+        knownSymbolFonts.add("Symbol");
+    }
+
     @Override
     public FontInfo getMappedFont(Graphics2D graphics, FontInfo fontInfo) {
         return getFontWithFallback(graphics, Drawable.FONT_MAP, fontInfo);
@@ -49,25 +58,35 @@ public class DrawFontManagerDefault implements DrawFontManager {
 
     public String mapFontCharset(Graphics2D graphics, FontInfo fontInfo, String text) {
         // TODO: find a real charset mapping solution instead of hard coding for Wingdings
-        String attStr = text;
-        if (fontInfo != null && "Wingdings".equalsIgnoreCase(fontInfo.getTypeface())) {
-            // wingdings doesn't contain high-surrogates, so chars are ok
-            boolean changed = false;
-            char chrs[] = attStr.toCharArray();
-            for (int i=0; i<chrs.length; i++) {
-                // only change valid chars
-                if ((0x20 <= chrs[i] && chrs[i] <= 0x7f) ||
-                    (0xa0 <= chrs[i] && chrs[i] <= 0xff)) {
-                    chrs[i] |= 0xf000;
-                    changed = true;
-                }
-            }
+        return (fontInfo != null && knownSymbolFonts.contains(fontInfo.getTypeface()))
+            ? mapSymbolChars(text)
+            : text;
+    }
 
-            if (changed) {
-                attStr = new String(chrs);
+    /**
+     * Symbol fonts like "Wingdings" or "Symbol" have glyphs mapped to a Unicode private use range via the Java font loader,
+     * although a system font viewer might show you the glyphs in the ASCII range.
+     * This helper function maps the chars of the text string to the corresponding private use range chars.
+     *
+     * @param text the input string, typically consists of ASCII chars
+     * @return the mapped string, typically consists of chars in the range of 0xf000 to 0xf0ff
+     *
+     * @since POI 4.0.0
+     */
+    public static String mapSymbolChars(String text) {
+        // wingdings doesn't contain high-surrogates, so chars are ok
+        boolean changed = false;
+        char chrs[] = text.toCharArray();
+        for (int i=0; i<chrs.length; i++) {
+            // only change valid chars
+            if ((0x20 <= chrs[i] && chrs[i] <= 0x7f) ||
+                    (0xa0 <= chrs[i] && chrs[i] <= 0xff)) {
+                chrs[i] |= 0xf000;
+                changed = true;
             }
         }
-        return attStr;
+
+        return changed ? new String(chrs) : text;
     }
 
     @Override
index 367d64b084e87b2122fdcadebbbbb7151f34845f..7e495e2868b674bfb406552118b5a33f267da73f 100644 (file)
@@ -138,20 +138,18 @@ public class HemfDraw {
 
             Point2D pnt[] = { new Point2D.Double(), new Point2D.Double(), new Point2D.Double() };
 
-            // points-1 because of the first point
-            final int pointCnt = hasStartPoint() ? points-2 : points;
-            for (int i=0; i+2<pointCnt; i+=3) {
-                // x (4 bytes): A 32-bit signed integer that defines the horizontal (x) coordinate of the point.
-                // y (4 bytes): A 32-bit signed integer that defines the vertical (y) coordinate of the point.
-                if (i==0) {
-                    if (hasStartPoint()) {
-                        size += readPoint(leis, pnt[0]);
-                        poly.moveTo(pnt[0].getX(), pnt[0].getY());
-                    } else {
-                        poly.moveTo(0, 0);
-                    }
+            int i=0;
+            if (hasStartPoint()) {
+                if (i < points) {
+                    size += readPoint(leis, pnt[0]);
+                    poly.moveTo(pnt[0].getX(), pnt[0].getY());
+                    i++;
                 }
+            } else {
+                poly.moveTo(0, 0);
+            }
 
+            for (; i+2<points; i+=3) {
                 size += readPoint(leis, pnt[0]);
                 size += readPoint(leis, pnt[1]);
                 size += readPoint(leis, pnt[2]);
@@ -758,7 +756,8 @@ public class HemfDraw {
             size += LittleEndianConsts.INT_SIZE;
             Point2D points[] = new Point2D[count];
             for (int i=0; i<count; i++) {
-                 size += readPoint(leis, points[i]);
+                points[i] = new Point2D.Double();
+                size += readPoint(leis, points[i]);
             }
 
             poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, count);
@@ -783,12 +782,14 @@ public class HemfDraw {
                     case 0x04:
                         int mode2 = leis.readUByte();
                         int mode3 = leis.readUByte();
-                        assert(mode2 == 0x04 && mode3 == 0x04);
+                        assert(mode2 == 0x04 && (mode3 == 0x04 || mode3 == 0x05));
                         poly.curveTo(
                             points[i].getX(), points[i].getY(),
                             points[i+1].getX(), points[i+1].getY(),
                             points[i+2].getX(), points[i+2].getY()
                         );
+                        // update mode for closePath handling below
+                        mode = mode3;
                         i+=2;
                         break;
                     // PT_MOVETO
index 5cd4879b158792ca8c23347bc4b55d5cdd0d8b51..8e19e8711661917e7f8cf1e922b9eb66998d5797 100644 (file)
@@ -683,33 +683,27 @@ public class HemfFill {
     }
 
     static int readXForm(LittleEndianInputStream leis, AffineTransform xform) {
-        // mapping <java AffineTransform> = <xform>:
-        // m00 (scaleX) = eM11 (Horizontal scaling component)
-        // m11 (scaleY) = eM22 (Vertical scaling component)
-        // m01 (shearX) = eM12 (Horizontal proportionality constant)
-        // m10 (shearY) = eM21 (Vertical proportionality constant)
-        // m02 (translateX) = eDx (The horizontal translation component, in logical units.)
-        // m12 (translateY) = eDy (The vertical translation component, in logical units.)
+        // mapping <java AffineTransform> = <xform>
 
-        // A 32-bit floating-point value of the transform matrix.
-        double eM11 = leis.readFloat();
+        // m00 (scaleX) = eM11 (Horizontal scaling component)
+        double m00 = leis.readFloat();
 
-        // A 32-bit floating-point value of the transform matrix.
-        double eM12 = leis.readFloat();
+        // m01 (shearX) = eM12 (Horizontal proportionality constant)
+        double m01 = leis.readFloat();
 
-        // A 32-bit floating-point value of the transform matrix.
-        double eM21 = leis.readFloat();
+        // m10 (shearY) = eM21 (Vertical proportionality constant)
+        double m10 = leis.readFloat();
 
-        // A 32-bit floating-point value of the transform matrix.
-        double eM22 = leis.readFloat();
+        // m11 (scaleY) = eM22 (Vertical scaling component)
+        double m11 = leis.readFloat();
 
-        // A 32-bit floating-point value that contains a horizontal translation component, in logical units.
-        double eDx = leis.readFloat();
+        // m02 (translateX) = eDx (The horizontal translation component, in logical units.)
+        double m02 = leis.readFloat();
 
-        // A 32-bit floating-point value that contains a vertical translation component, in logical units.
-        double eDy = leis.readFloat();
+        // m12 (translateY) = eDy (The vertical translation component, in logical units.)
+        double m12 = leis.readFloat();
 
-        xform.setTransform(eM11, eM21, eM12, eM22, eDx, eDy);
+        xform.setTransform(m00, m10, m01, m11, m02, m12);
 
         return 6 * LittleEndian.INT_SIZE;
     }
index ea3eb7754ec87eb1cf2fb6e80d78cb8c4fbfb555..a7eb4c8b1c6184094cc122880557c7940428c2cb 100644 (file)
@@ -273,15 +273,15 @@ public class HemfFont extends HwmfFont {
 
         // An 8-bit unsigned integer that specifies an italic font if set to 0x01;
         // otherwise, it MUST be set to 0x00.
-        italic = (leis.readUByte() == 0x01);
+        italic = (leis.readUByte() != 0x00);
 
         // An 8-bit unsigned integer that specifies an underlined font if set to 0x01;
         // otherwise, it MUST be set to 0x00.
-        underline = (leis.readUByte() == 0x01);
+        underline = (leis.readUByte() != 0x00);
 
         // An 8-bit unsigned integer that specifies a strikeout font if set to 0x01;
         // otherwise, it MUST be set to 0x00.
-        strikeOut = (leis.readUByte() == 0x01);
+        strikeOut = (leis.readUByte() != 0x00);
 
         // An 8-bit unsigned integer that specifies the set of character glyphs.
         // It MUST be a value in the WMF CharacterSet enumeration.
@@ -441,7 +441,8 @@ public class HemfFont extends HwmfFont {
 
             // A 32-bit unsigned integer that MUST be set to the value 0x08007664.
             int signature = leis.readInt();
-            assert (signature == 0x08007664);
+            // some non-conformant applications don't write the magic code in
+            // assert (signature == 0x08007664);
 
             // A 32-bit unsigned integer that specifies the number of elements in the
             // Values array. It MUST be in the range 0 to 16, inclusive.
index 206b7d3162eb9e88de61d9077589fb217f4cc110..55d8c4cc2324fd97d5310a1c44434323c2329e93 100644 (file)
@@ -27,7 +27,9 @@ import java.awt.geom.Point2D;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.function.Function;
 
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hwmf.draw.HwmfDrawProperties;
@@ -443,8 +445,6 @@ public class HemfMisc {
         protected HwmfBrushStyle brushStyle;
         protected HwmfHatchStyle hatchStyle;
 
-        protected int[] styleEntry;
-
         protected final HwmfBitmapDib bitmap = new HwmfBitmapDib();
 
 
@@ -477,7 +477,8 @@ public class HemfMisc {
 
             // A 32-bit unsigned integer that specifies the PenStyle.
             // The value MUST be defined from the PenStyle enumeration table
-            penStyle = HwmfPenStyle.valueOf((int) leis.readUInt());
+            final HemfPenStyle emfPS = HemfPenStyle.valueOf((int) leis.readUInt());
+            penStyle = emfPS;
 
             // A 32-bit unsigned integer that specifies the width of the line drawn by the pen.
             // If the pen type in the PenStyle field is PS_GEOMETRIC, this value is the width in logical
@@ -517,10 +518,14 @@ public class HemfMisc {
             // If the pen type in the PenStyle field is PS_GEOMETRIC, the lengths are specified in logical
             // units; otherwise, the lengths are specified in device units.
 
-            styleEntry = new int[numStyleEntries];
+            float[] dashPattern = new float[numStyleEntries];
 
             for (int i = 0; i < numStyleEntries; i++) {
-                styleEntry[i] = (int) leis.readUInt();
+                dashPattern[i] = (int) leis.readUInt();
+            }
+
+            if (penStyle.getLineDash() == HwmfLineDash.USERSTYLE) {
+                emfPS.setLineDashes(dashPattern);
             }
 
             size += numStyleEntries * LittleEndianConsts.INT_SIZE;
@@ -533,8 +538,11 @@ public class HemfMisc {
         @Override
         public String toString() {
             // TODO: add style entries + bmp
-            return super.toString().replaceFirst("\\{",
-                "{ brushStyle: '"+brushStyle+"', hatchStyle: '"+hatchStyle+"', ");
+            return
+                "{ brushStyle: '"+brushStyle+"'"+
+                ", hatchStyle: '"+hatchStyle+"'"+
+                ", dashPattern: "+ Arrays.toString(penStyle.getLineDashes())+
+                ", "+super.toString().substring(1);
         }
     }
 
@@ -602,7 +610,8 @@ public class HemfMisc {
 
         @Override
         public void draw(HemfGraphics ctx) {
-            AffineTransform tx = ctx.getInitTransform();
+            ctx.updateWindowMapMode();
+            AffineTransform tx = ctx.getTransform();
             tx.concatenate(xForm);
             ctx.setTransform(tx);
         }
@@ -649,30 +658,46 @@ public class HemfMisc {
                 return;
             }
 
+            final AffineTransform tx;
             switch (modifyWorldTransformMode) {
-                case MWT_IDENTITY:
-                    ctx.setTransform(ctx.getInitTransform());
+                case MWT_LEFTMULTIPLY:
+                    tx = ctx.getTransform();
+                    tx.concatenate(adaptXForm(tx));
                     break;
-                case MWT_LEFTMULTIPLY: {
-                    AffineTransform tx = new AffineTransform(xForm);
-                    tx.concatenate(ctx.getTransform());
-                    ctx.setTransform(tx);
+                case MWT_RIGHTMULTIPLY:
+                    tx = ctx.getTransform();
+                    tx.preConcatenate(adaptXForm(tx));
                     break;
-                }
-                case MWT_RIGHTMULTIPLY: {
-                    AffineTransform tx = new AffineTransform(xForm);
-                    tx.preConcatenate(ctx.getTransform());
-                    ctx.setTransform(tx);
+                case MWT_IDENTITY:
+                    ctx.updateWindowMapMode();
+                    tx = ctx.getTransform();
                     break;
-                }
                 default:
-                case MWT_SET: {
-                    AffineTransform tx = ctx.getInitTransform();
-                    tx.concatenate(xForm);
-                    ctx.setTransform(tx);
+                case MWT_SET:
+                    ctx.updateWindowMapMode();
+                    tx = ctx.getTransform();
+                    tx.concatenate(adaptXForm(tx));
                     break;
-                }
             }
+            ctx.setTransform(tx);
+        }
+
+        /**
+         * adapt xform depending on the base transformation (... experimental ...)
+         */
+        private AffineTransform adaptXForm(AffineTransform other) {
+            // normalize signed zero
+            Function<Double,Double> nn = (d) -> (d == 0. ? 0. : d);
+            double yDiff = Math.signum(nn.apply(xForm.getTranslateY())) == Math.signum(nn.apply(other.getTranslateY())) ? 1. : -1.;
+            double xDiff = Math.signum(nn.apply(xForm.getTranslateX())) == Math.signum(nn.apply(other.getTranslateX())) ? 1. : -1.;
+            return new AffineTransform(
+                    xForm.getScaleX() == 0 ? 1. : xForm.getScaleX(),
+                    yDiff * xForm.getShearY(),
+                    xDiff * xForm.getShearX(),
+                    xForm.getScaleY() == 0. ? 1. : xForm.getScaleY(),
+                    xForm.getTranslateX(),
+                    xForm.getTranslateY()
+            );
         }
 
         @Override
diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPenStyle.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPenStyle.java
new file mode 100644 (file)
index 0000000..74e9459
--- /dev/null
@@ -0,0 +1,45 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hemf.record.emf;
+
+import org.apache.poi.hwmf.record.HwmfPenStyle;
+
+public class HemfPenStyle extends HwmfPenStyle {
+
+    private float[] dashPattern;
+
+    public static HemfPenStyle valueOf(int flag) {
+        HemfPenStyle ps = new HemfPenStyle();
+        ps.flag = flag;
+        return ps;
+    }
+
+    @Override
+    public float[] getLineDashes() {
+        return (getLineDash() == HwmfLineDash.USERSTYLE) ? dashPattern : super.getLineDashes();
+    }
+
+    public void setLineDashes(float[] dashPattern) {
+        this.dashPattern = (dashPattern == null) ? null : dashPattern.clone();
+    }
+
+    @Override
+    public HemfPenStyle clone() {
+        return (HemfPenStyle)super.clone();
+    }
+}
index 4f11906bbdb20a675724eae1e868a36f389625f7..4fb2cc22d484eab2ec2494b96d8d516c85ef2cf3 100644 (file)
@@ -43,6 +43,10 @@ public interface HemfRecord {
      */
     long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException;
 
+    /**
+     * Draws the record, the default redirects to the parent WMF record drawing
+     * @param ctx the drawing context
+     */
     default void draw(HemfGraphics ctx) {
         if (this instanceof HwmfRecord) {
             ((HwmfRecord) this).draw(ctx);
index 207c8ee830e77a1d19721822c88a62baaba6d916..dfa68670e148bd9e63641063925599c280318b6f 100644 (file)
@@ -54,6 +54,8 @@ public class HemfRecordIterator implements Iterator<HemfRecord> {
             return null;
         }
 
+        final int readIndex = stream.getReadIndex();
+
         final long recordId, recordSize;
         try {
             recordId = stream.readUInt();
@@ -65,7 +67,7 @@ public class HemfRecordIterator implements Iterator<HemfRecord> {
 
         HemfRecordType type = HemfRecordType.getById(recordId);
         if (type == null) {
-            throw new RecordFormatException("Undefined record of type:"+recordId);
+            throw new RecordFormatException("Undefined record of type: "+recordId+" at "+Integer.toHexString(readIndex));
         }
         final HemfRecord record = type.constructor.get();
 
index f62155d18210da35aaf7607063a00fa229d89433..7e785315c0c93079c8e829d57a815043e5a1586a 100644 (file)
@@ -141,14 +141,12 @@ public class HemfText {
                                 dx.add((int) leis.readUInt());
                                 size += LittleEndianConsts.INT_SIZE;
                             }
-                        } else {
-                            // if there are no dx entries, reset the string end
-                            strEnd = (int)recordSize;
                         }
                         if (dx.size() < stringLength) {
                             // invalid dx array
                             dx.clear();
                         }
+                        strEnd = (int)recordSize;
                         break;
                     }
                     default:
index dadb0916996cbb639dd99b7a9b51519a3765a805..dd189a0f55b5d0c0bba4bbee1ffc3fe662dbbf75 100644 (file)
@@ -31,6 +31,7 @@ import java.awt.font.TextLayout;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Area;
 import java.awt.geom.Dimension2D;
+import java.awt.geom.NoninvertibleTransformException;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
@@ -43,6 +44,7 @@ import java.util.NoSuchElementException;
 import java.util.TreeMap;
 
 import org.apache.commons.codec.Charsets;
+import org.apache.poi.common.usermodel.fonts.FontCharset;
 import org.apache.poi.common.usermodel.fonts.FontInfo;
 import org.apache.poi.hwmf.record.HwmfBrushStyle;
 import org.apache.poi.hwmf.record.HwmfFont;
@@ -57,6 +59,7 @@ import org.apache.poi.hwmf.record.HwmfText;
 import org.apache.poi.hwmf.record.HwmfText.WmfExtTextOutOptions;
 import org.apache.poi.sl.draw.DrawFactory;
 import org.apache.poi.sl.draw.DrawFontManager;
+import org.apache.poi.sl.draw.DrawFontManagerDefault;
 import org.apache.poi.util.LocaleUtil;
 
 public class HwmfGraphics {
@@ -152,7 +155,7 @@ public class HwmfGraphics {
         int cap = ps.getLineCap().awtFlag;
         int join = ps.getLineJoin().awtFlag;
         float miterLimit = (float)getProperties().getPenMiterLimit();
-        float dashes[] = ps.getLineDash().dashes;
+        float dashes[] = ps.getLineDashes();
         boolean dashAlt = ps.isAlternateDash();
         // This value is not an integer index into the dash pattern array.
         // Instead, it is a floating-point value that specifies a linear distance.
@@ -370,6 +373,17 @@ public class HwmfGraphics {
     public void drawString(byte[] text, int length, Point2D reference, Dimension2D scale, Rectangle2D clip, WmfExtTextOutOptions opts, List<Integer> dx, boolean isUnicode) {
         final HwmfDrawProperties prop = getProperties();
 
+        final AffineTransform at = graphicsCtx.getTransform();
+        if (at.getScaleX() == 0. || at.getScaleY() == 0.) {
+            return;
+        }
+
+        try {
+            at.createInverse();
+        } catch (NoninvertibleTransformException e) {
+            return;
+        }
+
         HwmfFont font = prop.getFont();
         if (font == null || text == null || text.length == 0) {
             return;
@@ -390,11 +404,19 @@ public class HwmfGraphics {
         }
 
         String textString = new String(text, charset).substring(0,length).trim();
+
         if (textString.isEmpty()) {
             return;
         }
+
+        DrawFontManager fontHandler = DrawFactory.getInstance(graphicsCtx).getFontManager(graphicsCtx);
+        FontInfo fontInfo = fontHandler.getMappedFont(graphicsCtx, font);
+        if (fontInfo.getCharset() == FontCharset.SYMBOL) {
+            textString = DrawFontManagerDefault.mapSymbolChars(textString);
+        }
+
         AttributedString as = new AttributedString(textString);
-        addAttributes(as, font);
+        addAttributes(as, font, fontInfo.getTypeface());
 
         // disabled for the time being, as the results aren't promising
         /*
@@ -473,7 +495,6 @@ public class HwmfGraphics {
         tx.transform(src, dst);
 
         final Shape clipShape = graphicsCtx.getClip();
-        final AffineTransform at = graphicsCtx.getTransform();
         try {
             if (clip != null) {
                 graphicsCtx.translate(-clip.getCenterX(), -clip.getCenterY());
@@ -503,11 +524,8 @@ public class HwmfGraphics {
         }
     }
 
-    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());
+    private void addAttributes(AttributedString as, HwmfFont font, String typeface) {
+        as.addAttribute(TextAttribute.FAMILY, typeface);
         as.addAttribute(TextAttribute.SIZE, getFontHeight(font));
         if (font.isStrikeOut()) {
             as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
@@ -669,4 +687,11 @@ public class HwmfGraphics {
             graphicsCtx.setTransform(at);
         }
     }
+
+    /**
+     * @return the bounding box
+     */
+    public Rectangle2D getBbox() {
+        return (Rectangle2D)bbox.clone();
+    }
 }
index e353750bd950ea25458304f7a79e203c25198e26..3b420e401d652f8be864d20bb3f29b3c5efe6205 100644 (file)
@@ -499,8 +499,8 @@ public class HwmfFill {
             if (bitmap.isValid()) {
                 ctx.drawImage(getImage(), srcBounds, dstBounds);
             } else if (!dstBounds.isEmpty()) {
-                BufferedImage bi = new BufferedImage((int)dstBounds.getWidth(), (int)dstBounds.getHeight(), BufferedImage.TYPE_INT_ARGB);
-                ctx.drawImage(bi, dstBounds, dstBounds);
+                BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
+                ctx.drawImage(bi, new Rectangle2D.Double(0,0,100,100), dstBounds);
             }
         }
 
index 5366b3fb24d9656c7fa0292e09c9960253fad82a..776d48a3e7269ead0d6c1c647929495be80ce157 100644 (file)
@@ -142,7 +142,7 @@ public class HwmfPenStyle implements Cloneable {
     private static final BitField SUBSECTION_JOIN      = BitFieldFactory.getInstance(0x03000);
     private static final BitField SUBSECTION_GEOMETRIC = BitFieldFactory.getInstance(0x10000);
 
-    private int flag;
+    protected int flag;
     
     public static HwmfPenStyle valueOf(int flag) {
         HwmfPenStyle ps = new HwmfPenStyle();
@@ -161,7 +161,16 @@ public class HwmfPenStyle implements Cloneable {
     public HwmfLineDash getLineDash() {
         return HwmfLineDash.valueOf(SUBSECTION_DASH.getValue(flag));
     }
-    
+
+    /**
+     * Convienence method which should be used instead of accessing {@link HwmfLineDash#dashes}
+     * directly, so an subclass can provide user-style dashes
+     *
+     * @return the dash pattern
+     */
+    public float[] getLineDashes() {
+        return getLineDash().dashes;
+    }
 
     /**
      * The pen sets every other pixel (this style is applicable only for cosmetic pens).
index 34c948df0207281835e241cf9812f1287c430cc0..bbf5b9eb157e1bdfdb098614807a99190610f22d 100644 (file)
@@ -18,6 +18,7 @@
 package org.apache.poi.hwmf.record;
 
 import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
+import static org.apache.poi.hwmf.record.HwmfDraw.dimToString;
 import static org.apache.poi.hwmf.record.HwmfDraw.normalizeBounds;
 import static org.apache.poi.hwmf.record.HwmfDraw.pointToString;
 import static org.apache.poi.hwmf.record.HwmfDraw.readBounds;
@@ -30,6 +31,7 @@ import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 
+import org.apache.poi.hwmf.draw.HwmfDrawProperties;
 import org.apache.poi.hwmf.draw.HwmfGraphics;
 import org.apache.poi.util.Dimension2DDouble;
 import org.apache.poi.util.LittleEndianConsts;
@@ -56,8 +58,14 @@ public class HwmfWindowing {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            ctx.getProperties().setViewportOrg(origin.getX(), origin.getY());
-            ctx.updateWindowMapMode();
+            final HwmfDrawProperties prop = ctx.getProperties();
+            Rectangle2D old = prop.getViewport();
+            double oldX = (old == null ? 0 : old.getX());
+            double oldY = (old == null ? 0 : old.getY());
+            if (oldX != origin.getX() || oldY != origin.getY()) {
+                prop.setViewportOrg(origin.getX(), origin.getY());
+                ctx.updateWindowMapMode();
+            }
         }
 
         @Override
@@ -91,13 +99,19 @@ public class HwmfWindowing {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            ctx.getProperties().setViewportExt(extents.getWidth(), extents.getHeight());
-            ctx.updateWindowMapMode();
+            final HwmfDrawProperties prop = ctx.getProperties();
+            Rectangle2D old = prop.getViewport();
+            double oldW = (old == null ? 0 : old.getWidth());
+            double oldH = (old == null ? 0 : old.getHeight());
+            if (oldW != extents.getWidth() || oldH != extents.getHeight()) {
+                prop.setViewportExt(extents.getWidth(), extents.getHeight());
+                ctx.updateWindowMapMode();
+            }
         }
 
         @Override
         public String toString() {
-            return "{ width: "+extents.getWidth()+", height: "+extents.getHeight()+" }";
+            return dimToString(extents);
         }
     }
 
@@ -121,10 +135,14 @@ public class HwmfWindowing {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            Rectangle2D viewport = ctx.getProperties().getViewport();
-            double x = (viewport == null) ? 0 : viewport.getX();
-            double y = (viewport == null) ? 0 : viewport.getY();
-            ctx.getProperties().setViewportOrg(x+offset.getX(), y+offset.getY());
+            final HwmfDrawProperties prop = ctx.getProperties();
+            Rectangle2D viewport = prop.getViewport();
+            if (offset.getX() != 0 || offset.getY() != 0) {
+                double x = (viewport == null) ? 0 : viewport.getX();
+                double y = (viewport == null) ? 0 : viewport.getY();
+                prop.setViewportOrg(x + offset.getX(), y + offset.getY());
+                ctx.updateWindowMapMode();
+            }
         }
 
         @Override
@@ -152,8 +170,14 @@ public class HwmfWindowing {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            ctx.getProperties().setWindowOrg(getX(), getY());
-            ctx.updateWindowMapMode();
+            final HwmfDrawProperties prop = ctx.getProperties();
+            final Rectangle2D old = prop.getWindow();
+            double oldX = (old == null ? 0 : old.getX());
+            double oldY = (old == null ? 0 : old.getY());
+            if (oldX != getX() || oldY != getY()) {
+                prop.setWindowOrg(getX(), getY());
+                ctx.updateWindowMapMode();
+            }
         }
 
         public double getY() {
@@ -195,8 +219,14 @@ public class HwmfWindowing {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            ctx.getProperties().setWindowExt(size.getWidth(), size.getHeight());
-            ctx.updateWindowMapMode();
+            final HwmfDrawProperties prop = ctx.getProperties();
+            Rectangle2D old = prop.getWindow();
+            double oldW = (old == null ? 0 : old.getWidth());
+            double oldH = (old == null ? 0 : old.getHeight());
+            if (oldW != size.getWidth() || oldH != size.getHeight()) {
+                prop.setWindowExt(size.getWidth(), size.getHeight());
+                ctx.updateWindowMapMode();
+            }
         }
 
         public Dimension2D getSize() {
@@ -205,7 +235,7 @@ public class HwmfWindowing {
 
         @Override
         public String toString() {
-            return "{ width: "+size.getWidth()+", height: "+size.getHeight()+" }";
+            return dimToString(size);
         }
     }
 
@@ -229,9 +259,12 @@ public class HwmfWindowing {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            Rectangle2D window = ctx.getProperties().getWindow();
-            ctx.getProperties().setWindowOrg(window.getX()+offset.getX(), window.getY()+offset.getY());
-            ctx.updateWindowMapMode();
+            final HwmfDrawProperties prop = ctx.getProperties();
+            Rectangle2D old = prop.getWindow();
+            if (offset.getX() != 0 || offset.getY() != 0) {
+                prop.setWindowOrg(old.getX() + offset.getX(), old.getY() + offset.getY());
+                ctx.updateWindowMapMode();
+            }
         }
 
         @Override
@@ -275,11 +308,14 @@ public class HwmfWindowing {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            Rectangle2D window = ctx.getProperties().getWindow();
-            double width = window.getWidth() * scale.getWidth();
-            double height = window.getHeight() * scale.getHeight();
-            ctx.getProperties().setWindowExt(width, height);
-            ctx.updateWindowMapMode();
+            final HwmfDrawProperties prop = ctx.getProperties();
+            Rectangle2D old = prop.getWindow();
+            if (scale.getWidth() != 1.0 || scale.getHeight() != 1.0) {
+                double width = old.getWidth() * scale.getWidth();
+                double height = old.getHeight() * scale.getHeight();
+                ctx.getProperties().setWindowExt(width, height);
+                ctx.updateWindowMapMode();
+            }
         }
 
         @Override
@@ -325,13 +361,15 @@ public class HwmfWindowing {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            Rectangle2D viewport = ctx.getProperties().getViewport();
-            if (viewport == null) {
-                viewport = ctx.getProperties().getWindow();
+            final HwmfDrawProperties prop = ctx.getProperties();
+            final Rectangle2D old = prop.getViewport() == null ? prop.getWindow() : prop.getViewport();
+
+            if (scale.getWidth() != 1.0 || scale.getHeight() != 1.0) {
+                double width = old.getWidth() * scale.getWidth();
+                double height = old.getHeight() * scale.getHeight();
+                prop.setViewportExt(width, height);
+                ctx.updateWindowMapMode();
             }
-            double width = viewport.getWidth() * scale.getWidth();
-            double height = viewport.getHeight() * scale.getHeight();
-            ctx.getProperties().setViewportExt(width, height);
         }
 
         @Override