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 [2/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/...
Added: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfEmbeddedIterator.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfEmbeddedIterator.java?rev=1858625&view=auto
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfEmbeddedIterator.java (added)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfEmbeddedIterator.java Sat May 4 23:01:53 2019
@@ -0,0 +1,342 @@
+/* ====================================================================
+ 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.usermodel;
+
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.PixelInterleavedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Iterator;
+
+import javax.imageio.ImageIO;
+
+import org.apache.poi.hemf.record.emf.HemfComment;
+import org.apache.poi.hemf.record.emf.HemfRecord;
+import org.apache.poi.hemf.record.emfplus.HemfPlusObject;
+import org.apache.poi.hwmf.record.HwmfBitmapDib;
+import org.apache.poi.hwmf.record.HwmfFill;
+import org.apache.poi.hwmf.usermodel.HwmfEmbedded;
+import org.apache.poi.hwmf.usermodel.HwmfEmbeddedType;
+import org.apache.poi.poifs.filesystem.FileMagic;
+
+public class HemfEmbeddedIterator implements Iterator<HwmfEmbedded> {
+
+ private final Deque<Iterator<?>> iterStack = new ArrayDeque<>();
+ private Object current;
+
+ public HemfEmbeddedIterator(HemfPicture emf) {
+ this(emf.getRecords().iterator());
+ }
+
+ public HemfEmbeddedIterator(Iterator<HemfRecord> recordIterator) {
+ iterStack.add(recordIterator);
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (iterStack.isEmpty()) {
+ return false;
+ }
+
+ if (current != null) {
+ // don't search twice and potentially skip items
+ return true;
+ }
+
+ Iterator<?> iter;
+ do {
+ iter = iterStack.peek();
+ while (iter.hasNext()) {
+ Object obj = iter.next();
+ if (obj instanceof HemfComment.EmfComment) {
+ HemfComment.EmfCommentData cd = ((HemfComment.EmfComment)obj).getCommentData();
+ if (
+ cd instanceof HemfComment.EmfCommentDataWMF ||
+ cd instanceof HemfComment.EmfCommentDataGeneric
+ ) {
+ current = obj;
+ return true;
+ }
+
+ if (cd instanceof HemfComment.EmfCommentDataMultiformats) {
+ Iterator<?> iter2 = ((HemfComment.EmfCommentDataMultiformats)cd).getFormats().iterator();
+ if (iter2.hasNext()) {
+ iterStack.push(iter2);
+ continue;
+ }
+ }
+
+ if (cd instanceof HemfComment.EmfCommentDataPlus) {
+ Iterator<?> iter2 = ((HemfComment.EmfCommentDataPlus)cd).getRecords().iterator();
+ if (iter2.hasNext()) {
+ iter = iter2;
+ iterStack.push(iter2);
+ continue;
+ }
+ }
+ }
+
+ if (obj instanceof HemfComment.EmfCommentDataFormat) {
+ current = obj;
+ return true;
+ }
+
+ if (obj instanceof HemfPlusObject.EmfPlusObject && ((HemfPlusObject.EmfPlusObject)obj).getObjectType() == HemfPlusObject.EmfPlusObjectType.IMAGE) {
+ current = obj;
+ return true;
+ }
+
+ if (obj instanceof HwmfFill.WmfStretchDib) {
+ HwmfBitmapDib bitmap = ((HwmfFill.WmfStretchDib) obj).getBitmap();
+ if (bitmap.isValid()) {
+ current = obj;
+ return true;
+ }
+ }
+ }
+ iterStack.pop();
+ } while (!iterStack.isEmpty());
+
+ return false;
+ }
+
+ @Override
+ public HwmfEmbedded next() {
+ HwmfEmbedded emb;
+ if ((emb = checkEmfCommentDataWMF()) != null) {
+ return emb;
+ }
+ if ((emb = checkEmfCommentDataGeneric()) != null) {
+ return emb;
+ }
+ if ((emb = checkEmfCommentDataFormat()) != null) {
+ return emb;
+ }
+ if ((emb = checkEmfPlusObject()) != null) {
+ return emb;
+ }
+ if ((emb = checkWmfStretchDib()) != null) {
+ return emb;
+ }
+
+ return null;
+ }
+
+ private HwmfEmbedded checkEmfCommentDataWMF() {
+ if (!(current instanceof HemfComment.EmfCommentDataWMF && ((HemfComment.EmfComment)current).getCommentData() instanceof HemfComment.EmfCommentDataWMF)) {
+ return null;
+ }
+
+ HemfComment.EmfCommentDataWMF wmf = (HemfComment.EmfCommentDataWMF)((HemfComment.EmfComment)current).getCommentData();
+ HwmfEmbedded emb = new HwmfEmbedded();
+ emb.setEmbeddedType(HwmfEmbeddedType.WMF);
+ emb.setData(wmf.getWMFData());
+ current = null;
+ return emb;
+ }
+
+ private HwmfEmbedded checkEmfCommentDataGeneric() {
+ if (!(current instanceof HemfComment.EmfComment && ((HemfComment.EmfComment)current).getCommentData() instanceof HemfComment.EmfCommentDataGeneric)) {
+ return null;
+ }
+ HemfComment.EmfCommentDataGeneric cdg = (HemfComment.EmfCommentDataGeneric)((HemfComment.EmfComment)current).getCommentData();
+ HwmfEmbedded emb = new HwmfEmbedded();
+ emb.setEmbeddedType(HwmfEmbeddedType.UNKNOWN);
+ emb.setData(cdg.getPrivateData());
+ current = null;
+ return emb;
+ }
+
+ private HwmfEmbedded checkEmfCommentDataFormat() {
+ if (!(current instanceof HemfComment.EmfCommentDataFormat)) {
+ return null;
+ }
+ HemfComment.EmfCommentDataFormat cdf = (HemfComment.EmfCommentDataFormat)current;
+ HwmfEmbedded emb = new HwmfEmbedded();
+ boolean isEmf = (cdf.getSignature() == HemfComment.EmfFormatSignature.ENHMETA_SIGNATURE);
+ emb.setEmbeddedType(isEmf ? HwmfEmbeddedType.EMF : HwmfEmbeddedType.EPS);
+ emb.setData(cdf.getRawData());
+ current = null;
+ return emb;
+ }
+
+ private HwmfEmbedded checkWmfStretchDib() {
+ if (!(current instanceof HwmfFill.WmfStretchDib)) {
+ return null;
+ }
+ HwmfEmbedded emb = new HwmfEmbedded();
+ emb.setData(((HwmfFill.WmfStretchDib) current).getBitmap().getBMPData());
+ emb.setEmbeddedType(HwmfEmbeddedType.BMP);
+ current = null;
+ return emb;
+ }
+
+ private HwmfEmbedded checkEmfPlusObject() {
+ if (!(current instanceof HemfPlusObject.EmfPlusObject)) {
+ return null;
+ }
+
+ HemfPlusObject.EmfPlusObject epo = (HemfPlusObject.EmfPlusObject)current;
+ assert(epo.getObjectType() == HemfPlusObject.EmfPlusObjectType.IMAGE);
+ HemfPlusObject.EmfPlusImage img = epo.getObjectData();
+ assert(img.getImageDataType() != null);
+
+ HwmfEmbedded emb = getEmfPlusImageData();
+
+ HwmfEmbeddedType et;
+ switch (img.getImageDataType()) {
+ case BITMAP:
+ if (img.getBitmapType() == HemfPlusObject.EmfPlusBitmapDataType.COMPRESSED) {
+ switch (FileMagic.valueOf(emb.getRawData())) {
+ case JPEG:
+ et = HwmfEmbeddedType.JPEG;
+ break;
+ case GIF:
+ et = HwmfEmbeddedType.GIF;
+ break;
+ case PNG:
+ et = HwmfEmbeddedType.PNG;
+ break;
+ case TIFF:
+ et = HwmfEmbeddedType.TIFF;
+ break;
+ default:
+ et = HwmfEmbeddedType.BITMAP;
+ break;
+ }
+ } else {
+ et = HwmfEmbeddedType.PNG;
+ compressGDIBitmap(img, emb, et);
+ }
+ break;
+ case METAFILE:
+ assert(img.getMetafileType() != null);
+ switch (img.getMetafileType()) {
+ case Wmf:
+ case WmfPlaceable:
+ et = HwmfEmbeddedType.WMF;
+ break;
+ case Emf:
+ case EmfPlusDual:
+ case EmfPlusOnly:
+ et = HwmfEmbeddedType.EMF;
+ break;
+ default:
+ et = HwmfEmbeddedType.UNKNOWN;
+ break;
+ }
+ break;
+ default:
+ et = HwmfEmbeddedType.UNKNOWN;
+ break;
+ }
+ emb.setEmbeddedType(et);
+
+ return emb;
+ }
+
+ /**
+ * Compress GDIs internal format to something useful
+ */
+ private void compressGDIBitmap(HemfPlusObject.EmfPlusImage img, HwmfEmbedded emb, HwmfEmbeddedType et) {
+ final int width = img.getBitmapWidth();
+ final int height = img.getBitmapHeight();
+ final int stride = img.getBitmapStride();
+ final HemfPlusObject.EmfPlusPixelFormat pf = img.getPixelFormat();
+
+ int[] nBits, bOffs;
+ switch (pf) {
+ case ARGB_32BPP:
+ nBits = new int[]{8, 8, 8, 8};
+ bOffs = new int[]{2, 1, 0, 3};
+ break;
+ case RGB_24BPP:
+ nBits = new int[]{8, 8, 8};
+ bOffs = new int[]{2, 1, 0};
+ break;
+ default:
+ throw new RuntimeException("not yet implemented");
+ }
+
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+ ComponentColorModel cm = new ComponentColorModel
+ (cs, nBits, pf.isAlpha(), pf.isPreMultiplied(), Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
+ PixelInterleavedSampleModel csm =
+ new PixelInterleavedSampleModel(cm.getTransferType(), width, height, cm.getNumColorComponents(), stride, bOffs);
+
+ byte d[] = emb.getRawData();
+ WritableRaster raster = (WritableRaster) Raster.createRaster(csm, new DataBufferByte(d, d.length), null);
+
+ BufferedImage bi = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
+
+ try {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ // use HwmfEmbeddedType literal for conversion
+ ImageIO.write(bi, et.toString(), bos);
+ emb.setData(bos.toByteArray());
+ } catch (IOException e) {
+ // TODO: throw appropriate exception
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ private HwmfEmbedded getEmfPlusImageData() {
+ HemfPlusObject.EmfPlusObject epo = (HemfPlusObject.EmfPlusObject)current;
+ assert(epo.getObjectType() == HemfPlusObject.EmfPlusObjectType.IMAGE);
+
+ final int objectId = epo.getObjectId();
+
+ HwmfEmbedded emb = new HwmfEmbedded();
+
+ HemfPlusObject.EmfPlusImage img = (HemfPlusObject.EmfPlusImage)epo.getObjectData();
+ assert(img.getImageDataType() != null);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try {
+ for (;;) {
+ bos.write(img.getImageData());
+
+ current = null;
+ //noinspection ConstantConditions
+ if (hasNext() &&
+ (current instanceof HemfPlusObject.EmfPlusObject) &&
+ ((epo = (HemfPlusObject.EmfPlusObject) current).getObjectId() == objectId)
+ ) {
+ img = (HemfPlusObject.EmfPlusImage)epo.getObjectData();
+ } else {
+ return emb;
+ }
+ }
+ } catch (IOException e) {
+ // ByteArrayOutputStream doesn't throw IOException
+ return null;
+ } finally {
+ emb.setData(bos.toByteArray());
+ }
+ }
+}
Propchange: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfEmbeddedIterator.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java Sat May 4 23:01:53 2019
@@ -35,6 +35,7 @@ import org.apache.poi.hemf.record.emf.He
import org.apache.poi.hemf.record.emf.HemfRecord;
import org.apache.poi.hemf.record.emf.HemfRecordIterator;
import org.apache.poi.hemf.record.emf.HemfWindowing;
+import org.apache.poi.hwmf.usermodel.HwmfEmbedded;
import org.apache.poi.util.Dimension2DDouble;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndianInputStream;
@@ -158,4 +159,7 @@ public class HemfPicture implements Iter
}
}
+ public Iterable<HwmfEmbedded> getEmbeddings() {
+ return () -> new HemfEmbeddedIterator(HemfPicture.this);
+ }
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java Sat May 4 23:01:53 2019
@@ -142,7 +142,7 @@ public class HwmfGraphics {
graphicsCtx.fill(shape);
}
-// draw(shape);
+ draw(shape);
}
protected BasicStroke getStroke() {
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java Sat May 4 23:01:53 2019
@@ -25,6 +25,7 @@ import java.awt.LinearGradientPaint;
import java.awt.MultipleGradientPaint;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
+import java.awt.image.IndexColorModel;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -460,12 +461,29 @@ public class HwmfBitmapDib {
}
public BufferedImage getImage() {
+ return getImage(null, null, false);
+ }
+
+ public BufferedImage getImage(Color foreground, Color background, boolean hasAlpha) {
+ BufferedImage bi;
try {
- return ImageIO.read(getBMPStream());
+ bi = ImageIO.read(getBMPStream());
} catch (IOException|RuntimeException e) {
logger.log(POILogger.ERROR, "invalid bitmap data - returning placeholder image");
return getPlaceholder();
}
+
+ if (foreground != null && background != null && headerBitCount == HwmfBitmapDib.BitCount.BI_BITCOUNT_1) {
+ IndexColorModel cmOld = (IndexColorModel)bi.getColorModel();
+ int transPixel = hasAlpha ? (((cmOld.getRGB(0) & 0xFFFFFF) == 0) ? 0 : 1) : -1;
+ int transferType = bi.getData().getTransferType();
+ int fg = foreground.getRGB(), bg = background.getRGB();
+ int[] cmap = { (transPixel == 0 ? bg : fg), (transPixel == 1 ? bg : fg) };
+ IndexColorModel cmNew = new IndexColorModel(1, cmap.length, cmap, 0, hasAlpha, transPixel, transferType);
+ bi = new BufferedImage(cmNew, bi.getRaster(), false, null);
+ }
+
+ return bi;
}
@Override
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfEscape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfEscape.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfEscape.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfEscape.java Sat May 4 23:01:53 2019
@@ -18,10 +18,13 @@
package org.apache.poi.hwmf.record;
import java.io.IOException;
+import java.util.function.Supplier;
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianCP950Reader;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
@@ -30,135 +33,140 @@ import org.apache.poi.util.LittleEndianI
* might not be directly accessible through WMF records
*/
public class HwmfEscape implements HwmfRecord {
-
+ private static final int MAX_OBJECT_SIZE = 0xFFFF;
+
public enum EscapeFunction {
/** Notifies the printer driver that the application has finished writing to a page. */
- NEWFRAME(0x0001),
+ NEWFRAME(0x0001, WmfEscapeUnknownData::new),
/** Stops processing the current document. */
- ABORTDOC(0x0002),
+ ABORTDOC(0x0002, WmfEscapeUnknownData::new),
/** Notifies the printer driver that the application has finished writing to a band. */
- NEXTBAND(0x0003),
+ NEXTBAND(0x0003, WmfEscapeUnknownData::new),
/** Sets color table values. */
- SETCOLORTABLE(0x0004),
+ SETCOLORTABLE(0x0004, WmfEscapeUnknownData::new),
/** Gets color table values. */
- GETCOLORTABLE(0x0005),
+ GETCOLORTABLE(0x0005, WmfEscapeUnknownData::new),
/** Causes all pending output to be flushed to the output device. */
- FLUSHOUT(0x0006),
+ FLUSHOUT(0x0006, WmfEscapeUnknownData::new),
/** Indicates that the printer driver SHOULD print text only, and no graphics. */
- DRAFTMODE(0x0007),
+ DRAFTMODE(0x0007, WmfEscapeUnknownData::new),
/** Queries a printer driver to determine whether a specific escape function is supported on the output device it drives. */
- QUERYESCSUPPORT(0x0008),
+ QUERYESCSUPPORT(0x0008, WmfEscapeUnknownData::new),
/** Sets the application-defined function that allows a print job to be canceled during printing. */
- SETABORTPROC(0x0009),
+ SETABORTPROC(0x0009, WmfEscapeUnknownData::new),
/** Notifies the printer driver that a new print job is starting. */
- STARTDOC(0x000A),
+ STARTDOC(0x000A, WmfEscapeUnknownData::new),
/** Notifies the printer driver that the current print job is ending. */
- ENDDOC(0x000B),
+ ENDDOC(0x000B, WmfEscapeUnknownData::new),
/** Retrieves the physical page size currently selected on an output device. */
- GETPHYSPAGESIZE(0x000C),
+ GETPHYSPAGESIZE(0x000C, WmfEscapeUnknownData::new),
/** Retrieves the offset from the upper-left corner of the physical page where the actual printing or drawing begins. */
- GETPRINTINGOFFSET(0x000D),
+ GETPRINTINGOFFSET(0x000D, WmfEscapeUnknownData::new),
/** Retrieves the scaling factors for the x-axis and the y-axis of a printer. */
- GETSCALINGFACTOR(0x000E),
+ GETSCALINGFACTOR(0x000E, WmfEscapeUnknownData::new),
/** Used to embed an enhanced metafile format (EMF) metafile within a WMF metafile. */
- META_ESCAPE_ENHANCED_METAFILE(0x000F),
+ META_ESCAPE_ENHANCED_METAFILE(0x000F, WmfEscapeEMF::new),
/** Sets the width of a pen in pixels. */
- SETPENWIDTH(0x0010),
+ SETPENWIDTH(0x0010, WmfEscapeUnknownData::new),
/** Sets the number of copies. */
- SETCOPYCOUNT(0x0011),
+ SETCOPYCOUNT(0x0011, WmfEscapeUnknownData::new),
/** Sets the source, such as a particular paper tray or bin on a printer, for output forms. */
- SETPAPERSOURCE(0x0012),
+ SETPAPERSOURCE(0x0012, WmfEscapeUnknownData::new),
/** This record passes through arbitrary data. */
- PASSTHROUGH(0x0013),
+ PASSTHROUGH(0x0013, WmfEscapeUnknownData::new),
/** Gets information concerning graphics technology that is supported on a device. */
- GETTECHNOLOGY(0x0014),
+ GETTECHNOLOGY(0x0014, WmfEscapeUnknownData::new),
/** Specifies the line-drawing mode to use in output to a device. */
- SETLINECAP(0x0015),
+ SETLINECAP(0x0015, WmfEscapeUnknownData::new),
/** Specifies the line-joining mode to use in output to a device. */
- SETLINEJOIN(0x0016),
+ SETLINEJOIN(0x0016, WmfEscapeUnknownData::new),
/** Sets the limit for the length of miter joins to use in output to a device. */
- SETMITERLIMIT(0x0017),
+ SETMITERLIMIT(0x0017, WmfEscapeUnknownData::new),
/** Retrieves or specifies settings concerning banding on a device, such as the number of bands. */
- BANDINFO(0x0018),
+ BANDINFO(0x0018, WmfEscapeUnknownData::new),
/** Draws a rectangle with a defined pattern. */
- DRAWPATTERNRECT(0x0019),
+ DRAWPATTERNRECT(0x0019, WmfEscapeUnknownData::new),
/** Retrieves the physical pen size currently defined on a device. */
- GETVECTORPENSIZE(0x001A),
+ GETVECTORPENSIZE(0x001A, WmfEscapeUnknownData::new),
/** Retrieves the physical brush size currently defined on a device. */
- GETVECTORBRUSHSIZE(0x001B),
+ GETVECTORBRUSHSIZE(0x001B, WmfEscapeUnknownData::new),
/** Enables or disables double-sided (duplex) printing on a device. */
- ENABLEDUPLEX(0x001C),
+ ENABLEDUPLEX(0x001C, WmfEscapeUnknownData::new),
/** Retrieves or specifies the source of output forms on a device. */
- GETSETPAPERBINS(0x001D),
+ GETSETPAPERBINS(0x001D, WmfEscapeUnknownData::new),
/** Retrieves or specifies the paper orientation on a device. */
- GETSETPRINTORIENT(0x001E),
+ GETSETPRINTORIENT(0x001E, WmfEscapeUnknownData::new),
/** Retrieves information concerning the sources of different forms on an output device. */
- ENUMPAPERBINS(0x001F),
+ ENUMPAPERBINS(0x001F, WmfEscapeUnknownData::new),
/** Specifies the scaling of device-independent bitmaps (DIBs). */
- SETDIBSCALING(0x0020),
+ SETDIBSCALING(0x0020, WmfEscapeUnknownData::new),
/** Indicates the start and end of an encapsulated PostScript (EPS) section. */
- EPSPRINTING(0x0021),
+ EPSPRINTING(0x0021, WmfEscapeUnknownData::new),
/** Queries a printer driver for paper dimensions and other forms data. */
- ENUMPAPERMETRICS(0x0022),
+ ENUMPAPERMETRICS(0x0022, WmfEscapeUnknownData::new),
/** Retrieves or specifies paper dimensions and other forms data on an output device. */
- GETSETPAPERMETRICS(0x0023),
+ GETSETPAPERMETRICS(0x0023, WmfEscapeUnknownData::new),
/** Sends arbitrary PostScript data to an output device. */
- POSTSCRIPT_DATA(0x0025),
+ POSTSCRIPT_DATA(0x0025, WmfEscapeUnknownData::new),
/** Notifies an output device to ignore PostScript data. */
- POSTSCRIPT_IGNORE(0x0026),
+ POSTSCRIPT_IGNORE(0x0026, WmfEscapeUnknownData::new),
/** Gets the device units currently configured on an output device. */
- GETDEVICEUNITS(0x002A),
+ GETDEVICEUNITS(0x002A, WmfEscapeUnknownData::new),
/** Gets extended text metrics currently configured on an output device. */
- GETEXTENDEDTEXTMETRICS(0x0100),
+ GETEXTENDEDTEXTMETRICS(0x0100, WmfEscapeUnknownData::new),
/** Gets the font kern table currently defined on an output device. */
- GETPAIRKERNTABLE(0x0102),
+ GETPAIRKERNTABLE(0x0102, WmfEscapeUnknownData::new),
/** Draws text using the currently selected font, background color, and text color. */
- EXTTEXTOUT(0x0200),
+ EXTTEXTOUT(0x0200, WmfEscapeUnknownData::new),
/** Gets the font face name currently configured on a device. */
- GETFACENAME(0x0201),
+ GETFACENAME(0x0201, WmfEscapeUnknownData::new),
/** Sets the font face name on a device. */
- DOWNLOADFACE(0x0202),
+ DOWNLOADFACE(0x0202, WmfEscapeUnknownData::new),
/** Queries a printer driver about the support for metafiles on an output device. */
- METAFILE_DRIVER(0x0801),
+ METAFILE_DRIVER(0x0801, WmfEscapeUnknownData::new),
/** Queries the printer driver about its support for DIBs on an output device. */
- QUERYDIBSUPPORT(0x0C01),
+ QUERYDIBSUPPORT(0x0C01, WmfEscapeUnknownData::new),
/** Opens a path. */
- BEGIN_PATH(0x1000),
+ BEGIN_PATH(0x1000, WmfEscapeUnknownData::new),
/** Defines a clip region that is bounded by a path. The input MUST be a 16-bit quantity that defines the action to take. */
- CLIP_TO_PATH(0x1001),
+ CLIP_TO_PATH(0x1001, WmfEscapeUnknownData::new),
/** Ends a path. */
- END_PATH(0x1002),
+ END_PATH(0x1002, WmfEscapeUnknownData::new),
/** The same as STARTDOC specified with a NULL document and output filename, data in raw mode, and a type of zero. */
- OPEN_CHANNEL(0x100E),
+ OPEN_CHANNEL(0x100E, WmfEscapeUnknownData::new),
/** Instructs the printer driver to download sets of PostScript procedures. */
- DOWNLOADHEADER(0x100F),
+ DOWNLOADHEADER(0x100F, WmfEscapeUnknownData::new),
/** The same as ENDDOC. See OPEN_CHANNEL. */
- CLOSE_CHANNEL(0x1010),
+ CLOSE_CHANNEL(0x1010, WmfEscapeUnknownData::new),
/** Sends arbitrary data directly to a printer driver, which is expected to process this data only when in PostScript mode. */
- POSTSCRIPT_PASSTHROUGH(0x1013),
+ POSTSCRIPT_PASSTHROUGH(0x1013, WmfEscapeUnknownData::new),
/** Sends arbitrary data directly to the printer driver. */
- ENCAPSULATED_POSTSCRIPT(0x1014),
+ ENCAPSULATED_POSTSCRIPT(0x1014, WmfEscapeUnknownData::new),
/** Sets the printer driver to either PostScript or GDI mode. */
- POSTSCRIPT_IDENTIFY(0x1015),
+ POSTSCRIPT_IDENTIFY(0x1015, WmfEscapeUnknownData::new),
/** Inserts a block of raw data into a PostScript stream. The input MUST be
a 32-bit quantity specifying the number of bytes to inject, a 16-bit quantity specifying the
injection point, and a 16-bit quantity specifying the page number, followed by the bytes to
inject. */
- POSTSCRIPT_INJECTION(0x1016),
+ POSTSCRIPT_INJECTION(0x1016, WmfEscapeUnknownData::new),
/** Checks whether the printer supports a JPEG image. */
- CHECKJPEGFORMAT(0x1017),
+ CHECKJPEGFORMAT(0x1017, WmfEscapeUnknownData::new),
/** Checks whether the printer supports a PNG image */
- CHECKPNGFORMAT(0x1018),
+ CHECKPNGFORMAT(0x1018, WmfEscapeUnknownData::new),
/** Gets information on a specified feature setting for a PostScript printer driver. */
- GET_PS_FEATURESETTING(0x1019),
+ GET_PS_FEATURESETTING(0x1019, WmfEscapeUnknownData::new),
/** Enables applications to write documents to a file or to a printer in XML Paper Specification (XPS) format. */
- MXDC_ESCAPE(0x101A),
+ MXDC_ESCAPE(0x101A, WmfEscapeUnknownData::new),
/** Enables applications to include private procedures and other arbitrary data in documents. */
- SPCLPASSTHROUGH2(0x11D8);
+ SPCLPASSTHROUGH2(0x11D8, WmfEscapeUnknownData::new);
- int flag;
- EscapeFunction(int flag) {
+ public int flag;
+ public final Supplier<? extends HwmfEscape.HwmfEscapeData> constructor;
+
+
+ EscapeFunction(int flag, Supplier<? extends HwmfEscape.HwmfEscapeData> constructor) {
this.flag = flag;
+ this.constructor = constructor;
}
static EscapeFunction valueOf(int flag) {
@@ -169,21 +177,18 @@ public class HwmfEscape implements HwmfR
}
}
+ public interface HwmfEscapeData {
+ public int init(LittleEndianInputStream leis, long recordSize, EscapeFunction escapeFunction) throws IOException;
+ }
+
+
/**
* A 16-bit unsigned integer that defines the escape function. The
* value MUST be from the MetafileEscapes enumeration.
*/
private EscapeFunction escapeFunction;
- /**
- * A 16-bit unsigned integer that specifies the size, in bytes, of the
- * EscapeData field.
- */
- private int byteCount;
- /**
- * An array of bytes of size ByteCount.
- */
- private byte[] escapeData;
-
+ private HwmfEscapeData escapeData;
+
@Override
public HwmfRecordType getWmfRecordType() {
return HwmfRecordType.escape;
@@ -192,10 +197,22 @@ public class HwmfEscape implements HwmfR
@Override
public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
escapeFunction = EscapeFunction.valueOf(leis.readUShort());
- byteCount = leis.readUShort();
- escapeData = IOUtils.toByteArray(leis,byteCount);
+ // A 16-bit unsigned integer that specifies the size, in bytes, of the EscapeData field.
+ int byteCount = leis.readUShort();
+ int size = 2*LittleEndianConsts.SHORT_SIZE;
+
+ escapeData = escapeFunction.constructor.get();
+ size += escapeData.init(leis, byteCount, escapeFunction);
+
+ return size;
+ }
+
+ public EscapeFunction getEscapeFunction() {
+ return escapeFunction;
+ }
- return 2*LittleEndianConsts.SHORT_SIZE+byteCount;
+ public <T extends HwmfEscapeData> T getEscapeData() {
+ return (T)escapeData;
}
@Override
@@ -206,7 +223,126 @@ public class HwmfEscape implements HwmfR
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("escape - function: "+escapeFunction+"\n");
- sb.append(HexDump.dump(escapeData, 0, 0));
+ sb.append(escapeData.toString());
return sb.toString();
}
+
+ public static class WmfEscapeUnknownData implements HwmfEscapeData {
+ EscapeFunction escapeFunction;
+ private byte[] escapeDataBytes;
+
+ public byte[] getEscapeDataBytes() {
+ return escapeDataBytes;
+ }
+
+ @Override
+ public int init(LittleEndianInputStream leis, long recordSize, EscapeFunction escapeFunction) throws IOException {
+ this.escapeFunction = escapeFunction;
+ escapeDataBytes = IOUtils.toByteArray(leis,recordSize,MAX_OBJECT_SIZE);
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return HexDump.dump(escapeDataBytes, 0, 0);
+ }
+ }
+
+ public static class WmfEscapeEMF implements HwmfEscapeData {
+ // The magic for EMF parts, i.e. the byte sequence for "WMFC"
+ private static final int EMF_COMMENT_IDENTIFIER = 0x43464D57;
+
+ int commentIdentifier;
+ int commentType;
+ int version;
+ int checksum;
+ int flags;
+ int commentRecordCount;
+ int currentRecordSize;
+ int remainingBytes;
+ int emfRecordSize;
+ byte[] emfData;
+
+
+ @Override
+ public int init(LittleEndianInputStream leis, long recordSize, EscapeFunction escapeFunction) throws IOException {
+ if (recordSize < LittleEndianConsts.INT_SIZE) {
+ return 0;
+ }
+
+ // A 32-bit unsigned integer that defines this record as a WMF Comment record.
+ int commentIdentifier = leis.readInt();
+
+ if (commentIdentifier != EMF_COMMENT_IDENTIFIER) {
+ // there are some WMF implementation using this record as a MFCOMMENT or similar
+ // if the commentIdentifier doesn't match, then return immediately
+ return LittleEndianConsts.INT_SIZE;
+ }
+
+ // A 32-bit unsigned integer that identifies the type of comment in this record.
+ // This value MUST be 0x00000001.
+ commentType = leis.readInt();
+ assert(commentType == 0x00000001);
+
+ // A 32-bit unsigned integer that specifies EMF metafile interoperability. This SHOULD be 0x00010000.
+ version = leis.readInt();
+
+ // A 16-bit unsigned integer used to validate the correctness of the embedded EMF stream.
+ // This value MUST be the one's-complement of the result of applying an XOR operation to all WORDs in the EMF stream.
+ checksum = leis.readUShort();
+
+ // This 32-bit unsigned integer is unused and MUST be set to zero.
+ flags = leis.readInt();
+ assert(flags == 0);
+
+ // A 32-bit unsigned integer that specifies the total number of consecutive META_ESCAPE_ENHANCED_METAFILE
+ // records that contain the embedded EMF metafile.
+ commentRecordCount = leis.readInt();
+
+ // A 32-bit unsigned integer that specifies the size, in bytes, of the EnhancedMetafileData field.
+ // This value MUST be less than or equal to 8,192.
+ currentRecordSize = leis.readInt();
+ assert(0 <= currentRecordSize && currentRecordSize <= 0x2000);
+
+ // A 32-bit unsigned integer that specifies the number of bytes in the EMF stream that remain to be
+ // processed after this record. Those additional EMF bytes MUST follow in the EnhancedMetafileData
+ // fields of subsequent META_ESCAPE_ENHANDED_METAFILE escape records.
+ remainingBytes = leis.readInt();
+
+ // A 32-bit unsigned integer that specifies the total size of the EMF stream embedded in this
+ // sequence of META_ESCAPE_ENHANCED_METAFILE records.
+ emfRecordSize = leis.readInt();
+
+
+ // A segment of an EMF file. The bytes in consecutive META_ESCAPE_ENHANCED_METAFILE records
+ // MUST be concatenated to represent the entire embedded EMF file.
+ emfData = IOUtils.toByteArray(leis, currentRecordSize, MAX_OBJECT_SIZE);
+
+ return LittleEndianConsts.INT_SIZE*8+ LittleEndianConsts.SHORT_SIZE+emfData.length;
+ }
+
+ public boolean isValid() {
+ return commentIdentifier == EMF_COMMENT_IDENTIFIER;
+ }
+
+ public int getCommentRecordCount() {
+ return commentRecordCount;
+ }
+
+ public int getCurrentRecordSize() {
+ return currentRecordSize;
+ }
+
+ public int getRemainingBytes() {
+ return remainingBytes;
+ }
+
+ public int getEmfRecordSize() {
+ return emfRecordSize;
+ }
+
+ public byte[] getEmfData() {
+ return emfData;
+ }
+ }
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java Sat May 4 23:01:53 2019
@@ -20,6 +20,7 @@ package org.apache.poi.hwmf.record;
import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
import static org.apache.poi.hwmf.record.HwmfDraw.readPointS;
+import java.awt.Color;
import java.awt.Shape;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
@@ -29,6 +30,7 @@ import java.io.IOException;
import org.apache.poi.hwmf.draw.HwmfDrawProperties;
import org.apache.poi.hwmf.draw.HwmfGraphics;
+import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
@@ -37,7 +39,29 @@ public class HwmfFill {
* A record which contains an image (to be extracted)
*/
public interface HwmfImageRecord {
- BufferedImage getImage();
+
+ default BufferedImage getImage() {
+ return getImage(Color.BLACK, new Color(0x00FFFFFF, true), true);
+ }
+
+ /**
+ * Provide an image using the fore-/background color, in case of a 1-bit pattern
+ * @param foreground the foreground color
+ * @param background the background color
+ * @param hasAlpha if true, the background color is rendered transparent - see {@link HwmfMisc.WmfSetBkMode.HwmfBkMode}
+ * @return the image
+ *
+ * @since POI 4.1.1
+ */
+ BufferedImage getImage(Color foreground, Color background, boolean hasAlpha);
+
+ /**
+ * @return the raw BMP data
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/BMP_file_format">BMP format</a>
+ * @since POI 4.1.1
+ */
+ byte[] getBMPData();
}
/**
@@ -497,7 +521,9 @@ public class HwmfFill {
HwmfDrawProperties prop = ctx.getProperties();
prop.setRasterOp(rasterOperation);
if (bitmap.isValid()) {
- ctx.drawImage(getImage(), srcBounds, dstBounds);
+ BufferedImage bi = bitmap.getImage(prop.getPenColor().getColor(), prop.getBackgroundColor().getColor(),
+ prop.getBkMode() == HwmfBkMode.TRANSPARENT);
+ ctx.drawImage(bi, srcBounds, dstBounds);
} else if (!dstBounds.isEmpty()) {
BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
ctx.drawImage(bi, new Rectangle2D.Double(0,0,100,100), dstBounds);
@@ -505,8 +531,17 @@ public class HwmfFill {
}
@Override
- public BufferedImage getImage() {
- return bitmap.getImage();
+ public BufferedImage getImage(Color foreground, Color background, boolean hasAlpha) {
+ return bitmap.getImage(foreground,background,hasAlpha);
+ }
+
+ public HwmfBitmapDib getBitmap() {
+ return bitmap;
+ }
+
+ @Override
+ public byte[] getBMPData() {
+ return bitmap.getBMPData();
}
@Override
@@ -631,8 +666,13 @@ public class HwmfFill {
}
@Override
- public BufferedImage getImage() {
- return dib.getImage();
+ public BufferedImage getImage(Color foreground, Color background, boolean hasAlpha) {
+ return dib.getImage(foreground,background,hasAlpha);
+ }
+
+ @Override
+ public byte[] getBMPData() {
+ return dib.getBMPData();
}
}
@@ -738,8 +778,13 @@ public class HwmfFill {
}
@Override
- public BufferedImage getImage() {
- return (target != null && target.isValid()) ? target.getImage() : null;
+ public BufferedImage getImage(Color foreground, Color background, boolean hasAlpha) {
+ return (target != null && target.isValid()) ? target.getImage(foreground,background,hasAlpha) : null;
+ }
+
+ @Override
+ public byte[] getBMPData() {
+ return (target != null && target.isValid()) ? target.getBMPData() : null;
}
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java Sat May 4 23:01:53 2019
@@ -17,6 +17,7 @@
package org.apache.poi.hwmf.record;
+import java.awt.Color;
import java.awt.geom.Dimension2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
@@ -25,6 +26,7 @@ import org.apache.poi.hwmf.draw.HwmfDraw
import org.apache.poi.hwmf.draw.HwmfGraphics;
import org.apache.poi.hwmf.record.HwmfFill.ColorUsage;
import org.apache.poi.hwmf.record.HwmfFill.HwmfImageRecord;
+import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
import org.apache.poi.util.Dimension2DDouble;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInputStream;
@@ -459,18 +461,30 @@ public class HwmfMisc {
}
HwmfDrawProperties prop = ctx.getProperties();
prop.setBrushStyle(style);
- prop.setBrushBitmap(getImage());
+ prop.setBrushBitmap(getImage(prop.getBrushColor().getColor(), prop.getBackgroundColor().getColor(),
+ prop.getBkMode() == HwmfBkMode.TRANSPARENT));
}
@Override
- public BufferedImage getImage() {
+ public BufferedImage getImage(Color foreground, Color background, boolean hasAlpha) {
if (patternDib != null && patternDib.isValid()) {
- return patternDib.getImage();
+ return patternDib.getImage(foreground, background, hasAlpha);
} else if (pattern16 != null) {
return pattern16.getImage();
} else {
return null;
}
+ }
+
+ @Override
+ public byte[] getBMPData() {
+ if (patternDib != null && patternDib.isValid()) {
+ return patternDib.getBMPData();
+ } else if (pattern16 != null) {
+ return null;
+ } else {
+ return null;
+ }
}
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPlaceableHeader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPlaceableHeader.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPlaceableHeader.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPlaceableHeader.java Sat May 4 23:01:53 2019
@@ -66,7 +66,13 @@ public class HwmfPlaceableHeader {
* This value can be used to determine whether the metafile has become corrupted.
*/
leis.readShort();
-
+
+ // sometimes the placeable header is filled/aligned to dwords.
+ // check for padding 0 bytes.
+ leis.mark(LittleEndianConsts.INT_SIZE);
+ if (leis.readShort() != 0) {
+ leis.reset();
+ }
}
public static HwmfPlaceableHeader readHeader(LittleEndianInputStream leis) throws IOException {
Added: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbedded.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbedded.java?rev=1858625&view=auto
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbedded.java (added)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbedded.java Sat May 4 23:01:53 2019
@@ -0,0 +1,49 @@
+/* ====================================================================
+ 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.hwmf.usermodel;
+
+import org.apache.poi.util.Beta;
+
+/**
+ * An embedded resource - this class hides the logic of chained emf+ object records and other internals.
+ * Consider its API as unstable for now, i.e. there's no guarantee for backward compatibility
+ */
+@Beta
+public class HwmfEmbedded {
+
+ private HwmfEmbeddedType embeddedType;
+ private byte[] data;
+
+ public HwmfEmbeddedType getEmbeddedType() {
+ return embeddedType;
+ }
+
+ public byte[] getRawData() {
+ return data;
+ }
+
+ public void setEmbeddedType(HwmfEmbeddedType embeddedType) {
+ this.embeddedType = embeddedType;
+ }
+
+ public void setData(byte[] data) {
+ this.data = data;
+ }
+}
+
+
Propchange: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbedded.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbeddedIterator.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbeddedIterator.java?rev=1858625&view=auto
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbeddedIterator.java (added)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbeddedIterator.java Sat May 4 23:01:53 2019
@@ -0,0 +1,140 @@
+/* ====================================================================
+ 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.hwmf.usermodel;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Iterator;
+
+import org.apache.poi.hwmf.record.HwmfEscape;
+import org.apache.poi.hwmf.record.HwmfEscape.EscapeFunction;
+import org.apache.poi.hwmf.record.HwmfEscape.WmfEscapeEMF;
+import org.apache.poi.hwmf.record.HwmfFill.HwmfImageRecord;
+import org.apache.poi.hwmf.record.HwmfRecord;
+
+public class HwmfEmbeddedIterator implements Iterator<HwmfEmbedded> {
+
+ private final Deque<Iterator<?>> iterStack = new ArrayDeque<>();
+ private Object current;
+
+ public HwmfEmbeddedIterator(HwmfPicture wmf) {
+ this(wmf.getRecords().iterator());
+ }
+
+ public HwmfEmbeddedIterator(Iterator<HwmfRecord> recordIterator) {
+ iterStack.add(recordIterator);
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (iterStack.isEmpty()) {
+ return false;
+ }
+
+ if (current != null) {
+ // don't search twice and potentially skip items
+ return true;
+ }
+
+ Iterator<?> iter;
+ do {
+ iter = iterStack.peek();
+ while (iter.hasNext()) {
+ Object obj = iter.next();
+ if (obj instanceof HwmfImageRecord) {
+ current = obj;
+ return true;
+ }
+ if (obj instanceof HwmfEscape && ((HwmfEscape)obj).getEscapeFunction() == EscapeFunction.META_ESCAPE_ENHANCED_METAFILE) {
+ WmfEscapeEMF emfData = ((HwmfEscape)obj).getEscapeData();
+ if (emfData.isValid()) {
+ current = obj;
+ return true;
+ }
+ }
+ }
+ iterStack.pop();
+ } while (!iterStack.isEmpty());
+
+ return false;
+ }
+
+ @Override
+ public HwmfEmbedded next() {
+ HwmfEmbedded emb;
+ if ((emb = checkHwmfImageRecord()) != null) {
+ return emb;
+ }
+ if ((emb = checkHwmfEscapeRecord()) != null) {
+ return emb;
+ }
+ return null;
+ }
+
+ private HwmfEmbedded checkHwmfImageRecord() {
+ if (!(current instanceof HwmfImageRecord)) {
+ return null;
+ }
+
+ HwmfImageRecord hir = (HwmfImageRecord)current;
+ current = null;
+
+ HwmfEmbedded emb = new HwmfEmbedded();
+ emb.setEmbeddedType(HwmfEmbeddedType.BMP);
+ emb.setData(hir.getBMPData());
+
+ return emb;
+ }
+
+
+ private HwmfEmbedded checkHwmfEscapeRecord() {
+ if (!(current instanceof HwmfEscape)) {
+ return null;
+ }
+ final HwmfEscape esc = (HwmfEscape)current;
+ assert(esc.getEscapeFunction() == EscapeFunction.META_ESCAPE_ENHANCED_METAFILE);
+
+ WmfEscapeEMF img = esc.getEscapeData();
+ assert(img.isValid());
+ current = null;
+
+ final HwmfEmbedded emb = new HwmfEmbedded();
+ emb.setEmbeddedType(HwmfEmbeddedType.EMF);
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try {
+ for (;;) {
+ bos.write(img.getEmfData());
+
+ current = null;
+ if (img.getRemainingBytes() > 0 && hasNext() && (current instanceof HwmfEscape)) {
+ img = ((HwmfEscape)current).getEscapeData();
+ } else {
+ return emb;
+ }
+ }
+ } catch (IOException e) {
+ // ByteArrayOutputStream doesn't throw IOException
+ return null;
+ } finally {
+ emb.setData(bos.toByteArray());
+ }
+ }
+}
Propchange: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbeddedIterator.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbeddedType.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbeddedType.java?rev=1858625&view=auto
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbeddedType.java (added)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbeddedType.java Sat May 4 23:01:53 2019
@@ -0,0 +1,37 @@
+/* ====================================================================
+ 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.hwmf.usermodel;
+
+public enum HwmfEmbeddedType {
+ BITMAP(".bitmap"),
+ WMF(".wmf"),
+ EMF(".emf"),
+ EPS(".eps"),
+ JPEG(".jpg"),
+ GIF(".gif"),
+ TIFF(".tiff"),
+ PNG(".png"),
+ BMP(".bmp"),
+ UNKNOWN(".dat");
+
+ public final String extension;
+
+ HwmfEmbeddedType(String extension) {
+ this.extension = extension;
+ }
+}
Propchange: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfEmbeddedType.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java Sat May 4 23:01:53 2019
@@ -127,8 +127,10 @@ public class HwmfPicture {
ctx.scale(graphicsBounds.getWidth()/wmfBounds.getWidth(), graphicsBounds.getHeight()/wmfBounds.getHeight());
HwmfGraphics g = new HwmfGraphics(ctx, wmfBounds);
+ int idx = 0;
for (HwmfRecord r : records) {
r.draw(g);
+ idx++;
}
} finally {
ctx.setTransform(at);
@@ -184,4 +186,8 @@ public class HwmfPicture {
double coeff = Units.POINT_DPI/inch;
return new Dimension((int)Math.round(bounds.getWidth()*coeff), (int)Math.round(bounds.getHeight()*coeff));
}
+
+ public Iterable<HwmfEmbedded> getEmbeddings() {
+ return () -> new HwmfEmbeddedIterator(HwmfPicture.this);
+ }
}
Modified: poi/trunk/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java (original)
+++ poi/trunk/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java Sat May 4 23:01:53 2019
@@ -35,6 +35,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -51,6 +52,8 @@ import org.apache.poi.hemf.record.emf.He
import org.apache.poi.hemf.record.emf.HemfText;
import org.apache.poi.hwmf.record.HwmfRecord;
import org.apache.poi.hwmf.record.HwmfText;
+import org.apache.poi.hwmf.usermodel.HwmfEmbedded;
+import org.apache.poi.hwmf.usermodel.HwmfEmbeddedType;
import org.apache.poi.hwmf.usermodel.HwmfPicture;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.RecordFormatException;
@@ -73,16 +76,17 @@ public class HemfPictureTest {
// emfs/govdocs1/844/844795.ppt_2.emf
// emfs/commoncrawl2/TO/TOYZSTNUSW5OFCFUQ6T5FBLIDLCRF3NH_0.emf
- final boolean writeLog = true;
+ final boolean writeLog = false;
final boolean dumpRecords = false;
- final boolean savePng = true;
+ final boolean savePng = false;
+ final boolean dumpEmbedded = true;
Set<String> passed = new HashSet<>();
try (BufferedWriter sucWrite = parseEmfLog(passed, "emf-success.txt");
BufferedWriter parseError = parseEmfLog(passed, "emf-parse.txt");
BufferedWriter renderError = parseEmfLog(passed, "emf-render.txt");
- SevenZFile sevenZFile = new SevenZFile(new File("tmp/render_emf.7z"))) {
+ SevenZFile sevenZFile = new SevenZFile(new File("tmp/plus_emf.7z"))) {
for (int idx=0;;idx++) {
SevenZArchiveEntry entry = sevenZFile.getNextEntry();
if (entry == null) break;
@@ -90,6 +94,11 @@ public class HemfPictureTest {
if (entry.isDirectory() || !etName.endsWith(".emf") || passed.contains(etName)) continue;
+ if (!etName.equals("emfs/commoncrawl2/2S/2SYMYPLNJURGCXJKLNZCJQGIBHVMQTRS_0.emf")) continue;
+
+ // emfs/commoncrawl2/ZJ/ZJT2BZPLQR7DKSKYLYL6GRDEUM2KIO5F_4.emf
+ // emfs/govdocs1/005/005203.ppt_3.emf
+
System.out.println(etName);
int size = sevenZFile.read(buf);
@@ -116,6 +125,18 @@ public class HemfPictureTest {
dumpRecords(emf);
}
+ if (dumpEmbedded) {
+ int embIdx = 0;
+ for (HwmfEmbedded emb : emf.getEmbeddings()) {
+ final File embName = new File("build/tmp", "emb_"+etName.replaceFirst(".+/", "").replace(".emf", "_"+embIdx + emb.getEmbeddedType().extension) );
+// try (FileOutputStream fos = new FileOutputStream(embName)) {
+// fos.write(emb.getRawData());
+// }
+ embIdx++;
+ }
+ }
+
+
Graphics2D g = null;
try {
Dimension2D dim = emf.getSize();
@@ -194,7 +215,7 @@ public class HemfPictureTest {
if (Files.exists(log)) {
soo = StandardOpenOption.APPEND;
try (Stream<String> stream = Files.lines(log)) {
- stream.forEach((s) -> passed.add(s.split("\\s")[0]));
+ stream.filter(s -> !s.startsWith("#")).forEach((s) -> passed.add(s.split("\\s")[0]));
}
} else {
soo = StandardOpenOption.CREATE;
@@ -380,7 +401,28 @@ public class HemfPictureTest {
}
}
- /*
- govdocs1 064213.doc-0.emf contains an example of extextouta
- */
+ @Test
+ public void nestedWmfEmf() throws Exception {
+ try (InputStream is = sl_samples.openResourceAsStream("nested_wmf.emf")) {
+ HemfPicture emf1 = new HemfPicture(is);
+ List<HwmfEmbedded> embeds = new ArrayList<>();
+ emf1.getEmbeddings().forEach(embeds::add);
+ assertEquals(1, embeds.size());
+ assertEquals(HwmfEmbeddedType.WMF, embeds.get(0).getEmbeddedType());
+
+ HwmfPicture wmf = new HwmfPicture(new ByteArrayInputStream(embeds.get(0).getRawData()));
+ embeds.clear();
+ wmf.getEmbeddings().forEach(embeds::add);
+ assertEquals(3, embeds.size());
+ assertEquals(HwmfEmbeddedType.EMF, embeds.get(0).getEmbeddedType());
+
+ HemfPicture emf2 = new HemfPicture(new ByteArrayInputStream(embeds.get(0).getRawData()));
+ embeds.clear();
+ emf2.getEmbeddings().forEach(embeds::add);
+ assertTrue(embeds.isEmpty());
+ }
+ }
+
+
+ /* govdocs1 064213.doc-0.emf contains an example of extextouta */
}
\ No newline at end of file
Modified: poi/trunk/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java?rev=1858625&r1=1858624&r2=1858625&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java (original)
+++ poi/trunk/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java Sat May 4 23:01:53 2019
@@ -47,6 +47,7 @@ import org.apache.poi.hwmf.record.HwmfFo
import org.apache.poi.hwmf.record.HwmfRecord;
import org.apache.poi.hwmf.record.HwmfRecordType;
import org.apache.poi.hwmf.record.HwmfText;
+import org.apache.poi.hwmf.usermodel.HwmfEmbedded;
import org.apache.poi.hwmf.usermodel.HwmfPicture;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
@@ -82,8 +83,10 @@ public class TestHwmfParsing {
@Test
@Ignore("This is work-in-progress and not a real unit test ...")
public void paint() throws IOException {
- File f = samples.getFile("santa.wmf");
- // File f = new File("bla.wmf");
+ boolean dumpEmbedded = true;
+
+// File f = samples.getFile("santa.wmf");
+ File f = new File("testme.wmf");
FileInputStream fis = new FileInputStream(f);
HwmfPicture wmf = new HwmfPicture(fis);
fis.close();
@@ -92,12 +95,10 @@ public class TestHwmfParsing {
int width = Units.pointsToPixel(dim.getWidth());
// keep aspect ratio for height
int height = Units.pointsToPixel(dim.getHeight());
- double max = Math.max(width, height);
- if (max > 1500) {
- width *= 1500/max;
- height *= 1500/max;
- }
-
+ double scale = (width > height) ? 1500 / width : 1500 / width;
+ width *= scale;
+ height *= scale;
+
BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bufImg.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
@@ -110,6 +111,17 @@ public class TestHwmfParsing {
g.dispose();
ImageIO.write(bufImg, "PNG", new File("bla.png"));
+
+ if (dumpEmbedded) {
+ int embIdx = 0;
+ for (HwmfEmbedded emb : wmf.getEmbeddings()) {
+ final File embName = new File("build/tmp", "emb_"+embIdx + emb.getEmbeddedType().extension);
+ try (FileOutputStream fos = new FileOutputStream(embName)) {
+ fos.write(emb.getRawData());
+ }
+ embIdx++;
+ }
+ }
}
@Test
@@ -190,7 +202,7 @@ public class TestHwmfParsing {
int width = Units.pointsToPixel(dim.getWidth());
// keep aspect ratio for height
int height = Units.pointsToPixel(dim.getHeight());
-
+
BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bufImg.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Added: poi/trunk/test-data/slideshow/nested_wmf.emf
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/slideshow/nested_wmf.emf?rev=1858625&view=auto
==============================================================================
Binary file - no diff available.
Propchange: poi/trunk/test-data/slideshow/nested_wmf.emf
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org