You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2019/05/04 23:01:54 UTC
svn commit: r1858625 [1/2] - in /poi/trunk:
src/java/org/apache/poi/poifs/filesystem/ src/java/org/apache/poi/util/
src/scratchpad/src/org/apache/poi/hemf/record/emf/
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/
src/scratchpad/src/org/apache/...
Author: kiwiwings
Date: Sat May 4 23:01:53 2019
New Revision: 1858625
URL: http://svn.apache.org/viewvc?rev=1858625&view=rev
Log:
Bug 60656 - EMF image support in slideshows
Added:
poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java (with props)
poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java (with props)
poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java (with props)
poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfEmbeddedIterator.java (with props)
poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbedded.java (with props)
poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbeddedIterator.java (with props)
poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbeddedType.java (with props)
poi/trunk/test-data/slideshow/nested_wmf.emf (with props)
Modified:
poi/trunk/src/java/org/apache/poi/poifs/filesystem/FileMagic.java
poi/trunk/src/java/org/apache/poi/util/LittleEndianInputStream.java
poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java
poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java
poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPalette.java
poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java
poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java
poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordIterator.java
poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordType.java
poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java
poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java
poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfEscape.java
poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java
poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java
poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPlaceableHeader.java
poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java
poi/trunk/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java
poi/trunk/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java
Modified: poi/trunk/src/java/org/apache/poi/poifs/filesystem/FileMagic.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/filesystem/FileMagic.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/filesystem/FileMagic.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/filesystem/FileMagic.java Sat May 4 23:01:53 2019
@@ -48,21 +48,21 @@ public enum FileMagic {
0x09, 0x00, // sid=0x0009
0x04, 0x00, // size=0x0004
0x00, 0x00, // unused
- 0x70, 0x00 // 0x70 = multiple values
+ '?', 0x00 // '?' = multiple values
}),
/** BIFF3 raw stream - for Excel 3 */
BIFF3(new byte[]{
0x09, 0x02, // sid=0x0209
0x06, 0x00, // size=0x0006
0x00, 0x00, // unused
- 0x70, 0x00 // 0x70 = multiple values
+ '?', 0x00 // '?' = multiple values
}),
/** BIFF4 raw stream - for Excel 4 */
BIFF4(new byte[]{
0x09, 0x04, // sid=0x0409
0x06, 0x00, // size=0x0006
0x00, 0x00, // unused
- 0x70, 0x00 // 0x70 = multiple values
+ '?', 0x00 // '? = multiple values
},new byte[]{
0x09, 0x04, // sid=0x0409
0x06, 0x00, // size=0x0006
@@ -78,18 +78,22 @@ public enum FileMagic {
/** PDF document */
PDF("%PDF"),
/** Some different HTML documents */
- HTML("<!DOCTYP".getBytes(UTF_8),
- "<html".getBytes(UTF_8),
- "\n\r<html".getBytes(UTF_8),
- "\r\n<html".getBytes(UTF_8),
- "\r<html".getBytes(UTF_8),
- "\n<html".getBytes(UTF_8),
- "<HTML".getBytes(UTF_8),
- "\r\n<HTML".getBytes(UTF_8),
- "\n\r<HTML".getBytes(UTF_8),
- "\r<HTML".getBytes(UTF_8),
- "\n<HTML".getBytes(UTF_8)),
+ HTML("<!DOCTYP",
+ "<html","\n\r<html","\r\n<html","\r<html","\n<html",
+ "<HTML","\r\n<HTML","\n\r<HTML","\r<HTML","\n<HTML"),
WORD2(new byte[]{ (byte)0xdb, (byte)0xa5, 0x2d, 0x00}),
+ /** JPEG image */
+ JPEG(
+ new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xDB },
+ new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xE0, '?', '?', 'J', 'F', 'I', 'F', 0x00, 0x01 },
+ new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xEE },
+ new byte[]{ (byte)0xFF, (byte)0xD8, (byte)0xFF, (byte)0xE1, '?', '?', 'E', 'x', 'i', 'f', 0x00, 0x00 }),
+ /** GIF image */
+ GIF("GIF87a","GIF89a"),
+ /** PNG Image */
+ PNG(new byte[]{ (byte)0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A }),
+ /** TIFF Image */
+ TIFF("II*\u0000", "MM\u0000*" ),
// keep UNKNOWN always as last enum!
/** UNKNOWN magic */
UNKNOWN(new byte[0]);
@@ -100,13 +104,17 @@ public enum FileMagic {
this.magic = new byte[1][8];
LittleEndian.putLong(this.magic[0], 0, magic);
}
-
+
FileMagic(byte[]... magic) {
this.magic = magic;
}
- FileMagic(String magic) {
- this(magic.getBytes(LocaleUtil.CHARSET_1252));
+ FileMagic(String... magic) {
+ this.magic = new byte[magic.length][];
+ int i=0;
+ for (String s : magic) {
+ this.magic[i++] = s.getBytes(LocaleUtil.CHARSET_1252);
+ }
}
public static FileMagic valueOf(byte[] magic) {
@@ -123,9 +131,7 @@ public enum FileMagic {
private static boolean findMagic(byte[] expected, byte[] actual) {
int i=0;
for (byte expectedByte : expected) {
- byte actualByte = actual[i++];
- if ((actualByte != expectedByte &&
- (expectedByte != 0x70 || (actualByte != 0x10 && actualByte != 0x20 && actualByte != 0x40)))) {
+ if (actual[i++] != expectedByte && expectedByte != '?') {
return false;
}
}
Modified: poi/trunk/src/java/org/apache/poi/util/LittleEndianInputStream.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/util/LittleEndianInputStream.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/util/LittleEndianInputStream.java (original)
+++ poi/trunk/src/java/org/apache/poi/util/LittleEndianInputStream.java Sat May 4 23:01:53 2019
@@ -204,6 +204,9 @@ public class LittleEndianInputStream ext
public void skipFully(int len) throws IOException {
+ if (len == 0) {
+ return;
+ }
long skipped = IOUtils.skipFully(this, len);
if (skipped > Integer.MAX_VALUE) {
throw new IOException("can't skip further than "+Integer.MAX_VALUE);
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java Sat May 4 23:01:53 2019
@@ -221,6 +221,10 @@ public class HemfComment {
return privateData.length;
}
+ public byte[] getPrivateData() {
+ return privateData;
+ }
+
@Override
public String toString() {
return "\""+new String(privateData, LocaleUtil.CHARSET_1252).replaceAll("\\p{Cntrl}", ".")+"\"";
@@ -350,7 +354,15 @@ public class HemfComment {
}
public enum EmfFormatSignature {
+ /**
+ * The value of this member is the sequence of ASCII characters "FME ",
+ * which happens to be the reverse of the string "EMF", and it denotes EMF record data.
+ */
ENHMETA_SIGNATURE(0x464D4520),
+ /**
+ * The value of this member is the sequence of ASCII characters "FSPE", which happens to be the reverse
+ * of the string "EPSF", and it denotes encapsulated PostScript (EPS) format data.
+ */
EPS_SIGNATURE(0x46535045);
int id;
@@ -403,6 +415,10 @@ public class HemfComment {
public byte[] getRawData() {
return rawData;
}
+
+ public EmfFormatSignature getSignature() {
+ return signature;
+ }
}
public static class EmfCommentDataWMF implements EmfCommentData {
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java Sat May 4 23:01:53 2019
@@ -682,7 +682,7 @@ public class HemfFill {
return 4 * LittleEndianConsts.INT_SIZE;
}
- static int readXForm(LittleEndianInputStream leis, AffineTransform xform) {
+ public static int readXForm(LittleEndianInputStream leis, AffineTransform xform) {
// mapping <java AffineTransform> = <xform>
// m00 (scaleX) = eM11 (Horizontal scaling component)
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPalette.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPalette.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPalette.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPalette.java Sat May 4 23:01:53 2019
@@ -151,4 +151,59 @@ public class HemfPalette {
return 0;
}
}
+
+ /**
+ * The EMR_SETICMMODE record specifies the mode of Image Color Management (ICM) for graphics operations.
+ */
+ public static class EmfSetIcmMode implements HemfRecord {
+ /** The ICMMode enumeration defines values that specify when to turn on and off ICM. */
+ public enum ICMMode {
+ /**
+ * Turns off Image Color Management (ICM) in the playback device context.
+ * Turns on old-style color correction of halftones.
+ */
+ ICM_OFF(0x01),
+ /**
+ * Turns on ICM in the playback device context.
+ * Turns off old-style color correction of halftones.
+ */
+ ICM_ON(0x02),
+ /**
+ * Queries the current state of color management in the playback device context.
+ */
+ ICM_QUERY(0x03),
+ /**
+ * Turns off ICM in the playback device context, and turns off old-style color correction of halftones.
+ */
+ ICM_DONE_OUTSIDEDC(0x04)
+ ;
+
+ public final int id;
+
+ ICMMode(int id) {
+ this.id = id;
+ }
+
+ public static ICMMode valueOf(int id) {
+ for (ICMMode wrt : values()) {
+ if (wrt.id == id) return wrt;
+ }
+ return null;
+ }
+
+ }
+
+ private ICMMode icmMode;
+
+ @Override
+ public HemfRecordType getEmfRecordType() {
+ return HemfRecordType.seticmmode;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+ icmMode = ICMMode.valueOf(leis.readInt());
+ return LittleEndianConsts.INT_SIZE;
+ }
+ }
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java Sat May 4 23:01:53 2019
@@ -121,7 +121,7 @@ public enum HemfRecordType {
extCreatePen(0x0000005F, HemfMisc.EmfExtCreatePen::new),
polytextouta(0x00000060, HemfText.PolyTextOutA::new),
polytextoutw(0x00000061, HemfText.PolyTextOutW::new),
- seticmmode(0x00000062, UnimplementedHemfRecord::new),
+ seticmmode(0x00000062, HemfPalette.EmfSetIcmMode::new),
createcolorspace(0x00000063, UnimplementedHemfRecord::new),
setcolorspace(0x00000064, UnimplementedHemfRecord::new),
deletecolorspace(0x00000065, UnimplementedHemfRecord::new),
Added: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java?rev=1858625&view=auto
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java (added)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java Sat May 4 23:01:53 2019
@@ -0,0 +1,609 @@
+/* ====================================================================
+ 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.emfplus;
+
+import java.awt.Color;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
+
+import org.apache.commons.math3.linear.LUDecomposition;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.poi.hemf.record.emf.HemfFill;
+import org.apache.poi.hemf.record.emfplus.HemfPlusMisc.EmfPlusObjectId;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianInputStream;
+import org.apache.poi.util.StringUtil;
+
+public class HemfPlusDraw {
+ private static final int MAX_OBJECT_SIZE = 1_000_000;
+
+ public enum UnitType {
+ /** Specifies a unit of logical distance within the world space. */
+ World(0x00),
+ /** Specifies a unit of distance based on the characteristics of the physical display. */
+ Display(0x01),
+ /** Specifies a unit of 1 pixel. */
+ Pixel(0x02),
+ /** Specifies a unit of 1 printer's point, or 1/72 inch. */
+ Point(0x03),
+ /** Specifies a unit of 1 inch. */
+ Inch(0x04),
+ /** Specifies a unit of 1/300 inch. */
+ Document(0x05),
+ /** Specifies a unit of 1 millimeter. */
+ Millimeter(0x06)
+ ;
+
+ public final int id;
+
+ UnitType(int id) {
+ this.id = id;
+ }
+
+ public static UnitType valueOf(int id) {
+ for (UnitType wrt : values()) {
+ if (wrt.id == id) return wrt;
+ }
+ return null;
+ }
+
+ }
+
+ public interface EmfPlusCompressed {
+ /**
+ * This bit indicates whether the data in the RectData field is compressed.
+ * If set, RectData contains an EmfPlusRect object.
+ * If clear, RectData contains an EmfPlusRectF object object.
+ */
+ BitField COMPRESSED = BitFieldFactory.getInstance(0x4000);
+
+ int getFlags();
+
+ /**
+ * The index in the EMF+ Object Table to associate with the object
+ * created by this record. The value MUST be zero to 63, inclusive.
+ */
+ default boolean isCompressed() {
+ return COMPRESSED.isSet(getFlags());
+ }
+
+ default BiFunction<LittleEndianInputStream, Rectangle2D, Integer> getReadRect() {
+ return isCompressed() ? HemfPlusDraw::readRectS : HemfPlusDraw::readRectF;
+ }
+ }
+
+
+
+ /**
+ * The EmfPlusDrawPath record specifies drawing a graphics path
+ */
+ public static class EmfPlusDrawPath implements HemfPlusRecord {
+ private int flags;
+ private int penId;
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.drawPath;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+
+ // A 32-bit unsigned integer that specifies an index in the EMF+ Object Table for an EmfPlusPen object
+ // to use for drawing the EmfPlusPath. The value MUST be zero to 63, inclusive.
+ penId = leis.readInt();
+ assert (0 <= penId && penId <= 63);
+
+ assert (dataSize == LittleEndianConsts.INT_SIZE);
+
+ return LittleEndianConsts.INT_SIZE;
+ }
+ }
+
+ /**
+ * The EmfPlusFillRects record specifies filling the interiors of a series of rectangles.
+ */
+ public static class EmfPlusFillRects implements HemfPlusRecord, EmfPlusCompressed {
+ /**
+ * If set, brushId specifies a color as an EmfPlusARGB object.
+ * If clear, brushId contains the index of an EmfPlusBrush object in the EMF+ Object Table.
+ */
+ private static final BitField SOLID_COLOR = BitFieldFactory.getInstance(0x8000);
+
+ private int flags;
+ private int brushId;
+ private final ArrayList<Rectangle2D> rectData = new ArrayList<>();
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.fillRects;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+
+ // A 32-bit unsigned integer that defines the brush, the content of which is
+ // determined by the S bit in the Flags field.
+ brushId = leis.readInt();
+
+ // A 32-bit unsigned integer that specifies the number of rectangles in the RectData field.
+ int count = leis.readInt();
+
+ BiFunction<LittleEndianInputStream, Rectangle2D, Integer> readRect = getReadRect();
+
+ rectData.ensureCapacity(count);
+
+ int size = 2 * LittleEndianConsts.INT_SIZE;
+ for (int i = 0; i<count; i++) {
+ Rectangle2D rect = new Rectangle2D.Double();
+ size += readRect.apply(leis, rect);
+ rectData.add(rect);
+ }
+
+ return size;
+ }
+ }
+
+ public static class EmfPlusDrawImagePoints implements HemfPlusRecord, EmfPlusObjectId, EmfPlusCompressed {
+ /**
+ * This bit indicates that the rendering of the image includes applying an effect.
+ * If set, an object of the Effect class MUST have been specified in an earlier EmfPlusSerializableObject record.
+ */
+ private static final BitField EFFECT = BitFieldFactory.getInstance(0x2000);
+
+ /**
+ * This bit indicates whether the PointData field specifies relative or absolute locations.
+ * If set, each element in PointData specifies a location in the coordinate space that is relative to the
+ * location specified by the previous element in the array. In the case of the first element in PointData,
+ * a previous location at coordinates (0,0) is assumed.
+ * If clear, PointData specifies absolute locations according to the {@link #isCompressed()} flag.
+ *
+ * Note If this flag is set, the {@link #isCompressed()} flag (above) is undefined and MUST be ignored.
+ */
+ private static final BitField POSITION = BitFieldFactory.getInstance(0x0800);
+
+ private int flags;
+ private int imageAttributesID;
+ private UnitType srcUnit;
+ private final Rectangle2D srcRect = new Rectangle2D.Double();
+ private final Point2D upperLeft = new Point2D.Double();
+ private final Point2D lowerRight = new Point2D.Double();
+ private final Point2D lowerLeft = new Point2D.Double();
+ private final AffineTransform trans = new AffineTransform();
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.drawImagePoints;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+
+ // A 32-bit unsigned integer that contains the index of the
+ // optional EmfPlusImageAttributes object in the EMF+ Object Table.
+ imageAttributesID = leis.readInt();
+
+ // A 32-bit signed integer that defines the units of the SrcRect field.
+ // It MUST be the UnitPixel value of the UnitType enumeration
+ srcUnit = UnitType.valueOf(leis.readInt());
+ assert(srcUnit == UnitType.Pixel);
+
+ int size = 2 * LittleEndianConsts.INT_SIZE;
+
+ // An EmfPlusRectF object that defines a portion of the image to be rendered.
+ size += readRectF(leis, srcRect);
+
+ // A 32-bit unsigned integer that specifies the number of points in the PointData array.
+ // Exactly 3 points MUST be specified.
+ int count = leis.readInt();
+ assert(count == 3);
+ size += LittleEndianConsts.INT_SIZE;
+
+ BiFunction<LittleEndianInputStream, Point2D, Integer> readPoint;
+
+ if (POSITION.isSet(flags)) {
+ // If the POSITION flag is set in the Flags, the points specify relative locations.
+ readPoint = HemfPlusDraw::readPointR;
+ } else if (isCompressed()) {
+ // If the POSITION bit is clear and the COMPRESSED bit is set in the Flags field, the points
+ // specify absolute locations with integer values.
+ readPoint = HemfPlusDraw::readPointS;
+ } else {
+ // If the POSITION bit is clear and the COMPRESSED bit is clear in the Flags field, the points
+ // specify absolute locations with floating-point values.
+ readPoint = HemfPlusDraw::readPointF;
+ }
+
+ // TODO: handle relative coordinates
+
+ // An array of Count points that specify three points of a parallelogram.
+ // The three points represent the upper-left, upper-right, and lower-left corners of the parallelogram.
+ // The fourth point of the parallelogram is extrapolated from the first three.
+ // The portion of the image specified by the SrcRect field SHOULD have scaling and shearing transforms
+ // applied if necessary to fit inside the parallelogram.
+
+
+ // size += readPoint.apply(leis, upperLeft);
+ // size += readPoint.apply(leis, upperRight);
+ // size += readPoint.apply(leis, lowerLeft);
+
+ size += readPoint.apply(leis, lowerLeft);
+ size += readPoint.apply(leis, lowerRight);
+ size += readPoint.apply(leis, upperLeft);
+
+ // https://math.stackexchange.com/questions/2772737/how-to-transform-arbitrary-rectangle-into-specific-parallelogram
+
+ RealMatrix para2normal = MatrixUtils.createRealMatrix(new double[][] {
+ { lowerLeft.getX(), lowerRight.getX(), upperLeft.getX() },
+ { lowerLeft.getY(), lowerRight.getY(), upperLeft.getY() },
+ { 1, 1, 1 }
+ });
+
+ RealMatrix rect2normal = MatrixUtils.createRealMatrix(new double[][]{
+ { srcRect.getMinX(), srcRect.getMaxX(), srcRect.getMinX() },
+ { srcRect.getMinY(), srcRect.getMinY(), srcRect.getMaxY() },
+ { 1, 1, 1 }
+ });
+
+ RealMatrix normal2rect = new LUDecomposition(rect2normal).getSolver().getInverse();
+ double[][] m = para2normal.multiply(normal2rect).getData();
+ trans.setTransform(round10(m[0][0]), round10(m[1][0]), round10(m[0][1]), round10(m[1][1]), round10(m[0][2]), round10(m[1][2]));
+
+ return size;
+ }
+ }
+
+ /** The EmfPlusDrawImage record specifies drawing a scaled image. */
+ public static class EmfPlusDrawImage implements HemfPlusRecord, EmfPlusObjectId, EmfPlusCompressed {
+ private int flags;
+ private int imageAttributesID;
+ private UnitType srcUnit;
+ private final Rectangle2D srcRect = new Rectangle2D.Double();
+ private final Rectangle2D rectData = new Rectangle2D.Double();
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.drawImage;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+
+ // A 32-bit unsigned integer that contains the index of the
+ // optional EmfPlusImageAttributes object in the EMF+ Object Table.
+ imageAttributesID = leis.readInt();
+
+ // A 32-bit signed integer that defines the units of the SrcRect field.
+ // It MUST be the UnitPixel value of the UnitType enumeration
+ srcUnit = UnitType.valueOf(leis.readInt());
+ assert(srcUnit == UnitType.Pixel);
+
+ int size = 2 * LittleEndianConsts.INT_SIZE;
+
+ // An EmfPlusRectF object that specifies a portion of the image to be rendered. The portion of the image
+ // specified by this rectangle is scaled to fit the destination rectangle specified by the RectData field.
+ size += readRectF(leis, srcRect);
+
+ // Either an EmfPlusRect or EmfPlusRectF object that defines the bounding box of the image. The portion of
+ // the image specified by the SrcRect field is scaled to fit this rectangle.
+ size += getReadRect().apply(leis, rectData);
+
+ return size;
+ }
+ }
+
+ /** The EmfPlusFillRegion record specifies filling the interior of a graphics region. */
+ public static class EmfPlusFillRegion implements HemfPlusRecord {
+ private static final BitField SOLID_COLOR = BitFieldFactory.getInstance(0x8000);
+
+ private int flags;
+ private final byte[] brushId = new byte[LittleEndianConsts.INT_SIZE];
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.fillRegion;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ public boolean isSolidColor() {
+ return SOLID_COLOR.isSet(getFlags());
+ }
+
+ public int getBrushId() {
+ return (isSolidColor()) ? -1 : LittleEndian.getInt(brushId);
+ }
+
+ public Color getSolidColor() {
+ return (isSolidColor()) ? new Color(brushId[2], brushId[1], brushId[0], brushId[3]) : null;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+
+ // A 32-bit unsigned integer that defines the brush, the content of which is determined by
+ // the SOLID_COLOR bit in the Flags field.
+ // If SOLID_COLOR is set, BrushId specifies a color as an EmfPlusARGB object.
+ // If clear, BrushId contains the index of an EmfPlusBrush object in the EMF+ Object Table.
+ leis.readFully(brushId);
+
+ return LittleEndianConsts.INT_SIZE;
+ }
+ }
+
+ /** The EmfPlusFillPath record specifies filling the interior of a graphics path. */
+ public static class EmfPlusFillPath extends EmfPlusFillRegion {
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.fillPath;
+ }
+
+ }
+
+ /** The EmfPlusDrawDriverString record specifies text output with character positions. */
+ public static class EmfPlusDrawDriverString implements HemfPlusRecord, EmfPlusObjectId {
+ /**
+ * If set, brushId specifies a color as an EmfPlusARGB object.
+ * If clear, brushId contains the index of an EmfPlusBrush object in the EMF+ Object Table.
+ */
+ private static final BitField SOLID_COLOR = BitFieldFactory.getInstance(0x8000);
+
+ /**
+ * If set, the positions of character glyphs SHOULD be specified in a character map lookup table.
+ * If clear, the glyph positions SHOULD be obtained from an array of coordinates.
+ */
+ private static final BitField CMAP_LOOKUP = BitFieldFactory.getInstance(0x0001);
+
+ /**
+ * If set, the string SHOULD be rendered vertically.
+ * If clear, the string SHOULD be rendered horizontally.
+ */
+ private static final BitField VERTICAL = BitFieldFactory.getInstance(0x0002);
+
+ /**
+ * If set, character glyph positions SHOULD be calculated relative to the position of the first glyph.
+ * If clear, the glyph positions SHOULD be obtained from an array of coordinates.
+ */
+ private static final BitField REALIZED_ADVANCE = BitFieldFactory.getInstance(0x0004);
+
+ /**
+ * If set, less memory SHOULD be used to cache anti-aliased glyphs, which produces lower quality text rendering.
+ * If clear, more memory SHOULD be used, which produces higher quality text rendering.
+ */
+ private static final BitField LIMIT_SUBPIXEL = BitFieldFactory.getInstance(0x0008);
+
+
+
+ private int flags;
+ private int brushId;
+ private int optionsFlags;
+ private String glyphs;
+ private final List<Point2D> glpyhPos = new ArrayList<>();
+ private final AffineTransform transformMatrix = new AffineTransform();
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.drawDriverstring;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+
+ // A 32-bit unsigned integer that specifies either the foreground color of the text or a graphics brush,
+ // depending on the value of the SOLID_COLOR flag in the Flags.
+ brushId = leis.readInt();
+
+ // A 32-bit unsigned integer that specifies the spacing, orientation, and quality of rendering for the
+ // string. This value MUST be composed of DriverStringOptions flags
+ optionsFlags = leis.readInt();
+
+ // A 32-bit unsigned integer that specifies whether a transform matrix is present in the
+ // TransformMatrix field.
+ boolean hasMatrix = leis.readInt() == 1;
+
+ // A 32-bit unsigned integer that specifies number of glyphs in the string.
+ int glyphCount = leis.readInt();
+
+ int size = 4 * LittleEndianConsts.INT_SIZE;
+
+ // TOOD: implement Non-Cmap-Lookup correctly
+
+ // If the CMAP_LOOKUP flag in the optionsFlags field is set, each value in this array specifies a
+ // Unicode character. Otherwise, each value specifies an index to a character glyph in the EmfPlusFont
+ // object specified by the ObjectId value in Flags field.
+ byte[] glyphBuf = IOUtils.toByteArray(leis, glyphCount*2, MAX_OBJECT_SIZE);
+ glyphs = StringUtil.getFromUnicodeLE(glyphBuf);
+
+ size += glyphBuf.length;
+
+ // An array of EmfPlusPointF objects that specify the output position of each character glyph.
+ // There MUST be GlyphCount elements, which have a one-to-one correspondence with the elements
+ // in the Glyphs array.
+ //
+ // Glyph positions are calculated from the position of the first glyph if the REALIZED_ADVANCE flag in
+ // Options flags is set. In this case, GlyphPos specifies the position of the first glyph only.
+ int glyphPosCnt = REALIZED_ADVANCE.isSet(optionsFlags) ? 1 : glyphCount;
+ for (int i=0; i<glyphCount; i++) {
+ Point2D p = new Point2D.Double();
+ size += readPointF(leis, p);
+ glpyhPos.add(p);
+ }
+
+ if (hasMatrix) {
+ size += HemfFill.readXForm(leis, transformMatrix);
+ }
+
+
+ return size;
+ }
+ }
+
+ /** The EmfPlusDrawRects record specifies drawing a series of rectangles. */
+ public static class EmfPlusDrawRects implements HemfPlusRecord, EmfPlusObjectId, EmfPlusCompressed {
+ private int flags;
+ private final List<Rectangle2D> rectData = new ArrayList<>();
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.drawRects;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+
+ // A 32-bit unsigned integer that specifies the number of rectangles in the RectData member.
+ int count = leis.readInt();
+ int size = LittleEndianConsts.INT_SIZE;
+
+ BiFunction<LittleEndianInputStream, Rectangle2D, Integer> readRect = getReadRect();
+
+ for (int i=0; i<count; i++) {
+ Rectangle2D rect = new Rectangle2D.Double();
+ size += readRect.apply(leis, rect);
+ }
+
+ return size;
+ }
+ }
+
+
+ static double round10(double d) {
+ return new BigDecimal(d).setScale(10, BigDecimal.ROUND_HALF_UP).doubleValue();
+ }
+
+ static int readRectS(LittleEndianInputStream leis, Rectangle2D bounds) {
+ // A 16-bit signed integer that defines the ... coordinate
+ final int x = leis.readShort();
+ final int y = leis.readShort();
+ final int width = leis.readShort();
+ final int height = leis.readShort();
+ bounds.setRect(x, y, width, height);
+
+ return 4 * LittleEndianConsts.SHORT_SIZE;
+ }
+
+ static int readRectF(LittleEndianInputStream leis, Rectangle2D bounds) {
+ // A 32-bit floating-point that defines the ... coordinate
+ final double x = leis.readFloat();
+ final double y = leis.readFloat();
+ final double width = leis.readFloat();
+ final double height = leis.readFloat();
+ bounds.setRect(x, y, width, height);
+
+ return 4 * LittleEndianConsts.INT_SIZE;
+ }
+
+ /**
+ * The EmfPlusPoint object specifies an ordered pair of integer (X,Y) values that define an absolute
+ * location in a coordinate space.
+ */
+ static int readPointS(LittleEndianInputStream leis, Point2D point) {
+ double x = leis.readShort();
+ double y = leis.readShort();
+ point.setLocation(x,y);
+ return 2*LittleEndianConsts.SHORT_SIZE;
+ }
+
+ /**
+ * The EmfPlusPointF object specifies an ordered pair of floating-point (X,Y) values that define an
+ * absolute location in a coordinate space.
+ */
+ static int readPointF(LittleEndianInputStream leis, Point2D point) {
+ double x = leis.readFloat();
+ double y = leis.readFloat();
+ point.setLocation(x,y);
+ return 2*LittleEndianConsts.INT_SIZE;
+ }
+
+ /**
+ * The EmfPlusPointR object specifies an ordered pair of integer (X,Y) values that define a relative
+ * location in a coordinate space.
+ */
+ static int readPointR(LittleEndianInputStream leis, Point2D point) {
+ int[] p = { 0 };
+ int size = readEmfPlusInteger(leis, p);
+ double x = p[0];
+ size += readEmfPlusInteger(leis, p);
+ double y = p[0];
+ point.setLocation(x,y);
+ return size;
+ }
+
+ private static int readEmfPlusInteger(LittleEndianInputStream leis, int[] value) {
+ value[0] = leis.readByte();
+ // check for EmfPlusInteger7 value
+ if ((value[0] & 0x80) == 0) {
+ return LittleEndianConsts.BYTE_SIZE;
+ }
+ // ok we've read a EmfPlusInteger15
+ value[0] = ((value[0] << 8) | leis.readByte()) & 0x7FFF;
+ return LittleEndianConsts.SHORT_SIZE;
+ }
+}
Propchange: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java Sat May 4 23:01:53 2019
@@ -20,15 +20,40 @@ package org.apache.poi.hemf.record.emfpl
import java.io.IOException;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
@Internal
public class HemfPlusHeader implements HemfPlusRecord {
+ /**
+ * The GraphicsVersion enumeration defines versions of operating system graphics that are used to
+ * create EMF+ metafiles.
+ */
+ public enum GraphicsVersion {
+ V1(0x0001),
+ V1_1(0x0002)
+ ;
+
+ public final int id;
+
+ GraphicsVersion(int id) {
+ this.id = id;
+ }
+
+ public static GraphicsVersion valueOf(int id) {
+ for (GraphicsVersion wrt : values()) {
+ if (wrt.id == id) return wrt;
+ }
+ return null;
+ }
+ }
+
private int flags;
- private long version; //hack for now; replace with EmfPlusGraphicsVersion object
+ private final EmfPlusGraphicsVersion version = new EmfPlusGraphicsVersion();
private long emfPlusFlags;
private long logicalDpiX;
private long logicalDpiY;
@@ -45,11 +70,9 @@ public class HemfPlusHeader implements H
@Override
public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
this.flags = flags;
- version = leis.readUInt();
+ version.init(leis);
- // verify MetafileSignature (20 bits) == 0xDBC01 and
- // GraphicsVersion (12 bits) in (1 or 2)
- assert((version & 0xFFFFFA00) == 0xDBC01000L && ((version & 0x3FF) == 1 || (version & 0x3FF) == 2));
+ assert(version.getMetafileSignature() == 0xDBC01 && version.getGraphicsVersion() != null);
emfPlusFlags = leis.readUInt();
@@ -58,7 +81,7 @@ public class HemfPlusHeader implements H
return 4* LittleEndianConsts.INT_SIZE;
}
- public long getVersion() {
+ public EmfPlusGraphicsVersion getVersion() {
return version;
}
@@ -84,4 +107,38 @@ public class HemfPlusHeader implements H
", logicalDpiY=" + logicalDpiY +
'}';
}
+
+ public static class EmfPlusGraphicsVersion {
+ private static final BitField METAFILE_SIGNATURE = BitFieldFactory.getInstance(0xFFFFF000);
+
+ private static final BitField GRAPHICS_VERSION = BitFieldFactory.getInstance(0x00000FFF);
+
+
+ private int metafileSignature;
+ private GraphicsVersion graphicsVersion;
+
+
+ public int getMetafileSignature() {
+ return metafileSignature;
+ }
+
+ public GraphicsVersion getGraphicsVersion() {
+ return graphicsVersion;
+ }
+
+ public long init(LittleEndianInputStream leis) throws IOException {
+ int val = leis.readInt();
+ // A value that identifies the type of metafile. The value for an EMF+ metafile is 0xDBC01.
+ metafileSignature = METAFILE_SIGNATURE.getValue(val);
+ // The version of operating system graphics. This value MUST be defined in the GraphicsVersion enumeration
+ graphicsVersion = GraphicsVersion.valueOf(GRAPHICS_VERSION.getValue(val));
+
+ return LittleEndianConsts.INT_SIZE;
+ }
+
+ public String toString() {
+ return "{ metafileSignature=0x"+Integer.toHexString(metafileSignature)+
+ " , graphicsVersion='"+graphicsVersion+"' }";
+ }
+ }
}
\ No newline at end of file
Added: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java?rev=1858625&view=auto
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java (added)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java Sat May 4 23:01:53 2019
@@ -0,0 +1,346 @@
+/* ====================================================================
+ 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.emfplus;
+
+import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readRectF;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+
+import org.apache.poi.hemf.record.emf.HemfFill;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianInputStream;
+
+public class HemfPlusMisc {
+ public interface EmfPlusObjectId {
+ BitField OBJECT_ID = BitFieldFactory.getInstance(0x00FF);
+
+ int getFlags();
+
+ /**
+ * The index in the EMF+ Object Table to associate with the object
+ * created by this record. The value MUST be zero to 63, inclusive.
+ */
+ default int getObjectId() {
+ return OBJECT_ID.getValue(getFlags());
+ }
+ }
+
+ public enum CombineMode {
+ CombineModeReplace(0x00000000),
+ CombineModeIntersect(0x00000001),
+ CombineModeUnion(0x00000002),
+ CombineModeXOR(0x00000003),
+ CombineModeExclude(0x00000004),
+ CombineModeComplement(0x00000005)
+ ;
+
+ public final int id;
+
+ CombineMode(int id) {
+ this.id = id;
+ }
+
+ public static CombineMode valueOf(int id) {
+ for (CombineMode wrt : values()) {
+ if (wrt.id == id) return wrt;
+ }
+ return null;
+ }
+ }
+
+
+ public static abstract class EmfPlusFlagOnly implements HemfPlusRecord {
+ private int flags;
+ private HemfPlusRecordType recordType;
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public final HemfPlusRecordType getEmfPlusRecordType() {
+ return recordType;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+ assert(dataSize == 0);
+ recordType = HemfPlusRecordType.getById(recordId);
+ return 0;
+ }
+ }
+
+ public static class EmfPlusEOF extends EmfPlusFlagOnly {
+ }
+
+ /**
+ * The EmfPlusSetPixelOffsetMode record specifies how pixels are centered with respect to the
+ * coordinates of the drawing surface.
+ */
+ public static class EmfPlusSetPixelOffsetMode extends EmfPlusFlagOnly {
+ }
+
+ /**
+ * The EmfPlusSetAntiAliasMode record specifies the anti-aliasing mode for text output.
+ */
+ public static class EmfPlusSetAntiAliasMode extends EmfPlusFlagOnly {
+ }
+
+ /**
+ * The EmfPlusSetCompositingMode record specifies how source colors are combined with background colors.
+ */
+ public static class EmfPlusSetCompositingMode extends EmfPlusFlagOnly {
+ }
+
+ /**
+ * The EmfPlusSetCompositingQuality record specifies the desired level of quality for creating
+ * composite images from multiple objects.
+ */
+ public static class EmfPlusSetCompositingQuality extends EmfPlusFlagOnly {
+ }
+
+ /**
+ * The EmfPlusSetInterpolationMode record specifies how image scaling, including stretching and
+ * shrinking, is performed.
+ */
+ public static class EmfPlusSetInterpolationMode extends EmfPlusFlagOnly {
+ }
+
+ /**
+ * The EmfPlusGetDC record specifies that subsequent EMF records encountered in the metafile
+ * SHOULD be processed.
+ */
+ public static class EmfPlusGetDC extends EmfPlusFlagOnly {
+ }
+
+ /**
+ * The EmfPlusSetTextRenderingHint record specifies the quality of text rendering, including the type
+ * of anti-aliasing.
+ */
+ public static class EmfPlusSetTextRenderingHint extends EmfPlusFlagOnly {
+ }
+
+ /**
+ * The EmfPlusResetWorldTransform record resets the current world space transform to the identify matrix.
+ */
+ public static class EmfPlusResetWorldTransform extends EmfPlusFlagOnly {
+ }
+
+
+ /**
+ * The EmfPlusSetWorldTransform record sets the world transform according to the values in a
+ * specified transform matrix.
+ */
+ public static class EmfPlusSetWorldTransform implements HemfPlusRecord {
+ private int flags;
+ private final AffineTransform matrixData = new AffineTransform();
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.setWorldTransform;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+
+ return HemfFill.readXForm(leis, matrixData);
+ }
+ }
+
+ /**
+ * The EmfPlusMultiplyWorldTransform record multiplies the current world space transform by a
+ * specified transform matrix.
+ */
+ public static class EmfPlusMultiplyWorldTransform extends EmfPlusSetWorldTransform {
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.multiplyWorldTransform;
+ }
+ }
+
+ /**
+ * The EmfPlusSetPageTransform record specifies scaling factors and units for converting page space
+ * coordinates to device space coordinates.
+ */
+ public static class EmfPlusSetPageTransform implements HemfPlusRecord {
+ private int flags;
+ private double pageScale;
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.setPageTransform;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+ pageScale = leis.readFloat();
+ return LittleEndianConsts.INT_SIZE;
+ }
+ }
+
+ /**
+ * The EmfPlusSetClipRegion record combines the current clipping region with another graphics region.
+ */
+ public static class EmfPlusSetClipRegion extends EmfPlusSetClipPath {
+
+ }
+
+
+ /**
+ * The EmfPlusSetClipPath record combines the current clipping region with a graphics path.
+ */
+ public static class EmfPlusSetClipPath extends EmfPlusFlagOnly implements EmfPlusObjectId {
+ private static final BitField COMBINE_MODE = BitFieldFactory.getInstance(0x0F00);
+
+ public CombineMode getCombineMode() {
+ return CombineMode.valueOf(COMBINE_MODE.getValue(getFlags()));
+ }
+ }
+
+ /** The EmfPlusSetClipRect record combines the current clipping region with a rectangle. */
+ public static class EmfPlusSetClipRect implements HemfPlusRecord {
+ private static final BitField COMBINE_MODE = BitFieldFactory.getInstance(0x0F00);
+
+ private int flags;
+ private final Rectangle2D clipRect = new Rectangle2D.Double();
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.setClipRect;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ public CombineMode getCombineMode() {
+ return CombineMode.valueOf(COMBINE_MODE.getValue(getFlags()));
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+
+ // An EmfPlusRectF object that defines the rectangle to use in the CombineMode operation.
+ return readRectF(leis, clipRect);
+ }
+ }
+
+
+ /** The EmfPlusResetClip record resets the current clipping region for the world space to infinity. */
+ public static class EmfPlusResetClip extends EmfPlusFlagOnly {
+
+ }
+
+ /**
+ * The EmfPlusSave record saves the graphics state, identified by a specified index, on a stack of saved
+ * graphics states.
+ */
+ public static class EmfPlusSave implements HemfPlusRecord {
+ private int flags;
+ private int stackIndex;
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.save;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+
+ // A 32-bit unsigned integer that specifies a level to associate with the graphics state.
+ // The level value can be used by a subsequent EmfPlusRestore record operation to retrieve the graphics state.
+ stackIndex = leis.readInt();
+
+ return LittleEndianConsts.INT_SIZE;
+ }
+ }
+
+ /**
+ * The EmfPlusRestore record restores the graphics state, identified by a specified index, from a stack
+ * of saved graphics states.
+ */
+ public static class EmfPlusRestore extends EmfPlusSave {
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.restore;
+ }
+ }
+
+ /** The EmfPlusSetRenderingOrigin record specifies the rendering origin for graphics output. */
+ public static class EmfPlusSetRenderingOrigin implements HemfPlusRecord {
+ int flags;
+ Point2D origin = new Point2D.Double();
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.setRenderingOrigin;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ public Point2D getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+
+ // A 32-bit unsigned integer that defines the horizontal coordinate value of the rendering origin.
+ double x = leis.readUInt();
+ // A 32-bit unsigned integer that defines the vertical coordinate value of the rendering origin.
+ double y = leis.readUInt();
+
+ origin.setLocation(x,y);
+
+ return LittleEndianConsts.INT_SIZE*2;
+ }
+ }
+
+}
Propchange: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java?rev=1858625&view=auto
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java (added)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java Sat May 4 23:01:53 2019
@@ -0,0 +1,588 @@
+/* ====================================================================
+ 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.emfplus;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.util.function.Supplier;
+
+import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
+import org.apache.poi.hemf.record.emfplus.HemfPlusMisc.EmfPlusObjectId;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianInputStream;
+
+public class HemfPlusObject {
+ private static final int MAX_OBJECT_SIZE = 50_000_000;
+
+ /**
+ * The ObjectType enumeration defines types of graphics objects that can be created and used in graphics operations.
+ */
+ public enum EmfPlusObjectType {
+ /**
+ * The object is not a valid object.
+ */
+ INVALID(0x00000000, EmfPlusUnknownData::new),
+ /**
+ * Brush objects fill graphics regions.
+ */
+ BRUSH(0x00000001, EmfPlusUnknownData::new),
+ /**
+ * Pen objects draw graphics lines.
+ */
+ PEN(0x00000002, EmfPlusUnknownData::new),
+ /**
+ * Path objects specify sequences of lines, curves, and shapes.
+ */
+ PATH(0x00000003, EmfPlusUnknownData::new),
+ /**
+ * Region objects specify areas of the output surface.
+ */
+ REGION(0x00000004, EmfPlusUnknownData::new),
+ /**
+ * Image objects encapsulate bitmaps and metafiles.
+ */
+ IMAGE(0x00000005, EmfPlusImage::new),
+ /**
+ * Font objects specify font properties, including typeface style, em size, and font family.
+ */
+ FONT(0x00000006, EmfPlusUnknownData::new),
+ /**
+ * String format objects specify text layout, including alignment, orientation, tab stops, clipping,
+ * and digit substitution for languages that do not use Western European digits.
+ */
+ STRING_FORMAT(0x00000007, EmfPlusUnknownData::new),
+ /**
+ * Image attribute objects specify operations on pixels during image rendering, including color
+ * adjustment, grayscale adjustment, gamma correction, and color mapping.
+ */
+ IMAGE_ATTRIBUTES(0x00000008, EmfPlusImageAttributes::new),
+ /**
+ * Custom line cap objects specify shapes to draw at the ends of a graphics line, including
+ * squares, circles, and diamonds.
+ */
+ CUSTOM_LINE_CAP(0x00000009, EmfPlusUnknownData::new);
+
+ public final int id;
+ public final Supplier<? extends EmfPlusObjectData> constructor;
+
+ EmfPlusObjectType(int id, Supplier<? extends EmfPlusObjectData> constructor) {
+ this.id = id;
+ this.constructor = constructor;
+ }
+
+ public static EmfPlusObjectType valueOf(int id) {
+ for (EmfPlusObjectType wrt : values()) {
+ if (wrt.id == id) return wrt;
+ }
+ return null;
+ }
+ }
+
+ public enum EmfPlusImageDataType {
+ UNKNOWN(0x00000000),
+ BITMAP(0x00000001),
+ METAFILE(0x00000002),
+ CONTINUED(-1);
+
+ public final int id;
+
+ EmfPlusImageDataType(int id) {
+ this.id = id;
+ }
+
+ public static EmfPlusImageDataType valueOf(int id) {
+ for (EmfPlusImageDataType wrt : values()) {
+ if (wrt.id == id) return wrt;
+ }
+ return null;
+ }
+ }
+
+ public enum EmfPlusBitmapDataType {
+ PIXEL(0x00000000),
+ COMPRESSED(0x00000001);
+
+ public final int id;
+
+ EmfPlusBitmapDataType(int id) {
+ this.id = id;
+ }
+
+ public static EmfPlusBitmapDataType valueOf(int id) {
+ for (EmfPlusBitmapDataType wrt : values()) {
+ if (wrt.id == id) return wrt;
+ }
+ return null;
+ }
+ }
+
+ public enum EmfPlusPixelFormat {
+ UNDEFINED(0X00000000),
+ INDEXED_1BPP(0X00030101),
+ INDEXED_4BPP(0X00030402),
+ INDEXED_8BPP(0X00030803),
+ GRAYSCALE_16BPP(0X00101004),
+ RGB555_16BPP(0X00021005),
+ RGB565_16BPP(0X00021006),
+ ARGB1555_16BPP(0X00061007),
+ RGB_24BPP(0X00021808),
+ RGB_32BPP(0X00022009),
+ ARGB_32BPP(0X0026200A),
+ PARGB_32BPP(0X000E200B),
+ RGB_48BPP(0X0010300C),
+ ARGB_64BPP(0X0034400D),
+ PARGB_64BPP(0X001A400E),
+ ;
+
+ private static final BitField CANONICAL = BitFieldFactory.getInstance(0x00200000);
+ private static final BitField EXTCOLORS = BitFieldFactory.getInstance(0x00100000);
+ private static final BitField PREMULTI = BitFieldFactory.getInstance(0x00080000);
+ private static final BitField ALPHA = BitFieldFactory.getInstance(0x00040000);
+ private static final BitField GDI = BitFieldFactory.getInstance(0x00020000);
+ private static final BitField PALETTE = BitFieldFactory.getInstance(0x00010000);
+ private static final BitField BPP = BitFieldFactory.getInstance(0x0000FF00);
+ private static final BitField INDEX = BitFieldFactory.getInstance(0x000000FF);
+
+ public final int id;
+
+ EmfPlusPixelFormat(int id) {
+ this.id = id;
+ }
+
+ public static EmfPlusPixelFormat valueOf(int id) {
+ for (EmfPlusPixelFormat wrt : values()) {
+ if (wrt.id == id) return wrt;
+ }
+ return null;
+ }
+
+ /**
+ * The pixel format enumeration index.
+ */
+ public int getGDIEnumIndex() {
+ return id == -1 ? -1 : INDEX.getValue(id);
+ }
+
+ /**
+ * The total number of bits per pixel.
+ */
+ public int getBitsPerPixel() {
+ return id == -1 ? -1 : BPP.getValue(id);
+ }
+
+ /**
+ * If set, the pixel values are indexes into a palette.
+ * If clear, the pixel values are actual colors.
+ */
+ public boolean isPaletteIndexed() {
+ return id != -1 && PALETTE.isSet(id);
+ }
+
+ /**
+ * If set, the pixel format is supported in Windows GDI.
+ * If clear, the pixel format is not supported in Windows GDI.
+ */
+ public boolean isGDISupported() {
+ return id != -1 && GDI.isSet(id);
+ }
+
+ /**
+ * If set, the pixel format includes an alpha transparency component.
+ * If clear, the pixel format does not include a component that specifies transparency.
+ */
+ public boolean isAlpha() {
+ return id != -1 && ALPHA.isSet(id);
+ }
+
+ /**
+ * If set, each color component in the pixel has been premultiplied by the pixel's alpha transparency value.
+ * If clear, each color component is multiplied by the pixel's alpha transparency value when the source pixel
+ * is blended with the destination pixel.
+ */
+ public boolean isPreMultiplied() {
+ return id != -1 && PREMULTI.isSet(id);
+ }
+
+ /**
+ * If set, the pixel format supports extended colors in 16-bits per channel.
+ * If clear, extended colors are not supported.
+ */
+ public boolean isExtendedColors() {
+ return id != -1 && EXTCOLORS.isSet(id);
+ }
+
+ /**
+ * If set, the pixel format is "canonical", which means that 32 bits per pixel are
+ * supported, with 24-bits for color components and an 8-bit alpha channel.
+ * If clear, the pixel format is not canonical.
+ */
+ public boolean isCanonical() {
+ return id != -1 && CANONICAL.isSet(id);
+ }
+ }
+
+ public enum EmfPlusMetafileDataType {
+ Wmf(0x00000001),
+ WmfPlaceable(0x00000002),
+ Emf(0x00000003),
+ EmfPlusOnly(0x00000004),
+ EmfPlusDual(0x00000005);
+
+ public final int id;
+
+ EmfPlusMetafileDataType(int id) {
+ this.id = id;
+ }
+
+ public static EmfPlusMetafileDataType valueOf(int id) {
+ for (EmfPlusMetafileDataType wrt : values()) {
+ if (wrt.id == id) return wrt;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * The WrapMode enumeration defines how the pattern from a texture or gradient brush is tiled
+ * across a shape or at shape boundaries, when it is smaller than the area being filled.
+ */
+ public enum EmfPlusWrapMode {
+ WRAP_MODE_TILE(0x00000000),
+ WRAP_MODE_TILE_FLIP_X(0x00000001),
+ WRAP_MODE_TILE_FLIP_Y(0x00000002),
+ WRAP_MODE_TILE_FLIP_XY(0x00000003),
+ WRAP_MODE_CLAMP(0x00000004)
+ ;
+
+ public final int id;
+
+ EmfPlusWrapMode(int id) {
+ this.id = id;
+ }
+
+ public static EmfPlusWrapMode valueOf(int id) {
+ for (EmfPlusWrapMode wrt : values()) {
+ if (wrt.id == id) return wrt;
+ }
+ return null;
+ }
+ }
+
+ public enum EmfPlusObjectClamp {
+ /** The object is clamped to a rectangle. */
+ RectClamp(0x00000000),
+ /** The object is clamped to a bitmap. */
+ BitmapClamp(0x00000001)
+ ;
+
+ public final int id;
+
+ EmfPlusObjectClamp(int id) {
+ this.id = id;
+ }
+
+ public static EmfPlusObjectClamp valueOf(int id) {
+ for (EmfPlusObjectClamp wrt : values()) {
+ if (wrt.id == id) return wrt;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * The EmfPlusObject record specifies an object for use in graphics operations. The object definition
+ * can span multiple records, which is indicated by the value of the Flags field.
+ */
+ public static class EmfPlusObject implements HemfPlusRecord, EmfPlusObjectId {
+
+
+ /**
+ * Indicates that the object definition continues on in the next EmfPlusObject
+ * record. This flag is never set in the final record that defines the object.
+ */
+ private static final BitField CONTINUABLE = BitFieldFactory.getInstance(0x8000);
+
+ /**
+ * Specifies the metafileType of object to be created by this record, from the
+ * ObjectType enumeration
+ */
+ private static final BitField OBJECT_TYPE = BitFieldFactory.getInstance(0x7F00);
+
+ private int flags;
+ // for debugging
+ private int objectId;
+ private EmfPlusObjectData objectData;
+ private int totalObjectSize;
+
+ @Override
+ public HemfPlusRecordType getEmfPlusRecordType() {
+ return HemfPlusRecordType.object;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ public EmfPlusObjectType getObjectType() {
+ return EmfPlusObjectType.valueOf(OBJECT_TYPE.getValue(flags));
+ }
+
+ public <T extends EmfPlusObjectData> T getObjectData() {
+ return (T)objectData;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
+ this.flags = flags;
+
+ objectId = getObjectId();
+ EmfPlusObjectType objectType = getObjectType();
+ assert (objectType != null);
+
+ int size = 0;
+
+ totalObjectSize = 0;
+ int dataSize2 = (int) dataSize;
+
+ if (CONTINUABLE.isSet(flags)) {
+ // If the record is continuable, when the continue bit is set, this field will be present.
+ // Continuing objects have multiple EMF+ records starting with EmfPlusContinuedObjectRecord.
+ // Each EmfPlusContinuedObjectRecord will contain a TotalObjectSize. Once TotalObjectSize number
+ // of bytes has been read, the next EMF+ record will not be treated as part of the continuing object.
+ totalObjectSize = leis.readInt();
+ size += LittleEndianConsts.INT_SIZE;
+ dataSize2 -= LittleEndianConsts.INT_SIZE;
+ }
+
+ objectData = objectType.constructor.get();
+ size += objectData.init(leis, dataSize2, objectType, flags);
+
+ return size;
+ }
+ }
+
+ public interface EmfPlusObjectData {
+ long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException;
+ }
+
+ public static class EmfPlusUnknownData implements EmfPlusObjectData {
+ private EmfPlusObjectType objectType;
+ private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
+ private byte[] objectDataBytes;
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
+ this.objectType = objectType;
+
+ long size = graphicsVersion.init(leis);
+
+ objectDataBytes = IOUtils.toByteArray(leis, dataSize - size, MAX_OBJECT_SIZE);
+
+ return dataSize;
+ }
+ }
+
+ public static class EmfPlusImage implements EmfPlusObjectData {
+ private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
+ private EmfPlusImageDataType imageDataType;
+ private int bitmapWidth;
+ private int bitmapHeight;
+ private int bitmapStride;
+ private EmfPlusPixelFormat pixelFormat;
+ private EmfPlusBitmapDataType bitmapType;
+ private byte[] imageData;
+ private EmfPlusMetafileDataType metafileType;
+ private int metafileDataSize;
+
+ public EmfPlusImageDataType getImageDataType() {
+ return imageDataType;
+ }
+
+ public byte[] getImageData() {
+ return imageData;
+ }
+
+ public EmfPlusPixelFormat getPixelFormat() {
+ return pixelFormat;
+ }
+
+ public EmfPlusBitmapDataType getBitmapType() {
+ return bitmapType;
+ }
+
+ public int getBitmapWidth() {
+ return bitmapWidth;
+ }
+
+ public int getBitmapHeight() {
+ return bitmapHeight;
+ }
+
+ public int getBitmapStride() {
+ return bitmapStride;
+ }
+
+ public EmfPlusMetafileDataType getMetafileType() {
+ return metafileType;
+ }
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
+ leis.mark(LittleEndianConsts.INT_SIZE);
+ long size = graphicsVersion.init(leis);
+
+ if (graphicsVersion.getGraphicsVersion() == null || graphicsVersion.getMetafileSignature() != 0xDBC01) {
+ // CONTINUABLE is not always correctly set, so we check the version field if this record is continued
+ imageDataType = EmfPlusImageDataType.CONTINUED;
+ leis.reset();
+ size = 0;
+ } else {
+ imageDataType = EmfPlusImageDataType.valueOf(leis.readInt());
+ size += LittleEndianConsts.INT_SIZE;
+ }
+
+ if (imageDataType == null) {
+ imageDataType = EmfPlusImageDataType.UNKNOWN;
+ }
+
+ int fileSize;
+ switch (imageDataType) {
+ default:
+ case UNKNOWN:
+ case CONTINUED:
+ bitmapWidth = -1;
+ bitmapHeight = -1;
+ bitmapStride = -1;
+ bitmapType = null;
+ pixelFormat = null;
+
+ fileSize = (int) (dataSize);
+ break;
+
+ case BITMAP:
+ // A 32-bit signed integer that specifies the width in pixels of the area occupied by the bitmap.
+ // If the image is compressed, according to the Type field, this value is undefined and MUST be ignored.
+ bitmapWidth = leis.readInt();
+ // A 32-bit signed integer that specifies the height in pixels of the area occupied by the bitmap.
+ // If the image is compressed, according to the Type field, this value is undefined and MUST be ignored.
+ bitmapHeight = leis.readInt();
+ // A 32-bit signed integer that specifies the byte offset between the beginning of one scan-line
+ // and the next. This value is the number of bytes per pixel, which is specified in the PixelFormat
+ // field, multiplied by the width in pixels, which is specified in the Width field.
+ // The value of this field MUST be a multiple of four. If the image is compressed, according to the
+ // Type field, this value is undefined and MUST be ignored.
+ bitmapStride = leis.readInt();
+ // A 32-bit unsigned integer that specifies the format of the pixels that make up the bitmap image.
+ // The supported pixel formats are specified in the PixelFormat enumeration
+ int pixelFormatInt = leis.readInt();
+ // A 32-bit unsigned integer that specifies the metafileType of data in the BitmapData field.
+ // This value MUST be defined in the BitmapDataType enumeration
+ bitmapType = EmfPlusBitmapDataType.valueOf(leis.readInt());
+ size += 5 * LittleEndianConsts.INT_SIZE;
+
+ pixelFormat = (bitmapType == EmfPlusBitmapDataType.PIXEL)
+ ? EmfPlusPixelFormat.valueOf(pixelFormatInt)
+ : EmfPlusPixelFormat.UNDEFINED;
+ assert (pixelFormat != null);
+
+ fileSize = (int) (dataSize - size);
+
+ break;
+
+ case METAFILE:
+ // A 32-bit unsigned integer that specifies the type of metafile that is embedded in the
+ // MetafileData field. This value MUST be defined in the MetafileDataType enumeration
+ metafileType = EmfPlusMetafileDataType.valueOf(leis.readInt());
+
+ // A 32-bit unsigned integer that specifies the size in bytes of the
+ // metafile data in the MetafileData field.
+ metafileDataSize = leis.readInt();
+
+ size += 2 * LittleEndianConsts.INT_SIZE;
+
+ // ignore metafileDataSize, which might ignore a (placeable) header in front
+ // and also use the remaining bytes, which might contain padding bytes ...
+ fileSize = (int) (dataSize - size);
+ break;
+ }
+
+ assert (fileSize <= dataSize - size);
+
+ imageData = IOUtils.toByteArray(leis, fileSize, MAX_OBJECT_SIZE);
+
+ // TODO: remove padding bytes between placeable WMF header and body?
+
+ return size + fileSize;
+ }
+ }
+
+ public static class EmfPlusImageAttributes implements EmfPlusObjectData {
+ private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
+ private EmfPlusWrapMode wrapMode;
+ private Color clampColor;
+ private EmfPlusObjectClamp objectClamp;
+
+ @Override
+ public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
+ // An EmfPlusGraphicsVersion object that specifies the version of operating system graphics that
+ // was used to create this object.
+ long size = graphicsVersion.init(leis);
+
+ // A 32-bit field that is not used and MUST be ignored.
+ leis.skip(LittleEndianConsts.INT_SIZE);
+
+ // A 32-bit unsigned integer that specifies how to handle edge conditions with a value from the WrapMode enumeration
+ wrapMode = EmfPlusWrapMode.valueOf(leis.readInt());
+
+ // An EmfPlusARGB object that specifies the edge color to use when the WrapMode value is WrapModeClamp.
+ // This color is visible when the source rectangle processed by an EmfPlusDrawImage record is larger than the image itself.
+ byte[] buf = new byte[LittleEndianConsts.INT_SIZE];
+ leis.readFully(buf);
+ clampColor = new Color(buf[2], buf[1], buf[0], buf[3]);
+
+ // A 32-bit signed integer that specifies the object clamping behavior. It is not used until this object
+ // is applied to an image being drawn. This value MUST be one of the values defined in the following table.
+ objectClamp = EmfPlusObjectClamp.valueOf(leis.readInt());
+
+ // A value that SHOULD be set to zero and MUST be ignored upon receipt.
+ leis.skip(LittleEndianConsts.INT_SIZE);
+
+ return size + 5*LittleEndianConsts.INT_SIZE;
+ }
+
+ public EmfPlusGraphicsVersion getGraphicsVersion() {
+ return graphicsVersion;
+ }
+
+ public EmfPlusWrapMode getWrapMode() {
+ return wrapMode;
+ }
+
+ public Color getClampColor() {
+ return clampColor;
+ }
+
+ public EmfPlusObjectClamp getObjectClamp() {
+ return objectClamp;
+ }
+ }
+}
Propchange: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordIterator.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordIterator.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordIterator.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordIterator.java Sat May 4 23:01:53 2019
@@ -50,7 +50,8 @@ public class HemfPlusRecordIterator impl
@Override
public HemfPlusRecord next() {
HemfPlusRecord toReturn = currentRecord;
- final boolean isEOF = (limit == -1 || leis.getReadIndex()-startIdx >= limit);
+ // add the size for recordId/flags/recordSize/dataSize = 12 bytes
+ final boolean isEOF = (limit == -1 || (leis.getReadIndex()-startIdx)+12 >= limit);
// (currentRecord instanceof HemfPlusMisc.EmfEof)
currentRecord = isEOF ? null : _next();
return toReturn;
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordType.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordType.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordType.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordType.java Sat May 4 23:01:53 2019
@@ -24,16 +24,16 @@ import org.apache.poi.util.Internal;
@Internal
public enum HemfPlusRecordType {
header(0x4001, HemfPlusHeader::new),
- eof(0x4002, UnimplementedHemfPlusRecord::new),
+ eof(0x4002, HemfPlusMisc.EmfPlusEOF::new),
comment(0x4003, UnimplementedHemfPlusRecord::new),
- getDC(0x4004, UnimplementedHemfPlusRecord::new),
+ getDC(0x4004, HemfPlusMisc.EmfPlusGetDC::new),
multiFormatStart(0x4005, UnimplementedHemfPlusRecord::new),
multiFormatSection(0x4006, UnimplementedHemfPlusRecord::new),
multiFormatEnd(0x4007, UnimplementedHemfPlusRecord::new),
- object(0x4008, UnimplementedHemfPlusRecord::new),
+ object(0x4008, HemfPlusObject.EmfPlusObject::new),
clear(0x4009, UnimplementedHemfPlusRecord::new),
- fillRects(0x400A, UnimplementedHemfPlusRecord::new),
- drawRects(0x400B, UnimplementedHemfPlusRecord::new),
+ fillRects(0x400A, HemfPlusDraw.EmfPlusFillRects::new),
+ drawRects(0x400B, HemfPlusDraw.EmfPlusDrawRects::new),
fillPolygon(0x400C, UnimplementedHemfPlusRecord::new),
drawLines(0x400D, UnimplementedHemfPlusRecord::new),
fillEllipse(0x400E, UnimplementedHemfPlusRecord::new),
@@ -41,42 +41,42 @@ public enum HemfPlusRecordType {
fillPie(0x4010, UnimplementedHemfPlusRecord::new),
drawPie(0x4011, UnimplementedHemfPlusRecord::new),
drawArc(0x4012, UnimplementedHemfPlusRecord::new),
- fillRegion(0x4013, UnimplementedHemfPlusRecord::new),
- fillPath(0x4014, UnimplementedHemfPlusRecord::new),
- drawPath(0x4015, UnimplementedHemfPlusRecord::new),
+ fillRegion(0x4013, HemfPlusDraw.EmfPlusFillRegion::new),
+ fillPath(0x4014, HemfPlusDraw.EmfPlusFillPath::new),
+ drawPath(0x4015, HemfPlusDraw.EmfPlusDrawPath::new),
fillClosedCurve(0x4016, UnimplementedHemfPlusRecord::new),
drawClosedCurve(0x4017, UnimplementedHemfPlusRecord::new),
drawCurve(0x4018, UnimplementedHemfPlusRecord::new),
drawBeziers(0x4019, UnimplementedHemfPlusRecord::new),
- drawImage(0x401A, UnimplementedHemfPlusRecord::new),
- drawImagePoints(0x401B, UnimplementedHemfPlusRecord::new),
+ drawImage(0x401A, HemfPlusDraw.EmfPlusDrawImage::new),
+ drawImagePoints(0x401B, HemfPlusDraw.EmfPlusDrawImagePoints::new),
drawString(0x401C, UnimplementedHemfPlusRecord::new),
- setRenderingOrigin(0x401D, UnimplementedHemfPlusRecord::new),
- setAntiAliasMode(0x401E, UnimplementedHemfPlusRecord::new),
- setTextRenderingHint(0x401F, UnimplementedHemfPlusRecord::new),
+ setRenderingOrigin(0x401D, HemfPlusMisc.EmfPlusSetRenderingOrigin::new),
+ setAntiAliasMode(0x401E, HemfPlusMisc.EmfPlusSetAntiAliasMode::new),
+ setTextRenderingHint(0x401F, HemfPlusMisc.EmfPlusSetTextRenderingHint::new),
setTextContrast(0x4020, UnimplementedHemfPlusRecord::new),
- setInterpolationMode(0x4021, UnimplementedHemfPlusRecord::new),
- setPixelOffsetMode(0x4022, UnimplementedHemfPlusRecord::new),
- setComositingMode(0x4023, UnimplementedHemfPlusRecord::new),
- setCompositingQuality(0x4024, UnimplementedHemfPlusRecord::new),
- save(0x4025, UnimplementedHemfPlusRecord::new),
- restore(0x4026, UnimplementedHemfPlusRecord::new),
+ setInterpolationMode(0x4021, HemfPlusMisc.EmfPlusSetInterpolationMode::new),
+ setPixelOffsetMode(0x4022, HemfPlusMisc.EmfPlusSetPixelOffsetMode::new),
+ setCompositingMode(0x4023, HemfPlusMisc.EmfPlusSetCompositingMode::new),
+ setCompositingQuality(0x4024, HemfPlusMisc.EmfPlusSetCompositingQuality::new),
+ save(0x4025, HemfPlusMisc.EmfPlusSave::new),
+ restore(0x4026, HemfPlusMisc.EmfPlusRestore::new),
beginContainer(0x4027, UnimplementedHemfPlusRecord::new),
beginContainerNoParams(0x428, UnimplementedHemfPlusRecord::new),
endContainer(0x4029, UnimplementedHemfPlusRecord::new),
- setWorldTransform(0x402A, UnimplementedHemfPlusRecord::new),
- resetWorldTransform(0x402B, UnimplementedHemfPlusRecord::new),
- multiplyWorldTransform(0x402C, UnimplementedHemfPlusRecord::new),
+ setWorldTransform(0x402A, HemfPlusMisc.EmfPlusSetWorldTransform::new),
+ resetWorldTransform(0x402B, HemfPlusMisc.EmfPlusResetWorldTransform::new),
+ multiplyWorldTransform(0x402C, HemfPlusMisc.EmfPlusMultiplyWorldTransform::new),
translateWorldTransform(0x402D, UnimplementedHemfPlusRecord::new),
scaleWorldTransform(0x402E, UnimplementedHemfPlusRecord::new),
rotateWorldTransform(0x402F, UnimplementedHemfPlusRecord::new),
- setPageTransform(0x4030, UnimplementedHemfPlusRecord::new),
- resetClip(0x4031, UnimplementedHemfPlusRecord::new),
- setClipRect(0x4032, UnimplementedHemfPlusRecord::new),
- setClipRegion(0x4033, UnimplementedHemfPlusRecord::new),
- setClipPath(0x4034, UnimplementedHemfPlusRecord::new),
+ setPageTransform(0x4030, HemfPlusMisc.EmfPlusSetPageTransform::new),
+ resetClip(0x4031, HemfPlusMisc.EmfPlusResetClip::new),
+ setClipRect(0x4032, HemfPlusMisc.EmfPlusSetClipRect::new),
+ setClipRegion(0x4033, HemfPlusMisc.EmfPlusSetClipRegion::new),
+ setClipPath(0x4034, HemfPlusMisc.EmfPlusSetClipPath::new),
offsetClip(0x4035, UnimplementedHemfPlusRecord::new),
- drawDriverstring(0x4036, UnimplementedHemfPlusRecord::new),
+ drawDriverstring(0x4036, HemfPlusDraw.EmfPlusDrawDriverString::new),
strokeFillPath(0x4037, UnimplementedHemfPlusRecord::new),
serializableObject(0x4038, UnimplementedHemfPlusRecord::new),
setTSGraphics(0x4039, UnimplementedHemfPlusRecord::new),
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org