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 2018/09/14 21:37:38 UTC

svn commit: r1840956 [1/5] - in /poi/branches/hemf/src: java/org/apache/poi/util/ ooxml/java/org/apache/poi/xdgf/geom/ ooxml/java/org/apache/poi/xdgf/usermodel/ ooxml/java/org/apache/poi/xdgf/util/ scratchpad/src/org/apache/poi/hemf/draw/ scratchpad/sr...

Author: kiwiwings
Date: Fri Sep 14 21:37:37 2018
New Revision: 1840956

URL: http://svn.apache.org/viewvc?rev=1840956&view=rev
Log:
#60656 - Support export file that contains emf and render it correctly

Added:
    poi/branches/hemf/src/java/org/apache/poi/util/Dimension2DDouble.java
      - copied, changed from r1840955, poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/geom/Dimension2dDouble.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java   (with props)
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java   (with props)
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java
      - copied, changed from r1840955, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfComment.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java   (with props)
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java   (with props)
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java   (with props)
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfHeader.java
      - copied, changed from r1840955, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfHeader.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java   (with props)
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPalette.java   (with props)
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java
      - copied, changed from r1840955, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfRecord.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordIterator.java   (with props)
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecordType.java
      - copied, changed from r1840955, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfRecordType.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.java
      - copied, changed from r1840955, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfText.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java   (with props)
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/UnimplementedHemfRecord.java
      - copied, changed from r1840955, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/UnimplementedHemfRecord.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java
      - copied, changed from r1840955, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/hemfplus/record/HemfPlusHeader.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java
      - copied, changed from r1840955, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/hemfplus/record/HemfPlusRecord.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordIterator.java   (with props)
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecordType.java
      - copied, changed from r1840955, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/hemfplus/record/HemfPlusRecordType.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/UnimplementedHemfPlusRecord.java
      - copied, changed from r1840955, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/hemfplus/record/UnimplementedHemfPlusRecord.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/usermodel/
      - copied from r1840955, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/extractor/
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java
      - copied, changed from r1840385, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/extractor/HemfExtractor.java
    poi/branches/hemf/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/
      - copied from r1840955, poi/branches/hemf/src/scratchpad/testcases/org/apache/poi/hemf/extractor/
    poi/branches/hemf/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfPictureTest.java
      - copied, changed from r1840385, poi/branches/hemf/src/scratchpad/testcases/org/apache/poi/hemf/extractor/HemfExtractorTest.java
Removed:
    poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/geom/Dimension2dDouble.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/extractor/
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/hemfplus/
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/AbstractHemfComment.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfComment.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfCommentEMFPlus.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfCommentEMFSpool.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfCommentPublic.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfCommentRecord.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfHeader.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfRecord.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfRecordType.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfText.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/UnimplementedHemfRecord.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfExtractor.java
    poi/branches/hemf/src/scratchpad/testcases/org/apache/poi/hemf/extractor/
    poi/branches/hemf/src/scratchpad/testcases/org/apache/poi/hemf/usermodel/HemfExtractorTest.java
Modified:
    poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPage.java
    poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/util/VsdxToPng.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfDrawProperties.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBrushStyle.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfEscape.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfHatchStyle.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMapMode.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecord.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecordType.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java
    poi/branches/hemf/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java
    poi/branches/hemf/src/scratchpad/testcases/org/apache/poi/hemf/hemfplus/extractor/HemfPlusExtractorTest.java
    poi/branches/hemf/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java

Copied: poi/branches/hemf/src/java/org/apache/poi/util/Dimension2DDouble.java (from r1840955, poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/geom/Dimension2dDouble.java)
URL: http://svn.apache.org/viewvc/poi/branches/hemf/src/java/org/apache/poi/util/Dimension2DDouble.java?p2=poi/branches/hemf/src/java/org/apache/poi/util/Dimension2DDouble.java&p1=poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/geom/Dimension2dDouble.java&r1=1840955&r2=1840956&rev=1840956&view=diff
==============================================================================
--- poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/geom/Dimension2dDouble.java (original)
+++ poi/branches/hemf/src/java/org/apache/poi/util/Dimension2DDouble.java Fri Sep 14 21:37:37 2018
@@ -15,21 +15,21 @@
    limitations under the License.
 ==================================================================== */
 
-package org.apache.poi.xdgf.geom;
+package org.apache.poi.util;
 
 import java.awt.geom.Dimension2D;
 
-public class Dimension2dDouble extends Dimension2D {
+public class Dimension2DDouble extends Dimension2D {
 
     double width;
     double height;
 
-    public Dimension2dDouble() {
+    public Dimension2DDouble() {
         width = 0d;
         height = 0d;
     }
 
-    public Dimension2dDouble(double width, double height) {
+    public Dimension2DDouble(double width, double height) {
         this.width = width;
         this.height = height;
     }
@@ -52,8 +52,8 @@ public class Dimension2dDouble extends D
 
     @Override
     public boolean equals(Object obj) {
-        if (obj instanceof Dimension2dDouble) {
-            Dimension2dDouble other = (Dimension2dDouble) obj;
+        if (obj instanceof Dimension2DDouble) {
+            Dimension2DDouble other = (Dimension2DDouble) obj;
             return width == other.width && height == other.height;
         }
 
@@ -68,6 +68,6 @@ public class Dimension2dDouble extends D
 
     @Override
     public String toString() {
-        return "Dimension2dDouble[" + width + ", " + height + "]";
+        return "Dimension2DDouble[" + width + ", " + height + "]";
     }
 }

Modified: poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPage.java
URL: http://svn.apache.org/viewvc/poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPage.java?rev=1840956&r1=1840955&r2=1840956&view=diff
==============================================================================
--- poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPage.java (original)
+++ poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPage.java Fri Sep 14 21:37:37 2018
@@ -22,7 +22,7 @@ import java.awt.geom.Rectangle2D;
 
 import org.apache.poi.ooxml.POIXMLException;
 import org.apache.poi.util.Internal;
-import org.apache.poi.xdgf.geom.Dimension2dDouble;
+import org.apache.poi.util.Dimension2DDouble;
 
 import com.microsoft.schemas.office.visio.x2012.main.PageType;
 
@@ -75,14 +75,14 @@ public class XDGFPage {
     /**
      * @return width/height of page
      */
-    public Dimension2dDouble getPageSize() {
+    public Dimension2DDouble getPageSize() {
         XDGFCell w = _pageSheet.getCell("PageWidth");
         XDGFCell h = _pageSheet.getCell("PageHeight");
 
         if (w == null || h == null)
             throw new POIXMLException("Cannot determine page size");
 
-        return new Dimension2dDouble(Double.parseDouble(w.getValue()),
+        return new Dimension2DDouble(Double.parseDouble(w.getValue()),
                 Double.parseDouble(h.getValue()));
     }
 
@@ -109,7 +109,7 @@ public class XDGFPage {
      * @return bounding box of page
      */
     public Rectangle2D getBoundingBox() {
-        Dimension2dDouble sz = getPageSize();
+        Dimension2DDouble sz = getPageSize();
         Point2D.Double offset = getPageOffset();
 
         return new Rectangle2D.Double(-offset.getX(), -offset.getY(),

Modified: poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/util/VsdxToPng.java
URL: http://svn.apache.org/viewvc/poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/util/VsdxToPng.java?rev=1840956&r1=1840955&r2=1840956&view=diff
==============================================================================
--- poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/util/VsdxToPng.java (original)
+++ poi/branches/hemf/src/ooxml/java/org/apache/poi/xdgf/util/VsdxToPng.java Fri Sep 14 21:37:37 2018
@@ -25,7 +25,7 @@ import java.io.*;
 
 import javax.imageio.ImageIO;
 
-import org.apache.poi.xdgf.geom.Dimension2dDouble;
+import org.apache.poi.util.Dimension2DDouble;
 import org.apache.poi.xdgf.usermodel.XDGFPage;
 import org.apache.poi.xdgf.usermodel.XmlVisioDocument;
 import org.apache.poi.xdgf.usermodel.shape.ShapeDebuggerRenderer;
@@ -58,7 +58,7 @@ public class VsdxToPng {
     public static void renderToPng(XDGFPage page, File outFile, double scale,
             ShapeRenderer renderer) throws IOException {
 
-        Dimension2dDouble sz = page.getPageSize();
+        Dimension2DDouble sz = page.getPageSize();
 
         int width = (int) (scale * sz.getWidth());
         int height = (int) (scale * sz.getHeight());

Added: poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java
URL: http://svn.apache.org/viewvc/poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java?rev=1840956&view=auto
==============================================================================
--- poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java (added)
+++ poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java Fri Sep 14 21:37:37 2018
@@ -0,0 +1,30 @@
+/* ====================================================================
+   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.draw;
+
+import org.apache.poi.hwmf.draw.HwmfDrawProperties;
+
+public class HemfDrawProperties extends HwmfDrawProperties {
+
+    public HemfDrawProperties() {
+    }
+
+    public HemfDrawProperties(HemfDrawProperties other) {
+        super(other);
+    }
+}

Propchange: poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/HemfDrawProperties.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java
URL: http://svn.apache.org/viewvc/poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java?rev=1840956&view=auto
==============================================================================
--- poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java (added)
+++ poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java Fri Sep 14 21:37:37 2018
@@ -0,0 +1,29 @@
+/* ====================================================================
+   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.draw;
+
+import java.awt.Graphics2D;
+import java.awt.geom.Rectangle2D;
+
+import org.apache.poi.hwmf.draw.HwmfGraphics;
+
+public class HemfGraphics extends HwmfGraphics {
+    public HemfGraphics(Graphics2D graphicsCtx, Rectangle2D bbox) {
+        super(graphicsCtx,bbox);
+    }
+}

Propchange: poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/draw/HemfGraphics.java
------------------------------------------------------------------------------
    svn:eol-style = native

Copied: poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java (from r1840955, poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfComment.java)
URL: http://svn.apache.org/viewvc/poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java?p2=poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java&p1=poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfComment.java&r1=1840955&r2=1840956&rev=1840956&view=diff
==============================================================================
--- poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/HemfComment.java (original)
+++ poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java Fri Sep 14 21:37:37 2018
@@ -15,17 +15,430 @@
    limitations under the License.
 ==================================================================== */
 
-package org.apache.poi.hemf.record;
+package org.apache.poi.hemf.record.emf;
 
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Supplier;
+
+import org.apache.poi.hemf.record.emfplus.HemfPlusRecord;
+import org.apache.poi.hemf.record.emfplus.HemfPlusRecordIterator;
+import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.Internal;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianInputStream;
+import org.apache.poi.util.RecordFormatException;
 
 /**
  * Contains arbitrary data
  */
 @Internal
-public class HemfComment extends  AbstractHemfComment {
+public class HemfComment {
+    private static final int MAX_RECORD_LENGTH = 1_000_000;
+
+    public enum HemfCommentRecordType {
+        emfGeneric(-1, EmfCommentDataGeneric::new, false),
+        emfSpool(0x00000000, EmfCommentDataGeneric::new, false),
+        emfPlus(0x2B464D45, EmfCommentDataPlus::new, false),
+        emfPublic(0x43494447, null, false),
+        emfBeginGroup(0x00000002, EmfCommentDataBeginGroup::new, true),
+        emfEndGroup(0x00000003, EmfCommentDataEndGroup::new, true),
+        emfMultiFormats(0x40000004, EmfCommentDataMultiformats::new, true),
+        emfWMF(0x80000001, EmfCommentDataWMF::new, true),
+        emfUnicodeString(0x00000040, EmfCommentDataUnicode::new, true),
+        emfUnicodeEnd(0x00000080, EmfCommentDataUnicode::new, true)
+        ;
+
+
+        public final long id;
+        public final Supplier<? extends EmfCommentData> constructor;
+        public final boolean isEmfPublic;
+
+        HemfCommentRecordType(long id, Supplier<? extends EmfCommentData> constructor, boolean isEmfPublic) {
+            this.id = id;
+            this.constructor = constructor;
+            this.isEmfPublic = isEmfPublic;
+        }
+
+        public static HemfCommentRecordType getById(long id, boolean isEmfPublic) {
+            for (HemfCommentRecordType wrt : values()) {
+                if (wrt.id == id && wrt.isEmfPublic == isEmfPublic) return wrt;
+            }
+            return emfGeneric;
+        }
+    }
+
+    public interface EmfCommentData {
+        HemfCommentRecordType getCommentRecordType();
+
+        long init(LittleEndianInputStream leis, long dataSize) throws IOException;
+    }
+
+    public static class EmfComment implements HemfRecord {
+        private EmfCommentData data;
+
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.comment;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            int startIdx = leis.getReadIndex();
+            data = new EmfCommentDataIterator(leis, (int)recordSize, true).next();
+            return leis.getReadIndex()-startIdx;
+        }
+
+        public EmfCommentData getCommentData() {
+            return data;
+        }
+    }
+
+    public static class EmfCommentDataIterator implements Iterator<EmfCommentData> {
+
+        private final LittleEndianInputStream leis;
+        private final int startIdx;
+        private final int limit;
+        private EmfCommentData currentRecord;
+        /** is the caller the EmfComment */
+        private final boolean emfParent;
+
+        public EmfCommentDataIterator(LittleEndianInputStream leis, int limit, boolean emfParent) {
+            this.leis = leis;
+            this.limit = limit;
+            this.emfParent = emfParent;
+            startIdx = leis.getReadIndex();
+            //queue the first non-header record
+            currentRecord = _next();
+        }
+
+        @Override
+        public boolean hasNext() {
+            return currentRecord != null;
+        }
+
+        @Override
+        public EmfCommentData next() {
+            EmfCommentData toReturn = currentRecord;
+            final boolean isEOF = (limit == -1 || leis.getReadIndex() >= startIdx+limit);
+            // (currentRecord instanceof HemfPlusMisc.EmfEof)
+            currentRecord = isEOF ? null : _next();
+            return toReturn;
+        }
+
+        private EmfCommentData _next() {
+            long type, recordSize;
+            if (currentRecord == null && emfParent) {
+                type = HemfRecordType.comment.id;
+                recordSize = limit;
+            } else {
+                // A 32-bit unsigned integer from the RecordType enumeration that identifies this record
+                // as a comment record. This value MUST be 0x00000046.
+                type = leis.readUInt();
+                assert(type == HemfRecordType.comment.id);
+                // A 32-bit unsigned integer that specifies the size in bytes of this record in the
+                // metafile. This value MUST be a multiple of 4 bytes.
+                recordSize = leis.readUInt();
+            }
+
+            // A 32-bit unsigned integer that specifies the size, in bytes, of the CommentIdentifier and
+            // CommentRecordParm fields in the RecordBuffer field that follows.
+            // It MUST NOT include the size of itself or the size of the AlignmentPadding field, if present.
+            long dataSize = leis.readUInt();
+
+            try {
+                leis.mark(2*LittleEndianConsts.INT_SIZE);
+                // An optional, 32-bit unsigned integer that identifies the type of comment record.
+                // See the preceding table for descriptions of these record types.
+                // Valid comment identifier values are listed in the following table.
+                //
+                // If this field contains any other value, the comment record MUST be an EMR_COMMENT record
+                final int commentIdentifier = (int)leis.readUInt();
+                // A 32-bit unsigned integer that identifies the type of public comment record.
+                final int publicCommentIdentifier = (int)leis.readUInt();
+
+                final boolean isEmfPublic = (commentIdentifier == HemfCommentRecordType.emfPublic.id);
+                leis.reset();
+
+                final HemfCommentRecordType commentType = HemfCommentRecordType.getById
+                    (isEmfPublic ? publicCommentIdentifier : commentIdentifier, isEmfPublic);
+                assert(commentType != null);
+                final EmfCommentData record = commentType.constructor.get();
+
+                long readBytes = record.init(leis, dataSize);
+                final int skipBytes = (int)(recordSize-4-readBytes);
+                assert (skipBytes >= 0);
+                leis.skipFully(skipBytes);
+
+                return record;
+            } catch (IOException e) {
+                throw new RecordFormatException(e);
+            }
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException("Remove not supported");
+        }
+
+    }
+
+
+
+    /**
+     * Private data is unknown to EMF; it is meaningful only to applications that know the format of the
+     * data and how to use it. EMR_COMMENT private data records MAY be ignored.
+     */
+    public static class EmfCommentDataGeneric implements EmfCommentData {
+        private byte[] privateData;
+
+        @Override
+        public HemfCommentRecordType getCommentRecordType() {
+            return HemfCommentRecordType.emfGeneric;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long dataSize) throws IOException {
+            privateData = IOUtils.safelyAllocate(dataSize, MAX_RECORD_LENGTH);
+            leis.readFully(privateData);
+            return privateData.length;
+        }
+    }
+
+    /** The EMR_COMMENT_EMFPLUS record contains embedded EMF+ records. */
+    public static class EmfCommentDataPlus implements EmfCommentData {
+        private final List<HemfPlusRecord> records = new ArrayList<>();
+
+        @Override
+        public HemfCommentRecordType getCommentRecordType() {
+            return HemfCommentRecordType.emfPlus;
+        }
+
+        @Override
+        public long init(final LittleEndianInputStream leis, final long dataSize)
+        throws IOException {
+            long startIdx = leis.getReadIndex();
+            int commentIdentifier = leis.readInt();
+            assert (commentIdentifier == HemfCommentRecordType.emfPlus.id);
+            new HemfPlusRecordIterator(leis, (int)dataSize-LittleEndianConsts.INT_SIZE).forEachRemaining(records::add);
+            return leis.getReadIndex()-startIdx;
+        }
+
+        public List<HemfPlusRecord> getRecords() {
+            return Collections.unmodifiableList(records);
+        }
+    }
+
+    public static class EmfCommentDataBeginGroup implements EmfCommentData {
+        private final Rectangle2D bounds = new Rectangle2D.Double();
+        private String description;
+
+        @Override
+        public HemfCommentRecordType getCommentRecordType() {
+            return HemfCommentRecordType.emfBeginGroup;
+        }
+
+        @Override
+        public long init(final LittleEndianInputStream leis, final long dataSize)
+        throws IOException {
+            final int startIdx = leis.getReadIndex();
+            final int commentIdentifier = (int)leis.readUInt();
+            assert(commentIdentifier == HemfCommentRecordType.emfPublic.id);
+            final int publicCommentIdentifier = (int)leis.readUInt();
+            assert(publicCommentIdentifier == HemfCommentRecordType.emfBeginGroup.id);
+            HemfDraw.readRectL(leis, bounds);
+
+            // The number of Unicode characters in the optional description string that follows.
+            int nDescription = (int)leis.readUInt();
+
+            byte[] buf = IOUtils.safelyAllocate(nDescription*2, MAX_RECORD_LENGTH);
+            leis.readFully(buf);
+            description = new String(buf, StandardCharsets.UTF_16LE);
+
+            return leis.getReadIndex()-startIdx;
+        }
+    }
+
+    public static class EmfCommentDataEndGroup implements EmfCommentData {
+        @Override
+        public HemfCommentRecordType getCommentRecordType() {
+            return HemfCommentRecordType.emfEndGroup;
+        }
+
+        @Override
+        public long init(final LittleEndianInputStream leis, final long dataSize)
+        throws IOException {
+            final int startIdx = leis.getReadIndex();
+            final int commentIdentifier = (int)leis.readUInt();
+            assert(commentIdentifier == HemfCommentRecordType.emfPublic.id);
+            final int publicCommentIdentifier = (int)leis.readUInt();
+            assert(publicCommentIdentifier == HemfCommentRecordType.emfEndGroup.id);
+            return leis.getReadIndex()-startIdx;
+        }
+    }
+
+    public static class EmfCommentDataMultiformats implements EmfCommentData {
+        private final Rectangle2D bounds = new Rectangle2D.Double();
+        private final List<EmfCommentDataFormat> formats = new ArrayList<>();
+
+        @Override
+        public HemfCommentRecordType getCommentRecordType() {
+            return HemfCommentRecordType.emfMultiFormats;
+        }
+
+        @Override
+        public long init(final LittleEndianInputStream leis, final long dataSize)
+                throws IOException {
+            final int startIdx = leis.getReadIndex();
+            final int commentIdentifier = (int)leis.readUInt();
+            assert(commentIdentifier == HemfCommentRecordType.emfPublic.id);
+            final int publicCommentIdentifier = (int)leis.readUInt();
+            assert(publicCommentIdentifier == HemfCommentRecordType.emfMultiFormats.id);
+            HemfDraw.readRectL(leis, bounds);
+
+            // A 32-bit unsigned integer that specifies the number of graphics formats contained in this record.
+            int countFormats = (int)leis.readUInt();
+            for (int i=0; i<countFormats; i++) {
+                EmfCommentDataFormat fmt = new EmfCommentDataFormat();
+                long readBytes = fmt.init(leis, dataSize, startIdx);
+                formats.add(fmt);
+                if (readBytes == 0) {
+                    // binary data is appended without DataFormat header
+                    break;
+                }
+            }
+
+            for (EmfCommentDataFormat fmt : formats) {
+                int skip = fmt.offData-(leis.getReadIndex()-startIdx);
+                leis.skipFully(skip);
+                fmt.rawData = new byte[fmt.sizeData];
+                leis.readFully(fmt.rawData);
+            }
+
+            return leis.getReadIndex()-startIdx;
+        }
+
+        public List<EmfCommentDataFormat> getFormats() {
+            return Collections.unmodifiableList(formats);
+        }
+    }
+
+    public enum EmfFormatSignature {
+        ENHMETA_SIGNATURE(0x464D4520),
+        EPS_SIGNATURE(0x46535045);
+
+        int id;
+
+        EmfFormatSignature(int flag) {
+            this.id = id;
+        }
+
+        public static EmfFormatSignature getById(int id) {
+            for (EmfFormatSignature wrt : values()) {
+                if (wrt.id == id) return wrt;
+            }
+            return null;
+        }
+
+    }
+
+    public static class EmfCommentDataFormat {
+        private EmfFormatSignature signature;
+        private int version;
+        private int sizeData;
+        private int offData;
+        private byte[] rawData;
+
+        public long init(final LittleEndianInputStream leis, final long dataSize, long startIdx) throws IOException {
+            // A 32-bit unsigned integer that specifies the format of the image data.
+            signature = EmfFormatSignature.getById(leis.readInt());
+
+            // A 32-bit unsigned integer that specifies the format version number.
+            // If the Signature field specifies encapsulated PostScript (EPS), this value MUST be 0x00000001;
+            // otherwise, this value MUST be ignored.
+            version = leis.readInt();
+
+            // A 32-bit unsigned integer that specifies the size of the data in bytes.
+            sizeData = leis.readInt();
+
+            // A 32-bit unsigned integer that specifies the offset to the data from the start
+            // of the identifier field in an EMR_COMMENT_PUBLIC record. The offset MUST be 32-bit aligned.
+            offData = leis.readInt();
+            if (sizeData < 0) {
+                throw new RecordFormatException("size for emrformat must be > 0");
+            }
+            if (offData < 0) {
+                throw new RecordFormatException("offset for emrformat must be > 0");
+            }
+
+            return 4*LittleEndianConsts.INT_SIZE;
+        }
+
+        public byte[] getRawData() {
+            return rawData;
+        }
+    }
+
+    public static class EmfCommentDataWMF implements EmfCommentData {
+        private final Rectangle2D bounds = new Rectangle2D.Double();
+        private final List<EmfCommentDataFormat> formats = new ArrayList<>();
+
+        @Override
+        public HemfCommentRecordType getCommentRecordType() {
+            return HemfCommentRecordType.emfWMF;
+        }
+
+        @Override
+        public long init(final LittleEndianInputStream leis, final long dataSize)
+                throws IOException {
+            final int startIdx = leis.getReadIndex();
+            final int commentIdentifier = (int)leis.readUInt();
+            assert(commentIdentifier == HemfCommentRecordType.emfPublic.id);
+            final int publicCommentIdentifier = (int)leis.readUInt();
+            assert(publicCommentIdentifier == HemfCommentRecordType.emfWMF.id);
+
+            // A 16-bit unsigned integer that specifies the WMF metafile version in terms
+            //of support for device-independent bitmaps (DIBs)
+            int version = leis.readUShort();
+
+            // A 16-bit value that MUST be 0x0000 and MUST be ignored.
+            leis.skipFully(LittleEndianConsts.SHORT_SIZE);
+
+            // A 32-bit unsigned integer that specifies the checksum for this record.
+            int checksum = leis.readInt();
+
+            // A 32-bit value that MUST be 0x00000000 and MUST be ignored.
+            int flags = leis.readInt();
+
+            // A 32-bit unsigned integer that specifies the size, in bytes, of the
+            // WMF metafile in the WinMetafile field.
+            int winMetafileSize = (int)leis.readUInt();
+
+            byte[] winMetafile = IOUtils.safelyAllocate(winMetafileSize, MAX_RECORD_LENGTH);
+            leis.readFully(winMetafile);
+
+            return leis.getReadIndex()-startIdx;
+        }
+    }
+
+    public static class EmfCommentDataUnicode implements EmfCommentData {
+        private final Rectangle2D bounds = new Rectangle2D.Double();
+        private final List<EmfCommentDataFormat> formats = new ArrayList<>();
+
+        @Override
+        public HemfCommentRecordType getCommentRecordType() {
+            return HemfCommentRecordType.emfUnicodeString;
+        }
 
-    public HemfComment(byte[] rawBytes) {
-        super(rawBytes);
+        @Override
+        public long init(final LittleEndianInputStream leis, final long dataSize)
+                throws IOException {
+            throw new RecordFormatException("UNICODE_STRING/UNICODE_END values are reserved in CommentPublic records");
+        }
     }
 }

Added: poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java
URL: http://svn.apache.org/viewvc/poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java?rev=1840956&view=auto
==============================================================================
--- poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java (added)
+++ poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java Fri Sep 14 21:37:37 2018
@@ -0,0 +1,783 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hemf.record.emf;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+
+import org.apache.poi.hemf.draw.HemfGraphics;
+import org.apache.poi.hwmf.draw.HwmfGraphics;
+import org.apache.poi.hwmf.record.HwmfDraw;
+import org.apache.poi.hwmf.record.HwmfDraw.WmfSelectObject;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianInputStream;
+
+public class HemfDraw {
+    /**
+     * The EMR_SELECTOBJECT record adds a graphics object to the current metafile playback device
+     * context. The object is specified either by its index in the EMF Object Table or by its
+     * value from the StockObject enumeration.
+     */
+    public static class EmfSelectObject extends WmfSelectObject implements HemfRecord {
+
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.selectObject;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            // A 32-bit unsigned integer that specifies either the index of a graphics object in the
+            // EMF Object Table or the index of a stock object from the StockObject enumeration.
+            objectIndex = leis.readInt();
+            return LittleEndianConsts.INT_SIZE;
+        }
+    }
+
+
+    /** The EMR_POLYBEZIER record specifies one or more Bezier curves. */
+    public static class EmfPolyBezier extends HwmfDraw.WmfPolygon implements HemfRecord {
+        private final Rectangle2D bounds = new Rectangle2D.Double();
+
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polyBezier;
+        }
+
+        protected long readPoint(LittleEndianInputStream leis, Point2D point) {
+            return readPointL(leis, point);
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            long size = readRectL(leis, bounds);
+
+            /* A 32-bit unsigned integer that specifies the number of points in the points
+             * array. This value MUST be one more than three times the number of curves to
+             * be drawn, because each Bezier curve requires two control points and an
+             * endpoint, and the initial curve requires an additional starting point.
+             *
+             * Line width | Device supports wideline | Maximum points allowed
+             *    1       |            n/a           |          16K
+             *   > 1      |            yes           |          16K
+             *   > 1      |             no           |         1360
+             *
+             * Any extra points MUST be ignored.
+             */
+            final int count = (int)leis.readUInt();
+            final int points = Math.min(count, 16384);
+            size += LittleEndianConsts.INT_SIZE;
+
+            poly.reset();
+
+            /* Cubic Bezier curves are defined using the endpoints and control points
+             * specified by the points field. The first curve is drawn from the first
+             * point to the fourth point, using the second and third points as control
+             * points. Each subsequent curve in the sequence needs exactly three more points:
+             * the ending point of the previous curve is used as the starting point,
+             * the next two points in the sequence are control points,
+             * and the third is the ending point.
+             * The cubic Bezier curves SHOULD be drawn using the current pen.
+             */
+
+            Point2D pnt[] = { new Point2D.Double(), new Point2D.Double(), new Point2D.Double() };
+
+            // points-1 because of the first point
+            final int pointCnt = hasStartPoint() ? points-1 : points;
+            for (int i=0; i+3<pointCnt; i+=3) {
+                // x (4 bytes): A 32-bit signed integer that defines the horizontal (x) coordinate of the point.
+                // y (4 bytes): A 32-bit signed integer that defines the vertical (y) coordinate of the point.
+                if (i==0 && hasStartPoint()) {
+                    size += readPoint(leis, pnt[0]);
+                    poly.moveTo(pnt[0].getX(),pnt[0].getY());
+                }
+
+                size += readPoint(leis, pnt[0]);
+                size += readPoint(leis, pnt[1]);
+                size += readPoint(leis, pnt[2]);
+
+                poly.curveTo(
+                    pnt[0].getX(),pnt[0].getY(),
+                    pnt[1].getX(),pnt[1].getY(),
+                    pnt[2].getX(),pnt[2].getY()
+                );
+            }
+
+            return size;
+        }
+
+        /**
+         * @return true, if start point is in the list of points. false, if start point is taken from the context
+         */
+        protected boolean hasStartPoint() {
+            return true;
+        }
+    }
+
+    /**
+     * The EMR_POLYBEZIER16 record specifies one or more Bezier curves.
+     * The curves are drawn using the current pen.
+     */
+    public static class EmfPolyBezier16 extends EmfPolyBezier {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polyBezier16;
+        }
+
+        protected long readPoint(LittleEndianInputStream leis, Point2D point) {
+            return readPointS(leis, point);
+        }
+    }
+
+
+    /**
+     * The EMR_POLYGON record specifies a polygon consisting of two or more vertexes connected by
+     * straight lines.
+     */
+    public static class EmfPolygon extends HwmfDraw.WmfPolygon implements HemfRecord {
+        private final Rectangle2D bounds = new Rectangle2D.Double();
+
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polygon;
+        }
+
+        protected long readPoint(LittleEndianInputStream leis, Point2D point) {
+            return readPointL(leis, point);
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            long size = readRectL(leis, bounds);
+
+            // see PolyBezier about limits
+            final int count = (int)leis.readUInt();
+            final int points = Math.min(count, 16384);
+            size += LittleEndianConsts.INT_SIZE;
+
+            Point2D pnt = new Point2D.Double();
+            for (int i=0; i<points; i++) {
+                size += readPoint(leis, pnt);
+                if (i==0) {
+                    poly.moveTo(pnt.getX(), pnt.getY());
+
+                    if (hasStartPoint()) {
+                        continue;
+                    }
+
+                    // if this path is connected to the current position (= has no start point)
+                    // the first entry is a dummy entry and will be skipped later
+                }
+                poly.lineTo(pnt.getX(), pnt.getY());
+            }
+
+            return size;
+        }
+
+        /**
+         * @return true, if start point is in the list of points. false, if start point is taken from the context
+         */
+        protected boolean hasStartPoint() {
+            return true;
+        }
+    }
+
+    /**
+     * The EMR_POLYGON16 record specifies a polygon consisting of two or more vertexes connected by straight lines.
+     * The polygon is outlined by using the current pen and filled by using the current brush and polygon fill mode.
+     * The polygon is closed automatically by drawing a line from the last vertex to the first
+     */
+    public static class EmfPolygon16 extends EmfPolygon {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polygon16;
+        }
+
+        @Override
+        protected long readPoint(LittleEndianInputStream leis, Point2D point) {
+            return readPointS(leis, point);
+        }
+    }
+
+    /**
+     * The EMR_POLYLINE record specifies a series of line segments by connecting the points in the
+     * specified array.
+     */
+    public static class EmfPolyline extends EmfPolygon {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polyline;
+        }
+
+        @Override
+        protected boolean isFill() {
+            return false;
+        }
+    }
+
+    /**
+     * The EMR_POLYLINE16 record specifies a series of line segments by connecting the points in the
+     * specified array.
+     */
+    public static class EmfPolyline16 extends EmfPolyline {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polyline16;
+        }
+
+        @Override
+        protected long readPoint(LittleEndianInputStream leis, Point2D point) {
+            return readPointS(leis, point);
+        }
+    }
+
+    /**
+     * The EMR_POLYBEZIERTO record specifies one or more Bezier curves based upon the current
+     * position.
+     */
+    public static class EmfPolyBezierTo extends EmfPolyBezier {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polyBezierTo;
+        }
+
+        @Override
+        protected boolean hasStartPoint() {
+            return false;
+        }
+
+        @Override
+        protected Path2D getShape(HwmfGraphics ctx) {
+            return polyTo(ctx, poly);
+        }
+    }
+
+    /**
+     * The EMR_POLYBEZIERTO16 record specifies one or more Bezier curves based on the current
+     * position.
+     */
+    public static class EmfPolyBezierTo16 extends EmfPolyBezierTo {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polyBezierTo16;
+        }
+
+        @Override
+        protected long readPoint(LittleEndianInputStream leis, Point2D point) {
+            return readPointS(leis, point);
+        }
+    }
+
+    /** The EMR_POLYLINETO record specifies one or more straight lines based upon the current position. */
+    public static class EmfPolylineTo extends EmfPolyline {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polylineTo;
+        }
+
+        @Override
+        protected boolean hasStartPoint() {
+            return false;
+        }
+
+        @Override
+        protected Path2D getShape(HwmfGraphics ctx) {
+            return polyTo(ctx, poly);
+        }
+    }
+
+    /**
+     * The EMR_POLYLINETO16 record specifies one or more straight lines based upon the current position.
+     * A line is drawn from the current position to the first point specified by the points field by using the
+     * current pen. For each additional line, drawing is performed from the ending point of the previous
+     * line to the next point specified by points.
+     */
+    public static class EmfPolylineTo16 extends EmfPolylineTo {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polylineTo16;
+        }
+
+        @Override
+        protected long readPoint(LittleEndianInputStream leis, Point2D point) {
+            return readPointS(leis, point);
+        }
+    }
+
+    /**
+     * The EMR_POLYPOLYGON record specifies a series of closed polygons.
+     */
+    public static class EmfPolyPolygon extends HwmfDraw.WmfPolyPolygon implements HemfRecord {
+        private final Rectangle2D bounds = new Rectangle2D.Double();
+
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polyPolygon;
+        }
+
+        protected long readPoint(LittleEndianInputStream leis, Point2D point) {
+            return readPointL(leis, point);
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            long size = readRectL(leis, bounds);
+
+            // A 32-bit unsigned integer that specifies the number of polygons.
+            long numberOfPolygons = leis.readUInt();
+            // A 32-bit unsigned integer that specifies the total number of points in all polygons.
+            long count = Math.min(16384, leis.readUInt());
+
+            size += 2 * LittleEndianConsts.INT_SIZE;
+
+            // An array of 32-bit unsigned integers that specifies the point count for each polygon.
+            long[] polygonPointCount = new long[(int)numberOfPolygons];
+
+            size += numberOfPolygons * LittleEndianConsts.INT_SIZE;
+
+            for (int i=0; i<numberOfPolygons; i++) {
+                polygonPointCount[i] = leis.readUInt();
+            }
+
+            Point2D pnt = new Point2D.Double();
+            for (long nPoints : polygonPointCount) {
+                /**
+                 * An array of WMF PointL objects that specifies the points for all polygons in logical units.
+                 * The number of points is specified by the Count field value.
+                 */
+                Path2D poly = new Path2D.Double();
+                for (int i=0; i<nPoints; i++) {
+                    size += readPoint(leis, pnt);
+                    if (i == 0) {
+                        poly.moveTo(pnt.getX(), pnt.getY());
+                    } else {
+                        poly.lineTo(pnt.getX(), pnt.getY());
+                    }
+                }
+                if (isClosed()) {
+                    poly.closePath();
+                }
+                polyList.add(poly);
+            }
+            return size;
+        }
+
+        /**
+         * @return true, if a polyline should be closed, i.e. is a polygon
+         */
+        protected boolean isClosed() {
+            return true;
+        }
+    }
+
+    /**
+     * The EMR_POLYPOLYGON16 record specifies a series of closed polygons. Each polygon is outlined
+     * using the current pen, and filled using the current brush and polygon fill mode.
+     * The polygons drawn by this record can overlap.
+     */
+    public static class EmfPolyPolygon16 extends EmfPolyPolygon {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polyPolygon16;
+        }
+
+        @Override
+        protected long readPoint(LittleEndianInputStream leis, Point2D point) {
+            return readPointS(leis, point);
+        }
+    }
+
+    /**
+     * The EMR_POLYPOLYLINE record specifies multiple series of connected line segments.
+     */
+    public static class EmfPolyPolyline extends EmfPolyPolygon {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polyPolyline;
+        }
+
+        @Override
+        protected boolean isClosed() {
+            return false;
+        }
+
+        @Override
+        protected boolean isFill() {
+            return false;
+        }
+    }
+
+    /** The EMR_POLYPOLYLINE16 record specifies multiple series of connected line segments. */
+    public static class EmfPolyPolyline16 extends EmfPolyPolyline {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polyPolyline16;
+        }
+
+        @Override
+        protected long readPoint(LittleEndianInputStream leis, Point2D point) {
+            return readPointS(leis, point);
+        }
+    }
+
+    /**
+     * The EMR_SETPIXELV record defines the color of the pixel at the specified logical coordinates.
+     */
+    public static class EmfSetPixelV extends HwmfDraw.WmfSetPixel implements HemfRecord {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.setPixelV;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            long size = readPointL(leis, point);
+            size += colorRef.init(leis);
+            return size;
+        }
+    }
+
+    /**
+     * The EMR_MOVETOEX record specifies coordinates of the new current position, in logical units.
+     */
+    public static class EmfSetMoveToEx extends HwmfDraw.WmfMoveTo implements HemfRecord {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.setMoveToEx;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            return readPointL(leis, point);
+        }
+    }
+
+    /**
+     * The EMR_ARCTO record specifies an elliptical arc.
+     * It resets the current position to the end point of the arc.
+     */
+    public static class EmfArc extends HwmfDraw.WmfArc implements HemfRecord {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.arc;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            long size = readRectL(leis, bounds);
+            size += readPointL(leis, startPoint);
+            size += readPointL(leis, endPoint);
+            return size;
+        }
+
+        @Override
+        public void draw(HemfGraphics ctx) {
+            super.draw(ctx);
+            ctx.getProperties().setLocation(endPoint);
+        }
+    }
+
+    /**
+     * The EMR_CHORD record specifies a chord, which is a region bounded by the intersection of an
+     * ellipse and a line segment, called a secant. The chord is outlined by using the current pen
+     * and filled by using the current brush.
+     */
+    public static class EmfChord extends HwmfDraw.WmfChord implements HemfRecord {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.chord;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            long size = readRectL(leis, bounds);
+            size += readPointL(leis, startPoint);
+            size += readPointL(leis, endPoint);
+            return size;
+        }
+    }
+
+    /**
+     * The EMR_PIE record specifies a pie-shaped wedge bounded by the intersection of an ellipse and two
+     * radials. The pie is outlined by using the current pen and filled by using the current brush.
+     */
+    public static class EmfPie extends HwmfDraw.WmfPie implements HemfRecord {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.pie;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            long size = readRectL(leis, bounds);
+            size += readPointL(leis, startPoint);
+            size += readPointL(leis, endPoint);
+            return size;
+        }
+    }
+
+    /**
+     * The EMR_ELLIPSE record specifies an ellipse. The center of the ellipse is the center of the specified
+     * bounding rectangle. The ellipse is outlined by using the current pen and is filled by using the current
+     * brush.
+     */
+    public static class EmfEllipse extends HwmfDraw.WmfEllipse implements HemfRecord {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.ellipse;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            return readRectL(leis, bounds);
+        }
+    }
+
+    /**
+     * The EMR_RECTANGLE record draws a rectangle. The rectangle is outlined by using the current pen
+     * and filled by using the current brush.
+     */
+    public static class EmfRectangle extends HwmfDraw.WmfRectangle implements HemfRecord {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.rectangle;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            return readRectL(leis, bounds);
+        }
+    }
+
+    /**
+     * The EMR_ROUNDRECT record specifies a rectangle with rounded corners. The rectangle is outlined
+     * by using the current pen and filled by using the current brush.
+     */
+    public static class EmfRoundRect extends HwmfDraw.WmfRoundRect implements HemfRecord {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.roundRect;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            long size = readRectL(leis, bounds);
+
+            // A 32-bit unsigned integer that defines the x-coordinate of the point.
+            width = (int)leis.readUInt();
+            height = (int)leis.readUInt();
+
+            return size + 2*LittleEndianConsts.INT_SIZE;
+        }
+    }
+
+    /**
+     * The EMR_LINETO record specifies a line from the current position up to, but not including, the
+     * specified point. It resets the current position to the specified point.
+     */
+    public static class EmfLineTo extends HwmfDraw.WmfLineTo implements HemfRecord {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.lineTo;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            return readPointL(leis, point);
+        }
+    }
+
+    /**
+     * The EMR_ARCTO record specifies an elliptical arc.
+     * It resets the current position to the end point of the arc.
+     */
+    public static class EmfArcTo extends HwmfDraw.WmfArc implements HemfRecord {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.arcTo;
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            long size = readRectL(leis, bounds);
+            size += readPointL(leis, startPoint);
+            size += readPointL(leis, endPoint);
+            return size;
+        }
+
+        @Override
+        public void draw(HemfGraphics ctx) {
+            super.draw(ctx);
+            ctx.getProperties().setLocation(endPoint);
+        }
+    }
+
+    /** The EMR_POLYDRAW record specifies a set of line segments and Bezier curves. */
+    public static class EmfPolyDraw extends HwmfDraw.WmfPolygon implements HemfRecord {
+        private final Rectangle2D bounds = new Rectangle2D.Double();
+
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polyDraw;
+        }
+
+        protected long readPoint(LittleEndianInputStream leis, Point2D point) {
+            return readPointL(leis, point);
+        }
+
+        @Override
+        public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
+            long size = readRectL(leis, bounds);
+            int count = (int)leis.readUInt();
+            size += LittleEndianConsts.INT_SIZE;
+            Point2D points[] = new Point2D[count];
+            for (int i=0; i<count; i++) {
+                 size += readPoint(leis, points[i]);
+            }
+
+            poly.reset();
+
+            for (int i=0; i<count; i++) {
+                int mode = leis.readUByte();
+                switch (mode & 0x06) {
+                    // PT_LINETO
+                    // Specifies that a line is to be drawn from the current position to this point, which
+                    // then becomes the new current position.
+                    case 0x02:
+                        poly.lineTo(points[i].getX(), points[i].getY());
+                        break;
+                    // PT_BEZIERTO
+                    // Specifies that this point is a control point or ending point for a Bezier curve.
+                    // PT_BEZIERTO types always occur in sets of three.
+                    // The current position defines the starting point for the Bezier curve.
+                    // The first two PT_BEZIERTO points are the control points,
+                    // and the third PT_BEZIERTO point is the ending point.
+                    // The ending point becomes the new current position.
+                    // If there are not three consecutive PT_BEZIERTO points, an error results.
+                    case 0x04:
+                        int mode2 = leis.readUByte();
+                        int mode3 = leis.readUByte();
+                        assert(mode2 == 0x04 && mode3 == 0x04);
+                        poly.curveTo(
+                            points[i].getX(), points[i].getY(),
+                            points[i+1].getX(), points[i+1].getY(),
+                            points[i+2].getX(), points[i+2].getY()
+                        );
+                        i+=2;
+                        break;
+                    // PT_MOVETO
+                    // Specifies that this point starts a disjoint figure. This point becomes the new current position.
+                    case 0x06:
+                        poly.moveTo(points[i].getX(), points[i].getY());
+                        break;
+                    default:
+                        // TODO: log error
+                        break;
+                }
+
+                // PT_CLOSEFIGURE
+                // A PT_LINETO or PT_BEZIERTO type can be combined with this value by using the bitwise operator OR
+                // to indicate that the corresponding point is the last point in a figure and the figure is closed.
+                // The current position is set to the ending point of the closing line.
+                if ((mode & 0x01) == 0x01) {
+                    this.poly.closePath();
+                }
+            }
+            size += count;
+            return size;
+        }
+    }
+
+    public static class EmfPolyDraw16 extends EmfPolyDraw {
+        @Override
+        public HemfRecordType getEmfRecordType() {
+            return HemfRecordType.polyDraw16;
+        }
+
+        protected long readPoint(LittleEndianInputStream leis, Point2D point) {
+            return readPointS(leis, point);
+        }
+
+    }
+    static long readRectL(LittleEndianInputStream leis, Rectangle2D bounds) {
+        /* A 32-bit signed integer that defines the x coordinate, in logical coordinates,
+         * of the ... corner of the rectangle.
+         */
+        final int left = leis.readInt();
+        final int top = leis.readInt();
+        final int right = leis.readInt();
+        final int bottom = leis.readInt();
+        bounds.setRect(left, top, right-left, bottom-top);
+
+        return 4 * LittleEndianConsts.INT_SIZE;
+    }
+
+    static long readPointS(LittleEndianInputStream leis, Point2D point) {
+        // x (2 bytes): A 16-bit signed integer that defines the horizontal (x) coordinate of the point.
+        final int x = leis.readShort();
+        // y (2 bytes): A 16-bit signed integer that defines the vertical (y) coordinate of the point.
+        final int y = leis.readShort();
+        point.setLocation(x, y);
+
+        return 2*LittleEndianConsts.SHORT_SIZE;
+
+    }
+    static long readPointL(LittleEndianInputStream leis, Point2D point) {
+        // x (4 bytes): A 32-bit signed integer that defines the horizontal (x) coordinate of the point.
+        final int x = leis.readInt();
+        // y (4 bytes): A 32-bit signed integer that defines the vertical (y) coordinate of the point.
+        final int y = leis.readInt();
+        point.setLocation(x, y);
+
+        return 2*LittleEndianConsts.INT_SIZE;
+
+    }
+
+    static long readDimensionFloat(LittleEndianInputStream leis, Dimension2D dimension) {
+        final double width = leis.readFloat();
+        final double height = leis.readFloat();
+        dimension.setSize(width, height);
+        return 2*LittleEndianConsts.INT_SIZE;
+    }
+
+    static long readDimensionInt(LittleEndianInputStream leis, Dimension2D dimension) {
+        final double width = leis.readUInt();
+        final double height = leis.readUInt();
+        dimension.setSize(width, height);
+        return 2*LittleEndianConsts.INT_SIZE;
+    }
+
+    private static Path2D polyTo(HwmfGraphics ctx, Path2D poly) {
+        Path2D polyCopy = new Path2D.Double();
+        Point2D start = ctx.getProperties().getLocation();
+        polyCopy.moveTo(start.getX(), start.getY());
+
+        PathIterator iter = poly.getPathIterator(null);
+        iter.next();
+
+        polyCopy.append(iter, true);
+        return polyCopy;
+    }
+
+
+}

Propchange: poi/branches/hemf/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org