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

16 files changed:
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/HemfHeader.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/hemf/record/emf/HemfWindowing.java
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/HwmfBitmapDib.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/HwmfText.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java

index d18f0d411987aa1410531e21fd8e556bde7aa468..367d64b084e87b2122fdcadebbbbb7151f34845f 100644 (file)
@@ -18,6 +18,7 @@
 package org.apache.poi.hemf.record.emf;
 
 import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
+import static org.apache.poi.hwmf.record.HwmfDraw.normalizeBounds;
 
 import java.awt.Shape;
 import java.awt.geom.Arc2D;
@@ -660,7 +661,7 @@ public class HemfDraw {
 
         @Override
         public void draw(HemfGraphics ctx) {
-            ctx.draw(path -> path.append(bounds, false), FillDrawStyle.FILL_DRAW);
+            ctx.draw(path -> path.append(normalizeBounds(bounds), false), FillDrawStyle.FILL_DRAW);
         }
     }
 
@@ -1127,8 +1128,9 @@ public class HemfDraw {
     }
 
     static long readDimensionInt(LittleEndianInputStream leis, Dimension2D dimension) {
-        final double width = leis.readUInt();
-        final double height = leis.readUInt();
+        // although the spec says "use unsigned ints", there are examples out there using signed ints
+        final double width = leis.readInt();
+        final double height = leis.readInt();
         dimension.setSize(width, height);
         return 2*LittleEndianConsts.INT_SIZE;
     }
index 810c9325a3016eb5e968de3253d39acb57196cb4..5cd4879b158792ca8c23347bc4b55d5cdd0d8b51 100644 (file)
@@ -21,6 +21,7 @@ import static org.apache.poi.hemf.record.emf.HemfDraw.readPointL;
 import static org.apache.poi.hemf.record.emf.HemfDraw.readRectL;
 import static org.apache.poi.hemf.record.emf.HemfRecordIterator.HEADER_SIZE;
 import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
+import static org.apache.poi.hwmf.record.HwmfDraw.pointToString;
 
 import java.awt.Shape;
 import java.awt.geom.AffineTransform;
@@ -588,6 +589,17 @@ public class HemfFill {
 
             return size;
         }
+
+        @Override
+        public String toString() {
+            return
+                "{ bounds: " + boundsToString(bounds) +
+                ", dest: " + pointToString(dest) +
+                ", src: " + boundsToString(src) +
+                ", usageSrc: '" + usageSrc + "'" +
+                ", bitmap: " + bitmap +
+                "}";
+        }
     }
 
     static long readBitmap(final LittleEndianInputStream leis, final HwmfBitmapDib bitmap,
index 8690855f16ffa49c42025f58d67f5aaa0f56c490..ea3eb7754ec87eb1cf2fb6e80d78cb8c4fbfb555 100644 (file)
@@ -36,6 +36,11 @@ public class HemfFont extends HwmfFont {
 
     protected static class LogFontExDv implements LogFontDetails {
         protected int[] designVector;
+
+        @Override
+        public String toString() {
+            return "{ designVectorLen: " + (designVector == null ? 0 : designVector.length) + " }";
+        }
     }
 
     protected static class LogFontPanose implements LogFontDetails {
@@ -195,6 +200,25 @@ public class HemfFont extends HwmfFont {
         protected Letterform letterform;
         protected MidLine midLine;
         protected XHeight xHeight;
+
+        @Override
+        public String toString() {
+            return
+                "{ styleSize: " + styleSize +
+                ", vendorId: " + vendorId +
+                ", culture: " +  culture +
+                ", familyType: '" + familyType + "'" +
+                ", serifStyle: '" + serifStyle + "'" +
+                ", weight: '" + weight + "'" +
+                ", proportion: '" + proportion + "'" +
+                ", contrast: '" + contrast + "'" +
+                ", strokeVariation: '" + strokeVariation + "'" +
+                ", armStyle: '" + armStyle + "'" +
+                ", letterform: '" + letterform + "'" +
+                ", midLine: '" + midLine + "'" +
+                ", xHeight: '" + xHeight + "'" +
+                "}";
+        }
     }
 
     protected String fullname;
@@ -435,12 +459,19 @@ public class HemfFont extends HwmfFont {
             size += (2+numAxes)*LittleEndianConsts.INT_SIZE;
         }
 
-
-
-
         return size;
     }
 
+    @Override
+    public String toString() {
+        return
+            "{ fullname: '" + (fullname == null ? "" : fullname) + "'" +
+            ", style: '" + (style == null ? "" : style) + "'" +
+            ", script: '" + (script == null ? "" : script) + "'" +
+            ", details: " + details +
+            "," + super.toString().substring(1);
+    }
+
     @Override
     protected int readString(LittleEndianInputStream leis, StringBuilder sb, int limit) throws IOException {
         sb.setLength(0);
index 4b01a5a25693564c68e03b58c0cc4b49c4cc74ad..6c1cf5caddbac3d9c9e3c5d36cfbb88dbc8b07f3 100644 (file)
@@ -126,7 +126,7 @@ public class HemfHeader implements HemfRecord {
                 ", bytes: " + bytes +
                 ", records: " + records +
                 ", handles: " + handles +
-                ", description: '" + description + "'" +
+                ", description: '" + (description == null ? "" : description) + "'" +
                 ", nPalEntries: " + nPalEntries +
                 ", hasExtension1: " + hasExtension1 +
                 ", cbPixelFormat: " + cbPixelFormat +
index 00f77dd689d2cd7a5e3bd58e698f2064264130d1..206b7d3162eb9e88de61d9077589fb217f4cc110 100644 (file)
@@ -745,10 +745,22 @@ public class HemfMisc {
 
         @Override
         public void applyObject(HwmfGraphics ctx) {
+            if (!bitmap.isValid()) {
+                return;
+            }
             HwmfDrawProperties props = ctx.getProperties();
             props.setBrushStyle(HwmfBrushStyle.BS_PATTERN);
             BufferedImage bmp = bitmap.getImage();
             props.setBrushBitmap(bmp);
         }
+
+        @Override
+        public String toString() {
+            return
+                "{ penIndex: " + penIndex +
+                ", colorUsage: " + colorUsage +
+                ", bitmap: " + bitmap +
+                "}";
+        }
     }
 }
index fb0d2a79aae4911fd2449414816eee50d7df5d91..f62155d18210da35aaf7607063a00fa229d89433 100644 (file)
@@ -199,7 +199,11 @@ public class HemfText {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            ctx.drawString(rawTextBytes, stringLength, reference, bounds, options, dx, isUnicode());
+            // A 32-bit floating-point value that specifies the scale factor to apply along
+            // the axis to convert from page space units to .01mm units.
+            // This SHOULD be used only if the graphics mode specified by iGraphicsMode is GM_COMPATIBLE.
+            Dimension2D scl = graphicsMode == EmfGraphicsMode.GM_COMPATIBLE ? scale : null;
+            ctx.drawString(rawTextBytes, stringLength, reference, scl, bounds, options, dx, isUnicode());
         }
 
         @Override
index 955bbd9f03aacf5f1a55af9355f8e5c088a403f5..a312dc116e6330ed1712d3c91d5cc16f522ff004 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.poi.hemf.record.emf;
 
 import static org.apache.poi.hemf.record.emf.HemfDraw.readDimensionInt;
 import static org.apache.poi.hemf.record.emf.HemfDraw.readPointL;
+import static org.apache.poi.hwmf.record.HwmfDraw.normalizeBounds;
 
 import java.io.IOException;
 
@@ -135,7 +136,7 @@ public class HemfWindowing {
 
         @Override
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
-            return HemfDraw.readRectL(leis, bounds);
+            return HemfDraw.readRectL(leis, normalizeBounds(bounds));
         }
     }
 
index 9a58e57014eb617cd6a73a748bc281cdb5ef0aa7..8b8358c30f0b667a1c2a858b0c0a26e632d5c25c 100644 (file)
@@ -93,6 +93,8 @@ public class HwmfDrawProperties {
         textVAlignAsian = HwmfTextVerticalAlignment.TOP;
         rasterOp = HwmfTernaryRasterOp.PATCOPY;
         clip = null;
+        font = new HwmfFont();
+        font.initDefaults();
     }
     
     public HwmfDrawProperties(HwmfDrawProperties other) {
index 4ecca1e81acb3c8a27ae77fca6d5f898071d9f6f..dadb0916996cbb639dd99b7a9b51519a3765a805 100644 (file)
@@ -30,6 +30,7 @@ import java.awt.font.TextAttribute;
 import java.awt.font.TextLayout;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Area;
+import java.awt.geom.Dimension2D;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
@@ -333,8 +334,9 @@ public class HwmfGraphics {
         case MM_ANISOTROPIC:
             // scale window bounds to output bounds
             if (view != null) {
-                graphicsCtx.translate(view.getX() - win.getX(), view.getY() - win.getY());
+                graphicsCtx.translate(view.getCenterX(), view.getCenterY());
                 graphicsCtx.scale(view.getWidth() / win.getWidth(), view.getHeight() / win.getHeight());
+                graphicsCtx.translate(-win.getCenterX(), -win.getCenterY());
             }
             break;
         case MM_ISOTROPIC:
@@ -362,10 +364,10 @@ public class HwmfGraphics {
     }
 
     public void drawString(byte[] text, int length, Point2D reference) {
-        drawString(text, length, reference, null, null, null, false);
+        drawString(text, length, reference, null, null, null, null, false);
     }
 
-    public void drawString(byte[] text, int length, Point2D reference, Rectangle2D clip, WmfExtTextOutOptions opts, List<Integer> dx, boolean isUnicode) {
+    public void drawString(byte[] text, int length, Point2D reference, Dimension2D scale, Rectangle2D clip, WmfExtTextOutOptions opts, List<Integer> dx, boolean isUnicode) {
         final HwmfDrawProperties prop = getProperties();
 
         HwmfFont font = prop.getFont();
@@ -489,6 +491,9 @@ public class HwmfGraphics {
 
             graphicsCtx.translate(reference.getX(), reference.getY());
             graphicsCtx.rotate(angle);
+            if (scale != null) {
+                graphicsCtx.scale(scale.getWidth() < 0 ? -1 : 1, scale.getHeight() < 0 ? -1 : 1);
+            }
             graphicsCtx.translate(dst.getX(), dst.getY());
             graphicsCtx.setColor(prop.getTextColor().getColor());
             graphicsCtx.drawString(as.getIterator(), 0, 0);
index 1a7e33ec119b63ff922ee109c68d99f4d8eec1ff..a800bb0e0500684b3271fcf6352760026900f767 100644 (file)
@@ -45,9 +45,11 @@ import org.apache.poi.util.RecordFormatException;
  */
 public class HwmfBitmapDib {
 
+    private static final POILogger logger = POILogFactory.getLogger(HwmfBitmapDib.class);
+    private static final int BMP_HEADER_SIZE = 14;
     private static final int MAX_RECORD_LENGTH = HwmfPicture.MAX_RECORD_LENGTH;
 
-    public static enum BitCount {
+    public enum BitCount {
         /**
          * The image SHOULD be in either JPEG or PNG format. <6> Neither of these formats includes
          *  a color table, so this value specifies that no color table is present. See [JFIF] and [RFC2083]
@@ -129,7 +131,7 @@ public class HwmfBitmapDib {
         }
     }
 
-    public static enum Compression {
+    public enum Compression {
         /**
          * The bitmap is in uncompressed red green blue (RGB) format that is not compressed
          * and does not use color masks.
@@ -198,9 +200,7 @@ public class HwmfBitmapDib {
         }
     }
 
-    private final static POILogger logger = POILogFactory.getLogger(HwmfBitmapDib.class);
-    private static final int BMP_HEADER_SIZE = 14;
-    
+
     private int headerSize;
     private int headerWidth;
     private int headerHeight;
@@ -406,7 +406,27 @@ public class HwmfBitmapDib {
     }
 
     public boolean isValid() {
-        return (imageData != null);
+        // the recordsize ended before the image data
+        if (imageData == null) {
+            return false;
+        }
+
+        // ignore all black mono-brushes
+        if (this.headerBitCount == BitCount.BI_BITCOUNT_1) {
+            if (colorTable == null) {
+                return false;
+            }
+
+            for (Color c : colorTable) {
+                if (!Color.BLACK.equals(c)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        return true;
     }
 
     public InputStream getBMPStream() {
@@ -448,6 +468,24 @@ public class HwmfBitmapDib {
         }
     }
 
+    @Override
+    public String toString() {
+        return
+            "{ headerSize: " + headerSize +
+            ", width: " + headerWidth +
+            ", height: " + headerHeight +
+            ", planes: " + headerPlanes +
+            ", bitCount: '" + headerBitCount + "'" +
+            ", compression: '" + headerCompression + "'" +
+            ", imageSize: " + headerImageSize +
+            ", xPelsPerMeter: " + headerXPelsPerMeter +
+            ", yPelsPerMeter: " + headerYPelsPerMeter +
+            ", colorUsed: " + headerColorUsed +
+            ", colorImportant: " + headerColorImportant +
+            ", imageSize: " + (imageData == null ? 0 : imageData.length) +
+            "}";
+    }
+
     protected BufferedImage getPlaceholder() {
         BufferedImage bi = new BufferedImage(headerWidth, headerHeight, BufferedImage.TYPE_INT_ARGB);
         Graphics2D g = bi.createGraphics();
index f71becda8a8e44cfe8bff8e59c0e976d3a47d9df..17cd3c83d800074648e4f361f8347e17a3da2f35 100644 (file)
@@ -763,5 +763,15 @@ public class HwmfDraw {
         return "{ w: "+dim.getWidth()+", h: "+dim.getHeight()+" }";
     }
 
+    @Internal
+    public static Rectangle2D normalizeBounds(Rectangle2D bounds) {
+        return (bounds.getWidth() >= 0 && bounds.getHeight() >= 0) ? bounds
+                : new Rectangle2D.Double(
+                bounds.getWidth() >= 0 ? bounds.getMinX() : bounds.getMaxX(),
+                bounds.getHeight() >= 0 ? bounds.getMinY() : bounds.getMaxY(),
+                Math.abs(bounds.getWidth()),
+                Math.abs(bounds.getHeight())
+        );
+    }
 
 }
index 131699f6b39d6d40e374d0454d34953f38fdd9eb..e353750bd950ea25458304f7a79e203c25198e26 100644 (file)
@@ -498,7 +498,7 @@ public class HwmfFill {
             prop.setRasterOp(rasterOperation);
             if (bitmap.isValid()) {
                 ctx.drawImage(getImage(), srcBounds, dstBounds);
-            } else {
+            } else if (!dstBounds.isEmpty()) {
                 BufferedImage bi = new BufferedImage((int)dstBounds.getWidth(), (int)dstBounds.getHeight(), BufferedImage.TYPE_INT_ARGB);
                 ctx.drawImage(bi, dstBounds, dstBounds);
             }
index 8cb84867719f1dfe116f53aa1cc4e30fa925b235..4413983ef31887efb25836c8d49bd351e13aef8a 100644 (file)
@@ -369,6 +369,21 @@ public class HwmfFont implements FontInfo {
         return 5*LittleEndianConsts.SHORT_SIZE+8*LittleEndianConsts.BYTE_SIZE+readBytes;
     }
 
+    public void initDefaults() {
+        height = -12;
+        width = 0;
+        escapement = 0;
+        weight = 400;
+        italic = false;
+        underline = false;
+        strikeOut = false;
+        charSet = FontCharset.ANSI;
+        outPrecision = WmfOutPrecision.OUT_DEFAULT_PRECIS;
+        quality = WmfFontQuality.ANTIALIASED_QUALITY;
+        pitchAndFamily = FontFamily.FF_DONTCARE.getFlag() | (FontPitch.DEFAULT.getNativeId() << 6);
+        facename = "SansSerif";
+    }
+
     public int getHeight() {
         return height;
     }
@@ -479,7 +494,7 @@ public class HwmfFont implements FontInfo {
                 ", charset: '"+charSet+"'"+
                 ", outPrecision: '"+outPrecision+"'"+
                 ", clipPrecision: '"+clipPrecision+"'"+
-                ", qualtiy: '"+quality+"'"+
+                ", quality: '"+quality+"'"+
                 ", pitch: '"+getPitch()+"'"+
                 ", family: '"+getFamily()+"'"+
                 ", facename: '"+facename+"'"+
index 7303d2c6fe976e74261b4750e5272e1f02ae3f4e..eef15e30d7a2b1f766ee61d01e882aff66727b86 100644 (file)
@@ -454,6 +454,9 @@ public class HwmfMisc {
         
         @Override
         public void applyObject(HwmfGraphics ctx) {
+            if (patternDib != null && !patternDib.isValid()) {
+                return;
+            }
             HwmfDrawProperties prop = ctx.getProperties();
             prop.setBrushStyle(style);
             prop.setBrushBitmap(getImage());
index 020e6148d42a31ec6ced41c18b5fa2d2dad6e6a5..391215d65763c41fc0c60b1d64e79c5ddac71439 100644 (file)
@@ -391,7 +391,7 @@ public class HwmfText {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            ctx.drawString(rawTextBytes, stringLength, reference, bounds, options, dx, false);
+            ctx.drawString(rawTextBytes, stringLength, reference, null, bounds, options, dx, false);
         }
 
         public String getText(Charset charset) throws IOException {
index a3fb60bd1dd62be42dcabfa2b2fc7f47a5b4628b..34c948df0207281835e241cf9812f1287c430cc0 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.normalizeBounds;
 import static org.apache.poi.hwmf.record.HwmfDraw.pointToString;
 import static org.apache.poi.hwmf.record.HwmfDraw.readBounds;
 import static org.apache.poi.hwmf.record.HwmfDraw.readPointS;
@@ -398,6 +399,7 @@ public class HwmfWindowing {
         
         @Override
         public void applyObject(HwmfGraphics ctx) {
+            ctx.setClip(normalizeBounds(bounds), HwmfRegionMode.RGN_DIFF, false);
         }
 
         @Override