From f927e551cc6001f8d321770de374db9312dd6fd8 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Tue, 9 Oct 2018 20:51:14 +0000 Subject: [PATCH] #60656 - Support export file that contains emf and render it correctly git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hemf@1843342 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/hemf/draw/HemfGraphics.java | 135 +++++++++++++++++- .../poi/hemf/record/emf/HemfComment.java | 2 +- .../apache/poi/hemf/record/emf/HemfDraw.java | 129 +---------------- .../apache/poi/hemf/record/emf/HemfFill.java | 56 +++++--- .../apache/poi/hemf/record/emf/HemfMisc.java | 65 ++++++++- .../poi/hemf/record/emf/HemfRecordType.java | 2 +- .../apache/poi/hemf/record/emf/HemfText.java | 25 +--- .../poi/hemf/usermodel/HemfPicture.java | 4 +- .../apache/poi/hwmf/draw/HwmfGraphics.java | 58 ++++---- .../org/apache/poi/hwmf/record/HwmfFill.java | 9 ++ .../org/apache/poi/hwmf/record/HwmfMisc.java | 6 +- .../org/apache/poi/hwmf/record/HwmfText.java | 55 ++++--- .../poi/hemf/usermodel/HemfPictureTest.java | 83 +++++++---- 13 files changed, 363 insertions(+), 266 deletions(-) diff --git a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java index 40effa0dde..2cc1f0889d 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java +++ b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java @@ -17,6 +17,10 @@ package org.apache.poi.hemf.draw; +import static org.apache.poi.hwmf.record.HwmfBrushStyle.BS_NULL; +import static org.apache.poi.hwmf.record.HwmfBrushStyle.BS_SOLID; + +import java.awt.Color; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.geom.Path2D; @@ -29,11 +33,20 @@ import java.util.function.Consumer; import org.apache.poi.hemf.record.emf.HemfBounded; import org.apache.poi.hemf.record.emf.HemfRecord; import org.apache.poi.hwmf.draw.HwmfGraphics; +import org.apache.poi.hwmf.record.HwmfColorRef; import org.apache.poi.hwmf.record.HwmfObjectTableEntry; +import org.apache.poi.hwmf.record.HwmfPenStyle; import org.apache.poi.util.Internal; public class HemfGraphics extends HwmfGraphics { + private static final HwmfColorRef WHITE = new HwmfColorRef(Color.WHITE); + private static final HwmfColorRef LTGRAY = new HwmfColorRef(new Color(0x00C0C0C0)); + private static final HwmfColorRef GRAY = new HwmfColorRef(new Color(0x00808080)); + private static final HwmfColorRef DKGRAY = new HwmfColorRef(new Color(0x00404040)); + private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK); + + private final Deque transforms = new ArrayDeque<>(); public HemfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) { @@ -52,8 +65,7 @@ public class HemfGraphics extends HwmfGraphics { @Override public void saveProperties() { - assert(prop != null); - propStack.add(prop); + propStack.add(getProperties()); prop = new HemfDrawProperties((HemfDrawProperties)prop); } @@ -138,6 +150,125 @@ public class HemfGraphics extends HwmfGraphics { } } + @Override + public void applyObjectTableEntry(int index) { + if ((index & 0x80000000) != 0) { + selectStockObject(index); + } else { + super.applyObjectTableEntry(index); + } + } + + private void selectStockObject(int objectIndex) { + final HemfDrawProperties prop = getProperties(); + switch (objectIndex) { + case 0x80000000: + // WHITE_BRUSH - A white, solid-color brush + // BrushStyle: BS_SOLID + // Color: 0x00FFFFFF + prop.setBrushColor(WHITE); + prop.setBrushStyle(BS_SOLID); + break; + case 0x80000001: + // LTGRAY_BRUSH - A light gray, solid-color brush + // BrushStyle: BS_SOLID + // Color: 0x00C0C0C0 + prop.setBrushColor(LTGRAY); + prop.setBrushStyle(BS_SOLID); + break; + case 0x80000002: + // GRAY_BRUSH - A gray, solid-color brush + // BrushStyle: BS_SOLID + // Color: 0x00808080 + prop.setBrushColor(GRAY); + prop.setBrushStyle(BS_SOLID); + break; + case 0x80000003: + // DKGRAY_BRUSH - A dark gray, solid color brush + // BrushStyle: BS_SOLID + // Color: 0x00404040 + prop.setBrushColor(DKGRAY); + prop.setBrushStyle(BS_SOLID); + break; + case 0x80000004: + // BLACK_BRUSH - A black, solid color brush + // BrushStyle: BS_SOLID + // Color: 0x00000000 + prop.setBrushColor(BLACK); + prop.setBrushStyle(BS_SOLID); + break; + case 0x80000005: + // NULL_BRUSH - A null brush + // BrushStyle: BS_NULL + prop.setBrushStyle(BS_NULL); + break; + case 0x80000006: + // WHITE_PEN - A white, solid-color pen + // PenStyle: PS_COSMETIC + PS_SOLID + // ColorRef: 0x00FFFFFF + prop.setPenStyle(HwmfPenStyle.valueOf(0)); + prop.setPenWidth(1); + prop.setPenColor(WHITE); + break; + case 0x80000007: + // BLACK_PEN - A black, solid-color pen + // PenStyle: PS_COSMETIC + PS_SOLID + // ColorRef: 0x00000000 + prop.setPenStyle(HwmfPenStyle.valueOf(0)); + prop.setPenWidth(1); + prop.setPenColor(BLACK); + break; + case 0x80000008: + // NULL_PEN - A null pen + // PenStyle: PS_NULL + prop.setPenStyle(HwmfPenStyle.valueOf(HwmfPenStyle.HwmfLineDash.NULL.wmfFlag)); + break; + case 0x8000000A: + // OEM_FIXED_FONT - A fixed-width, OEM character set + // Charset: OEM_CHARSET + // PitchAndFamily: FF_DONTCARE + FIXED_PITCH + break; + case 0x8000000B: + // ANSI_FIXED_FONT - A fixed-width font + // Charset: ANSI_CHARSET + // PitchAndFamily: FF_DONTCARE + FIXED_PITCH + break; + case 0x8000000C: + // ANSI_VAR_FONT - A variable-width font + // Charset: ANSI_CHARSET + // PitchAndFamily: FF_DONTCARE + VARIABLE_PITCH + break; + case 0x8000000D: + // SYSTEM_FONT - A font that is guaranteed to be available in the operating system + break; + case 0x8000000E: + // DEVICE_DEFAULT_FONT + // The default font that is provided by the graphics device driver for the current output device + break; + case 0x8000000F: + // DEFAULT_PALETTE + // The default palette that is defined for the current output device. + break; + case 0x80000010: + // SYSTEM_FIXED_FONT + // A fixed-width font that is guaranteed to be available in the operating system. + break; + case 0x80000011: + // DEFAULT_GUI_FONT + // The default font that is used for user interface objects such as menus and dialog boxes. + break; + case 0x80000012: + // DC_BRUSH + // The solid-color brush that is currently selected in the playback device context. + break; + case 0x80000013: + // DC_PEN + // The solid-color pen that is currently selected in the playback device context. + break; + } + } + + /** saves the current affine transform on the stack */ private void saveTransform() { diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java index 1752e99e1a..c24562a047 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java @@ -40,7 +40,7 @@ import org.apache.poi.util.RecordFormatException; */ @Internal public class HemfComment { - private static final int MAX_RECORD_LENGTH = 1_000_000; + private static final int MAX_RECORD_LENGTH = 2_000_000; public enum HemfCommentRecordType { emfGeneric(-1, EmfCommentDataGeneric::new, false), 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 f9c6e975a2..0cffdbc4b5 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 @@ -47,12 +47,6 @@ public class HemfDraw { */ public static class EmfSelectObject extends WmfSelectObject implements HemfRecord { - private static final HwmfColorRef WHITE = new HwmfColorRef(Color.WHITE); - private static final HwmfColorRef LTGRAY = new HwmfColorRef(new Color(0x00C0C0C0)); - private static final HwmfColorRef GRAY = new HwmfColorRef(new Color(0x00808080)); - private static final HwmfColorRef DKGRAY = new HwmfColorRef(new Color(0x00404040)); - private static final HwmfColorRef BLACK = new HwmfColorRef(Color.BLACK); - private static final String[] STOCK_IDS = { "0x80000000 /* WHITE_BRUSH */", "0x80000001 /* LTGRAY_BRUSH */", @@ -88,124 +82,6 @@ public class HemfDraw { return LittleEndianConsts.INT_SIZE; } - @Override - public void draw(HemfGraphics ctx) { - if ((objectIndex & 0x80000000) != 0) { - selectStockObject(ctx); - } else { - super.draw(ctx); - } - } - - private void selectStockObject(HemfGraphics ctx) { - final HemfDrawProperties prop = ctx.getProperties(); - switch (objectIndex) { - case 0x80000000: - // WHITE_BRUSH - A white, solid-color brush - // BrushStyle: BS_SOLID - // Color: 0x00FFFFFF - prop.setBrushColor(WHITE); - prop.setBrushStyle(BS_SOLID); - break; - case 0x80000001: - // LTGRAY_BRUSH - A light gray, solid-color brush - // BrushStyle: BS_SOLID - // Color: 0x00C0C0C0 - prop.setBrushColor(LTGRAY); - prop.setBrushStyle(BS_SOLID); - break; - case 0x80000002: - // GRAY_BRUSH - A gray, solid-color brush - // BrushStyle: BS_SOLID - // Color: 0x00808080 - prop.setBrushColor(GRAY); - prop.setBrushStyle(BS_SOLID); - break; - case 0x80000003: - // DKGRAY_BRUSH - A dark gray, solid color brush - // BrushStyle: BS_SOLID - // Color: 0x00404040 - prop.setBrushColor(DKGRAY); - prop.setBrushStyle(BS_SOLID); - break; - case 0x80000004: - // BLACK_BRUSH - A black, solid color brush - // BrushStyle: BS_SOLID - // Color: 0x00000000 - prop.setBrushColor(BLACK); - prop.setBrushStyle(BS_SOLID); - break; - case 0x80000005: - // NULL_BRUSH - A null brush - // BrushStyle: BS_NULL - prop.setBrushStyle(BS_NULL); - break; - case 0x80000006: - // WHITE_PEN - A white, solid-color pen - // PenStyle: PS_COSMETIC + PS_SOLID - // ColorRef: 0x00FFFFFF - prop.setPenStyle(HwmfPenStyle.valueOf(0)); - prop.setPenWidth(1); - prop.setPenColor(WHITE); - break; - case 0x80000007: - // BLACK_PEN - A black, solid-color pen - // PenStyle: PS_COSMETIC + PS_SOLID - // ColorRef: 0x00000000 - prop.setPenStyle(HwmfPenStyle.valueOf(0)); - prop.setPenWidth(1); - prop.setPenColor(BLACK); - break; - case 0x80000008: - // NULL_PEN - A null pen - // PenStyle: PS_NULL - prop.setPenStyle(HwmfPenStyle.valueOf(HwmfPenStyle.HwmfLineDash.NULL.wmfFlag)); - break; - case 0x8000000A: - // OEM_FIXED_FONT - A fixed-width, OEM character set - // Charset: OEM_CHARSET - // PitchAndFamily: FF_DONTCARE + FIXED_PITCH - break; - case 0x8000000B: - // ANSI_FIXED_FONT - A fixed-width font - // Charset: ANSI_CHARSET - // PitchAndFamily: FF_DONTCARE + FIXED_PITCH - break; - case 0x8000000C: - // ANSI_VAR_FONT - A variable-width font - // Charset: ANSI_CHARSET - // PitchAndFamily: FF_DONTCARE + VARIABLE_PITCH - break; - case 0x8000000D: - // SYSTEM_FONT - A font that is guaranteed to be available in the operating system - break; - case 0x8000000E: - // DEVICE_DEFAULT_FONT - // The default font that is provided by the graphics device driver for the current output device - break; - case 0x8000000F: - // DEFAULT_PALETTE - // The default palette that is defined for the current output device. - break; - case 0x80000010: - // SYSTEM_FIXED_FONT - // A fixed-width font that is guaranteed to be available in the operating system. - break; - case 0x80000011: - // DEFAULT_GUI_FONT - // The default font that is used for user interface objects such as menus and dialog boxes. - break; - case 0x80000012: - // DC_BRUSH - // The solid-color brush that is currently selected in the playback device context. - break; - case 0x80000013: - // DC_PEN - // The solid-color pen that is currently selected in the playback device context. - break; - } - } - @Override public String toString() { return "{ index: "+ @@ -1166,6 +1042,11 @@ public class HemfDraw { private static void polyTo(final HemfGraphics ctx, final Path2D poly) { final PathIterator pi = poly.getPathIterator(null); + if (pi.isDone()) { + // ignore empty polys + return; + } + // ignore dummy start point (moveTo) pi.next(); assert (!pi.isDone()); 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 f1726b7e92..ab632e28c8 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 @@ -19,6 +19,7 @@ package org.apache.poi.hemf.record.emf; 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 java.awt.geom.AffineTransform; import java.awt.geom.Area; @@ -100,7 +101,7 @@ public class HemfFill { @Override public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { long size = readPointL(leis, start); - size = colorRef.init(leis); + size += colorRef.init(leis); // A 32-bit unsigned integer that specifies how to use the Color value to determine the area for // the flood fill operation. The value MUST be in the FloodFill enumeration mode = (int)leis.readUInt(); @@ -117,7 +118,7 @@ public class HemfFill { protected final Rectangle2D bounds = new Rectangle2D.Double(); /** An XForm object that specifies a world-space to page-space transform to apply to the source bitmap. */ - protected final byte[] xformSrc = new byte[24]; + protected final AffineTransform xFormSrc = new AffineTransform(); /** A WMF ColorRef object that specifies the background color of the source bitmap. */ protected final HwmfColorRef bkColorSrc = new HwmfColorRef(); @@ -155,8 +156,7 @@ public class HemfFill { final Point2D srcPnt = new Point2D.Double(); size += readPointL(leis, srcPnt); - leis.readFully(xformSrc); - size += 24; + size += readXForm(leis, xFormSrc); size += bkColorSrc.init(leis); @@ -168,6 +168,10 @@ public class HemfFill { // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header. final int cbBmiSrc = (int)leis.readUInt(); + size += 3*LittleEndianConsts.INT_SIZE; + if (size <= recordSize) { + return size; + } // A 32-bit unsigned integer that specifies the offset, in bytes, from the // start of this record to the source bitmap bits in the BitmapBuffer field. @@ -176,7 +180,7 @@ public class HemfFill { // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap bits. final int cbBitsSrc = (int)leis.readUInt(); - size += 5*LittleEndianConsts.INT_SIZE; + size += 2*LittleEndianConsts.INT_SIZE; if (srcEqualsDstDimension()) { srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), dstBounds.getWidth(), dstBounds.getHeight()); @@ -205,6 +209,16 @@ public class HemfFill { protected boolean srcEqualsDstDimension() { return false; } + + @Override + public String toString() { + return + "{ bounds: { x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+"}"+ + ", xFormSrc: { scaleX: "+xFormSrc.getScaleX()+", shearX: "+xFormSrc.getShearX()+", transX: "+xFormSrc.getTranslateX()+", scaleY: "+xFormSrc.getScaleY()+", shearY: "+xFormSrc.getShearY()+", transY: "+xFormSrc.getTranslateY()+" }"+ + ", bkColorSrc: "+bkColorSrc+ + ", usageSrc: "+usageSrc+", " + + super.toString().substring(1); + } } /** @@ -589,18 +603,24 @@ public class HemfFill { } static long readBitmap(final LittleEndianInputStream leis, final HwmfBitmapDib bitmap, - final int startIdx, final int offBmiSrc, final int cbBmiSrc, final int offBitsSrc, int cbBitsSrc) + final int startIdx, final int offBmi, final int cbBmi, final int offBits, int cbBits) throws IOException { - final int offCurr = leis.getReadIndex()-startIdx; - final int undefinedSpace1 = offBmiSrc-offCurr; - assert(undefinedSpace1 >= 0); + if (offBmi == 0) { + return 0; + } + + final int offCurr = leis.getReadIndex()-(startIdx-HEADER_SIZE); + final int undefinedSpace1 = offBmi-offCurr; + if (undefinedSpace1 < 0) { + return 0; + } - final int undefinedSpace2 = offBitsSrc-offCurr-cbBmiSrc-undefinedSpace1; + final int undefinedSpace2 = offBits-offCurr-cbBmi-undefinedSpace1; assert(undefinedSpace2 >= 0); leis.skipFully(undefinedSpace1); - if (cbBmiSrc == 0 || cbBitsSrc == 0) { + if (cbBmi == 0 || cbBits == 0) { return undefinedSpace1; } @@ -608,18 +628,18 @@ public class HemfFill { if (undefinedSpace2 == 0) { leisDib = leis; } else { - final ByteArrayOutputStream bos = new ByteArrayOutputStream(cbBmiSrc+cbBitsSrc); - final long cbBmiSrcAct = IOUtils.copy(leis, bos, cbBmiSrc); - assert (cbBmiSrcAct == cbBmiSrc); + final ByteArrayOutputStream bos = new ByteArrayOutputStream(cbBmi+cbBits); + final long cbBmiSrcAct = IOUtils.copy(leis, bos, cbBmi); + assert (cbBmiSrcAct == cbBmi); leis.skipFully(undefinedSpace2); - final long cbBitsSrcAct = IOUtils.copy(leis, bos, cbBitsSrc); - assert (cbBitsSrcAct == cbBitsSrc); + final long cbBitsSrcAct = IOUtils.copy(leis, bos, cbBits); + assert (cbBitsSrcAct == cbBits); leisDib = new LittleEndianInputStream(new ByteArrayInputStream(bos.toByteArray())); } - final int dibSize = cbBmiSrc+cbBitsSrc; + final int dibSize = cbBmi+cbBits; final int dibSizeAct = bitmap.init(leisDib, dibSize); assert (dibSizeAct <= dibSize); - return undefinedSpace1 + cbBmiSrc + undefinedSpace2 + cbBitsSrc; + return undefinedSpace1 + cbBmi + undefinedSpace2 + cbBits; } 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 d128084c6f..dd444f7c0e 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 @@ -33,6 +33,7 @@ import org.apache.poi.hwmf.record.HwmfBinaryRasterOp; import org.apache.poi.hwmf.record.HwmfBitmapDib; import org.apache.poi.hwmf.record.HwmfBrushStyle; import org.apache.poi.hwmf.record.HwmfColorRef; +import org.apache.poi.hwmf.record.HwmfFill; import org.apache.poi.hwmf.record.HwmfHatchStyle; import org.apache.poi.hwmf.record.HwmfMapMode; import org.apache.poi.hwmf.record.HwmfMisc; @@ -65,8 +66,8 @@ public class HemfMisc { int size = 2 * LittleEndianConsts.INT_SIZE; - if (offPalEntries > 0) { - int undefinedSpace1 = (int) (offPalEntries - size - HEADER_SIZE); + if (nPalEntries > 0 && offPalEntries > 0) { + int undefinedSpace1 = (int) (offPalEntries - (size + HEADER_SIZE)); assert (undefinedSpace1 >= 0); leis.skipFully(undefinedSpace1); size += undefinedSpace1; @@ -283,6 +284,59 @@ public class HemfMisc { } } + /** + * The EMR_CREATEDIBPATTERNBRUSHPT record defines a pattern brush for graphics operations. + * The pattern is specified by a DIB. + */ + public static class EmfCreateDibPatternBrushPt extends HwmfMisc.WmfDibCreatePatternBrush implements HemfRecord { + protected int brushIdx; + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.createDibPatternBrushPt; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + final int startIdx = leis.getReadIndex(); + + style = HwmfBrushStyle.BS_DIBPATTERNPT; + + // A 32-bit unsigned integer that specifies the index of the pattern brush + // object in the EMF Object Table + brushIdx = (int)leis.readUInt(); + + // A 32-bit unsigned integer that specifies how to interpret values in the color + // table in the DIB header. This value MUST be in the DIBColors enumeration + colorUsage = HwmfFill.ColorUsage.valueOf((int)leis.readUInt()); + + // A 32-bit unsigned integer that specifies the offset from the start of this + // record to the DIB header. + final int offBmi = leis.readInt(); + + // A 32-bit unsigned integer that specifies the size of the DIB header. + final int cbBmi = leis.readInt(); + + // A 32-bit unsigned integer that specifies the offset from the start of this record to the DIB bits. + final int offBits = leis.readInt(); + + // A 32-bit unsigned integer that specifies the size of the DIB bits. + final int cbBits = leis.readInt(); + + int size = 6*LittleEndianConsts.INT_SIZE; + + patternDib = new HwmfBitmapDib(); + size += readBitmap(leis, patternDib, startIdx, offBmi, cbBmi, offBits, cbBits); + return size; + } + + @Override + public void draw(HemfGraphics ctx) { + ctx.addObjectTableEntry(this, brushIdx); + } + + } + /** * The EMR_DELETEOBJECT record deletes a graphics object, which is specified by its index * in the EMF Object Table @@ -519,5 +573,12 @@ public class HemfMisc { return size + LittleEndianConsts.INT_SIZE; } + + @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+" }"; + } } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java index addb1d63a4..35d09b18af 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java @@ -117,7 +117,7 @@ public enum HemfRecordType { polyPolygon16(0x0000005B, HemfDraw.EmfPolyPolygon16::new), polyDraw16(0x0000005C, HemfDraw.EmfPolyDraw16::new), createmonobrush16(0x0000005D, UnimplementedHemfRecord::new), - createdibpatternbrushpt(0x0000005E, UnimplementedHemfRecord::new), + createDibPatternBrushPt(0x0000005E, HemfMisc.EmfCreateDibPatternBrushPt::new), extCreatePen(0x0000005F, HemfMisc.EmfExtCreatePen::new), polytextouta(0x00000060, HemfText.PolyTextOutA::new), polytextoutw(0x00000061, HemfText.PolyTextOutW::new), 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 d78b750aa3..92528c8993 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 @@ -113,7 +113,7 @@ public class HemfText { int offDx = (int)leis.readUInt(); size += LittleEndianConsts.INT_SIZE; - int undefinedSpace1 = (int)(offString - size - HEADER_SIZE); + int undefinedSpace1 = (int)(offString - (size + HEADER_SIZE)); assert (undefinedSpace1 >= 0); leis.skipFully(undefinedSpace1); size += undefinedSpace1; @@ -124,7 +124,7 @@ public class HemfText { dx.clear(); if (offDx > 0) { - int undefinedSpace2 = (int) (offDx - size - HEADER_SIZE); + int undefinedSpace2 = (int) (offDx - (size + HEADER_SIZE)); assert (undefinedSpace2 >= 0); leis.skipFully(undefinedSpace2); size += undefinedSpace2; @@ -149,10 +149,6 @@ public class HemfText { return size; } - protected boolean isUnicode() { - return false; - } - /** * * To be implemented! We need to get the current character set @@ -185,21 +181,10 @@ public class HemfText { @Override public String toString() { - String text = ""; - try { - text = getText(isUnicode() ? Charsets.UTF_16LE : LocaleUtil.CHARSET_1252); - } catch (IOException ignored) { - } - return - "{ bounds: { x: "+bounds.getX()+ - ", y: "+bounds.getY()+ - ", w: "+bounds.getWidth()+ - ", h: "+bounds.getHeight()+ - "}, graphicsMode: '"+graphicsMode+"'"+ - ", scale: { w: "+scale.getWidth()+", h: "+scale.getHeight()+" }"+ - ", text: '"+text.replaceAll("\\p{Cntrl}",".")+"'"+ - "}"; + "{ graphicsMode: '"+graphicsMode+"'"+ + ", scale: { w: "+scale.getWidth()+", h: "+scale.getHeight()+" },"+ + super.toString().substring(1); } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java b/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java index 3a68547823..77f34fd3bf 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java +++ b/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java @@ -108,11 +108,11 @@ public class HemfPicture implements Iterable { ctx.scale(graphicsBounds.getWidth()/emfBounds.getWidth(), graphicsBounds.getHeight()/emfBounds.getHeight()); ctx.translate(-emfBounds.getCenterX(), -emfBounds.getCenterY()); - - + int idx = 0; HemfGraphics g = new HemfGraphics(ctx, emfBounds); for (HemfRecord r : getRecords()) { g.draw(r); + idx++; } } finally { ctx.setTransform(at); 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 23ccd1c70f..7ac295e5ce 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java @@ -323,11 +323,7 @@ public class HwmfGraphics { } public void drawString(byte[] text, Rectangle2D bounds) { - drawString(text, bounds, null); - } - - public void drawString(byte[] text, Rectangle2D bounds, List dx) { - drawString(text, bounds, dx, false); + drawString(text, bounds, null, false); } public void drawString(byte[] text, Rectangle2D bounds, List dx, boolean isUnicode) { @@ -352,10 +348,12 @@ public class HwmfGraphics { } String textString = new String(text, charset).trim(); + if (textString.isEmpty()) { + return; + } AttributedString as = new AttributedString(textString); - if (dx == null || dx.isEmpty()) { - addAttributes(as, font); - } else { + addAttributes(as, font); + if (dx != null && !dx.isEmpty()) { //for multi-byte encodings (e.g. Shift_JIS), the byte length //might not equal the string length(). //The x information is stored in dx[], an array parallel to the @@ -371,31 +369,21 @@ public class HwmfGraphics { //dxNormed[0] = 13 textString.get(0) = U+30D7 //dxNormed[1] = 14 textString.get(1) = U+30ED - final List dxNormed; - if (textString.length() == text.length) { - dxNormed = new ArrayList<>(dx); - } else { - dxNormed = new ArrayList<>(dx.size()); - int dxPosition = 0; - int[] chars = {0}; - for (int offset = 0; offset < textString.length(); ) { - dxNormed.add(dx.get(dxPosition)); - chars[0] = textString.codePointAt(offset); - //now figure out how many bytes it takes to encode that - //code point in the charset - int byteLength = new String(chars, 0, chars.length).getBytes(charset).length; - dxPosition += byteLength; - offset += Character.charCount(chars[0]); + final int cps = textString.codePointCount(0, textString.length()); + final int unicodeSteps = Math.max(dx.size()/cps, 1); + int dxPosition = 0; + int beginIndex = 0; + int[] chars = {0}; + while (beginIndex < textString.length() && dxPosition < dx.size()) { + int endIndex = textString.offsetByCodePoints(beginIndex, 1); + if (beginIndex > 0) { + // Tracking works as a prefix/advance space on characters whereas + // dx[...] is the complete width of the current char + // therefore we need to add the additional/suffix width to the next char + as.addAttribute(TextAttribute.TRACKING, (float)((dx.get(dxPosition) - fontW) / fontH), beginIndex, endIndex); } - } - - int cps = textString.codePointCount(0, textString.length()); - for (int i = 0; i < Math.min(dxNormed.size(),cps-1); i++) { - addAttributes(as, font); - // Tracking works as a prefix/advance space on characters whereas - // dx[...] is the complete width of the current char - // therefore we need to add the additional/suffix width to the next char - as.addAttribute(TextAttribute.TRACKING, (dxNormed.get(i) - fontW) / fontH, i + 1, i + 2); + dxPosition += (isUnicode) ? unicodeSteps : (endIndex-beginIndex); + beginIndex = endIndex; } } @@ -413,7 +401,7 @@ public class HwmfGraphics { graphicsCtx.fill(new Rectangle2D.Double(0, 0, bounds.getWidth(), bounds.getHeight())); } graphicsCtx.setColor(getProperties().getTextColor().getColor()); - graphicsCtx.drawString(as.getIterator(), 0, 0); // (float)bounds.getX(), (float)bounds.getY()); + graphicsCtx.drawString(as.getIterator(), 0, 0); } finally { graphicsCtx.setTransform(at); } @@ -425,7 +413,9 @@ public class HwmfGraphics { as.addAttribute(TextAttribute.FAMILY, fontInfo.getTypeface()); as.addAttribute(TextAttribute.SIZE, getFontHeight(font)); - as.addAttribute(TextAttribute.STRIKETHROUGH, font.isStrikeOut()); + if (font.isStrikeOut()) { + as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON); + } if (font.isUnderline()) { as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); } 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 3878e36ae4..0d6f85e4d8 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java @@ -415,6 +415,15 @@ public class HwmfFill { public void draw(HwmfGraphics ctx) { } + + @Override + public String toString() { + return + "{ rasterOperation: '"+rasterOperation+"'"+ + ", srcBounds: { x: "+srcBounds.getX()+", y: "+srcBounds.getY()+", w: "+srcBounds.getWidth()+", h: "+srcBounds.getHeight()+" }"+ + ", dstBounds: { x: "+dstBounds.getX()+", y: "+dstBounds.getY()+", w: "+dstBounds.getWidth()+", h: "+dstBounds.getHeight()+" }"+ + "}"; + } } /** 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 e66678389d..70e1d58aed 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java @@ -377,7 +377,7 @@ public class HwmfMisc { */ public static class WmfDibCreatePatternBrush implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry { - private HwmfBrushStyle style; + protected HwmfBrushStyle style; /** * A 16-bit unsigned integer that defines whether the Colors field of a DIB @@ -388,9 +388,9 @@ public class HwmfMisc { * * If the Style field specified anything but BS_PATTERN, this field MUST be one of the ColorUsage values. */ - private ColorUsage colorUsage; + protected ColorUsage colorUsage; - private HwmfBitmapDib patternDib; + protected HwmfBitmapDib patternDib; private HwmfBitmap16 pattern16; @Override 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 cb2cbd7560..5228d11d0b 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java @@ -31,6 +31,7 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; +import org.apache.commons.codec.Charsets; import org.apache.poi.hwmf.draw.HwmfDrawProperties; import org.apache.poi.hwmf.draw.HwmfGraphics; import org.apache.poi.hwmf.record.HwmfMisc.WmfSetMapMode; @@ -39,6 +40,7 @@ import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.IOUtils; import org.apache.poi.util.LittleEndianConsts; import org.apache.poi.util.LittleEndianInputStream; +import org.apache.poi.util.LocaleUtil; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.apache.poi.util.RecordFormatException; @@ -291,6 +293,10 @@ public class HwmfText { public boolean isClipped() { return ETO_CLIPPED.isSet(flag); } + + public boolean isYDisplaced() { + return ETO_PDY.isSet(flag); + } } /** @@ -393,37 +399,11 @@ 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); + ctx.drawString(rawTextBytes, bounds, dx, false); } - public String getText(Charset charset) throws IOException { - StringBuilder sb = new StringBuilder(); - try (Reader r = new InputStreamReader(new ByteArrayInputStream(rawTextBytes), charset)) { - for (int i = 0; i < stringLength; i++) { - sb.appendCodePoint(readCodePoint(r)); - } - } - return sb.toString(); - } - - //TODO: move this to IOUtils? - private int readCodePoint(Reader r) throws IOException { - int c1 = r.read(); - if (c1 == -1) { - throw new EOFException("Tried to read beyond byte array"); - } - if (!Character.isHighSurrogate((char)c1)) { - return c1; - } - int c2 = r.read(); - if (c2 == -1) { - throw new EOFException("Tried to read beyond byte array"); - } - if (!Character.isLowSurrogate((char)c2)) { - throw new RecordFormatException("Expected low surrogate after high surrogate"); - } - return Character.toCodePoint((char)c1, (char)c2); + return new String(rawTextBytes, charset); } public Point2D getReference() { @@ -433,6 +413,25 @@ public class HwmfText { public Rectangle2D getBounds() { return bounds; } + + protected boolean isUnicode() { + return false; + } + + @Override + public String toString() { + String text = ""; + try { + text = getText(isUnicode() ? Charsets.UTF_16LE : LocaleUtil.CHARSET_1252); + } catch (IOException ignored) { + } + + return + "{ reference: { x: "+reference.getX()+", y: "+reference.getY()+" }"+ + ", bounds: { x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+"}"+ + ", text: '"+text.replaceAll("\\p{Cntrl}",".")+"'"+ + "}"; + } } public enum HwmfTextAlignment { diff --git a/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java b/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java index 22c29cbb6d..3b078e1213 100644 --- a/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java +++ b/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java @@ -38,6 +38,8 @@ import java.io.InputStream; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; import javax.imageio.ImageIO; @@ -64,42 +66,61 @@ public class HemfPictureTest { @Test @Ignore("Only for manual tests") public void paint() throws IOException { - File f = new File("picture_14.emf"); // sl_samples.getFile("wrench.emf"); - try (FileInputStream fis = new FileInputStream(f)) { - HemfPicture emf = new HemfPicture(fis); - - Dimension2D dim = emf.getSize(); - int width = Units.pointsToPixel(dim.getWidth()); - // keep aspect ratio for height - int height = Units.pointsToPixel(dim.getHeight()); - double max = Math.max(width, height); - if (max > 1500) { - width *= 1500 / max; - height *= 1500 / max; - } +// File f = new File("picture_14.emf"); // sl_samples.getFile("wrench.emf"); +// try (FileInputStream fis = new FileInputStream(f)) { + + try (ZipInputStream zis = new ZipInputStream(new FileInputStream("tmp/emf.zip"))) { + for (;;) { + ZipEntry ze = zis.getNextEntry(); + if (ze == null) { + break; + } + final File pngName = new File("build/tmp",ze.getName().replaceFirst( ".*/","").replace(".emf", ".png")); + if (pngName.exists()) { + continue; + } - BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - Graphics2D 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); - - FileWriter fw = new FileWriter("record-list.txt"); - int i=0; - for (HemfRecord r : emf.getRecords()) { - if (r.getEmfRecordType() != HemfRecordType.comment) { - fw.write(i + " " + r.getEmfRecordType() + " " + r.toString() + "\n"); + + // 263/263282_000.emf +// if (!ze.getName().contains("298/298837_000.emf")) continue; + HemfPicture emf = new HemfPicture(zis); + System.out.println(ze.getName()); + + Dimension2D dim = emf.getSize(); + int width = Units.pointsToPixel(dim.getWidth()); + // keep aspect ratio for height + int height = Units.pointsToPixel(dim.getHeight()); + double max = Math.max(width, height); + if (max > 1500) { + width *= 1500 / max; + height *= 1500 / max; } - i++; - } - fw.close(); - emf.draw(g, new Rectangle2D.Double(0,0,width,height)); + BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D 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); + + FileWriter fw = new FileWriter("record-list.txt"); + int i = 0; + for (HemfRecord r : emf.getRecords()) { + if (r.getEmfRecordType() != HemfRecordType.comment) { + fw.write(i + " " + r.getEmfRecordType() + " " + r.toString() + "\n"); + } + i++; + } + fw.close(); + + emf.draw(g, new Rectangle2D.Double(0, 0, width, height)); - g.dispose(); + g.dispose(); - ImageIO.write(bufImg, "PNG", new File("bla.png")); + ImageIO.write(bufImg, "PNG", pngName); + +// break; + } } } -- 2.39.5