From: Andreas Beeker Date: Fri, 2 Nov 2018 18:01:50 +0000 (+0000) Subject: #60656 - Support export file that contains emf and render it correctly X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=668e9a1fdf96bf22b665deb0a227e17be18a98c3;p=poi.git #60656 - Support export file that contains emf and render it correctly git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hemf@1845612 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java index d18f0d4119..367d64b084 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java @@ -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; } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java index 810c9325a3..5cd4879b15 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java @@ -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, diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java index 8690855f16..ea3eb7754e 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java @@ -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); diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfHeader.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfHeader.java index 4b01a5a256..6c1cf5cadd 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfHeader.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfHeader.java @@ -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 + diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java index 00f77dd689..206b7d3162 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java @@ -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 + + "}"; + } } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.java index fb0d2a79aa..f62155d182 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.java @@ -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 diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java index 955bbd9f03..a312dc116e 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java @@ -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)); } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java index 9a58e57014..8b8358c30f 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java @@ -93,6 +93,8 @@ public class HwmfDrawProperties { textVAlignAsian = HwmfTextVerticalAlignment.TOP; rasterOp = HwmfTernaryRasterOp.PATCOPY; clip = null; + font = new HwmfFont(); + font.initDefaults(); } public HwmfDrawProperties(HwmfDrawProperties other) { diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java index 4ecca1e81a..dadb091699 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java @@ -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 dx, boolean isUnicode) { + public void drawString(byte[] text, int length, Point2D reference, Dimension2D scale, Rectangle2D clip, WmfExtTextOutOptions opts, List 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); diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java index 1a7e33ec11..a800bb0e05 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java @@ -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(); diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java index f71becda8a..17cd3c83d8 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java @@ -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()) + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java index 131699f6b3..e353750bd9 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java @@ -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); } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java index 8cb8486771..4413983ef3 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java @@ -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+"'"+ diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java index 7303d2c6fe..eef15e30d7 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java @@ -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()); diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java index 020e6148d4..391215d657 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java @@ -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 { diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java index a3fb60bd1d..34c948df02 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java @@ -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