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/12 19:50:04 UTC

svn commit: r1859159 [2/2] - in /poi/trunk/src/scratchpad: src/org/apache/poi/hemf/record/emfplus/ src/org/apache/poi/hemf/usermodel/ testcases/org/apache/poi/hemf/usermodel/

Added: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java?rev=1859159&view=auto
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java (added)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java Sun May 12 19:50:04 2019
@@ -0,0 +1,602 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hemf.record.emfplus;
+
+import static org.apache.poi.hemf.record.emf.HemfFill.readXForm;
+import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readPointF;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.io.IOException;
+import java.util.function.Consumer;
+
+import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
+import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
+import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
+import org.apache.poi.hemf.record.emfplus.HemfPlusPath.EmfPlusPath;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianInputStream;
+
+public class HemfPlusPen {
+    /**
+     * The LineCapType enumeration defines types of line caps to use at the ends of lines that are drawn
+     * with graphics pens.
+     */
+    public enum EmfPlusLineCapType {
+        /** Specifies a squared-off line cap. The end of the line MUST be the last point in the line. */
+        FLAT(0X00000000),
+        /**
+         * Specifies a square line cap. The center of the square MUST be located at
+         * the last point in the line. The width of the square is the line width.
+         */
+        SQUARE(0X00000001),
+        /**
+         * Specifies a circular line cap. The center of the circle MUST be located at
+         * the last point in the line. The diameter of the circle is the line width.
+         */
+        ROUND(0X00000002),
+        /**
+         * Specifies a triangular line cap. The base of the triangle MUST be located
+         * at the last point in the line. The base of the triangle is the line width.
+         */
+        TRIANGLE(0X00000003),
+        /** Specifies that the line end is not anchored. */
+        NO_ANCHOR(0X00000010),
+        /**
+         * Specifies that the line end is anchored with a square line cap. The center of the square MUST be located
+         * at the last point in the line. The height and width of the square are the line width.
+         */
+        SQUARE_ANCHOR(0X00000011),
+        /**
+         * Specifies that the line end is anchored with a circular line cap. The center of the circle MUST be located
+         * at the last point in the line. The circle SHOULD be wider than the line.
+         */
+        ROUND_ANCHOR(0X00000012),
+        /**
+         * Specifies that the line end is anchored with a diamond-shaped line cap, which is a square turned at
+         * 45 degrees. The center of the diamond MUST be located at the last point in the line.
+         * The diamond SHOULD be wider than the line.
+         */
+        DIAMOND_ANCHOR(0X00000013),
+        /**
+         * Specifies that the line end is anchored with an arrowhead shape. The arrowhead point MUST be located at
+         * the last point in the line. The arrowhead SHOULD be wider than the line.
+         */
+        ARROW_ANCHOR(0X00000014),
+        /** Mask used to check whether a line cap is an anchor cap. */
+        ANCHOR_MASK(0X000000F0),
+        /** Specifies a custom line cap. */
+        CUSTOM(0X000000FF)
+        ;
+
+        public final int id;
+
+        EmfPlusLineCapType(int id) {
+            this.id = id;
+        }
+
+        public static EmfPlusLineCapType valueOf(int id) {
+            for (EmfPlusLineCapType wrt : values()) {
+                if (wrt.id == id) return wrt;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * The LineJoinType enumeration defines ways to join two lines that are drawn by the same graphics
+     * pen and whose ends meet.
+     */
+    public enum EmfPlusLineJoin {
+        MITER(0X00000000),
+        BEVEL(0X00000001),
+        ROUND(0X00000002),
+        MITER_CLIPPED(0X00000003)
+        ;
+
+        public final int id;
+
+        EmfPlusLineJoin(int id) {
+            this.id = id;
+        }
+
+        public static EmfPlusLineJoin valueOf(int id) {
+            for (EmfPlusLineJoin wrt : values()) {
+                if (wrt.id == id) return wrt;
+            }
+            return null;
+        }
+
+    }
+
+    /** The LineStyle enumeration defines styles of lines that are drawn with graphics pens. */
+    public enum EmfPlusLineStyle {
+        /** Specifies a solid line. */
+        SOLID(0X00000000),
+        /** Specifies a dashed line. */
+        DASH(0X00000001),
+        /** Specifies a dotted line. */
+        DOT(0X00000002),
+        /** Specifies an alternating dash-dot line. */
+        DASH_DOT(0X00000003),
+        /** Specifies an alternating dash-dot-dot line. */
+        DASH_DOT_DOT(0X00000004),
+        /** Specifies a user-defined, custom dashed line. */
+        CUSTOM(0X00000005)
+        ;
+
+        public final int id;
+
+        EmfPlusLineStyle(int id) {
+            this.id = id;
+        }
+
+        public static EmfPlusLineStyle valueOf(int id) {
+            for (EmfPlusLineStyle wrt : values()) {
+                if (wrt.id == id) return wrt;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * The DashedLineCapType enumeration defines types of line caps to use at the ends of dashed lines
+     * that are drawn with graphics pens.
+     */
+    public enum EmfPlusDashedLineCapType {
+        /** Specifies a flat dashed line cap. */
+        FLAT(0X00000000),
+        /** Specifies a round dashed line cap. */
+        ROUND(0X00000002),
+        /** Specifies a triangular dashed line cap. */
+        TRIANGLE(0X00000003)
+        ;
+
+        public final int id;
+
+        EmfPlusDashedLineCapType(int id) {
+            this.id = id;
+        }
+
+        public static EmfPlusDashedLineCapType valueOf(int id) {
+            for (EmfPlusDashedLineCapType wrt : values()) {
+                if (wrt.id == id) return wrt;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * The PenAlignment enumeration defines the distribution of the width of the pen with respect to the
+     * line being drawn.
+     */
+    public enum EmfPlusPenAlignment {
+        /** Specifies that the EmfPlusPen object is centered over the theoretical line. */
+        CENTER(0X00000000),
+        /** Specifies that the pen is positioned on the inside of the theoretical line. */
+        INSET(0X00000001),
+        /** Specifies that the pen is positioned to the left of the theoretical line. */
+        LEFT(0X00000002),
+        /** Specifies that the pen is positioned on the outside of the theoretical line. */
+        OUTSET(0X00000003),
+        /** Specifies that the pen is positioned to the right of the theoretical line. */
+        RIGHT(0X00000004)
+        ;
+
+        public final int id;
+
+        EmfPlusPenAlignment(int id) {
+            this.id = id;
+        }
+
+        public static EmfPlusPenAlignment valueOf(int id) {
+            for (EmfPlusPenAlignment wrt : values()) {
+                if (wrt.id == id) return wrt;
+            }
+            return null;
+        }
+    }
+
+    public static class EmfPlusPen implements EmfPlusObjectData {
+
+
+        /**
+         * If set, a 2x3 transform matrix MUST be specified in the OptionalData field of an EmfPlusPenData object.
+         */
+        private final static BitField TRANSFORM = BitFieldFactory.getInstance(0x00000001);
+        /**
+         * If set, the style of a starting line cap MUST be specified in the OptionalData field of an
+         * EmfPlusPenData object.
+         */
+        private final static BitField START_CAP = BitFieldFactory.getInstance(0x00000002);
+        /**
+         * Indicates whether the style of an ending line cap MUST be specified in the OptionalData field
+         * of an EmfPlusPenData object.
+         */
+        private final static BitField END_CAP = BitFieldFactory.getInstance(0x00000004);
+        /**
+         * Indicates whether a line join type MUST be specified in the OptionalData
+         * field of an EmfPlusPenData object.
+         */
+        private final static BitField JOIN = BitFieldFactory.getInstance(0x00000008);
+        /**
+         * Indicates whether a miter limit MUST be specified in the OptionalData field of an EmfPlusPenData object.
+         */
+        private final static BitField MITER_LIMIT = BitFieldFactory.getInstance(0x00000010);
+        /**
+         * Indicates whether a line style MUST be specified in the OptionalData field of an EmfPlusPenData object.
+         */
+        private final static BitField LINE_STYLE = BitFieldFactory.getInstance(0x00000020);
+        /**
+         * Indicates whether a dashed line cap MUST be specified in the OptionalData field of an EmfPlusPenData object.
+         */
+        private final static BitField DASHED_LINE_CAP = BitFieldFactory.getInstance(0x00000040);
+        /**
+         * Indicates whether a dashed line offset MUST be specified in the OptionalData field of an EmfPlusPenData object.
+         */
+        private final static BitField DASHED_LINE_OFFSET = BitFieldFactory.getInstance(0x00000080);
+        /**
+         * Indicates whether an EmfPlusDashedLineData object MUST be specified in the
+         * OptionalData field of an EmfPlusPenData object.
+         */
+        private final static BitField DASHED_LINE = BitFieldFactory.getInstance(0x00000100);
+        /**
+         * Indicates whether a pen alignment MUST be specified in the OptionalData field of an EmfPlusPenData object.
+         */
+        private final static BitField NON_CENTER = BitFieldFactory.getInstance(0x00000200);
+        /**
+         * Indicates whether the length and content of a EmfPlusCompoundLineData object are present in the
+         * OptionalData field of an EmfPlusPenData object.
+         */
+        private final static BitField COMPOUND_LINE = BitFieldFactory.getInstance(0x00000400);
+        /**
+         * Indicates whether an EmfPlusCustomStartCapData object MUST be specified
+         * in the OptionalData field of an EmfPlusPenData object.y
+         */
+        private final static BitField CUSTOM_START_CAP = BitFieldFactory.getInstance(0x00000800);
+        /**
+         * Indicates whether an EmfPlusCustomEndCapData object MUST be specified in
+         * the OptionalData field of an EmfPlusPenData object.
+         */
+        private final static BitField CUSTOM_END_CAP = BitFieldFactory.getInstance(0x00001000);
+
+        private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
+
+
+        private int type;
+        private int penDataFlags;
+        private HemfPlusDraw.EmfPlusUnitType unitType;
+        private double penWidth;
+        private AffineTransform trans;
+        private EmfPlusLineCapType startCap, endCap;
+        private EmfPlusLineJoin join;
+        private Double mitterLimit;
+        private EmfPlusLineStyle style;
+        EmfPlusDashedLineCapType dashedLineCapType;
+        private Double dashOffset;
+        private double[] dashedLineData;
+        private EmfPlusPenAlignment penAlignment;
+        private double[] compoundLineData;
+        private EmfPlusCustomLineCap customStartCap;
+        private EmfPlusCustomLineCap customEndCap;
+
+        @Override
+        public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
+            // An EmfPlusGraphicsVersion object that specifies the version of operating system graphics that
+            // was used to create this object.
+            long size = graphicsVersion.init(leis);
+            // This field MUST be set to zero.
+            type = leis.readInt();
+            // A 32-bit unsigned integer that specifies the data in the OptionalData field.
+            // This value MUST be composed of PenData flags
+            penDataFlags = leis.readInt();
+            // A 32-bit unsigned integer that specifies the measuring units for the pen.
+            // The value MUST be from the UnitType enumeration
+            unitType = HemfPlusDraw.EmfPlusUnitType.valueOf(leis.readInt());
+            // A 32-bit floating-point value that specifies the width of the line drawn by the pen in the units specified
+            // by the PenUnit field. If a zero width is specified, a minimum value is used, which is determined by the units.
+            penWidth = leis.readFloat();
+            size += 4* LittleEndianConsts.INT_SIZE;
+
+            if (TRANSFORM.isSet(penDataFlags)) {
+                // An optional EmfPlusTransformMatrix object that specifies a world space to device space transform for
+                // the pen. This field MUST be present if the PenDataTransform flag is set in the PenDataFlags field of
+                // the EmfPlusPenData object.
+                trans = new AffineTransform();
+                size += readXForm(leis, trans);
+            }
+
+            if (START_CAP.isSet(penDataFlags)) {
+                // An optional 32-bit signed integer that specifies the shape for the start of a line in the
+                // CustomStartCapData field. This field MUST be present if the PenDataStartCap flag is set in the
+                // PenDataFlags field of the EmfPlusPenData object, and the value MUST be defined in the LineCapType enumeration
+                startCap = EmfPlusLineCapType.valueOf(leis.readInt());
+                size += LittleEndianConsts.INT_SIZE;
+            }
+
+            if (END_CAP.isSet(penDataFlags)) {
+                // An optional 32-bit signed integer that specifies the shape for the end of a line in the
+                // CustomEndCapData field. This field MUST be present if the PenDataEndCap flag is set in the
+                // PenDataFlags field of the EmfPlusPenData object, and the value MUST be defined in the LineCapType enumeration.
+                endCap = EmfPlusLineCapType.valueOf(leis.readInt());
+                size += LittleEndianConsts.INT_SIZE;
+            }
+
+            if (JOIN.isSet(penDataFlags)) {
+                // An optional 32-bit signed integer that specifies how to join two lines that are drawn by the same pen
+                // and whose ends meet. This field MUST be present if the PenDataJoin flag is set in the PenDataFlags
+                // field of the EmfPlusPenData object, and the value MUST be defined in the LineJoinType enumeration
+                join = EmfPlusLineJoin.valueOf(leis.readInt());
+                size += LittleEndianConsts.INT_SIZE;
+            }
+
+            if (MITER_LIMIT.isSet(penDataFlags)) {
+                // An optional 32-bit floating-point value that specifies the miter limit, which is the maximum allowed
+                // ratio of miter length to line width. The miter length is the distance from the intersection of the
+                // line walls on the inside the join to the intersection of the line walls outside the join. The miter
+                // length can be large when the angle between two lines is small. This field MUST be present if the
+                // PenDataMiterLimit flag is set in the PenDataFlags field of the EmfPlusPenData object.
+                mitterLimit = (double)leis.readFloat();
+                size += LittleEndianConsts.INT_SIZE;
+            }
+
+            if (LINE_STYLE.isSet(penDataFlags)) {
+                // An optional 32-bit signed integer that specifies the style used for lines drawn with this pen object.
+                // This field MUST be present if the PenDataLineStyle flag is set in the PenDataFlags field of the
+                // EmfPlusPenData object, and the value MUST be defined in the LineStyle enumeration
+                style = EmfPlusLineStyle.valueOf(leis.readInt());
+                size += LittleEndianConsts.INT_SIZE;
+            }
+
+            if (DASHED_LINE_CAP.isSet(penDataFlags)) {
+                // An optional 32-bit signed integer that specifies the shape for both ends of each dash in a dashed line.
+                // This field MUST be present if the PenDataDashedLineCap flag is set in the PenDataFlags field of the
+                // EmfPlusPenData object, and the value MUST be defined in the DashedLineCapType enumeration
+                dashedLineCapType = EmfPlusDashedLineCapType.valueOf(leis.readInt());
+                size += LittleEndianConsts.INT_SIZE;
+            }
+
+            if (DASHED_LINE_OFFSET.isSet(penDataFlags)) {
+                // An optional 32-bit floating-point value that specifies the distance from the start of a line to the
+                // start of the first space in a dashed line pattern. This field MUST be present if the
+                // PenDataDashedLineOffset flag is set in the PenDataFlags field of the EmfPlusPenData object.
+                dashOffset = (double)leis.readFloat();
+                size += LittleEndianConsts.INT_SIZE;
+            }
+
+            if (DASHED_LINE.isSet(penDataFlags)) {
+                // A 32-bit unsigned integer that specifies the number of elements in the DashedLineData field.
+                int dashesSize = leis.readInt();
+                if (dashesSize < 0 || dashesSize > 1000) {
+                    throw new RuntimeException("Invalid dash data size");
+                }
+
+                // An array of DashedLineDataSize floating-point values that specify the lengths of the dashes and spaces in a dashed line.
+                dashedLineData = new double[dashesSize];
+                for (int i=0; i<dashesSize; i++) {
+                    dashedLineData[i] = leis.readFloat();
+                }
+
+                size += LittleEndianConsts.INT_SIZE * (dashesSize+1);
+            }
+
+            if (NON_CENTER.isSet(penDataFlags)) {
+                // An optional 32-bit signed integer that specifies the distribution of the pen width with respect to
+                // the coordinates of the line being drawn. This field MUST be present if the PenDataNonCenter flag is
+                // set in the PenDataFlags field of the EmfPlusPenData object, and the value MUST be defined in the
+                // PenAlignment enumeration
+                penAlignment = EmfPlusPenAlignment.valueOf(leis.readInt());
+                size += LittleEndianConsts.INT_SIZE;
+            }
+
+            if (COMPOUND_LINE.isSet(penDataFlags)) {
+                // A 32-bit unsigned integer that specifies the number of elements in the CompoundLineData field.
+                int compoundSize = leis.readInt();
+                if (compoundSize < 0 || compoundSize > 1000) {
+                    throw new RuntimeException("Invalid compound line data size");
+                }
+
+                // An array of CompoundLineDataSize floating-point values that specify the compound line of a pen.
+                // The elements MUST be in increasing order, and their values MUST be between 0.0 and 1.0, inclusive.
+                compoundLineData = new double[compoundSize];
+
+                for (int i=0; i<compoundSize; i++) {
+                    compoundLineData[i] = leis.readFloat();
+                }
+                size += LittleEndianConsts.INT_SIZE * (compoundSize+1);
+            }
+
+            if (CUSTOM_START_CAP.isSet(penDataFlags)) {
+                size += initCustomCap(c -> customStartCap = c, leis);
+            }
+
+            if (CUSTOM_END_CAP.isSet(penDataFlags)) {
+                size += initCustomCap(c -> customEndCap = c, leis);
+            }
+
+            return size;
+        }
+
+        private long initCustomCap(Consumer<EmfPlusCustomLineCap> setter, LittleEndianInputStream leis) throws IOException {
+            EmfPlusGraphicsVersion version = new EmfPlusGraphicsVersion();
+            long size = version.init(leis);
+
+            boolean adjustableArrow = (leis.readInt() != 0);
+            size += LittleEndianConsts.INT_SIZE;
+
+            EmfPlusCustomLineCap cap = (adjustableArrow) ? new EmfPlusAdjustableArrowCap() : new EmfPlusPathArrowCap();
+            size += cap.init(leis);
+
+            setter.accept(cap);
+
+            return size;
+        }
+
+        @Internal
+        public interface EmfPlusCustomLineCap {
+            long init(LittleEndianInputStream leis) throws IOException;
+        }
+
+        public static class EmfPlusPathArrowCap implements EmfPlusCustomLineCap {
+            /**
+             * If set, an EmfPlusFillPath object MUST be specified in the OptionalData field of the
+             * EmfPlusCustomLineCapData object for filling the custom line cap.
+             */
+            private static final BitField FILL_PATH = BitFieldFactory.getInstance(0x00000001);
+            /**
+             * If set, an EmfPlusLinePath object MUST be specified in the OptionalData field of the
+             * EmfPlusCustomLineCapData object for outlining the custom line cap.
+             */
+            private static final BitField LINE_PATH = BitFieldFactory.getInstance(0x00000002);
+
+
+            private int dataFlags;
+            private EmfPlusLineCapType baseCap;
+            private double baseInset;
+            private EmfPlusLineCapType startCap;
+            private EmfPlusLineCapType endCap;
+            private EmfPlusLineJoin join;
+            private double mitterLimit;
+            private double widthScale;
+            private final Point2D fillHotSpot = new Point2D.Double();
+            private final Point2D lineHotSpot = new Point2D.Double();
+            private EmfPlusPath fillPath;
+            private EmfPlusPath outlinePath;
+
+            @Override
+            public long init(LittleEndianInputStream leis) throws IOException {
+                // A 32-bit unsigned integer that specifies the data in the OptionalData field.
+                // This value MUST be composed of CustomLineCapData flags
+                dataFlags = leis.readInt();
+
+                // A 32-bit unsigned integer that specifies the value from the LineCap enumeration on which
+                // the custom line cap is based.
+                baseCap = EmfPlusLineCapType.valueOf(leis.readInt());
+
+                // A 32-bit floating-point value that specifies the distance between the
+                // beginning of the line cap and the end of the line.
+                baseInset = leis.readFloat();
+
+                // A 32-bit unsigned integer that specifies the value in the LineCap enumeration that indicates the line
+                // cap used at the start/end of the line to be drawn.
+                startCap = EmfPlusLineCapType.valueOf(leis.readInt());
+                endCap = EmfPlusLineCapType.valueOf(leis.readInt());
+
+                // A 32-bit unsigned integer that specifies the value in the LineJoin enumeration, which specifies how
+                // to join two lines that are drawn by the same pen and whose ends meet. At the intersection of the two
+                // line ends, a line join makes the connection look more continuous.
+                join = EmfPlusLineJoin.valueOf(leis.readInt());
+
+                // A 32-bit floating-point value that contains the limit of the thickness of the join on a mitered corner
+                // by setting the maximum allowed ratio of miter length to line width.
+                mitterLimit = leis.readFloat();
+
+                // A 32-bit floating-point value that specifies the amount by which to scale the custom line cap with
+                // respect to the width of the EmfPlusPen object that is used to draw the lines.
+                widthScale = leis.readFloat();
+
+                int size = 8* LittleEndianConsts.INT_SIZE;
+
+                // An EmfPlusPointF object that is not currently used. It MUST be set to {0.0, 0.0}.
+                size += readPointF(leis, fillHotSpot);
+                size += readPointF(leis, lineHotSpot);
+
+                if (FILL_PATH.isSet(dataFlags)) {
+                    fillPath = new EmfPlusPath();
+                    size += fillPath.init(leis, -1, null, -1);
+                }
+
+                if (LINE_PATH.isSet(dataFlags)) {
+                    outlinePath = new EmfPlusPath();
+                    size += outlinePath.init(leis, -1, null, -1);
+                }
+
+                return size;
+            }
+        }
+
+        public static class EmfPlusAdjustableArrowCap implements EmfPlusCustomLineCap {
+            private double width;
+            private double height;
+            private double middleInset;
+            private boolean isFilled;
+            private EmfPlusLineCapType startCap;
+            private EmfPlusLineCapType endCap;
+            private EmfPlusLineJoin join;
+            private double mitterLimit;
+            private double widthScale;
+            private final Point2D fillHotSpot = new Point2D.Double();
+            private final Point2D lineHotSpot = new Point2D.Double();
+
+            @Override
+            public long init(LittleEndianInputStream leis) throws IOException {
+                // A 32-bit floating-point value that specifies the width of the arrow cap.
+                // The width of the arrow cap is scaled by the width of the EmfPlusPen object that is used to draw the
+                // line being capped. For example, when drawing a capped line with a pen that has a width of 5 pixels,
+                // and the adjustable arrow cap object has a width of 3, the actual arrow cap is drawn 15 pixels wide.
+                width = leis.readFloat();
+
+                // A 32-bit floating-point value that specifies the height of the arrow cap.
+                // The height of the arrow cap is scaled by the width of the EmfPlusPen object that is used to draw the
+                // line being capped. For example, when drawing a capped line with a pen that has a width of 5 pixels,
+                // and the adjustable arrow cap object has a height of 3, the actual arrow cap is drawn 15 pixels high.
+                height = leis.readFloat();
+
+                // A 32-bit floating-point value that specifies the number of pixels between the outline of the arrow
+                // cap and the fill of the arrow cap.
+                middleInset = leis.readFloat();
+
+                // A 32-bit Boolean value that specifies whether the arrow cap is filled.
+                // If the arrow cap is not filled, only the outline is drawn.
+                isFilled = (leis.readInt() != 0);
+
+                // A 32-bit unsigned integer that specifies the value in the LineCap enumeration that indicates
+                // the line cap to be used at the start/end of the line to be drawn.
+                startCap = EmfPlusLineCapType.valueOf(leis.readInt());
+                endCap = EmfPlusLineCapType.valueOf(leis.readInt());
+
+                // 32-bit unsigned integer that specifies the value in the LineJoin enumeration that specifies how to
+                // join two lines that are drawn by the same pen and whose ends meet. At the intersection of the two
+                // line ends, a line join makes the connection look more continuous.
+                join = EmfPlusLineJoin.valueOf(leis.readInt());
+
+                // A 32-bit floating-point value that specifies the limit of the thickness of the join on a mitered
+                // corner by setting the maximum allowed ratio of miter length to line width.
+                mitterLimit = leis.readFloat();
+
+                // A 32-bit floating-point value that specifies the amount by which to scale an EmfPlusCustomLineCap
+                // object with respect to the width of the graphics pen that is used to draw the lines.
+                widthScale = leis.readFloat();
+
+                int size = 9 * LittleEndianConsts.INT_SIZE;
+
+                // An EmfPlusPointF object that is not currently used. It MUST be set to {0.0, 0.0}.
+                size += readPointF(leis, fillHotSpot);
+
+                // An EmfPlusPointF object that is not currently used. It MUST be set to {0.0, 0.0}.
+                size += readPointF(leis, lineHotSpot);
+
+                return size;
+            }
+        }
+
+    }
+}

Propchange: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java?rev=1859159&view=auto
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java (added)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java Sun May 12 19:50:04 2019
@@ -0,0 +1,173 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hemf.record.emfplus;
+
+import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readRectF;
+
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
+import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
+import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
+import org.apache.poi.hemf.record.emfplus.HemfPlusPath.EmfPlusPath;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianInputStream;
+
+public class HemfPlusRegion {
+    public enum EmfPlusRegionNodeDataType {
+        /**
+         * Specifies a region node with child nodes. A Boolean AND operation SHOULD be applied to the left and right
+         * child nodes specified by an EmfPlusRegionNodeChildNodes object
+         */
+        AND(0X00000001, EmfPlusRegionNode::new),
+        /**
+         * Specifies a region node with child nodes. A Boolean OR operation SHOULD be applied to the left and right
+         * child nodes specified by an EmfPlusRegionNodeChildNodes object.
+         */
+        OR(0X00000002, EmfPlusRegionNode::new),
+        /**
+         * Specifies a region node with child nodes. A Boolean XOR operation SHOULD be applied to the left and right
+         * child nodes specified by an EmfPlusRegionNodeChildNodes object.
+         */
+        XOR(0X00000003, EmfPlusRegionNode::new),
+        /**
+         * Specifies a region node with child nodes. A Boolean operation, defined as "the part of region 1 that is excluded
+         * from region 2", SHOULD be applied to the left and right child nodes specified by an EmfPlusRegionNodeChildNodes object.
+         */
+        EXCLUDE(0X00000004, EmfPlusRegionNode::new),
+        /**
+         * Specifies a region node with child nodes. A Boolean operation, defined as "the part of region 2 that is excluded
+         * from region 1", SHOULD be applied to the left and right child nodes specified by an EmfPlusRegionNodeChildNodes object.
+         */
+        COMPLEMENT(0X00000005, EmfPlusRegionNode::new),
+        /**
+         * Specifies a region node with no child nodes.
+         * The RegionNodeData field SHOULD specify a boundary with an EmfPlusRectF object.
+         */
+        RECT(0X10000000, EmfPlusRegionRect::new),
+        /**
+         * Specifies a region node with no child nodes.
+         * The RegionNodeData field SHOULD specify a boundary with an EmfPlusRegionNodePath object
+         */
+        PATH(0X10000001, EmfPlusRegionPath::new),
+        /** Specifies a region node with no child nodes. The RegionNodeData field SHOULD NOT be present. */
+        EMPTY(0X10000002, EmfPlusRegionEmpty::new),
+        /** Specifies a region node with no child nodes, and its bounds are not defined. */
+        INFINITE(0X10000003, EmfPlusRegionInfinite::new)
+        ;
+
+        public final int id;
+        public final Supplier<EmfPlusRegionNodeData> constructor;
+
+        EmfPlusRegionNodeDataType(int id, Supplier<EmfPlusRegionNodeData> constructor) {
+            this.id = id;
+            this.constructor = constructor;
+        }
+
+        public static EmfPlusRegionNodeDataType valueOf(int id) {
+            for (EmfPlusRegionNodeDataType wrt : values()) {
+                if (wrt.id == id) return wrt;
+            }
+            return null;
+        }
+    }
+
+    public static class EmfPlusRegion implements EmfPlusObjectData {
+
+        private final EmfPlusGraphicsVersion version = new EmfPlusGraphicsVersion();
+        private EmfPlusRegionNodeData regionNode;
+
+        @Override
+        public long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException {
+            long size = version.init(leis);
+
+            // A 32-bit unsigned integer that specifies the number of child nodes in the RegionNode field.
+            int nodeCount = leis.readInt();
+            size += LittleEndianConsts.INT_SIZE;
+
+            // An array of RegionNodeCount+1 EmfPlusRegionNode objects. Regions are specified as a binary tree of
+            // region nodes, and each node MUST either be a terminal node or specify one or two child nodes.
+            // RegionNode MUST contain at least one element.
+            size += readNode(leis, d -> regionNode = d);
+
+            return size;
+        }
+
+
+    }
+
+
+    public interface EmfPlusRegionNodeData {
+        long init(LittleEndianInputStream leis) throws IOException;
+    }
+
+    public static class EmfPlusRegionPath extends EmfPlusPath implements EmfPlusRegionNodeData {
+        public long init(LittleEndianInputStream leis) throws IOException {
+            int dataSize = leis.readInt();
+            return super.init(leis, dataSize, EmfPlusObjectType.PATH, 0) + LittleEndianConsts.INT_SIZE;
+        }
+    }
+
+    public static class EmfPlusRegionInfinite implements EmfPlusRegionNodeData {
+        @Override
+        public long init(LittleEndianInputStream leis) throws IOException {
+            return 0;
+        }
+    }
+
+    public static class EmfPlusRegionEmpty implements EmfPlusRegionNodeData {
+        @Override
+        public long init(LittleEndianInputStream leis) throws IOException {
+            return 0;
+        }
+    }
+
+    public static class EmfPlusRegionRect implements EmfPlusRegionNodeData {
+        private final Rectangle2D rect = new Rectangle2D.Double();
+
+        @Override
+        public long init(LittleEndianInputStream leis) {
+            return readRectF(leis, rect);
+        }
+    }
+
+    public static class EmfPlusRegionNode implements EmfPlusRegionNodeData {
+        private EmfPlusRegionNodeData left, right;
+
+        @Override
+        public long init(LittleEndianInputStream leis) throws IOException {
+            long size = readNode(leis, n -> left = n);
+            size += readNode(leis, n -> right = n);
+            return size;
+        }
+    }
+
+
+    private static long readNode(LittleEndianInputStream leis, Consumer<EmfPlusRegionNodeData> con) throws IOException {
+        // A 32-bit unsigned integer that specifies the type of data in the RegionNodeData field.
+        // This value MUST be defined in the RegionNodeDataType enumeration
+        EmfPlusRegionNodeDataType type = EmfPlusRegionNodeDataType.valueOf(leis.readInt());
+        assert(type != null);
+        EmfPlusRegionNodeData nd = type.constructor.get();
+        con.accept(nd);
+        return LittleEndianConsts.INT_SIZE + nd.init(leis);
+    }
+}

Propchange: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 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=1859159&r1=1859158&r2=1859159&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfEmbeddedIterator.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfEmbeddedIterator.java Sun May 12 19:50:04 2019
@@ -36,7 +36,11 @@ 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.HemfPlusImage.EmfPlusBitmapDataType;
+import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusImage;
+import org.apache.poi.hemf.record.emfplus.HemfPlusImage.EmfPlusPixelFormat;
 import org.apache.poi.hemf.record.emfplus.HemfPlusObject;
+import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObject;
 import org.apache.poi.hwmf.record.HwmfBitmapDib;
 import org.apache.poi.hwmf.record.HwmfFill;
 import org.apache.poi.hwmf.usermodel.HwmfEmbedded;
@@ -105,7 +109,7 @@ public class HemfEmbeddedIterator implem
                     return true;
                 }
 
-                if (obj instanceof HemfPlusObject.EmfPlusObject && ((HemfPlusObject.EmfPlusObject)obj).getObjectType() == HemfPlusObject.EmfPlusObjectType.IMAGE) {
+                if (obj instanceof EmfPlusObject && ((EmfPlusObject)obj).getObjectType() == HemfPlusObject.EmfPlusObjectType.IMAGE) {
                     current = obj;
                     return true;
                 }
@@ -196,13 +200,13 @@ public class HemfEmbeddedIterator implem
     }
 
     private HwmfEmbedded checkEmfPlusObject() {
-        if (!(current instanceof HemfPlusObject.EmfPlusObject)) {
+        if (!(current instanceof EmfPlusObject)) {
             return null;
         }
 
-        HemfPlusObject.EmfPlusObject epo = (HemfPlusObject.EmfPlusObject)current;
+        EmfPlusObject epo = (EmfPlusObject)current;
         assert(epo.getObjectType() == HemfPlusObject.EmfPlusObjectType.IMAGE);
-        HemfPlusObject.EmfPlusImage img = epo.getObjectData();
+        EmfPlusImage img = epo.getObjectData();
         assert(img.getImageDataType() != null);
 
         HwmfEmbedded emb = getEmfPlusImageData();
@@ -210,7 +214,7 @@ public class HemfEmbeddedIterator implem
         HwmfEmbeddedType et;
         switch (img.getImageDataType()) {
             case BITMAP:
-                if (img.getBitmapType() == HemfPlusObject.EmfPlusBitmapDataType.COMPRESSED) {
+                if (img.getBitmapType() == EmfPlusBitmapDataType.COMPRESSED) {
                     switch (FileMagic.valueOf(emb.getRawData())) {
                         case JPEG:
                             et = HwmfEmbeddedType.JPEG;
@@ -262,11 +266,11 @@ public class HemfEmbeddedIterator implem
     /**
      * Compress GDIs internal format to something useful
      */
-    private void compressGDIBitmap(HemfPlusObject.EmfPlusImage img, HwmfEmbedded emb, HwmfEmbeddedType et) {
+    private void compressGDIBitmap(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();
+        final EmfPlusPixelFormat pf = img.getPixelFormat();
 
         int[] nBits, bOffs;
         switch (pf) {
@@ -306,14 +310,14 @@ public class HemfEmbeddedIterator implem
 
 
     private HwmfEmbedded getEmfPlusImageData() {
-        HemfPlusObject.EmfPlusObject epo = (HemfPlusObject.EmfPlusObject)current;
+        EmfPlusObject epo = (EmfPlusObject)current;
         assert(epo.getObjectType() == HemfPlusObject.EmfPlusObjectType.IMAGE);
 
         final int objectId = epo.getObjectId();
 
         HwmfEmbedded emb = new HwmfEmbedded();
 
-        HemfPlusObject.EmfPlusImage img = (HemfPlusObject.EmfPlusImage)epo.getObjectData();
+        EmfPlusImage img = (EmfPlusImage)epo.getObjectData();
         assert(img.getImageDataType() != null);
 
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -324,10 +328,10 @@ public class HemfEmbeddedIterator implem
                 current = null;
                 //noinspection ConstantConditions
                 if (hasNext() &&
-                    (current instanceof HemfPlusObject.EmfPlusObject) &&
-                    ((epo = (HemfPlusObject.EmfPlusObject) current).getObjectId() == objectId)
+                    (current instanceof EmfPlusObject) &&
+                    ((epo = (EmfPlusObject) current).getObjectId() == objectId)
                 ) {
-                    img = (HemfPlusObject.EmfPlusImage)epo.getObjectData();
+                    img = (EmfPlusImage)epo.getObjectData();
                 } else {
                     return emb;
                 }

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=1859159&r1=1859158&r2=1859159&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 Sun May 12 19:50:04 2019
@@ -94,7 +94,7 @@ public class HemfPictureTest {
 
                 if (entry.isDirectory() || !etName.endsWith(".emf") || passed.contains(etName)) continue;
 
-                if (!etName.equals("emfs/commoncrawl2/2S/2SYMYPLNJURGCXJKLNZCJQGIBHVMQTRS_0.emf")) continue;
+                // if (!etName.equals("emfs/commoncrawl2/2S/2SYMYPLNJURGCXJKLNZCJQGIBHVMQTRS_0.emf")) continue;
 
                 // emfs/commoncrawl2/ZJ/ZJT2BZPLQR7DKSKYLYL6GRDEUM2KIO5F_4.emf
                 // emfs/govdocs1/005/005203.ppt_3.emf



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