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/09/11 21:24:08 UTC

svn commit: r1866808 [2/7] - in /poi: site/src/documentation/content/xdocs/ trunk/src/java/org/apache/poi/common/usermodel/ trunk/src/java/org/apache/poi/common/usermodel/fonts/ trunk/src/java/org/apache/poi/ddf/ trunk/src/java/org/apache/poi/hssf/user...

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java Wed Sep 11 21:24:06 2019
@@ -16,10 +16,17 @@
 ==================================================================== */
 package org.apache.poi.poifs.crypt;
 
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.common.usermodel.GenericRecord;
+
 /**
  * Used when checking if a key is valid for a document 
  */
-public abstract class EncryptionVerifier implements Cloneable {
+public abstract class EncryptionVerifier implements Cloneable, GenericRecord {
     private byte[] salt;
     private byte[] encryptedVerifier;
     private byte[] encryptedVerifierHash;
@@ -105,4 +112,18 @@ public abstract class EncryptionVerifier
         other.encryptedKey = (encryptedKey == null) ? null : encryptedKey.clone();
         return other;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+        m.put("salt", this::getSalt);
+        m.put("encryptedVerifier", this::getEncryptedVerifier);
+        m.put("encryptedVerifierHash", this::getEncryptedVerifierHash);
+        m.put("encryptedKey", this::getEncryptedKey);
+        m.put("spinCount", this::getSpinCount);
+        m.put("cipherAlgorithm", this::getCipherAlgorithm);
+        m.put("chainingMode", this::getChainingMode);
+        m.put("hashAlgorithm", this::getHashAlgorithm);
+        return Collections.unmodifiableMap(m);
+    }
 }

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java Wed Sep 11 21:24:06 2019
@@ -19,15 +19,19 @@ package org.apache.poi.poifs.crypt;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.security.GeneralSecurityException;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.GenericRecordUtil;
 
-public abstract class Encryptor implements Cloneable {
+public abstract class Encryptor implements Cloneable, GenericRecord {
     protected static final String DEFAULT_POIFS_ENTRY = Decryptor.DEFAULT_POIFS_ENTRY;
     private EncryptionInfo encryptionInfo;
     private SecretKey secretKey;
@@ -93,4 +97,11 @@ public abstract class Encryptor implemen
         // encryptionInfo is set from outside
         return other;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "secretKey", secretKey::getEncoded
+        );
+    }
 }

Modified: poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/draw/DrawPictureShape.java Wed Sep 11 21:24:06 2019
@@ -76,9 +76,9 @@ public class DrawPictureShape extends Dr
      * @param graphics the graphics context
      * @return the image renderer
      */
-    @SuppressWarnings({"WeakerAccess", "unchecked"})
+    @SuppressWarnings({"unchecked"})
     public static ImageRenderer getImageRenderer(Graphics2D graphics, String contentType) {
-        final ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER);
+        final ImageRenderer renderer = (graphics != null) ? (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER) : null;
         if (renderer != null && renderer.canRender(contentType)) {
             return renderer;
         }

Modified: poi/trunk/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java Wed Sep 11 21:24:06 2019
@@ -23,8 +23,8 @@ import java.awt.BasicStroke;
 import java.awt.Color;
 import java.awt.Graphics2D;
 import java.awt.Paint;
+import java.awt.Stroke;
 import java.awt.geom.AffineTransform;
-import java.awt.geom.Area;
 import java.awt.geom.Ellipse2D;
 import java.awt.geom.Path2D;
 import java.awt.geom.Rectangle2D;
@@ -62,6 +62,10 @@ public class DrawSimpleShape extends Dra
             return;
         }
 
+        Paint oldPaint = graphics.getPaint();
+        Stroke oldStroke = graphics.getStroke();
+        Color oldColor = graphics.getColor();
+
         Paint fill = getFillPaint(graphics);
         Paint line = getLinePaint(graphics);
         BasicStroke stroke = getStroke(); // the stroke applies both to the shadow and the shape
@@ -115,8 +119,12 @@ public class DrawSimpleShape extends Dra
             }
         }
 
-		// draw line decorations
+        // draw line decorations
         drawDecoration(graphics, line, stroke);
+
+        graphics.setColor(oldColor);
+        graphics.setPaint(oldPaint);
+        graphics.setStroke(oldStroke);
     }
 
     private void fillArea(Graphics2D graphics, PaintModifier pm, Path2D area) {

Modified: poi/trunk/src/java/org/apache/poi/sl/draw/ImageRenderer.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/draw/ImageRenderer.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/draw/ImageRenderer.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/draw/ImageRenderer.java Wed Sep 11 21:24:06 2019
@@ -26,6 +26,8 @@ import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.InputStream;
 
+import org.apache.poi.common.usermodel.GenericRecord;
+
 /**
  * Classes can implement this interfaces to support other formats, for
  * example, use Apache Batik to render WMF, PICT can be rendered using Apple QuickTime API for Java:
@@ -134,4 +136,7 @@ public interface ImageRenderer {
      * @return true if the picture data was successfully rendered
      */
     boolean drawImage(Graphics2D graphics, Rectangle2D anchor, Insets clip);
+
+    default GenericRecord getGenericRecord() { return null; }
+
 }
\ No newline at end of file

Modified: poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java Wed Sep 11 21:24:06 2019
@@ -105,21 +105,38 @@ public interface TextShape<
      */
     enum TextPlaceholder {
         /** Title placeholder shape text */
-        TITLE,
+        TITLE(0),
         /** Body placeholder shape text */
-        BODY,
+        BODY(1),
         /** Center title placeholder shape text */
-        CENTER_TITLE,
+        CENTER_TITLE(6),
         /** Center body placeholder shape text */
-        CENTER_BODY,
+        CENTER_BODY(5),
         /** Half-sized body placeholder shape text */
-        HALF_BODY,
+        HALF_BODY(7),
         /** Quarter-sized body placeholder shape text */
-        QUARTER_BODY,
+        QUARTER_BODY(8),
         /** Notes placeholder shape text */
-        NOTES,
+        NOTES(2),
         /** Any other text */
-        OTHER
+        OTHER(4);
+
+        public final int nativeId;
+
+        TextPlaceholder(int nativeId) {
+            this.nativeId = nativeId;
+        }
+
+        public static TextPlaceholder fromNativeId(int nativeId) {
+            for (TextPlaceholder ld : values()) {
+                if (ld.nativeId == nativeId) return ld;
+            }
+            return null;
+        }
+
+        public static boolean isTitle(int nativeId) {
+            return (nativeId == TITLE.nativeId || nativeId == CENTER_TITLE.nativeId);
+        }
     }
 
     /**

Added: poi/trunk/src/java/org/apache/poi/util/GenericRecordJsonWriter.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/util/GenericRecordJsonWriter.java?rev=1866808&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/util/GenericRecordJsonWriter.java (added)
+++ poi/trunk/src/java/org/apache/poi/util/GenericRecordJsonWriter.java Wed Sep 11 21:24:06 2019
@@ -0,0 +1,455 @@
+/*
+ *  ====================================================================
+ *    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.util;
+
+import java.awt.Color;
+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.Closeable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.Flushable;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.lang.reflect.Array;
+import java.nio.charset.StandardCharsets;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.poi.common.usermodel.GenericRecord;
+import org.apache.poi.util.GenericRecordUtil.AnnotatedFlag;
+
+@Beta
+public class GenericRecordJsonWriter implements Closeable {
+    private static final String TABS;
+    private static final String ZEROS = "0000000000000000";
+    private static final Pattern ESC_CHARS = Pattern.compile("[\"\\p{Cntrl}\\\\]");
+
+    private static final List<Map.Entry<Class,BiConsumer<GenericRecordJsonWriter,Object>>> handler = new ArrayList<>();
+
+    static {
+        char[] t = new char[255];
+        Arrays.fill(t, '\t');
+        TABS = new String(t);
+        handler(String.class, GenericRecordJsonWriter::printObject);
+        handler(Number.class, GenericRecordJsonWriter::printNumber);
+        handler(Boolean.class, GenericRecordJsonWriter::printBoolean);
+        handler(List.class, GenericRecordJsonWriter::printList);
+        handler(GenericRecord.class, GenericRecordJsonWriter::printGenericRecord);
+        handler(AnnotatedFlag.class, GenericRecordJsonWriter::printAnnotatedFlag);
+        handler(byte[].class, GenericRecordJsonWriter::printBytes);
+        handler(Point2D.class, GenericRecordJsonWriter::printPoint);
+        handler(Dimension2D.class, GenericRecordJsonWriter::printDimension);
+        handler(Rectangle2D.class, GenericRecordJsonWriter::printRectangle);
+        handler(Path2D.class, GenericRecordJsonWriter::printPath);
+        handler(AffineTransform.class, GenericRecordJsonWriter::printAffineTransform);
+        handler(Color.class, GenericRecordJsonWriter::printColor);
+        handler(Array.class, GenericRecordJsonWriter::printArray);
+        handler(Object.class, GenericRecordJsonWriter::printObject);
+    }
+
+    private static void handler(Class c, BiConsumer<GenericRecordJsonWriter,Object> printer) {
+        handler.add(new AbstractMap.SimpleEntry<>(c,printer));
+    }
+
+    private final PrintWriter fw;
+    private int indent = 0;
+    private boolean withComments = true;
+    private int childIndex = 0;
+
+    public GenericRecordJsonWriter(File fileName) throws IOException {
+        OutputStream os = ("null".equals(fileName.getName())) ? new NullOutputStream() : new FileOutputStream(fileName);
+        fw = new PrintWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));
+    }
+
+    public GenericRecordJsonWriter(Appendable buffer) {
+        fw = new PrintWriter(new Writer(){
+            @Override
+            public void write(char[] cbuf, int off, int len) throws IOException {
+                buffer.append(String.valueOf(cbuf), off, len);
+            }
+
+            @Override
+            public void flush() throws IOException {
+                if (buffer instanceof Flushable) {
+                    ((Flushable)buffer).flush();
+                }
+            }
+
+            @Override
+            public void close() throws IOException {
+                flush();
+                if (buffer instanceof Closeable) {
+                    ((Closeable)buffer).close();
+                }
+            }
+        });
+    }
+
+    public static String marshal(GenericRecord record) {
+        return marshal(record, true);
+    }
+
+    public static String marshal(GenericRecord record, boolean withComments) {
+        final StringBuilder sb = new StringBuilder();
+        try (GenericRecordJsonWriter w = new GenericRecordJsonWriter(sb)) {
+            w.setWithComments(withComments);
+            w.write(record);
+            return sb.toString();
+        } catch (IOException e) {
+            return "{}";
+        }
+    }
+
+    public void setWithComments(boolean withComments) {
+        this.withComments = withComments;
+    }
+
+    @Override
+    public void close() throws IOException {
+        fw.close();
+    }
+
+    private String tabs() {
+        return TABS.substring(0, Math.min(indent, TABS.length()));
+    }
+
+    public void write(GenericRecord record) {
+        final String tabs = tabs();
+        Enum type = record.getGenericRecordType();
+        String recordName = (type != null) ? type.name() : record.getClass().getSimpleName();
+        fw.append(tabs);
+        fw.append("{");
+        if (withComments) {
+            fw.append("   /* ");
+            fw.append(recordName);
+            if (childIndex > 0) {
+                fw.append(" - index: ");
+                fw.print(childIndex);
+            }
+            fw.append(" */");
+        }
+        fw.println();
+
+        Map<String, Supplier<?>> prop = record.getGenericProperties();
+        if (prop != null) {
+            final int oldChildIndex = childIndex;
+            childIndex = 0;
+            prop.forEach(this::writeProp);
+            childIndex = oldChildIndex;
+        }
+
+        fw.println();
+        List<? extends GenericRecord> list = record.getGenericChildren();
+        if (list != null && !list.isEmpty()) {
+            indent++;
+            fw.append(tabs());
+            if (prop != null && !prop.isEmpty()) {
+                fw.append(", ");
+            }
+            fw.append("children: [");
+            final int oldChildIndex = childIndex;
+            childIndex = 0;
+            list.forEach(l -> { writeValue(l); childIndex++; });
+            childIndex = oldChildIndex;
+            fw.println();
+            fw.append(tabs());
+            fw.append("]");
+            fw.println();
+            indent--;
+        }
+
+        fw.append(tabs);
+        fw.append("}");
+    }
+
+    public void writeError(String errorMsg) {
+        fw.append("{ error: ");
+        printObject(errorMsg);
+        fw.append(" }");
+    }
+
+    private void writeProp(String k, Supplier<?> v) {
+        final boolean isNext = (childIndex++>0);
+        if (isNext) {
+            fw.println();
+        }
+        fw.write(tabs());
+        fw.write('\t');
+        fw.write(isNext ? ", " : "  ");
+        fw.write(k);
+        fw.write(": ");
+        final int oldChildIndex = childIndex;
+        childIndex = 0;
+        writeValue(v.get());
+        childIndex = oldChildIndex;
+    }
+
+    private void writeValue(Object o) {
+        if (childIndex > 0) {
+            fw.println(',');
+        }
+        if (o == null) {
+            fw.write("null");
+        } else {
+            handler.stream().
+                filter(h -> matchInstanceOrArray(h.getKey(), o)).
+                findFirst().
+                ifPresent(h -> h.getValue().accept(this, o));
+        }
+    }
+
+    private static boolean matchInstanceOrArray(Class key, Object instance) {
+        return key.isInstance(instance) || (Array.class.equals(key) && instance.getClass().isArray());
+    }
+
+    private void printNumber(Object o) {
+        Number n = (Number)o;
+        fw.print(n.toString());
+
+        final int size;
+        if (n instanceof Byte) {
+            size = 2;
+        } else if (n instanceof Short) {
+            size = 4;
+        } else if (n instanceof Integer) {
+            size = 8;
+        } else if (n instanceof Long) {
+            size = 16;
+        } else {
+            size = -1;
+        }
+
+        long l = n.longValue();
+        if (withComments && size > 0 && (l < 0 || l > 9)) {
+            fw.write(" /* 0x");
+            fw.write(trimHex(l, size));
+            fw.write(" */");
+        }
+    }
+
+    private void printBoolean(Object o) {
+        fw.write(((Boolean)o).toString());
+    }
+
+    private void printList(Object o) {
+        fw.println('[');
+        final int[] c = new int[1];
+        //noinspection unchecked
+        int oldChildIndex = childIndex;
+        childIndex = 0;
+        ((List)o).forEach(e -> { writeValue(e); childIndex++; });
+        childIndex = oldChildIndex;
+        fw.write(']');
+    }
+
+    private void printGenericRecord(Object o) {
+        fw.println();
+        this.indent++;
+        write((GenericRecord) o);
+        this.indent--;
+    }
+
+    private void printAnnotatedFlag(Object o) {
+        AnnotatedFlag af = (AnnotatedFlag) o;
+        fw.write("0x");
+        fw.write(Long.toHexString(af.getValue().get().longValue()));
+        if (withComments) {
+            fw.write(" /* ");
+            fw.write(af.getDescription());
+            fw.write(" */ ");
+        }
+    }
+
+    private void printBytes(Object o) {
+        fw.write('"');
+        fw.write(DatatypeConverter.printBase64Binary((byte[]) o));
+        fw.write('"');
+    }
+
+    private void printPoint(Object o) {
+        Point2D p = (Point2D)o;
+        fw.write("{ x: "+p.getX()+", y: "+p.getY()+" }");
+    }
+
+    private void printDimension(Object o) {
+        Dimension2D p = (Dimension2D)o;
+        fw.write("{ width: "+p.getWidth()+", height: "+p.getHeight()+" }");
+    }
+
+    private void printRectangle(Object o) {
+        Rectangle2D p = (Rectangle2D)o;
+        fw.write("{ x: "+p.getX()+", y: "+p.getY()+", width: "+p.getWidth()+", height: "+p.getHeight()+" }");
+    }
+
+    private void printPath(Object o) {
+        final PathIterator iter = ((Path2D)o).getPathIterator(null);
+        final double[] pnts = new double[6];
+        fw.print("[");
+
+        indent += 2;
+        String t = tabs();
+        indent -= 2;
+
+        boolean isNext = false;
+        while (!iter.isDone()) {
+            fw.println(isNext ? ", " : "");
+            fw.print(t);
+            isNext = true;
+            final int segType = iter.currentSegment(pnts);
+            fw.append("{ type: ");
+            switch (segType) {
+                case PathIterator.SEG_MOVETO:
+                    fw.write("'move', x: "+pnts[0]+", y: "+pnts[1]);
+                    break;
+                case PathIterator.SEG_LINETO:
+                    fw.write("'lineto', x: "+pnts[0]+", y: "+pnts[1]);
+                    break;
+                case PathIterator.SEG_QUADTO:
+                    fw.write("'quad', x1: "+pnts[0]+", y1: "+pnts[1]+", x2: "+pnts[2]+", y2: "+pnts[3]);
+                    break;
+                case PathIterator.SEG_CUBICTO:
+                    fw.write("'cubic', x1: "+pnts[0]+", y1: "+pnts[1]+", x2: "+pnts[2]+", y2: "+pnts[3]+", x3: "+pnts[4]+", y3: "+pnts[5]);
+                    break;
+                case PathIterator.SEG_CLOSE:
+                    fw.write("'close'");
+                    break;
+            }
+            fw.append(" }");
+            iter.next();
+        }
+
+        fw.write("]");
+    }
+
+    private void printObject(Object o) {
+        fw.write('"');
+
+        final Matcher m = ESC_CHARS.matcher(o.toString());
+        final StringBuffer sb = new StringBuffer();
+        while (m.find()) {
+            String repl;
+            String match = m.group();
+            switch (match) {
+                case "\n":
+                    repl = "\\\\n";
+                    break;
+                case "\r":
+                    repl = "\\\\r";
+                    break;
+                case "\t":
+                    repl = "\\\\t";
+                    break;
+                case "\b":
+                    repl = "\\\\b";
+                    break;
+                case "\f":
+                    repl = "\\\\f";
+                    break;
+                case "\\":
+                    repl = "\\\\\\\\";
+                    break;
+                case "\"":
+                    repl = "\\\\\"";
+                    break;
+                default:
+                    repl = "\\\\u" + trimHex(match.charAt(0), 4);
+                    break;
+            }
+            m.appendReplacement(sb, repl);
+        }
+        m.appendTail(sb);
+        fw.write(sb.toString());
+
+        fw.write('"');
+    }
+
+    private void printAffineTransform(Object o) {
+        AffineTransform xForm = (AffineTransform)o;
+        fw.write(
+            "{ scaleX: "+xForm.getScaleX()+
+            ", shearX: "+xForm.getShearX()+
+            ", transX: "+xForm.getTranslateX()+
+            ", scaleY: "+xForm.getScaleY()+
+            ", shearY: "+xForm.getShearY()+
+            ", transY: "+xForm.getTranslateY()+" }");
+    }
+
+    private void printColor(Object o) {
+        final int rgb = ((Color)o).getRGB();
+        fw.print(rgb);
+
+        if (withComments) {
+            fw.write(" /* 0x");
+            fw.write(trimHex(rgb, 8));
+            fw.write(" */");
+        }
+    }
+
+    private void printArray(Object o) {
+        fw.println('[');
+        int length = Array.getLength(o);
+        final int oldChildIndex = childIndex;
+        for (childIndex=0; childIndex<length; childIndex++) {
+            writeValue(Array.get(o, childIndex));
+        }
+        childIndex = oldChildIndex;
+        fw.write(']');
+    }
+
+    private String trimHex(final long l, final int size) {
+        final String b = Long.toHexString(l);
+        int len = b.length();
+        return ZEROS.substring(0, Math.max(0,size-len)) + b.substring(Math.max(0,len-size), len);
+    }
+
+    private static class NullOutputStream extends OutputStream {
+        private NullOutputStream() {
+        }
+
+        @Override
+        public void write(byte[] b, int off, int len) {
+        }
+
+        @Override
+        public void write(int b) {
+        }
+
+        @Override
+        public void write(byte[] b) {
+        }
+    }
+}

Propchange: poi/trunk/src/java/org/apache/poi/util/GenericRecordJsonWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: poi/trunk/src/java/org/apache/poi/util/GenericRecordUtil.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/util/GenericRecordUtil.java?rev=1866808&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/util/GenericRecordUtil.java (added)
+++ poi/trunk/src/java/org/apache/poi/util/GenericRecordUtil.java Wed Sep 11 21:24:06 2019
@@ -0,0 +1,142 @@
+/*
+ *  ====================================================================
+ *    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.util;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+@Internal
+public final class GenericRecordUtil {
+    private GenericRecordUtil() {}
+
+    public static Map<String, Supplier<?>>
+    getGenericProperties(String val1, Supplier<?> sup1) {
+        return Collections.unmodifiableMap(Collections.singletonMap(val1, sup1));
+    }
+
+    public static Map<String, Supplier<?>> getGenericProperties(
+        String val1, Supplier<?> sup1,
+        String val2, Supplier<?> sup2
+    ) {
+        return getGenericProperties(val1, sup1, val2, sup2, null, null, null, null, null, null, null, null);
+    }
+
+    public static Map<String, Supplier<?>> getGenericProperties(
+            String val1, Supplier<?> sup1,
+            String val2, Supplier<?> sup2,
+            String val3, Supplier<?> sup3
+    ) {
+        return getGenericProperties(val1, sup1, val2, sup2, val3, sup3, null, null, null, null, null, null);
+    }
+
+    public static Map<String, Supplier<?>> getGenericProperties(
+            String val1, Supplier<?> sup1,
+            String val2, Supplier<?> sup2,
+            String val3, Supplier<?> sup3,
+            String val4, Supplier<?> sup4
+    ) {
+        return getGenericProperties(val1, sup1, val2, sup2, val3, sup3, val4, sup4, null, null, null, null);
+
+    }
+
+    public static Map<String, Supplier<?>> getGenericProperties(
+            String val1, Supplier<?> sup1,
+            String val2, Supplier<?> sup2,
+            String val3, Supplier<?> sup3,
+            String val4, Supplier<?> sup4,
+            String val5, Supplier<?> sup5
+    ) {
+        return getGenericProperties(val1, sup1, val2, sup2, val3, sup3, val4, sup4, val5, sup5, null, null);
+    }
+
+    public static Map<String, Supplier<?>> getGenericProperties(
+            String val1, Supplier<?> sup1,
+            String val2, Supplier<?> sup2,
+            String val3, Supplier<?> sup3,
+            String val4, Supplier<?> sup4,
+            String val5, Supplier<?> sup5,
+            String val6, Supplier<?> sup6
+    ) {
+        final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+
+        final String[] vals = { val1, val2, val3, val4, val5, val6 };
+        final Supplier<?>[] sups = { sup1, sup2, sup3, sup4, sup5, sup6 };
+
+        for (int i=0; i<vals.length && vals[i] != null; i++) {
+            assert(sups[i] != null);
+            if ("base".equals(vals[i])) {
+                Object baseMap = sups[i].get();
+                assert(baseMap instanceof Map);
+                //noinspection unchecked
+                m.putAll((Map<String,Supplier<?>>)baseMap);
+            } else {
+                m.put(vals[i], sups[i]);
+            }
+        }
+
+        return Collections.unmodifiableMap(m);
+    }
+
+    public static <T extends Enum> Supplier<T> safeEnum(T[] values, Supplier<Number> ordinal) {
+        return safeEnum(values, ordinal, null);
+    }
+
+    public static <T extends Enum> Supplier<T> safeEnum(T[] values, Supplier<Number> ordinal, T defaultVal) {
+        int ord = ordinal.get().intValue();
+        return () -> (0 <= ord && ord < values.length) ? values[ord] : defaultVal;
+    }
+
+    public static Supplier<AnnotatedFlag> getBitsAsString(Supplier<Number> flags, final int[] masks, final String[] names) {
+        return () -> new AnnotatedFlag(flags, masks, names);
+    }
+
+    public static class AnnotatedFlag {
+        private final Supplier<Number> value;
+        private final Map<Integer,String> masks = new LinkedHashMap<>();
+
+        AnnotatedFlag(Supplier<Number> value, int[] masks, String[] names) {
+            assert(masks.length == names.length);
+
+            this.value = value;
+            for (int i=0; i<masks.length; i++) {
+                this.masks.put(masks[i], names[i]);
+            }
+        }
+
+        public Supplier<Number> getValue() {
+            return value;
+        }
+
+        public String getDescription() {
+            final int val = value.get().intValue();
+            return masks.entrySet().stream().
+                filter(e -> match(val, e.getKey())).
+                map(Map.Entry::getValue).
+                collect(Collectors.joining(" | "));
+        }
+
+        private static boolean match(final int val, int mask) {
+            return (val & mask) == mask;
+        }
+    }
+}

Propchange: poi/trunk/src/java/org/apache/poi/util/GenericRecordUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java Wed Sep 11 21:24:06 2019
@@ -16,16 +16,19 @@
 ==================================================================== */
 package org.apache.poi.poifs.crypt.agile;
 
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.crypt.ChainingMode;
-import org.apache.poi.poifs.crypt.CipherAlgorithm;
-import org.apache.poi.poifs.crypt.EncryptionHeader;
-import org.apache.poi.poifs.crypt.HashAlgorithm;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import com.microsoft.schemas.office.x2006.encryption.CTDataIntegrity;
 import com.microsoft.schemas.office.x2006.encryption.CTKeyData;
 import com.microsoft.schemas.office.x2006.encryption.EncryptionDocument;
 import com.microsoft.schemas.office.x2006.encryption.STCipherChaining;
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.crypt.ChainingMode;
+import org.apache.poi.poifs.crypt.CipherAlgorithm;
+import org.apache.poi.poifs.crypt.EncryptionHeader;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.util.GenericRecordUtil;
 
 public class AgileEncryptionHeader extends EncryptionHeader implements Cloneable {
     private byte[] encryptedHmacKey;
@@ -132,4 +135,12 @@ public class AgileEncryptionHeader exten
         return other;
     }
 
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "encryptedHmacKey", this::getEncryptedHmacKey,
+            "encryptedHmacValue", this::getEncryptedHmacValue
+        );
+    }
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java Wed Sep 11 21:24:06 2019
@@ -19,24 +19,45 @@
 
 package org.apache.poi.xslf.util;
 
-import java.awt.Dimension;
+import static java.util.Spliterator.NONNULL;
+import static java.util.Spliterator.ORDERED;
+
+import java.awt.AlphaComposite;
 import java.awt.Graphics2D;
 import java.awt.RenderingHints;
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
+import java.io.Closeable;
 import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
 import java.lang.ref.WeakReference;
-import java.util.List;
+import java.util.Collections;
 import java.util.Locale;
 import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators.AbstractSpliterator;
 import java.util.TreeSet;
+import java.util.function.Consumer;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import javax.imageio.ImageIO;
 
+import org.apache.poi.common.usermodel.GenericRecord;
+import org.apache.poi.sl.draw.DrawPictureShape;
 import org.apache.poi.sl.draw.Drawable;
+import org.apache.poi.sl.draw.ImageRenderer;
+import org.apache.poi.sl.usermodel.PictureData;
 import org.apache.poi.sl.usermodel.Slide;
 import org.apache.poi.sl.usermodel.SlideShow;
 import org.apache.poi.sl.usermodel.SlideShowFactory;
+import org.apache.poi.util.GenericRecordJsonWriter;
 
 /**
  * An utility to convert slides of a .pptx slide show to a PNG image
@@ -57,12 +78,14 @@ public class PPTX2PNG {
             (error == null ? "" : ("Error: "+error+"\n")) +
             "Options:\n" +
             "    -scale <float>    scale factor\n" +
+            "    -fixSide <side>   specify side (long,short,width,height) to fix - use <scale> as amount of pixels\n" +
             "    -slide <integer>  1-based index of a slide to render\n" +
             "    -format <type>    png,gif,jpg (,null for testing)\n" +
             "    -outdir <dir>     output directory, defaults to origin of the ppt/pptx file\n" +
             "    -outfile <file>   output filename, defaults to '"+OUTPUT_PAT_REGEX+"'\n" +
             "    -outpat <pattern> output filename pattern, defaults to '"+OUTPUT_PAT_REGEX+"'\n" +
             "                      patterns: basename, slideno, format, ext\n" +
+            "    -dump <file>      dump the annotated records to a file\n" +
             "    -quiet            do not write to console (for normal processing)";
 
         System.out.println(msg);
@@ -82,7 +105,10 @@ public class PPTX2PNG {
         File outdir = null;
         String outfile = null;
         boolean quiet = false;
-        String outpattern = OUTPUT_PAT_REGEX;
+        String outPattern = OUTPUT_PAT_REGEX;
+        File dumpfile = null;
+        String fixSide = "scale";
+
 
         for (int i = 0; i < args.length; i++) {
             String opt = (i+1 < args.length) ? args[i+1] : null;
@@ -108,12 +134,20 @@ public class PPTX2PNG {
                     i++;
                     break;
                 case "-outpat":
-                    outpattern = opt;
+                    outPattern = opt;
                     i++;
                     break;
                 case "-quiet":
                     quiet = true;
                     break;
+                case "-dump":
+                    dumpfile = new File(opt);
+                    i++;
+                    break;
+                case "-fixside":
+                    fixSide = opt.toLowerCase(Locale.ROOT);
+                    i++;
+                    break;
                 default:
                     file = new File(args[i]);
                     break;
@@ -144,28 +178,64 @@ public class PPTX2PNG {
             return;
         }
 
+        if (!"long,short,width,height,scale".contains(fixSide)) {
+            usage("<fixside> must be one of long / short / width / height");
+            return;
+        }
+
         if (!quiet) {
             System.out.println("Processing " + file);
         }
-        try (SlideShow<?, ?> ss = SlideShowFactory.create(file, null, true)) {
-            List<? extends Slide<?, ?>> slides = ss.getSlides();
 
-            Set<Integer> slidenum = slideIndexes(slides.size(), slidenumStr);
 
+        try (MFProxy proxy = initProxy(file)) {
+            final Set<Integer> slidenum = proxy.slideIndexes(slidenumStr);
             if (slidenum.isEmpty()) {
-                usage("slidenum must be either -1 (for all) or within range: [1.." + slides.size() + "] for " + file);
+                usage("slidenum must be either -1 (for all) or within range: [1.." + proxy.getSlideCount() + "] for " + file);
                 return;
             }
 
-            Dimension pgsize = ss.getPageSize();
-            int width = (int) (pgsize.width * scale);
-            int height = (int) (pgsize.height * scale);
-
-            for (Integer slideNo : slidenum) {
-                Slide<?, ?> slide = slides.get(slideNo);
-                String title = slide.getTitle();
+            final Dimension2D pgsize = proxy.getSize();
+            final double lenSide;
+            switch (fixSide) {
+                default:
+                case "scale":
+                    lenSide = 1;
+                    break;
+                case "long":
+                    lenSide = Math.max(pgsize.getWidth(), pgsize.getHeight());
+                    break;
+                case "short":
+                    lenSide = Math.min(pgsize.getWidth(), pgsize.getHeight());
+                    break;
+                case "width":
+                    lenSide = pgsize.getWidth();
+                    break;
+                case "height":
+                    lenSide = pgsize.getHeight();
+                    break;
+            }
+
+            final int width = (int) Math.rint(pgsize.getWidth() * scale / lenSide);
+            final int height = (int) Math.rint(pgsize.getHeight() * scale / lenSide);
+
+
+            for (int slideNo : slidenum) {
+                proxy.setSlideNo(slideNo);
                 if (!quiet) {
-                    System.out.println("Rendering slide " + (slideNo+1) + (title == null ? "" : ": " + title.trim()));
+                    String title = proxy.getTitle();
+                    System.out.println("Rendering slide " + (slideNo + 1) + (title == null ? "" : ": " + title.trim()));
+                }
+
+                GenericRecord gr = proxy.getRoot();
+                if (dumpfile != null) {
+                    try (GenericRecordJsonWriter fw = new GenericRecordJsonWriter(dumpfile)) {
+                        if (gr == null) {
+                            fw.writeError(file.getName()+" doesn't support GenericRecord interface and can't be dumped to a file.");
+                        } else {
+                            fw.write(gr);
+                        }
+                    }
                 }
 
                 BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
@@ -174,19 +244,25 @@ public class PPTX2PNG {
                 // default rendering options
                 graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                 graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+                graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
                 graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
                 graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
                 graphics.setRenderingHint(Drawable.BUFFERED_IMAGE, new WeakReference<>(img));
 
-                graphics.scale(scale, scale);
+                graphics.scale(scale / lenSide, scale / lenSide);
+
+                graphics.setComposite(AlphaComposite.Clear);
+                graphics.fillRect(0, 0, (int)width, (int)height);
+                graphics.setComposite(AlphaComposite.SrcOver);
 
                 // draw stuff
-                slide.draw(graphics);
+                proxy.draw(graphics);
 
                 // save the result
                 if (!"null".equals(format)) {
-                    String inname = String.format(Locale.ROOT, "%04d|%s|%s", slideNo+1, format, file.getName());
-                    String outname = (outfile != null) ? outfile : INPUT_PATTERN.matcher(inname).replaceAll(outpattern);
+                    String inname = String.format(Locale.ROOT, "%04d|%s|%s", slideNo, format, file.getName());
+                    String outpat = (proxy.getSlideCount() > 1 ? outPattern : outPattern.replaceAll("-?\\$\\{slideno\\}", ""));
+                    String outname = (outfile != null) ? outfile : INPUT_PATTERN.matcher(inname).replaceAll(outpat);
                     ImageIO.write(img, format, new File(outdir, outname));
                 }
 
@@ -200,42 +276,181 @@ public class PPTX2PNG {
         }
     }
 
-    private static Set<Integer> slideIndexes(final int slideCount, String range) {
-        Set<Integer> slideIdx = new TreeSet<>();
-        if ("-1".equals(range)) {
-            for (int i=0; i<slideCount; i++) {
-                slideIdx.add(i);
-            }
-        } else {
-            for (String subrange : range.split(",")) {
-                String[] idx = subrange.split("-");
-                switch (idx.length) {
-                default:
-                case 0: break;
-                case 1: {
-                    int subidx = Integer.parseInt(idx[0]);
-                    if (subrange.contains("-")) {
-                        int startIdx = subrange.startsWith("-") ? 0 : subidx;
-                        int endIdx = subrange.endsWith("-") ? slideCount : Math.min(subidx,slideCount);
-                        for (int i=Math.max(startIdx,1); i<endIdx; i++) {
-                            slideIdx.add(i-1);
-                        }
-                    } else {
-                        slideIdx.add(Math.max(subidx,1)-1);
-                    }
-                    break;
-                }
-                case 2: {
-                    int startIdx = Math.min(Integer.parseInt(idx[0]), slideCount);
-                    int endIdx = Math.min(Integer.parseInt(idx[1]), slideCount);
-                    for (int i=Math.max(startIdx,1); i<endIdx; i++) {
-                        slideIdx.add(i-1);
+    private static MFProxy initProxy(File file) throws IOException {
+        MFProxy proxy;
+        final String fileName = file.getName().toLowerCase(Locale.ROOT);
+        switch (fileName.contains(".") ? fileName.substring(fileName.lastIndexOf('.')) : "") {
+            case ".emf":
+                proxy = new EMFHandler();
+                break;
+            case ".wmf":
+                proxy = new WMFHandler();
+                break;
+            default:
+                proxy = new PPTHandler();
+                break;
+        }
+
+        proxy.parse(file);
+        return proxy;
+    }
+
+    private interface MFProxy extends Closeable {
+        void parse(File file) throws IOException;
+//        boolean isEmpty();
+//        void dumpRecords(Writer writer) throws IOException;
+//        Iterable<HwmfEmbedded> getEmbeddings();
+        Dimension2D getSize();
+
+        default void setSlideNo(int slideNo) {}
+
+        String getTitle();
+        void draw(Graphics2D ctx);
+
+        default int getSlideCount() { return 1; }
+
+        default Set<Integer> slideIndexes(String range) {
+            return Collections.singleton(1);
+        }
+
+        GenericRecord getRoot();
+    }
+
+    /** Handler for ppt and pptx files */
+    private static class PPTHandler implements MFProxy {
+        SlideShow<?,?> ppt;
+        Slide<?,?> slide;
+
+        @Override
+        public void parse(File file) throws IOException {
+            ppt = SlideShowFactory.create(file, null, true);
+            slide = ppt.getSlides().get(0);
+        }
+
+        @Override
+        public Dimension2D getSize() {
+            return ppt.getPageSize();
+        }
+
+        @Override
+        public int getSlideCount() {
+            return ppt.getSlides().size();
+        }
+
+        @Override
+        public void setSlideNo(int slideNo) {
+            slide = ppt.getSlides().get(slideNo-1);
+        }
+
+        @Override
+        public String getTitle() {
+            return slide.getTitle();
+        }
+
+        private static final String RANGE_PATTERN = "(^|,)(?<from>\\d+)?(-(?<to>\\d+))?";
+
+        @Override
+        public Set<Integer> slideIndexes(String range) {
+            final Matcher matcher = Pattern.compile(RANGE_PATTERN).matcher(range);
+            Spliterator<Matcher> sp = new AbstractSpliterator<Matcher>(range.length(), ORDERED|NONNULL){
+                @Override
+                public boolean tryAdvance(Consumer<? super Matcher> action) {
+                    boolean b = matcher.find();
+                    if (b) {
+                        action.accept(matcher);
                     }
-                    break;
+                    return b;
                 }
+            };
+
+            return StreamSupport.stream(sp, false).
+                flatMap(this::range).
+                collect(Collectors.toCollection(TreeSet::new));
+        }
+
+        @Override
+        public void draw(Graphics2D ctx) {
+            slide.draw(ctx);
+        }
+
+        @Override
+        public void close() throws IOException {
+            if (ppt != null) {
+                ppt.close();
+            }
+        }
+
+        @Override
+        public GenericRecord getRoot() {
+            return (ppt instanceof GenericRecord) ? (GenericRecord)ppt : null;
+        }
+
+        private Stream<Integer> range(Matcher m) {
+            final int slideCount = ppt.getSlides().size();
+            String fromStr = m.group("from");
+            String toStr = m.group("to");
+            int from = (fromStr == null || fromStr.isEmpty() ? 1 : Integer.parseInt(fromStr));
+            int to = (toStr == null) ? from
+                : (toStr.isEmpty() || ((fromStr == null || fromStr.isEmpty()) && "1".equals(toStr))) ? slideCount
+                : Integer.parseInt(toStr);
+            return IntStream.rangeClosed(from, to).filter(i -> i <= slideCount).boxed();
+        }
+    }
+
+    private static class EMFHandler implements MFProxy {
+        private ImageRenderer imgr = null;
+        private InputStream is;
+
+        @Override
+        public void parse(File file) throws IOException {
+            imgr = DrawPictureShape.getImageRenderer(null, getContentType());
+            // stream needs to be kept open
+            is = file.toURI().toURL().openStream();
+            imgr.loadImage(is, getContentType());
+        }
+
+        protected String getContentType() {
+            return PictureData.PictureType.EMF.contentType;
+        }
+
+        @Override
+        public Dimension2D getSize() {
+            return imgr.getDimension();
+        }
+
+        @Override
+        public String getTitle() {
+            return "";
+        }
+
+        @Override
+        public void draw(Graphics2D ctx) {
+            Dimension2D dim = getSize();
+            imgr.drawImage(ctx, new Rectangle2D.Double(0, 0, dim.getWidth(), dim.getHeight()));
+        }
+
+        @Override
+        public void close() throws IOException {
+            if (is != null) {
+                try {
+                    is.close();
+                } finally {
+                    is = null;
                 }
             }
         }
-        return slideIdx;
+
+        @Override
+        public GenericRecord getRoot() {
+            return imgr.getGenericRecord();
+        }
     }
+
+    private static class WMFHandler extends EMFHandler {
+        @Override
+        protected String getContentType() {
+            return PictureData.PictureType.WMF.contentType;
+        }
+    }
+
 }

Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java (original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java Wed Sep 11 21:24:06 2019
@@ -48,7 +48,7 @@ public class TestPPTX2PNG {
     private static final String files =
         "53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, " +
         "backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt, keyframes.pptx," +
-        "customGeo.pptx, customGeo.ppt";
+        "customGeo.pptx, customGeo.ppt, wrench.emf, santa.wmf";
 
         
     
@@ -83,8 +83,12 @@ public class TestPPTX2PNG {
             "-slide", "-1", // -1 for all
             "-outdir", new File("build/tmp/").getCanonicalPath(),
             "-outpat", "${basename}-${slideno}-${ext}.${format}",
-            "-scale", "1.333333333",
+            // "-dump", new File("build/tmp/", pptFile+".dump").getCanonicalPath(),
+            "-dump", "null",
             "-quiet",
+            "-fixside", "long",
+            "-scale", "800",
+            // "-scale", "1.333333333",
             (basedir == null ? samples.getFile(pptFile) : new File(basedir, pptFile)).getAbsolutePath()
         };
         PPTX2PNG.main(args);

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java Wed Sep 11 21:24:06 2019
@@ -24,11 +24,11 @@ import java.awt.RenderingHints;
 import java.awt.geom.Dimension2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
-import java.awt.image.RescaleOp;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hemf.usermodel.HemfPicture;
 import org.apache.poi.sl.draw.BitmapImageRenderer;
 import org.apache.poi.sl.draw.ImageRenderer;
@@ -109,4 +109,8 @@ public class HemfImageRenderer implement
         }
     }
 
+    @Override
+    public GenericRecord getGenericRecord() {
+        return image;
+    }
 }

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java Wed Sep 11 21:24:06 2019
@@ -24,12 +24,16 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hemf.record.emfplus.HemfPlusRecord;
 import org.apache.poi.hemf.record.emfplus.HemfPlusRecordIterator;
 import org.apache.poi.hwmf.usermodel.HwmfPicture;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianConsts;
@@ -78,10 +82,15 @@ public class HemfComment {
         }
     }
 
-    public interface EmfCommentData {
+    public interface EmfCommentData extends GenericRecord {
         HemfCommentRecordType getCommentRecordType();
 
         long init(LittleEndianInputStream leis, long dataSize) throws IOException;
+
+        @Override
+        default Enum getGenericRecordType() {
+            return getCommentRecordType();
+        }
     }
 
     public static class EmfComment implements HemfRecord {
@@ -116,7 +125,12 @@ public class HemfComment {
 
         @Override
         public String toString() {
-            return "{ data: "+data+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("data", this::getCommentData);
         }
     }
 
@@ -241,7 +255,19 @@ public class HemfComment {
 
         @Override
         public String toString() {
-            return "\""+new String(privateData, LocaleUtil.CHARSET_1252).replaceAll("\\p{Cntrl}", ".")+"\"";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public String getPrivateDataAsString() {
+            return new String(privateData, LocaleUtil.CHARSET_1252);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "privateData", this::getPrivateData,
+                "privateDataAsString", this::getPrivateDataAsString
+            );
         }
     }
 
@@ -271,6 +297,16 @@ public class HemfComment {
         public void draw(HemfGraphics ctx) {
             records.forEach(ctx::draw);
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return null;
+        }
+
+        @Override
+        public List<HemfPlusRecord> getGenericChildren() {
+            return getRecords();
+        }
     }
 
     public static class EmfCommentDataBeginGroup implements EmfCommentData {
@@ -301,6 +337,22 @@ public class HemfComment {
 
             return leis.getReadIndex()-startIdx;
         }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "bounds", this::getBounds,
+                "description", this::getDescription
+            );
+        }
     }
 
     public static class EmfCommentDataEndGroup implements EmfCommentData {
@@ -319,6 +371,11 @@ public class HemfComment {
             assert(publicCommentIdentifier == HemfCommentRecordType.emfEndGroup.id);
             return leis.getReadIndex()-startIdx;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return null;
+        }
     }
 
     public static class EmfCommentDataMultiformats implements EmfCommentData {
@@ -369,6 +426,20 @@ public class HemfComment {
         public List<EmfCommentDataFormat> getFormats() {
             return Collections.unmodifiableList(formats);
         }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("bounds", this::getBounds);
+        }
+
+        @Override
+        public List<EmfCommentDataFormat> getGenericChildren() {
+            return getFormats();
+        }
     }
 
     public enum EmfFormatSignature {
@@ -400,7 +471,7 @@ public class HemfComment {
 
     }
 
-    public static class EmfCommentDataFormat {
+    public static class EmfCommentDataFormat implements GenericRecord {
         private EmfFormatSignature signature;
         private int version;
         private int sizeData;
@@ -439,11 +510,20 @@ public class HemfComment {
         public EmfFormatSignature getSignature() {
             return signature;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "signature", this::getSignature,
+                "version", () -> version,
+                "sizeData", () -> sizeData,
+                "offData", () -> offData
+            );
+        }
     }
 
     public static class EmfCommentDataWMF implements EmfCommentData {
         private final Rectangle2D bounds = new Rectangle2D.Double();
-        private final List<EmfCommentDataFormat> formats = new ArrayList<>();
         private byte[] wmfData;
         @Override
         public HemfCommentRecordType getCommentRecordType() {
@@ -485,12 +565,21 @@ public class HemfComment {
         public byte[] getWMFData() {
             return wmfData;
         }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "bounds", this::getBounds,
+                "wmfData", this::getWMFData
+            );
+        }
     }
 
     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;
@@ -501,5 +590,10 @@ public class HemfComment {
                 throws IOException {
             throw new RecordFormatException("UNICODE_STRING/UNICODE_END values are reserved in CommentPublic records");
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return null;
+        }
     }
 }

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java Wed Sep 11 21:24:06 2019
@@ -17,11 +17,10 @@
 
 package org.apache.poi.hemf.record.emf;
 
-import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
 import static org.apache.poi.hwmf.record.HwmfDraw.normalizeBounds;
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
 
 import java.awt.Shape;
-import java.awt.geom.AffineTransform;
 import java.awt.geom.Arc2D;
 import java.awt.geom.Dimension2D;
 import java.awt.geom.Path2D;
@@ -29,13 +28,17 @@ import java.awt.geom.PathIterator;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
+import java.util.Map;
+import java.util.function.Supplier;
+import java.util.stream.IntStream;
 
 import org.apache.poi.hemf.draw.HemfDrawProperties;
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hwmf.draw.HwmfGraphics.FillDrawStyle;
 import org.apache.poi.hwmf.record.HwmfDraw;
 import org.apache.poi.hwmf.record.HwmfDraw.WmfSelectObject;
-import org.apache.poi.util.Internal;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
@@ -47,26 +50,30 @@ public class HemfDraw {
      */
     public static class EmfSelectObject extends WmfSelectObject implements HemfRecord {
 
-        private static final String[] STOCK_IDS = {
-            "0x80000000 /* WHITE_BRUSH */",
-            "0x80000001 /* LTGRAY_BRUSH */",
-            "0x80000002 /* GRAY_BRUSH */",
-            "0x80000003 /* DKGRAY_BRUSH */",
-            "0x80000004 /* BLACK_BRUSH */",
-            "0x80000005 /* NULL_BRUSH */",
-            "0x80000006 /* WHITE_PEN */",
-            "0x80000007 /* BLACK_PEN */",
-            "0x80000008 /* NULL_PEN */",
-            "0x8000000A /* OEM_FIXED_FONT */",
-            "0x8000000B /* ANSI_FIXED_FONT */",
-            "0x8000000C /* ANSI_VAR_FONT */",
-            "0x8000000D /* SYSTEM_FONT */",
-            "0x8000000E /* DEVICE_DEFAULT_FONT */",
-            "0x8000000F /* DEFAULT_PALETTE */",
-            "0x80000010 /* SYSTEM_FIXED_FONT */",
-            "0x80000011 /* DEFAULT_GUI_FONT */",
-            "0x80000012 /* DC_BRUSH */",
-            "0x80000013 /* DC_PEN */"
+        private static final int[] IDX_MASKS = IntStream.rangeClosed(0x80000000,0x80000013).toArray();
+
+        private static final String[] IDX_NAMES = {
+            "WHITE_BRUSH",
+            "LTGRAY_BRUSH",
+            "GRAY_BRUSH",
+            "DKGRAY_BRUSH",
+            "BLACK_BRUSH",
+            "NULL_BRUSH",
+            "WHITE_PEN",
+            "BLACK_PEN",
+            "NULL_PEN",
+            // 0x80000009 is not a valid stock object
+            "INVALID",
+            "OEM_FIXED_FONT",
+            "ANSI_FIXED_FONT",
+            "ANSI_VAR_FONT",
+            "SYSTEM_FONT",
+            "DEVICE_DEFAULT_FONT",
+            "DEFAULT_PALETTE",
+            "SYSTEM_FIXED_FONT",
+            "DEFAULT_GUI_FONT",
+            "DC_BRUSH",
+            "DC_PEN"
         };
 
         @Override
@@ -84,12 +91,20 @@ public class HemfDraw {
 
         @Override
         public String toString() {
-            return "{ index: "+
-                (((objectIndex & 0x80000000) != 0 && (objectIndex & 0x3FFFFFFF) <= 13 )
-                ? STOCK_IDS[objectIndex & 0x3FFFFFFF]
-                : objectIndex)+" }";
+            return GenericRecordJsonWriter.marshal(this);
         }
 
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "objectIndex", getBitsAsString(this::getObjectIndex, IDX_MASKS, IDX_NAMES)
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
 
@@ -183,6 +198,23 @@ public class HemfDraw {
         public void draw(HemfGraphics ctx) {
             ctx.draw(path -> path.append(poly, !hasStartPoint()), getFillDrawStyle());
         }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "bounds", this::getBounds
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -267,6 +299,23 @@ public class HemfDraw {
         public void draw(HemfGraphics ctx) {
             ctx.draw(path -> path.append(poly, false), getFillDrawStyle());
         }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "bounds", this::getBounds
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -429,10 +478,8 @@ public class HemfDraw {
 
             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.
-                 */
+                // 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(Path2D.WIND_EVEN_ODD, (int)nPoints);
                 for (int i=0; i<nPoints; i++) {
                     size += readPoint(leis, pnt);
@@ -460,6 +507,23 @@ public class HemfDraw {
 
             ctx.draw(path -> path.append(shape, false), getFillDrawStyle());
         }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "bounds", this::getBounds
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -527,6 +591,11 @@ public class HemfDraw {
             size += colorRef.init(leis);
             return size;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -547,6 +616,11 @@ public class HemfDraw {
         public void draw(final HemfGraphics ctx) {
             ctx.draw((path) -> path.moveTo(point.getX(), point.getY()), FillDrawStyle.NONE);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -571,6 +645,11 @@ public class HemfDraw {
         public void draw(HemfGraphics ctx) {
             ctx.draw(path -> path.append(getShape(), false), getFillDrawStyle());
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -596,6 +675,11 @@ public class HemfDraw {
         public void draw(HemfGraphics ctx) {
             ctx.draw(path -> path.append(getShape(), false), getFillDrawStyle());
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -620,6 +704,11 @@ public class HemfDraw {
         public void draw(HemfGraphics ctx) {
             ctx.draw(path -> path.append(getShape(), false), getFillDrawStyle());
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -642,6 +731,11 @@ public class HemfDraw {
         public void draw(HemfGraphics ctx) {
             ctx.draw(path -> path.append(getShape(), false), FillDrawStyle.FILL_DRAW);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -663,6 +757,11 @@ public class HemfDraw {
         public void draw(HemfGraphics ctx) {
             ctx.draw(path -> path.append(normalizeBounds(bounds), false), FillDrawStyle.FILL_DRAW);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -680,8 +779,9 @@ public class HemfDraw {
             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();
+            int width = (int)leis.readUInt();
+            int height = (int)leis.readUInt();
+            corners.setSize(width, height);
 
             return size + 2*LittleEndianConsts.INT_SIZE;
         }
@@ -690,6 +790,11 @@ public class HemfDraw {
         public void draw(HemfGraphics ctx) {
             ctx.draw(path -> path.append(getShape(), false), FillDrawStyle.FILL_DRAW);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -711,6 +816,11 @@ public class HemfDraw {
         public void draw(final HemfGraphics ctx) {
             ctx.draw((path) -> path.lineTo(point.getX(), point.getY()), FillDrawStyle.DRAW);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -736,6 +846,11 @@ public class HemfDraw {
             final Arc2D arc = getShape();
             ctx.draw((path) -> path.append(arc, true), getFillDrawStyle());
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /** The EMR_POLYDRAW record specifies a set of line segments and Bezier curves. */
@@ -829,6 +944,23 @@ public class HemfDraw {
         public void draw(HemfGraphics ctx) {
             ctx.draw(path -> path.append(poly, false), getFillDrawStyle());
         }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "bounds", this::getBounds
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     public static class EmfPolyDraw16 extends EmfPolyDraw {
@@ -852,7 +984,7 @@ public class HemfDraw {
      * When an application processes the EMR_BEGINPATH record, all previous paths
      * MUST be discarded from the playback device context.
      */
-    public static class EmfBeginPath implements HemfRecord {
+    public static class EmfBeginPath implements HemfRecordWithoutProperties {
         @Override
         public HemfRecordType getEmfRecordType() {
             return HemfRecordType.beginPath;
@@ -880,7 +1012,7 @@ public class HemfDraw {
      * This record closes a path bracket and selects the path defined by the bracket into
      * the playback device context.
      */
-    public static class EmfEndPath implements HemfRecord {
+    public static class EmfEndPath implements HemfRecordWithoutProperties {
         @Override
         public HemfRecordType getEmfRecordType() {
             return HemfRecordType.endPath;
@@ -906,7 +1038,7 @@ public class HemfDraw {
     /**
      * This record aborts a path bracket or discards the path from a closed path bracket.
      */
-    public static class EmfAbortPath implements HemfRecord {
+    public static class EmfAbortPath implements HemfRecordWithoutProperties {
         @Override
         public HemfRecordType getEmfRecordType() {
             return HemfRecordType.abortPath;
@@ -949,7 +1081,7 @@ public class HemfDraw {
      * After processing the EMR_CLOSEFIGURE record, adding a line or curve to the path
      * MUST start a new figure.
      */
-    public static class EmfCloseFigure implements HemfRecord {
+    public static class EmfCloseFigure implements HemfRecordWithoutProperties {
         @Override
         public HemfRecordType getEmfRecordType() {
             return HemfRecordType.closeFigure;
@@ -980,7 +1112,7 @@ public class HemfDraw {
      * This record transforms any curves in the selected path into the playback device
      * context; each curve MUST be turned into a sequence of lines.
      */
-    public static class EmfFlattenPath implements HemfRecord {
+    public static class EmfFlattenPath implements HemfRecordWithoutProperties {
         @Override
         public HemfRecordType getEmfRecordType() {
             return HemfRecordType.flattenPath;
@@ -996,7 +1128,7 @@ public class HemfDraw {
      * This record redefines the current path as the area that would be painted if the path
      * were drawn using the pen currently selected into the playback device context.
      */
-    public static class EmfWidenPath implements HemfRecord {
+    public static class EmfWidenPath implements HemfRecordWithoutProperties {
         @Override
         public HemfRecordType getEmfRecordType() {
             return HemfRecordType.widenPath;
@@ -1040,7 +1172,16 @@ public class HemfDraw {
 
         @Override
         public String toString() {
-            return boundsToString(bounds);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("bounds", this::getBounds);
         }
     }
 
@@ -1155,16 +1296,4 @@ public class HemfDraw {
 
         ctx.draw((path) -> path.append(pi, true), fillDrawStyle);
     }
-
-
-    @Internal
-    public static String xformToString(AffineTransform xForm) {
-        return (xForm == null) ? "null" :
-            "{ scaleX: "+xForm.getScaleX()+
-            ", shearX: "+xForm.getShearX()+
-            ", transX: "+xForm.getTranslateX()+
-            ", scaleY: "+xForm.getScaleY()+
-            ", shearY: "+xForm.getShearY()+
-            ", transY: "+xForm.getTranslateY()+" }";
-    }
 }

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java?rev=1866808&r1=1866807&r2=1866808&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java Wed Sep 11 21:24:06 2019
@@ -19,10 +19,7 @@ package org.apache.poi.hemf.record.emf;
 
 import static org.apache.poi.hemf.record.emf.HemfDraw.readPointL;
 import static org.apache.poi.hemf.record.emf.HemfDraw.readRectL;
-import static org.apache.poi.hemf.record.emf.HemfDraw.xformToString;
 import static org.apache.poi.hemf.record.emf.HemfRecordIterator.HEADER_SIZE;
-import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
-import static org.apache.poi.hwmf.record.HwmfDraw.pointToString;
 
 import java.awt.Shape;
 import java.awt.geom.AffineTransform;
@@ -33,7 +30,11 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hemf.draw.HemfDrawProperties;
 import org.apache.poi.hemf.draw.HemfGraphics;
@@ -45,6 +46,8 @@ import org.apache.poi.hwmf.record.HwmfFi
 import org.apache.poi.hwmf.record.HwmfFill.ColorUsage;
 import org.apache.poi.hwmf.record.HwmfRegionMode;
 import org.apache.poi.hwmf.record.HwmfTernaryRasterOp;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianConsts;
@@ -70,6 +73,11 @@ public class HemfFill {
             polyFillMode = HwmfPolyfillMode.valueOf((int)leis.readUInt());
             return LittleEndianConsts.INT_SIZE;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     public static class EmfExtFloodFill extends HwmfFill.WmfExtFloodFill implements HemfRecord {
@@ -85,9 +93,14 @@ public class HemfFill {
             size += colorRef.init(leis);
             // A 32-bit unsigned integer that specifies how to use the Color value to determine the area for
             // the flood fill operation. The value MUST be in the FloodFill enumeration
-            mode = (int)leis.readUInt();
+            mode = HwmfFloodFillMode.values()[(int)leis.readUInt()];
             return size + LittleEndianConsts.INT_SIZE;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -185,11 +198,34 @@ public class HemfFill {
 
         @Override
         public String toString() {
-            return
-                "{ bounds: "+boundsToString(bounds)+
-                ", xFormSrc: " + xformToString(xFormSrc) +
-                ", bkColorSrc: "+bkColorSrc+
-                ","+super.toString().substring(1);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        public AffineTransform getXFormSrc() {
+            return xFormSrc;
+        }
+
+        public HwmfColorRef getBkColorSrc() {
+            return bkColorSrc;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "bounds", this::getBounds,
+                "xFormSrc", this::getXFormSrc,
+                "bkColorSrc", this::getBkColorSrc
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
         }
     }
 
@@ -259,6 +295,23 @@ public class HemfFill {
 
             return size;
         }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "bounds", this::getBounds
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -296,9 +349,10 @@ public class HemfFill {
             // A 32-bit unsigned integer that specifies the brush EMF Object Table index.
             brushIndex = (int)leis.readUInt();
             // A 32-bit signed integer that specifies the width of the vertical brush stroke, in logical units.
-            width = leis.readInt();
+            int width = leis.readInt();
             // A 32-bit signed integer that specifies the height of the horizontal brush stroke, in logical units.
-            height = leis.readInt();
+            int height = leis.readInt();
+            frame.setSize(width,height);
             size += 4*LittleEndianConsts.INT_SIZE;
             size += readRgnData(leis, rgnRects);
             return size;
@@ -313,6 +367,28 @@ public class HemfFill {
         protected Shape getShape() {
             return getRgnShape(rgnRects);
         }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        public List<Rectangle2D> getRgnRects() {
+            return rgnRects;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "bounds", this::getBounds,
+                "rgnRects", this::getRgnRects
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /** The EMR_INVERTRGN record inverts the colors in the specified region. */
@@ -338,6 +414,22 @@ public class HemfFill {
         protected Shape getShape() {
             return getRgnShape(rgnRects);
         }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        public List<Rectangle2D> getRgnRects() {
+            return rgnRects;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "bounds", this::getBounds,
+                "rgnRects", this::getRgnRects
+            );
+        }
     }
 
     /**
@@ -375,6 +467,28 @@ public class HemfFill {
         protected Shape getShape() {
             return getRgnShape(rgnRects);
         }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        public List<Rectangle2D> getRgnRects() {
+            return rgnRects;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "bounds", this::getBounds,
+                "rgnRects", this::getRgnRects
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     public static class EmfExtSelectClipRgn implements HemfRecord {
@@ -414,19 +528,23 @@ public class HemfFill {
 
         @Override
         public String toString() {
-            StringBuilder sb = new StringBuilder();
-            sb.append("{ regionMode: '"+regionMode+"'");
-            sb.append(", regions: [");
-            boolean isFirst = true;
-            for (Rectangle2D r : rgnRects) {
-                if (!isFirst) {
-                    sb.append(",");
-                }
-                isFirst = false;
-                sb.append(boundsToString(r));
-            }
-            sb.append("]}");
-            return sb.toString();
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfRegionMode getRegionMode() {
+            return regionMode;
+        }
+
+        public List<Rectangle2D> getRgnRects() {
+            return rgnRects;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "regionMode", this::getRegionMode,
+                "rgnRects", this::getRgnRects
+            );
         }
     }
 
@@ -539,6 +657,23 @@ public class HemfFill {
 
             return size;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+            m.put("bounds", () -> bounds);
+            m.put("destRect", () -> destRect);
+            m.put("srcRect", () -> srcRect);
+            m.put("blendOperation", () -> blendOperation);
+            m.put("blendFlags", () -> blendFlags);
+            m.put("srcConstantAlpha", () -> srcConstantAlpha);
+            m.put("alphaFormat", () -> alphaFormat);
+            m.put("xFormSrc", () -> xFormSrc);
+            m.put("bkColorSrc", () -> bkColorSrc);
+            m.put("usageSrc", () -> usageSrc);
+            m.put("bitmap", () -> bitmap);
+            return Collections.unmodifiableMap(m);
+        }
     }
 
     /**
@@ -591,15 +726,40 @@ public class HemfFill {
             return size;
         }
 
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        public Point2D getDest() {
+            return dest;
+        }
+
+        public Rectangle2D getSrc() {
+            return src;
+        }
+
+        public ColorUsage getUsageSrc() {
+            return usageSrc;
+        }
+
+        public HwmfBitmapDib getBitmap() {
+            return bitmap;
+        }
+
         @Override
         public String toString() {
-            return
-                "{ bounds: " + boundsToString(bounds) +
-                ", dest: " + pointToString(dest) +
-                ", src: " + boundsToString(src) +
-                ", usageSrc: '" + usageSrc + "'" +
-                ", bitmap: " + bitmap +
-                "}";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "bounds", this::getBounds,
+                "dest", this::getDest,
+                "src", this::getSrc,
+                "usageSrc", this::getUsageSrc,
+                "bitmap", this::getBitmap
+            );
         }
     }
 
@@ -670,9 +830,7 @@ public class HemfFill {
 
 
     static int readBounds2(LittleEndianInputStream leis, Rectangle2D bounds) {
-        /**
-         * The 32-bit signed integers that defines the corners of the bounding rectangle.
-         */
+        // The 32-bit signed integers that defines the corners of the bounding rectangle.
         int x = leis.readInt();
         int y = leis.readInt();
         int w = leis.readInt();



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