From 2b12e29c92bc2751b344e8f2d86347d4d55910c8 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sat, 6 Oct 2018 18:23:59 +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@1843025 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/hemf/record/emf/HemfFill.java | 142 +++++++++++++----- .../poi/hemf/record/emf/HemfRecordType.java | 2 +- .../org/apache/poi/hwmf/record/HwmfFill.java | 7 +- 3 files changed, 111 insertions(+), 40 deletions(-) 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 5dca14537f..f1726b7e92 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,7 +19,6 @@ 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; @@ -114,7 +113,7 @@ public class HemfFill { * optionally in combination with a brush pattern, according to a specified raster operation, stretching or * compressing the output to fit the dimensions of the destination, if necessary. */ - public static class EmfStretchBlt extends HwmfFill.WmfBitBlt implements HemfRecord { + public static class EmfStretchBlt extends HwmfFill.WmfBitBlt implements HemfRecord, HemfBounded { 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. */ @@ -129,11 +128,7 @@ public class HemfFill { */ protected int usageSrc; - /** The source bitmap header. */ - protected byte[] bmiSrc; - - /** The source bitmap bits. */ - protected byte[] bitsSrc; + protected final HwmfBitmapDib bitmap = new HwmfBitmapDib(); @Override public HemfRecordType getEmfRecordType() { @@ -142,6 +137,8 @@ public class HemfFill { @Override public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + int startIdx = leis.getReadIndex(); + long size = readRectL(leis, bounds); size += readBounds2(leis, this.dstBounds); @@ -190,26 +187,19 @@ public class HemfFill { srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), srcWidth, srcHeight); } - // size + type and size field - final int undefinedSpace1 = (int)(offBmiSrc - size - HEADER_SIZE); - assert(undefinedSpace1 >= 0); - leis.skipFully(undefinedSpace1); - size += undefinedSpace1; - - bmiSrc = IOUtils.safelyAllocate(cbBmiSrc, MAX_RECORD_LENGTH); - leis.readFully(bmiSrc); - size += cbBmiSrc; + size += readBitmap(leis, bitmap, startIdx, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc); - final int undefinedSpace2 = (int)(offBitsSrc - size - HEADER_SIZE); - assert(undefinedSpace2 >= 0); - leis.skipFully(undefinedSpace2); - size += undefinedSpace2; + return size; + } - bitsSrc = IOUtils.safelyAllocate(cbBitsSrc, MAX_RECORD_LENGTH); - leis.readFully(bitsSrc); - size += cbBitsSrc; + @Override + public Rectangle2D getRecordBounds() { + return bounds; + } - return size; + @Override + public Rectangle2D getShapeBounds(HemfGraphics ctx) { + return dstBounds; } protected boolean srcEqualsDstDimension() { @@ -217,6 +207,83 @@ public class HemfFill { } } + /** + * The EMR_STRETCHDIBITS record specifies a block transfer of pixels from a source bitmap to a + * destination rectangle, optionally in combination with a brush pattern, according to a specified raster + * operation, stretching or compressing the output to fit the dimensions of the destination, if necessary. + */ + public static class EmfStretchDiBits extends HwmfFill.WmfStretchDib implements HemfRecord, HemfBounded { + protected final Rectangle2D bounds = new Rectangle2D.Double(); + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.stretchDiBits; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + final int startIdx = leis.getReadIndex(); + + long size = readRectL(leis, bounds); + + // A 32-bit signed integer that specifies the logical x-coordinate of the upper-left + // corner of the destination rectangle. + int xDest = leis.readInt(); + int yDest = leis.readInt(); + size += 2*LittleEndianConsts.INT_SIZE; + + size += readBounds2(leis, srcBounds); + + // A 32-bit unsigned integer that specifies the offset, in bytes from the start + // of this record to the source bitmap header. + int offBmiSrc = (int)leis.readUInt(); + + // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header. + int cbBmiSrc = (int)leis.readUInt(); + + // A 32-bit unsigned integer that specifies the offset, in bytes, from the + // start of this record to the source bitmap bits. + int offBitsSrc = (int)leis.readUInt(); + + // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap bits. + int cbBitsSrc = (int)leis.readUInt(); + + // A 32-bit unsigned integer that specifies how to interpret values in the color table + // in the source bitmap header. This value MUST be in the DIBColors enumeration + colorUsage = ColorUsage.valueOf(leis.readInt()); + + // A 32-bit unsigned integer that specifies a raster operation code. + // These codes define how the color data of the source rectangle is to be combined with the color data + // of the destination rectangle and optionally a brush pattern, to achieve the final color. + // The value MUST be in the WMF Ternary Raster Operation enumeration + rasterOperation = HwmfTernaryRasterOp.valueOf(leis.readInt()); + + // A 32-bit signed integer that specifies the logical width of the destination rectangle. + int cxDest = leis.readInt(); + + // A 32-bit signed integer that specifies the logical height of the destination rectangle. + int cyDest = leis.readInt(); + + dstBounds.setRect(xDest, yDest, cxDest, cyDest); + + size += 8*LittleEndianConsts.INT_SIZE; + + size += readBitmap(leis, dib, startIdx, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc); + + return size; + } + + @Override + public Rectangle2D getRecordBounds() { + return bounds; + } + + @Override + public Rectangle2D getShapeBounds(HemfGraphics ctx) { + return dstBounds; + } + } + /** * The EMR_BITBLT record specifies a block transfer of pixels from a source bitmap to a destination rectangle, * optionally in combination with a brush pattern, according to a specified raster operation. @@ -235,7 +302,7 @@ public class HemfFill { /** The EMR_FRAMERGN record draws a border around the specified region using the specified brush. */ - public static class EmfFrameRgn extends HwmfDraw.WmfFrameRegion implements HemfRecord { + public static class EmfFrameRgn extends HwmfDraw.WmfFrameRegion implements HemfRecord, HemfBounded { private final Rectangle2D bounds = new Rectangle2D.Double(); private final List rgnRects = new ArrayList<>(); @@ -263,18 +330,23 @@ public class HemfFill { @Override public void draw(HwmfGraphics ctx) { ctx.applyObjectTableEntry(brushIndex); + ctx.fill(getShape()); + } - Area frame = new Area(); - for (Rectangle2D rct : rgnRects) { - frame.add(new Area(rct)); - } - Rectangle2D frameBounds = frame.getBounds2D(); - AffineTransform at = new AffineTransform(); - at.translate(bounds.getX()-frameBounds.getX(), bounds.getY()-frameBounds.getY()); - at.scale(bounds.getWidth()/frameBounds.getWidth(), bounds.getHeight()/frameBounds.getHeight()); - frame.transform(at); + @Override + public Rectangle2D getRecordBounds() { + return bounds; + } + + @Override + public Rectangle2D getShapeBounds(HemfGraphics ctx) { + return getShape().getBounds2D(); + } - ctx.fill(frame); + protected Area getShape() { + final Area frame = new Area(); + rgnRects.forEach((rct) -> frame.add(new Area(rct))); + return frame; } } 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 f6584443a1..addb1d63a4 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 @@ -104,7 +104,7 @@ public enum HemfRecordType { maskblt(0x0000004E, UnimplementedHemfRecord::new), plgblt(0x0000004F, UnimplementedHemfRecord::new), setDiBitsToDevice(0x00000050, HemfFill.EmfSetDiBitsToDevice::new), - stretchdibits(0x00000051, UnimplementedHemfRecord::new), + stretchDiBits(0x00000051, HemfFill.EmfStretchDiBits::new), extCreateFontIndirectW(0x00000052, HemfText.ExtCreateFontIndirectW::new), exttextouta(0x00000053, HemfText.EmfExtTextOutA::new), exttextoutw(0x00000054, HemfText.EmfExtTextOutW::new), 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 26aa55696b..3878e36ae4 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java @@ -430,13 +430,13 @@ public class HwmfFill { * the playback device context, and the destination pixels are to be combined to * form the new image. */ - private HwmfTernaryRasterOp rasterOperation; + protected HwmfTernaryRasterOp rasterOperation; /** * A 16-bit unsigned integer that defines whether the Colors field of the * DIB contains explicit RGB values or indexes into a palette. */ - private ColorUsage colorUsage; + protected ColorUsage colorUsage; /** the source rectangle. */ protected final Rectangle2D srcBounds = new Rectangle2D.Double(); @@ -448,7 +448,7 @@ public class HwmfFill { * A variable-sized DeviceIndependentBitmap Object (section 2.2.2.9) that is the * source of the color data. */ - private HwmfBitmapDib dib; + protected final HwmfBitmapDib dib = new HwmfBitmapDib(); @Override public HwmfRecordType getWmfRecordType() { @@ -471,7 +471,6 @@ public class HwmfFill { size += readBounds2(leis, srcBounds); size += readBounds2(leis, dstBounds); - dib = new HwmfBitmapDib(); size += dib.init(leis, (int)(recordSize-6-size)); return size; -- 2.39.5