From commits-return-11639-archive-asf-public=cust-asf.ponee.io@poi.apache.org Fri Sep 14 23:37:42 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id 9086318067E for ; Fri, 14 Sep 2018 23:37:40 +0200 (CEST) Received: (qmail 11257 invoked by uid 500); 14 Sep 2018 21:37:39 -0000 Mailing-List: contact commits-help@poi.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@poi.apache.org Delivered-To: mailing list commits@poi.apache.org Received: (qmail 11185 invoked by uid 99); 14 Sep 2018 21:37:39 -0000 Received: from Unknown (HELO svn01-us-west.apache.org) (209.188.14.144) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 14 Sep 2018 21:37:39 +0000 Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id E42943A0D40 for ; Fri, 14 Sep 2018 21:37:38 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1840956 [2/5] - in /poi/branches/hemf/src: java/org/apache/poi/util/ ooxml/java/org/apache/poi/xdgf/geom/ ooxml/java/org/apache/poi/xdgf/usermodel/ ooxml/java/org/apache/poi/xdgf/util/ scratchpad/src/org/apache/poi/hemf/draw/ scratchpad/sr... Date: Fri, 14 Sep 2018 21:37:38 -0000 To: commits@poi.apache.org From: kiwiwings@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20180914213738.E42943A0D40@svn01-us-west.apache.org> Added: poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java URL: http://svn.apache.org/viewvc/poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java?rev=1840956&view=auto ============================================================================== --- poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java (added) +++ poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java Fri Sep 14 21:37:37 2018 @@ -0,0 +1,620 @@ +/* ==================================================================== + 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 static org.apache.poi.hemf.record.emf.HemfDraw.readRectL; +import static org.apache.poi.hemf.record.emf.HemfDraw.readPointL; +import static org.apache.poi.hemf.record.emf.HemfRecordIterator.HEADER_SIZE; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Area; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.hwmf.draw.HwmfGraphics; +import org.apache.poi.hwmf.record.HwmfBitmapDib; +import org.apache.poi.hwmf.record.HwmfColorRef; +import org.apache.poi.hwmf.record.HwmfDraw; +import org.apache.poi.hwmf.record.HwmfFill; +import org.apache.poi.hwmf.record.HwmfFill.ColorUsage; +import org.apache.poi.hwmf.record.HwmfTernaryRasterOp; +import org.apache.poi.util.IOUtils; +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianConsts; +import org.apache.poi.util.LittleEndianInputStream; + +public class HemfFill { + private static final int MAX_RECORD_LENGTH = 10_000_000; + + public enum HemfRegionMode { + RGN_AND(0x01), + RGN_OR(0x02), + RGN_XOR(0x03), + RGN_DIFF(0x04), + RGN_COPY(0x05); + + int flag; + HemfRegionMode(int flag) { + this.flag = flag; + } + + public static HemfRegionMode valueOf(int flag) { + for (HemfRegionMode rm : values()) { + if (rm.flag == flag) return rm; + } + return null; + } + + } + + + /** + * The EMR_SETPOLYFILLMODE record defines polygon fill mode. + */ + public static class EmfSetPolyfillMode extends HwmfFill.WmfSetPolyfillMode implements HemfRecord { + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.setPolyfillMode; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + // A 32-bit unsigned integer that specifies the polygon fill mode and + // MUST be in the PolygonFillMode enumeration. + polyfillMode = HwmfPolyfillMode.valueOf((int)leis.readUInt()); + return LittleEndianConsts.INT_SIZE; + } + } + + public static class EmfExtFloodFill extends HwmfFill.WmfExtFloodFill implements HemfRecord { + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.extFloodFill; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + long size = readPointL(leis, start); + 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(); + return size + LittleEndianConsts.INT_SIZE; + } + } + + /** + * The EMR_STRETCHBLT 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 EmfStretchBlt extends HwmfFill.WmfBitBlt implements HemfRecord { + 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]; + + /** A WMF ColorRef object that specifies the background color of the source bitmap. */ + protected final HwmfColorRef bkColorSrc = new HwmfColorRef(); + + /** + * 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 + */ + protected int usageSrc; + + /** The source bitmap header. */ + protected byte[] bmiSrc; + + /** The source bitmap bits. */ + protected byte[] bitsSrc; + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.stretchBlt; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + long size = readRectL(leis, bounds); + + size += readBounds2(leis, this.dstBounds); + + // A 32-bit unsigned integer that specifies the raster operation code. This code defines 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. + int rasterOpIndex = (int)leis.readUInt(); + + rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex); + + size += LittleEndianConsts.INT_SIZE; + + final Point2D srcPnt = new Point2D.Double(); + size += readPointL(leis, srcPnt); + + leis.readFully(xformSrc); + size += 24; + + size += bkColorSrc.init(leis); + + usageSrc = (int)leis.readUInt(); + + // A 32-bit unsigned integer that specifies the offset, in bytes, from the + // start of this record to the source bitmap header in the BitmapBuffer field. + final int offBmiSrc = (int)leis.readUInt(); + + // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header. + final 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 in the BitmapBuffer field. + final int offBitsSrc = (int)leis.readUInt(); + + // 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; + + if (srcEqualsDstDimension()) { + srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), dstBounds.getWidth(), dstBounds.getHeight()); + } else { + int srcWidth = leis.readInt(); + int srcHeight = leis.readInt(); + size += 2 * LittleEndianConsts.INT_SIZE; + 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; + + final int undefinedSpace2 = (int)(offBitsSrc - size - HEADER_SIZE); + assert(undefinedSpace2 >= 0); + leis.skipFully(undefinedSpace2); + size += undefinedSpace2; + + bitsSrc = IOUtils.safelyAllocate(cbBitsSrc, MAX_RECORD_LENGTH); + leis.readFully(bitsSrc); + size += cbBitsSrc; + + return size; + } + + protected boolean srcEqualsDstDimension() { + return false; + } + } + + /** + * 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. + */ + public static class EmfBitBlt extends EmfStretchBlt { + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.bitBlt; + } + + @Override + protected boolean srcEqualsDstDimension() { + return false; + } + } + + + /** The EMR_FRAMERGN record draws a border around the specified region using the specified brush. */ + public static class EmfFrameRgn extends HwmfDraw.WmfFrameRegion implements HemfRecord { + private final Rectangle2D bounds = new Rectangle2D.Double(); + private final List rgnRects = new ArrayList<>(); + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.frameRgn; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + long size = readRectL(leis, bounds); + // A 32-bit unsigned integer that specifies the size of region data, in bytes. + long rgnDataSize = leis.readUInt(); + // A 32-bit unsigned integer that specifies the brush EMF Object Table index. + brushIndex = (int)leis.readUInt(); + // A 32-bit signed integer that specifies the width of the vertical brush stroke, in logical units. + width = leis.readInt(); + // A 32-bit signed integer that specifies the height of the horizontal brush stroke, in logical units. + height = leis.readInt(); + size += 4*LittleEndianConsts.INT_SIZE; + size += readRgnData(leis, rgnRects); + return size; + } + + @Override + public void draw(HwmfGraphics ctx) { + ctx.applyObjectTableEntry(brushIndex); + + 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); + + ctx.fill(frame); + } + } + + /** The EMR_INVERTRGN record inverts the colors in the specified region. */ + public static class EmfInvertRgn implements HemfRecord { + protected final Rectangle2D bounds = new Rectangle2D.Double(); + protected final List rgnRects = new ArrayList<>(); + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.invertRgn; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + long size = readRectL(leis, bounds); + // A 32-bit unsigned integer that specifies the size of region data, in bytes. + long rgnDataSize = leis.readUInt(); + size += LittleEndianConsts.INT_SIZE; + size += readRgnData(leis, rgnRects); + return size; + } + } + + /** + * The EMR_PAINTRGN record paints the specified region by using the brush currently selected into the + * playback device context. + */ + public static class EmfPaintRgn extends EmfInvertRgn { + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.paintRgn; + } + } + + /** The EMR_FILLRGN record fills the specified region by using the specified brush. */ + public static class EmfFillRgn extends HwmfFill.WmfFillRegion implements HemfRecord { + protected final Rectangle2D bounds = new Rectangle2D.Double(); + protected final List rgnRects = new ArrayList<>(); + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.fillRgn; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + long size = readRectL(leis, bounds); + // A 32-bit unsigned integer that specifies the size of region data, in bytes. + long rgnDataSize = leis.readUInt(); + brushIndex = (int)leis.readUInt(); + size += 2*LittleEndianConsts.INT_SIZE; + size += readRgnData(leis, rgnRects); + return size; + } + } + + public static class EmfExtSelectClipRgn implements HemfRecord { + protected HemfRegionMode regionMode; + protected final List rgnRects = new ArrayList<>(); + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.extSelectClipRgn; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + // A 32-bit unsigned integer that specifies the size of region data in bytes + long rgnDataSize = leis.readUInt(); + // A 32-bit unsigned integer that specifies the way to use the region. + regionMode = HemfRegionMode.valueOf((int)leis.readUInt()); + long size = 2* LittleEndianConsts.INT_SIZE; + + // If RegionMode is RGN_COPY, this data can be omitted and the clip region + // SHOULD be set to the default (NULL) clip region. + if (regionMode != HemfRegionMode.RGN_COPY) { + size += readRgnData(leis, rgnRects); + } + return size; + } + } + + public static class EmfAlphaBlend implements HemfRecord { + /** the destination bounding rectangle in device units */ + protected final Rectangle2D bounds = new Rectangle2D.Double(); + /** the destination rectangle */ + protected final Rectangle2D destRect = new Rectangle2D.Double(); + /** the source rectangle */ + protected final Rectangle2D srcRect = new Rectangle2D.Double(); + /** + * The blend operation code. The only source and destination blend operation that has been defined + * is 0x00, which specifies that the source bitmap MUST be combined with the destination bitmap based + * on the alpha transparency values of the source pixels. + */ + protected byte blendOperation; + /** This value MUST be 0x00 and MUST be ignored. */ + protected byte blendFlags; + /** + * An 8-bit unsigned integer that specifies alpha transparency, which determines the blend of the source + * and destination bitmaps. This value MUST be used on the entire source bitmap. The minimum alpha + * transparency value, zero, corresponds to completely transparent; the maximum value, 0xFF, corresponds + * to completely opaque. In effect, a value of 0xFF specifies that the per-pixel alpha values determine + * the blend of the source and destination bitmaps. + */ + protected int srcConstantAlpha; + /** + * A byte that specifies how source and destination pixels are interpreted with respect to alpha transparency. + * + * 0x00: + * The pixels in the source bitmap do not specify alpha transparency. + * In this case, the SrcConstantAlpha value determines the blend of the source and destination bitmaps. + * Note that in the following equations SrcConstantAlpha is divided by 255, + * which produces a value in the range 0 to 1. + * + * 0x01: "AC_SRC_ALPHA" + * Indicates that the source bitmap is 32 bits-per-pixel and specifies an alpha transparency value + * for each pixel. + */ + protected byte alphaFormat; + /** a world-space to page-space transform to apply to the source bitmap. */ + protected final AffineTransform xFormSrc = new AffineTransform(); + /** the background color of the source bitmap. */ + protected final HwmfColorRef bkColorSrc = new HwmfColorRef(); + /** + * A 32-bit unsigned integer that specifies how to interpret values in the + * color table in the source bitmap header. + */ + protected ColorUsage usageSrc; + + protected final HwmfBitmapDib bitmap = new HwmfBitmapDib(); + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.alphaBlend; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + final int startIdx = leis.getReadIndex(); + + long size = readRectL(leis, bounds); + size += readBounds2(leis, destRect); + + blendOperation = leis.readByte(); + assert (blendOperation == 0); + blendFlags = leis.readByte(); + assert (blendOperation == 0); + srcConstantAlpha = leis.readUByte(); + alphaFormat = leis.readByte(); + + // A 32-bit signed integer that specifies the logical x-coordinate of the upper-left + // corner of the source rectangle. + final int xSrc = leis.readInt(); + // A 32-bit signed integer that specifies the logical y-coordinate of the upper-left + // corner of the source rectangle. + final int ySrc = leis.readInt(); + + size += 3*LittleEndianConsts.INT_SIZE; + size += readXForm(leis, xFormSrc); + size += bkColorSrc.init(leis); + + usageSrc = ColorUsage.valueOf((int)leis.readUInt()); + + + // A 32-bit unsigned integer that specifies the offset, in bytes, from the + // start of this record to the source bitmap header in the BitmapBuffer field. + final int offBmiSrc = (int)leis.readUInt(); + + // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header. + final 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 in the BitmapBuffer field. + final int offBitsSrc = (int)leis.readUInt(); + // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap bits. + final int cbBitsSrc = (int)leis.readUInt(); + + // A 32-bit signed integer that specifies the logical width of the source rectangle. + // This value MUST be greater than zero. + final int cxSrc = leis.readInt(); + // A 32-bit signed integer that specifies the logical height of the source rectangle. + // This value MUST be greater than zero. + final int cySrc = leis.readInt(); + + srcRect.setRect(xSrc, ySrc, cxSrc, cySrc); + + size += 7 * LittleEndianConsts.INT_SIZE; + + size += readBitmap(leis, bitmap, startIdx, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc); + + return size; + } + } + + public static class EmfSetDiBitsToDevice implements HemfRecord { + protected final Rectangle2D bounds = new Rectangle2D.Double(); + protected final Point2D dest = new Point2D.Double(); + protected final Rectangle2D src = new Rectangle2D.Double(); + protected ColorUsage usageSrc; + protected final HwmfBitmapDib bitmap = new HwmfBitmapDib(); + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.setDiBitsToDevice; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + int startIdx = leis.getReadIndex(); + + // A WMF RectL object that defines the destination bounding rectangle in device units. + long size = readRectL(leis, bounds); + // the logical x/y-coordinate of the upper-left corner of the destination rectangle. + size += readPointL(leis, dest); + // the source rectangle + size += readBounds2(leis, src); + // A 32-bit unsigned integer that specifies the offset, in bytes, from the + // start of this record to the source bitmap header in the BitmapBuffer field. + final int offBmiSrc = (int)leis.readUInt(); + // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap header. + final 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 in the BitmapBuffer field. + final int offBitsSrc = (int)leis.readUInt(); + // A 32-bit unsigned integer that specifies the size, in bytes, of the source bitmap bits. + final 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 + usageSrc = ColorUsage.valueOf((int)leis.readUInt()); + // A 32-bit unsigned integer that specifies the first scan line in the array. + final int iStartScan = (int)leis.readUInt(); + // A 32-bit unsigned integer that specifies the number of scan lines. + final int cScans = (int)leis.readUInt(); + size += 7*LittleEndianConsts.INT_SIZE; + + size += readBitmap(leis, bitmap, startIdx, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc); + + return size; + } + } + + static long readBitmap(final LittleEndianInputStream leis, final HwmfBitmapDib bitmap, + final int startIdx, final int offBmiSrc, final int cbBmiSrc, final int offBitsSrc, int cbBitsSrc) + throws IOException { + final int offCurr = leis.getReadIndex()-(startIdx-HEADER_SIZE); + final int undefinedSpace1 = offBmiSrc-offCurr; + assert(undefinedSpace1 >= 0); + + final int undefinedSpace2 = offBitsSrc-offCurr-cbBmiSrc-undefinedSpace1; + assert(undefinedSpace2 >= 0); + + leis.skipFully(undefinedSpace1); + + if (cbBmiSrc == 0 || cbBitsSrc == 0) { + return undefinedSpace1; + } + + final LittleEndianInputStream leisDib; + if (undefinedSpace2 == 0) { + leisDib = leis; + } else { + final ByteArrayOutputStream bos = new ByteArrayOutputStream(cbBmiSrc+cbBitsSrc); + final long cbBmiSrcAct = IOUtils.copy(leis, bos, cbBmiSrc); + assert (cbBmiSrcAct == cbBmiSrc); + leis.skipFully(undefinedSpace2); + final long cbBitsSrcAct = IOUtils.copy(leis, bos, cbBitsSrc); + assert (cbBitsSrcAct == cbBitsSrc); + leisDib = new LittleEndianInputStream(new ByteArrayInputStream(bos.toByteArray())); + } + final int dibSize = cbBmiSrc+cbBitsSrc; + final int dibSizeAct = bitmap.init(leisDib, dibSize); + assert (dibSizeAct <= dibSize); + return undefinedSpace1 + cbBmiSrc + undefinedSpace2 + cbBitsSrc; + } + + + static long readRgnData(final LittleEndianInputStream leis, final List rgnRects) { + // *** RegionDataHeader *** + // A 32-bit unsigned integer that specifies the size of this object in bytes. This MUST be 0x00000020. + long rgnHdrSiez = leis.readUInt(); + assert(rgnHdrSiez == 0x20); + // A 32-bit unsigned integer that specifies the region type. This SHOULD be RDH_RECTANGLES (0x00000001) + long rgnHdrType = leis.readUInt(); + assert(rgnHdrType == 1); + // A 32-bit unsigned integer that specifies the number of rectangles in this region. + long rgnCntRect = leis.readUInt(); + // A 32-bit unsigned integer that specifies the size of the buffer of rectangles in bytes. + long rgnCntBytes = leis.readUInt(); + long size = 4*LittleEndianConsts.INT_SIZE; + // A 128-bit WMF RectL object, which specifies the bounds of the region. + Rectangle2D rgnBounds = new Rectangle2D.Double(); + size += readRectL(leis, rgnBounds); + for (int i=0; i = : + // 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.) + + // A 32-bit floating-point value of the transform matrix. + double eM11 = leis.readFloat(); + + // A 32-bit floating-point value of the transform matrix. + double eM12 = leis.readFloat(); + + // A 32-bit floating-point value of the transform matrix. + double eM21 = leis.readFloat(); + + // A 32-bit floating-point value of the transform matrix. + double eM22 = leis.readFloat(); + + // A 32-bit floating-point value that contains a horizontal translation component, in logical units. + double eDx = leis.readFloat(); + + // A 32-bit floating-point value that contains a vertical translation component, in logical units. + double eDy = leis.readFloat(); + + xform.setTransform(eM11, eM21, eM12, eM22, eDx, eDy); + + return 6 * LittleEndian.INT_SIZE; + } +} Propchange: poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java ------------------------------------------------------------------------------ svn:eol-style = native Added: poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java URL: http://svn.apache.org/viewvc/poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java?rev=1840956&view=auto ============================================================================== --- poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java (added) +++ poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java Fri Sep 14 21:37:37 2018 @@ -0,0 +1,464 @@ +/* ==================================================================== + 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 java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.common.usermodel.fonts.FontCharset; +import org.apache.poi.hwmf.record.HwmfFont; +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.LittleEndianConsts; +import org.apache.poi.util.LittleEndianInputStream; + +public class HemfFont extends HwmfFont { + private static final int LOGFONT_SIZE = 92; + private static final int LOGFONTPANOSE_SIZE = 320; + + protected interface LogFontDetails {} + + protected static class LogFontExDv implements LogFontDetails { + protected int[] designVector; + } + + protected static class LogFontPanose implements LogFontDetails { + enum FamilyType { + PAN_ANY, + PAN_NO_FIT, + PAN_FAMILY_TEXT_DISPLAY, + PAN_FAMILY_SCRIPT, + PAN_FAMILY_DECORATIVE, + PAN_FAMILY_PICTORIAL + } + + enum SerifType { + PAN_ANY, + PAN_NO_FIT, + PAN_SERIF_COVE, + PAN_SERIF_OBTUSE_COVE, + PAN_SERIF_SQUARE_COVE, + PAN_SERIF_OBTUSE_SQUARE_COVE, + PAN_SERIF_SQUARE, + PAN_SERIF_THIN, + PAN_SERIF_BONE, + PAN_SERIF_EXAGGERATED, + PAN_SERIF_TRIANGLE, + PAN_SERIF_NORMAL_SANS, + PAN_SERIF_OBTUSE_SANS, + PAN_SERIF_PERP_SANS, + PAN_SERIF_FLARED, + PAN_SERIF_ROUNDED + } + + enum FontWeight { + PAN_ANY, + PAN_NO_FIT, + PAN_WEIGHT_VERY_LIGHT, + PAN_WEIGHT_LIGHT, + PAN_WEIGHT_THIN, + PAN_WEIGHT_BOOK, + PAN_WEIGHT_MEDIUM, + PAN_WEIGHT_DEMI, + PAN_WEIGHT_BOLD, + PAN_WEIGHT_HEAVY, + PAN_WEIGHT_BLACK, + PAN_WEIGHT_NORD + } + + enum Proportion { + PAN_ANY, + PAN_NO_FIT, + PAN_PROP_OLD_STYLE, + PAN_PROP_MODERN, + PAN_PROP_EVEN_WIDTH, + PAN_PROP_EXPANDED, + PAN_PROP_CONDENSED, + PAN_PROP_VERY_EXPANDED, + PAN_PROP_VERY_CONDENSED, + PAN_PROP_MONOSPACED + } + + enum Contrast { + PAN_ANY, + PAN_NO_FIT, + PAN_CONTRAST_NONE, + PAN_CONTRAST_VERY_LOW, + PAN_CONTRAST_LOW, + PAN_CONTRAST_MEDIUM_LOW, + PAN_CONTRAST_MEDIUM, + PAN_CONTRAST_MEDIUM_HIGH, + PAN_CONTRAST_HIGH, + PAN_CONTRAST_VERY_HIGH + } + + enum StrokeVariation { + PAN_ANY, + PAN_NO_FIT, + PAN_STROKE_GRADUAL_DIAG, + PAN_STROKE_GRADUAL_TRAN, + PAN_STROKE_GRADUAL_VERT, + PAN_STROKE_GRADUAL_HORZ, + PAN_STROKE_RAPID_VERT, + PAN_STROKE_RAPID_HORZ, + PAN_STROKE_INSTANT_VERT + } + + enum ArmStyle { + PAN_ANY, + PAN_NO_FIT, + PAN_STRAIGHT_ARMS_HORZ, + PAN_STRAIGHT_ARMS_WEDGE, + PAN_STRAIGHT_ARMS_VERT, + PAN_STRAIGHT_ARMS_SINGLE_SERIF, + PAN_STRAIGHT_ARMS_DOUBLE_SERIF, + PAN_BENT_ARMS_HORZ, + PAN_BENT_ARMS_WEDGE, + PAN_BENT_ARMS_VERT, + PAN_BENT_ARMS_SINGLE_SERIF, + PAN_BENT_ARMS_DOUBLE_SERIF + } + + enum Letterform { + PAN_ANY, + PAN_NO_FIT, + PAN_LETT_NORMAL_CONTACT, + PAN_LETT_NORMAL_WEIGHTED, + PAN_LETT_NORMAL_BOXED, + PAN_LETT_NORMAL_FLATTENED, + PAN_LETT_NORMAL_ROUNDED, + PAN_LETT_NORMAL_OFF_CENTER, + PAN_LETT_NORMAL_SQUARE, + PAN_LETT_OBLIQUE_CONTACT, + PAN_LETT_OBLIQUE_WEIGHTED, + PAN_LETT_OBLIQUE_BOXED, + PAN_LETT_OBLIQUE_FLATTENED, + PAN_LETT_OBLIQUE_ROUNDED, + PAN_LETT_OBLIQUE_OFF_CENTER, + PAN_LETT_OBLIQUE_SQUARE + } + + enum MidLine { + PAN_ANY, + PAN_NO_FIT, + PAN_MIDLINE_STANDARD_TRIMMED, + PAN_MIDLINE_STANDARD_POINTED, + PAN_MIDLINE_STANDARD_SERIFED, + PAN_MIDLINE_HIGH_TRIMMED, + PAN_MIDLINE_HIGH_POINTED, + PAN_MIDLINE_HIGH_SERIFED, + PAN_MIDLINE_CONSTANT_TRIMMED, + PAN_MIDLINE_CONSTANT_POINTED, + PAN_MIDLINE_CONSTANT_SERIFED, + PAN_MIDLINE_LOW_TRIMMED, + PAN_MIDLINE_LOW_POINTED, + PAN_MIDLINE_LOW_SERIFED + } + + enum XHeight { + PAN_ANY, + PAN_NO_FIT, + PAN_XHEIGHT_CONSTANT_SMALL, + PAN_XHEIGHT_CONSTANT_STD, + PAN_XHEIGHT_CONSTANT_LARGE, + PAN_XHEIGHT_DUCKING_SMALL, + PAN_XHEIGHT_DUCKING_STD, + PAN_XHEIGHT_DUCKING_LARGE + } + + protected int styleSize; + protected int vendorId; + protected int culture; + protected FamilyType familyType; + protected SerifType serifStyle; + protected FontWeight weight; + protected Proportion proportion; + protected Contrast contrast; + protected StrokeVariation strokeVariation; + protected ArmStyle armStyle; + protected Letterform letterform; + protected MidLine midLine; + protected XHeight xHeight; + } + + protected String fullname; + protected String style; + protected String script; + + protected LogFontDetails details; + + @Override + public int init(LittleEndianInputStream leis, long recordSize) throws IOException { + // A 32-bit signed integer that specifies the height, in logical units, of the font's + // character cell or character. The character height value, also known as the em size, is the + // character cell height value minus the internal leading value. The font mapper SHOULD + // interpret the value specified in the Height field in the following manner. + // + // 0x00000000 < value: + // The font mapper transforms this value into device units and matches it against + // the cell height of the available fonts. + // + // 0x00000000 + // The font mapper uses a default height value when it searches for a match. + // + // value < 0x00000000: + // The font mapper transforms this value into device units and matches its + // absolute value against the character height of the available fonts. + // + // For all height comparisons, the font mapper SHOULD look for the largest font that does not + // exceed the requested size. + height = leis.readInt(); + + // A 32-bit signed integer that specifies the average width, in logical units, of + // characters in the font. If the Width field value is zero, an appropriate value SHOULD be + // calculated from other LogFont values to find a font that has the typographer's intended + // aspect ratio. + width = leis.readInt(); + + // A 32-bit signed integer that specifies the angle, in tenths of degrees, + // between the escapement vector and the x-axis of the device. The escapement vector is + // parallel to the baseline of a row of text. + // + // When the graphics mode is set to GM_ADVANCED, the escapement angle of the string can + // be specified independently of the orientation angle of the string's characters. + escapement = leis.readInt(); + + // A 32-bit signed integer that specifies the angle, in tenths of degrees, + // between each character's baseline and the x-axis of the device. + orientation = leis.readInt(); + + // A 32-bit signed integer that specifies the weight of the font in the range zero through 1000. + // For example, 400 is normal and 700 is bold. If this value is zero, a default weight can be used. + weight = leis.readInt(); + + // 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); + + // 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); + + // 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); + + // An 8-bit unsigned integer that specifies the set of character glyphs. + // It MUST be a value in the WMF CharacterSet enumeration. + // If the character set is unknown, metafile processing SHOULD NOT attempt + // to translate or interpret strings that are rendered with that font. + // If a typeface name is specified in the Facename field, the CharSet field + // value MUST match the character set of that typeface. + charSet = FontCharset.valueOf(leis.readUByte()); + + // An 8-bit unsigned integer that specifies the output precision. + // The output precision defines how closely the font is required to match the requested height, width, + // character orientation, escapement, pitch, and font type. + // It MUST be a value from the WMF OutPrecision enumeration. + // Applications can use the output precision to control how the font mapper chooses a font when the + // operating system contains more than one font with a specified name. For example, if an operating + // system contains a font named Symbol in rasterized and TrueType forms, an output precision value + // of OUT_TT_PRECIS forces the font mapper to choose the TrueType version. + // A value of OUT_TT_ONLY_PRECIS forces the font mapper to choose a TrueType font, even if it is + // necessary to substitute a TrueType font with another name. + outPrecision = WmfOutPrecision.valueOf(leis.readUByte()); + + // An 8-bit unsigned integer that specifies the clipping precision. + // The clipping precision defines how to clip characters that are partially outside the clipping region. + // It can be one or more of the WMF ClipPrecision Flags + clipPrecision.init(leis); + + // An 8-bit unsigned integer that specifies the output quality. The output quality defines how closely + // to attempt to match the logical-font attributes to those of an actual physical font. + // It MUST be one of the values in the WMF FontQuality enumeration + quality = WmfFontQuality.valueOf(leis.readUByte()); + + // A WMF PitchAndFamily object that specifies the pitch and family of the font. + // Font families describe the look of a font in a general way. + // They are intended for specifying a font when the specified typeface is not available. + pitchAndFamily = leis.readUByte(); + + int size = 5* LittleEndianConsts.INT_SIZE+8*LittleEndianConsts.BYTE_SIZE; + + StringBuilder sb = new StringBuilder(); + + // A string of no more than 32 Unicode characters that specifies the typeface name of the font. + // If the length of this string is less than 32 characters, a terminating NULL MUST be present, + // after which the remainder of this field MUST be ignored. + int readBytes = readString(leis, sb, 32); + if (readBytes == -1) { + throw new IOException("Font facename can't be determined."); + } + facename = sb.toString(); + size += readBytes; + + if (recordSize <= LOGFONT_SIZE) { + return size; + } + + // A string of 64 Unicode characters that contains the font's full name. + // Ifthe length of this string is less than 64 characters, a terminating + // NULL MUST be present, after which the remainder of this field MUST be ignored. + readBytes = readString(leis, sb, 64); + if (readBytes == -1) { + throw new IOException("Font fullname can't be determined."); + } + fullname = sb.toString(); + size += readBytes; + + // A string of 32 Unicode characters that defines the font's style. If the length of + // this string is less than 32 characters, a terminating NULL MUST be present, + // after which the remainder of this field MUST be ignored. + readBytes = readString(leis, sb, 32); + if (readBytes == -1) { + throw new IOException("Font style can't be determined."); + } + style = sb.toString(); + size += readBytes; + + if (recordSize == LOGFONTPANOSE_SIZE) { + // LogFontPanose Object + + LogFontPanose logPan = new LogFontPanose(); + details = logPan; + + int version = leis.readInt(); + + // A 32-bit unsigned integer that specifies the point size at which font + //hinting is performed. If set to zero, font hinting is performed at the point size corresponding + //to the Height field in the LogFont object in the LogFont field. + logPan.styleSize = (int)leis.readUInt(); + + int match = leis.readInt(); + + int reserved = leis.readInt(); + + logPan.vendorId = leis.readInt(); + + logPan.culture = leis.readInt(); + + // An 8-bit unsigned integer that specifies the family type. + // The value MUST be in the FamilyType enumeration table. + logPan.familyType = LogFontPanose.FamilyType.values()[leis.readUByte()]; + + // An 8-bit unsigned integer that specifies the serif style. + // The value MUST be in the SerifType enumeration table. + logPan.serifStyle = LogFontPanose.SerifType.values()[leis.readUByte()]; + + // An 8-bit unsigned integer that specifies the weight of the font. + // The value MUST be in the Weight enumeration table. + logPan.weight = LogFontPanose.FontWeight.values()[leis.readUByte()]; + + // An 8-bit unsigned integer that specifies the proportion of the font. + // The value MUST be in the Proportion enumeration table. + logPan.proportion = LogFontPanose.Proportion.values()[leis.readUByte()]; + + // An 8-bit unsigned integer that specifies the proportion of the font. + // The value MUST be in the Proportion enumeration table. + logPan.contrast = LogFontPanose.Contrast.values()[leis.readUByte()]; + + // An 8-bit unsigned integer that specifies the stroke variation for the font. + // The value MUST be in the StrokeVariation enumeration table. + logPan.strokeVariation = LogFontPanose.StrokeVariation.values()[leis.readUByte()]; + + // An 8-bit unsigned integer that specifies the arm style of the font. + // The value MUST be in the ArmStyle enumeration table. + logPan.armStyle = LogFontPanose.ArmStyle.values()[leis.readUByte()]; + + // An 8-bit unsigned integer that specifies the letterform of the font. + // The value MUST be in the Letterform enumeration table. + logPan.letterform = LogFontPanose.Letterform.values()[leis.readUByte()]; + + // An 8-bit unsigned integer that specifies the midline of the font. + // The value MUST be in the MidLine enumeration table. + logPan.midLine = LogFontPanose.MidLine.values()[leis.readUByte()]; + + // An 8-bit unsigned integer that specifies the x height of the font. + // The value MUST be in the XHeight enumeration table. + logPan.xHeight = LogFontPanose.XHeight.values()[leis.readUByte()]; + + // skip 2 byte to ensure 32-bit alignment of this structure. + leis.skip(2); + + size += 6*LittleEndianConsts.INT_SIZE+10* LittleEndianConsts.BYTE_SIZE+2; + } else { + // LogFontExDv Object + + LogFontExDv logEx = new LogFontExDv(); + details = logEx; + + // A string of 32 Unicode characters that defines the character set of the font. + // If the length of this string is less than 32 characters, a terminating NULL MUST be present, + // after which the remainder of this field MUST be ignored. + readBytes = readString(leis, sb, 32); + if (readBytes == -1) { + throw new IOException("Font script can't be determined."); + } + script = sb.toString(); + size += readBytes; + + // Design Vector + + // A 32-bit unsigned integer that MUST be set to the value 0x08007664. + int signature = leis.readInt(); + 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. + int numAxes = leis.readInt(); + assert (0 <= numAxes && numAxes <= 16); + + // An optional array of 32-bit signed integers that specify the values of the font axes of a + // multiple master, OpenType font. The maximum number of values in the array is 16. + if (numAxes > 0) { + logEx.designVector = new int[numAxes]; + for (int i=0; i= 100) { + if (size+12 <= recordSize) { hasExtension1 = true; - cbPixelFormat = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE; - offPixelFormat = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE; - bOpenGL= LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE; + cbPixelFormat = leis.readUInt(); + offPixelFormat = leis.readUInt(); + bOpenGL = leis.readUInt(); + size += 3*LittleEndianConsts.INT_SIZE; } - if (recordSize+8 >= 108) { + if (size+8 <= recordSize) { hasExtension2 = true; - micrometersX = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE; - micrometersY = LittleEndian.getUInt(data, offset); offset += LittleEndian.INT_SIZE; + size += readDimensionInt(leis, microDimension); } - return recordSize; + return size; } } Added: poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java URL: http://svn.apache.org/viewvc/poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java?rev=1840956&view=auto ============================================================================== --- poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java (added) +++ poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java Fri Sep 14 21:37:37 2018 @@ -0,0 +1,447 @@ +/* ==================================================================== + 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 static org.apache.poi.hemf.record.emf.HemfDraw.readDimensionInt; +import static org.apache.poi.hemf.record.emf.HemfFill.readBitmap; +import static org.apache.poi.hemf.record.emf.HemfRecordIterator.HEADER_SIZE; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.hemf.draw.HemfGraphics; +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.HwmfHatchStyle; +import org.apache.poi.hwmf.record.HwmfMapMode; +import org.apache.poi.hwmf.record.HwmfMisc; +import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode; +import org.apache.poi.hwmf.record.HwmfPalette.PaletteEntry; +import org.apache.poi.hwmf.record.HwmfPenStyle; +import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash; +import org.apache.poi.util.IOUtils; +import org.apache.poi.util.LittleEndianConsts; +import org.apache.poi.util.LittleEndianInputStream; + +public class HemfMisc { + private static final int MAX_RECORD_LENGTH = 10_000_000; + + public static class EmfEof implements HemfRecord { + protected final List palette = new ArrayList<>(); + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.eof; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + + // A 32-bit unsigned integer that specifies the number of palette entries. + int nPalEntries = (int)leis.readUInt(); + // A 32-bit unsigned integer that specifies the offset to the palette entries from the start of this record. + int offPalEntries = (int)leis.readUInt(); + + int size = 2*LittleEndianConsts.INT_SIZE; + int undefinedSpace1 = (int)(offPalEntries - size - HEADER_SIZE); + assert (undefinedSpace1 >= 0); + leis.skipFully(undefinedSpace1); + size += undefinedSpace1; + + for (int i=0; i= 0); + leis.skipFully(undefinedSpace2); + size += undefinedSpace2; + + // A 32-bit unsigned integer that MUST be the same as Size and MUST be the + // last field of the record and hence the metafile. + // LogPaletteEntry objects, if they exist, MUST precede this field. + long sizeLast = leis.readUInt(); + size += LittleEndianConsts.INT_SIZE; + assert ((sizeLast-HEADER_SIZE) == recordSize && recordSize == size); + + return size; + } + } + + /** + * The EMF_SAVEDC record saves the playback device context for later retrieval. + */ + public static class EmfSaveDc implements HemfRecord { + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.saveDc; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + return 0; + } + + @Override + public void draw(HemfGraphics ctx) { + ctx.saveProperties(); + } + } + + /** + * The EMF_RESTOREDC record restores the playback device context from a previously saved device + * context. + */ + public static class EmfRestoreDc implements HemfRecord { + + /** + * SavedDC (4 bytes): A 32-bit signed integer that specifies the saved state to restore relative to + * the current state. This value MUST be negative; –1 represents the state that was most + * recently saved on the stack, –2 the one before that, etc. + */ + private int nSavedDC; + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.restoreDc; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + nSavedDC = leis.readInt(); + return LittleEndianConsts.INT_SIZE; + } + + @Override + public void draw(HemfGraphics ctx) { + ctx.restoreProperties(nSavedDC); + } + } + + /** + * The META_SETBKCOLOR record sets the background color in the playback device context to a + * specified color, or to the nearest physical color if the device cannot represent the specified color. + */ + public static class EmfSetBkColor implements HemfRecord { + + private HwmfColorRef colorRef; + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.setBkColor; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + colorRef = new HwmfColorRef(); + return colorRef.init(leis); + } + + @Override + public void draw(HemfGraphics ctx) { + ctx.getProperties().setBackgroundColor(colorRef); + } + } + + + /** + * The EMR_SETBKMODE record specifies the background mix mode of the playback device context. + * The background mix mode is used with text, hatched brushes, and pen styles that are not solid + * lines. + */ + public static class EmfSetBkMode extends WmfSetBkMode implements HemfRecord { + public HemfRecordType getEmfRecordType() { + return HemfRecordType.setBkMode; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + /* + * A 32-bit unsigned integer that specifies the background mode + * and MUST be in the BackgroundMode (section 2.1.4) enumeration + */ + bkMode = HwmfBkMode.valueOf((int)leis.readUInt()); + return LittleEndianConsts.INT_SIZE; + } + } + + /** + * The EMR_SETMAPPERFLAGS record specifies parameters of the process of matching logical fonts to + * physical fonts, which is performed by the font mapper. + */ + public static class EmfSetMapperFlags extends HwmfMisc.WmfSetMapperFlags implements HemfRecord { + public HemfRecordType getEmfRecordType() { + return HemfRecordType.setMapperFlags; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + return super.init(leis, recordSize, (int)recordId); + } + } + + /** + * The EMR_SETMAPMODE record specifies the mapping mode of the playback device context. The + * mapping mode specifies the unit of measure used to transform page space units into device space + * units, and also specifies the orientation of the device's x-axis and y-axis. + */ + public static class EmfSetMapMode extends HwmfMisc.WmfSetMapMode implements HemfRecord { + public HemfRecordType getEmfRecordType() { + return HemfRecordType.setMapMode; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + // A 32-bit unsigned integer whose definition MUST be in the MapMode enumeration + mapMode = HwmfMapMode.valueOf((int)leis.readUInt()); + return LittleEndianConsts.INT_SIZE; + } + } + + /** + * The EMR_SETROP2 record defines a binary raster operation mode. + */ + public static class EmfSetRop2 extends HwmfMisc.WmfSetRop2 implements HemfRecord { + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.setRop2; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + // A 32-bit unsigned integer that specifies the raster operation mode and + // MUST be in the WMF Binary Raster Op enumeration + drawMode = HwmfBinaryRasterOp.valueOf((int)leis.readUInt()); + return LittleEndianConsts.INT_SIZE; + } + } + + + /** + * The EMR_SETSTRETCHBLTMODE record specifies bitmap stretch mode. + */ + public static class EmfSetStretchBltMode extends HwmfMisc.WmfSetStretchBltMode implements HemfRecord { + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.setStretchBltMode; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + // A 32-bit unsigned integer that specifies the stretch mode and MAY be + // in the StretchMode enumeration. + stretchBltMode = StretchBltMode.valueOf((int)leis.readUInt()); + return LittleEndianConsts.INT_SIZE; + } + } + + /** The EMR_CREATEBRUSHINDIRECT record defines a logical brush for graphics operations. */ + public static class EmfCreateBrushIndirect extends HwmfMisc.WmfCreateBrushIndirect implements HemfRecord { + /** + * A 32-bit unsigned integer that specifies the index of the logical brush object in the + * EMF Object Table. This index MUST be saved so that this object can be reused or modified. + */ + private int brushIdx; + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.createBrushIndirect; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + brushIdx = (int)leis.readUInt(); + + brushStyle = HwmfBrushStyle.valueOf((int)leis.readUInt()); + colorRef = new HwmfColorRef(); + int size = colorRef.init(leis); + brushHatch = HwmfHatchStyle.valueOf((int)leis.readUInt()); + return size+3*LittleEndianConsts.INT_SIZE; + + } + } + + /** + * The EMR_DELETEOBJECT record deletes a graphics object, which is specified by its index + * in the EMF Object Table + */ + public static class EmfDeleteObject extends HwmfMisc.WmfDeleteObject implements HemfRecord { + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.deleteobject; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + objectIndex = (int)leis.readUInt(); + return LittleEndianConsts.INT_SIZE; + } + } + + /** The EMR_CREATEPEN record defines a logical pen for graphics operations. */ + public static class EmfCreatePen extends HwmfMisc.WmfCreatePenIndirect implements HemfRecord { + /** + * A 32-bit unsigned integer that specifies the index of the logical palette object + * in the EMF Object Table. This index MUST be saved so that this object can be + * reused or modified. + */ + protected int penIndex; + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.createPen; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + penIndex = (int)leis.readUInt(); + + // 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()); + + int widthX = leis.readInt(); + int widthY = leis.readInt(); + dimension.setSize(widthX, widthY); + + int size = colorRef.init(leis); + + return size + 4*LittleEndianConsts.INT_SIZE; + } + } + + public static class EmfExtCreatePen extends EmfCreatePen { + protected HwmfBrushStyle brushStyle; + protected HwmfHatchStyle hatchStyle; + + protected int[] styleEntry; + + protected final HwmfBitmapDib bitmap = new HwmfBitmapDib(); + + + @Override + public HemfRecordType getEmfRecordType() { + return HemfRecordType.extCreatePen; + } + + @Override + public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException { + final int startIdx = leis.getReadIndex(); + + penIndex = (int)leis.readUInt(); + + // A 32-bit unsigned integer that specifies the offset from the start of this + // record to the DIB header, if the record contains a DIB. + int offBmi = (int)leis.readUInt(); + + // A 32-bit unsigned integer that specifies the size of the DIB header, if the + // record contains a DIB. + int cbBmi = (int)leis.readUInt(); + + // A 32-bit unsigned integer that specifies the offset from the start of this + // record to the DIB bits, if the record contains a DIB. + int offBits = (int)leis.readUInt(); + + // A 32-bit unsigned integer that specifies the size of the DIB bits, if the record + // contains a DIB. + int cbBits = (int)leis.readUInt(); + + // 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()); + + // 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 + // units; otherwise, the width is specified in device units. If the pen type in the PenStyle field is + // PS_COSMETIC, this value MUST be 0x00000001. + long width = leis.readUInt(); + dimension.setSize(width, 0); + + // A 32-bit unsigned integer that specifies a brush style for the pen from the WMF BrushStyle enumeration + // + // If the pen type in the PenStyle field is PS_GEOMETRIC, this value MUST be either BS_SOLID or BS_HATCHED. + // The value of this field can be BS_NULL, but only if the line style specified in PenStyle is PS_NULL. + // The BS_NULL style SHOULD be used to specify a brush that has no effect + brushStyle = HwmfBrushStyle.valueOf((int)leis.readUInt()); + + int size = 8 * LittleEndianConsts.INT_SIZE; + + size += colorRef.init(leis); + + hatchStyle = HwmfHatchStyle.valueOf(leis.readInt()); + + // The number of elements in the array specified in the StyleEntry + // field. This value SHOULD be zero if PenStyle does not specify PS_USERSTYLE. + final int numStyleEntries = (int)leis.readUInt(); + size += 2*LittleEndianConsts.INT_SIZE; + + assert(numStyleEntries == 0 || penStyle.getLineDash() == HwmfLineDash.USERSTYLE); + + // An optional array of 32-bit unsigned integers that defines the lengths of + // dashes and gaps in the line drawn by this pen, when the value of PenStyle is + // PS_USERSTYLE line style for the pen. The array contains a number of entries specified by + // NumStyleEntries, but it is used as if it repeated indefinitely. + // The first entry in the array specifies the length of the first dash. The second entry specifies + // the length of the first gap. Thereafter, lengths of dashes and gaps alternate. + // 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]; + + for (int i=0; i { + + static final int HEADER_SIZE = 2*LittleEndianConsts.INT_SIZE; + + private final LittleEndianInputStream stream; + private HemfRecord currentRecord; + + public HemfRecordIterator(LittleEndianInputStream leis) { + stream = leis; + //queue the first non-header record + currentRecord = _next(); + } + + @Override + public boolean hasNext() { + return currentRecord != null; + } + + @Override + public HemfRecord next() { + HemfRecord toReturn = currentRecord; + currentRecord = (currentRecord instanceof HemfMisc.EmfEof) ? null : _next(); + return toReturn; + } + + private HemfRecord _next() { + if (currentRecord != null && HemfRecordType.eof == currentRecord.getEmfRecordType()) { + return null; + } + long recordId = stream.readUInt(); + long recordSize = stream.readUInt(); + + HemfRecordType type = HemfRecordType.getById(recordId); + if (type == null) { + throw new RecordFormatException("Undefined record of type:"+recordId); + } + final HemfRecord record = type.constructor.get(); + + try { + long remBytes = recordSize-HEADER_SIZE; + long readBytes = record.init(stream, remBytes, recordId); + assert (readBytes <= remBytes); + stream.skipFully((int)(remBytes-readBytes)); + } catch (IOException|RuntimeException e) { + throw new RecordFormatException(e); + } + + return record; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Remove not supported"); + } + +} \ No newline at end of file Propchange: poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordIterator.java ------------------------------------------------------------------------------ svn:eol-style = native --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org For additional commands, e-mail: commits-help@poi.apache.org