]> source.dussan.org Git - poi.git/commitdiff
#63745 - Add traversing and debugging interface
authorAndreas Beeker <kiwiwings@apache.org>
Wed, 11 Sep 2019 21:24:06 +0000 (21:24 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Wed, 11 Sep 2019 21:24:06 +0000 (21:24 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1866808 13f79535-47bb-0310-9956-ffa450edef68

165 files changed:
src/java/org/apache/poi/common/usermodel/GenericRecord.java [new file with mode: 0644]
src/java/org/apache/poi/common/usermodel/fonts/FontHeader.java
src/java/org/apache/poi/ddf/AbstractEscherOptRecord.java
src/java/org/apache/poi/ddf/EscherBSERecord.java
src/java/org/apache/poi/ddf/EscherBitmapBlip.java
src/java/org/apache/poi/ddf/EscherBlipRecord.java
src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java
src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java
src/java/org/apache/poi/ddf/EscherClientDataRecord.java
src/java/org/apache/poi/ddf/EscherContainerRecord.java
src/java/org/apache/poi/ddf/EscherDgRecord.java
src/java/org/apache/poi/ddf/EscherDggRecord.java
src/java/org/apache/poi/ddf/EscherMetafileBlip.java
src/java/org/apache/poi/ddf/EscherOptRecord.java
src/java/org/apache/poi/ddf/EscherPictBlip.java
src/java/org/apache/poi/ddf/EscherRecord.java
src/java/org/apache/poi/ddf/EscherRecordTypes.java [new file with mode: 0644]
src/java/org/apache/poi/ddf/EscherSpRecord.java
src/java/org/apache/poi/ddf/EscherSpgrRecord.java
src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java
src/java/org/apache/poi/ddf/EscherTertiaryOptRecord.java
src/java/org/apache/poi/ddf/EscherTextboxRecord.java
src/java/org/apache/poi/ddf/UnknownEscherRecord.java
src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java
src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java
src/java/org/apache/poi/poifs/crypt/Decryptor.java
src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java
src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java
src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java
src/java/org/apache/poi/poifs/crypt/Encryptor.java
src/java/org/apache/poi/sl/draw/DrawPictureShape.java
src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
src/java/org/apache/poi/sl/draw/ImageRenderer.java
src/java/org/apache/poi/sl/usermodel/TextShape.java
src/java/org/apache/poi/util/GenericRecordJsonWriter.java [new file with mode: 0644]
src/java/org/apache/poi/util/GenericRecordUtil.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java
src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java
src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java
src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfHeader.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPalette.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java
src/scratchpad/src/org/apache/poi/hemf/record/emf/UnimplementedHemfRecord.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusFont.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusImage.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java
src/scratchpad/src/org/apache/poi/hemf/record/emfplus/UnimplementedHemfPlusRecord.java
src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/BitMaskTextProp.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/FontAlignmentProp.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/IndentProp.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextAlignmentProp.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPFException9.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java
src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/CString.java
src/scratchpad/src/org/apache/poi/hslf/record/ColorSchemeAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/Comment2000.java
src/scratchpad/src/org/apache/poi/hslf/record/Comment2000Atom.java
src/scratchpad/src/org/apache/poi/hslf/record/DocInfoListContainer.java
src/scratchpad/src/org/apache/poi/hslf/record/DocumentAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/EscherPlaceholder.java
src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java
src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/ExEmbedAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java
src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlinkAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/ExObjList.java
src/scratchpad/src/org/apache/poi/hslf/record/ExObjListAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/ExObjRefAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java
src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java
src/scratchpad/src/org/apache/poi/hslf/record/FontEmbeddedData.java
src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersContainer.java
src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfo.java
src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfoAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/MasterTextPropAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/NotesAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/OutlineTextRefAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java
src/scratchpad/src/org/apache/poi/hslf/record/PPDrawingGroup.java
src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java
src/scratchpad/src/org/apache/poi/hslf/record/Record.java
src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java
src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
src/scratchpad/src/org/apache/poi/hslf/record/RoundTripHFPlaceholder12.java
src/scratchpad/src/org/apache/poi/hslf/record/SSSlideInfoAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/SlideAtomLayout.java
src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java
src/scratchpad/src/org/apache/poi/hslf/record/SlidePersistAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/SoundData.java
src/scratchpad/src/org/apache/poi/hslf/record/StyleTextProp9Atom.java
src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/TextCharsAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoRun.java
src/scratchpad/src/org/apache/poi/hslf/record/TxInteractiveInfoAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/TxMasterStyleAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/UnknownRecordPlaceholder.java
src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/VBAInfoAtom.java
src/scratchpad/src/org/apache/poi/hslf/record/VBAInfoContainer.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeFactory.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlide.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java
src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java
src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmap16.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfEscape.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfHeader.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecord.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java
src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java
src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java
src/scratchpad/testcases/org/apache/poi/hslf/record/TestRecordTypes.java
src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextHeaderAtom.java
src/scratchpad/testcases/org/apache/poi/hslf/record/TestTxMasterStyleAtom.java
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextShape.java
src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java
src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java

diff --git a/src/java/org/apache/poi/common/usermodel/GenericRecord.java b/src/java/org/apache/poi/common/usermodel/GenericRecord.java
new file mode 100644 (file)
index 0000000..e029bf9
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *  ====================================================================
+ *    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.common.usermodel;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+
+public interface GenericRecord {
+    default Enum getGenericRecordType() { return null; }
+
+    Map<String, Supplier<?>> getGenericProperties();
+
+    default List<? extends GenericRecord> getGenericChildren() { return null; }
+}
index 9777f0cc7589b8d7100f244f090055d6a1c9a908..2589b16f17d8414128a454ea10f8b74e2a852241 100644 (file)
 
 package org.apache.poi.common.usermodel.fonts;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+import static org.apache.poi.util.GenericRecordUtil.safeEnum;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndianByteArrayInputStream;
 import org.apache.poi.util.LittleEndianInput;
@@ -34,8 +42,76 @@ import org.apache.poi.util.LittleEndianInputStream;
  *
  * @see <a href="http://www.w3.org/Submission/EOT">Embedded OpenType (EOT) File Format</a>
  */
-@SuppressWarnings({"FieldCanBeLocal", "unused", "Duplicates"})
-public class FontHeader implements FontInfo {
+@SuppressWarnings({"FieldCanBeLocal", "unused", "Duplicates", "WeakerAccess"})
+public class FontHeader implements FontInfo, GenericRecord {
+
+    public enum PanoseFamily {
+        ANY, NO_FIT, TEXT_DISPLAY, SCRIPT, DECORATIVE, PICTORIAL
+    }
+
+    public enum PanoseSerif {
+        ANY, NO_FIT, COVE, OBTUSE_COVE, SQUARE_COVE, OBTUSE_SQUARE_COVE, SQUARE, THIN, BONE,
+        EXAGGERATED, TRIANGLE, NORMAL_SANS, OBTUSE_SANS, PERP_SANS, FLARED, ROUNDED
+    }
+
+    public enum PanoseWeight {
+        ANY, NO_FIT, VERY_LIGHT, LIGHT, THIN, BOOK, MEDIUM, DEMI, BOLD, HEAVY, BLACK, NORD
+    }
+
+    public enum PanoseProportion {
+        ANY, NO_FIT, OLD_STYLE, MODERN, EVEN_WIDTH, EXPANDED, CONDENSED, VERY_EXPANDED, VERY_CONDENSED, MONOSPACED
+    }
+
+    public enum PanoseContrast {
+        ANY, NO_FIT, NONE, VERY_LOW, LOW, MEDIUM_LOW, MEDIUM, MEDIUM_HIGH, HIGH, VERY_HIGH
+    }
+
+    public enum PanoseStroke {
+        ANY, NO_FIT, GRADUAL_DIAG, GRADUAL_TRAN, GRADUAL_VERT, GRADUAL_HORZ, RAPID_VERT, RAPID_HORZ, INSTANT_VERT
+    }
+
+    public enum PanoseArmStyle {
+        ANY, NO_FIT, STRAIGHT_ARMS_HORZ, STRAIGHT_ARMS_WEDGE, STRAIGHT_ARMS_VERT, STRAIGHT_ARMS_SINGLE_SERIF,
+        STRAIGHT_ARMS_DOUBLE_SERIF, BENT_ARMS_HORZ, BENT_ARMS_WEDGE, BENT_ARMS_VERT, BENT_ARMS_SINGLE_SERIF,
+        BENT_ARMS_DOUBLE_SERIF,
+    }
+
+    public enum PanoseLetterForm {
+        ANY, NO_FIT, NORMAL_CONTACT, NORMAL_WEIGHTED, NORMAL_BOXED, NORMAL_FLATTENED, NORMAL_ROUNDED,
+        NORMAL_OFF_CENTER, NORMAL_SQUARE, OBLIQUE_CONTACT, OBLIQUE_WEIGHTED, OBLIQUE_BOXED, OBLIQUE_FLATTENED,
+        OBLIQUE_ROUNDED, OBLIQUE_OFF_CENTER, OBLIQUE_SQUARE
+    }
+
+    public enum PanoseMidLine {
+        ANY, NO_FIT, STANDARD_TRIMMED, STANDARD_POINTED, STANDARD_SERIFED, HIGH_TRIMMED, HIGH_POINTED, HIGH_SERIFED,
+        CONSTANT_TRIMMED, CONSTANT_POINTED, CONSTANT_SERIFED, LOW_TRIMMED, LOW_POINTED, LOW_SERIFED
+    }
+
+    public enum PanoseXHeight {
+        ANY, NO_FIT, CONSTANT_SMALL, CONSTANT_STD, CONSTANT_LARGE, DUCKING_SMALL, DUCKING_STD, DUCKING_LARGE
+    }
+
+    private static final int[] FLAGS_MASKS = {
+        0x00000001, 0x00000004, 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x10000000
+    };
+
+    private static final String[] FLAGS_NAMES = {
+        "SUBSET", "TTCOMPRESSED", "FAILIFVARIATIONSIMULATED", "EMBEDEUDC", "VALIDATIONTESTS", "WEBOBJECT", "XORENCRYPTDATA"
+    };
+
+    private static final int[] FSTYPE_MASKS = {
+        0x0000, 0x0002, 0x0004, 0x0008, 0x0100, 0x0200
+    };
+
+    private static final String[] FSTYPE_NAMES = {
+        "INSTALLABLE_EMBEDDING",
+        "RESTRICTED_LICENSE_EMBEDDING",
+        "PREVIEW_PRINT_EMBEDDING",
+        "EDITABLE_EMBEDDING",
+        "NO_SUBSETTING",
+        "BITMAP_EMBEDDING_ONLY"
+    };
+
     /**
      * Fonts with a font weight of 400 are regarded as regular weighted.
      * Higher font weights (up to 1000) are bold - lower weights are thin.
@@ -142,53 +218,54 @@ public class FontHeader implements FontInfo {
     }
 
     public FontPitch getPitch() {
-        byte familyKind = panose[0];
-        switch (familyKind) {
+        switch (getPanoseFamily()) {
             default:
-            // Any
-            case 0:
-            // No Fit
-            case 1:
+            case ANY:
+            case NO_FIT:
                 return FontPitch.VARIABLE;
 
             // Latin Text
-            case 2:
-                // Latin Decorative
-            case 4:
-                byte proportion = panose[3];
-                return proportion == 9 ? FontPitch.FIXED : FontPitch.VARIABLE;
+            case TEXT_DISPLAY:
+            // Latin Decorative
+            case DECORATIVE:
+                return (getPanoseProportion() == PanoseProportion.MONOSPACED) ? FontPitch.FIXED : FontPitch.VARIABLE;
 
             // Latin Hand Written
-            case 3:
-                // Latin Symbol
-            case 5:
-                byte spacing = panose[3];
-                return spacing == 3 ? FontPitch.FIXED : FontPitch.VARIABLE;
+            case SCRIPT:
+            // Latin Symbol
+            case PICTORIAL:
+                return (getPanoseProportion() == PanoseProportion.MODERN) ? FontPitch.FIXED : FontPitch.VARIABLE;
         }
 
     }
 
     public FontFamily getFamily() {
-        switch (panose[0]) {
-            // Any
-            case 0:
-            // No Fit
-            case 1:
+        switch (getPanoseFamily()) {
+            case ANY:
+            case NO_FIT:
                 return FontFamily.FF_DONTCARE;
             // Latin Text
-            case 2:
-                byte serifStyle = panose[1];
-                return (10 <= serifStyle && serifStyle <= 15)
-                    ? FontFamily.FF_SWISS : FontFamily.FF_ROMAN;
+            case TEXT_DISPLAY:
+                switch (getPanoseSerif()) {
+                    case TRIANGLE:
+                    case NORMAL_SANS:
+                    case OBTUSE_SANS:
+                    case PERP_SANS:
+                    case FLARED:
+                    case ROUNDED:
+                        return FontFamily.FF_SWISS;
+                    default:
+                        return FontFamily.FF_ROMAN;
+                }
             // Latin Hand Written
-            case 3:
+            case SCRIPT:
                 return FontFamily.FF_SCRIPT;
             // Latin Decorative
             default:
-            case 4:
+            case DECORATIVE:
                 return FontFamily.FF_DECORATIVE;
             // Latin Symbol
-            case 5:
+            case PICTORIAL:
                 return FontFamily.FF_MODERN;
         }
     }
@@ -221,7 +298,79 @@ public class FontHeader implements FontInfo {
     public int getFlags() {
         return flags;
     }
-}
 
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+        m.put("eotSize", () -> eotSize);
+        m.put("fontDataSize", () -> fontDataSize);
+        m.put("version", () -> version);
+        m.put("flags", getBitsAsString(this::getFlags, FLAGS_MASKS, FLAGS_NAMES));
+        m.put("panose.familyType", this::getPanoseFamily);
+        m.put("panose.serifType", this::getPanoseSerif);
+        m.put("panose.weight", this::getPanoseWeight);
+        m.put("panose.proportion", this::getPanoseProportion);
+        m.put("panose.contrast", this::getPanoseContrast);
+        m.put("panose.stroke", this::getPanoseStroke);
+        m.put("panose.armStyle", this::getPanoseArmStyle);
+        m.put("panose.letterForm", this::getPanoseLetterForm);
+        m.put("panose.midLine", this::getPanoseMidLine);
+        m.put("panose.xHeight", this::getPanoseXHeight);
+        m.put("charset", this::getCharset);
+        m.put("italic", this::isItalic);
+        m.put("weight", this::getWeight);
+        m.put("fsType", getBitsAsString(() -> fsType, FSTYPE_MASKS, FSTYPE_NAMES));
+        m.put("unicodeRange1", () -> unicodeRange1);
+        m.put("unicodeRange2", () -> unicodeRange2);
+        m.put("unicodeRange3", () -> unicodeRange3);
+        m.put("unicodeRange4", () -> unicodeRange4);
+        m.put("codePageRange1", () -> codePageRange1);
+        m.put("codePageRange2", () -> codePageRange2);
+        m.put("checkSumAdjustment", () -> checkSumAdjustment);
+        m.put("familyName", this::getFamilyName);
+        m.put("styleName", this::getStyleName);
+        m.put("versionName", this::getVersionName);
+        m.put("fullName", this::getFullName);
+        return Collections.unmodifiableMap(m);
+    }
 
+    public PanoseFamily getPanoseFamily() {
+        return safeEnum(PanoseFamily.values(), () -> panose[0]).get();
+    }
+
+    public PanoseSerif getPanoseSerif() {
+        return safeEnum(PanoseSerif.values(), () -> panose[1]).get();
+    }
+
+    public PanoseWeight getPanoseWeight() {
+        return safeEnum(PanoseWeight.values(), () -> panose[2]).get();
+    }
+
+    public PanoseProportion getPanoseProportion() {
+        return safeEnum(PanoseProportion.values(), () -> panose[3]).get();
+    }
 
+    public PanoseContrast getPanoseContrast() {
+        return safeEnum(PanoseContrast.values(), () -> panose[4]).get();
+    }
+
+    public PanoseStroke getPanoseStroke() {
+        return safeEnum(PanoseStroke.values(), () -> panose[5]).get();
+    }
+
+    public PanoseArmStyle getPanoseArmStyle() {
+        return safeEnum(PanoseArmStyle.values(), () -> panose[6]).get();
+    }
+
+    public PanoseLetterForm getPanoseLetterForm() {
+        return safeEnum(PanoseLetterForm.values(), () -> panose[7]).get();
+    }
+
+    public PanoseMidLine getPanoseMidLine() {
+        return safeEnum(PanoseMidLine.values(), () -> panose[8]).get();
+    }
+
+    public PanoseXHeight getPanoseXHeight() {
+        return safeEnum(PanoseXHeight.values(), () -> panose[9]).get();
+    }
+}
index bd1da1db8d66e224b566f0f67aa5413c45a998f4..b3643ff172db087597038a4c3a7ecea69cf028a3 100644 (file)
@@ -18,9 +18,11 @@ package org.apache.poi.ddf;
 
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -29,7 +31,7 @@ import org.apache.poi.util.LittleEndian;
  */
 public abstract class AbstractEscherOptRecord extends EscherRecord
 {
-    private List<EscherProperty> properties = new ArrayList<>();
+    private final List<EscherProperty> properties = new ArrayList<>();
 
     /**
      * Add a property to this record.
@@ -50,7 +52,8 @@ public abstract class AbstractEscherOptRecord extends EscherRecord
         int pos = offset + 8;
 
         EscherPropertyFactory f = new EscherPropertyFactory();
-        properties = f.createProperties( data, pos, propertiesCount );
+        properties.clear();
+        properties.addAll( f.createProperties( data, pos, propertiesCount ) );
         return bytesRemaining + 8;
     }
 
@@ -132,16 +135,8 @@ public abstract class AbstractEscherOptRecord extends EscherRecord
     /**
      * Records should be sorted by property number before being stored.
      */
-    public void sortProperties()
-    {
-        properties.sort(new Comparator<EscherProperty>() {
-            @Override
-            public int compare(EscherProperty p1, EscherProperty p2) {
-                short s1 = p1.getPropertyNumber();
-                short s2 = p2.getPropertyNumber();
-                return Short.compare(s1, s2);
-            }
-        });
+    public void sortProperties() {
+        properties.sort(Comparator.comparingInt(EscherProperty::getPropertyNumber));
     }
 
     /**
@@ -151,24 +146,13 @@ public abstract class AbstractEscherOptRecord extends EscherRecord
      * @param value the property to set.
      */
     public void setEscherProperty(EscherProperty value){
-        for ( Iterator<EscherProperty> iterator =
-                      properties.iterator(); iterator.hasNext(); ) {
-            EscherProperty prop = iterator.next();
-            if (prop.getId() == value.getId()){
-                iterator.remove();
-            }
-        }
+        properties.removeIf(prop -> prop.getId() == value.getId());
         properties.add( value );
         sortProperties();
     }
 
     public void removeEscherProperty(int num){
-        for ( Iterator<EscherProperty> iterator = getEscherProperties().iterator(); iterator.hasNext(); ) {
-            EscherProperty prop = iterator.next();
-            if (prop.getPropertyNumber() == num){
-                iterator.remove();
-            }
-        }
+        properties.removeIf(prop -> prop.getPropertyNumber() == num);
     }
 
     @Override
@@ -187,4 +171,12 @@ public abstract class AbstractEscherOptRecord extends EscherRecord
             attrList.toArray()
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "isContainer", this::isContainerRecord
+        );
+    }
 }
index d3e0514be71ee6ccf497c3f5d8b20165478dccbf..c00c0e2812784c671dd69624da17cafff0374ed4 100644 (file)
 
 package org.apache.poi.ddf;
 
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -32,8 +37,7 @@ public final class EscherBSERecord extends EscherRecord {
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 100_000;
 
-    public static final short RECORD_ID = (short) 0xF007;
-    public static final String RECORD_DESCRIPTION = "MsofbtBSE";
+    public static final short RECORD_ID = EscherRecordTypes.BSE.typeID;
 
     public static final byte BT_ERROR = 0;
     public static final byte BT_UNKNOWN = 1;
@@ -147,7 +151,7 @@ public final class EscherBSERecord extends EscherRecord {
 
     @Override
     public String getRecordName() {
-        return "BSE";
+        return EscherRecordTypes.BSE.recordName;
     }
 
     /**
@@ -401,4 +405,28 @@ public final class EscherBSERecord extends EscherRecord {
             { "Extra Data", _remainingData }
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String, Supplier<?>> m = new LinkedHashMap<>(super.getGenericProperties());
+        m.put("blipTypeWin32", this::getBlipTypeWin32);
+        m.put("blipTypeMacOS", this::getBlipTypeMacOS);
+        m.put("suid", this::getUid);
+        m.put("tag", this::getTag);
+        m.put("size", this::getSize);
+        m.put("ref", this::getRef);
+        m.put("offset", this::getOffset);
+        m.put("usage", this::getUsage);
+        m.put("name", this::getName);
+        m.put("unused2", this::getUnused2);
+        m.put("unused3", this::getUnused3);
+        m.put("blipRecord", this::getBlipRecord);
+        m.put("remainingData", this::getRemainingData);
+        return Collections.unmodifiableMap(m);
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.BSE;
+    }
 }
index 05ad9ae66fe5193243925486f56513161b6b1c62..04f08433491f7857bb03681315e72922efb904e5 100644 (file)
 
 package org.apache.poi.ddf;
 
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
 import org.apache.poi.util.LittleEndian;
 
 public class EscherBitmapBlip extends EscherBlipRecord {
-    public static final short RECORD_ID_JPEG = (short) 0xF018 + 5;
-    public static final short RECORD_ID_PNG = (short) 0xF018 + 6;
-    public static final short RECORD_ID_DIB = (short) 0xF018 + 7;
+    public static final short RECORD_ID_JPEG = EscherRecordTypes.BLIP_JPEG.typeID;
+    public static final short RECORD_ID_PNG = EscherRecordTypes.BLIP_PNG.typeID;
+    public static final short RECORD_ID_DIB = EscherRecordTypes.BLIP_DIB.typeID;
 
     private static final int HEADER_SIZE = 8;
 
@@ -115,4 +120,14 @@ public class EscherBitmapBlip extends EscherBlipRecord {
             { "Extra Data", getPicturedata() }
         };
     }
+
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String, Supplier<?>> m = new LinkedHashMap<>(super.getGenericProperties());
+        m.put("uid", this::getUID);
+        m.put("marker", this::getMarker);
+        return Collections.unmodifiableMap(m);
+    }
+
 }
index f49f09f0e8e51fc37c264bcbc39df1a35e71ac6a..41464b3c19065080a667145fac7e77e2fe1e5200 100644 (file)
 
 package org.apache.poi.ddf;
 
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -25,9 +29,8 @@ public class EscherBlipRecord extends EscherRecord {
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 104_857_600;
 
-    public static final short  RECORD_ID_START    = (short) 0xF018;
-    public static final short  RECORD_ID_END      = (short) 0xF117;
-    public static final String RECORD_DESCRIPTION = "msofbtBlip";
+    public static final short  RECORD_ID_START    = EscherRecordTypes.BLIP_START.typeID;
+    public static final short  RECORD_ID_END      = EscherRecordTypes.BLIP_END.typeID;
 
     private static final int   HEADER_SIZE               = 8;
 
@@ -67,7 +70,8 @@ public class EscherBlipRecord extends EscherRecord {
 
     @Override
     public String getRecordName() {
-        return "Blip";
+        EscherRecordTypes t = EscherRecordTypes.forTypeID(getRecordId());
+        return (t != EscherRecordTypes.UNKNOWN ? t : EscherRecordTypes.BLIP_START).recordName;
     }
 
     /**
@@ -109,4 +113,18 @@ public class EscherBlipRecord extends EscherRecord {
             { "Extra Data", getPicturedata() }
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "pictureData", this::getPicturedata
+        );
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        EscherRecordTypes t = EscherRecordTypes.forTypeID(getRecordId());
+        return (t != EscherRecordTypes.UNKNOWN) ? t : EscherRecordTypes.BLIP_START;
+    }
 }
index b7a7a8d78c4c68eb840cb59d26e1735339686a0f..152ab213f7e9703de4e46a4d5a2261d0b065ac6d 100644 (file)
 
 package org.apache.poi.ddf;
 
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -26,11 +30,8 @@ import org.apache.poi.util.LittleEndian;
  *
  * @see EscherChildAnchorRecord
  */
-public class EscherChildAnchorRecord
-        extends EscherRecord
-{
-    public static final short RECORD_ID = (short) 0xF00F;
-    public static final String RECORD_DESCRIPTION = "MsofbtChildAnchor";
+public class EscherChildAnchorRecord extends EscherRecord {
+    public static final short RECORD_ID = EscherRecordTypes.CHILD_ANCHOR.typeID;
 
     private int field_1_dx1;
     private int field_2_dy1;
@@ -91,7 +92,7 @@ public class EscherChildAnchorRecord
 
     @Override
     public String getRecordName() {
-        return "ChildAnchor";
+        return EscherRecordTypes.CHILD_ANCHOR.recordName;
     }
 
 
@@ -184,4 +185,20 @@ public class EscherChildAnchorRecord
             { "Y2", field_4_dy2 }
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "x1", this::getDx1,
+            "y1", this::getDy1,
+            "x2", this::getDx2,
+            "y2", this::getDy2
+        );
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.CHILD_ANCHOR;
+    }
 }
index 741ea0fdb47fdd26df35da479356cf7c3d363a00..3662d5bd7701ae58fbc16bcabc67a9c489bd6ae9 100644 (file)
 
 package org.apache.poi.ddf;
 
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -28,14 +33,11 @@ import org.apache.poi.util.LittleEndian;
  *
  * @see EscherChildAnchorRecord
  */
-public class EscherClientAnchorRecord
-        extends EscherRecord
-{
+public class EscherClientAnchorRecord extends EscherRecord {
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 100_000;
 
-    public static final short RECORD_ID = (short) 0xF010;
-    public static final String RECORD_DESCRIPTION = "MsofbtClientAnchor";
+    public static final short RECORD_ID = EscherRecordTypes.CLIENT_ANCHOR.typeID;
 
     /**
      * bit[0] -  fMove (1 bit): A bit that specifies whether the shape will be kept intact when the cells are moved.
@@ -135,7 +137,7 @@ public class EscherClientAnchorRecord
 
     @Override
     public String getRecordName() {
-        return "ClientAnchor";
+        return EscherRecordTypes.CLIENT_ANCHOR.recordName;
     }
 
     /**
@@ -361,4 +363,25 @@ public class EscherClientAnchorRecord
             { "Extra Data", remainingData }
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String,Supplier<?>> m = new LinkedHashMap<>(super.getGenericProperties());
+        m.put("flag", this::getFlag);
+        m.put("col1", this::getCol1);
+        m.put("dx1", this::getDx1);
+        m.put("row1", this::getRow1);
+        m.put("dy1", this::getDy1);
+        m.put("col2", this::getCol2);
+        m.put("dx2", this::getDx2);
+        m.put("row2", this::getRow2);
+        m.put("dy2", this::getDy2);
+        m.put("remainingData", this::getRemainingData);
+        return Collections.unmodifiableMap(m);
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.CLIENT_ANCHOR;
+    }
 }
index 801098854d97686505c6168ff0185ddb4d771ce1..468d229972ac6fa392849362ceecd47e9ee44170 100644 (file)
 
 package org.apache.poi.ddf;
 
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -31,8 +35,7 @@ public class EscherClientDataRecord
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 100_000;
 
-    public static final short RECORD_ID = (short) 0xF011;
-    public static final String RECORD_DESCRIPTION = "MsofbtClientData";
+    public static final short RECORD_ID = EscherRecordTypes.CLIENT_DATA.typeID;
 
     private byte[] remainingData;
 
@@ -75,7 +78,7 @@ public class EscherClientDataRecord
 
     @Override
     public String getRecordName() {
-        return "ClientData";
+        return EscherRecordTypes.CLIENT_DATA.recordName;
     }
 
     /**
@@ -105,4 +108,17 @@ public class EscherClientDataRecord
             { "Extra Data", getRemainingData() }
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "remainingData", this::getRemainingData
+        );
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.CLIENT_DATA;
+    }
 }
index d7889015d5b491c50a247f5831545db7ad7b5772..bb487a5fc89c3505840269e3b97398a19b76e68e 100644 (file)
@@ -22,7 +22,10 @@ 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.util.GenericRecordUtil;
 import org.apache.poi.util.HexDump;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.POILogFactory;
@@ -35,12 +38,12 @@ import org.apache.poi.util.POILogger;
  * used to represent many different types of records.
  */
 public final class EscherContainerRecord extends EscherRecord implements Iterable<EscherRecord> {
-    public static final short DGG_CONTAINER    = (short)0xF000;
-    public static final short BSTORE_CONTAINER = (short)0xF001;
-    public static final short DG_CONTAINER     = (short)0xF002;
-    public static final short SPGR_CONTAINER   = (short)0xF003;
-    public static final short SP_CONTAINER     = (short)0xF004;
-    public static final short SOLVER_CONTAINER = (short)0xF005;
+    public static final short DGG_CONTAINER    = EscherRecordTypes.DGG_CONTAINER.typeID;
+    public static final short BSTORE_CONTAINER = EscherRecordTypes.BSTORE_CONTAINER.typeID;
+    public static final short DG_CONTAINER     = EscherRecordTypes.DG_CONTAINER.typeID;
+    public static final short SPGR_CONTAINER   = EscherRecordTypes.SPGR_CONTAINER.typeID;
+    public static final short SP_CONTAINER     = EscherRecordTypes.SP_CONTAINER.typeID;
+    public static final short SOLVER_CONTAINER = EscherRecordTypes.SOLVER_CONTAINER.typeID;
 
     private static final POILogger log = POILogFactory.getLogger(EscherContainerRecord.class);
 
@@ -201,22 +204,9 @@ public final class EscherContainerRecord extends EscherRecord implements Iterabl
 
     @Override
     public String getRecordName() {
-        switch (getRecordId()) {
-            case DGG_CONTAINER:
-                return "DggContainer";
-            case BSTORE_CONTAINER:
-                return "BStoreContainer";
-            case DG_CONTAINER:
-                return "DgContainer";
-            case SPGR_CONTAINER:
-                return "SpgrContainer";
-            case SP_CONTAINER:
-                return "SpContainer";
-            case SOLVER_CONTAINER:
-                return "SolverContainer";
-            default:
-                return "Container 0x" + HexDump.toHex(getRecordId());
-        }
+        final short id = getRecordId();
+        EscherRecordTypes t = EscherRecordTypes.forTypeID(id);
+        return (t != EscherRecordTypes.UNKNOWN) ? t.recordName : "Container 0x" + HexDump.toHex(id);
     }
 
     @Override
@@ -298,4 +288,17 @@ public final class EscherContainerRecord extends EscherRecord implements Iterabl
             chList.toArray()
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "isContainer", this::isContainerRecord
+        );
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.forTypeID(getRecordId());
+    }
 }
index 956f0f6478114f182cbcc70d95f0234c050cb0ec..3af8c69183afb0ed7e7aebc72b6f86cb5bc6ac9e 100644 (file)
 
 package org.apache.poi.ddf;
 
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 
 /**
  * This record simply holds the number of shapes in the drawing group and the
  * last shape id used for this drawing group.
  */
-public class EscherDgRecord
-    extends EscherRecord
-{
-    public static final short RECORD_ID = (short) 0xF008;
-    public static final String RECORD_DESCRIPTION = "MsofbtDg";
+public class EscherDgRecord extends EscherRecord {
+    public static final short RECORD_ID = EscherRecordTypes.DG.typeID;
 
     private int field_1_numShapes;
     private int field_2_lastMSOSPID;
@@ -81,7 +82,7 @@ public class EscherDgRecord
 
     @Override
     public String getRecordName() {
-        return "Dg";
+        return EscherRecordTypes.DG.recordName;
     }
 
     /**
@@ -150,4 +151,19 @@ public class EscherDgRecord
             { "LastMSOSPID", field_2_lastMSOSPID }
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "numShapes", this::getNumShapes,
+            "lastMSOSPID", this::getLastMSOSPID,
+            "drawingGroupId", this::getDrawingGroupId
+        );
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.DG;
+    }
 }
index d8ed75ec67921d7581b607f4fbebe7b3046d1b09..608947e03c74a69548ef98837b38f8d4e128acc1 100644 (file)
@@ -22,7 +22,11 @@ import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.RecordFormatException;
 
@@ -30,9 +34,8 @@ import org.apache.poi.util.RecordFormatException;
  * This record defines the drawing groups used for a particular sheet.
  */
 public final class EscherDggRecord extends EscherRecord {
-    public static final short RECORD_ID = (short) 0xF006;
-    public static final String RECORD_DESCRIPTION = "MsofbtDgg";
-    
+    public static final short RECORD_ID = EscherRecordTypes.DGG.typeID;
+
     private int field_1_shapeIdMax;
     // for some reason the number of clusters is actually the real number + 1
     // private int field_2_numIdClusters;
@@ -41,7 +44,7 @@ public final class EscherDggRecord extends EscherRecord {
     private final List<FileIdCluster> field_5_fileIdClusters = new ArrayList<>();
     private int maxDgId;
 
-    public static class FileIdCluster {
+    public static class FileIdCluster implements GenericRecord {
         private int field_1_drawingGroupId;
         private int field_2_numShapeIdsUsed;
 
@@ -61,6 +64,14 @@ public final class EscherDggRecord extends EscherRecord {
         private void incrementUsedShapeId() {
             field_2_numShapeIdsUsed++;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "drawingGroupId", this::getDrawingGroupId,
+                "numShapeIdUsed", this::getNumShapeIdsUsed
+            );
+        }
     }
 
     @Override
@@ -129,7 +140,7 @@ public final class EscherDggRecord extends EscherRecord {
 
     @Override
     public String getRecordName() {
-        return "Dgg";
+        return EscherRecordTypes.DGG.recordName;
     }
 
     /**
@@ -344,4 +355,21 @@ public final class EscherDggRecord extends EscherRecord {
             fldIds.toArray()
         };
     }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.DGG;
+    }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "fileIdClusters", () -> field_5_fileIdClusters,
+            "shapeIdMax", this::getShapeIdMax,
+            "numIdClusters", this::getNumIdClusters,
+            "numShapesSaved", this::getNumShapesSaved,
+            "drawingsSaved", this::getDrawingsSaved
+        );
+    }
 }
index fcc6d92b05c115ecd2674e771a0e368864b0861b..fc5affac94ad900c4228d2394e9d617448d9c7d6 100644 (file)
@@ -22,6 +22,10 @@ import java.awt.Rectangle;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 import java.util.zip.DeflaterOutputStream;
 import java.util.zip.InflaterInputStream;
 
@@ -36,9 +40,9 @@ public final class EscherMetafileBlip extends EscherBlipRecord {
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 100_000_000;
 
-    public static final short RECORD_ID_EMF = (short) 0xF018 + 2;
-    public static final short RECORD_ID_WMF = (short) 0xF018 + 3;
-    public static final short RECORD_ID_PICT = (short) 0xF018 + 4;
+    public static final short RECORD_ID_EMF = EscherRecordTypes.BLIP_EMF.typeID;
+    public static final short RECORD_ID_WMF = EscherRecordTypes.BLIP_WMF.typeID;
+    public static final short RECORD_ID_PICT = EscherRecordTypes.BLIP_PICT.typeID;
 
     private static final int HEADER_SIZE = 8;
 
@@ -348,10 +352,10 @@ public final class EscherMetafileBlip extends EscherBlipRecord {
      * @return the blip signature
      */
     public short getSignature() {
-        switch (getRecordId()) {
-            case RECORD_ID_EMF:  return HSSFPictureData.MSOBI_EMF;
-            case RECORD_ID_WMF:  return HSSFPictureData.MSOBI_WMF;
-            case RECORD_ID_PICT: return HSSFPictureData.MSOBI_PICT;
+        switch (EscherRecordTypes.forTypeID(getRecordId())) {
+            case BLIP_EMF:  return HSSFPictureData.MSOBI_EMF;
+            case BLIP_WMF:  return HSSFPictureData.MSOBI_WMF;
+            case BLIP_PICT: return HSSFPictureData.MSOBI_PICT;
         }
         if (log.check(POILogger.WARN)) {
             log.log(POILogger.WARN, "Unknown metafile: " + getRecordId());
@@ -397,4 +401,17 @@ public final class EscherMetafileBlip extends EscherBlipRecord {
             { "Remaining Data", remainingData }
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String, Supplier<?>> m = new LinkedHashMap<>(super.getGenericProperties());
+        m.put("uid", this::getUID);
+        m.put("uncompressedSize", this::getUncompressedSize);
+        m.put("bounds", this::getBounds);
+        m.put("sizeInEMU", this::getSizeEMU);
+        m.put("compressedSize", this::getCompressedSize);
+        m.put("isCompressed", this::isCompressed);
+        m.put("filter", this::getFilter);
+        return Collections.unmodifiableMap(m);
+    }
 }
index f941c672e8535ab0b52748d6fb925b32ba381efc..f1ac813aa17db85a1767c4a735b2d6935fd576b4 100644 (file)
@@ -26,8 +26,8 @@ import org.apache.poi.util.Internal;
  */
 public class EscherOptRecord extends AbstractEscherOptRecord
 {
-    public static final String RECORD_DESCRIPTION = "msofbtOPT";
-    public static final short RECORD_ID = (short) 0xF00B;
+    public static final short RECORD_ID = EscherRecordTypes.OPT.typeID;
+    public static final String RECORD_DESCRIPTION = EscherRecordTypes.OPT.description;
 
     @Override
     public short getInstance()
@@ -50,9 +50,8 @@ public class EscherOptRecord extends AbstractEscherOptRecord
     }
 
     @Override
-    public String getRecordName()
-    {
-        return "Opt";
+    public String getRecordName() {
+        return EscherRecordTypes.OPT.recordName;
     }
 
     @Override
@@ -72,4 +71,9 @@ public class EscherOptRecord extends AbstractEscherOptRecord
 
         super.setVersion( value );
     }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.OPT;
+    }
 }
index 69cf60138f4c4bf8700c0c78f3b08fc4901f2f9d..44bfe1eca0f2723c8c317cea831ce71a7f99567c 100644 (file)
@@ -22,6 +22,10 @@ import java.awt.Rectangle;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 import java.util.zip.InflaterInputStream;
 
 import org.apache.poi.util.IOUtils;
@@ -34,9 +38,9 @@ public final class EscherPictBlip extends EscherBlipRecord {
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 100_000;
 
-    public static final short RECORD_ID_EMF = (short) 0xF018 + 2;
-    public static final short RECORD_ID_WMF = (short) 0xF018 + 3;
-    public static final short RECORD_ID_PICT = (short) 0xF018 + 4;
+    public static final short RECORD_ID_EMF = EscherRecordTypes.BLIP_EMF.typeID;
+    public static final short RECORD_ID_WMF = EscherRecordTypes.BLIP_WMF.typeID;
+    public static final short RECORD_ID_PICT = EscherRecordTypes.BLIP_PICT.typeID;
 
     private static final int HEADER_SIZE = 8;
 
@@ -293,4 +297,17 @@ public final class EscherPictBlip extends EscherBlipRecord {
             { "Extra Data", getPicturedata() },
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String, Supplier<?>> m = new LinkedHashMap<>(super.getGenericProperties());
+        m.put("uid", this::getUID);
+        m.put("uncompressedSize", this::getUncompressedSize);
+        m.put("bounds", this::getBounds);
+        m.put("sizeInEMU", this::getSizeEMU);
+        m.put("compressedSize", this::getCompressedSize);
+        m.put("isCompressed", this::isCompressed);
+        m.put("filter", this::getFilter);
+        return Collections.unmodifiableMap(m);
+    }
 }
index 45fce3642734d0b60cef9dcecb837f0d70260a5a..ff97b87ce3a708bc22b6ea53fc0f1da11fada9d2 100644 (file)
@@ -21,9 +21,13 @@ package org.apache.poi.ddf;
 import java.io.PrintWriter;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.HexDump;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndian;
@@ -32,7 +36,7 @@ import org.apache.poi.util.LittleEndian;
  * The base abstract record from which all escher records are defined.  Subclasses will need
  * to define methods for serialization/deserialization and for determining the record size.
  */
-public abstract class EscherRecord implements Cloneable {
+public abstract class EscherRecord implements Cloneable, GenericRecord {
     private static final BitField fInstance = BitFieldFactory.getInstance(0xfff0);
     private static final BitField fVersion = BitFieldFactory.getInstance(0x000f);
 
@@ -500,4 +504,20 @@ public abstract class EscherRecord implements Cloneable {
             }
         }
     }
+
+    @Override
+    public List<? extends GenericRecord> getGenericChildren() {
+        return getChildRecords();
+    }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "recordId", this::getRecordId,
+            "version", this::getVersion,
+            "instance", this::getInstance,
+            "options", this::getOptions,
+            "recordSize", this::getRecordSize
+        );
+    }
 }
\ No newline at end of file
diff --git a/src/java/org/apache/poi/ddf/EscherRecordTypes.java b/src/java/org/apache/poi/ddf/EscherRecordTypes.java
new file mode 100644 (file)
index 0000000..0080c23
--- /dev/null
@@ -0,0 +1,92 @@
+/* ====================================================================
+   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.ddf;
+
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public enum EscherRecordTypes {
+    // records greater then 0xF000 belong to with Microsoft Office Drawing format also known as Escher
+    DGG_CONTAINER(0xF000, "DggContainer", null),
+    BSTORE_CONTAINER(0xf001, "BStoreContainer", null),
+    DG_CONTAINER(0xf002, "DgContainer", null),
+    SPGR_CONTAINER(0xf003, "SpgrContainer", null),
+    SP_CONTAINER(0xf004, "SpContainer", null),
+    SOLVER_CONTAINER(0xf005, "SolverContainer", null),
+    DGG(0xf006, "Dgg", "MsofbtDgg"),
+    BSE(0xf007, "BSE", "MsofbtBSE"),
+    DG(0xf008, "Dg", "MsofbtDg"),
+    SPGR(0xf009, "Spgr", "MsofbtSpgr"),
+    SP(0xf00a, "Sp", "MsofbtSp"),
+    OPT(0xf00b, "Opt", "msofbtOPT"),
+    TEXTBOX(0xf00c, null, null),
+    CLIENT_TEXTBOX(0xf00d, "ClientTextbox", "msofbtClientTextbox"),
+    ANCHOR(0xf00e, null, null),
+    CHILD_ANCHOR(0xf00f, "ChildAnchor", "MsofbtChildAnchor"),
+    CLIENT_ANCHOR(0xf010, "ClientAnchor", "MsofbtClientAnchor"),
+    CLIENT_DATA(0xf011, "ClientData", "MsofbtClientData"),
+    CONNECTOR_RULE(0xf012, null, null),
+    ALIGN_RULE(0xf013, null, null),
+    ARC_RULE(0xf014, null, null),
+    CLIENT_RULE(0xf015, null, null),
+    CLSID(0xf016, null, null),
+    CALLOUT_RULE(0xf017, null, null),
+    BLIP_START(0xf018, "Blip", "msofbtBlip"),
+    BLIP_EMF(0xf018 + 2, "BlipEmf", null),
+    BLIP_WMF(0xf018 + 3, "BlipWmf", null),
+    BLIP_PICT(0xf018 + 4, "BlipPict", null),
+    BLIP_JPEG(0xf018 + 5, "BlipJpeg", null),
+    BLIP_PNG(0xf018 + 6, "BlipPng", null),
+    BLIP_DIB(0xf018 + 7, "BlipDib", null),
+    BLIP_END(0xf117, "Blip", "msofbtBlip"),
+    REGROUP_ITEMS(0xf118, null, null),
+    SELECTION(0xf119, null, null),
+    COLOR_MRU(0xf11a, null, null),
+    DELETED_PSPL(0xf11d, null, null),
+    SPLIT_MENU_COLORS(0xf11e, "SplitMenuColors", "MsofbtSplitMenuColors"),
+    OLE_OBJECT(0xf11f, null, null),
+    COLOR_SCHEME(0xf120, null, null),
+    // same as EscherTertiaryOptRecord.RECORD_ID
+    USER_DEFINED(0xf122, "TertiaryOpt", null),
+    UNKNOWN(0xffff, "unknown", "unknown");
+
+    public final short typeID;
+    public final String recordName;
+    public final String description;
+
+    EscherRecordTypes(int typeID, String recordName, String description) {
+        this.typeID = (short) typeID;
+        this.recordName = recordName;
+        this.description = description;
+    }
+
+    private Short getTypeId() {
+        return typeID;
+    }
+
+    private static final Map<Short, EscherRecordTypes> LOOKUP =
+        Stream.of(values()).collect(Collectors.toMap(EscherRecordTypes::getTypeId, Function.identity()));
+
+    public static EscherRecordTypes forTypeID(int typeID) {
+        EscherRecordTypes rt = LOOKUP.get((short)typeID);
+        return (rt != null) ? rt : EscherRecordTypes.UNKNOWN;
+    }
+
+}
index b08ff4dde88ba073bb50b35e7f66624eadcb8462..ad1ce992e32d8646b3dee28723231d1c620871e7 100644 (file)
 
 package org.apache.poi.ddf;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.HexDump;
 import org.apache.poi.util.LittleEndian;
 
@@ -24,11 +30,8 @@ import org.apache.poi.util.LittleEndian;
  * Together the the EscherOptRecord this record defines some of the basic
  * properties of a shape.
  */
-public class EscherSpRecord
-    extends EscherRecord
-{
-    public static final short RECORD_ID = (short) 0xF00A;
-    public static final String RECORD_DESCRIPTION = "MsofbtSp";
+public class EscherSpRecord extends EscherRecord {
+    public static final short RECORD_ID = EscherRecordTypes.SP.typeID;
 
     public static final int FLAG_GROUP = 0x0001;
     public static final int FLAG_CHILD = 0x0002;
@@ -43,6 +46,36 @@ public class EscherSpRecord
     public static final int FLAG_BACKGROUND = 0x0400;
     public static final int FLAG_HASSHAPETYPE = 0x0800;
 
+    private static final int[] FLAGS_MASKS = {
+        FLAG_GROUP,
+        FLAG_CHILD,
+        FLAG_PATRIARCH,
+        FLAG_DELETED,
+        FLAG_OLESHAPE,
+        FLAG_HAVEMASTER,
+        FLAG_FLIPHORIZ,
+        FLAG_FLIPVERT,
+        FLAG_CONNECTOR,
+        FLAG_HAVEANCHOR,
+        FLAG_BACKGROUND,
+        FLAG_HASSHAPETYPE
+    };
+
+    private static final String[] FLAGS_NAMES = {
+            "GROUP",
+            "CHILD",
+            "PATRIARCH",
+            "DELETED",
+            "OLESHAPE",
+            "HAVEMASTER",
+            "FLIPHORIZ",
+            "FLIPVERT",
+            "CONNECTOR",
+            "HAVEANCHOR",
+            "BACKGROUND",
+            "HASSHAPETYPE"
+    };
+
     private int field_1_shapeId;
     private int field_2_flags;
 
@@ -98,7 +131,7 @@ public class EscherSpRecord
 
     @Override
     public String getRecordName() {
-        return "Sp";
+        return EscherRecordTypes.SP.recordName;
     }
 
     /**
@@ -106,7 +139,7 @@ public class EscherSpRecord
      */
     private String decodeFlags( int flags )
     {
-        StringBuffer result = new StringBuffer();
+        StringBuilder result = new StringBuilder();
         result.append( ( flags & FLAG_GROUP ) != 0 ? "|GROUP" : "" );
         result.append( ( flags & FLAG_CHILD ) != 0 ? "|CHILD" : "" );
         result.append( ( flags & FLAG_PATRIARCH ) != 0 ? "|PATRIARCH" : "" );
@@ -222,4 +255,19 @@ public class EscherSpRecord
             { "Flags", decodeFlags(field_2_flags)+" (0x"+HexDump.toHex(field_2_flags)+")" }
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "shapeType", this::getShapeType,
+            "shapeId", this::getShapeId,
+            "flags", getBitsAsString(this::getFlags, FLAGS_MASKS, FLAGS_NAMES)
+        );
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.SP;
+    }
 }
index 199439cedc094119a715e105d58064d9fb6ab16e..49979e5a05773196a6a3002e36387710bba643c7 100644 (file)
 
 package org.apache.poi.ddf;
 
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.RecordFormatException;
 
@@ -24,11 +28,8 @@ import org.apache.poi.util.RecordFormatException;
  * The spgr record defines information about a shape group.  Groups in escher
  * are simply another form of shape that you can't physically see.
  */
-public class EscherSpgrRecord
-    extends EscherRecord
-{
-    public static final short RECORD_ID = (short) 0xF009;
-    public static final String RECORD_DESCRIPTION = "MsofbtSpgr";
+public class EscherSpgrRecord extends EscherRecord {
+    public static final short RECORD_ID = EscherRecordTypes.SPGR.typeID;
 
     private int field_1_rectX1;
     private int field_2_rectY1;
@@ -84,7 +85,7 @@ public class EscherSpgrRecord
 
     @Override
     public String getRecordName() {
-        return "Spgr";
+        return EscherRecordTypes.SPGR.recordName;
     }
 
     /**
@@ -175,4 +176,20 @@ public class EscherSpgrRecord
             { "RectHeight", field_4_rectY2 }
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "rectX1", this::getRectX1,
+            "rectY1", this::getRectY1,
+            "rectX2", this::getRectX2,
+            "rectY2", this::getRectY2
+        );
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.SPGR;
+    }
 }
index 25071c0ea64abd3b11128d2397632bfccf9d6dc9..b6d2ba4af0bfc6035b1dbff1763e505f06e54720 100644 (file)
 
 package org.apache.poi.ddf;
 
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.RecordFormatException;
 
@@ -24,11 +28,8 @@ import org.apache.poi.util.RecordFormatException;
  * A list of the most recently used colours for the drawings contained in
  * this document.
  */
-public class EscherSplitMenuColorsRecord
-    extends EscherRecord
-{
-    public static final short RECORD_ID = (short) 0xF11E;
-    public static final String RECORD_DESCRIPTION = "MsofbtSplitMenuColors";
+public class EscherSplitMenuColorsRecord extends EscherRecord {
+    public static final short RECORD_ID = EscherRecordTypes.SPLIT_MENU_COLORS.typeID;
 
     private int field_1_color1;
     private int field_2_color2;
@@ -82,7 +83,7 @@ public class EscherSplitMenuColorsRecord
 
     @Override
     public String getRecordName() {
-        return "SplitMenuColors";
+        return EscherRecordTypes.SPLIT_MENU_COLORS.recordName;
     }
 
     /**
@@ -166,4 +167,20 @@ public class EscherSplitMenuColorsRecord
             { "Color4", field_4_color4 }
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "color1", this::getColor1,
+            "color2", this::getColor2,
+            "color3", this::getColor3,
+            "color4", this::getColor4
+        );
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.SPLIT_MENU_COLORS;
+    }
 }
index 6182942119cc16618a70953511488a470ecf00c5..0677f15c2e6d81a6ea78066f9ecbd02d124cf796 100644 (file)
@@ -22,11 +22,15 @@ package org.apache.poi.ddf;
  */
 public class EscherTertiaryOptRecord extends AbstractEscherOptRecord
 {
-    public static final short RECORD_ID = (short) 0xF122;
+    public static final short RECORD_ID = EscherRecordTypes.USER_DEFINED.typeID;
 
     @Override
-    public String getRecordName()
-    {
-        return "TertiaryOpt";
+    public String getRecordName() {
+        return EscherRecordTypes.USER_DEFINED.recordName;
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.USER_DEFINED;
     }
 }
index fbf37f49b05e5f9ddca74870b239da6ae44f484d..16213b8a3e80051affda3ad2c87d5171682cffe9 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.ddf;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.RecordFormatException;
@@ -35,8 +38,7 @@ public final class EscherTextboxRecord extends EscherRecord implements Cloneable
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 100_000;
 
-    public static final short RECORD_ID = (short)0xF00D;
-    public static final String RECORD_DESCRIPTION = "msofbtClientTextbox";
+    public static final short RECORD_ID = EscherRecordTypes.CLIENT_TEXTBOX.typeID;
 
     private static final byte[] NO_BYTES = new byte[0];
 
@@ -134,7 +136,7 @@ public final class EscherTextboxRecord extends EscherRecord implements Cloneable
 
     @Override
     public String getRecordName() {
-        return "ClientTextbox";
+        return EscherRecordTypes.CLIENT_TEXTBOX.recordName;
     }
 
     @Override
@@ -154,4 +156,18 @@ public final class EscherTextboxRecord extends EscherRecord implements Cloneable
             { "Extra Data", thedata }
         };
     }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.CLIENT_TEXTBOX;
+    }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "isContainer", this::isContainerRecord,
+            "extraData", this::getData
+        );
+    }
 }
index 38475be81881ad015f767d7ef6d56efedc458a29..8db83cd46f115d691a1658841a8a4296554f2160 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.ddf;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.HexDump;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
@@ -158,4 +161,17 @@ public final class UnknownEscherRecord extends EscherRecord implements Cloneable
             { "Extra Data", thedata }
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "data", this::getData
+        );
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return EscherRecordTypes.UNKNOWN;
+    }
 }
index 56595457450d45dc13c9ccab6166c2102672c60a..64a027be1fd143bcd6f657880c662f48c6f1d916 100644 (file)
@@ -26,7 +26,7 @@ import org.apache.poi.ddf.EscherOptRecord;
 import org.apache.poi.ddf.EscherProperties;
 import org.apache.poi.ddf.EscherProperty;
 import org.apache.poi.ddf.EscherRecord;
-import org.apache.poi.ddf.EscherTextboxRecord;
+import org.apache.poi.ddf.EscherRecordTypes;
 import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
 import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord;
 import org.apache.poi.hssf.record.EscherAggregate;
@@ -71,11 +71,11 @@ public class HSSFShapeFactory {
             TextObjectRecord txtRecord = null;
 
             for (EscherRecord record : container) {
-                switch (record.getRecordId()) {
-                    case EscherClientDataRecord.RECORD_ID:
+                switch (EscherRecordTypes.forTypeID(record.getRecordId())) {
+                    case CLIENT_DATA:
                         objRecord = (ObjRecord) shapeToObj.get(record);
                         break;
-                    case EscherTextboxRecord.RECORD_ID:
+                    case CLIENT_TEXTBOX:
                         txtRecord = (TextObjectRecord) shapeToObj.get(record);
                         break;
                     default:
index 69036df6f1cccee2c76d20986434f9ab792b3f95..1787baaccdf2b0f492c05c679eb9f49dada00e20 100644 (file)
 
 package org.apache.poi.hssf.usermodel;
 
-import org.apache.poi.ddf.*;
-import org.apache.poi.hssf.record.*;
-
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
 import java.util.Iterator;
+import java.util.List;
+
+import org.apache.poi.ddf.DefaultEscherRecordFactory;
+import org.apache.poi.ddf.EscherBoolProperty;
+import org.apache.poi.ddf.EscherChildAnchorRecord;
+import org.apache.poi.ddf.EscherClientAnchorRecord;
+import org.apache.poi.ddf.EscherClientDataRecord;
+import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.ddf.EscherOptRecord;
+import org.apache.poi.ddf.EscherProperties;
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherRecordTypes;
+import org.apache.poi.ddf.EscherSpRecord;
+import org.apache.poi.ddf.EscherSpgrRecord;
+import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
+import org.apache.poi.hssf.record.EndSubRecord;
+import org.apache.poi.hssf.record.EscherAggregate;
+import org.apache.poi.hssf.record.GroupMarkerSubRecord;
+import org.apache.poi.hssf.record.ObjRecord;
 
 /**
  * A shape group may contain other shapes.  It was no actual form on the
@@ -40,13 +55,13 @@ public class HSSFShapeGroup extends HSSFShape implements HSSFShapeContainer {
         EscherContainerRecord spContainer = spgrContainer.getChildContainers().get(0);
         _spgrRecord = (EscherSpgrRecord) spContainer.getChild(0);
         for (EscherRecord ch : spContainer.getChildRecords()) {
-            switch (ch.getRecordId()) {
-                case EscherSpgrRecord.RECORD_ID:
+            switch (EscherRecordTypes.forTypeID(ch.getRecordId())) {
+                case SPGR:
                     break;
-                case EscherClientAnchorRecord.RECORD_ID:
+                case CLIENT_ANCHOR:
                     anchor = new HSSFClientAnchor((EscherClientAnchorRecord) ch);
                     break;
-                case EscherChildAnchorRecord.RECORD_ID:
+                case CHILD_ANCHOR:
                     anchor = new HSSFChildAnchor((EscherChildAnchorRecord) ch);
                     break;
                 default:
@@ -307,8 +322,7 @@ public class HSSFShapeGroup extends HSSFShape implements HSSFShapeContainer {
      */
     public int countOfAllChildren() {
         int count = shapes.size();
-        for (Iterator<HSSFShape> iterator = shapes.iterator(); iterator.hasNext(); ) {
-            HSSFShape shape = iterator.next();
+        for (HSSFShape shape : shapes) {
             count += shape.countOfAllChildren();
         }
         return count;
index 6e9e3623191443141d07f52b4f0c3640b9256333..07c5666e6b67b3c545cc7ee8c28c028bf9de6b9f 100644 (file)
@@ -19,16 +19,20 @@ package org.apache.poi.poifs.crypt;
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.GeneralSecurityException;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import javax.crypto.Cipher;
 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 Decryptor implements Cloneable {
+public abstract class Decryptor implements Cloneable, GenericRecord {
     public static final String DEFAULT_PASSWORD="VelvetSweatshop";
     public static final String DEFAULT_POIFS_ENTRY="EncryptedPackage";
     
@@ -185,4 +189,14 @@ public abstract class Decryptor implements Cloneable {
         // encryptionInfo is set from outside
         return other;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "secretKey", secretKey::getEncoded,
+            "verifier", this::getVerifier,
+            "integrityHmacKey", this::getIntegrityHmacKey,
+            "integrityHmacValue", this::getIntegrityHmacValue
+        );
+    }
 }
\ No newline at end of file
index a00ac44c826b8993ae37196928a1a1a5eec79945..6518281d6f2da808320e3dc4147fcc99635243b9 100644 (file)
 ==================================================================== */
 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.EncryptedDocumentException;
+import org.apache.poi.common.usermodel.GenericRecord;
 
 /**
  * Reads and processes OOXML Encryption Headers
  * The constants are largely based on ZIP constants.
  */
-public abstract class EncryptionHeader implements Cloneable {
+public abstract class EncryptionHeader implements Cloneable, GenericRecord {
     public static final int ALGORITHM_RC4 = CipherAlgorithm.rc4.ecmaId;
     public static final int ALGORITHM_AES_128 = CipherAlgorithm.aes128.ecmaId;
     public static final int ALGORITHM_AES_192 = CipherAlgorithm.aes192.ecmaId;
@@ -156,4 +162,20 @@ public abstract class EncryptionHeader implements Cloneable {
         other.keySalt = (keySalt == null) ? null : keySalt.clone();
         return other;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+        m.put("flags", this::getFlags);
+        m.put("sizeExtra", this::getSizeExtra);
+        m.put("cipherAlgorithm", this::getCipherAlgorithm);
+        m.put("hashAlgorithm", this::getHashAlgorithm);
+        m.put("keyBits", this::getKeySize);
+        m.put("blockSize", this::getBlockSize);
+        m.put("providerType", this::getCipherProvider);
+        m.put("chainingMode", this::getChainingMode);
+        m.put("keySalt", this::getKeySalt);
+        m.put("cspName", this::getCspName);
+        return Collections.unmodifiableMap(m);
+    }
 }
index abd955a870ce315bfff16ccb29532479f683b912..826b57e41977cb65f8485b30c1c44776c4e417cb 100644 (file)
@@ -21,10 +21,16 @@ import static org.apache.poi.poifs.crypt.EncryptionMode.binaryRC4;
 import static org.apache.poi.poifs.crypt.EncryptionMode.cryptoAPI;
 import static org.apache.poi.poifs.crypt.EncryptionMode.standard;
 import static org.apache.poi.poifs.crypt.EncryptionMode.xor;
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
 
 import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 
 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.BitField;
@@ -36,7 +42,7 @@ import org.apache.poi.util.LittleEndianInput;
  * some {@link EncryptionMode}s.
  * @see #getBuilder(EncryptionMode)
  */
-public class EncryptionInfo implements Cloneable {
+public class EncryptionInfo implements Cloneable, GenericRecord {
     private final EncryptionMode encryptionMode;
     private final int versionMajor;
     private final int versionMinor;
@@ -72,8 +78,15 @@ public class EncryptionInfo implements Cloneable {
      * ECMA-376. If the fAES bit is 1, the fCryptoAPI bit MUST also be 1.
      */
     public static final BitField flagAES = BitFieldFactory.getInstance(0x20);
-    
-    
+
+    private static final int[] FLAGS_MASKS = {
+        0x04, 0x08, 0x10, 0x20
+    };
+
+    private static final String[] FLAGS_NAMES = {
+        "CRYPTO_API", "DOC_PROPS", "EXTERNAL", "AES"
+    };
+
     /**
      * Opens for decryption
      */
@@ -277,4 +290,18 @@ public class EncryptionInfo implements Cloneable {
         other.encryptor.setEncryptionInfo(other);
         return other;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+        m.put("encryptionMode", this::getEncryptionMode);
+        m.put("versionMajor", this::getVersionMajor);
+        m.put("versionMinor", this::getVersionMinor);
+        m.put("encryptionFlags", getBitsAsString(this::getEncryptionFlags, FLAGS_MASKS, FLAGS_NAMES));
+        m.put("header", this::getHeader);
+        m.put("verifier", this::getVerifier);
+        m.put("decryptor", this::getDecryptor);
+        m.put("encryptor", this::getEncryptor);
+        return Collections.unmodifiableMap(m);
+    }
 }
\ No newline at end of file
index 3601e01022339bc0569a0c5bd3d5b5c04eb16c7b..0f3e44b10b550b13d5e9fdcd29c5f3bba684993f 100644 (file)
 ==================================================================== */
 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 implements Cloneable {
         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);
+    }
 }
index b62ad862fe2981a93ee8916edb7424aca76a6052..8c241be7803978469eb8effe38a547b23f05d1bb 100644 (file)
@@ -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 implements Cloneable {
         // encryptionInfo is set from outside
         return other;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "secretKey", secretKey::getEncoded
+        );
+    }
 }
index 2a1d1effe945464757d60fd2659e529f1b409cf3..83b50c22db9e56b6e21d4323d8bec91d03598311 100644 (file)
@@ -76,9 +76,9 @@ public class DrawPictureShape extends DrawSimpleShape {
      * @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;
         }
index c77cf32d0805f7aeaa8a373c255849084f25a373..cc50ed737c4dbdad3680cc3ccb8b4dff315870fa 100644 (file)
@@ -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 DrawShape {
             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 DrawShape {
             }
         }
 
-               // 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) {
index 4703483b51cdbac7cef5980648b322f5ac7eb808..c657bbadfd6ac90063348a425a344d1f5e620127 100644 (file)
@@ -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
index 93426074346ba1c143bb1f86563bd5c69e4a2f25..8219836281827eb439c28755d10787e875d99f40 100644 (file)
@@ -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);
+        }
     }
 
     /**
diff --git a/src/java/org/apache/poi/util/GenericRecordJsonWriter.java b/src/java/org/apache/poi/util/GenericRecordJsonWriter.java
new file mode 100644 (file)
index 0000000..1598d58
--- /dev/null
@@ -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) {
+        }
+    }
+}
diff --git a/src/java/org/apache/poi/util/GenericRecordUtil.java b/src/java/org/apache/poi/util/GenericRecordUtil.java
new file mode 100644 (file)
index 0000000..00c46ce
--- /dev/null
@@ -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;
+        }
+    }
+}
index ed058d1df0371b320865f0b06c75a926d4359cdf..d943014bc0a5033841f8e88cb02bfdba3bb1c211 100644 (file)
 ==================================================================== */
 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 extends EncryptionHeader implements Cloneable
         return other;
     }
 
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "encryptedHmacKey", this::getEncryptedHmacKey,
+            "encryptedHmacValue", this::getEncryptedHmacValue
+        );
+    }
 }
index b8bea4aea0eb2fcfa4c8458306ba18b3e6c6b2b5..43c7f398e15cd1ea21cfaf78965e6dc5ee96ca0b 100644 (file)
 
 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);
+            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;
+            }
 
-            for (Integer slideNo : slidenum) {
-                Slide<?, ?> slide = slides.get(slideNo);
-                String title = slide.getTitle();
+            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;
+        }
+    }
+
 }
index c471ae1717486edfab7dbffba8c647d26d22aeb1..3002dd28328e4b57daa41217eaf593d2d5ac9f15 100644 (file)
@@ -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);
index 39db61492b59441a66cd615c31c8f84aeae67e53..9d32e2d0531819c4e1b9b34b121974360d48be27 100644 (file)
@@ -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 implements ImageRenderer {
         }
     }
 
+    @Override
+    public GenericRecord getGenericRecord() {
+        return image;
+    }
 }
index 1bd5bd6c25d214d5ff1e1e1984bb5f7629e4a3a9..70df41135acc6955639135df52d1d123ef5711bb 100644 (file)
@@ -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;
+        }
     }
 }
index ae0fbd2d07eee7a77e7f6f20fb57221a9590b9c5..77f2b0d4b65e9c70e3b1ec980aeb14215f18fea6 100644 (file)
 
 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()+" }";
-    }
 }
index 6979a37b7161a6650d34b62877a5bc1156a072cf..514db1d309f23072e6058926c839a7090bcb10b0 100644 (file)
@@ -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.HwmfFill;
 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();
index dd42fe63be1be7b1f133882924c33c31f747986b..f7106053a7a4238414fc7e49e772775ed730961d 100644 (file)
@@ -19,10 +19,17 @@ package org.apache.poi.hemf.record.emf;
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 
-import org.apache.poi.util.IOUtils;
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.common.usermodel.fonts.FontCharset;
 import org.apache.poi.hwmf.record.HwmfFont;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
+import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
@@ -41,7 +48,7 @@ public class HemfFont extends HwmfFont {
         }
     }
 
-    protected static class LogFontPanose implements LogFontDetails {
+    protected static class LogFontPanose implements LogFontDetails, GenericRecord {
         enum FamilyType {
             PAN_ANY,
             PAN_NO_FIT,
@@ -201,21 +208,26 @@ public class HemfFont extends HwmfFont {
 
         @Override
         public String toString() {
-            return
-                "{ styleSize: " + styleSize +
-                ", vendorId: " + vendorId +
-                ", culture: " +  culture +
-                ", familyType: '" + familyType + "'" +
-                ", serifStyle: '" + serifStyle + "'" +
-                ", weight: '" + weight + "'" +
-                ", proportion: '" + proportion + "'" +
-                ", contrast: '" + contrast + "'" +
-                ", strokeVariation: '" + strokeVariation + "'" +
-                ", armStyle: '" + armStyle + "'" +
-                ", letterform: '" + letterform + "'" +
-                ", midLine: '" + midLine + "'" +
-                ", xHeight: '" + xHeight + "'" +
-                "}";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+            m.put("styleSize", () -> styleSize);
+            m.put("vendorId", () -> vendorId);
+            m.put("culture", () -> culture);
+            m.put("familyType", () -> familyType);
+            m.put("serifStyle", () -> serifStyle);
+            m.put("weight", () -> weight);
+            m.put("proportion", () -> proportion);
+            m.put("contrast", () -> contrast);
+            m.put("strokeVariation", () -> strokeVariation);
+            m.put("armStyle", () -> armStyle);
+            m.put("letterform", () -> letterform);
+            m.put("midLine", () -> midLine);
+            m.put("xHeight", () -> xHeight);
+            return Collections.unmodifiableMap(m);
         }
     }
 
@@ -466,12 +478,18 @@ public class HemfFont extends HwmfFont {
 
     @Override
     public String toString() {
-        return
-            "{ fullname: '" + (fullname == null ? "" : fullname) + "'" +
-            ", style: '" + (style == null ? "" : style) + "'" +
-            ", script: '" + (script == null ? "" : script) + "'" +
-            ", details: " + details +
-            "," + super.toString().substring(1);
+        return GenericRecordJsonWriter.marshal(this);
+    }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "fullname", () -> fullname,
+            "style", () -> style,
+            "script", () -> script,
+            "details", () -> details
+        );
     }
 
     @Override
index 6c1cf5caddbac3d9c9e3c5d36cfbb88dbc8b07f3..52a405502961d776e55b3167a8cde0533c7af6f7 100644 (file)
@@ -20,15 +20,18 @@ package org.apache.poi.hemf.record.emf;
 import static org.apache.poi.hemf.record.emf.HemfDraw.readDimensionInt;
 import static org.apache.poi.hemf.record.emf.HemfDraw.readRectL;
 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.dimToString;
 
 import java.awt.geom.Dimension2D;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.util.Dimension2DDouble;
+import org.apache.poi.util.GenericRecordJsonWriter;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
@@ -82,7 +85,7 @@ public class HemfHeader implements HemfRecord {
 
     public String getDescription() { return description; }
 
-    public long getnPalEntries() {
+    public long getNPalEntries() {
         return nPalEntries;
     }
 
@@ -120,23 +123,7 @@ public class HemfHeader implements HemfRecord {
 
     @Override
     public String toString() {
-        return "HemfHeader{" +
-                "boundsRectangle: " + boundsToString(boundsRectangle) +
-                ", frameRectangle: " + boundsToString(frameRectangle) +
-                ", bytes: " + bytes +
-                ", records: " + records +
-                ", handles: " + handles +
-                ", description: '" + (description == null ? "" : description) + "'" +
-                ", nPalEntries: " + nPalEntries +
-                ", hasExtension1: " + hasExtension1 +
-                ", cbPixelFormat: " + cbPixelFormat +
-                ", offPixelFormat: " + offPixelFormat +
-                ", bOpenGL: " + bOpenGL +
-                ", hasExtension2: " + hasExtension2 +
-                ", deviceDimension: " + dimToString(deviceDimension) +
-                ", microDimension: " + dimToString(microDimension) +
-                ", milliDimension: " + dimToString(milliDimension) +
-                '}';
+        return GenericRecordJsonWriter.marshal(this);
     }
 
     @Override
@@ -204,4 +191,25 @@ public class HemfHeader implements HemfRecord {
 
         return size;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+        m.put("boundsRectangle", this::getBoundsRectangle);
+        m.put("frameRectangle", this::getFrameRectangle);
+        m.put("bytes", this::getBytes);
+        m.put("records", this::getRecords);
+        m.put("handles", this::getHandles);
+        m.put("description", this::getDescription);
+        m.put("nPalEntries", this::getNPalEntries);
+        m.put("hasExtension1", this::isHasExtension1);
+        m.put("cbPixelFormat", this::getCbPixelFormat);
+        m.put("offPixelFormat", this::getOffPixelFormat);
+        m.put("bOpenGL", this::getbOpenGL);
+        m.put("hasExtension2", this::isHasExtension2);
+        m.put("deviceDimension", this::getDeviceDimension);
+        m.put("milliDimension", this::getMilliDimension);
+        m.put("microDimension", this::getMicroDimension);
+        return Collections.unmodifiableMap(m);
+    }
 }
index 36b82946f421d3163d41b391414a7124cc4f389a..e32fde84e94efc2f401e315895fa4d82af0fb747 100644 (file)
@@ -18,7 +18,6 @@
 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.xformToString;
 import static org.apache.poi.hemf.record.emf.HemfFill.readBitmap;
 import static org.apache.poi.hemf.record.emf.HemfFill.readXForm;
 import static org.apache.poi.hemf.record.emf.HemfRecordIterator.HEADER_SIZE;
@@ -28,9 +27,10 @@ import java.awt.geom.Point2D;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Function;
+import java.util.function.Supplier;
 
 import org.apache.poi.hemf.draw.HemfDrawProperties;
 import org.apache.poi.hemf.draw.HemfGraphics;
@@ -49,9 +49,12 @@ import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
 import org.apache.poi.hwmf.record.HwmfPalette.PaletteEntry;
 import org.apache.poi.hwmf.record.HwmfPenStyle;
 import org.apache.poi.hwmf.record.HwmfPenStyle.HwmfLineDash;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
+@SuppressWarnings("WeakerAccess")
 public class HemfMisc {
 
     public enum HemfModifyWorldTransformMode {
@@ -138,12 +141,21 @@ public class HemfMisc {
 
             return size;
         }
+
+        public List<PaletteEntry> getPalette() {
+            return palette;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("palette", this::getPalette);
+        }
     }
 
     /**
      * The EMF_SAVEDC record saves the playback device context for later retrieval.
      */
-    public static class EmfSaveDc extends HwmfMisc.WmfSaveDc implements HemfRecord {
+    public static class EmfSaveDc extends HwmfMisc.WmfSaveDc implements HemfRecordWithoutProperties {
         @Override
         public HemfRecordType getEmfRecordType() {
             return HemfRecordType.saveDc;
@@ -153,6 +165,11 @@ public class HemfMisc {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return 0;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -174,6 +191,11 @@ public class HemfMisc {
             nSavedDC = leis.readInt();
             return LittleEndianConsts.INT_SIZE;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -191,6 +213,11 @@ public class HemfMisc {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return colorRef.init(leis);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
 
@@ -213,6 +240,11 @@ public class HemfMisc {
             bkMode = HwmfBkMode.valueOf((int) leis.readUInt());
             return LittleEndianConsts.INT_SIZE;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -228,6 +260,11 @@ public class HemfMisc {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return super.init(leis, recordSize, (int) recordId);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -246,6 +283,11 @@ public class HemfMisc {
             mapMode = HwmfMapMode.valueOf((int) leis.readUInt());
             return LittleEndianConsts.INT_SIZE;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -264,6 +306,11 @@ public class HemfMisc {
             drawMode = HwmfBinaryRasterOp.valueOf((int) leis.readUInt());
             return LittleEndianConsts.INT_SIZE;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
 
@@ -284,6 +331,11 @@ public class HemfMisc {
             stretchBltMode = StretchBltMode.valueOf((int) leis.readUInt());
             return LittleEndianConsts.INT_SIZE;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -317,14 +369,26 @@ public class HemfMisc {
             ctx.addObjectTableEntry(this, brushIdx);
         }
 
+        public int getBrushIdx() {
+            return brushIdx;
+        }
 
         @Override
         public String toString() {
-            return
-                "{ brushIndex: "+brushIdx+
-                ", brushStyle: '"+brushStyle+"'"+
-                ", colorRef: "+colorRef+
-                ", brushHatch: '"+brushHatch+"' }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "brushIdx", this::getBrushIdx
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
         }
     }
 
@@ -379,6 +443,22 @@ public class HemfMisc {
             ctx.addObjectTableEntry(this, brushIdx);
         }
 
+        public int getBrushIdx() {
+            return brushIdx;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "brushIdx", this::getBrushIdx
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -397,6 +477,11 @@ public class HemfMisc {
             objectIndex = (int) leis.readUInt();
             return LittleEndianConsts.INT_SIZE;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -439,7 +524,24 @@ public class HemfMisc {
 
         @Override
         public String toString() {
-            return super.toString().replaceFirst("\\{", "{ penIndex: "+penIndex+", ");
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public int getPenIndex() {
+            return penIndex;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "penIndex", this::getPenIndex
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
         }
     }
 
@@ -539,12 +641,34 @@ public class HemfMisc {
 
         @Override
         public String toString() {
-            // TODO: add style entries + bmp
-            return
-                "{ brushStyle: '"+brushStyle+"'"+
-                ", hatchStyle: '"+hatchStyle+"'"+
-                ", dashPattern: "+ Arrays.toString(penStyle.getLineDashes())+
-                ", "+super.toString().substring(1);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfBrushStyle getBrushStyle() {
+            return brushStyle;
+        }
+
+        public HwmfHatchStyle getHatchStyle() {
+            return hatchStyle;
+        }
+
+        public HwmfBitmapDib getBitmap() {
+            return bitmap;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "brushStyle", this::getBrushStyle,
+                "hatchStyle", this::getHatchStyle,
+                "bitmap", this::getBitmap
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
         }
     }
 
@@ -573,7 +697,16 @@ public class HemfMisc {
 
         @Override
         public String toString() {
-            return "{ miterLimit: "+miterLimit+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public int getMiterLimit() {
+            return miterLimit;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("miterLimit", this::getMiterLimit);
         }
     }
 
@@ -593,7 +726,16 @@ public class HemfMisc {
 
         @Override
         public String toString() {
-            return "{ x: "+origin.getX()+", y: "+origin.getY()+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Point2D getOrigin() {
+            return origin;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("origin", this::getOrigin);
         }
     }
 
@@ -620,7 +762,16 @@ public class HemfMisc {
 
         @Override
         public String toString() {
-            return "{ xForm: " + xformToString(xForm)+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public AffineTransform getXForm() {
+            return xForm;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("xForm", this::getXForm);
         }
     }
 
@@ -687,9 +838,23 @@ public class HemfMisc {
 
         @Override
         public String toString() {
-            return
-                "{ xForm: " + xformToString(xForm) +
-                ", modifyWorldTransformMode: '"+modifyWorldTransformMode+"' }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public AffineTransform getXForm() {
+            return xForm;
+        }
+
+        public HemfModifyWorldTransformMode getModifyWorldTransformMode() {
+            return modifyWorldTransformMode;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "xForm", this::getXForm,
+                "modifyWorldTransformMode", this::getModifyWorldTransformMode
+            );
         }
     }
 
@@ -760,11 +925,28 @@ public class HemfMisc {
 
         @Override
         public String toString() {
-            return
-                "{ penIndex: " + penIndex +
-                ", colorUsage: " + colorUsage +
-                ", bitmap: " + bitmap +
-                "}";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public int getPenIndex() {
+            return penIndex;
+        }
+
+        public HwmfFill.ColorUsage getColorUsage() {
+            return colorUsage;
+        }
+
+        public HwmfBitmapDib getBitmap() {
+            return bitmap;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "penIndex", this::getPenIndex,
+                "colorUsage", this::getColorUsage,
+                "bitmap", this::getBitmap
+            );
         }
     }
 
index 3d002f00441b77a0dd77bfddf500ff5e77e37a9c..5c5f9283ac175d9f520469322c0da5c96c3d831c 100644 (file)
 package org.apache.poi.hemf.record.emf;
 
 import java.io.IOException;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hwmf.record.HwmfPalette;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
+@SuppressWarnings("WeakerAccess")
 public class HemfPalette {
     /** The EMR_SELECTPALETTE record specifies a logical palette for the playback device context. */
     public static class EmfSelectPalette extends HwmfPalette.WmfSelectPalette implements HemfRecord {
@@ -42,6 +46,11 @@ public class HemfPalette {
             paletteIndex = (int)leis.readUInt();
             return LittleEndianConsts.INT_SIZE;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /** The EMR_CREATEPALETTE record defines a logical palette for graphics operations. */
@@ -73,6 +82,23 @@ public class HemfPalette {
         public void draw(HemfGraphics ctx) {
             ctx.addObjectTableEntry(this, paletteIndex);
         }
+
+        public int getPaletteIndex() {
+            return paletteIndex;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "paletteIndex", this::getPaletteIndex
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -104,6 +130,23 @@ public class HemfPalette {
         public void draw(HemfGraphics ctx) {
             ctx.addObjectTableEntry(this, paletteIndex);
         }
+
+        public int getPaletteIndex() {
+            return paletteIndex;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "paletteIndex", this::getPaletteIndex
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -134,6 +177,23 @@ public class HemfPalette {
         public void draw(HemfGraphics ctx) {
             ctx.addObjectTableEntry(this, paletteIndex);
         }
+
+        public int getPaletteIndex() {
+            return paletteIndex;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "paletteIndex", this::getPaletteIndex
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -150,6 +210,11 @@ public class HemfPalette {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return 0;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -205,5 +270,14 @@ public class HemfPalette {
             icmMode = ICMMode.valueOf(leis.readInt());
             return LittleEndianConsts.INT_SIZE;
         }
+
+        public ICMMode getIcmMode() {
+            return icmMode;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("icmMode", this::getIcmMode);
+        }
     }
 }
index 41e9f3044296d23089fe00d6d3305d90e01a8ea1..cd33b10ca0812cbbce585c78cc45ac1bdbbefec2 100644 (file)
@@ -19,14 +19,17 @@ package org.apache.poi.hemf.record.emf;
 
 
 import java.io.IOException;
+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.hwmf.record.HwmfRecord;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianInputStream;
 
 @Internal
-public interface HemfRecord {
+public interface HemfRecord extends GenericRecord {
 
     HemfRecordType getEmfRecordType();
 
@@ -58,4 +61,16 @@ public interface HemfRecord {
      * @param header the emf header
      */
     default void setHeader(HemfHeader header) {}
+
+    @Override
+    default Enum getGenericRecordType() {
+        return getEmfRecordType();
+    }
+}
+
+interface HemfRecordWithoutProperties extends HemfRecord {
+    default Map<String, Supplier<?>> getGenericProperties() {
+        return null;
+    }
+
 }
index 7e785315c0c93079c8e829d57a815043e5a1586a..f806185d253536f0af93a513389fc9ebcfad9e63 100644 (file)
@@ -27,12 +27,16 @@ import java.awt.geom.Dimension2D;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.nio.charset.Charset;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hwmf.draw.HwmfGraphics;
 import org.apache.poi.hwmf.record.HwmfText;
 import org.apache.poi.hwmf.record.HwmfText.WmfSetTextAlign;
 import org.apache.poi.util.Dimension2DDouble;
+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;
@@ -45,6 +49,7 @@ import org.apache.poi.util.RecordFormatException;
  * implemented at this point!
  */
 @Internal
+@SuppressWarnings("WeakerAccess")
 public class HemfText {
 
     private static final int MAX_RECORD_LENGTH = 1_000_000;
@@ -181,7 +186,7 @@ public class HemfText {
          *
          * @param charset the charset to be used to decode the character bytes
          * @return text from this text element
-         * @throws IOException
+         * @throws IOException if the charset is not compatible to the underlying bytes
          */
         public String getText(Charset charset) throws IOException {
             return super.getText(charset);
@@ -206,10 +211,21 @@ public class HemfText {
 
         @Override
         public String toString() {
-            return
-                "{ graphicsMode: '"+graphicsMode+"'"+
-                ", scale: { w: "+scale.getWidth()+", h: "+scale.getHeight()+" },"+
-                super.toString().substring(1);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "boundsIgnored", () -> boundsIgnored,
+                "graphicsMode", this::getGraphicsMode
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
         }
     }
 
@@ -240,15 +256,18 @@ public class HemfText {
 
         @Override
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
-            /**
-             * A 32-bit unsigned integer that specifies text alignment by using a mask of text alignment flags.
-             * These are either WMF TextAlignmentMode Flags for text with a horizontal baseline,
-             * or WMF VerticalTextAlignmentMode Flags for text with a vertical baseline.
-             * Only one value can be chosen from those that affect horizontal and vertical alignment.
-             */
+            // A 32-bit unsigned integer that specifies text alignment by using a mask of text alignment flags.
+            // These are either WMF TextAlignmentMode Flags for text with a horizontal baseline,
+            // or WMF VerticalTextAlignmentMode Flags for text with a vertical baseline.
+            // Only one value can be chosen from those that affect horizontal and vertical alignment.
             textAlignmentMode = (int)leis.readUInt();
             return LittleEndianConsts.INT_SIZE;
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -264,6 +283,11 @@ public class HemfText {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return colorRef.init(leis);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
 
@@ -297,7 +321,24 @@ public class HemfText {
 
         @Override
         public String toString() {
-            return "{ index: "+fontIdx+", font: "+font+" } ";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public int getFontIdx() {
+            return fontIdx;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "base", super::getGenericProperties,
+                "fontIdx", this::getFontIdx
+            );
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
         }
     }
 
@@ -306,7 +347,7 @@ public class HemfText {
         public int init(LittleEndianInputStream leis) {
             // A 32-bit unsigned integer that specifies how to use the rectangle specified in the Rectangle field.
             // This field can be a combination of more than one ExtTextOutOptions enumeration
-            flag = (int)leis.readUInt();
+            flags = (int)leis.readUInt();
             return LittleEndianConsts.INT_SIZE;
         }
     }
index a312dc116e6330ed1712d3c91d5cc16f522ff004..d95b9d94a030669bd324f2220322a9550b1a7b6f 100644 (file)
@@ -21,15 +21,21 @@ import static org.apache.poi.hemf.record.emf.HemfDraw.readDimensionInt;
 import static org.apache.poi.hemf.record.emf.HemfDraw.readPointL;
 import static org.apache.poi.hwmf.record.HwmfDraw.normalizeBounds;
 
+import java.awt.geom.Dimension2D;
 import java.io.IOException;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hemf.draw.HemfDrawProperties;
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hwmf.record.HwmfRegionMode;
 import org.apache.poi.hwmf.record.HwmfWindowing;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
+@SuppressWarnings("WeakerAccess")
 public class HemfWindowing {
 
     /**
@@ -45,6 +51,11 @@ public class HemfWindowing {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return readDimensionInt(leis, size);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -60,6 +71,11 @@ public class HemfWindowing {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return readPointL(leis, origin);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -75,6 +91,11 @@ public class HemfWindowing {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return readDimensionInt(leis, extents);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -90,6 +111,11 @@ public class HemfWindowing {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return readPointL(leis, origin);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -106,6 +132,11 @@ public class HemfWindowing {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return readPointL(leis, offset);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -122,6 +153,11 @@ public class HemfWindowing {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return HemfDraw.readRectL(leis, bounds);
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -138,6 +174,11 @@ public class HemfWindowing {
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
             return HemfDraw.readRectL(leis, normalizeBounds(bounds));
         }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
+        }
     }
 
     /**
@@ -152,12 +193,12 @@ public class HemfWindowing {
 
         @Override
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
-            double xNum = leis.readInt();
-            double xDenom = leis.readInt();
-            double yNum = leis.readInt();
-            double yDenom = leis.readInt();
-            scale.setSize(xNum / xDenom, yNum / yDenom);
-            return 4*LittleEndianConsts.INT_SIZE;
+            return readScale(leis, scale);
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
         }
     }
 
@@ -173,14 +214,12 @@ public class HemfWindowing {
 
         @Override
         public long init(LittleEndianInputStream leis, long recordSize, long recordId) throws IOException {
-            double xNum = leis.readInt();
-            double xDenom = leis.readInt();
-            double yNum = leis.readInt();
-            double yDenom = leis.readInt();
-
-            scale.setSize(xNum / xDenom, yNum / yDenom);
+            return readScale(leis, scale);
+        }
 
-            return 4*LittleEndianConsts.INT_SIZE;
+        @Override
+        public Enum getGenericRecordType() {
+            return getEmfRecordType();
         }
     }
 
@@ -213,8 +252,25 @@ public class HemfWindowing {
 
         @Override
         public String toString() {
-            return "{ regionMode: '"+regionMode+"' }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfRegionMode getRegionMode() {
+            return regionMode;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("regionMode", this::getRegionMode);
         }
     }
 
+    private static int readScale(LittleEndianInputStream leis, Dimension2D scale) {
+        double xNum = leis.readInt();
+        double xDenom = leis.readInt();
+        double yNum = leis.readInt();
+        double yDenom = leis.readInt();
+        scale.setSize(xNum / xDenom, yNum / yDenom);
+        return 4*LittleEndianConsts.INT_SIZE;
+    }
 }
\ No newline at end of file
index 12034376c412d7e7eebd19ae19654bf72693b0e2..6a2fb0e6c95693820a53dece6b94155f32fe0a99 100644 (file)
@@ -25,7 +25,7 @@ import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianInputStream;
 
 @Internal
-public class UnimplementedHemfRecord implements HemfRecord {
+public class UnimplementedHemfRecord implements HemfRecordWithoutProperties {
 
     private HemfRecordType recordType;
 
index 36d7b90ec70423c3040e06b20eac6a728bebff6b..5a2a1ee2547b311601e5b4033029a03a0e0a92c3 100644 (file)
 
 package org.apache.poi.hemf.record.emfplus;
 
-import static java.util.stream.Collectors.joining;
-import static org.apache.poi.hemf.record.emf.HemfDraw.xformToString;
 import static org.apache.poi.hemf.record.emf.HemfFill.readXForm;
 import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readARGB;
 import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readPointF;
 import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readRectF;
-import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
-import static org.apache.poi.hwmf.record.HwmfDraw.pointToString;
 
 import java.awt.Color;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
-import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
-import java.util.stream.Stream;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hemf.draw.HemfDrawProperties;
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hemf.record.emfplus.HemfPlusHeader.EmfPlusGraphicsVersion;
@@ -47,10 +45,10 @@ import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
 import org.apache.poi.hemf.record.emfplus.HemfPlusPath.EmfPlusPath;
 import org.apache.poi.hwmf.record.HwmfBrushStyle;
 import org.apache.poi.hwmf.record.HwmfColorRef;
-import org.apache.poi.hwmf.record.HwmfDraw;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
-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;
 
@@ -243,7 +241,7 @@ public class HemfPlusBrush {
 
     }
 
-    public interface EmfPlusBrushData {
+    public interface EmfPlusBrushData extends GenericRecord {
         /**
          * This flag is meaningful in EmfPlusPathGradientBrushData objects.
          *
@@ -343,9 +341,24 @@ public class HemfPlusBrush {
 
         @Override
         public String toString() {
-            return
-                "{ brushType: '"+brushType+"'" +
-                ", brushData: "+brushData+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public EmfPlusBrushData getBrushData() {
+            return brushData;
+        }
+
+        @Override
+        public EmfPlusBrushType getGenericRecordType() {
+            return brushType;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "graphicsVersion", this::getGraphicsVersion,
+                "brushData", this::getBrushData
+            );
         }
     }
 
@@ -366,7 +379,17 @@ public class HemfPlusBrush {
 
         @Override
         public String toString() {
-            return "{ solidColor: "+new HwmfColorRef(solidColor)+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return EmfPlusBrushType.SOLID_COLOR;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("solidColor", () -> solidColor);
         }
     }
 
@@ -392,10 +415,21 @@ public class HemfPlusBrush {
 
         @Override
         public String toString() {
-            return
-                "{ style: '"+style+"'" +
-                ", foreColor: "+new HwmfColorRef(foreColor) +
-                ", backColor: "+new HwmfColorRef(backColor) + " }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return EmfPlusBrushType.HATCH_FILL;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "style", () -> style,
+                "foreColor", () -> foreColor,
+                "backColor", () -> backColor
+            );
         }
     }
 
@@ -462,23 +496,31 @@ public class HemfPlusBrush {
 
         @Override
         public String toString() {
-            return
-                "{ flags: "+dataFlags+
-                ", wrapMode: '"+wrapMode+"'"+
-                ", rect: "+boundsToString(rect)+
-                ", startColor: "+new HwmfColorRef(startColor)+
-                ", endColor: "+new HwmfColorRef(endColor)+
-                ", transform: "+xformToString(transform)+
-                ", positions: "+ Arrays.toString(positions)+
-                ", blendColors: "+ colorsToString(blendColors)+
-                ", positionsV: "+ Arrays.toString(positionsV)+
-                ", blendFactorsV: "+ Arrays.toString(blendFactorsV)+
-                ", positionsH: "+ Arrays.toString(positionsH)+
-                ", blendFactorsH: "+ Arrays.toString(blendFactorsH)+
-                "}";
+            return GenericRecordJsonWriter.marshal(this);
         }
 
+        @Override
+        public Enum getGenericRecordType() {
+            return EmfPlusBrushType.LINEAR_GRADIENT;
+        }
 
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+            m.put("flags", () -> dataFlags);
+            m.put("wrapMode", () -> wrapMode);
+            m.put("rect", () -> rect);
+            m.put("startColor", () -> startColor);
+            m.put("endColor", () -> endColor);
+            m.put("transform", () -> transform);
+            m.put("positions", () -> positions);
+            m.put("blendColors", () -> blendColors);
+            m.put("positionsV", () -> positionsV);
+            m.put("blendFactorsV", () -> blendFactorsV);
+            m.put("positionsH", () -> positionsH);
+            m.put("blendFactorsH", () -> blendFactorsH);
+            return Collections.unmodifiableMap(m);
+        }
     }
 
     /** The EmfPlusPathGradientBrushData object specifies a path gradient for a graphics brush. */
@@ -593,22 +635,32 @@ public class HemfPlusBrush {
 
         @Override
         public String toString() {
-            return
-                "{ flags: "+dataFlags+
-                ", wrapMode: '"+wrapMode+"'"+
-                ", centerColor: "+new HwmfColorRef(centerColor)+
-                ", centerPoint: "+pointToString(centerPoint)+
-                ", surroundingColor: "+colorsToString(surroundingColor)+
-                ", boundaryPath: "+(boundaryPath == null ? "null" : boundaryPath)+
-                ", boundaryPoints: "+pointsToString(boundaryPoints)+
-                ", transform: "+xformToString(transform)+
-                ", positions: "+Arrays.toString(positions)+
-                ", blendColors: "+colorsToString(blendColors)+
-                ", blendFactorsH: "+Arrays.toString(blendFactorsH)+
-                ", focusScaleX: "+focusScaleX+
-                ", focusScaleY: "+focusScaleY+
-                "}"
-                ;
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+
+        @Override
+        public Enum getGenericRecordType() {
+            return EmfPlusBrushType.PATH_GRADIENT;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+            m.put("flags", () -> dataFlags);
+            m.put("wrapMode", () -> wrapMode);
+            m.put("centerColor", () -> centerColor);
+            m.put("centerPoint", () -> centerPoint);
+            m.put("surroundingColor", () -> surroundingColor);
+            m.put("boundaryPath", () -> boundaryPath);
+            m.put("boundaryPoints", () -> boundaryPoints);
+            m.put("transform", () -> transform);
+            m.put("positions", () -> positions);
+            m.put("blendColors", () -> blendColors);
+            m.put("blendFactorsH", () -> blendFactorsH);
+            m.put("focusScaleX", () -> focusScaleX);
+            m.put("focusScaleY", () -> focusScaleY);
+            return Collections.unmodifiableMap(m);
         }
     }
 
@@ -652,12 +704,22 @@ public class HemfPlusBrush {
 
         @Override
         public String toString() {
-            return
-                "{ flags: "+dataFlags+
-                ", wrapMode: '"+wrapMode+"'"+
-                ", transform: "+xformToString(transform)+
-                ", image: "+image+
-                "]";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Enum getGenericRecordType() {
+            return EmfPlusBrushType.TEXTURE_FILL;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "dataFlags", () -> dataFlags,
+                "wrapMode", () -> wrapMode,
+                "transform", () -> transform,
+                "image", () -> image
+            );
         }
     }
 
@@ -696,18 +758,4 @@ public class HemfPlusBrush {
         facs.accept(factors);
         return size + factors.length * LittleEndianConsts.INT_SIZE;
     }
-
-    @Internal
-    public static String colorsToString(Color[] colors) {
-        return (colors == null ? "null" :
-            Stream.of(colors).map(HwmfColorRef::new).map(Object::toString).
-            collect(joining(",", "{", "}")));
-    }
-
-    @Internal
-    public static String pointsToString(Point2D[] points) {
-        return (points == null ? "null" :
-            Stream.of(points).map(HwmfDraw::pointToString).
-            collect(joining(",", "{", "}")));
-    }
 }
index 036b544fdc3fc23d14ad31275e1e7506911cf76a..e254d91816f7f9beee4060a99f623201b60afc3b 100644 (file)
 
 package org.apache.poi.hemf.record.emfplus;
 
-import static java.util.stream.Collectors.joining;
-import static org.apache.poi.hemf.record.emf.HemfDraw.xformToString;
-import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
-import static org.apache.poi.hwmf.record.HwmfDraw.pointToString;
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
 
 import java.awt.Color;
 import java.awt.geom.AffineTransform;
@@ -32,8 +29,12 @@ import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.function.BiFunction;
+import java.util.function.Supplier;
 
 import org.apache.commons.math3.linear.LUDecomposition;
 import org.apache.commons.math3.linear.MatrixUtils;
@@ -46,16 +47,18 @@ import org.apache.poi.hemf.record.emfplus.HemfPlusMisc.EmfPlusObjectId;
 import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObject;
 import org.apache.poi.hwmf.record.HwmfBrushStyle;
 import org.apache.poi.hwmf.record.HwmfColorRef;
-import org.apache.poi.hwmf.record.HwmfDraw;
 import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
 import org.apache.poi.hwmf.record.HwmfTernaryRasterOp;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 import org.apache.poi.util.StringUtil;
 
+@SuppressWarnings("WeakerAccess")
 public class HemfPlusDraw {
     private static final int MAX_OBJECT_SIZE = 1_000_000;
 
@@ -185,6 +188,10 @@ public class HemfPlusDraw {
             return flags;
         }
 
+        public int getPenId() {
+            return penId;
+        }
+
         @Override
         public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
             this.flags = flags;
@@ -213,9 +220,20 @@ public class HemfPlusDraw {
 
         @Override
         public String toString() {
-            return
-                "{ flags: "+flags+
-                ", penId: "+penId+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public HemfPlusRecordType getGenericRecordType() {
+            return getEmfPlusRecordType();
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", this::getFlags,
+                "penId", this::getPenId
+            );
         }
     }
 
@@ -278,11 +296,25 @@ public class HemfPlusDraw {
 
         @Override
         public String toString() {
-            return
-                "{ flags: "+flags+
-                ", brushId: "+brushId+
-                ", rectData: "+rectData.stream().map(HwmfDraw::boundsToString).collect(joining(",", "{", "}"))+
-                "}";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public HemfPlusRecordType getGenericRecordType() {
+            return getEmfPlusRecordType();
+        }
+
+        public List<Rectangle2D> getRectData() {
+            return rectData;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", this::getFlags,
+                "brushId", this::getBrushId,
+                "rectData", this::getRectData
+            );
         }
     }
 
@@ -428,17 +460,21 @@ public class HemfPlusDraw {
 
         @Override
         public String toString() {
-            return
-                "{ flags: "+flags+
-                ", imageAttributesID: "+imageAttributesID+
-                ", srcUnit: '"+srcUnit+"'"+
-                ", srcRect: "+boundsToString(srcRect)+
-                ", upperLeft: "+pointToString(upperLeft)+
-                ", lowerLeft: "+pointToString(lowerLeft)+
-                ", lowerRight: "+pointToString(lowerRight)+
-                ", transform: "+xformToString(trans)+
-                "}"
-                ;
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+            m.put("flags", this::getFlags);
+            m.put("imageAttributesID", () -> imageAttributesID);
+            m.put("srcUnit", () -> srcUnit);
+            m.put("srcRect", () -> srcRect);
+            m.put("upperLeft", () -> upperLeft);
+            m.put("lowerLeft", () -> lowerLeft);
+            m.put("lowerRight", () -> lowerRight);
+            m.put("transform", () -> trans);
+            return Collections.unmodifiableMap(m);
         }
     }
 
@@ -500,14 +536,18 @@ public class HemfPlusDraw {
 
         @Override
         public String toString() {
-            return
-                "{ flags: "+flags+
-                ", imageAttributesID: "+imageAttributesID+
-                ", srcUnit: '"+srcUnit+"'"+
-                ", srcRect: "+boundsToString(srcRect)+
-                ", rectData: "+boundsToString(rectData)+
-                "}"
-                ;
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", this::getFlags,
+                "imageAttributesID", () -> imageAttributesID,
+                "srcUnit", () -> srcUnit,
+                "srcRect", () -> srcRect,
+                "rectData", () -> rectData
+            );
         }
     }
 
@@ -554,9 +594,15 @@ public class HemfPlusDraw {
 
         @Override
         public String toString() {
-            return
-                "{ flags: "+flags+
-                ", brushId: "+brushId+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", this::getFlags,
+                "brushId", () -> brushId
+            );
         }
     }
 
@@ -596,7 +642,11 @@ public class HemfPlusDraw {
          */
         private static final BitField LIMIT_SUBPIXEL = BitFieldFactory.getInstance(0x0008);
 
+        private static final int[] OPTIONS_MASK = { 0x0001, 0x0002, 0x0004, 0x0008 };
 
+        private static final String[] OPTIONS_NAMES = {
+            "CMAP_LOOKUP", "VERTICAL", "REALIZED_ADVANCE", "LIMIT_SUBPIXEL"
+        };
 
         private int flags;
         private int brushId;
@@ -671,6 +721,23 @@ public class HemfPlusDraw {
 
             return size;
         }
+
+        @Override
+        public String toString() {
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", this::getFlags,
+                "brushId", this::getBrushId,
+                "optionsFlags", getBitsAsString(() -> optionsFlags, OPTIONS_MASK, OPTIONS_NAMES),
+                "glyphs", () -> glyphs,
+                "glyphPos", () -> glpyhPos,
+                "transform", () -> transformMatrix
+            );
+        }
     }
 
     /** The EmfPlusDrawRects record specifies drawing a series of rectangles. */
@@ -705,6 +772,19 @@ public class HemfPlusDraw {
 
             return size;
         }
+
+        @Override
+        public String toString() {
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", this::getFlags,
+                "rectData", () -> rectData
+            );
+        }
     }
 
 
index a2201320b42086557938ed815aeae121b1a91e42..e0a2fc759fb843328f03d123dfdceba0624ea67f 100644 (file)
@@ -19,13 +19,18 @@ package org.apache.poi.hemf.record.emfplus;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hemf.record.emfplus.HemfPlusDraw.EmfPlusUnitType;
 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.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 import org.apache.poi.util.StringUtil;
@@ -69,7 +74,7 @@ public class HemfPlusFont {
         private String family;
 
         @Override
-        public long init(LittleEndianInputStream leis, long dataSize, HemfPlusObject.EmfPlusObjectType objectType, int flags) throws IOException {
+        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);
@@ -108,5 +113,26 @@ public class HemfPlusFont {
         public EmfPlusGraphicsVersion getGraphicsVersion() {
             return graphicsVersion;
         }
+
+        @Override
+        public String toString() {
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "graphicsVersion", () -> graphicsVersion,
+                "emSize", () -> emSize,
+                "sizeUnit", () -> sizeUnit,
+                "styleFlags", () -> styleFlags,
+                "family", () -> family
+            );
+        }
+
+        @Override
+        public EmfPlusObjectType getGenericRecordType() {
+            return EmfPlusObjectType.FONT;
+        }
     }
 }
index aa93853173862c914289ea4afadbe477605a2fc9..36209f289eaeec8f33d23b9b41c46497fcdea66e 100644 (file)
 package org.apache.poi.hemf.record.emfplus;
 
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+
 import java.io.IOException;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hemf.draw.HemfGraphics.EmfRenderState;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
@@ -53,6 +59,8 @@ public class HemfPlusHeader implements HemfPlusRecord {
         }
     }
 
+    private static final int[] FLAGS_MASK = { 0x0001 };
+    private static final String[] FLAGS_NAMES = { "DUAL_MODE" };
 
     private int flags;
     private final EmfPlusGraphicsVersion version = new EmfPlusGraphicsVersion();
@@ -121,13 +129,18 @@ public class HemfPlusHeader implements HemfPlusRecord {
 
     @Override
     public String toString() {
-        return "HemfPlusHeader{" +
-                "flags=" + flags +
-                ", version=" + version +
-                ", emfPlusFlags=" + emfPlusFlags +
-                ", logicalDpiX=" + logicalDpiX +
-                ", logicalDpiY=" + logicalDpiY +
-                '}';
+        return GenericRecordJsonWriter.marshal(this);
+    }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "flags", this::getFlags,
+            "version", this::getVersion,
+            "emfPlusFlags", getBitsAsString(this::getEmfPlusFlags, FLAGS_MASK, FLAGS_NAMES),
+            "logicalDpiX", this::getLogicalDpiX,
+            "logicalDpiY", this::getLogicalDpiY
+        );
     }
 
     public static class EmfPlusGraphicsVersion {
index ffa9d9c8e9d60e90006ee4889017c3ffca7ff6ca..0748ec717a45ddf2c85c930ef6d89aa6c7529e66 100644 (file)
@@ -36,8 +36,12 @@ import java.awt.image.WritableRaster;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.function.BiConsumer;
+import java.util.function.Supplier;
 
 import javax.imageio.ImageIO;
 
@@ -50,6 +54,8 @@ import org.apache.poi.hemf.usermodel.HemfPicture;
 import org.apache.poi.hwmf.usermodel.HwmfPicture;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
@@ -280,7 +286,6 @@ public class HemfPlusImage {
     public static class EmfPlusImage implements EmfPlusObjectData {
         private static final int MAX_OBJECT_SIZE = 50_000_000;
 
-
         private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
         private EmfPlusImageDataType imageDataType;
         private int bitmapWidth;
@@ -595,9 +600,35 @@ public class HemfPlusImage {
 
             return bufImg;
         }
-    }
 
+        @Override
+        public String toString() {
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public EmfPlusObjectType getGenericRecordType() {
+            return EmfPlusObjectType.IMAGE;
+        }
 
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+
+            m.put("graphicsVersion", this::getGraphicsVersion);
+            m.put("imageDataType", this::getImageDataType);
+            m.put("bitmapWidth", this::getBitmapWidth);
+            m.put("bitmapHeight", this::getBitmapHeight);
+            m.put("bitmapStride", this::getBitmapStride);
+            m.put("pixelFormat", this::getPixelFormat);
+            m.put("bitmapType", this::getBitmapType);
+            m.put("imageData", this::getImageData);
+            m.put("metafileType", this::getMetafileType);
+            m.put("metafileDataSize", () -> metafileDataSize);
+
+            return Collections.unmodifiableMap(m);
+        }
+    }
 
     public static class EmfPlusImageAttributes implements EmfPlusObjectData {
         private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
@@ -650,6 +681,26 @@ public class HemfPlusImage {
         @Override
         public void applyObject(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
         }
+
+        @Override
+        public String toString() {
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "graphicsVersion", this::getGraphicsVersion,
+                "wrapMode", this::getWrapMode,
+                "clampColor", this::getClampColor,
+                "objectClamp", this::getObjectClamp
+            );
+        }
+
+        @Override
+        public EmfPlusObjectType getGenericRecordType() {
+            return EmfPlusObjectType.IMAGE_ATTRIBUTES;
+        }
     }
 
 }
index 2d006563abf79ea8a1c498bfb65b2eb882aed9b2..94566a92b1943dad042e7d24638ef1a92071c552 100644 (file)
@@ -19,19 +19,24 @@ package org.apache.poi.hemf.record.emfplus;
 
 import static org.apache.poi.hemf.record.emf.HemfMisc.adaptXForm;
 import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readRectF;
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
 
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hemf.record.emf.HemfFill;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
+@SuppressWarnings("WeakerAccess")
 public class HemfPlusMisc {
     public interface EmfPlusObjectId {
         BitField OBJECT_ID = BitFieldFactory.getInstance(0x00FF);
@@ -75,6 +80,9 @@ public class HemfPlusMisc {
         private int flags;
         private HemfPlusRecordType recordType;
 
+        private static final int[] FLAGS_MASK = { 0x0F00 };
+        private static final String[] FLAGS_NAMES = { "COMBINE_MODE" };
+
         @Override
         public int getFlags() {
             return flags;
@@ -92,6 +100,18 @@ public class HemfPlusMisc {
             recordType = HemfPlusRecordType.getById(recordId);
             return 0;
         }
+
+        @Override
+        public HemfPlusRecordType getGenericRecordType() {
+            return getEmfPlusRecordType();
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", getBitsAsString(this::getFlags,FLAGS_MASK,FLAGS_NAMES)
+            );
+        }
     }
 
     public static class EmfPlusEOF extends EmfPlusFlagOnly {
@@ -193,6 +213,14 @@ public class HemfPlusMisc {
             tx.concatenate(getMatrixData());
             ctx.setTransform(tx);
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", this::getFlags,
+                "matrixData", this::getMatrixData
+            );
+        }
     }
 
     /**
@@ -239,6 +267,18 @@ public class HemfPlusMisc {
             pageScale = leis.readFloat();
             return LittleEndianConsts.INT_SIZE;
         }
+
+        public double getPageScale() {
+            return pageScale;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", this::getFlags,
+                "pageScale", this::getPageScale
+            );
+        }
     }
 
     /**
@@ -264,6 +304,9 @@ public class HemfPlusMisc {
     public static class EmfPlusSetClipRect implements HemfPlusRecord {
         private static final BitField COMBINE_MODE = BitFieldFactory.getInstance(0x0F00);
 
+        private static final int[] FLAGS_MASK = { 0x0F00 };
+        private static final String[] FLAGS_NAMES = { "COMBINE_MODE" };
+
         private int flags;
         private final Rectangle2D clipRect = new Rectangle2D.Double();
 
@@ -288,6 +331,18 @@ public class HemfPlusMisc {
             // An EmfPlusRectF object that defines the rectangle to use in the CombineMode operation.
             return readRectF(leis, clipRect);
         }
+
+        public Rectangle2D getClipRect() {
+            return clipRect;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", getBitsAsString(this::getFlags, FLAGS_MASK, FLAGS_NAMES),
+                "clipRect", this::getClipRect
+            );
+        }
     }
 
 
@@ -314,6 +369,10 @@ public class HemfPlusMisc {
             return flags;
         }
 
+        public int getStackIndex() {
+            return stackIndex;
+        }
+
         @Override
         public long init(LittleEndianInputStream leis, long dataSize, long recordId, int flags) throws IOException {
             this.flags = flags;
@@ -324,6 +383,14 @@ public class HemfPlusMisc {
 
             return LittleEndianConsts.INT_SIZE;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", this::getFlags,
+                "stackIndex", this::getStackIndex
+            );
+        }
     }
 
     /**
@@ -369,6 +436,14 @@ public class HemfPlusMisc {
 
             return LittleEndianConsts.INT_SIZE*2;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", this::getFlags,
+                "origin", this::getOrigin
+            );
+        }
     }
 
 }
index 4d23bab6e068074cf94167f293028b344518dabe..6b0c75478861a56d60a3676e32edf45e0220c7fc 100644 (file)
 
 package org.apache.poi.hemf.record.emfplus;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+
 import java.io.IOException;
 import java.util.ArrayList;
 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.HemfPlusBrush.EmfPlusBrush;
 import org.apache.poi.hemf.record.emfplus.HemfPlusFont.EmfPlusFont;
@@ -36,6 +40,7 @@ import org.apache.poi.hwmf.draw.HwmfGraphics;
 import org.apache.poi.hwmf.record.HwmfObjectTableEntry;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
@@ -113,8 +118,6 @@ public class HemfPlusObject {
      * can span multiple records), which is indicated by the value of the Flags field.
      */
     public static class EmfPlusObject implements HemfPlusRecord, EmfPlusObjectId, HwmfObjectTableEntry {
-
-
         /**
          * Indicates that the object definition continues on in the next EmfPlusObject
          * record. This flag is never set in the final record that defines the object.
@@ -127,6 +130,10 @@ public class HemfPlusObject {
          */
         private static final BitField OBJECT_TYPE = BitFieldFactory.getInstance(0x7F00);
 
+        private static final int[] FLAGS_MASKS = { 0x7F00, 0x8000 };
+
+        private static final String[] FLAGS_NAMES = { "OBJECT_TYPE", "CONTINUABLE" };
+
         private int flags;
         // for debugging
         private int objectId;
@@ -211,9 +218,20 @@ public class HemfPlusObject {
         List<EmfPlusObjectData> getContinuedObject() {
             return continuedObjectData;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", getBitsAsString(this::getFlags, FLAGS_MASKS, FLAGS_NAMES),
+                "objectId", this::getObjectId,
+                "continuedObjectData", this::getContinuedObject,
+                "totalObjectSize", () -> totalObjectSize
+            );
+        }
+
     }
 
-    public interface EmfPlusObjectData {
+    public interface EmfPlusObjectData extends GenericRecord {
         long init(LittleEndianInputStream leis, long dataSize, EmfPlusObjectType objectType, int flags) throws IOException;
 
         void applyObject(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData);
@@ -251,5 +269,18 @@ public class HemfPlusObject {
         public EmfPlusGraphicsVersion getGraphicsVersion() {
             return graphicsVersion;
         }
+
+        @Override
+        public EmfPlusObjectType getGenericRecordType() {
+            return objectType;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "graphicsVersion", this::getGraphicsVersion,
+                "objectDataBytes", () -> objectDataBytes
+            );
+        }
     }
 }
index c8405fece68c879ad0d4193a45e354042b6fdc21..71d71b73ebfe2f8f04f37f4ec8574ea0d3362821 100644 (file)
 
 package org.apache.poi.hemf.record.emfplus;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+
 import java.awt.geom.Path2D;
 import java.awt.geom.Point2D;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.function.BiFunction;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hemf.draw.HemfDrawProperties;
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hemf.record.emfplus.HemfPlusDraw.EmfPlusCompressed;
@@ -32,6 +39,7 @@ import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectData;
 import org.apache.poi.hemf.record.emfplus.HemfPlusObject.EmfPlusObjectType;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
@@ -73,6 +81,12 @@ public class HemfPlusPath {
 
         private static final BitField POINT_RLE_COUNT = BitFieldFactory.getInstance(0x3F);
 
+        private static final int[] FLAGS_MASKS = { 0x0800, 0x1000, 0x4000 };
+        private static final String[] FLAGS_NAMES = { "RELATIVE_POSITION", "RLE_COMPRESSED", "FORMAT_COMPRESSED" };
+
+        private static final int[] TYPE_MASKS = { 0x10, 0x20, 0x80 };
+        private static final String[] TYPE_NAMES = { "DASHED", "MARKER", "CLOSE" };
+
         private final HemfPlusHeader.EmfPlusGraphicsVersion graphicsVersion = new HemfPlusHeader.EmfPlusGraphicsVersion();
         private int pointFlags;
         private Point2D[] pathPoints;
@@ -154,7 +168,9 @@ public class HemfPlusPath {
             return pointFlags;
         }
 
-
+        public Point2D getPoint(int index) {
+            return pathPoints[index];
+        }
 
         @Override
         public void applyObject(HemfGraphics ctx, List<? extends EmfPlusObjectData> continuedObjectData) {
@@ -184,8 +200,32 @@ public class HemfPlusPath {
             }
         }
 
+        @Override
+        public EmfPlusObjectType getGenericRecordType() {
+            return EmfPlusObjectType.PATH;
+        }
 
-    }
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "graphicsVersion", this::getGraphicsVersion,
+                "flags", getBitsAsString(this::getFlags, FLAGS_MASKS, FLAGS_NAMES),
+                "points", this::getGenericPoints
+            );
+        }
 
+        private List<GenericRecord> getGenericPoints() {
+            return IntStream.range(0, pathPoints.length).
+                mapToObj(this::getGenericPoint).
+                collect(Collectors.toList());
+        }
 
+        private GenericRecord getGenericPoint(final int idx) {
+            return () -> GenericRecordUtil.getGenericProperties(
+                "flags", getBitsAsString(() -> pointTypes[idx], TYPE_MASKS, TYPE_NAMES),
+                "type", () -> getPointType(idx),
+                "point", () -> getPoint(idx)
+            );
+        }
+    }
 }
index c412b425a7a0c805037efb3abdf8f22ab86686f6..4540aab3b009d2a0b22419cf1d55899eae79262c 100644 (file)
@@ -19,13 +19,19 @@ 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 static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
 
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Point2D;
 import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hemf.draw.HemfDrawProperties;
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hemf.record.emfplus.HemfPlusDraw.EmfPlusUnitType;
@@ -40,6 +46,7 @@ import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
+@SuppressWarnings("WeakerAccess")
 public class HemfPlusPen {
     /**
      * The LineCapType enumeration defines types of line caps to use at the ends of lines that are drawn
@@ -222,7 +229,7 @@ public class HemfPlusPen {
 
 
     @Internal
-    public interface EmfPlusCustomLineCap {
+    public interface EmfPlusCustomLineCap extends GenericRecord {
         long init(LittleEndianInputStream leis) throws IOException;
     }
 
@@ -290,6 +297,18 @@ public class HemfPlusPen {
          */
         private final static BitField CUSTOM_END_CAP = BitFieldFactory.getInstance(0x00001000);
 
+        private static final int[] FLAGS_MASKS = {
+            0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010,
+            0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200,
+            0x00000400, 0x00000800, 0x00001000
+        };
+
+        private static final String[] FLAGS_NAMES = {
+            "TRANSFORM", "START_CAP", "END_CAP", "JOIN", "MITER_LIMIT",
+            "LINE_STYLE", "DASHED_LINE_CAP", "DASHED_LINE_OFFSET", "DASHED_LINE", "NON_CENTER",
+            "COMPOUND_LINE", "CUSTOM_START_CAP", "CUSTOM_END_CAP"
+        };
+
         private final EmfPlusGraphicsVersion graphicsVersion = new EmfPlusGraphicsVersion();
 
 
@@ -528,6 +547,34 @@ public class HemfPlusPen {
                 }
             });
         }
+
+        @Override
+        public EmfPlusObjectType getGenericRecordType() {
+            return EmfPlusObjectType.PEN;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+            m.put("type", () -> type);
+            m.put("flags", getBitsAsString(() -> penDataFlags, FLAGS_MASKS, FLAGS_NAMES));
+            m.put("unitType", () -> unitType);
+            m.put("penWidth", () -> penWidth);
+            m.put("trans", () -> trans);
+            m.put("startCap", () -> startCap);
+            m.put("endCap", () -> endCap);
+            m.put("join", () -> join);
+            m.put("miterLimit", () -> miterLimit);
+            m.put("style", () -> style);
+            m.put("dashedLineCapType", () -> dashedLineCapType);
+            m.put("dashOffset", () -> dashOffset);
+            m.put("dashedLineData", () -> dashedLineData);
+            m.put("penAlignment", () -> penAlignment);
+            m.put("compoundLineData", () -> compoundLineData);
+            m.put("customStartCap", () -> customStartCap);
+            m.put("customEndCap", () -> customEndCap);
+            return Collections.unmodifiableMap(m);
+        }
     }
 
     public static class EmfPlusPathArrowCap implements EmfPlusCustomLineCap {
@@ -542,6 +589,9 @@ public class HemfPlusPen {
          */
         private static final BitField LINE_PATH = BitFieldFactory.getInstance(0x00000002);
 
+        private static final int[] FLAGS_MASKS = { 0x00000001, 0x00000002 };
+
+        private static final String[] FLAGS_NAMES = { "FILL_PATH", "LINE_PATH" };
 
         private int dataFlags;
         private EmfPlusLineCapType baseCap;
@@ -549,7 +599,7 @@ public class HemfPlusPen {
         private EmfPlusLineCapType startCap;
         private EmfPlusLineCapType endCap;
         private EmfPlusLineJoin join;
-        private double mitterLimit;
+        private double miterLimit;
         private double widthScale;
         private final Point2D fillHotSpot = new Point2D.Double();
         private final Point2D lineHotSpot = new Point2D.Double();
@@ -582,7 +632,7 @@ public class HemfPlusPen {
 
             // 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();
+            miterLimit = 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.
@@ -606,6 +656,24 @@ public class HemfPlusPen {
 
             return size;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+            m.put("flags", getBitsAsString(() -> dataFlags, FLAGS_MASKS, FLAGS_NAMES));
+            m.put("baseCap", () -> baseCap);
+            m.put("baseInset", () -> baseInset);
+            m.put("startCap", () -> startCap);
+            m.put("endCap", () -> endCap);
+            m.put("join", () -> join);
+            m.put("miterLimit", () -> miterLimit);
+            m.put("widthScale", () -> widthScale);
+            m.put("fillHotSpot", () -> fillHotSpot);
+            m.put("lineHotSpot", () -> lineHotSpot);
+            m.put("fillPath", () -> fillPath);
+            m.put("outlinePath", () -> outlinePath);
+            return Collections.unmodifiableMap(m);
+        }
     }
 
     public static class EmfPlusAdjustableArrowCap implements EmfPlusCustomLineCap {
@@ -616,7 +684,7 @@ public class HemfPlusPen {
         private EmfPlusLineCapType startCap;
         private EmfPlusLineCapType endCap;
         private EmfPlusLineJoin join;
-        private double mitterLimit;
+        private double miterLimit;
         private double widthScale;
         private final Point2D fillHotSpot = new Point2D.Double();
         private final Point2D lineHotSpot = new Point2D.Double();
@@ -655,7 +723,7 @@ public class HemfPlusPen {
 
             // 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();
+            miterLimit = 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.
@@ -671,5 +739,23 @@ public class HemfPlusPen {
 
             return size;
         }
+
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+            m.put("width", () -> width);
+            m.put("height", () -> height);
+            m.put("middleInset", () -> middleInset);
+            m.put("isFilled", () -> isFilled);
+            m.put("startCap", () -> startCap);
+            m.put("endCap", () -> endCap);
+            m.put("join", () -> join);
+            m.put("miterLimit", () -> miterLimit);
+            m.put("widthScale", () -> widthScale);
+            m.put("fillHotSpot", () -> fillHotSpot);
+            m.put("lineHotSpot", () -> lineHotSpot);
+            return Collections.unmodifiableMap(m);
+        }
     }
 }
index c074b66704358b25acc26dcf3445f07a7ce5a2ae..608bba7e7fa7e3c8a10667e972f9d03e9c51343a 100644 (file)
@@ -20,12 +20,13 @@ package org.apache.poi.hemf.record.emfplus;
 
 import java.io.IOException;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianInputStream;
 
 @Internal
-public interface HemfPlusRecord {
+public interface HemfPlusRecord extends GenericRecord {
 
     HemfPlusRecordType getEmfPlusRecordType();
 
index 7caeff1f3b3ba16567ac4b4fe2217f6efd23c207..f8e43fc646eccdd3094a5e58467a2967b18c0e68 100644 (file)
@@ -22,14 +22,17 @@ import static org.apache.poi.hemf.record.emfplus.HemfPlusDraw.readRectF;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Consumer;
 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.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.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
@@ -122,10 +125,23 @@ public class HemfPlusRegion {
         public EmfPlusGraphicsVersion getGraphicsVersion() {
             return graphicsVersion;
         }
+
+        @Override
+        public EmfPlusObjectType getGenericRecordType() {
+            return EmfPlusObjectType.REGION;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "graphicsVersion", this::getGraphicsVersion,
+                "regionNode", () -> regionNode
+            );
+        }
     }
 
 
-    public interface EmfPlusRegionNodeData {
+    public interface EmfPlusRegionNodeData extends GenericRecord {
         long init(LittleEndianInputStream leis) throws IOException;
     }
 
@@ -141,6 +157,11 @@ public class HemfPlusRegion {
         public long init(LittleEndianInputStream leis) throws IOException {
             return 0;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return null;
+        }
     }
 
     public static class EmfPlusRegionEmpty implements EmfPlusRegionNodeData {
@@ -148,6 +169,11 @@ public class HemfPlusRegion {
         public long init(LittleEndianInputStream leis) throws IOException {
             return 0;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return null;
+        }
     }
 
     public static class EmfPlusRegionRect implements EmfPlusRegionNodeData {
@@ -157,6 +183,11 @@ public class HemfPlusRegion {
         public long init(LittleEndianInputStream leis) {
             return readRectF(leis, rect);
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("rect", () -> rect);
+        }
     }
 
     public static class EmfPlusRegionNode implements EmfPlusRegionNodeData {
@@ -168,8 +199,15 @@ public class HemfPlusRegion {
             size += readNode(leis, n -> right = n);
             return size;
         }
-    }
 
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "left", () -> left,
+                "right", () -> right
+            );
+        }
+    }
 
     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.
index bc7152cd7e3edbde4d841a01a15465bb324c6235..9ca1b375eb6b015677d5b231b5c766e167670851 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.hemf.record.emfplus;
 
 
 import java.io.IOException;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianInputStream;
@@ -56,4 +59,12 @@ public class UnimplementedHemfPlusRecord implements HemfPlusRecord {
         //should probably defensively return a copy.
         return recordBytes;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "flags", this::getFlags,
+            "recordBytes", () -> recordBytes
+        );
+    }
 }
\ No newline at end of file
index b6cacdbaa9bff599a483807939295f3c4ad57378..92030fa02c284d599254270f4f8dd26ca1b2eae8 100644 (file)
@@ -30,9 +30,12 @@ import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Spliterator;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hemf.draw.HemfDrawProperties;
 import org.apache.poi.hemf.draw.HemfGraphics;
 import org.apache.poi.hemf.record.emf.HemfHeader;
@@ -49,7 +52,7 @@ import org.apache.poi.util.Units;
  * Read-only EMF extractor.  Lots remain
  */
 @Internal
-public class HemfPicture implements Iterable<HemfRecord> {
+public class HemfPicture implements Iterable<HemfRecord>, GenericRecord {
     private final LittleEndianInputStream stream;
     private final List<HemfRecord> records = new ArrayList<>();
     private boolean isParsed = false;
@@ -179,4 +182,14 @@ public class HemfPicture implements Iterable<HemfRecord> {
     public Iterable<HwmfEmbedded> getEmbeddings() {
         return () -> new HemfEmbeddedIterator(HemfPicture.this);
     }
+
+    @Override
+    public List<? extends GenericRecord> getGenericChildren() {
+        return getRecords();
+    }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return null;
+    }
 }
index ec01408930f8d2fd0e65d34d0ee411e7abdd5199..e28c0d713a679abc55184aad068bc526f61cb76b 100644 (file)
 
 package org.apache.poi.hslf.model.textproperties;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
@@ -178,5 +184,13 @@ public abstract class BitMaskTextProp extends TextProp implements Cloneable {
        
     public BitMaskTextProp cloneAll(){
         return (BitMaskTextProp)super.clone();
-    }  
+    }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "base", super::getGenericProperties,
+                       "flags", getBitsAsString(this::getValue, subPropMasks, subPropNames)
+               );
+       }
 }
\ No newline at end of file
index 904feeedf8ded5475815627bbcc6b35f0be3a14f..7c87b5d16a30e9a9ad7c091edb92f751818e55d4 100644 (file)
 
 package org.apache.poi.hslf.model.textproperties;
 
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.sl.usermodel.TextParagraph.FontAlign;
+import org.apache.poi.util.GenericRecordUtil;
+
 /**
  * Definition for the font alignment property.
  */
@@ -30,4 +36,27 @@ public class FontAlignmentProp extends TextProp {
        public FontAlignmentProp() {
                super(2, 0x10000, NAME);
        }
+
+       public FontAlign getFontAlign() {
+               switch (getValue()) {
+                       default:
+                               return FontAlign.AUTO;
+                       case BASELINE:
+                               return FontAlign.BASELINE;
+                       case TOP:
+                               return FontAlign.TOP;
+                       case CENTER:
+                               return FontAlign.CENTER;
+                       case BOTTOM:
+                               return FontAlign.BOTTOM;
+               }
+       }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "base", super::getGenericProperties,
+                       "fontAlign", this::getFontAlign
+               );
+       }
 }
\ No newline at end of file
index 78cc3fab4d24da76521776d3c0eaab04e257c59d..5f747c109fdbe2b3bdf0c6bf6630507737d32070 100644 (file)
 
 package org.apache.poi.hslf.model.textproperties;
 
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.sl.usermodel.TabStop;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.Units;
 
 @Internal
-public class HSLFTabStop implements TabStop, Cloneable {
+public class HSLFTabStop implements TabStop, Cloneable, GenericRecord {
     /**
      * A signed integer that specifies an offset, in master units, of the tab stop.
      * 
@@ -113,4 +118,12 @@ public class HSLFTabStop implements TabStop, Cloneable {
     public String toString() {
         return type + " @ " + position;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "type", this::getType,
+            "position", this::getPosition
+        );
+    }
 }
index dca666903bcf715d29473407aee69c718f759dad..92b6abbf8128f85792b5980d9248c57e7895686a 100644 (file)
@@ -20,8 +20,11 @@ package org.apache.poi.hslf.model.textproperties;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.sl.usermodel.TabStop.TabStopType;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianByteArrayInputStream;
 import org.apache.poi.util.LittleEndianConsts;
@@ -150,5 +153,13 @@ public class HSLFTabStopPropCollection extends TextProp {
         
         return sb.toString();
     }
-    
+
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "tabStops", this::getTabStops
+        );
+    }
 }
index 40adf46d8f819cdd473e666cbcd8391725c5db20..e62b4b9b6304deea47f2c3fefd7ac4510e3257b4 100644 (file)
 
 package org.apache.poi.hslf.model.textproperties;
 
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hslf.record.TxMasterStyleAtom;
+import org.apache.poi.util.GenericRecordUtil;
 
 /** 
  * Definition of the indent level of some text. Defines how many
@@ -25,7 +30,7 @@ import org.apache.poi.hslf.record.TxMasterStyleAtom;
  * 
  * This is defined by the slightly confusingly named MasterTextPropRun 
  */
-public class IndentProp  {
+public class IndentProp implements GenericRecord {
     private int charactersCovered;
     private short indentLevel;
 
@@ -62,4 +67,12 @@ public class IndentProp  {
     public void updateTextSize(int textSize) {
         charactersCovered = textSize;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "charactersCovered", this::getCharactersCovered,
+            "indentLevel", this::getIndentLevel
+        );
+    }
 }
\ No newline at end of file
index 82430f0de261fd618fa767bad53a9f00eca7cfe0..62fc80cf0ee56146cca0c0e7fe094a4770b6db11 100644 (file)
 
 package org.apache.poi.hslf.model.textproperties;
 
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.sl.usermodel.TextParagraph.TextAlign;
+import org.apache.poi.util.GenericRecordUtil;
+
 /**
  * Definition for the text alignment property.
  */
@@ -63,4 +69,30 @@ public class TextAlignmentProp extends TextProp {
        public TextAlignmentProp() {
                super(2, 0x800, "alignment");
        }
+
+       public TextAlign getTextAlign() {
+               switch (getValue()) {
+                       default:
+                       case LEFT:
+                               return TextAlign.LEFT;
+                       case CENTER:
+                               return TextAlign.CENTER;
+                       case RIGHT:
+                               return TextAlign.RIGHT;
+                       case JUSTIFY:
+                               return TextAlign.JUSTIFY;
+                       case DISTRIBUTED:
+                               return TextAlign.DIST;
+                       case THAIDISTRIBUTED:
+                               return TextAlign.THAI_DIST;
+               }
+       }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "base", super::getGenericProperties,
+                       "textAlign", this::getTextAlign
+               );
+       }
 }
\ No newline at end of file
index 128bc836536e3a69a600a7937437b126ac0ac825..0ad60d0e7a5c42d30cae18167644320f4a46921b 100644 (file)
  */
 package org.apache.poi.hslf.model.textproperties;
 
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.sl.usermodel.AutoNumberingScheme;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -34,7 +39,11 @@ import org.apache.poi.util.LittleEndian;
  * @author Alex Nikiforov [mailto:anikif@gmail.com]
  *
  */
-public class TextPFException9 {
+public class TextPFException9 implements GenericRecord {
+
+       private final static AutoNumberingScheme DEFAULT_AUTONUMBER_SCHEME = AutoNumberingScheme.arabicPeriod;
+       private final static Short DEFAULT_START_NUMBER = 1;
+
        //private final byte mask1;
        //private final byte mask2;
        private final byte mask3;
@@ -42,9 +51,7 @@ public class TextPFException9 {
        private final Short bulletBlipRef;
        private final Short fBulletHasAutoNumber;
        private final AutoNumberingScheme autoNumberScheme;
-       private final static AutoNumberingScheme DEFAULT_AUTONUMBER_SHEME = AutoNumberingScheme.arabicPeriod;
        private final Short autoNumberStartNumber;
-       private final static Short DEFAULT_START_NUMBER = 1;
        private final int recordLength;
        public TextPFException9(final byte[] source, final int startIndex) { // NOSONAR
                //this.mask1 = source[startIndex];
@@ -86,23 +93,24 @@ public class TextPFException9 {
                return fBulletHasAutoNumber;
        }
        public AutoNumberingScheme getAutoNumberScheme() {
-               if (null != this.autoNumberScheme) {
-                       return this.autoNumberScheme;
-               }
-               if (null != this.fBulletHasAutoNumber && 1 == this.fBulletHasAutoNumber.shortValue()) {
-                       return DEFAULT_AUTONUMBER_SHEME;
+               if (autoNumberScheme != null) {
+                       return autoNumberScheme;
                }
-               return null;
+               return hasBulletAutoNumber() ? DEFAULT_AUTONUMBER_SCHEME : null;
        }
+
        public Short getAutoNumberStartNumber() {
-               if (null != this.autoNumberStartNumber) {
-                       return this.autoNumberStartNumber;
+               if (autoNumberStartNumber != null) {
+                       return autoNumberStartNumber;
                }
-               if (null != this.fBulletHasAutoNumber && 1 == this.fBulletHasAutoNumber.shortValue()) {
-                       return DEFAULT_START_NUMBER;
-               }
-               return null;
+               return hasBulletAutoNumber() ? DEFAULT_START_NUMBER : null;
+       }
+
+       private boolean hasBulletAutoNumber() {
+               final Short one = 1;
+               return one.equals(fBulletHasAutoNumber);
        }
+
        public int getRecordLength() {
                return recordLength;
        }
@@ -115,4 +123,14 @@ public class TextPFException9 {
                sb.append("autoNumberStartNumber: ").append(this.autoNumberStartNumber).append("\n");
                return sb.toString();
        }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "bulletBlipRef", this::getBulletBlipRef,
+                       "bulletHasAutoNumber", this::hasBulletAutoNumber,
+                       "autoNumberScheme", this::getAutoNumberScheme,
+                       "autoNumberStartNumber", this::getAutoNumberStartNumber
+               );
+       }
 }
index dfb1a1e544e7abac5a7d956e774449a2361c7649..4dd8b52c096c539e46277fe988245a1b01a7ac9c 100644 (file)
 package org.apache.poi.hslf.model.textproperties;
 
 import java.util.Locale;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.common.usermodel.GenericRecord;
+import org.apache.poi.util.GenericRecordUtil;
 
 /** 
  * Definition of a property of some text, or its paragraph. Defines 
@@ -30,7 +35,7 @@ import java.util.Locale;
  *  TxMasterTextProps, the definitions of the standard
  *  TextProps is stored in the different record classes 
  */
-public class TextProp implements Cloneable {
+public class TextProp implements Cloneable, GenericRecord {
        private int sizeOfDataBlock; // Number of bytes the data part uses
        private String propName;
        private int dataValue;
@@ -152,4 +157,14 @@ public class TextProp implements Cloneable {
         }
         return String.format(Locale.ROOT, "%s = %d (%0#"+len+"X mask / %d bytes)", getName(), getValue(), getMask(), getSize());
     }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "sizeOfDataBlock", this::getSize,
+                       "propName", this::getName,
+                       "dataValue", this::getValue,
+                       "maskInHeader", this::getMask
+               );
+       }
 }
\ No newline at end of file
index b859a4c0992047d39dbfb1c4ce524ff8b4ade857..d564241e11872f83ab4ea7412d236886e4620ff1 100644 (file)
@@ -21,10 +21,14 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.hslf.record.StyleTextPropAtom;
 import org.apache.poi.util.HexDump;
@@ -38,7 +42,7 @@ import org.apache.poi.util.POILogger;
  * Used to hold the number of characters affected, the list of active
  *  properties, and the indent level if required.
  */
-public class TextPropCollection {
+public class TextPropCollection implements GenericRecord {
     private static final POILogger LOG = POILogFactory.getLogger(TextPropCollection.class);
     
     /** All the different kinds of paragraph properties we might handle */
@@ -393,4 +397,15 @@ public class TextPropCollection {
         
         return out.toString();
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        Map<String,Supplier<?>> m = new LinkedHashMap<>();
+        m.put("charactersCovered", this::getCharactersCovered);
+        m.put("indentLevel", this::getIndentLevel);
+        textProps.forEach((s,t) -> m.put(s, () -> t));
+        m.put("maskSpecial", this::getSpecialMask);
+        m.put("textPropType", this::getTextPropType);
+        return Collections.unmodifiableMap(m);
+    }
 }
index d9451d95b25138feaa76705514aa0a2d4f7dc4d6..b7636d2da0b9877770588f05ee88f02fdf05f48b 100644 (file)
 
 package org.apache.poi.hslf.record;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -36,35 +41,57 @@ public final class AnimationInfoAtom extends RecordAtom {
     /**
      * whether the animation plays in the reverse direction
      */
-    public static final int Reverse = 1;
+    public static final int Reverse = 0x0001;
     /**
      * whether the animation starts automatically
      */
-    public static final int Automatic = 4;
+    public static final int Automatic = 0x0004;
     /**
      * whether the animation has an associated sound
      */
-    public static final int Sound = 16;
+    public static final int Sound = 0x0010;
     /**
      * whether all playing sounds are stopped when this animation begins
      */
-    public static final int StopSound = 64;
+    public static final int StopSound = 0x0040;
     /**
      * whether an associated sound, media or action verb is activated when the shape is clicked.
      */
-    public static final int Play = 256;
+    public static final int Play = 0x0100;
     /**
      * specifies that the animation, while playing, stops other slide show actions.
      */
-    public static final int Synchronous = 1024;
+    public static final int Synchronous = 0x0400;
     /**
      * whether the shape is hidden while the animation is not playing
      */
-    public static final int Hide = 4096;
+    public static final int Hide = 0x1000;
     /**
      * whether the background of the shape is animated
      */
-    public static final int AnimateBg = 16384;
+    public static final int AnimateBg = 0x4000;
+
+    private static final int[] FLAGS_MASKS = {
+        Reverse,
+        Automatic,
+        Sound,
+        StopSound,
+        Play,
+        Synchronous,
+        Hide,
+        AnimateBg
+    };
+
+    private static final String[] FLAGS_NAMES = {
+        "REVERSE",
+        "AUTOMATIC",
+        "SOUND",
+        "STOP_SOUND",
+        "PLAY",
+        "SYNCHRONOUS",
+        "HIDE",
+        "ANIMATE_BG"
+    };
 
     /**
      * Record header.
@@ -275,4 +302,15 @@ public final class AnimationInfoAtom extends RecordAtom {
         return buf.toString();
     }
 
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "dimColor", this::getDimColor,
+            "flags", getBitsAsString(this::getMask, FLAGS_MASKS, FLAGS_NAMES),
+            "soundIdRef", this::getSoundIdRef,
+            "delayTime", this::getDelayTime,
+            "orderID", this::getOrderID,
+            "slideCount", this::getSlideCount
+        );
+    }
 }
index f126a819040051eb8600e021ba0ec1b857b4eab6..5f98cc8a8cbf8d45eb38b73e17748aef9a5d7885 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.StringUtil;
@@ -38,7 +41,6 @@ public final class CString extends RecordAtom {
        private static final int MAX_RECORD_LENGTH = 1_000_000;
 
        private byte[] _header;
-       private static long _type = 4026l;
 
        /** The bytes that make up the text */
        private byte[] _text;
@@ -104,7 +106,9 @@ public final class CString extends RecordAtom {
        /**
         * We are of type 4026
         */
-       public long getRecordType() { return _type; }
+       public long getRecordType() {
+               return RecordTypes.CString.typeID;
+       }
 
        /**
         * Write the contents of the record back, so it can be written
@@ -125,4 +129,9 @@ public final class CString extends RecordAtom {
     public String toString() {
         return getText();
     }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties("text", this::getText);
+       }
 }
index 02f3c28b66765cb33b1a6cd5c6c47370275c5cf9..9bd51ed8ad3e9e049442a12e7c6f41018f4ffdf6 100644 (file)
@@ -20,6 +20,10 @@ package org.apache.poi.hslf.record;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.util.LittleEndian;
@@ -217,4 +221,18 @@ public final class ColorSchemeAtom extends RecordAtom {
                                fillsColourRGB, accentColourRGB, accentAndHyperlinkColourRGB, accentAndFollowingHyperlinkColourRGB};
                return clr[idx];
        }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               final Map<String, Supplier<?>> m = new LinkedHashMap<>();
+               m.put("backgroundColourRGB", this::getBackgroundColourRGB);
+               m.put("textAndLinesColourRGB", this::getTextAndLinesColourRGB);
+               m.put("shadowsColourRGB", this::getShadowsColourRGB);
+               m.put("titleTextColourRGB", this::getTitleTextColourRGB);
+               m.put("fillsColourRGB", this::getFillsColourRGB);
+               m.put("accentColourRGB", this::getAccentColourRGB);
+               m.put("accentAndHyperlinkColourRGB", this::getAccentAndHyperlinkColourRGB);
+               m.put("accentAndFollowingHyperlinkColourRGB", this::getAccentAndFollowingHyperlinkColourRGB);
+               return Collections.unmodifiableMap(m);
+       }
 }
index b64134b0d438a056bf8a2fd64ec69e5d2accb990..9dc836bf53e4102db66fee1b4b1464ce7a1144e9 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.poi.util.POILogger;
  */
 public final class Comment2000 extends RecordContainer {
        private byte[] _header;
-       private static long _type = 12000;
+       private static final long _type = RecordTypes.Comment2000.typeID;
 
        // Links to our more interesting children
 
@@ -172,5 +172,4 @@ public final class Comment2000 extends RecordContainer {
        public void writeOut(OutputStream out) throws IOException {
                writeOut(_header[0],_header[1],_type,_children,out);
        }
-
 }
index f73b4362802aecaef53aef57832a9ab2d9e47644..de01e4f9aad427c209846e6ce312cb520304c028 100644 (file)
@@ -20,8 +20,11 @@ package org.apache.poi.hslf.record;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Date;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hslf.util.SystemTimeUtils;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -158,4 +161,14 @@ public final class Comment2000Atom extends RecordAtom
         out.write(_header);
         out.write(_data);
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "number", this::getNumber,
+            "date", this::getDate,
+            "xOffset", this::getXOffset,
+            "yOffset", this::getYOffset
+        );
+    }
 }
index eed1de23f73be353f1fb20ad0aa4e1e52e5ec29f..e046161f41f9cfeb27b06a4309a6e8d81eb151fa 100644 (file)
@@ -19,6 +19,8 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.util.LittleEndian;
 
@@ -27,7 +29,7 @@ import org.apache.poi.util.LittleEndian;
  */
 public final class DocInfoListContainer extends RecordContainer {
        private byte[] _header;
-       private static long _type = RecordTypes.List.typeID;
+       private static final long _type = RecordTypes.List.typeID;
 
        // Links to our more interesting children
 
@@ -81,4 +83,8 @@ public final class DocInfoListContainer extends RecordContainer {
                writeOut(_header[0],_header[1],_type,_children,out);
        }
 
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return null;
+       }
 }
index cf01f0b2c9c82bf3029b76a2c31473c173dc1142..c0195d7a2755ad06f4f99f6c15197465ff367d0e 100644 (file)
@@ -19,9 +19,14 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndianByteArrayInputStream;
+import org.apache.poi.util.Removal;
 
 /**
  * A Document Atom (type 1001). Holds misc information on the PowerPoint
@@ -29,11 +34,37 @@ import org.apache.poi.util.LittleEndianByteArrayInputStream;
  */
 
 @SuppressWarnings({"WeakerAccess", "unused"})
-public final class DocumentAtom extends RecordAtom
-{
+public final class DocumentAtom extends RecordAtom {
+
+       /**
+        * Holds the different Slide Size values
+        */
+       public enum SlideSize {
+               /** Slide size ratio is consistent with a computer screen. */
+               ON_SCREEN,
+               /** Slide size ratio is consistent with letter paper. */
+               LETTER_SIZED_PAPER,
+               /** Slide size ratio is consistent with A4 paper. */
+               A4_SIZED_PAPER,
+               /** Slide size ratio is consistent with 35mm photo slides. */
+               ON_35MM,
+               /** Slide size ratio is consistent with overhead projector slides. */
+               OVERHEAD,
+               /** Slide size ratio is consistent with a banner. */
+               BANNER,
+               /**
+                * Slide size ratio that is not consistent with any of the other specified slide sizes in
+                * this enumeration.
+                */
+               CUSTOM
+       }
+
+
        //arbitrarily selected; may need to increase
        private static final int MAX_RECORD_LENGTH = 1_000_000;
 
+
+
        private final byte[] _header = new byte[8];
        private static long _type = RecordTypes.DocumentAtom.typeID;
 
@@ -79,9 +110,22 @@ public final class DocumentAtom extends RecordAtom
 
        public int getFirstSlideNum() { return firstSlideNum; }
 
-       /** The Size of the Document's slides, @see DocumentAtom.SlideSize for values */
+       /**
+        * The Size of the Document's slides, @see DocumentAtom.SlideSize for values
+        * @deprecated to be replaced by enum
+        */
+       @Deprecated
+       @Removal(version = "5.0.0")
        public int getSlideSizeType() { return slideSizeType; }
 
+       public SlideSize getSlideSizeTypeEnum() {
+               return SlideSize.values()[slideSizeType];
+       }
+
+       public void setSlideSize(SlideSize size) {
+               slideSizeType = size.ordinal();
+       }
+
        /** Was the document saved with True Type fonts embeded? */
        public boolean getSaveWithFonts() {
                return saveWithFonts != 0;
@@ -191,16 +235,23 @@ public final class DocumentAtom extends RecordAtom
                out.write(reserved);
        }
 
-       /**
-        * Holds the different Slide Size values
-        */
-       public static final class SlideSize {
-               public static final int ON_SCREEN = 0;
-               public static final int LETTER_SIZED_PAPER = 1;
-               public static final int A4_SIZED_PAPER = 2;
-               public static final int ON_35MM = 3;
-               public static final int OVERHEAD = 4;
-               public static final int BANNER = 5;
-               public static final int CUSTOM = 6;
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               final Map<String, Supplier<?>> m = new LinkedHashMap<>();
+               m.put("slideSizeX", this::getSlideSizeX);
+               m.put("slideSizeY", this::getSlideSizeY);
+               m.put("notesSizeX", this::getNotesSizeX);
+               m.put("notesSizeY", this::getNotesSizeY);
+               m.put("serverZoomFrom", this::getServerZoomFrom);
+               m.put("serverZoomTo", this::getServerZoomTo);
+               m.put("notesMasterPersist", this::getNotesMasterPersist);
+               m.put("handoutMasterPersist", this::getHandoutMasterPersist);
+               m.put("firstSlideNum", this::getFirstSlideNum);
+               m.put("slideSize", this::getSlideSizeTypeEnum);
+               m.put("saveWithFonts", this::getSaveWithFonts);
+               m.put("omitTitlePlace", this::getOmitTitlePlace);
+               m.put("rightToLeft", this::getRightToLeft);
+               m.put("showComments", this::getShowComments);
+               return Collections.unmodifiableMap(m);
        }
 }
index 7637408326f37c2e77c802ebb6c66c03d53ff208..abcf03628ea7695bba29321b9d6c916b8d8830ea 100644 (file)
@@ -21,6 +21,7 @@ import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.EncryptedDocumentException;
 import org.apache.poi.poifs.crypt.CipherAlgorithm;
@@ -29,6 +30,7 @@ import org.apache.poi.poifs.crypt.EncryptionMode;
 import org.apache.poi.poifs.crypt.HashAlgorithm;
 import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionHeader;
 import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionVerifier;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianByteArrayOutputStream;
 import org.apache.poi.util.LittleEndianInputStream;
@@ -40,7 +42,7 @@ import org.apache.poi.util.LittleEndianInputStream;
  * @author Nick Burch
  */
 public final class DocumentEncryptionAtom extends PositionDependentRecordAtom {
-    private static final long _type = 12052l;
+    private static final long _type = RecordTypes.DocumentEncryptionAtom.typeID;
        private final byte[] _header;
        private EncryptionInfo ei;
 
@@ -132,4 +134,11 @@ public final class DocumentEncryptionAtom extends PositionDependentRecordAtom {
     public void updateOtherRecordReferences(Map<Integer,Integer> oldToNewReferencesLookup) {
         // nothing to update
     }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "encryptionInfo", this::getEncryptionInfo
+               );
+       }
 }
index 9d984180eb4c717e2599270124c8f0f4a0f9a1ec..a3d29b01da36e6352b4ca181e5e14c5779416fdb 100644 (file)
 
 package org.apache.poi.hslf.record;
 
+import java.util.Map;
+import java.util.function.Supplier;
+
 import org.apache.poi.ddf.EscherRecord;
 import org.apache.poi.ddf.EscherRecordFactory;
 import org.apache.poi.ddf.EscherSerializationListener;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -77,6 +81,18 @@ public class EscherPlaceholder extends EscherRecord {
         return "ClientTextboxPlaceholder";
     }
 
+    public int getPosition() {
+        return position;
+    }
+
+    public byte getPlacementId() {
+        return placementId;
+    }
+
+    public byte getSize() {
+        return size;
+    }
+
     @Override
     protected Object[][] getAttributeMap() {
         return new Object[][] {
@@ -86,4 +102,19 @@ public class EscherPlaceholder extends EscherRecord {
             { "unused", unused }
         };
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "base", super::getGenericProperties,
+            "position", this::getPosition,
+            "placementId", this::getPlacementId,
+            "size", this::getSize
+        );
+    }
+
+    @Override
+    public Enum getGenericRecordType() {
+        return RecordTypes.OEPlaceholderAtom;
+    }
 }
index 10744e49d75ccc08613c603718a026dc2f8a36f2..783c59e496defe8d5e4f007e93ce03c9886c06f9 100644 (file)
 
 package org.apache.poi.hslf.record;
 
-import org.apache.poi.ddf.*;
-
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.ByteArrayOutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.ddf.EscherTextboxRecord;
+import org.apache.poi.util.GenericRecordUtil;
 
 /**
  * A wrapper around a DDF (Escher) EscherTextbox Record. Causes the DDF
@@ -118,4 +121,12 @@ public final class EscherTextboxWrapper extends RecordContainer {
        public StyleTextProp9Atom getStyleTextProp9Atom() {
                return this.styleTextProp9Atom;
        }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "shapeId", this::getShapeId,
+                       "escherRecord", this::getEscherRecord
+               );
+       }
 }
index b5540cee5a4ff39e563cc3b1a3295c6407d4a0d9..93942674afa05bd38072045f24cc5dbe8602f317 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -114,4 +117,8 @@ public final class ExControlAtom extends RecordAtom {
         out.write(data);
     }
 
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties("slideId", this::getSlideId);
+    }
 }
index 5ebb2a43c6a6a510bb58f35a835b8f91ed018c0b..e72f052347811352e3a9cc0f94d26ed033d6cc72 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -166,4 +169,25 @@ public class ExEmbedAtom extends RecordAtom {
         out.write(_data);
     }
 
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "followColorScheme", this::getFollowColorSchemeString,
+            "cantLockServer", this::getCantLockServerB,
+            "noSizeToServer", this::getNoSizeToServerB,
+            "isTable", this::getIsTable
+        );
+    }
+
+    private String getFollowColorSchemeString() {
+        switch (getFollowColorScheme()) {
+            default:
+            case DOES_NOT_FOLLOW_COLOR_SCHEME:
+                return "DOES_NOT_FOLLOW_COLOR_SCHEME";
+            case FOLLOWS_ENTIRE_COLOR_SCHEME:
+                return "FOLLOWS_ENTIRE_COLOR_SCHEME";
+            case FOLLOWS_TEXT_AND_BACKGROUND_SCHEME:
+                return "FOLLOWS_TEXT_AND_BACKGROUND_SCHEME";
+        }
+    }
 }
index ca9d68d788b13b6d4695f591504ebc7ac3f538b6..da25e248bffe72c5bf5ed3b3367fe4be9e0ff76b 100644 (file)
@@ -27,9 +27,10 @@ import org.apache.poi.util.POILogger;
  * @author Nick Burch
  */
 public class ExHyperlink extends RecordContainer {
+       private static final long _type = RecordTypes.ExHyperlink.typeID;
+
        private byte[] _header;
-       private static long _type = 4055;
-       
+
        // Links to our more interesting children
        private ExHyperlinkAtom linkAtom;
        private CString linkDetailsA;
index 648f2aaa5702600b2747ca401c64ffa529d63971..bc994309989b7cce5721861ffe1789489d0b0b31 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -114,4 +117,9 @@ public final class ExHyperlinkAtom extends RecordAtom {
         out.write(_header);
         out.write(_data);
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties("number", this::getNumber);
+    }
 }
index 1ed5829532a1e0f5251a67b5c6cca3e213d30b50..97e2594ad10d024a08d01dfb537256b78b05aa78 100644 (file)
 
 package org.apache.poi.hslf.record;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -46,6 +51,12 @@ public final class ExMediaAtom extends RecordAtom
      */
     public static final int fNarration = 4;
 
+
+    private static final int[] FLAG_MASKS = { fLoop, fRewind, fNarration };
+
+    private static final String[] FLAG_NAMES = { "LOOP", "REWIND", "NARRATION" };
+
+
     /**
      * Record header.
      */
@@ -169,4 +180,11 @@ public final class ExMediaAtom extends RecordAtom
         return buf.toString();
     }
 
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "objectId", this::getObjectId,
+            "flags", getBitsAsString(this::getMask, FLAG_MASKS, FLAG_NAMES)
+        );
+    }
 }
index 11e48fa1f1d5902f99c097caf07c1deb07938cc4..ced5508a9d559ca5c2b1774f9ef3eefbf7163218 100644 (file)
@@ -29,7 +29,7 @@ import org.apache.poi.util.LittleEndian;
  */
 public class ExObjList extends RecordContainer {
        private byte[] _header;
-       private static long _type = 1033;
+       private static final long _type = RecordTypes.ExObjList.typeID;
        
        // Links to our more interesting children
        private ExObjListAtom exObjListAtom; 
index e1224ef0bc0fd7ed26536e753421df1723097816..0c345a286be90c10ea685eed8a8e0eb2704a0da0 100644 (file)
@@ -20,7 +20,10 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -116,4 +119,11 @@ public class ExObjListAtom extends RecordAtom
         out.write(_header);
         out.write(_data);
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "objectIDSeed", this::getObjectIDSeed
+        );
+    }
 }
index 1381e6ab6c27669f93d8ff10df3b7bd5b63bbaad..930f54c380b625b80b034cfee93ba02fe7bdb2db 100644 (file)
 
 package org.apache.poi.hslf.record;
 
-import org.apache.poi.util.LittleEndian;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
+import org.apache.poi.util.LittleEndian;
 
 /**
  * ExObjRefAtom (3009).
@@ -91,4 +95,9 @@ public final class ExObjRefAtom extends RecordAtom {
 
         out.write(recdata);
        }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties("exObjIdRef", this::getExObjIdRef);
+    }
 }
index 7e679f27aa832db2134e3c198b12c41cdb699292..0b0b66ebab79864a929ffa89316589b75b2e0e80 100644 (file)
 
 package org.apache.poi.hslf.record;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+import static org.apache.poi.util.GenericRecordUtil.safeEnum;
+
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
 /**
  * Atom storing information for an OLE object.
  *
- * <!--
  * offset   type    name         description
  *
  * 0        uint4   drawAspect   Stores whether the object can be completely seen
@@ -60,12 +65,45 @@ import org.apache.poi.util.LittleEndian;
  *
  * 20       bool1    isBlank          Set if the object's image is blank
  *           (note: KOffice has this as an int.)
- * -->
- *
- * @author Daniel Noll
  */
 public class ExOleObjAtom extends RecordAtom {
 
+    public enum OleType {
+        /** An embedded OLE object; the object is serialized and saved within the file. */
+        EMBEDDED,
+        /** A linked OLE object; the object is saved outside of the file. */
+        LINKED,
+        /** The OLE object is an ActiveX control. */
+        CONTROL
+    }
+
+    public enum Subtype {
+        DEFAULT,
+        CLIPART_GALLERY,
+        WORD_TABLE,
+        EXCEL,
+        GRAPH,
+        ORGANIZATION_CHART,
+        EQUATION,
+        WORDART,
+        SOUND,
+        IMAGE,
+        POWERPOINT_PRESENTATION,
+        POWERPOINT_SLIDE,
+        PROJECT,
+        NOTEIT,
+        EXCEL_CHART,
+        MEDIA_PLAYER
+    }
+
+    private static final int[] DRAWASPECT_MASKS = {
+        0x0001, 0x0002, 0x0004, 0x0008
+    };
+
+    private static final String[] DRAWASPECT_NAMES = {
+        "CONTENT", "THUMBNAIL", "ICON", "DOCPRINT"
+    };
+
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 10_485_760;
 
@@ -314,4 +352,16 @@ public class ExOleObjAtom extends RecordAtom {
         buf.append("  options: " + getOptions() + "\n");
         return buf.toString();
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "drawAspect", getBitsAsString(this::getDrawAspect, DRAWASPECT_MASKS, DRAWASPECT_NAMES),
+            "type", safeEnum(OleType.values(), this::getType),
+            "objID", this::getObjID,
+            "subType", safeEnum(Subtype.values(), this::getSubType),
+            "objStgDataRef", this::getObjStgDataRef,
+            "options", this::getOptions
+        );
+    }
 }
index 3e62549d9e09d5a7c820acefb92fb9473ecaa095..3fdfa04ecdaa0f5e8caea43bd0d152ee747ae241 100644 (file)
@@ -23,10 +23,12 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Map;
+import java.util.function.Supplier;
 import java.util.zip.DeflaterOutputStream;
 import java.util.zip.InflaterInputStream;
 
 import org.apache.poi.util.BoundedInputStream;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -185,4 +187,14 @@ public class ExOleObjStg extends PositionDependentRecordAtom implements PersistR
     public void updateOtherRecordReferences(Map<Integer,Integer> oldToNewReferencesLookup) {
         // nothing to update
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "compressed", this::isCompressed,
+            "persistId", this::getPersistId,
+            "dataLength", this::getDataLength,
+            "data", this::getData
+        );
+    }
 }
\ No newline at end of file
index bddb79efc67af2b434760e8624bab2ed3b49e391..db2ffcf6d915511a1e1d4111afedd56585438c77 100644 (file)
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.common.usermodel.fonts.FontHeader;
 import org.apache.poi.common.usermodel.fonts.FontInfo;
@@ -200,4 +201,9 @@ public final class FontCollection extends RecordContainer {
     public List<HSLFFontInfo> getFonts() {
         return new ArrayList<>(fonts.values());
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return null;
+    }
 }
index f60ba4a6973b0abb6c2b24923ae114e7e8e3db6d..f28bb3f9902839022a1741f44ccf6fb08f4a7b22 100644 (file)
@@ -19,9 +19,12 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.common.usermodel.fonts.FontFacet;
 import org.apache.poi.common.usermodel.fonts.FontHeader;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -113,4 +116,9 @@ public class FontEmbeddedData extends RecordAtom implements FontFacet {
     public Object getFontData() {
         return this;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties("fontHeader", this::getFontHeader);
+    }
 }
index 2d172d7b5e2f0aea86556756deb73fd90e66d1be..0e9f732833a2ba99e83659aab02d0d3c488c9d5d 100644 (file)
 
 package org.apache.poi.hslf.record;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Arrays;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.fonts.FontFamily;
+import org.apache.poi.common.usermodel.fonts.FontPitch;
 import org.apache.poi.hslf.exceptions.HSLFException;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.StringUtil;
@@ -40,6 +47,18 @@ public final class FontEntityAtom extends RecordAtom {
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 1_000_000;
 
+    private static final int[] FLAGS_MASKS = {
+        0x0001, 0x0100, 0x0200, 0x0400, 0x0800
+    };
+
+    private static final String[] FLAGS_NAMES = {
+        "EMBED_SUBSETTED",
+        "RASTER_FONT",
+        "DEVICE_FONT",
+        "TRUETYPE_FONT",
+        "NO_FONT_SUBSTITUTION"
+    };
+
     /**
      * record header
      */
@@ -210,4 +229,16 @@ public final class FontEntityAtom extends RecordAtom {
                out.write(_header);
                out.write(_recdata);
        }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "fontName", this::getFontName,
+            "fontIndex", this::getFontIndex,
+            "charset", this::getCharSet,
+            "fontFlags", getBitsAsString(this::getFontFlags, FLAGS_MASKS, FLAGS_NAMES),
+            "fontPitch", () -> FontPitch.valueOfPitchFamily((byte)getPitchAndFamily()),
+            "fontFamily", () -> FontFamily.valueOfPitchFamily((byte)getPitchAndFamily())
+        );
+    }
 }
index 9e9140578e4a97c8eee6182761557c27f50298fb..ee6e645ee1587b6c5afdfc538bc72e49e6ad3544 100644 (file)
 
 package org.apache.poi.hslf.record;
 
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.LittleEndian;
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+import static org.apache.poi.util.GenericRecordUtil.safeEnum;
+
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndian;
 
 /**
  * An atom record that specifies options for displaying headers and footers
@@ -31,10 +38,33 @@ import java.io.OutputStream;
 
 public final class HeadersFootersAtom extends RecordAtom {
 
+    /** FormatIndex enum without LCID mapping */
+    public enum FormatIndex {
+        SHORT_DATE,
+        LONG_DATE,
+        LONG_DATE_WITHOUT_WEEKDAY,
+        ALTERNATE_SHORT_DATE,
+        ISO_STANDARD_DATE,
+        SHORT_DATE_WITH_ABBREVIATED_MONTH,
+        SHORT_DATE_WITH_SLASHES,
+        ALTERNATE_SHORT_DATE_WITH_ABBREVIATED_MONTH,
+        ENGLISH_DATE,
+        MONTH_AND_YEAR,
+        ABBREVIATED_MONTH_AND_YEAR,
+        DATE_AND_HOUR12_TIME,
+        DATE_AND_HOUR12_TIME_WITH_SECONDS,
+        HOUR12_TIME,
+        HOUR12_TIME_WITH_SECONDS,
+        HOUR24_TIME,
+        HOUR24_TIME_WITH_SECONDS,
+        CHINESE1,
+        CHINESE2,
+        CHINESE3
+    }
+
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 100_000;
 
-
     /**
      * A bit that specifies whether the date is displayed in the footer.
      * @see #getMask()
@@ -82,6 +112,21 @@ public final class HeadersFootersAtom extends RecordAtom {
      */
     public static final int fHasFooter = 32;
 
+    private static final int[] PLACEHOLDER_MASKS = {
+        fHasDate,
+        fHasTodayDate,
+        fHasUserDate,
+        fHasSlideNumber,
+        fHasHeader,
+        fHasFooter
+    };
+
+    private static final String[] PLACEHOLDER_NAMES = {
+        "DATE", "TODAY_DATE", "USER_DATE", "SLIDE_NUMBER", "HEADER", "FOOTER"
+    };
+
+
+
     /**
      * record header
      */
@@ -213,4 +258,12 @@ public final class HeadersFootersAtom extends RecordAtom {
         buf.append("\t  fHasFooter      : " + getFlag(fHasFooter) + "\n");
         return buf.toString();
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "formatIndex", safeEnum(FormatIndex.values(), this::getFormatId),
+            "flags", getBitsAsString(this::getMask, PLACEHOLDER_MASKS, PLACEHOLDER_NAMES)
+        );
+    }
 }
index 9f2a0eecd7faa21b5fe0637119dd05906a600905..c7aa32876b74d080860e5bf9127651c070a0ee7c 100644 (file)
 
 package org.apache.poi.hslf.record;
 
+import java.io.IOException;
+import java.io.OutputStream;
+
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.POILogger;
 
-import java.io.OutputStream;
-import java.io.IOException;
-
 /**
  * A container record that specifies information about the footers on a presentation slide.
  * <p>
@@ -63,24 +63,39 @@ public final class HeadersFootersContainer extends RecordContainer {
         System.arraycopy(source,start,_header,0,8);
 
         _children = Record.findChildRecords(source,start+8,len-8);
-        for(int i=0; i < _children.length; i++){
-            if(_children[i] instanceof HeadersFootersAtom) hdAtom = (HeadersFootersAtom)_children[i];
-            else if(_children[i] instanceof CString) {
-                CString cs = (CString)_children[i];
+        findInterestingChildren();
+    }
+
+    /**
+     * Go through our child records, picking out the ones that are
+     *  interesting, and saving those for use by the easy helper
+     *  methods.
+     */
+    private void findInterestingChildren() {
+        for (Record child : _children) {
+            if (child instanceof HeadersFootersAtom) {
+                hdAtom = (HeadersFootersAtom) child;
+            } else if (child instanceof CString) {
+                CString cs = (CString) child;
                 int opts = cs.getOptions() >> 4;
-                switch(opts){
-                    case USERDATEATOM: csDate = cs; break;
-                    case HEADERATOM: csHeader = cs; break;
-                    case FOOTERATOM: csFooter = cs; break;
+                switch (opts) {
+                    case USERDATEATOM:
+                        csDate = cs;
+                        break;
+                    case HEADERATOM:
+                        csHeader = cs;
+                        break;
+                    case FOOTERATOM:
+                        csFooter = cs;
+                        break;
                     default:
                         logger.log(POILogger.WARN, "Unexpected CString.Options in HeadersFootersContainer: " + opts);
                         break;
                 }
             } else {
-                logger.log(POILogger.WARN, "Unexpected record in HeadersFootersContainer: " + _children[i]);
+                logger.log(POILogger.WARN, "Unexpected record in HeadersFootersContainer: " + child);
             }
         }
-
     }
 
     public HeadersFootersContainer(short options) {
index 8ca722d856c0a76e722c01eacf3d78a26857d340..9c0962c6446188501b8e56c9cc3c7be7365106d7 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.poi.util.POILogger;
  */
 public class InteractiveInfo extends RecordContainer {
        private byte[] _header;
-       private static long _type = 4082;
+       private static final long _type = RecordTypes.InteractiveInfo.typeID;
        
        // Links to our more interesting children
        private InteractiveInfoAtom infoAtom;
index 15dc05b50db967bb66e1e778446113500e4d76b6..71a5adc123db983c3c68412cf1cd53f69019f85d 100644 (file)
 
 package org.apache.poi.hslf.record;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+import static org.apache.poi.util.GenericRecordUtil.safeEnum;
+
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -35,6 +41,39 @@ public class InteractiveInfoAtom extends RecordAtom {
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 100_000;
 
+    public enum Action {
+        NONE,
+        MACRO,
+        RUN_PROGRAM,
+        JUMP,
+        HYPERLINK,
+        OLE,
+        MEDIA,
+        CUSTOM_SHOW
+    }
+
+    public enum Jump {
+        NONE,
+        NEXT_SLIDE,
+        PREVIOUS_SLIDE,
+        FIRST_SLIDE,
+        LAST_SLIDE,
+        LAST_SLIDE_VIEWED,
+        END_SHOW
+    }
+
+    public enum Link {
+        NEXT_SLIDE,
+        PREVIOUS_SLIDE,
+        FIRST_SLIDE,
+        LAST_SLIDE,
+        CUSTOM_SHOW,
+        SLIDE_NUMBER,
+        URL,
+        OTHER_PRESENTATION,
+        OTHER_FILE,
+        NULL
+    }
 
     /**
      * Action Table
@@ -73,6 +112,14 @@ public class InteractiveInfoAtom extends RecordAtom {
     public static final byte LINK_OtherFile = 0x0A;
     public static final byte LINK_NULL = (byte)0xFF;
 
+    private static final int[] FLAGS_MASKS = {
+        0x0001, 0x0002, 0x0004, 0x0008
+    };
+
+    private static final String[] FLAGS_NAMES = {
+        "ANIMATED", "STOP_SOUND", "CUSTOM_SHOW_RETURN", "VISITED"
+    };
+
     /**
      * Record header.
      */
@@ -280,4 +327,16 @@ public class InteractiveInfoAtom extends RecordAtom {
         out.write(_header);
         out.write(_data);
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "hyperlinkID", this::getHyperlinkID,
+            "soundRef", this::getSoundRef,
+            "action", safeEnum(Action.values(), this::getAction),
+            "jump", safeEnum(Jump.values(), this::getJump),
+            "hyperlinkType", safeEnum(Link.values(), this::getHyperlinkType, Link.NULL),
+            "flags", getBitsAsString(this::getFlags, FLAGS_MASKS, FLAGS_NAMES)
+        );
+    }
 }
index 9d76556e86eec786007b5b50e523db6cb299fa85..0e1b4dcab1ea9d72a45e68c115e88b75ac5f416a 100644 (file)
@@ -22,9 +22,12 @@ import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.commons.math3.util.ArithmeticUtils;
 import org.apache.poi.hslf.model.textproperties.IndentProp;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.POILogger;
@@ -157,4 +160,11 @@ public final class MasterTextPropAtom extends RecordAtom {
     public List<IndentProp> getIndents() {
         return Collections.unmodifiableList(indents);
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "indents", this::getIndents
+        );
+    }
 }
index c4e284098b8e8649dca64be23b412746140969b7..40b455495565e4e7122adcf15168ae2b30c321c4 100644 (file)
 
 package org.apache.poi.hslf.record;
 
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.LittleEndian;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndian;
 
 /**
  * A Notes Atom (type 1009). Holds information on the parent Notes, such
@@ -121,4 +125,14 @@ public final class NotesAtom extends RecordAtom
                // Reserved fields
                out.write(reserved);
        }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "slideId", this::getSlideID,
+                       "followMasterObjects", this::getFollowMasterObjects,
+                       "followMasterScheme", this::getFollowMasterScheme,
+                       "followMasterBackground", this::getFollowMasterBackground
+               );
+       }
 }
index e91ddd0421c6cafd96521d526fb6d30aecd41c48..f25164d2a20d8042007153b4897d2187ceb9038a 100644 (file)
@@ -19,8 +19,11 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.sl.usermodel.Placeholder;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -176,4 +179,13 @@ public final class OEPlaceholderAtom extends RecordAtom{
 
         out.write(recdata);
        }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "placementId", this::getPlacementId,
+            "placeholderId", this::getPlaceholderId,
+            "placeholderSize", this::getPlaceholderSize
+        );
+    }
 }
index e44b360a589a414ae5abbbcfe4ff1c3ea99a31b5..e0c368825b7b1d109d0e6abb8001f988b6d893f5 100644 (file)
 
 package org.apache.poi.hslf.record;
 
-import org.apache.poi.util.LittleEndian;
-
-import java.io.OutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
+import org.apache.poi.util.LittleEndian;
 
 /**
  * OEPlaceholderAtom (3998).
@@ -106,4 +109,10 @@ public final class OutlineTextRefAtom extends RecordAtom {
         return _index;
     }
 
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "textIndex", this::getTextIndex
+        );
+    }
 }
index 7f6f6ab0d538019ec1bbea377e035dd64afc6482..c2bceecd813b2ec676c444bbb0b30d3dcafd8dfe 100644 (file)
@@ -20,8 +20,15 @@ package org.apache.poi.hslf.record;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
-import java.util.LinkedList;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import org.apache.poi.ddf.DefaultEscherRecordFactory;
 import org.apache.poi.ddf.EscherBoolProperty;
@@ -31,11 +38,13 @@ import org.apache.poi.ddf.EscherOptRecord;
 import org.apache.poi.ddf.EscherProperties;
 import org.apache.poi.ddf.EscherRGBProperty;
 import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherRecordTypes;
 import org.apache.poi.ddf.EscherSimpleProperty;
 import org.apache.poi.ddf.EscherSpRecord;
 import org.apache.poi.ddf.EscherSpgrRecord;
 import org.apache.poi.ddf.EscherTextboxRecord;
 import org.apache.poi.sl.usermodel.ShapeType;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.POILogger;
@@ -52,7 +61,7 @@ import org.apache.poi.util.POILogger;
 
 // For now, pretending to be an atom. Might not always be, but that
 //  would require a wrapping class
-public final class PPDrawing extends RecordAtom {
+public final class PPDrawing extends RecordAtom implements Iterable<EscherRecord> {
 
        //arbitrarily selected; may need to increase
        private static final int MAX_RECORD_LENGTH = 10_485_760;
@@ -70,8 +79,14 @@ public final class PPDrawing extends RecordAtom {
        /**
         * Get access to the underlying Escher Records
         */
+       @SuppressWarnings("WeakerAccess")
        public List<EscherRecord> getEscherRecords() { return childRecords; }
 
+       @Override
+       public Iterator<EscherRecord> iterator() {
+               return childRecords.iterator();
+       }
+
        /**
         * Get access to the atoms inside Textboxes
         */
@@ -97,7 +112,7 @@ public final class PPDrawing extends RecordAtom {
        /**
         * Sets everything up, groks the escher etc
         */
-       protected PPDrawing(byte[] source, int start, int len) {
+       PPDrawing(byte[] source, int start, int len) {
                // Get the header
                _header = new byte[8];
                System.arraycopy(source,start,_header,0,8);
@@ -115,61 +130,59 @@ public final class PPDrawing extends RecordAtom {
                EscherContainerRecord dgContainer = getDgContainer();
 
                if (dgContainer != null) {
-                       textboxWrappers = findInDgContainer(dgContainer);
+                       textboxWrappers = Stream.of(dgContainer).
+                               flatMap(findEscherContainer(EscherRecordTypes.SPGR_CONTAINER)).
+                               flatMap(findEscherContainer(EscherRecordTypes.SP_CONTAINER)).
+                               flatMap(PPDrawing::getTextboxHelper).
+                               toArray(EscherTextboxWrapper[]::new);
                } else {
                        // Find and EscherTextboxRecord's, and wrap them up
                        final List<EscherTextboxWrapper> textboxes = new ArrayList<>();
                        findEscherTextboxRecord(childRecords, textboxes);
-                       this.textboxWrappers = textboxes.toArray(new EscherTextboxWrapper[textboxes.size()]);
+                       this.textboxWrappers = textboxes.toArray(new EscherTextboxWrapper[0]);
                }
        }
-       private EscherTextboxWrapper[] findInDgContainer(final EscherContainerRecord dgContainer) {
-               final List<EscherTextboxWrapper> found = new LinkedList<>();
-               final EscherContainerRecord spgrContainer = findFirstEscherContainerRecordOfType(RecordTypes.EscherSpgrContainer, dgContainer);
-               final EscherContainerRecord[] spContainers = findAllEscherContainerRecordOfType(RecordTypes.EscherSpContainer, spgrContainer);
-               for (EscherContainerRecord spContainer : spContainers) {
-                       EscherSpRecord sp = (EscherSpRecord)findFirstEscherRecordOfType(RecordTypes.EscherSp, spContainer);
-                       EscherTextboxRecord clientTextbox = (EscherTextboxRecord)findFirstEscherRecordOfType(RecordTypes.EscherClientTextbox, spContainer);
-                       if (null == clientTextbox) { continue; }
-
-                       EscherTextboxWrapper w = new EscherTextboxWrapper(clientTextbox);
-            StyleTextProp9Atom nineAtom = findInSpContainer(spContainer);
-                       w.setStyleTextProp9Atom(nineAtom);
-                       if (null != sp) {
-                           w.setShapeId(sp.getShapeId());
-                       }
-                       found.add(w);
+
+       private static Stream<EscherTextboxWrapper> getTextboxHelper(EscherContainerRecord spContainer) {
+               Optional<EscherTextboxRecord> oTB = firstEscherRecord(spContainer, EscherRecordTypes.CLIENT_TEXTBOX);
+               if (!oTB.isPresent()) {
+                       return Stream.empty();
                }
-               return found.toArray(new EscherTextboxWrapper[found.size()]);
+
+               EscherTextboxWrapper tbw = new EscherTextboxWrapper(oTB.get());
+               findInSpContainer(spContainer).ifPresent(tbw::setStyleTextProp9Atom);
+
+               Optional<EscherSpRecord> oSP = firstEscherRecord(spContainer, EscherRecordTypes.SP);
+               oSP.map(EscherSpRecord::getShapeId).ifPresent(tbw::setShapeId);
+
+               return Stream.of(tbw);
        }
-       
-       private StyleTextProp9Atom findInSpContainer(final EscherContainerRecord spContainer) {
-        HSLFEscherClientDataRecord cldata = spContainer.getChildById(RecordTypes.EscherClientData.typeID);
-        if (cldata == null) {
-            return null;
-        }
-        DummyPositionSensitiveRecordWithChildren progTags =
-            getChildRecord(cldata.getHSLFChildRecords(), RecordTypes.ProgTags);
-        if (progTags == null) {
-            return null;
-        }
-        DummyPositionSensitiveRecordWithChildren progBinaryTag =
-            (DummyPositionSensitiveRecordWithChildren)progTags.findFirstOfType(RecordTypes.ProgBinaryTag.typeID);
-        if (progBinaryTag == null) {
-            return null;
-        }
-               int size = progBinaryTag.getChildRecords().length;
-               if (2 != size) { return null; }
-
-               final Record r0 = progBinaryTag.getChildRecords()[0];
-               final Record r1 = progBinaryTag.getChildRecords()[1];
-               
-               if (!(r0 instanceof CString)) { return null; }
-               if (!("___PPT9".equals(((CString) r0).getText()))) { return null; }
-               if (!(r1 instanceof BinaryTagDataBlob )) { return null; }
-               final BinaryTagDataBlob blob = (BinaryTagDataBlob) r1;
-               if (1 != blob.getChildRecords().length) { return null; }
-               return (StyleTextProp9Atom) blob.findFirstOfType(RecordTypes.StyleTextProp9Atom.typeID);
+
+       private static Optional<StyleTextProp9Atom> findInSpContainer(final EscherContainerRecord spContainer) {
+               Optional<HSLFEscherClientDataRecord> oCD = firstEscherRecord(spContainer, EscherRecordTypes.CLIENT_DATA);
+               return oCD.map(HSLFEscherClientDataRecord::getHSLFChildRecords).map(List::stream).orElseGet(Stream::empty).
+                       filter(sameHSLF(RecordTypes.ProgTags)).
+                       flatMap(r -> Stream.of(r.getChildRecords())).
+                       filter(sameHSLF(RecordTypes.ProgBinaryTag)).
+                       flatMap(PPDrawing::findInProgBinaryTag).
+                       findFirst();
+       }
+
+       private static Stream<StyleTextProp9Atom> findInProgBinaryTag(Record r) {
+               Record[] ch = r.getChildRecords();
+               if (ch != null &&
+                       ch.length == 2 &&
+                       ch[0] instanceof CString &&
+                       ch[1] instanceof BinaryTagDataBlob &&
+                       "___PPT9".equals(((CString)ch[0]).getText())
+               ) {
+                       BinaryTagDataBlob blob = (BinaryTagDataBlob) ch[1];
+                       StyleTextProp9Atom prop9 = (StyleTextProp9Atom) blob.findFirstOfType(RecordTypes.StyleTextProp9Atom.typeID);
+                       if (prop9 != null) {
+                               return Stream.of(prop9);
+                       }
+               }
+               return Stream.empty();
        }
 
        /**
@@ -192,7 +205,7 @@ public final class PPDrawing extends RecordAtom {
                        logger.log(POILogger.WARN, "Hit short DDF record at " + startPos + " - " + size);
                }
 
-               /**
+               /*
                 * Sanity check. Always advance the cursor by the correct value.
                 *
                 * getRecordSize() must return exactly the same number of bytes that was written in fillFields.
@@ -349,15 +362,7 @@ public final class PPDrawing extends RecordAtom {
         * @since POI 3.14-Beta2
         */
        public EscherContainerRecord getDgContainer() {
-           if (childRecords.isEmpty()) {
-               return null;
-           }
-           EscherRecord r = childRecords.get(0);
-           if (r instanceof EscherContainerRecord && r.getRecordId() == RecordTypes.EscherDgContainer.typeID) {
-               return (EscherContainerRecord)r;
-           } else {
-               return null;
-           }
+               return (EscherContainerRecord)firstEscherRecord(this, EscherRecordTypes.DG_CONTAINER).orElse(null);
        }
        
        /**
@@ -367,72 +372,45 @@ public final class PPDrawing extends RecordAtom {
         */
        public EscherDgRecord getEscherDgRecord(){
                if (dg == null) {
-                   EscherContainerRecord dgr = getDgContainer();
-                   if (dgr != null) {
-                       for(EscherRecord r : dgr.getChildRecords()){
-                               if(r instanceof EscherDgRecord){
-                                       dg = (EscherDgRecord)r;
-                                       break;
-                               }
-                       }
-                   }
+                       firstEscherRecord(this, EscherRecordTypes.DG_CONTAINER).
+                       flatMap(c -> firstEscherRecord((EscherContainerRecord)c, EscherRecordTypes.DG)).
+                       ifPresent(c -> dg = (EscherDgRecord)c);
                }
                return dg;
        }
 
-    protected EscherContainerRecord findFirstEscherContainerRecordOfType(RecordTypes type, EscherContainerRecord parent) {
-       if (null == parent) { return null; }
-               final List<EscherContainerRecord> children = parent.getChildContainers();
-               for (EscherContainerRecord child : children) {
-                       if (type.typeID == child.getRecordId()) {
-                               return child;
-                       }
-               }
-               return null;
-    }
-    protected EscherRecord findFirstEscherRecordOfType(RecordTypes type, EscherContainerRecord parent) {
-       if (null == parent) { return null; }
-               final List<EscherRecord> children = parent.getChildRecords();
-               for (EscherRecord child : children) {
-                       if (type.typeID == child.getRecordId()) {
-                               return child;
-                       }
-               }
-               return null;
-    }
-    protected EscherContainerRecord[] findAllEscherContainerRecordOfType(RecordTypes type, EscherContainerRecord parent) {
-       if (null == parent) { return new EscherContainerRecord[0]; }
-               final List<EscherContainerRecord> children = parent.getChildContainers();
-               final List<EscherContainerRecord> result = new LinkedList<>();
-               for (EscherContainerRecord child : children) {
-                       if (type.typeID == child.getRecordId()) {
-                               result.add(child);
-                       }
-               }
-               return result.toArray(new EscherContainerRecord[result.size()]);
+    public StyleTextProp9Atom[] getNumberedListInfo() {
+               EscherContainerRecord dgContainer = getDgContainer();
+
+               return (dgContainer == null) ? new StyleTextProp9Atom[0] : Stream.of(dgContainer).
+                       flatMap(findEscherContainer(EscherRecordTypes.SPGR_CONTAINER)).
+                       flatMap(findEscherContainer(EscherRecordTypes.SP_CONTAINER)).
+                       map(PPDrawing::findInSpContainer).
+                       filter(Optional::isPresent).
+                       map(Optional::get).
+                       toArray(StyleTextProp9Atom[]::new);
     }
 
-    public StyleTextProp9Atom[] getNumberedListInfo() {
-       final List<StyleTextProp9Atom> result = new LinkedList<>();
-       EscherContainerRecord dgContainer = getDgContainer();
-               final EscherContainerRecord spgrContainer = findFirstEscherContainerRecordOfType(RecordTypes.EscherSpgrContainer, dgContainer);
-               final EscherContainerRecord[] spContainers = findAllEscherContainerRecordOfType(RecordTypes.EscherSpContainer, spgrContainer);
-               for (EscherContainerRecord spContainer : spContainers) {
-                   StyleTextProp9Atom prop9 = findInSpContainer(spContainer);
-                   if (prop9 != null) {
-                       result.add(prop9);
-                   }
-               }
-       return result.toArray(new StyleTextProp9Atom[result.size()]);
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties("escherRecords", this::getEscherRecords);
        }
-    
-    @SuppressWarnings("unchecked")
-    private static <T extends Record> T getChildRecord(List<? extends Record> children, RecordTypes type) {
-        for (Record r : children) {
-            if (r.getRecordType() == type.typeID) {
-                return (T)r;
-            }
-        }
-        return null;
-    }
+
+       private static Predicate<Record> sameHSLF(RecordTypes type) {
+               return (p) -> p.getRecordType() == type.typeID;
+       }
+
+       private static Predicate<EscherRecord> sameEscher(EscherRecordTypes type) {
+               return (p) -> p.getRecordId() == type.typeID;
+       }
+
+       @SuppressWarnings("unchecked")
+       private static <T extends EscherRecord> Optional<T> firstEscherRecord(Iterable<EscherRecord> container, EscherRecordTypes type) {
+               return StreamSupport.stream(container.spliterator(), false).filter(sameEscher(type)).map(o -> (T)o).findFirst();
+       }
+
+       private static Function<EscherContainerRecord,Stream<EscherContainerRecord>> findEscherContainer(EscherRecordTypes type) {
+               return (r) -> r.getChildContainers().stream().filter(sameEscher(type));
+       }
+
 }
index 694f2ba148c607f54a015315f32ce2c2a4f78cbd..8f244607b0da64980f608984e0a8c64951570255 100644 (file)
 
 package org.apache.poi.hslf.record;
 
-import org.apache.poi.ddf.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.ddf.DefaultEscherRecordFactory;
+import org.apache.poi.ddf.EscherContainerRecord;
+import org.apache.poi.ddf.EscherDggRecord;
+import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
-import java.io.OutputStream;
-import java.io.IOException;
-import java.io.ByteArrayOutputStream;
-import java.util.Iterator;
-
 /**
  * Container records which always exists inside Document.
  * It always acts as a holder for escher DGG container
  *  which may contain which Escher BStore container information
  *  about pictures containes in the presentation (if any).
- *
- * @author Yegor Kozlov
  */
 public final class PPDrawingGroup extends RecordAtom {
 
@@ -131,4 +134,11 @@ public final class PPDrawingGroup extends RecordAtom {
         }
         return dgg;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+           "dggContainer", this::getDggContainer
+        );
+    }
 }
index f2e7c05ce0f5a675c92870ba7ce5b3857e2ff45f..841d49d9e1f96e33c63ad0362a22a707ca7f6551 100644 (file)
@@ -25,10 +25,13 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.TreeMap;
+import java.util.function.Supplier;
 
 import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.POILogger;
@@ -41,8 +44,6 @@ import org.apache.poi.util.POILogger;
  *  moves, then we have update all of these. If we come up with a new version
  *  of a slide, then we have to add one of these to the end of the chain
  *  (via CurrentUserAtom and UserEditAtom) pointing to the new slide location
- *
- * @author Nick Burch
  */
 
 public final class PersistPtrHolder extends PositionDependentRecordAtom
@@ -62,8 +63,8 @@ public final class PersistPtrHolder extends PositionDependentRecordAtom
         */
        private Map<Integer,Integer> _slideLocations;
 
-       private static final BitField persistIdFld = new BitField(0X000FFFFF);
-       private static final BitField cntPersistFld  = new BitField(0XFFF00000);
+       private static final BitField persistIdFld = BitFieldFactory.getInstance(0X000FFFFF);
+       private static final BitField cntPersistFld  = BitFieldFactory.getInstance(0XFFF00000);
        
     /**
      * Return the value we were given at creation, be it 6001 or 6002
@@ -248,4 +249,11 @@ public final class PersistPtrHolder extends PositionDependentRecordAtom
             return buf;
         }
     }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "slideLocations", this::getSlideLocationsLookup
+               );
+       }
 }
index bec722cb8dd26f6786f254bb4fad554ecb3f0a04..c4ec8c983f5699ffcb8f3b21c4efd9a1e65a8069 100644 (file)
@@ -20,8 +20,10 @@ package org.apache.poi.hslf.record;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.hslf.record.RecordTypes.RecordConstructor;
@@ -37,7 +39,7 @@ import org.apache.poi.util.POILogger;
  * @author Nick Burch
  */
 
-public abstract class Record
+public abstract class Record implements GenericRecord
 {
     // For logging
        protected static final POILogger logger = POILogFactory.getLogger(Record.class);
@@ -71,6 +73,17 @@ public abstract class Record
         */
        public abstract void writeOut(OutputStream o) throws IOException;
 
+       @Override
+       public Enum getGenericRecordType() {
+               return RecordTypes.forTypeID((int)getRecordType());
+       }
+
+       @Override
+       public List<Record> getGenericChildren() {
+               Record[] recs = getChildRecords();
+               return (recs == null) ? null : Arrays.asList(recs);
+       }
+
        /**
         * When writing out, write out a signed int (32bit) in Little Endian format
         */
@@ -188,4 +201,6 @@ public abstract class Record
                // Return the created record
                return toReturn;
        }
+
+
 }
index a714f1950b2e1467d8183c626641c544e8b2748c..f7cf20107b7d9d58f727eda2f4d7c6c2f79ee08d 100644 (file)
@@ -21,6 +21,8 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hslf.util.MutableByteArrayOutputStream;
 import org.apache.poi.util.ArrayUtil;
@@ -370,5 +372,8 @@ public abstract class RecordContainer extends Record
         }
     }
 
-
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return null;
+       }
 }
index a1697d511e4aa625c72d4be4cf70054710c14ef4..c57003c89b1257014937dd00130a73ce2ff2f522 100644 (file)
@@ -169,43 +169,7 @@ public enum RecordTypes {
 
     // Records ~12050 seem to be related to Document Encryption
     DocumentEncryptionAtom(12052,DocumentEncryptionAtom::new),
-
-    // records greater then 0xF000 belong to with Microsoft Office Drawing format also known as Escher
-    EscherDggContainer(0xF000,null),
-    EscherDgg(0xf006,null),
-    EscherCLSID(0xf016,null),
-    EscherOPT(0xf00b,null),
-    EscherBStoreContainer(0xf001,null),
-    EscherBSE(0xf007,null),
-    EscherBlip_START(0xf018,null),
-    EscherBlip_END(0xf117,null),
-    EscherDgContainer(0xf002,null),
-    EscherDg(0xf008,null),
-    EscherRegroupItems(0xf118,null),
-    EscherColorScheme(0xf120,null),
-    EscherSpgrContainer(0xf003,null),
-    EscherSpContainer(0xf004,null),
-    EscherSpgr(0xf009,null),
-    EscherSp(0xf00a,null),
-    EscherTextbox(0xf00c,null),
-    EscherClientTextbox(0xf00d,null),
-    EscherAnchor(0xf00e,null),
-    EscherChildAnchor(0xf00f,null),
-    EscherClientAnchor(0xf010,null),
-    EscherClientData(0xf011,null),
-    EscherSolverContainer(0xf005,null),
-    EscherConnectorRule(0xf012,null),
-    EscherAlignRule(0xf013,null),
-    EscherArcRule(0xf014,null),
-    EscherClientRule(0xf015,null),
-    EscherCalloutRule(0xf017,null),
-    EscherSelection(0xf119,null),
-    EscherColorMRU(0xf11a,null),
-    EscherDeletedPspl(0xf11d,null),
-    EscherSplitMenuColors(0xf11e,null),
-    EscherOleObject(0xf11f,null),
-    // same as EscherTertiaryOptRecord.RECORD_ID
-    EscherUserDefined(0xf122,null);
+    ;
 
     @FunctionalInterface
     public interface RecordConstructor<T extends Record> {
index e843f1dd739f6b726f2a9218db1998404e1649c0..f2c8f03b82780e0f9897c6de1707acf340e0e222 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -102,4 +105,9 @@ public final class RoundTripHFPlaceholder12 extends RecordAtom {
         out.write(_header);
         out.write(_placeholderId);
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties("placeholderId", this::getPlaceholderId );
+    }
 }
index f07542ee069d4e83ad7bd72e17cb014bef721fd8..e0d28e5dfafc7c71a06e0f90b9aaf592b12f5804 100644 (file)
 
 package org.apache.poi.hslf.record;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianConsts;
 
@@ -108,7 +113,27 @@ public class SSSlideInfoAtom extends RecordAtom {
     // public static int RESERVED5_BIT       = 1 << 9;
     // public static int RESERVED6_BIT       = 1 << 11;
     // public static int RESERVED7_BIT       = 1 << 13 | 1 << 14 | 1 << 15;
-    
+
+    private static final int[] EFFECT_MASKS = {
+        MANUAL_ADVANCE_BIT,
+        HIDDEN_BIT,
+        SOUND_BIT,
+        LOOP_SOUND_BIT,
+        STOP_SOUND_BIT,
+        AUTO_ADVANCE_BIT,
+        CURSOR_VISIBLE_BIT
+    };
+
+    private static final String[] EFFECT_NAMES = {
+        "MANUAL_ADVANCE",
+        "HIDDEN",
+        "SOUND",
+        "LOOP_SOUND",
+        "STOP_SOUND",
+        "AUTO_ADVANCE",
+        "CURSOR_VISIBLE"
+    };
+
     private static final long _type = RecordTypes.SSSlideInfoAtom.typeID;
 
     private byte[] _header;
@@ -286,4 +311,16 @@ public class SSSlideInfoAtom extends RecordAtom {
     public void setSpeed(short speed) {
         this._speed = speed;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "effectTransitionFlags", getBitsAsString(this::getEffectTransitionFlags, EFFECT_MASKS, EFFECT_NAMES),
+            "slideTime", this::getSlideTime,
+            "soundIdRef", this::getSoundIdRef,
+            "effectDirection", this::getEffectDirection,
+            "effectType", this::getEffectType,
+            "speed", this::getSpeed
+        );
+    }
 }
index 5d9b347bce4b066d2d7a1023b507c9ab6b3aaa7f..e5b85a7e0022209fed8d60b8c1f34a0d1b8f20c7 100644 (file)
@@ -19,8 +19,11 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hslf.record.SlideAtomLayout.SlideLayoutType;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -170,4 +173,16 @@ public final class SlideAtom extends RecordAtom {
                // Reserved data
                out.write(reserved);
        }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "masterID", this::getMasterID,
+                       "notesID", this::getNotesID,
+                       "followMasterObjects", this::getFollowMasterObjects,
+                       "followMasterScheme", this::getFollowMasterScheme,
+                       "followMasterBackground", this::getFollowMasterBackground,
+                       "layoutAtom", this::getSSlideLayoutAtom
+               );
+       }
 }
index c6bd7957578e6973a0710f0464bdf337272009b0..9b27fe2282c9b937ed83a938f69a04648c7c57a0 100644 (file)
@@ -19,8 +19,12 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hslf.exceptions.HSLFException;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndian;
 
@@ -32,7 +36,7 @@ import org.apache.poi.util.LittleEndian;
  * This might eventually merged with the XSLF counterpart
  */
 @Internal
-public class SlideAtomLayout {
+public class SlideAtomLayout implements GenericRecord {
     // The different kinds of geometry
     public enum SlideLayoutType {
         /** One title and one subtitle placeholder shapes. */
@@ -130,4 +134,11 @@ public class SlideAtomLayout {
         out.write(placeholderIDs);
     }
 
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "geometry", this::getGeometryType,
+            "placeholderIDs", () -> placeholderIDs
+        );
+    }
 }
index 08ed2e846875e1505f72c2a82d287fe295ac37a7..0c6e825d9bdef2e1dcea44f658b21590575286d1 100644 (file)
@@ -47,6 +47,7 @@ import org.apache.poi.util.LittleEndian;
 
 // For now, pretend to be an atom
 public final class SlideListWithText extends RecordContainer {
+       private static final long _type = RecordTypes.SlideListWithText.typeID;
 
        /**
         * Instance filed of the record header indicates that this SlideListWithText stores
@@ -65,7 +66,6 @@ public final class SlideListWithText extends RecordContainer {
        public static final int NOTES = 2;
 
        private byte[] _header;
-       private static long _type = 4080;
 
        private SlideAtomsSet[] slideAtomsSets;
 
index 7c4907f939388a528b1b4fd423f67d2ef321d303..34a55602533f5487741e17db98c51818773b34c2 100644 (file)
 
 package org.apache.poi.hslf.record;
 
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.LittleEndian;
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndian;
 
 /**
  * A SlidePersist Atom (type 1011). Holds information on the text of a
  *  given slide, which are stored in the same SlideListWithText
- *
- * @author Nick Burch
  */
 public final class SlidePersistAtom extends RecordAtom {
 
        //arbitrarily selected; may need to increase
        private static final int MAX_RECORD_LENGTH = 32;
 
-       private byte[] _header;
-       private static long _type = 1011l;
+       private static final long _type = 1011l;
+       private static final int HAS_SHAPES_OTHER_THAN_PLACEHOLDERS = 4;
 
-       /**
-        * Slide reference ID. Should correspond to the PersistPtr
-        *  "sheet ID" of the matching slide/notes record
-        */
+       private static final int[] FLAGS_MASKS = { HAS_SHAPES_OTHER_THAN_PLACEHOLDERS };
+       private static final String[] FLAGS_NAMES = { "HAS_SHAPES_OTHER_THAN_PLACEHOLDERS" };
+
+
+       private final byte[] _header = new byte[8];
+
+       /** Slide reference ID. Should correspond to the PersistPtr "sheet ID" of the matching slide/notes record */
        private int refID;
-       private boolean hasShapesOtherThanPlaceholders;
+       private int flags;
+
        /** Number of placeholder texts that will follow in the SlideListWithText */
        private int numPlaceholderTexts;
-       /**
-        * The internal identifier (256+), which is used to tie slides
-        *  and notes together
-        */
+       /** The internal identifier (256+), which is used to tie slides and notes together */
        private int slideIdentifier;
        /** Reserved fields. Who knows what they do */
        private byte[] reservedFields;
 
-       public int getRefID() { return refID; }
-       public int getSlideIdentifier() { return slideIdentifier; }
-       public int getNumPlaceholderTexts() { return numPlaceholderTexts; }
-       public boolean getHasShapesOtherThanPlaceholders() { return hasShapesOtherThanPlaceholders; }
+       public int getRefID() {
+               return refID;
+       }
+
+       public int getSlideIdentifier() {
+               return slideIdentifier;
+       }
+
+       public int getNumPlaceholderTexts() {
+               return numPlaceholderTexts;
+       }
+
+       public boolean getHasShapesOtherThanPlaceholders() {
+               return (flags & HAS_SHAPES_OTHER_THAN_PLACEHOLDERS) != 0;
+       }
 
        // Only set these if you know what you're doing!
        public void setRefID(int id) {
@@ -75,19 +91,13 @@ public final class SlidePersistAtom extends RecordAtom {
                if(len < 8) { len = 8; }
 
                // Get the header
-               _header = new byte[8];
                System.arraycopy(source,start,_header,0,8);
 
                // Grab the reference ID
                refID = LittleEndian.getInt(source,start+8);
 
                // Next up is a set of flags, but only bit 3 is used!
-               int flags = LittleEndian.getInt(source,start+12);
-               if(flags == 4) {
-                       hasShapesOtherThanPlaceholders = true;
-               } else {
-                       hasShapesOtherThanPlaceholders = false;
-               }
+               flags = LittleEndian.getInt(source,start+12);
 
                // Now the number of Placeholder Texts
                numPlaceholderTexts = LittleEndian.getInt(source,start+16);
@@ -104,13 +114,12 @@ public final class SlidePersistAtom extends RecordAtom {
        /**
         * Create a new SlidePersistAtom, for use with a new Slide
         */
-       public SlidePersistAtom(){
-               _header = new byte[8];
+       public SlidePersistAtom() {
                LittleEndian.putUShort(_header, 0, 0);
                LittleEndian.putUShort(_header, 2, (int)_type);
                LittleEndian.putInt(_header, 4, 20);
 
-               hasShapesOtherThanPlaceholders = true;
+               flags = HAS_SHAPES_OTHER_THAN_PLACEHOLDERS;
                reservedFields = new byte[4];
        }
 
@@ -127,12 +136,6 @@ public final class SlidePersistAtom extends RecordAtom {
                // Header - size or type unchanged
                out.write(_header);
 
-               // Compute the flags part - only bit 3 is used
-               int flags = 0;
-               if(hasShapesOtherThanPlaceholders) {
-                       flags = 4;
-               }
-
                // Write out our fields
                writeLittleEndian(refID,out);
                writeLittleEndian(flags,out);
@@ -140,4 +143,14 @@ public final class SlidePersistAtom extends RecordAtom {
                writeLittleEndian(slideIdentifier,out);
                out.write(reservedFields);
        }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "refID", this::getRefID,
+                       "flags", getBitsAsString(() -> flags, FLAGS_MASKS, FLAGS_NAMES),
+                       "numPlaceholderTexts", this::getNumPlaceholderTexts,
+                       "slideIdentifier", this::getSlideIdentifier
+               );
+       }
 }
index a4676f63a6b826ebab12f28dc6053ac1bab71245..09f9d5c1a7f63e748dd914e90918bf97295138b6 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -102,4 +105,9 @@ public final class SoundData extends RecordAtom {
         out.write(_header);
         out.write(_data);
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties("data", this::getData);
+    }
 }
index 3f1ec4b18aa720024df8e9418f3e367768df3ae6..d11f406faeddcaeefa24286826935904b1dd7b3b 100644 (file)
@@ -21,8 +21,11 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hslf.model.textproperties.TextPFException9;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -145,4 +148,11 @@ public final class StyleTextProp9Atom extends RecordAtom {
         // Update the size (header bytes 5-8)
         LittleEndian.putInt(header, 4, data.length);
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "autoNumberSchemes", this::getAutoNumberTypes
+        );
+    }
 }
\ No newline at end of file
index 096e35c89b229072ceb9022e19a8ca1137604c84..372c52d9ad6b8f2649bc04786619203e7dfad840 100644 (file)
@@ -22,10 +22,13 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.hslf.model.textproperties.TextPropCollection;
 import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.HexDump;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
@@ -109,11 +112,7 @@ public final class StyleTextPropAtom extends RecordAtom {
         return getCharactersCovered(charStyles);
     }
     private int getCharactersCovered(List<TextPropCollection> styles) {
-        int length = 0;
-        for(TextPropCollection tpc : styles) {
-            length += tpc.getCharactersCovered();
-        }
-        return length;
+        return styles.stream().mapToInt(TextPropCollection::getCharactersCovered).sum();
     }
 
     /* *************** record code follows ********************** */
@@ -406,4 +405,12 @@ public final class StyleTextPropAtom extends RecordAtom {
 
         return out.toString();
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return !initialised ? null : GenericRecordUtil.getGenericProperties(
+            "paragraphStyles", this::getParagraphStyles,
+            "characterStyles", this::getCharacterStyles
+        );
+    }
 }
index 5e9eeea795ec7ce641f3b338c437bfe81c587507..e5f8d4f4dcd0f95c6857360798a18c6fa9134c7a 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.HexDump;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
@@ -116,4 +119,11 @@ public final class TextBytesAtom extends RecordAtom {
                out.append( HexDump.dump(_text, 0, 0) );
                return out.toString();
        }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "text", this::getText
+               );
+       }
 }
index 349ee8166b4101e0857113d53a43a85456989bd4..0e8b95b3ab58a75b8e0399ba8d25301afaa1dead 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.HexDump;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
@@ -112,4 +115,11 @@ public final class TextCharsAtom extends RecordAtom {
                out.append( HexDump.dump(_text, 0, 0) );
                return out.toString();
        }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "text", this::getText
+               );
+       }
 }
index 05c61a667db066a2baf49eb84fe6bc598d718a98..3264b7879362304401c12a86161f770faaee5c16 100644 (file)
@@ -19,8 +19,12 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hslf.exceptions.HSLFException;
+import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -34,15 +38,6 @@ public final class TextHeaderAtom extends RecordAtom implements ParentAwareRecor
        private byte[] _header;
        private RecordContainer parentRecord;
 
-       public static final int TITLE_TYPE = 0;
-       public static final int BODY_TYPE = 1;
-       public static final int NOTES_TYPE = 2;
-       public static final int OTHER_TYPE = 4;
-       public static final int CENTRE_BODY_TYPE = 5;
-       public static final int CENTER_TITLE_TYPE = 6;
-       public static final int HALF_BODY_TYPE = 7;
-       public static final int QUARTER_BODY_TYPE = 8;
-
        /** The kind of text it is */
        private int textType;
        /** position in the owning SlideListWithText */
@@ -50,7 +45,15 @@ public final class TextHeaderAtom extends RecordAtom implements ParentAwareRecor
 
        public int getTextType() { return textType; }
        public void setTextType(int type) { textType = type; }
-       
+
+       public TextPlaceholder getTextTypeEnum() {
+               return TextPlaceholder.fromNativeId(textType);
+       }
+
+       public void setTextTypeEnum(TextPlaceholder placeholder) {
+               textType = placeholder.nativeId;
+       }
+
     /**
      * @return  0-based index of the text run in the SLWT container
      */
@@ -97,7 +100,7 @@ public final class TextHeaderAtom extends RecordAtom implements ParentAwareRecor
                LittleEndian.putUShort(_header, 2, (int)_type);
                LittleEndian.putInt(_header, 4, 4);
 
-               textType = OTHER_TYPE;
+               textType = TextPlaceholder.OTHER.nativeId;
        }
 
        /**
@@ -118,4 +121,12 @@ public final class TextHeaderAtom extends RecordAtom implements ParentAwareRecor
                // Write out our type
                writeLittleEndian(textType,out);
        }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties(
+                       "index", this::getIndex,
+                       "textType", this::getTextTypeEnum
+               );
+       }
 }
index b8ef54ad1047b65d3e7df448089ac047fb1632ed..3b194b7cc028888e6885042e6f3578236acaea57 100644 (file)
@@ -25,10 +25,13 @@ import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hslf.model.textproperties.HSLFTabStop;
 import org.apache.poi.hslf.model.textproperties.HSLFTabStopPropCollection;
 import org.apache.poi.util.BitField;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianByteArrayInputStream;
@@ -222,4 +225,15 @@ public final class TextRulerAtom extends RecordAtom {
         this.indent[0] = (int)indent;
         this.indent[1] = (int)indent;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "defaultTabSize", this::getDefaultTabSize,
+            "numLevels", this::getNumberOfLevels,
+            "tabStops", this::getTabStops,
+            "leftMargins", () -> leftMargin,
+            "indents", () -> indent
+        );
+    }
 }
index d506f48282921f267ffb9460750df7aee4683b23..115dc21e4cb34ba80822df05ce916fddc24a8c61 100644 (file)
@@ -22,8 +22,11 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hslf.exceptions.HSLFException;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianByteArrayInputStream;
@@ -177,4 +180,11 @@ public final class TextSpecInfoAtom extends RecordAtom {
         return lst.toArray(new TextSpecInfoRun[lst.size()]);
     }
 
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "charactersCovered", this::getCharactersCovered,
+            "textSpecInfoRuns", this::getTextSpecInfoRuns
+        );
+    }
 }
index 8a046c1613d79b897f40dacab1b6e09c13fb35ad..9c890a2b56e51d10fe7dd5569a2ccc49d39a5901 100644 (file)
 
 package org.apache.poi.hslf.record;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianByteArrayInputStream;
 
-public class TextSpecInfoRun {
+public class TextSpecInfoRun implements GenericRecord {
 
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 1_000_000;
@@ -51,34 +59,42 @@ public class TextSpecInfoRun {
     }
 
     /** A bit that specifies whether the spellInfo field exists. */
-    private static final BitField spellFld    = new BitField(0X00000001);
+    private static final BitField spellFld    = BitFieldFactory.getInstance(0X00000001);
     /** A bit that specifies whether the lid field exists. */
-    private static final BitField langFld     = new BitField(0X00000002);
+    private static final BitField langFld     = BitFieldFactory.getInstance(0X00000002);
     /** A bit that specifies whether the altLid field exists. */
-    private static final BitField altLangFld  = new BitField(0X00000004);
+    private static final BitField altLangFld  = BitFieldFactory.getInstance(0X00000004);
     // unused1, unused2 - Undefined and MUST be ignored.
     /** A bit that specifies whether the pp10runid, reserved3, and grammarError fields exist. */
-    private static final BitField pp10extFld  = new BitField(0X00000020);
+    private static final BitField pp10extFld  = BitFieldFactory.getInstance(0X00000020);
     /** A bit that specifies whether the bidi field exists. */
-    private static final BitField bidiFld     = new BitField(0X00000040);
+    private static final BitField bidiFld     = BitFieldFactory.getInstance(0X00000040);
     // unused3 - Undefined and MUST be ignored.
     // reserved1 - MUST be zero and MUST be ignored.
     /** A bit that specifies whether the smartTags field exists. */
-    private static final BitField smartTagFld = new BitField(0X00000200);
+    private static final BitField smartTagFld = BitFieldFactory.getInstance(0X00000200);
     // reserved2 - MUST be zero and MUST be ignored.
 
     /**
      * An optional unsigned integer that specifies an identifier for a character
      * run that contains StyleTextProp11 data. It MUST exist if and only if pp10ext is TRUE.
      **/
-    private static final BitField pp10runidFld = new BitField(0X0000000F);
+    private static final BitField pp10runidFld = BitFieldFactory.getInstance(0X0000000F);
     // reserved3 - An optional unsigned integer that MUST be zero, and MUST be ignored. It
     // MUST exist if and only if fPp10ext is TRUE.
     /**
      * An optional bit that specifies a grammar error. It MUST exist if and
      * only if fPp10ext is TRUE.
      **/
-    private static final BitField grammarErrorFld = new BitField(0X80000000);
+    private static final BitField grammarErrorFld = BitFieldFactory.getInstance(0X80000000);
+
+    private static final int[] FLAGS_MASKS = {
+        0X00000001, 0X00000002, 0X00000004, 0X00000020, 0X00000040, 0X00000200,
+    };
+
+    private static final String[] FLAGS_NAMES = {
+        "SPELL", "LANG", "ALT_LANG", "PP10_EXT", "BIDI", "SMART_TAG"
+    };
 
     //Length of special info run.
     private int length;
@@ -357,4 +373,18 @@ public class TextSpecInfoRun {
         // if both parameters are invalid, remove the extension mask
         mask = pp10extFld.setBoolean(mask, pp10extMask != -1);
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+        m.put("flags", getBitsAsString(() -> mask, FLAGS_MASKS, FLAGS_NAMES));
+        m.put("spellInfo", this::getSpellInfo);
+        m.put("langId", this::getLangId);
+        m.put("altLangId", this::getAltLangId);
+        m.put("bidi", this::getBidi);
+        m.put("pp10RunId", this::getPP10RunId);
+        m.put("grammarError", this::getGrammarError);
+        m.put("smartTags", this::getSmartTagsBytes);
+        return Collections.unmodifiableMap(m);
+    }
 }
index 66ec91b566ce5216d4c94fd19e29f9d0d599096f..469de32a56261b80f13cef29c8cb63adceb2d45b 100644 (file)
 
 package org.apache.poi.hslf.record;
 
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
-import java.io.OutputStream;
-import java.io.IOException;
-
 /**
  * Tne atom that holds starting and ending character positions of a hyperlink
  *
@@ -125,4 +128,12 @@ public final class TxInteractiveInfoAtom extends RecordAtom {
         out.write(_header);
         out.write(_data);
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "startIndex", this::getStartIndex,
+            "endIndex", this::getEndIndex
+        );
+    }
 }
index 70b3221df6ce61d77d3b9dc7e0cf1bc56269f36d..a706d0d33b16910eaa1760cf216397725c863e2c 100644 (file)
@@ -22,10 +22,14 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.hslf.model.textproperties.TextPropCollection;
 import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType;
+import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianOutputStream;
@@ -45,8 +49,6 @@ import org.apache.poi.util.POILogger;
  *  each pair describes an indent level. The first pair describes
  *  first-level paragraph with no indentation.
  * </p>
- *
- *  @author Yegor Kozlov
  */
 public final class TxMasterStyleAtom extends RecordAtom {
     private static final POILogger LOG = POILogFactory.getLogger(TxMasterStyleAtom.class);
@@ -58,8 +60,9 @@ public final class TxMasterStyleAtom extends RecordAtom {
      */
     public static final int MAX_INDENT = 5;
 
+    private static final long _type = RecordTypes.TxMasterStyleAtom.typeID;
+
     private byte[] _header;
-    private static long _type = 4003;
     private byte[] _data;
 
     private List<TextPropCollection> paragraphStyles;
@@ -152,7 +155,7 @@ public final class TxMasterStyleAtom extends RecordAtom {
 
         for(short i = 0; i < levels; i++) {
             TextPropCollection prprops = new TextPropCollection(0, TextPropType.paragraph);
-            if (type >= TextHeaderAtom.CENTRE_BODY_TYPE) {
+            if (type >= TextPlaceholder.CENTER_BODY.nativeId) {
                 // Fetch the 2 byte value, that is safe to ignore for some types of text
                 short indentLevel = LittleEndian.getShort(_data, pos);
                 prprops.setIndentLevel(indentLevel);
@@ -195,7 +198,7 @@ public final class TxMasterStyleAtom extends RecordAtom {
             for (int i=0; i<levels; i++) {
                 prdummy.copy(paragraphStyles.get(i));
                 chdummy.copy(charStyles.get(i));
-                if (type >= TextHeaderAtom.CENTRE_BODY_TYPE) {
+                if (type >= TextPlaceholder.CENTER_BODY.nativeId) {
                     leos.writeShort(prdummy.getIndentLevel());
                 }
                 
@@ -213,4 +216,12 @@ public final class TxMasterStyleAtom extends RecordAtom {
             throw new HSLFException("error in updating master style properties", e);
         }
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "paragraphStyles", this::getParagraphStyles,
+            "charStyles", this::getCharacterStyles
+        );
+    }
 }
index 4790ae1ee690e87793fa710784d654047ca2e8aa..237027b088060d555fdf52138de372f880dd6a7c 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 
@@ -76,4 +79,9 @@ public final class UnknownRecordPlaceholder extends RecordAtom
        public void writeOut(OutputStream out) throws IOException {
                out.write(_contents);
        }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return GenericRecordUtil.getGenericProperties("contents", () -> _contents);
+       }
 }
index 54a0e5033aa6a449cc4c9239a351943ac208d8ba..287da2779862a11cfa8ef30f7c1885db88b72dc7 100644 (file)
@@ -19,7 +19,10 @@ package org.apache.poi.hslf.record;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.util.LittleEndian;
@@ -32,8 +35,6 @@ import org.apache.poi.util.LittleEndianConsts;
  * ** WARNING ** stores byte offsets from the start of the PPT stream to
  *  other records! If you change the size of any elements before one of
  *  these, you'll need to update the offsets!
- *
- * @author Nick Burch
  */
 
 public final class UserEditAtom extends PositionDependentRecordAtom
@@ -44,7 +45,7 @@ public final class UserEditAtom extends PositionDependentRecordAtom
        public static final int LAST_VIEW_NOTES = 3;
 
        private byte[] _header;
-       private static long _type = 4085l;
+       private static final long _type = RecordTypes.UserEditAtom.typeID;
        private short unused;
 
        private int lastViewedSlideID;
@@ -190,4 +191,18 @@ public final class UserEditAtom extends PositionDependentRecordAtom
                    writeLittleEndian(encryptSessionPersistIdRef,out);
                }
        }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               final Map<String, Supplier<?>> m = new LinkedHashMap<>();
+               m.put("lastViewedSlideID", this::getLastViewedSlideID);
+               m.put("pptVersion", () -> pptVersion);
+               m.put("lastUserEditAtomOffset", this::getLastUserEditAtomOffset);
+               m.put("persistPointersOffset", this::getPersistPointersOffset);
+               m.put("docPersistRef", this::getDocPersistRef);
+               m.put("maxPersistWritten", this::getMaxPersistWritten);
+               m.put("lastViewType", this::getLastViewType);
+               m.put("encryptSessionPersistIdRef", this::getEncryptSessionPersistIdRef);
+               return Collections.unmodifiableMap(m);
+       }
 }
index d847c38539f5a6ce46edfdcdcfdf0882c7eeec86..eb4d3373832b9fc23c9a3c70ff17c774584798fc 100644 (file)
 
 package org.apache.poi.hslf.record;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -114,5 +116,12 @@ public final class VBAInfoAtom extends RecordAtom {
         this.version = version;
     }
 
-    
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "persistIdRef", this::getPersistIdRef,
+            "hasMacros", this::isHasMacros,
+            "version", this::getVersion
+        );
+    }
 }
index 03577653ed823a89aafae30218bd6f4f1e6d3bdd..013ace9fed70b795f68f06a9844bf79e57bddf48 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.poi.util.LittleEndian;
  */
 public final class VBAInfoContainer extends RecordContainer {
     private byte[] _header;
-    private static long _type = RecordTypes.VBAInfo.typeID;
+    private static final long _type = RecordTypes.VBAInfo.typeID;
 
     // Links to our more interesting children
 
index 806c206fb5bb3765118690653f43eccba9572fc1..52952e21914273dfd8cab47bc63c46531bc8add9 100644 (file)
@@ -29,9 +29,9 @@ import org.apache.poi.ddf.EscherColorRef;
 import org.apache.poi.ddf.EscherContainerRecord;
 import org.apache.poi.ddf.EscherProperties;
 import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherRecordTypes;
 import org.apache.poi.ddf.EscherSimpleProperty;
 import org.apache.poi.hslf.record.Document;
-import org.apache.poi.hslf.record.RecordTypes;
 import org.apache.poi.sl.draw.DrawPaint;
 import org.apache.poi.sl.usermodel.ColorStyle;
 import org.apache.poi.sl.usermodel.FillStyle;
@@ -257,7 +257,7 @@ public final class HSLFFill {
     private boolean isRotatedWithShape() {
         // NOFILLHITTEST can be in the normal escher opt record but also in the tertiary record
         // the extended bit fields seem to be in the second
-        AbstractEscherOptRecord opt = shape.getEscherChild(RecordTypes.EscherUserDefined);
+        AbstractEscherOptRecord opt = shape.getEscherChild(EscherRecordTypes.USER_DEFINED);
         EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST);
         int propVal = (p == null) ? 0 : p.getPropertyValue();
         return FILL_USE_USE_SHAPE_ANCHOR.isSet(propVal) && FILL_USE_SHAPE_ANCHOR.isSet(propVal);
index c10daef5e91c47e9916da757bb0fe23a8d9588a2..8f538d65ec8187eae33a73771ab2c2a04927379d 100644 (file)
@@ -35,13 +35,13 @@ import org.apache.poi.ddf.EscherContainerRecord;
 import org.apache.poi.ddf.EscherProperties;
 import org.apache.poi.ddf.EscherProperty;
 import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherRecordTypes;
 import org.apache.poi.ddf.EscherSimpleProperty;
 import org.apache.poi.ddf.EscherSpRecord;
 import org.apache.poi.ddf.EscherTextboxRecord;
 import org.apache.poi.hslf.record.ColorSchemeAtom;
 import org.apache.poi.hslf.record.HSLFEscherClientDataRecord;
 import org.apache.poi.hslf.record.Record;
-import org.apache.poi.hslf.record.RecordTypes;
 import org.apache.poi.sl.draw.DrawFactory;
 import org.apache.poi.sl.usermodel.FillStyle;
 import org.apache.poi.sl.usermodel.PresetColor;
@@ -241,7 +241,7 @@ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> {
     /**
      * @since POI 3.14-Beta2
      */
-    public static <T extends EscherRecord> T getEscherChild(EscherContainerRecord owner, RecordTypes recordId){
+    public static <T extends EscherRecord> T getEscherChild(EscherContainerRecord owner, EscherRecordTypes recordId){
         return getEscherChild(owner, recordId.typeID);
     }
 
@@ -252,7 +252,7 @@ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> {
     /**
      * @since POI 3.14-Beta2
      */
-    public <T extends EscherRecord> T getEscherChild(RecordTypes recordId){
+    public <T extends EscherRecord> T getEscherChild(EscherRecordTypes recordId){
         return getEscherChild(recordId.typeID);
     }
     
@@ -577,9 +577,9 @@ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> {
     }
 
     public AbstractEscherOptRecord getEscherOptRecord() {
-        AbstractEscherOptRecord opt = getEscherChild(RecordTypes.EscherOPT);
+        AbstractEscherOptRecord opt = getEscherChild(EscherRecordTypes.OPT);
         if (opt == null) {
-            opt = getEscherChild(RecordTypes.EscherUserDefined);
+            opt = getEscherChild(EscherRecordTypes.USER_DEFINED);
         }
         return opt;
     }
index 58e216a4f5ddda1c08f993487cbdea79473d3ad8..7ff8404e7f38c24c1e9647da8344a33bec61c12a 100644 (file)
@@ -27,6 +27,7 @@ import org.apache.poi.ddf.EscherProperties;
 import org.apache.poi.ddf.EscherProperty;
 import org.apache.poi.ddf.EscherPropertyFactory;
 import org.apache.poi.ddf.EscherRecord;
+import org.apache.poi.ddf.EscherRecordTypes;
 import org.apache.poi.ddf.EscherSimpleProperty;
 import org.apache.poi.ddf.EscherSpRecord;
 import org.apache.poi.ddf.EscherTextboxRecord;
@@ -64,7 +65,7 @@ public final class HSLFShapeFactory {
     public static HSLFGroupShape createShapeGroup(EscherContainerRecord spContainer, ShapeContainer<HSLFShape,HSLFTextParagraph> parent){
         boolean isTable = false;
         EscherContainerRecord ecr = (EscherContainerRecord)spContainer.getChild(0);
-        EscherRecord opt = HSLFShape.getEscherChild(ecr, RecordTypes.EscherUserDefined);
+        EscherRecord opt = HSLFShape.getEscherChild(ecr, EscherRecordTypes.USER_DEFINED);
 
         if (opt != null) {
             EscherPropertyFactory f = new EscherPropertyFactory();
index 673b72009aa1faa512a82198c441a2a05e4f1ca0..14d85b56f8382009927510c295faafbcf8261acf 100644 (file)
@@ -24,6 +24,7 @@ import java.util.List;
 import org.apache.poi.ddf.EscherContainerRecord;
 import org.apache.poi.ddf.EscherDgRecord;
 import org.apache.poi.ddf.EscherDggRecord;
+import org.apache.poi.ddf.EscherRecordTypes;
 import org.apache.poi.ddf.EscherSpRecord;
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.hslf.model.HeadersFooters;
@@ -47,6 +48,7 @@ import org.apache.poi.sl.usermodel.Notes;
 import org.apache.poi.sl.usermodel.Placeholder;
 import org.apache.poi.sl.usermodel.ShapeType;
 import org.apache.poi.sl.usermodel.Slide;
+import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder;
 
 /**
  * This class represents a slide in a PowerPoint Document. It allows
@@ -168,12 +170,12 @@ public final class HSLFSlide extends HSLFSheet implements Slide<HSLFShape,HSLFTe
 
         for (EscherContainerRecord c : dgContainer.getChildContainers()) {
             EscherSpRecord spr = null;
-            switch(c.getRecordId()){
-                case EscherContainerRecord.SPGR_CONTAINER:
+            switch(EscherRecordTypes.forTypeID(c.getRecordId())){
+                case SPGR_CONTAINER:
                     EscherContainerRecord dc = (EscherContainerRecord)c.getChild(0);
                     spr = dc.getChildById(EscherSpRecord.RECORD_ID);
                     break;
-                case EscherContainerRecord.SP_CONTAINER:
+                case SP_CONTAINER:
                     spr = c.getChildById(EscherSpRecord.RECORD_ID);
                     break;
                 default:
@@ -197,7 +199,7 @@ public final class HSLFSlide extends HSLFSheet implements Slide<HSLFShape,HSLFTe
                HSLFPlaceholder pl = new HSLFPlaceholder();
                pl.setShapeType(ShapeType.RECT);
                pl.setPlaceholder(Placeholder.TITLE);
-               pl.setRunType(TextHeaderAtom.TITLE_TYPE);
+               pl.setRunType(TextPlaceholder.TITLE.nativeId);
                pl.setText("Click to edit title");
                pl.setAnchor(new java.awt.Rectangle(54, 48, 612, 90));
                addShape(pl);
@@ -222,12 +224,10 @@ public final class HSLFSlide extends HSLFSheet implements Slide<HSLFShape,HSLFTe
                 continue;
             }
                        int type = tp.get(0).getRunType();
-                       switch (type) {
-                       case TextHeaderAtom.CENTER_TITLE_TYPE:
-                       case TextHeaderAtom.TITLE_TYPE:
-                           String str = HSLFTextParagraph.getRawText(tp);
-                           return HSLFTextParagraph.toExternalString(str, type);
-                       }
+                   if (TextPlaceholder.isTitle(type)) {
+                String str = HSLFTextParagraph.getRawText(tp);
+                return HSLFTextParagraph.toExternalString(str, type);
+            }
                }
                return null;
        }
index c7535f709a8a8abb5d57694e5c7c4b60d5f96efa..f33f641fccaf01d08de105a25c4148933a6e9f14 100644 (file)
@@ -26,6 +26,7 @@ import org.apache.poi.hslf.model.textproperties.TextPropCollection;
 import org.apache.poi.hslf.record.MainMaster;
 import org.apache.poi.hslf.record.TextHeaderAtom;
 import org.apache.poi.hslf.record.TxMasterStyleAtom;
+import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder;
 import org.apache.poi.util.Internal;
 
 /**
@@ -106,13 +107,15 @@ public final class HSLFSlideMaster extends HSLFMasterSheet {
             }
         }
 
-        switch (txtype) {
-            case TextHeaderAtom.CENTRE_BODY_TYPE:
-            case TextHeaderAtom.HALF_BODY_TYPE:
-            case TextHeaderAtom.QUARTER_BODY_TYPE:
-                return getPropCollection(TextHeaderAtom.BODY_TYPE, level, name, isCharacter);
-            case TextHeaderAtom.CENTER_TITLE_TYPE:
-                return getPropCollection(TextHeaderAtom.TITLE_TYPE, level, name, isCharacter);
+        switch (TextPlaceholder.fromNativeId(txtype)) {
+            case BODY:
+            case CENTER_BODY:
+            case HALF_BODY:
+            case QUARTER_BODY:
+                return getPropCollection(TextPlaceholder.BODY.nativeId, level, name, isCharacter);
+            case TITLE:
+            case CENTER_TITLE:
+                return getPropCollection(TextPlaceholder.TITLE.nativeId, level, name, isCharacter);
             default:
                 return null;
         }
index 5cc28e4f543f6d951996fdc4bbc02a4692a0a712..490a590d3060c935c0b051444828123da0a9e2d9 100644 (file)
@@ -31,7 +31,9 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.common.usermodel.fonts.FontInfo;
 import org.apache.poi.ddf.EscherBSERecord;
 import org.apache.poi.ddf.EscherContainerRecord;
@@ -63,7 +65,7 @@ import org.apache.poi.util.Units;
  * TODO: - figure out how to match notes to their correct sheet (will involve
  * understanding DocSlideList and DocNotesList) - handle Slide creation cleaner
  */
-public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagraph>, Closeable {
+public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagraph>, Closeable, GenericRecord {
 
        //arbitrarily selected; may need to increase
        private static final int MAX_RECORD_LENGTH = 10_000_000;
@@ -1163,4 +1165,14 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
        public Object getPersistDocument() {
                return getSlideShowImpl();
        }
+
+       @Override
+       public Map<String, Supplier<?>> getGenericProperties() {
+               return null;
+       }
+
+       @Override
+       public List<? extends GenericRecord> getGenericChildren() {
+               return Arrays.asList(_hslfSlideShow.getRecords());
+       }
 }
index 627100203710cb1e829194200055fe2d0784d7a8..16f039d7d8f02daf708037d8861269ceb919fe04 100644 (file)
@@ -31,8 +31,8 @@ import org.apache.poi.ddf.EscherArrayProperty;
 import org.apache.poi.ddf.EscherContainerRecord;
 import org.apache.poi.ddf.EscherOptRecord;
 import org.apache.poi.ddf.EscherProperties;
+import org.apache.poi.ddf.EscherRecordTypes;
 import org.apache.poi.ddf.EscherSimpleProperty;
-import org.apache.poi.hslf.record.RecordTypes;
 import org.apache.poi.sl.usermodel.ShapeContainer;
 import org.apache.poi.sl.usermodel.TableShape;
 import org.apache.poi.util.LittleEndian;
@@ -100,14 +100,14 @@ implements HSLFShapeContainer, TableShape<HSLFShape,HSLFTextParagraph> {
 
         EscherContainerRecord spCont = (EscherContainerRecord) getSpContainer().getChild(0);
         AbstractEscherOptRecord opt = new EscherOptRecord();
-        opt.setRecordId(RecordTypes.EscherUserDefined.typeID);
+        opt.setRecordId(EscherRecordTypes.USER_DEFINED.typeID);
         opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GROUPSHAPE__TABLEPROPERTIES, 1));
         EscherArrayProperty p = new EscherArrayProperty((short)(0x4000 | EscherProperties.GROUPSHAPE__TABLEROWPROPERTIES), false, null);
         p.setSizeOfElements(0x0004);
         p.setNumberOfElementsInArray(numRows);
         p.setNumberOfElementsInMemory(numRows);
         opt.addEscherProperty(p);
-        spCont.addChildBefore(opt, RecordTypes.EscherClientAnchor.typeID);
+        spCont.addChildBefore(opt, EscherRecordTypes.CLIENT_ANCHOR.typeID);
     }
 
     /**
@@ -367,7 +367,7 @@ implements HSLFShapeContainer, TableShape<HSLFShape,HSLFTextParagraph> {
         }
 
         // update row height in the table properties
-        AbstractEscherOptRecord opt = getEscherChild(RecordTypes.EscherUserDefined.typeID);
+        AbstractEscherOptRecord opt = getEscherChild(EscherRecordTypes.USER_DEFINED);
         EscherArrayProperty p = opt.lookup(EscherProperties.GROUPSHAPE__TABLEROWPROPERTIES);
         byte[] masterBytes = p.getElement(row);
         double currentHeight = Units.masterToPoints(LittleEndian.getInt(masterBytes, 0));
@@ -460,7 +460,7 @@ implements HSLFShapeContainer, TableShape<HSLFShape,HSLFTextParagraph> {
     }
 
     private void updateRowHeightsProperty() {
-        AbstractEscherOptRecord opt = getEscherChild(RecordTypes.EscherUserDefined.typeID);
+        AbstractEscherOptRecord opt = getEscherChild(EscherRecordTypes.USER_DEFINED);
         EscherArrayProperty p = opt.lookup(EscherProperties.GROUPSHAPE__TABLEROWPROPERTIES);
         byte[] val = new byte[4];
         for (int rowIdx = 0; rowIdx < cells.length; rowIdx++) {
index 49b114512ba17ebc97826ed9c23df4eab4315d9e..c46881d8fc8b7140b8885ff3ff1c09b597814976 100644 (file)
@@ -50,6 +50,7 @@ import org.apache.poi.sl.usermodel.Placeholder;
 import org.apache.poi.sl.usermodel.TabStop;
 import org.apache.poi.sl.usermodel.TabStop.TabStopType;
 import org.apache.poi.sl.usermodel.TextParagraph;
+import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 import org.apache.poi.util.StringUtil;
@@ -1291,20 +1292,12 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFShape,HSLFText
         // them to \n
         String text = rawText.replace('\r', '\n');
 
-        switch (runType) {
-        // 0xB acts like cariage return in page titles and like blank in the
-        // others
-        case -1:
-        case org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE:
-        case org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE:
-            text = text.replace((char) 0x0B, '\n');
-            break;
-        default:
-            text = text.replace((char) 0x0B, ' ');
-            break;
-        }
+        // 0xB acts like carriage return in page titles and like blank in the others
+        final char repl = (runType == -1 ||
+            runType == TextPlaceholder.TITLE.nativeId ||
+            runType == TextPlaceholder.CENTER_TITLE.nativeId) ? '\n' : ' ';
 
-        return text;
+        return text.replace('\u000b', repl);
     }
 
     /**
index 710e8c16d869af75350d447f48a4c782f5ef2081..28756f6d2e228da2d00c7a4a00be646ecc70e1d9 100644 (file)
@@ -19,6 +19,8 @@ package org.apache.poi.hslf.usermodel;
 
 import static org.apache.poi.hslf.record.RecordTypes.OEPlaceholderAtom;
 import static org.apache.poi.hslf.record.RecordTypes.RoundTripHFPlaceholder12;
+import static org.apache.poi.sl.usermodel.TextShape.TextPlaceholder.CENTER_TITLE;
+import static org.apache.poi.sl.usermodel.TextShape.TextPlaceholder.TITLE;
 
 import java.awt.Graphics2D;
 import java.awt.geom.Rectangle2D;
@@ -376,7 +378,7 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
     /* package */ HSLFTextAnchor getAlignment(){
         AbstractEscherOptRecord opt = getEscherOptRecord();
         EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT);
-        HSLFTextAnchor align = HSLFTextAnchor.TOP;
+        final HSLFTextAnchor align;
         if (prop == null){
             /**
              * If vertical alignment was not found in the shape properties then try to
@@ -386,29 +388,17 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
             HSLFSheet sh = getSheet();
             HSLFMasterSheet master = (sh != null) ? sh.getMasterSheet() : null;
             HSLFTextShape masterShape = (master != null) ? master.getPlaceholderByTextType(type) : null;
-            if (masterShape != null && type != TextHeaderAtom.OTHER_TYPE) {
+            if (masterShape != null && type != TextPlaceholder.OTHER.nativeId) {
                 align = masterShape.getAlignment();
             } else {
                 //not found in the master sheet. Use the hardcoded defaults.
-                switch (type){
-                     case TextHeaderAtom.TITLE_TYPE:
-                     case TextHeaderAtom.CENTER_TITLE_TYPE:
-                         align = HSLFTextAnchor.MIDDLE;
-                         break;
-                     default:
-                         align = HSLFTextAnchor.TOP;
-                         break;
-                 }
+                align = (TextPlaceholder.isTitle(type)) ? HSLFTextAnchor.MIDDLE : HSLFTextAnchor.TOP;
             }
         } else {
             align = HSLFTextAnchor.fromNativeId(prop.getPropertyValue());
         }
 
-        if (align == null) {
-            align = HSLFTextAnchor.TOP;
-        }
-
-        return align;
+        return (align == null) ?  HSLFTextAnchor.TOP : align;
     }
 
     /**
@@ -866,34 +856,34 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
         switch (placeholder) {
             default:
             case BODY:
-                runType = TextHeaderAtom.BODY_TYPE;
+                runType = TextPlaceholder.BODY.nativeId;
                 ph = Placeholder.BODY;
                 break;
             case TITLE:
-                runType = TextHeaderAtom.TITLE_TYPE;
+                runType = TITLE.nativeId;
                 ph = Placeholder.TITLE;
                 break;
             case CENTER_BODY:
-                runType = TextHeaderAtom.CENTRE_BODY_TYPE;
+                runType = TextPlaceholder.CENTER_BODY.nativeId;
                 ph = Placeholder.BODY;
                 break;
             case CENTER_TITLE:
-                runType = TextHeaderAtom.CENTER_TITLE_TYPE;
+                runType = CENTER_TITLE.nativeId;
                 ph = Placeholder.TITLE;
                 break;
             case HALF_BODY:
-                runType = TextHeaderAtom.HALF_BODY_TYPE;
+                runType = TextPlaceholder.HALF_BODY.nativeId;
                 ph = Placeholder.BODY;
                 break;
             case QUARTER_BODY:
-                runType = TextHeaderAtom.QUARTER_BODY_TYPE;
+                runType = TextPlaceholder.QUARTER_BODY.nativeId;
                 ph = Placeholder.BODY;
                 break;
             case NOTES:
-                runType = TextHeaderAtom.NOTES_TYPE;
+                runType = TextPlaceholder.NOTES.nativeId;
                 break;
             case OTHER:
-                runType = TextHeaderAtom.OTHER_TYPE;
+                runType = TextPlaceholder.OTHER.nativeId;
                 break;
         }
         setRunType(runType);
@@ -904,17 +894,7 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
 
     @Override
     public TextPlaceholder getTextPlaceholder() {
-        switch (getRunType()) {
-            default:
-            case TextHeaderAtom.BODY_TYPE: return TextPlaceholder.BODY;
-            case TextHeaderAtom.TITLE_TYPE: return TextPlaceholder.TITLE;
-            case TextHeaderAtom.NOTES_TYPE: return TextPlaceholder.NOTES;
-            case TextHeaderAtom.OTHER_TYPE: return TextPlaceholder.OTHER;
-            case TextHeaderAtom.CENTRE_BODY_TYPE: return TextPlaceholder.CENTER_BODY;
-            case TextHeaderAtom.CENTER_TITLE_TYPE: return TextPlaceholder.CENTER_TITLE;
-            case TextHeaderAtom.HALF_BODY_TYPE: return TextPlaceholder.HALF_BODY;
-            case TextHeaderAtom.QUARTER_BODY_TYPE: return TextPlaceholder.QUARTER_BODY;
-        }
+        return TextPlaceholder.fromNativeId(getRunType());
     }
 
 
index 0a3eecd00cf41ed67d73176a59bfa1e0694554e1..e17811086ca15da7c80ed8c89214ae21d8121127 100644 (file)
@@ -31,7 +31,6 @@ import java.awt.font.FontRenderContext;
 import java.awt.font.TextAttribute;
 import java.awt.font.TextLayout;
 import java.awt.geom.AffineTransform;
-import java.awt.geom.Area;
 import java.awt.geom.Dimension2D;
 import java.awt.geom.NoninvertibleTransformException;
 import java.awt.geom.Point2D;
@@ -45,6 +44,7 @@ import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.TreeMap;
+import java.util.function.BiConsumer;
 
 import org.apache.commons.codec.Charsets;
 import org.apache.poi.common.usermodel.fonts.FontCharset;
@@ -68,7 +68,24 @@ import org.apache.poi.util.LocaleUtil;
 public class HwmfGraphics {
 
     public enum FillDrawStyle {
-        NONE, FILL, DRAW, FILL_DRAW
+        NONE(FillDrawStyle::fillNone),
+        FILL(HwmfGraphics::fill),
+        DRAW(HwmfGraphics::draw),
+        FILL_DRAW(FillDrawStyle::fillDraw);
+
+        public final BiConsumer<HwmfGraphics,Shape> handler;
+
+        FillDrawStyle(BiConsumer<HwmfGraphics,Shape> handler) {
+            this.handler = handler;
+        }
+
+        private static void fillNone(HwmfGraphics g, Shape s) {
+        }
+
+        private static void fillDraw(HwmfGraphics g, Shape s) {
+            g.fill(s);
+            g.draw(s);
+        }
     }
 
     protected final List<HwmfDrawProperties> propStack = new LinkedList<>();
index bc96032f030b84c16983b835eaa9feb11ab7e51a..ed34e355a9ec8549641f5810c61fe26a37b782e6 100644 (file)
@@ -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.hwmf.usermodel.HwmfPicture;
 import org.apache.poi.sl.draw.BitmapImageRenderer;
 import org.apache.poi.sl.draw.DrawPictureShape;
@@ -112,4 +112,9 @@ public class HwmfImageRenderer implements ImageRenderer {
             return true;
         }
     }
+
+    @Override
+    public GenericRecord getGenericRecord() {
+        return image;
+    }
 }
index 09bbb9f16e6c7443ce3b93e1f8b639c16bc322f8..cc103b2c06df58b0078ffc48fb921c61848d2a31 100644 (file)
@@ -19,20 +19,26 @@ package org.apache.poi.hwmf.record;
 
 import java.awt.image.BufferedImage;
 import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
-public class HwmfBitmap16 {
-    final boolean isPartial;
-    int type;
-    int width;
-    int height;
-    int widthBytes;
-    int planes;
-    int bitsPixel;
-    
+public class HwmfBitmap16 implements GenericRecord {
+    private final boolean isPartial;
+    private int type;
+    private int width;
+    private int height;
+    private int widthBytes;
+    private int planes;
+    private int bitsPixel;
+    private byte[] bitmap;
+
     public HwmfBitmap16() {
         this(false);
     }
@@ -74,7 +80,7 @@ public class HwmfBitmap16 {
         }
 
         int length = (((width * bitsPixel + 15) >> 4) << 1) * height;
-        /*byte buf[] =*/ IOUtils.toByteArray(leis, length);
+        bitmap = IOUtils.toByteArray(leis, length);
         
         // TODO: this is not implemented ... please provide a sample, if it
         // ever happens to you, to come here ...
@@ -85,4 +91,18 @@ public class HwmfBitmap16 {
     public BufferedImage getImage() {
         return new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+        m.put("isPartial", () -> isPartial);
+        m.put("type", () -> type);
+        m.put("width", () -> width);
+        m.put("height", () -> height);
+        m.put("widthBytes", () -> widthBytes);
+        m.put("planes", () -> planes);
+        m.put("bitsPixel", () -> bitsPixel);
+        m.put("bitmap", () -> bitmap);
+        return Collections.unmodifiableMap(m);
+    }
 }
index 2ef22a407aa7d0e562a4d5ae516768b260938f23..a21eb5a7ef2f6466915c0759bb6e42d274beb652 100644 (file)
@@ -29,10 +29,16 @@ import java.awt.image.IndexColorModel;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import javax.imageio.ImageIO;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hwmf.usermodel.HwmfPicture;
+import org.apache.poi.util.GenericRecordJsonWriter;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianConsts;
@@ -44,7 +50,7 @@ import org.apache.poi.util.RecordFormatException;
 /**
  * The DeviceIndependentBitmap Object defines an image in device-independent bitmap (DIB) format.
  */
-public class HwmfBitmapDib {
+public class HwmfBitmapDib implements GenericRecord {
 
     private static final POILogger logger = POILogFactory.getLogger(HwmfBitmapDib.class);
     private static final int BMP_HEADER_SIZE = 14;
@@ -435,8 +441,12 @@ public class HwmfBitmapDib {
     }
 
     public byte[] getBMPData() {
+        if (headerWidth <= 0 || headerHeight <= 0) {
+            return null;
+        }
+
         if (imageData == null) {
-            throw new RecordFormatException("bitmap not initialized ... need to call init() before");
+            throw new RecordFormatException("used to throw exception: bitmap not initialized ... need to call init() before");
         }
 
         // sometimes there are missing bytes after the imageData which will be 0-filled
@@ -488,23 +498,33 @@ public class HwmfBitmapDib {
 
     @Override
     public String toString() {
-        return
-            "{ headerSize: " + headerSize +
-            ", width: " + headerWidth +
-            ", height: " + headerHeight +
-            ", planes: " + headerPlanes +
-            ", bitCount: '" + headerBitCount + "'" +
-            ", compression: '" + headerCompression + "'" +
-            ", imageSize: " + headerImageSize +
-            ", xPelsPerMeter: " + headerXPelsPerMeter +
-            ", yPelsPerMeter: " + headerYPelsPerMeter +
-            ", colorUsed: " + headerColorUsed +
-            ", colorImportant: " + headerColorImportant +
-            ", imageSize: " + (imageData == null ? 0 : imageData.length) +
-            "}";
+        return GenericRecordJsonWriter.marshal(this);
+    }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+        m.put("headerSize", () -> headerSize);
+        m.put("width", () -> headerWidth);
+        m.put("height", () -> headerHeight);
+        m.put("planes", () -> headerPlanes);
+        m.put("bitCount", () -> headerBitCount);
+        m.put("compression", () -> headerCompression);
+        m.put("imageSize", () -> headerImageSize);
+        m.put("xPelsPerMeter", () -> headerXPelsPerMeter);
+        m.put("yPelsPerMeter", () -> headerYPelsPerMeter);
+        m.put("colorUsed", () -> headerColorUsed);
+        m.put("colorImportant", () -> headerColorImportant);
+        m.put("image", this::getImage);
+        m.put("bmpData", this::getBMPData);
+        return Collections.unmodifiableMap(m);
     }
 
     protected BufferedImage getPlaceholder() {
+        if (headerHeight <= 0 || headerWidth <= 0) {
+            return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
+        }
+
         BufferedImage bi = new BufferedImage(headerWidth, headerHeight, BufferedImage.TYPE_INT_ARGB);
         Graphics2D g = bi.createGraphics();
         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
index a757617752bce77882208c01e766393e2b60307b..cd78ccdc8aa625212c9203629ebf182dcf902b5f 100644 (file)
@@ -19,8 +19,12 @@ package org.apache.poi.hwmf.record;
 
 import java.awt.Color;
 import java.io.IOException;
-import java.util.Locale;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
@@ -31,7 +35,7 @@ import org.apache.poi.util.LittleEndianInputStream;
  * Blue (1 byte):  An 8-bit unsigned integer that defines the relative intensity of blue.
  * Reserved (1 byte):  An 8-bit unsigned integer that MUST be 0x00.
  */
-public class HwmfColorRef implements Cloneable {
+public class HwmfColorRef implements Cloneable, GenericRecord {
     private Color colorRef = Color.BLACK;
     
     public HwmfColorRef() {}
@@ -77,6 +81,11 @@ public class HwmfColorRef implements Cloneable {
 
     @Override
     public String toString() {
-        return String.format(Locale.ROOT, "%#08X", colorRef.getRGB()&0xFFFFFF);
+        return GenericRecordJsonWriter.marshal(this);
+    }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties("color", colorRef::getRGB);
     }
 }
index 6f8b3aa045be2305a688f1bd5161e8b7d1c5b182..38089ca7ab652675227c1864af2b4da41928b0e2 100644 (file)
 package org.apache.poi.hwmf.record;
 
 import java.awt.Shape;
-import java.awt.geom.AffineTransform;
 import java.awt.geom.Arc2D;
 import java.awt.geom.Area;
 import java.awt.geom.Dimension2D;
 import java.awt.geom.Ellipse2D;
 import java.awt.geom.Line2D;
 import java.awt.geom.Path2D;
-import java.awt.geom.PathIterator;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.geom.RoundRectangle2D;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hwmf.draw.HwmfGraphics;
 import org.apache.poi.hwmf.draw.HwmfGraphics.FillDrawStyle;
+import org.apache.poi.util.Dimension2DDouble;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
-public class HwmfDraw {
+@SuppressWarnings("WeakerAccess")
+public final class HwmfDraw {
+
+    private HwmfDraw() {}
+
     /**
      * The META_MOVETO record sets the output position in the playback device context to a specified
      * point.
@@ -65,7 +72,16 @@ public class HwmfDraw {
 
         @Override
         public String toString() {
-            return pointToString(point);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Point2D getPoint() {
+            return point;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("point", this::getPoint);
         }
     }
 
@@ -97,7 +113,16 @@ public class HwmfDraw {
 
         @Override
         public String toString() {
-            return pointToString(point);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Point2D getPoint() {
+            return point;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("point", this::getPoint);
         }
     }
 
@@ -117,9 +142,7 @@ public class HwmfDraw {
 
         @Override
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
-            /**
-             * A 16-bit signed integer that defines the number of points in the array.
-             */
+            //A 16-bit signed integer that defines the number of points in the array.
             int numberofPoints = leis.readShort();
 
             poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, numberofPoints);
@@ -143,23 +166,12 @@ public class HwmfDraw {
             Path2D p = (Path2D)poly.clone();
             // don't close the path
             p.setWindingRule(ctx.getProperties().getWindingRule());
-            switch (getFillDrawStyle()) {
-                case FILL:
-                    ctx.fill(p);
-                    break;
-                case DRAW:
-                    ctx.draw(p);
-                    break;
-                case FILL_DRAW:
-                    ctx.fill(p);
-                    ctx.draw(p);
-                    break;
-            }
+            getFillDrawStyle().handler.accept(ctx, p);
         }
 
         @Override
         public String toString() {
-            return "{ poly: "+polyToString(poly)+" }";
+            return GenericRecordJsonWriter.marshal(this);
         }
 
         /**
@@ -168,6 +180,15 @@ public class HwmfDraw {
         protected FillDrawStyle getFillDrawStyle() {
             return FillDrawStyle.FILL;
         }
+
+        public Path2D getPoly() {
+            return poly;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("poly", this::getPoly);
+        }
     }
 
     /**
@@ -216,7 +237,16 @@ public class HwmfDraw {
 
         @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);
         }
     }
 
@@ -235,16 +265,11 @@ public class HwmfDraw {
          * Brush to use for filling the region.
          */
         protected int brushIndex;
+
         /**
-         * A 16-bit signed integer that defines the height, in logical units, of the
-         * region frame.
-         */
-        protected int height;
-        /**
-         * A 16-bit signed integer that defines the width, in logical units, of the
-         * region frame.
+         * The region frame, in logical units
          */
-        protected int width;
+        protected final Dimension2D frame = new Dimension2DDouble();
 
         @Override
         public HwmfRecordType getWmfRecordType() {
@@ -255,8 +280,10 @@ public class HwmfDraw {
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
             regionIndex = leis.readUShort();
             brushIndex = leis.readUShort();
-            height = leis.readShort();
-            width = leis.readShort();
+            // A 16-bit signed integer that defines the height/width, in logical units, of the region frame.
+            int height = leis.readShort();
+            int width = leis.readShort();
+            frame.setSize(width, height);
             return 4*LittleEndianConsts.SHORT_SIZE;
         }
 
@@ -265,15 +292,35 @@ public class HwmfDraw {
             ctx.applyObjectTableEntry(brushIndex);
             ctx.applyObjectTableEntry(regionIndex);
             Rectangle2D inner = ctx.getProperties().getRegion().getBounds();
-            double x = inner.getX()-width;
-            double y = inner.getY()-height;
-            double w = inner.getWidth()+2.0*width;
-            double h = inner.getHeight()+2.0*height;
+            double x = inner.getX()-frame.getWidth();
+            double y = inner.getY()-frame.getHeight();
+            double w = inner.getWidth()+2.0*frame.getWidth();
+            double h = inner.getHeight()+2.0*frame.getHeight();
             Rectangle2D outer = new Rectangle2D.Double(x,y,w,h);
             Area frame = new Area(outer);
             frame.subtract(new Area(inner));
             ctx.fill(frame);
         }
+
+        public int getRegionIndex() {
+            return regionIndex;
+        }
+
+        public int getBrushIndex() {
+            return brushIndex;
+        }
+
+        public Dimension2D getFrame() {
+            return frame;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "regionIndex", this::getRegionIndex,
+                "brushIndex", this::getBrushIndex,
+                "frame", this::getFrame);
+        }
     }
 
     /**
@@ -292,15 +339,11 @@ public class HwmfDraw {
 
         @Override
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
-            // see http://secunia.com/gfx/pdf/SA31675_BA.pdf ;)
-            /**
-             * A 16-bit unsigned integer that defines the number of polygons in the object.
-             */
+            // see also CVE-2008-3014 - https://dl.packetstormsecurity.net/papers/attack/CVE-2008-3014.pdf ;)
+            // A 16-bit unsigned integer that defines the number of polygons in the object.
             int numberOfPolygons = leis.readUShort();
-            /**
-             * A NumberOfPolygons array of 16-bit unsigned integers that define the number of
-             * points for each polygon in the object.
-             */
+            // A NumberOfPolygons array of 16-bit unsigned integers that define the number of points for
+            // each polygon in the object.
             int[] pointsPerPolygon = new int[numberOfPolygons];
 
             int size = LittleEndianConsts.SHORT_SIZE;
@@ -311,10 +354,8 @@ public class HwmfDraw {
             }
 
             for (int nPoints : pointsPerPolygon) {
-                /**
-                 * An array of 16-bit signed integers that define the coordinates of the polygons.
-                 * (Note: MS-WMF wrongly says unsigned integers ...)
-                 */
+                // An array of 16-bit signed integers that define the coordinates of the polygons.
+                // (Note: MS-WMF wrongly says unsigned integers ...)
                 Path2D poly = new Path2D.Double(Path2D.WIND_EVEN_ODD, nPoints);
                 for (int i=0; i<nPoints; i++) {
                     int x = leis.readShort();
@@ -396,20 +437,16 @@ public class HwmfDraw {
 
         @Override
         public String toString() {
-            final StringBuilder sb = new StringBuilder();
-            sb.append("{ polyList: [");
-            boolean isFirst = true;
-            for (Path2D p : polyList) {
-                if (!isFirst) {
-                    sb.append(",");
-                }
-                isFirst = false;
-                sb.append("{ points: ");
-                sb.append(polyToString(p));
-                sb.append(" }");
-            }
-            sb.append(" }");
-            return sb.toString();
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public List<Path2D> getPolyList() {
+            return polyList;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("polyList", this::getPolyList);
         }
     }
 
@@ -437,7 +474,16 @@ public class HwmfDraw {
 
         @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);
         }
     }
 
@@ -469,6 +515,22 @@ public class HwmfDraw {
             Shape s = new Rectangle2D.Double(point.getX(), point.getY(), 1, 1);
             ctx.fill(s);
         }
+
+        public HwmfColorRef getColorRef() {
+            return colorRef;
+        }
+
+        public Point2D getPoint() {
+            return point;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "colorRef", this::getColorRef,
+                "point", this::getPoint
+            );
+        }
     }
 
     /**
@@ -476,17 +538,7 @@ public class HwmfDraw {
      * using the pen and filled using the brush, as defined in the playback device context.
      */
     public static class WmfRoundRect implements HwmfRecord {
-        /**
-         * A 16-bit signed integer that defines the height, in logical coordinates, of the
-         * ellipse used to draw the rounded corners.
-         */
-        protected int height;
-
-        /**
-         * A 16-bit signed integer that defines the width, in logical coordinates, of the
-         * ellipse used to draw the rounded corners.
-         */
-        protected int width;
+        protected final Dimension2D corners = new Dimension2DDouble();
 
         protected final Rectangle2D bounds = new Rectangle2D.Double();
 
@@ -498,8 +550,11 @@ public class HwmfDraw {
 
         @Override
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
-            height = leis.readShort();
-            width = leis.readShort();
+            // A 16-bit signed integer that defines the height/width, in logical coordinates,
+            // of the ellipse used to draw the rounded corners.
+            int height = leis.readShort();
+            int width = leis.readShort();
+            corners.setSize(width, height);
             return 2*LittleEndianConsts.SHORT_SIZE+readBounds(leis, bounds);
         }
 
@@ -509,8 +564,28 @@ public class HwmfDraw {
         }
 
         protected RoundRectangle2D getShape() {
-            return new RoundRectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), width, height);
+            return new RoundRectangle2D.Double(
+                bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(),
+                corners.getWidth(), corners.getHeight()
+            );
+        }
+
+        public Dimension2D getCorners() {
+            return corners;
         }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "bounds", this::getBounds,
+                "corners", this::getCorners
+            );
+        }
+
     }
 
 
@@ -518,6 +593,22 @@ public class HwmfDraw {
      * The META_ARC record draws an elliptical arc.
      */
     public static class WmfArc implements HwmfRecord {
+        public enum WmfArcClosure {
+            ARC(HwmfRecordType.arc, Arc2D.OPEN, FillDrawStyle.DRAW),
+            CHORD(HwmfRecordType.chord, Arc2D.CHORD, FillDrawStyle.FILL_DRAW),
+            PIE(HwmfRecordType.pie, Arc2D.PIE, FillDrawStyle.FILL_DRAW);
+
+            public final HwmfRecordType recordType;
+            public final int awtType;
+            public final FillDrawStyle drawStyle;
+
+            WmfArcClosure(HwmfRecordType recordType, int awtType, FillDrawStyle drawStyle) {
+                this.recordType = recordType;
+                this.awtType = awtType;
+                this.drawStyle = drawStyle;
+            }
+        }
+
         /** starting point of the arc */
         protected final Point2D startPoint = new Point2D.Double();
 
@@ -544,32 +635,25 @@ public class HwmfDraw {
 
         @Override
         public void draw(HwmfGraphics ctx) {
-            Shape s = getShape();
-            switch (getFillDrawStyle()) {
-                case FILL:
-                    ctx.fill(s);
-                    break;
-                case DRAW:
-                    ctx.draw(s);
-                    break;
-                case FILL_DRAW:
-                    ctx.fill(s);
-                    ctx.draw(s);
-                    break;
-            }
+            getFillDrawStyle().handler.accept(ctx, getShape());
         }
 
-        protected FillDrawStyle getFillDrawStyle() {
+        public WmfArcClosure getArcClosure() {
             switch (getWmfRecordType()) {
                 default:
                 case arc:
-                    return FillDrawStyle.DRAW;
+                    return WmfArcClosure.ARC;
                 case chord:
+                    return WmfArcClosure.CHORD;
                 case pie:
-                    return FillDrawStyle.FILL_DRAW;
+                    return WmfArcClosure.PIE;
             }
         }
 
+        protected FillDrawStyle getFillDrawStyle() {
+            return getArcClosure().drawStyle;
+        }
+
         protected Arc2D getShape() {
             double startAngle = Math.toDegrees(Math.atan2(-(startPoint.getY() - bounds.getCenterY()), startPoint.getX() - bounds.getCenterX()));
             double endAngle =   Math.toDegrees(Math.atan2(-(endPoint.getY() - bounds.getCenterY()), endPoint.getX() - bounds.getCenterX()));
@@ -578,33 +662,37 @@ public class HwmfDraw {
                 startAngle += 360;
             }
 
-            int arcClosure;
-            switch (getWmfRecordType()) {
-                default:
-                case arc:
-                    arcClosure = Arc2D.OPEN;
-                    break;
-                case chord:
-                    arcClosure = Arc2D.CHORD;
-                    break;
-                case pie:
-                    arcClosure = Arc2D.PIE;
-                    break;
-            }
-
-            return new Arc2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), startAngle, arcAngle, arcClosure);
+            return new Arc2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(),
+                startAngle, arcAngle, getArcClosure().awtType);
         }
 
         @Override
         public String toString() {
-            Arc2D arc = getShape();
-            return
-                "{ startPoint: "+pointToString(startPoint)+
-                ", endPoint: "+pointToString(endPoint)+
-                ", startAngle: "+arc.getAngleStart()+
-                ", extentAngle: "+arc.getAngleExtent()+
-                ", bounds: "+boundsToString(bounds)+
-                " }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Point2D getStartPoint() {
+            return startPoint;
+        }
+
+        public Point2D getEndPoint() {
+            return endPoint;
+        }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            final Arc2D arc = getShape();
+            return GenericRecordUtil.getGenericProperties(
+                "startPoint", this::getStartPoint,
+                "endPoint", this::getEndPoint,
+                "startAngle", arc::getAngleStart,
+                "extentAngle", arc::getAngleExtent,
+                "bounds", this::getBounds
+            );
         }
     }
 
@@ -668,14 +756,22 @@ public class HwmfDraw {
 
         @Override
         public String toString() {
-            return "{ index: "+objectIndex +" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public int getObjectIndex() {
+            return objectIndex;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("objectIndex", this::getObjectIndex);
         }
     }
 
+    @SuppressWarnings("DuplicatedCode")
     static int readBounds(LittleEndianInputStream leis, Rectangle2D bounds) {
-        /**
-         * The 16-bit signed integers that defines the corners of the bounding rectangle.
-         */
+        // The 16-bit signed integers that defines the corners of the bounding rectangle.
         int bottom = leis.readShort();
         int right = leis.readShort();
         int top = leis.readShort();
@@ -691,10 +787,9 @@ public class HwmfDraw {
         return 4 * LittleEndianConsts.SHORT_SIZE;
     }
 
+    @SuppressWarnings("DuplicatedCode")
     static int readRectS(LittleEndianInputStream leis, Rectangle2D bounds) {
-        /**
-         * The 16-bit signed integers that defines the corners of the bounding rectangle.
-         */
+        // The 16-bit signed integers that defines the corners of the bounding rectangle.
         int left = leis.readShort();
         int top = leis.readShort();
         int right = leis.readShort();
@@ -711,58 +806,13 @@ public class HwmfDraw {
     }
 
     static int readPointS(LittleEndianInputStream leis, Point2D point) {
-        /** a signed integer that defines the x/y-coordinate, in logical units. */
+        // a signed integer that defines the x/y-coordinate, in logical units.
         int y = leis.readShort();
         int x = leis.readShort();
         point.setLocation(x, y);
         return 2*LittleEndianConsts.SHORT_SIZE;
     }
 
-    static String polyToString(Path2D poly) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("[");
-        final PathIterator iter = poly.getPathIterator(null);
-        double[] pnts = new double[6];
-        while (!iter.isDone()) {
-            int segType = iter.currentSegment(pnts);
-            switch (segType) {
-                case PathIterator.SEG_MOVETO:
-                    sb.append("{ type: 'move', x: "+pnts[0]+", y: "+pnts[1]+" }, ");
-                    break;
-                case PathIterator.SEG_LINETO:
-                    sb.append("{ type: 'lineto', x: "+pnts[0]+", y: "+pnts[1]+" }, ");
-                    break;
-                case PathIterator.SEG_QUADTO:
-                    sb.append("{ type: 'quad', x1: "+pnts[0]+", y1: "+pnts[1]+", x2: "+pnts[2]+", y2: "+pnts[3]+" }, ");
-                    break;
-                case PathIterator.SEG_CUBICTO:
-                    sb.append("{ type: 'cubic', x1: "+pnts[0]+", y1: "+pnts[1]+", x2: "+pnts[2]+", y2: "+pnts[3]+", x3: "+pnts[4]+", y3: "+pnts[5]+" }, ");
-                    break;
-                case PathIterator.SEG_CLOSE:
-                    sb.append("{ type: 'close' }, ");
-                    break;
-            }
-            iter.next();
-        }
-        sb.append("]");
-        return sb.toString();
-    }
-
-    @Internal
-    public static String pointToString(Point2D point) {
-        return (point == null) ? "null" : "{ x: "+point.getX()+", y: "+point.getY()+" }";
-    }
-
-    @Internal
-    public static String boundsToString(Rectangle2D bounds) {
-        return (bounds == null) ? "null" : "{ x: "+bounds.getX()+", y: "+bounds.getY()+", w: "+bounds.getWidth()+", h: "+bounds.getHeight()+" }";
-    }
-
-    @Internal
-    public static String dimToString(Dimension2D dim) {
-        return (dim == null) ? "null" : "{ w: "+dim.getWidth()+", h: "+dim.getHeight()+" }";
-    }
-
     @Internal
     public static Rectangle2D normalizeBounds(Rectangle2D bounds) {
         return (bounds.getWidth() >= 0 && bounds.getHeight() >= 0) ? bounds
index a2a7082a3532b61e054894b1f5444187c4eeed53..788ad62534aedc3e297ca4dc51b15d1abbbadc40 100644 (file)
 package org.apache.poi.hwmf.record;
 
 import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hwmf.draw.HwmfGraphics;
-import org.apache.poi.util.HexDump;
+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.LittleEndianCP950Reader;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
@@ -32,6 +35,7 @@ import org.apache.poi.util.LittleEndianInputStream;
  * The MetafileEscapes specifies printer driver functionality that
  * might not be directly accessible through WMF records
  */
+@SuppressWarnings("WeakerAccess")
 public class HwmfEscape implements HwmfRecord {
     private static final int MAX_OBJECT_SIZE = 0xFFFF;
 
@@ -221,13 +225,18 @@ public class HwmfEscape implements HwmfRecord {
     }
     
     public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("escape - function: "+escapeFunction+"\n");
-        sb.append(escapeData.toString());
-        return sb.toString();
+        return GenericRecordJsonWriter.marshal(this);
     }
 
-    public static class WmfEscapeUnknownData implements HwmfEscapeData {
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "escapeFunction", this::getEscapeFunction,
+            "escapeData", this::getEscapeData
+        );
+    }
+
+    public static class WmfEscapeUnknownData implements HwmfEscapeData, GenericRecord {
         EscapeFunction escapeFunction;
         private byte[] escapeDataBytes;
 
@@ -239,16 +248,21 @@ public class HwmfEscape implements HwmfRecord {
         public int init(LittleEndianInputStream leis, long recordSize, EscapeFunction escapeFunction) throws IOException {
             this.escapeFunction = escapeFunction;
             escapeDataBytes = IOUtils.toByteArray(leis,recordSize,MAX_OBJECT_SIZE);
-            return 0;
+            return (int)recordSize;
         }
 
         @Override
         public String toString() {
-            return HexDump.dump(escapeDataBytes, 0, 0);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("escapeDataBytes", this::getEscapeDataBytes);
         }
     }
 
-    public static class WmfEscapeEMF implements HwmfEscapeData {
+    public static class WmfEscapeEMF implements HwmfEscapeData, GenericRecord {
         // The magic for EMF parts, i.e. the byte sequence for "WMFC"
         private static final int EMF_COMMENT_IDENTIFIER = 0x43464D57;
 
@@ -276,7 +290,9 @@ public class HwmfEscape implements HwmfRecord {
             if (commentIdentifier != EMF_COMMENT_IDENTIFIER) {
                 // there are some WMF implementation using this record as a MFCOMMENT or similar
                 // if the commentIdentifier doesn't match, then return immediately
-                return LittleEndianConsts.INT_SIZE;
+                emfData = IOUtils.toByteArray(leis, recordSize-LittleEndianConsts.INT_SIZE, MAX_OBJECT_SIZE);
+                remainingBytes = emfData.length;
+                return (int)recordSize;
             }
 
             // A 32-bit unsigned integer that identifies the type of comment in this record.
@@ -344,5 +360,21 @@ public class HwmfEscape implements HwmfRecord {
         public byte[] getEmfData() {
             return emfData;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+            m.put("commentIdentifier", () -> commentIdentifier);
+            m.put("commentType", () -> commentType);
+            m.put("version", () -> version);
+            m.put("checksum", () -> checksum);
+            m.put("flags", () -> flags);
+            m.put("commentRecordCount", this::getCommentRecordCount);
+            m.put("currentRecordSize", this::getCurrentRecordSize);
+            m.put("remainingBytes", this::getRemainingBytes);
+            m.put("emfRecordSize", this::getEmfRecordSize);
+            m.put("emfData", this::getEmfData);
+            return Collections.unmodifiableMap(m);
+        }
     }
 }
index 20c48b8ee14a09a43fa9864a21ce76106566cf14..5e50e2e48d0b33b678c27752eb88d531c14da8bf 100644 (file)
@@ -17,7 +17,6 @@
 
 package org.apache.poi.hwmf.record;
 
-import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
 import static org.apache.poi.hwmf.record.HwmfDraw.readPointS;
 
 import java.awt.Color;
@@ -27,17 +26,20 @@ import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hwmf.draw.HwmfDrawProperties;
 import org.apache.poi.hwmf.draw.HwmfGraphics;
 import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
+@SuppressWarnings("WeakerAccess")
 public class HwmfFill {
-    /**
-     * A record which contains an image (to be extracted)
-     */
+    /** A record which contains an image (to be extracted) */
     public interface HwmfImageRecord {
 
         default BufferedImage getImage() {
@@ -70,9 +72,7 @@ public class HwmfFill {
      * i.e. if contains explicit RGB values or indexes into a palette.
      */
     public enum ColorUsage {
-        /**
-         * The color table contains RGB values
-         */
+        /** The color table contains RGB values */
         DIB_RGB_COLORS(0x0000),
         /**
          * The color table contains 16-bit indices into the current logical palette in
@@ -139,7 +139,22 @@ public class HwmfFill {
             if (region != null) {
                 ctx.fill(region);
             }
-            
+        }
+
+        public int getRegionIndex() {
+            return regionIndex;
+        }
+
+        public int getBrushIndex() {
+            return brushIndex;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "regionIndex", this::getRegionIndex,
+                "brushIndex", this::getBrushIndex
+            );
         }
     }
 
@@ -173,6 +188,15 @@ public class HwmfFill {
                 ctx.fill(region);
             }        
         }
+
+        public int getRegionIndex() {
+            return regionIndex;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("regionIndex", this::getRegionIndex);
+        }
     }
     
     
@@ -182,9 +206,7 @@ public class HwmfFill {
      */
     public static class WmfFloodFill implements HwmfRecord {
         
-        /**
-         * A 32-bit ColorRef Object that defines the color value.
-         */
+        /** A 32-bit ColorRef Object that defines the color value. */
         protected final HwmfColorRef colorRef = new HwmfColorRef();
 
         /** the point where filling is to start. */
@@ -206,6 +228,22 @@ public class HwmfFill {
         public void draw(HwmfGraphics ctx) {
             
         }
+
+        public HwmfColorRef getColorRef() {
+            return colorRef;
+        }
+
+        public Point2D getStart() {
+            return start;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "colorRef", this::getColorRef,
+                "start", this::getStart
+            );
+        }
     }
 
     /**
@@ -267,7 +305,16 @@ public class HwmfFill {
 
         @Override
         public String toString() {
-            return "{ polyFillMode: '"+ polyFillMode +"' }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfPolyfillMode getPolyFillMode() {
+            return polyFillMode;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("polyFillMode", this::getPolyFillMode);
         }
     }
 
@@ -277,21 +324,22 @@ public class HwmfFill {
      * the playback device context.
      */
     public static class WmfExtFloodFill extends WmfFloodFill {
-        
-        /**
-         * A 16-bit unsigned integer that defines the fill operation to be performed. This
-         * member MUST be one of the values in the FloodFill Enumeration table:
-         * 
-         * FLOODFILLBORDER = 0x0000:
-         * The fill area is bounded by the color specified by the Color member.
-         * This style is identical to the filling performed by the META_FLOODFILL record.
-         * 
-         * FLOODFILLSURFACE = 0x0001:
-         * The fill area is bounded by the color that is specified by the Color member.
-         * Filling continues outward in all directions as long as the color is encountered.
-         * This style is useful for filling areas with multicolored boundaries.
-         */
-        protected int mode;
+
+        public enum HwmfFloodFillMode {
+            /**
+             * The fill area is bounded by the color specified by the Color member.
+             * This style is identical to the filling performed by the META_FLOODFILL record.
+             */
+            FLOOD_FILL_BORDER,
+            /**
+             * The fill area is bounded by the color that is specified by the Color member.
+             * Filling continues outward in all directions as long as the color is encountered.
+             * This style is useful for filling areas with multicolored boundaries.
+             */
+            FLOOD_FILL_SURFACE
+        }
+
+        protected HwmfFloodFillMode mode;
         
         @Override
         public HwmfRecordType getWmfRecordType() {
@@ -300,7 +348,9 @@ public class HwmfFill {
         
         @Override
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
-            mode = leis.readUShort();
+            // A 16-bit unsigned integer that defines the fill operation to be performed. This
+            // member MUST be one of the values in the FloodFill Enumeration table:
+            mode = HwmfFloodFillMode.values()[leis.readUShort()];
             return super.init(leis, recordSize, recordFunction)+LittleEndianConsts.SHORT_SIZE;
         }
 
@@ -308,6 +358,15 @@ public class HwmfFill {
         public void draw(HwmfGraphics ctx) {
             
         }
+
+        public HwmfFloodFillMode getMode() {
+            return mode;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("mode", this::getMode);
+        }
     }
 
     /**
@@ -319,7 +378,7 @@ public class HwmfFill {
          * A 16-bit unsigned integer used to index into the WMF Object Table to get
          * the region to be inverted.
          */
-        private int region;
+        private int regionIndex;
         
         @Override
         public HwmfRecordType getWmfRecordType() {
@@ -328,7 +387,7 @@ public class HwmfFill {
         
         @Override
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
-            region = leis.readUShort();
+            regionIndex = leis.readUShort();
             return LittleEndianConsts.SHORT_SIZE;
         }
 
@@ -336,6 +395,15 @@ public class HwmfFill {
         public void draw(HwmfGraphics ctx) {
             
         }
+
+        public int getRegionIndex() {
+            return regionIndex;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("regionIndex", this::getRegionIndex);
+        }
     }
     
 
@@ -361,12 +429,7 @@ public class HwmfFill {
         
         @Override
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
-            int rasterOpCode = leis.readUShort();
-            int rasterOpIndex = leis.readUShort();
-
-            rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
-            assert(rasterOpCode == rasterOperation.opCode);
-            
+            rasterOperation = readRasterOperation(leis);
             return readBounds2(leis, bounds)+2*LittleEndianConsts.SHORT_SIZE;
         }
 
@@ -374,6 +437,21 @@ public class HwmfFill {
         public void draw(HwmfGraphics ctx) {
             
         }
+
+        public HwmfTernaryRasterOp getRasterOperation() {
+            return rasterOperation;
+        }
+
+        public Rectangle2D getBounds() {
+            return bounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "rasterOperation", this::getRasterOperation,
+                "bounds", this::getBounds);
+        }
     }
 
     /**
@@ -415,13 +493,9 @@ public class HwmfFill {
         
         @Override
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
-            boolean hasBitmap = (recordSize > ((recordFunction >> 8) + 3));
+            final boolean hasBitmap = hasBitmap(recordSize, recordFunction);
 
-            int rasterOpCode = leis.readUShort();
-            int rasterOpIndex = leis.readUShort();
-            
-            rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
-            assert(rasterOpCode == rasterOperation.opCode);
+            rasterOperation = readRasterOperation(leis);
 
             int size = 2*LittleEndianConsts.SHORT_SIZE;
 
@@ -449,11 +523,33 @@ public class HwmfFill {
 
         @Override
         public String toString() {
-            return
-                "{ rasterOperation: '"+rasterOperation+"'"+
-                ", srcBounds: "+boundsToString(srcBounds)+
-                ", dstBounds: "+boundsToString(dstBounds)+
-                "}";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfTernaryRasterOp getRasterOperation() {
+            return rasterOperation;
+        }
+
+        public Rectangle2D getSrcBounds() {
+            return srcBounds;
+        }
+
+        public Rectangle2D getDstBounds() {
+            return dstBounds;
+        }
+
+        public HwmfBitmap16 getTarget() {
+            return target;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "rasterOperation", this::getRasterOperation,
+                "srcBounds", this::getSrcBounds,
+                "dstBounds", this::getDstBounds,
+                "target", this::getTarget
+            );
         }
     }
 
@@ -498,12 +594,7 @@ public class HwmfFill {
         
         @Override
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
-            int rasterOpCode = leis.readUShort();
-            int rasterOpIndex = leis.readUShort();
-            
-            rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
-            assert(rasterOpCode == rasterOperation.opCode);
-
+            rasterOperation = readRasterOperation(leis);
             colorUsage = ColorUsage.valueOf(leis.readUShort());
 
             int size = 3*LittleEndianConsts.SHORT_SIZE;
@@ -546,12 +637,33 @@ public class HwmfFill {
 
         @Override
         public String toString() {
-            return
-                "{ rasterOperation: '"+rasterOperation+"'"+
-                ", colorUsage: '"+colorUsage+"'"+
-                ", srcBounds: "+boundsToString(srcBounds)+
-                ", dstBounds: "+boundsToString(dstBounds)+
-                "}";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfTernaryRasterOp getRasterOperation() {
+            return rasterOperation;
+        }
+
+        public ColorUsage getColorUsage() {
+            return colorUsage;
+        }
+
+        public Rectangle2D getSrcBounds() {
+            return srcBounds;
+        }
+
+        public Rectangle2D getDstBounds() {
+            return dstBounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "rasterOperation", this::getRasterOperation,
+                "colorUsage", this::getColorUsage,
+                "srcBounds", this::getSrcBounds,
+                "dstBounds", this::getDstBounds
+            );
         }
     }
     
@@ -564,13 +676,9 @@ public class HwmfFill {
         
         @Override
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
-            boolean hasBitmap = (recordSize/2 != ((recordFunction >> 8) + 3));
+            final boolean hasBitmap = hasBitmap(recordSize/2, recordFunction);
 
-            int rasterOpCode = leis.readUShort();
-            int rasterOpIndex = leis.readUShort();
-            
-            rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
-            assert(rasterOpCode == rasterOperation.opCode);
+            rasterOperation = readRasterOperation(leis);
 
             int size = 2*LittleEndianConsts.SHORT_SIZE;
 
@@ -674,6 +782,38 @@ public class HwmfFill {
         public byte[] getBMPData() {
             return dib.getBMPData();
         }
+
+        public ColorUsage getColorUsage() {
+            return colorUsage;
+        }
+
+        public int getScanCount() {
+            return scanCount;
+        }
+
+        public int getStartScan() {
+            return startScan;
+        }
+
+        public Rectangle2D getSrcBounds() {
+            return srcBounds;
+        }
+
+        public Rectangle2D getDstBounds() {
+            return dstBounds;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "colorUsage", this::getColorUsage,
+                "scanCount", this::getScanCount,
+                "startScan", this::getStartScan,
+                "srcBounds", this::getSrcBounds,
+                "dstBounds", this::getDstBounds,
+                "dib", () -> dib
+            );
+        }
     }
 
 
@@ -685,13 +825,9 @@ public class HwmfFill {
         
         @Override
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
-            boolean hasBitmap = (recordSize/2 != ((recordFunction >> 8) + 3));
+            final boolean hasBitmap = hasBitmap(recordSize/2, recordFunction);
 
-            int rasterOpCode = leis.readUShort();
-            int rasterOpIndex = leis.readUShort();
-            
-            rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
-            assert(rasterOpCode == rasterOperation.opCode);
+            rasterOperation = readRasterOperation(leis);
 
             int size = 2*LittleEndianConsts.SHORT_SIZE;
 
@@ -752,13 +888,9 @@ public class HwmfFill {
         
         @Override
         public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
-            boolean hasBitmap = (recordSize > ((recordFunction >> 8) + 3));
+            final boolean hasBitmap = hasBitmap(recordSize, recordFunction);
 
-            int rasterOpCode = leis.readUShort();
-            int rasterOpIndex = leis.readUShort();
-            
-            rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
-            assert(rasterOperation != null && rasterOpCode == rasterOperation.opCode);
+            rasterOperation = readRasterOperation(leis);
 
             int size = 2*LittleEndianConsts.SHORT_SIZE;
 
@@ -802,12 +934,36 @@ public class HwmfFill {
         public byte[] getBMPData() {
             return (target != null && target.isValid()) ? target.getBMPData() : null;
         }
+
+        public HwmfTernaryRasterOp getRasterOperation() {
+            return rasterOperation;
+        }
+
+        public Rectangle2D getSrcBounds() {
+            return srcBounds;
+        }
+
+        public Rectangle2D getDstBounds() {
+            return dstBounds;
+        }
+
+        public HwmfBitmapDib getTarget() {
+            return target;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "rasterOperation", this::getRasterOperation,
+                "srcBounds", this::getSrcBounds,
+                "dstBounds", this::getDstBounds,
+                "target", this::getTarget
+            );
+        }
     }
 
     static int readBounds2(LittleEndianInputStream leis, Rectangle2D bounds) {
-        /**
-         * The 16-bit signed integers that defines the corners of the bounding rectangle.
-         */
+        // The 16-bit signed integers that defines the corners of the bounding rectangle.
         int h = leis.readShort();
         int w = leis.readShort();
         int y = leis.readShort();
@@ -818,4 +974,16 @@ public class HwmfFill {
         return 4 * LittleEndianConsts.SHORT_SIZE;
     }
 
+    private static boolean hasBitmap(long recordSize, int recordFunction) {
+        return (recordSize > ((recordFunction >> 8) + 3));
+    }
+
+    private static HwmfTernaryRasterOp readRasterOperation(LittleEndianInputStream leis) {
+        int rasterOpCode = leis.readUShort();
+        int rasterOpIndex = leis.readUShort();
+
+        HwmfTernaryRasterOp rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
+        assert(rasterOperation != null && rasterOpCode == rasterOperation.opCode);
+        return rasterOperation;
+    }
 }
index 211c9fc263785e1aad4ced897d941bd76f294348..14432432dbed24d9176c9adf2985106282f76af2 100644 (file)
@@ -19,7 +19,12 @@ package org.apache.poi.hwmf.record;
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.common.usermodel.fonts.FontCharset;
 import org.apache.poi.common.usermodel.fonts.FontFamily;
 import org.apache.poi.common.usermodel.fonts.FontHeader;
@@ -27,6 +32,8 @@ import org.apache.poi.common.usermodel.fonts.FontInfo;
 import org.apache.poi.common.usermodel.fonts.FontPitch;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
@@ -34,7 +41,7 @@ import org.apache.poi.util.LittleEndianInputStream;
  * The Font object specifies the attributes of a logical font
  */
 @SuppressWarnings({"unused", "Duplicates"})
-public class HwmfFont implements FontInfo {
+public class HwmfFont implements FontInfo, GenericRecord {
 
     /**
      * The output precision defines how closely the output must match the requested font's height,
@@ -108,17 +115,17 @@ public class HwmfFont implements FontInfo {
      * ClipPrecision Flags specify clipping precision, which defines how to clip characters that are
      * partially outside a clipping region. These flags can be combined to specify multiple options.
      */
-    public static class WmfClipPrecision {
+    public static class WmfClipPrecision implements GenericRecord {
 
         /** Specifies that default clipping MUST be used. */
-        private static final BitField CLIP_DEFAULT_PRECIS = BitFieldFactory.getInstance(0x0000);
+        private static final BitField DEFAULT_PRECIS = BitFieldFactory.getInstance(0x0003);
 
 
         /** This value SHOULD NOT be used. */
-        private static final BitField CLIP_CHARACTER_PRECIS = BitFieldFactory.getInstance(0x0001);
+        private static final BitField CHARACTER_PRECIS = BitFieldFactory.getInstance(0x0001);
 
         /** This value MAY be returned when enumerating rasterized, TrueType and vector fonts. */
-        private static final BitField CLIP_STROKE_PRECIS = BitFieldFactory.getInstance(0x0002);
+        private static final BitField STROKE_PRECIS = BitFieldFactory.getInstance(0x0002);
 
         /**
          * This value is used to control font rotation, as follows:
@@ -128,39 +135,79 @@ public class HwmfFont implements FontInfo {
          * If clear, device fonts SHOULD rotate counterclockwise, but the rotation of other fonts
          * SHOULD be determined by the orientation of the coordinate system.
          */
-        private static final BitField CLIP_LH_ANGLES = BitFieldFactory.getInstance(0x0010);
+        private static final BitField LH_ANGLES = BitFieldFactory.getInstance(0x0010);
 
         /** This value SHOULD NOT be used. */
-        private static final BitField CLIP_TT_ALWAYS = BitFieldFactory.getInstance(0x0020);
+        private static final BitField TT_ALWAYS = BitFieldFactory.getInstance(0x0020);
 
-        /** This value specifies that font association SHOULD< be turned off. */
-        private static final BitField CLIP_DFA_DISABLE = BitFieldFactory.getInstance(0x0040);
+        /** This value specifies that font association SHOULD be turned off. */
+        private static final BitField DFA_DISABLE = BitFieldFactory.getInstance(0x0040);
 
         /**
          * This value specifies that font embedding MUST be used to render document content;
          * embedded fonts are read-only.
          */
-        private static final BitField CLIP_EMBEDDED = BitFieldFactory.getInstance(0x0080);
+        private static final BitField EMBEDDED = BitFieldFactory.getInstance(0x0080);
 
-        int flag;
+        private static final int[] FLAG_MASKS = {
+            0x0001, 0x0002, 0x0010, 0x0020, 0x0040, 0x0080
+        };
+
+        private static final String[] FLAG_NAMES = {
+            "CHARACTER_PRECIS",
+            "STROKE_PRECIS",
+            "LH_ANGLES",
+            "TT_ALWAYS",
+            "DFA_DISABLE",
+            "EMBEDDED"
+        };
+
+        private int flag;
 
         public int init(LittleEndianInputStream leis) {
             flag = leis.readUByte();
             return LittleEndianConsts.BYTE_SIZE;
         }
 
+        public boolean isDefaultPrecision() {
+            return !DEFAULT_PRECIS.isSet(flag);
+        }
+
+        public boolean isCharacterPrecision() {
+            return CHARACTER_PRECIS.isSet(flag);
+        }
+
+        public boolean isStrokePrecision() {
+            return STROKE_PRECIS.isSet(flag);
+        }
+
+        public boolean isLeftHandAngles() {
+            return LH_ANGLES.isSet(flag);
+        }
+
+        public boolean isTrueTypeAlways() {
+            return TT_ALWAYS.isSet(flag);
+        }
+
+        public boolean isFontAssociated() {
+            return !DFA_DISABLE.isSet(flag);
+        }
+
+        public boolean useEmbeddedFont() {
+            return EMBEDDED.isSet(flag);
+        }
+
         @Override
         public String toString() {
-            return
-                (((flag&0x3) == 0 ? "default " : " ")+
-                (CLIP_CHARACTER_PRECIS.isSet(flag) ? "char " : " ")+
-                (CLIP_STROKE_PRECIS.isSet(flag) ? "stroke " : " ")+
-                (CLIP_LH_ANGLES.isSet(flag) ? "angles " : " ")+
-                (CLIP_TT_ALWAYS.isSet(flag) ? "tt_always " : " ")+
-                (CLIP_DFA_DISABLE.isSet(flag) ? "dfa " : " ")+
-                (CLIP_EMBEDDED.isSet(flag) ? "embedded " : " ")
-                ).trim()
-            ;
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "isDefaultPrecision", this::isDefaultPrecision,
+                "flag", GenericRecordUtil.getBitsAsString(() -> flag, FLAG_MASKS, FLAG_NAMES)
+            );
         }
     }
 
@@ -456,21 +503,7 @@ public class HwmfFont implements FontInfo {
 
     @Override
     public String toString() {
-        return "{ height: "+height+
-                ", width: "+width+
-                ", escapment: "+escapement+
-                ", weight: "+weight+
-                ", italic: "+italic+
-                ", underline: "+underline+
-                ", strikeOut: "+strikeOut+
-                ", charset: '"+charSet+"'"+
-                ", outPrecision: '"+outPrecision+"'"+
-                ", clipPrecision: '"+clipPrecision+"'"+
-                ", quality: '"+quality+"'"+
-                ", pitch: '"+getPitch()+"'"+
-                ", family: '"+getFamily()+"'"+
-                ", facename: '"+facename+"'"+
-                "}";
+        return GenericRecordJsonWriter.marshal(this);
     }
 
     protected int readString(LittleEndianInputStream leis, StringBuilder sb, int limit) throws IOException {
@@ -489,4 +522,24 @@ public class HwmfFont implements FontInfo {
 
         return readBytes;
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+        m.put("height", this::getHeight);
+        m.put("width", this::getWidth);
+        m.put("escapment", this::getEscapement);
+        m.put("weight", this::getWeight);
+        m.put("italic", this::isItalic);
+        m.put("underline", this::isUnderline);
+        m.put("strikeOut", this::isStrikeOut);
+        m.put("charset", this::getCharset);
+        m.put("outPrecision", this::getOutPrecision);
+        m.put("clipPrecision", this::getClipPrecision);
+        m.put("quality", this::getQuality);
+        m.put("pitch", this::getPitch);
+        m.put("family", this::getFamily);
+        m.put("typeface", this::getTypeface);
+        return Collections.unmodifiableMap(m);
+    }
 }
index 811caa0d20fd23d8f2402477d5191058f7b73b6e..2f1ada526b9bc5e921ce5cc4016e57315c631551 100644 (file)
 package org.apache.poi.hwmf.record;
 
 import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
-public class HwmfHeader {
-    private int type;
+public class HwmfHeader implements GenericRecord {
+    public enum HwmfHeaderMetaType {
+        MEMORY_METAFILE, DISK_METAFILE
+    }
+
+    private HwmfHeaderMetaType type;
     private int recordSize;
     private int version;
     private int filesize;
@@ -34,7 +43,7 @@ public class HwmfHeader {
     public HwmfHeader(LittleEndianInputStream leis) throws IOException {
         // Type (2 bytes):  A 16-bit unsigned integer that defines the type of metafile
         // MEMORYMETAFILE = 0x0001, DISKMETAFILE = 0x0002 
-        type = leis.readUShort();
+        type = HwmfHeaderMetaType.values()[leis.readUShort()-1];
 
         // HeaderSize (2 bytes):  A 16-bit unsigned integer that defines the number
         // of 16-bit words in the header.
@@ -74,4 +83,17 @@ public class HwmfHeader {
             assert(len == bytesLeft);
         }
     }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+        m.put("type", () -> type);
+        m.put("recordSize", () -> recordSize);
+        m.put("version", () -> version);
+        m.put("filesize", () -> filesize);
+        m.put("numberOfObjects", () -> numberOfObjects);
+        m.put("maxRecord", () -> maxRecord);
+        m.put("numberOfMembers", () -> numberOfMembers);
+        return Collections.unmodifiableMap(m);
+    }
 }
index bd9264b6ac2b9afdb88430d733b3713b0c48c556..45ccf645fa31ddaec229844e69c9e389cb3860ba 100644 (file)
@@ -21,6 +21,8 @@ import java.awt.Color;
 import java.awt.geom.Dimension2D;
 import java.awt.image.BufferedImage;
 import java.io.IOException;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.poi.hwmf.draw.HwmfDrawProperties;
 import org.apache.poi.hwmf.draw.HwmfGraphics;
@@ -28,9 +30,12 @@ import org.apache.poi.hwmf.record.HwmfFill.ColorUsage;
 import org.apache.poi.hwmf.record.HwmfFill.HwmfImageRecord;
 import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
 import org.apache.poi.util.Dimension2DDouble;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
+@SuppressWarnings("WeakerAccess")
 public class HwmfMisc {
 
     /**
@@ -56,6 +61,11 @@ public class HwmfMisc {
         public String toString() {
             return "{}";
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return null;
+        }
     }
 
     /**
@@ -74,6 +84,11 @@ public class HwmfMisc {
         public void draw(HwmfGraphics ctx) {
 
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return null;
+        }
     }
 
     /**
@@ -107,7 +122,16 @@ public class HwmfMisc {
 
         @Override
         public String toString() {
-            return "{ nSavedDC: "+nSavedDC+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public int getNSavedDC() {
+            return nSavedDC;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("nSavedDC", this::getNSavedDC);
         }
     }
 
@@ -136,7 +160,16 @@ public class HwmfMisc {
 
         @Override
         public String toString() {
-            return "{ colorRef: "+colorRef+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfColorRef getColorRef() {
+            return colorRef;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("colorRef", this::getColorRef);
         }
     }
 
@@ -184,7 +217,16 @@ public class HwmfMisc {
 
         @Override
         public String toString() {
-            return "{ bkMode: '"+bkMode+"' }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfBkMode getBkMode() {
+            return bkMode;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("bkMode", this::getBkMode);
         }
     }
 
@@ -219,6 +261,11 @@ public class HwmfMisc {
         public void draw(HwmfGraphics ctx) {
 
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("layout", () -> layout);
+        }
     }
 
     /**
@@ -249,7 +296,16 @@ public class HwmfMisc {
 
         @Override
         public String toString() {
-            return "{ mapMode: '"+mapMode+"' }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfMapMode getMapMode() {
+            return mapMode;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("mapMode", this::getMapMode);
         }
     }
 
@@ -285,7 +341,12 @@ public class HwmfMisc {
 
         @Override
         public String toString() {
-            return "{ mapperValues: "+mapperValues+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("mapperValues", () -> mapperValues);
         }
     }
 
@@ -317,7 +378,16 @@ public class HwmfMisc {
 
         @Override
         public String toString() {
-            return "{ drawMode: '"+drawMode+"' }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfBinaryRasterOp getDrawMode() {
+            return drawMode;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("drawMode", this::getDrawMode);
         }
     }
 
@@ -394,7 +464,16 @@ public class HwmfMisc {
 
         @Override
         public String toString() {
-            return "{ stretchBltMode: '"+stretchBltMode+"' }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public StretchBltMode getStretchBltMode() {
+            return stretchBltMode;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("stretchBltMode", this::getStretchBltMode);
         }
     }
 
@@ -486,6 +565,24 @@ public class HwmfMisc {
                 return null;
             }
         }
+
+        public HwmfBrushStyle getStyle() {
+            return style;
+        }
+
+        public ColorUsage getColorUsage() {
+            return colorUsage;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "style", this::getStyle,
+                "colorUsage", this::getColorUsage,
+                "pattern", () -> (patternDib != null && patternDib.isValid()) ? patternDib : pattern16,
+                "bmpData", this::getBMPData
+            );
+        }
     }
 
     /**
@@ -524,7 +621,16 @@ public class HwmfMisc {
 
         @Override
         public String toString() {
-            return "{ index: "+objectIndex+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public int getObjectIndex() {
+            return objectIndex;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("objectIndex", this::getObjectIndex);
         }
     }
 
@@ -554,6 +660,15 @@ public class HwmfMisc {
             dp.setBrushBitmap(pattern.getImage());
             dp.setBrushStyle(HwmfBrushStyle.BS_PATTERN);
         }
+
+        public HwmfBitmap16 getPattern() {
+            return pattern;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("pattern", this::getPattern);
+        }
     }
 
     public static class WmfCreatePenIndirect implements HwmfRecord, HwmfObjectTableEntry {
@@ -599,10 +714,28 @@ public class HwmfMisc {
 
         @Override
         public String toString() {
-            return
-                "{ penStyle: "+penStyle+
-                ", dimension: { width: "+dimension.getWidth()+", height: "+dimension.getHeight()+" }"+
-                ", colorRef: "+colorRef+"}";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfPenStyle getPenStyle() {
+            return penStyle;
+        }
+
+        public Dimension2D getDimension() {
+            return dimension;
+        }
+
+        public HwmfColorRef getColorRef() {
+            return colorRef;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "penStyle", this::getPenStyle,
+                "dimension", this::getDimension,
+                "colorRef", this::getColorRef
+            );
         }
     }
 
@@ -687,10 +820,28 @@ public class HwmfMisc {
 
         @Override
         public String toString() {
-            return
-                "{ brushStyle: '"+brushStyle+"'"+
-                ", colorRef: "+colorRef+
-                ", brushHatch: '"+brushHatch+"' }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfBrushStyle getBrushStyle() {
+            return brushStyle;
+        }
+
+        public HwmfColorRef getColorRef() {
+            return colorRef;
+        }
+
+        public HwmfHatchStyle getBrushHatch() {
+            return brushHatch;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "brushStyle", this::getBrushStyle,
+                "colorRef", this::getColorRef,
+                "brushHatch", this::getBrushHatch
+            );
         }
     }
 }
\ No newline at end of file
index cef75f698d610a92f1bc59c5ac2247d203fae9f7..efaa774497fe127482e352e923fb444ec8b74e0b 100644 (file)
 
 package org.apache.poi.hwmf.record;
 
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
+
 import java.awt.Color;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hwmf.draw.HwmfDrawProperties;
 import org.apache.poi.hwmf.draw.HwmfGraphics;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
 public class HwmfPalette {
 
-    public static class PaletteEntry {
+    public static class PaletteEntry implements GenericRecord {
         private static final BitField PC_RESERVED   = BitFieldFactory.getInstance(0x01);
         private static final BitField PC_EXPLICIT   = BitFieldFactory.getInstance(0x02);
         private static final BitField PC_NOCOLLAPSE = BitFieldFactory.getInstance(0x04);
 
+        private static final int[] FLAGS_MASKS = { 1,2,4 };
+
+        private static final String[] FLAGS_NAMES = { "RESERVED", "EXPLICIT", "NOCOLLAPSE" };
+
         private int values;
         private Color colorRef;
 
@@ -91,6 +101,18 @@ public class HwmfPalette {
         public boolean isNoCollapse() {
             return PC_NOCOLLAPSE.isSet(values);
         }
+
+        public Color getColorRef() {
+            return colorRef;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "flags", getBitsAsString(() -> values, FLAGS_MASKS, FLAGS_NAMES),
+                "color", this::getColorRef
+            );
+        }
     }
 
     public static abstract class WmfPaletteParent implements HwmfRecord, HwmfObjectTableEntry  {
@@ -112,10 +134,8 @@ public class HwmfPalette {
         }
 
         protected int readPaletteEntries(LittleEndianInputStream leis, int nbrOfEntries) throws IOException {
-            /**
-             * NumberOfEntries (2 bytes):  A 16-bit unsigned integer that defines the number of objects in
-             * aPaletteEntries.
-             */
+            // NumberOfEntries (2 bytes):  A 16-bit unsigned integer that defines the number of objects in
+            // aPaletteEntries.
             final int numberOfEntries = (nbrOfEntries > -1) ? nbrOfEntries : leis.readUShort();
             int size = (nbrOfEntries > -1) ? 0 : LittleEndianConsts.SHORT_SIZE;
             for (int i=0; i<numberOfEntries; i++) {
@@ -131,7 +151,7 @@ public class HwmfPalette {
             ctx.addObjectTableEntry(this);
         }
         
-        protected List<PaletteEntry> getPaletteCopy() {
+        List<PaletteEntry> getPaletteCopy() {
             List<PaletteEntry> newPalette = new ArrayList<>();
             for (PaletteEntry et : palette) {
                 newPalette.add(new PaletteEntry(et));
@@ -139,9 +159,17 @@ public class HwmfPalette {
             return newPalette;
         }
 
-        protected int getPaletteStart() {
+        int getPaletteStart() {
             return start;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "paletteStart", this::getPaletteStart,
+                "pallete", this::getPaletteCopy
+            );
+        }
     }
 
     /**
@@ -233,6 +261,15 @@ public class HwmfPalette {
             palette = palette.subList(0, numberOfEntries);
             props.setPalette(palette);
         }
+
+        public int getNumberOfEntries() {
+            return numberOfEntries;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("numberOfEntries", this::getNumberOfEntries);
+        }
     }
 
     /**
@@ -260,6 +297,15 @@ public class HwmfPalette {
         public void draw(HwmfGraphics ctx) {
             ctx.applyObjectTableEntry(paletteIndex);
         }
+
+        public int getPaletteIndex() {
+            return paletteIndex;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("paletteIndex", this::getPaletteIndex);
+        }
     }
 
     /**
@@ -281,6 +327,11 @@ public class HwmfPalette {
         public void draw(HwmfGraphics ctx) {
 
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return null;
+        }
     }
 
     /**
@@ -306,7 +357,7 @@ public class HwmfPalette {
             HwmfDrawProperties props = ctx.getProperties();
             List<PaletteEntry> dest = props.getPalette();
             List<PaletteEntry> src = getPaletteCopy();
-            int start = getPaletteStart();
+            final int start = getPaletteStart();
             if (dest == null) {
                 dest = new ArrayList<>();
             }
index 776d48a3e7269ead0d6c1c647929495be80ce157..261b60a439fe96ff6a12420a6009b60555b4cfea 100644 (file)
 package org.apache.poi.hwmf.record;
 
 import java.awt.BasicStroke;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 
 /**
  * The 16-bit PenStyle Enumeration is used to specify different types of pens
@@ -32,7 +37,7 @@ import org.apache.poi.util.BitFieldFactory;
  * The defaults in case the other values of the subsection aren't set are
  * solid, round end caps, round joins and cosmetic type.
  */
-public class HwmfPenStyle implements Cloneable {
+public class HwmfPenStyle implements Cloneable, GenericRecord {
     public enum HwmfLineCap {
         /** Rounded ends */
         ROUND(0, BasicStroke.CAP_ROUND),
@@ -207,12 +212,18 @@ public class HwmfPenStyle implements Cloneable {
 
     @Override
     public String toString() {
-        return
-            "{ lineCap: '"+getLineCap()+"'"+
-            ", lineDash: '"+getLineDash()+"'"+
-            ", lineJoin: '"+getLineJoin()+"'"+
-            (isAlternateDash()?", alternateDash: true ":"")+
-            (isGeometric()?", geometric: true ":"")+
-            "}";
+        return GenericRecordJsonWriter.marshal(this);
+    }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return GenericRecordUtil.getGenericProperties(
+            "lineCap", this::getLineCap,
+            "lineJoin", this::getLineJoin,
+            "lineDash", this::getLineDash,
+            "lineDashes", this::getLineDashes,
+            "alternateDash", this::isAlternateDash,
+            "geometric", this::isGeometric
+        );
     }
 }
index 2673e8078335d3acb769f5f56526dce901381f54..b68af184606a0d050fbcb325f350177e137a5bdb 100644 (file)
@@ -19,10 +19,11 @@ package org.apache.poi.hwmf.record;
 
 import java.io.IOException;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hwmf.draw.HwmfGraphics;
 import org.apache.poi.util.LittleEndianInputStream;
 
-public interface HwmfRecord {
+public interface HwmfRecord extends GenericRecord {
     HwmfRecordType getWmfRecordType();
 
     /**
@@ -40,4 +41,9 @@ public interface HwmfRecord {
      * @param ctx the graphics context to modify
      */
     void draw(HwmfGraphics ctx);
+
+    @Override
+    default Enum getGenericRecordType() {
+        return getWmfRecordType();
+    }
 }
index 6af61b81ac5dec1f628552edcb8684469224da69..533c85a8215302bce2214c6d9cba435107ba5ef5 100644 (file)
 
 package org.apache.poi.hwmf.record;
 
-import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
-import static org.apache.poi.hwmf.record.HwmfDraw.pointToString;
 import static org.apache.poi.hwmf.record.HwmfDraw.readPointS;
 import static org.apache.poi.hwmf.record.HwmfDraw.readRectS;
+import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
 
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
-import org.apache.commons.codec.Charsets;
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hwmf.draw.HwmfDrawProperties;
 import org.apache.poi.hwmf.draw.HwmfGraphics;
 import org.apache.poi.hwmf.record.HwmfMisc.WmfSetMapMode;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
-import org.apache.poi.util.LocaleUtil;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
@@ -76,6 +79,11 @@ public class HwmfText {
         public void draw(HwmfGraphics ctx) {
 
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("charExtra", () -> charExtra);
+        }
     }
     
     /**
@@ -102,7 +110,16 @@ public class HwmfText {
 
         @Override
         public String toString() {
-            return "{ colorRef: "+colorRef+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public HwmfColorRef getColorRef() {
+            return colorRef;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("colorRef", this::getColorRef);
         }
     }
     
@@ -141,6 +158,14 @@ public class HwmfText {
         public void draw(HwmfGraphics ctx) {
 
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "breakCount", () -> breakCount,
+                "breakExtra", () -> breakExtra
+            );
+        }
     }
     
     /**
@@ -204,9 +229,14 @@ public class HwmfText {
             System.arraycopy(rawTextBytes, 0, ret, 0, stringLength);
             return ret;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("text", () -> getText(StandardCharsets.US_ASCII));
+        }
     }
 
-    public static class WmfExtTextOutOptions {
+    public static class WmfExtTextOutOptions implements GenericRecord {
         /**
          * Indicates that the background color that is defined in the playback device context
          * SHOULD be used to fill the rectangle.
@@ -272,23 +302,37 @@ public class HwmfText {
         /** This bit is reserved and SHOULD NOT be used. */
         private static final BitField ETO_REVERSE_INDEX_MAP = BitFieldFactory.getInstance(0x10000);
 
-        protected int flag;
+        private static final int[] FLAGS_MASKS = {
+            0x0002, 0x0004, 0x0010, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x10000
+        };
+
+        private static final String[] FLAGS_NAMES = {
+            "OPAQUE", "CLIPPED", "GLYPH_INDEX", "RTLREADING", "NO_RECT", "SMALL_CHARS", "NUMERICSLOCAL",
+            "NUMERICSLATIN", "IGNORELANGUAGE", "PDY", "REVERSE_INDEX_MAP"
+        };
+
+        protected int flags;
 
         public int init(LittleEndianInputStream leis) {
-            flag = leis.readUShort();
+            flags = leis.readUShort();
             return LittleEndianConsts.SHORT_SIZE;
         }
 
         public boolean isOpaque() {
-            return ETO_OPAQUE.isSet(flag);
+            return ETO_OPAQUE.isSet(flags);
         }
 
         public boolean isClipped() {
-            return ETO_CLIPPED.isSet(flag);
+            return ETO_CLIPPED.isSet(flags);
         }
 
         public boolean isYDisplaced() {
-            return ETO_PDY.isSet(flag);
+            return ETO_PDY.isSet(flags);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("flags", getBitsAsString(() -> flags, FLAGS_MASKS, FLAGS_NAMES));
         }
     }
 
@@ -417,17 +461,24 @@ public class HwmfText {
 
         @Override
         public String toString() {
-            String text = "";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        private String getGenericText() {
             try {
-                text = getText(isUnicode() ? Charsets.UTF_16LE : LocaleUtil.CHARSET_1252);
-            } catch (IOException ignored) {
+                return getText(isUnicode() ? StandardCharsets.UTF_16LE : StandardCharsets.US_ASCII);
+            } catch (IOException e) {
+                return "";
             }
+        }
 
-            return
-                "{ reference: " + pointToString(reference) +
-                ", bounds: " + boundsToString(bounds) +
-                ", text: '"+text.replaceAll("\\p{Cntrl}",".")+"'"+
-                "}";
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "reference", this::getReference,
+                "bounds", this::getBounds,
+                "text", this::getGenericText
+            );
         }
     }
     
@@ -555,12 +606,17 @@ public class HwmfText {
 
         @Override
         public String toString() {
-            return
-                "{ align: '"+ getAlignLatin() + "'" +
-                ", valign: '"+ getVAlignLatin() + "'" +
-                ", alignAsian: '"+ getAlignAsian() + "'" +
-                ", valignAsian: '"+ getVAlignAsian() + "'" +
-                "}";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "align", this::getAlignLatin,
+                "valign", this::getVAlignLatin,
+                "alignAsian", this::getAlignAsian,
+                "valignAsian", this::getVAlignAsian
+            );
         }
 
         private HwmfTextAlignment getAlignLatin() {
@@ -649,7 +705,12 @@ public class HwmfText {
 
         @Override
         public String toString() {
-            return "{ font: "+font+" } ";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("font", this::getFont);
         }
     }
 }
index 442884e78c019d263a7a3130ddc39c617f7ecdfe..ebe819155fe90f0a091cd6c77e75af96d42e29a8 100644 (file)
 
 package org.apache.poi.hwmf.record;
 
-import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
-import static org.apache.poi.hwmf.record.HwmfDraw.dimToString;
 import static org.apache.poi.hwmf.record.HwmfDraw.normalizeBounds;
-import static org.apache.poi.hwmf.record.HwmfDraw.pointToString;
 import static org.apache.poi.hwmf.record.HwmfDraw.readBounds;
 import static org.apache.poi.hwmf.record.HwmfDraw.readPointS;
 
@@ -31,10 +28,18 @@ import java.awt.geom.Dimension2D;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hwmf.draw.HwmfDrawProperties;
 import org.apache.poi.hwmf.draw.HwmfGraphics;
 import org.apache.poi.util.Dimension2DDouble;
+import org.apache.poi.util.GenericRecordJsonWriter;
+import org.apache.poi.util.GenericRecordUtil;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInputStream;
 
@@ -71,7 +76,16 @@ public class HwmfWindowing {
 
         @Override
         public String toString() {
-            return pointToString(origin);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Point2D getOrigin() {
+            return origin;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("origin", this::getOrigin);
         }
     }
 
@@ -112,7 +126,16 @@ public class HwmfWindowing {
 
         @Override
         public String toString() {
-            return dimToString(extents);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Dimension2D getExtents() {
+            return extents;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("extents", this::getExtents);
         }
     }
 
@@ -148,7 +171,16 @@ public class HwmfWindowing {
 
         @Override
         public String toString() {
-            return pointToString(offset);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Point2D getOffset() {
+            return offset;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("offset", this::getOffset);
         }
     }
 
@@ -189,8 +221,18 @@ public class HwmfWindowing {
 
         @Override
         public String toString() {
-            return pointToString(origin);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Point2D getOrigin() {
+            return origin;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("origin", this::getOrigin);
+        }
+
     }
 
     /**
@@ -234,7 +276,12 @@ public class HwmfWindowing {
 
         @Override
         public String toString() {
-            return dimToString(size);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("size", this::getSize);
         }
     }
 
@@ -268,7 +315,16 @@ public class HwmfWindowing {
 
         @Override
         public String toString() {
-            return pointToString(offset);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Point2D getOffset() {
+            return offset;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("offset", this::getOffset);
         }
     }
 
@@ -319,8 +375,18 @@ public class HwmfWindowing {
 
         @Override
         public String toString() {
-            return "{ scaleX: "+scale.getWidth()+", scaleY: "+scale.getHeight()+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Dimension2D getScale() {
+            return scale;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("scale", this::getScale);
         }
+
     }
 
 
@@ -373,7 +439,16 @@ public class HwmfWindowing {
 
         @Override
         public String toString() {
-            return "{ scaleX: "+scale.getWidth()+", scaleY: "+scale.getHeight()+" }";
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Dimension2D getScale() {
+            return scale;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("scale", this::getScale);
         }
     }
 
@@ -409,8 +484,18 @@ public class HwmfWindowing {
 
         @Override
         public String toString() {
-            return pointToString(offset);
+            return GenericRecordJsonWriter.marshal(this);
+        }
+
+        public Point2D getOffset() {
+            return offset;
         }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("offset", this::getOffset);
+        }
+
     }
 
     /**
@@ -439,7 +524,16 @@ public class HwmfWindowing {
         
         @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);
         }
     }
 
@@ -470,7 +564,16 @@ public class HwmfWindowing {
         
         @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);
         }
     }
 
@@ -500,9 +603,18 @@ public class HwmfWindowing {
         public void draw(HwmfGraphics ctx) {
 
         }
+
+        public int getRegion() {
+            return region;
+        }
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties("region", this::getRegion);
+        }
     }
 
-    public static class WmfScanObject {
+    public static class WmfScanObject implements GenericRecord {
         /**
          * A 16-bit unsigned integer that specifies the number of horizontal (x-axis)
          * coordinates in the ScanLines array. This value MUST be a multiple of 2, since left and right
@@ -549,6 +661,19 @@ public class HwmfWindowing {
             size += LittleEndianConsts.SHORT_SIZE;
             return size;
         }
+
+        @SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            return GenericRecordUtil.getGenericProperties(
+                "count", () -> count,
+                "top", () -> top,
+                "bottom", () -> bottom,
+                "left_scanline", () -> Arrays.asList(left_scanline),
+                "right_scanline", () -> Arrays.asList(right_scanline),
+                "count2", () -> count2
+            );
+        }
     }
 
     public static class WmfCreateRegion implements HwmfRecord, HwmfObjectTableEntry {
@@ -578,7 +703,7 @@ public class HwmfWindowing {
          */
         private int maxScan;
 
-        private Rectangle2D bounds = new Rectangle2D.Double();
+        private final Rectangle2D bounds = new Rectangle2D.Double();
 
         /**
          * An array of Scan objects that define the scanlines in the region.
@@ -651,5 +776,20 @@ public class HwmfWindowing {
 
             ctx.getProperties().setRegion(region);
         }
+
+
+        @Override
+        public Map<String, Supplier<?>> getGenericProperties() {
+            final Map<String,Supplier<?>> m = new LinkedHashMap<>();
+            m.put("nextInChain", () -> nextInChain);
+            m.put("objectType", () -> objectType);
+            m.put("objectCount", () -> objectCount);
+            m.put("regionSize", () -> regionSize);
+            m.put("scanCount", () -> scanCount);
+            m.put("maxScan", () -> maxScan);
+            m.put("bounds", () -> bounds);
+            m.put("scanObjects", () -> Arrays.asList(scanObjects));
+            return Collections.unmodifiableMap(m);
+        }
     }
 }
index cb4e92133da3a6d714669045a31a87135e7e1e9f..c5a4bad5220f5c51d44a3ca27750818ee85ad8ac 100644 (file)
@@ -26,9 +26,14 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.util.Spliterator;
+import java.util.function.Supplier;
 
+import org.apache.poi.common.usermodel.GenericRecord;
 import org.apache.poi.hwmf.draw.HwmfDrawProperties;
 import org.apache.poi.hwmf.draw.HwmfGraphics;
 import org.apache.poi.hwmf.record.HwmfHeader;
@@ -45,7 +50,7 @@ import org.apache.poi.util.POILogger;
 import org.apache.poi.util.RecordFormatException;
 import org.apache.poi.util.Units;
 
-public class HwmfPicture {
+public class HwmfPicture implements Iterable<HwmfRecord>, GenericRecord {
     /** Max. record length - processing longer records will throw an exception */
     public static final int MAX_RECORD_LENGTH = 50_000_000;
 
@@ -227,4 +232,24 @@ public class HwmfPicture {
     public Iterable<HwmfEmbedded> getEmbeddings() {
         return () -> new HwmfEmbeddedIterator(HwmfPicture.this);
     }
+
+    @Override
+    public Iterator<HwmfRecord> iterator() {
+        return getRecords().iterator();
+    }
+
+    @Override
+    public Spliterator<HwmfRecord> spliterator() {
+        return getRecords().spliterator();
+    }
+
+    @Override
+    public Map<String, Supplier<?>> getGenericProperties() {
+        return null;
+    }
+
+    @Override
+    public List<? extends GenericRecord> getGenericChildren() {
+        return getRecords();
+    }
 }
index e03bded11df635504c4a02b3e3149b8d2d880d62..eb4c368bacb3327a9d7ed5e5e7885cd50b935749 100644 (file)
 
 package org.apache.poi.hslf.model;
 
-import static org.apache.poi.hslf.record.TextHeaderAtom.BODY_TYPE;
-import static org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE;
-import static org.apache.poi.hslf.record.TextHeaderAtom.CENTRE_BODY_TYPE;
-import static org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE;
+import static org.apache.poi.sl.usermodel.TextShape.TextPlaceholder.BODY;
+import static org.apache.poi.sl.usermodel.TextShape.TextPlaceholder.CENTER_BODY;
+import static org.apache.poi.sl.usermodel.TextShape.TextPlaceholder.CENTER_TITLE;
+import static org.apache.poi.sl.usermodel.TextShape.TextPlaceholder.TITLE;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import java.io.ByteArrayInputStream;
@@ -33,7 +34,6 @@ import org.apache.poi.POIDataSamples;
 import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp;
 import org.apache.poi.hslf.model.textproperties.TextProp;
 import org.apache.poi.hslf.record.Environment;
-import org.apache.poi.hslf.record.TextHeaderAtom;
 import org.apache.poi.hslf.usermodel.HSLFMasterSheet;
 import org.apache.poi.hslf.usermodel.HSLFSlide;
 import org.apache.poi.hslf.usermodel.HSLFSlideMaster;
@@ -42,6 +42,7 @@ import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl;
 import org.apache.poi.hslf.usermodel.HSLFTextParagraph;
 import org.apache.poi.hslf.usermodel.HSLFTextRun;
 import org.apache.poi.hslf.usermodel.HSLFTitleMaster;
+import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder;
 import org.junit.Test;
 
 /**
@@ -63,30 +64,30 @@ public final class TestSlideMaster {
         assertEquals(2, ppt.getSlideMasters().size());
 
         //character attributes
-        assertEquals(40, getMasterVal(ppt, 0, TITLE_TYPE, "font.size", true));
-        assertEquals(48, getMasterVal(ppt, 1, TITLE_TYPE, "font.size", true));
+        assertEquals(40, getMasterVal(ppt, 0, TITLE, "font.size", true));
+        assertEquals(48, getMasterVal(ppt, 1, TITLE, "font.size", true));
 
-        int font1 = getMasterVal(ppt, 0, TITLE_TYPE, "font.index", true);
-        int font2 = getMasterVal(ppt, 1, TITLE_TYPE, "font.index", true);
+        int font1 = getMasterVal(ppt, 0, TITLE, "font.index", true);
+        int font2 = getMasterVal(ppt, 1, TITLE, "font.index", true);
         assertEquals("Arial", env.getFontCollection().getFontInfo(font1).getTypeface());
         assertEquals("Georgia", env.getFontCollection().getFontInfo(font2).getTypeface());
 
-        CharFlagsTextProp prop1 = getMasterProp(ppt, 0, TITLE_TYPE, "char_flags", true);
-        assertEquals(false, prop1.getSubValue(CharFlagsTextProp.BOLD_IDX));
-        assertEquals(false, prop1.getSubValue(CharFlagsTextProp.ITALIC_IDX));
-        assertEquals(true, prop1.getSubValue(CharFlagsTextProp.UNDERLINE_IDX));
+        CharFlagsTextProp prop1 = getMasterProp(ppt, 0, TITLE, "char_flags", true);
+        assertFalse(prop1.getSubValue(CharFlagsTextProp.BOLD_IDX));
+        assertFalse(prop1.getSubValue(CharFlagsTextProp.ITALIC_IDX));
+        assertTrue(prop1.getSubValue(CharFlagsTextProp.UNDERLINE_IDX));
 
-        CharFlagsTextProp prop2 = getMasterProp(ppt, 1, TITLE_TYPE, "char_flags", true);
-        assertEquals(false, prop2.getSubValue(CharFlagsTextProp.BOLD_IDX));
-        assertEquals(true, prop2.getSubValue(CharFlagsTextProp.ITALIC_IDX));
-        assertEquals(false, prop2.getSubValue(CharFlagsTextProp.UNDERLINE_IDX));
+        CharFlagsTextProp prop2 = getMasterProp(ppt, 1, TITLE, "char_flags", true);
+        assertFalse(prop2.getSubValue(CharFlagsTextProp.BOLD_IDX));
+        assertTrue(prop2.getSubValue(CharFlagsTextProp.ITALIC_IDX));
+        assertFalse(prop2.getSubValue(CharFlagsTextProp.UNDERLINE_IDX));
 
         //now paragraph attributes
-        assertEquals(0x266B, getMasterVal(ppt, 0, BODY_TYPE, "bullet.char", false));
-        assertEquals(0x2022, getMasterVal(ppt, 1, BODY_TYPE, "bullet.char", false));
+        assertEquals(0x266B, getMasterVal(ppt, 0, BODY, "bullet.char", false));
+        assertEquals(0x2022, getMasterVal(ppt, 1, BODY, "bullet.char", false));
 
-        int b1 = getMasterVal(ppt, 0, BODY_TYPE, "bullet.font", false);
-        int b2 = getMasterVal(ppt, 1, BODY_TYPE, "bullet.font", false);
+        int b1 = getMasterVal(ppt, 0, BODY, "bullet.font", false);
+        int b2 = getMasterVal(ppt, 1, BODY, "bullet.font", false);
         assertEquals("Arial", env.getFontCollection().getFontInfo(b1).getTypeface());
         assertEquals("Georgia", env.getFontCollection().getFontInfo(b2).getTypeface());
 
@@ -94,11 +95,11 @@ public final class TestSlideMaster {
     }
 
     @SuppressWarnings("unchecked")
-    private static <T extends TextProp> T getMasterProp(HSLFSlideShow ppt, int masterIdx, int txtype, String propName, boolean isCharacter) {
-        return (T)ppt.getSlideMasters().get(masterIdx).getPropCollection(txtype, 0, propName, isCharacter).findByName(propName);
+    private static <T extends TextProp> T getMasterProp(HSLFSlideShow ppt, int masterIdx, TextPlaceholder txtype, String propName, boolean isCharacter) {
+        return (T)ppt.getSlideMasters().get(masterIdx).getPropCollection(txtype.nativeId, 0, propName, isCharacter).findByName(propName);
     }
 
-    private static int getMasterVal(HSLFSlideShow ppt, int masterIdx, int txtype, String propName, boolean isCharacter) {
+    private static int getMasterVal(HSLFSlideShow ppt, int masterIdx, TextPlaceholder txtype, String propName, boolean isCharacter) {
         return getMasterProp(ppt, masterIdx, txtype, propName, isCharacter).getValue();
     }
 
@@ -111,17 +112,17 @@ public final class TestSlideMaster {
         HSLFSlideShow ppt = new HSLFSlideShow(_slTests.openResourceAsStream("slide_master.ppt"));
         assertEquals(1, ppt.getTitleMasters().size());
 
-        assertEquals(40, getMasterVal(ppt, 0, CENTER_TITLE_TYPE, "font.size", true));
-        CharFlagsTextProp prop1 = getMasterProp(ppt, 0, CENTER_TITLE_TYPE, "char_flags", true);
-        assertEquals(false, prop1.getSubValue(CharFlagsTextProp.BOLD_IDX));
-        assertEquals(false, prop1.getSubValue(CharFlagsTextProp.ITALIC_IDX));
-        assertEquals(true, prop1.getSubValue(CharFlagsTextProp.UNDERLINE_IDX));
+        assertEquals(40, getMasterVal(ppt, 0, CENTER_TITLE, "font.size", true));
+        CharFlagsTextProp prop1 = getMasterProp(ppt, 0, CENTER_TITLE, "char_flags", true);
+        assertFalse(prop1.getSubValue(CharFlagsTextProp.BOLD_IDX));
+        assertFalse(prop1.getSubValue(CharFlagsTextProp.ITALIC_IDX));
+        assertTrue(prop1.getSubValue(CharFlagsTextProp.UNDERLINE_IDX));
 
-        assertEquals(32, getMasterVal(ppt, 0, CENTRE_BODY_TYPE, "font.size", true));
-        CharFlagsTextProp prop2 = getMasterProp(ppt, 0, CENTRE_BODY_TYPE, "char_flags", true);
-        assertEquals(false, prop2.getSubValue(CharFlagsTextProp.BOLD_IDX));
-        assertEquals(false, prop2.getSubValue(CharFlagsTextProp.ITALIC_IDX));
-        assertEquals(false, prop2.getSubValue(CharFlagsTextProp.UNDERLINE_IDX));
+        assertEquals(32, getMasterVal(ppt, 0, CENTER_BODY, "font.size", true));
+        CharFlagsTextProp prop2 = getMasterProp(ppt, 0, CENTER_BODY, "char_flags", true);
+        assertFalse(prop2.getSubValue(CharFlagsTextProp.BOLD_IDX));
+        assertFalse(prop2.getSubValue(CharFlagsTextProp.ITALIC_IDX));
+        assertFalse(prop2.getSubValue(CharFlagsTextProp.UNDERLINE_IDX));
 
         ppt.close();
     }
@@ -138,18 +139,18 @@ public final class TestSlideMaster {
 
         for (List<HSLFTextParagraph> txt : slide.getTextParagraphs()) {
             HSLFTextRun rt = txt.get(0).getTextRuns().get(0);
-            switch(txt.get(0).getRunType()){
-                case TextHeaderAtom.CENTER_TITLE_TYPE:
+            switch(TextPlaceholder.fromNativeId(txt.get(0).getRunType())){
+                case CENTER_TITLE:
                     assertEquals("Arial", rt.getFontFamily());
                     assertEquals(32, rt.getFontSize(), 0);
-                    assertEquals(true, rt.isBold());
-                    assertEquals(true, rt.isUnderlined());
+                    assertTrue(rt.isBold());
+                    assertTrue(rt.isUnderlined());
                     break;
-                case TextHeaderAtom.CENTRE_BODY_TYPE:
+                case CENTER_BODY:
                     assertEquals("Courier New", rt.getFontFamily());
                     assertEquals(20, rt.getFontSize(), 0);
-                    assertEquals(true, rt.isBold());
-                    assertEquals(false, rt.isUnderlined());
+                    assertTrue(rt.isBold());
+                    assertFalse(rt.isUnderlined());
                     break;
             }
 
@@ -167,12 +168,12 @@ public final class TestSlideMaster {
         assertEquals(3, slide.size());
         for (List<HSLFTextParagraph> tparas : slide.get(0).getTextParagraphs()) {
             HSLFTextParagraph tpara = tparas.get(0);
-            if (tpara.getRunType() == TextHeaderAtom.TITLE_TYPE){
+            if (tpara.getRunType() == TITLE.nativeId){
                 HSLFTextRun rt = tpara.getTextRuns().get(0);
                 assertEquals(40, rt.getFontSize(), 0);
-                assertEquals(true, rt.isUnderlined());
+                assertTrue(rt.isUnderlined());
                 assertEquals("Arial", rt.getFontFamily());
-            } else if (tpara.getRunType() == TextHeaderAtom.BODY_TYPE){
+            } else if (tpara.getRunType() == BODY.nativeId){
                 HSLFTextRun rt = tpara.getTextRuns().get(0);
                 assertEquals(0, tpara.getIndentLevel());
                 assertEquals(32, rt.getFontSize(), 0);
@@ -189,12 +190,12 @@ public final class TestSlideMaster {
 
         for (List<HSLFTextParagraph> tparas : slide.get(1).getTextParagraphs()) {
             HSLFTextParagraph tpara = tparas.get(0);
-            if (tpara.getRunType() == TextHeaderAtom.TITLE_TYPE){
+            if (tpara.getRunType() == TITLE.nativeId){
                 HSLFTextRun rt = tpara.getTextRuns().get(0);
                 assertEquals(48, rt.getFontSize(), 0);
-                assertEquals(true, rt.isItalic());
+                assertTrue(rt.isItalic());
                 assertEquals("Georgia", rt.getFontFamily());
-            } else if (tpara.getRunType() == TextHeaderAtom.BODY_TYPE){
+            } else if (tpara.getRunType() == BODY.nativeId){
                 HSLFTextRun rt;
                 rt = tpara.getTextRuns().get(0);
                 assertEquals(0, tpara.getIndentLevel());
@@ -253,12 +254,12 @@ public final class TestSlideMaster {
 
         for (List<HSLFTextParagraph> tparas : slide.getTextParagraphs()) {
             HSLFTextParagraph tpara = tparas.get(0);
-            if (tpara.getRunType() == TextHeaderAtom.TITLE_TYPE){
+            if (tpara.getRunType() == TITLE.nativeId){
                 HSLFTextRun rt = tpara.getTextRuns().get(0);
                 assertEquals(40, rt.getFontSize(), 0);
-                assertEquals(true, rt.isUnderlined());
+                assertTrue(rt.isUnderlined());
                 assertEquals("Arial", rt.getFontFamily());
-            } else if (tpara.getRunType() == TextHeaderAtom.BODY_TYPE){
+            } else if (tpara.getRunType() == BODY.nativeId){
                 int[] indents = {32, 28, 24};
                 for (HSLFTextRun rt : tpara.getTextRuns()) {
                     int indent = tpara.getIndentLevel();
index 3c7bc9c9a535bbf03727e4363485a18146bee7f2..2dddf749504d7a0ee2736f37ad69922ecad33a49 100644 (file)
@@ -29,7 +29,6 @@ import java.io.IOException;
 import java.util.List;
 
 import org.apache.poi.POIDataSamples;
-import org.apache.poi.hslf.record.TextHeaderAtom;
 import org.apache.poi.hslf.usermodel.HSLFShape;
 import org.apache.poi.hslf.usermodel.HSLFSlide;
 import org.apache.poi.hslf.usermodel.HSLFSlideShow;
@@ -39,6 +38,7 @@ import org.apache.poi.sl.usermodel.Shape;
 import org.apache.poi.sl.usermodel.Slide;
 import org.apache.poi.sl.usermodel.SlideShow;
 import org.apache.poi.sl.usermodel.TableShape;
+import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder;
 import org.junit.Test;
 
 /**
@@ -60,7 +60,7 @@ public final class TestTable {
 
         HSLFTableCell cell = tbl.getCell(0, 0);
         //table cells have type=TextHeaderAtom.OTHER_TYPE, see bug #46033
-        assertEquals(TextHeaderAtom.OTHER_TYPE, cell.getTextParagraphs().get(0).getRunType());
+        assertEquals(TextPlaceholder.OTHER.nativeId, cell.getTextParagraphs().get(0).getRunType());
 
         HSLFShape tblSh = slide.getShapes().get(0);
         assertTrue(tblSh instanceof HSLFTable);
index dbf89692bb13e8fe5aab3761b44a758e4688f2a8..be224a9d240138107a55c1d8eec5cad841761700 100644 (file)
 package org.apache.poi.hslf.record;
 
 
-import org.junit.Test;
-
 import static org.junit.Assert.assertEquals;
 
+import org.apache.poi.ddf.EscherRecordTypes;
+import org.junit.Test;
+
 /**
  * Tests that RecordTypes returns the right records and classes when asked
  */
@@ -35,9 +36,9 @@ public final class TestRecordTypes {
 
     @Test
        public void testEscherNameLookups() {
-               assertEquals("EscherDggContainer", RecordTypes.EscherDggContainer.name());
-               assertEquals("EscherClientTextbox", RecordTypes.EscherClientTextbox.name());
-               assertEquals("EscherSelection", RecordTypes.EscherSelection.name());
+               assertEquals("DGG_CONTAINER", EscherRecordTypes.DGG_CONTAINER.name());
+               assertEquals("CLIENT_TEXTBOX", EscherRecordTypes.CLIENT_TEXTBOX.name());
+               assertEquals("SELECTION", EscherRecordTypes.SELECTION.name());
        }
 
     @Test
@@ -46,11 +47,4 @@ public final class TestRecordTypes {
                // This is checking the "unhandled default" stuff works
                assertEquals(RecordTypes.UnknownRecordPlaceholder, RecordTypes.forTypeID(-10));
        }
-
-    @Test
-    public void testEscherClassLookups() {
-               // Should all come back with null, as DDF handles them
-               assertEquals(null, RecordTypes.EscherDggContainer.recordConstructor);
-               assertEquals(null, RecordTypes.EscherBStoreContainer.recordConstructor);
-       }
 }
index 8ac74534056618b7d3e4577543477149fc0677ea..fb3c47dc2882183b290f7695549a738beee67fc0 100644 (file)
 package org.apache.poi.hslf.record;
 
 
-import junit.framework.TestCase;
 import java.io.ByteArrayOutputStream;
 
+import junit.framework.TestCase;
+import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder;
+
 /**
  * Tests that TextHeaderAtom works properly
  *
@@ -40,9 +42,9 @@ public final class TestTextHeaderAtom extends TestCase {
                TextHeaderAtom n_tha = new TextHeaderAtom(notes_data,0,12);
                TextHeaderAtom t_tha = new TextHeaderAtom(title_data,0,12);
                TextHeaderAtom b_tha = new TextHeaderAtom(body_data,0,12);
-               assertEquals(TextHeaderAtom.NOTES_TYPE, n_tha.getTextType());
-               assertEquals(TextHeaderAtom.TITLE_TYPE, t_tha.getTextType());
-               assertEquals(TextHeaderAtom.BODY_TYPE, b_tha.getTextType());
+               assertEquals(TextPlaceholder.NOTES.nativeId, n_tha.getTextType());
+               assertEquals(TextPlaceholder.TITLE.nativeId, t_tha.getTextType());
+               assertEquals(TextPlaceholder.BODY.nativeId, b_tha.getTextType());
        }
 
        public void testWrite() throws Exception {
index 7e7c9f4d371ca3e7f2443e77c7a3ddaa8805ad2a..30c177f0866a7d3553614b6f2a51ecf05529da34 100644 (file)
@@ -22,10 +22,10 @@ import java.util.List;
 
 import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
-
 import org.apache.poi.hslf.model.textproperties.TextProp;
 import org.apache.poi.hslf.model.textproperties.TextPropCollection;
 import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder;
 
 
 /**
@@ -47,26 +47,26 @@ public final class TestTxMasterStyleAtom extends TestCase {
         TxMasterStyleAtom[] txmaster = getMasterStyles();
         for (final TxMasterStyleAtom atom : txmaster) {
             final int txtype = atom.getTextType();
-            switch (txtype){
-                case TextHeaderAtom.TITLE_TYPE:
+            switch (TextPlaceholder.fromNativeId(txtype)){
+                case TITLE:
                     checkTitleType(atom);
                     break;
-                case TextHeaderAtom.BODY_TYPE:
+                case BODY:
                     checkBodyType(atom);
                     break;
-                case TextHeaderAtom.NOTES_TYPE:
+                case NOTES:
                     checkNotesType(atom);
                     break;
-                case TextHeaderAtom.OTHER_TYPE:
+                case OTHER:
                     checkOtherType(atom);
                     break;
-                case TextHeaderAtom.CENTRE_BODY_TYPE:
+                case CENTER_BODY:
                     break;
-                case TextHeaderAtom.CENTER_TITLE_TYPE:
+                case CENTER_TITLE:
                     break;
-                case TextHeaderAtom.HALF_BODY_TYPE:
+                case HALF_BODY:
                     break;
-                case TextHeaderAtom.QUARTER_BODY_TYPE:
+                case QUARTER_BODY:
                     break;
                 default:
                     fail("Unknown text type: " + txtype);
@@ -230,7 +230,7 @@ public final class TestTxMasterStyleAtom extends TestCase {
                 }
 
                 assertEquals("Document.Environment must contain TxMasterStyleAtom  with type=TextHeaderAtom.OTHER_TYPE",
-                        TextHeaderAtom.OTHER_TYPE, txstyle.getTextType());
+                        TextPlaceholder.OTHER.nativeId, txstyle.getTextType());
                 lst.add(txstyle);
             }
         }
index 8499ed7b48b8f98f4a2c880f6495835f735732a7..94ee89002a1a16686e0778fe0a32fab698fc0c18 100644 (file)
@@ -32,8 +32,8 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.poi.hslf.HSLFTestDataSamples;
-import org.apache.poi.hslf.record.TextHeaderAtom;
 import org.apache.poi.sl.usermodel.ShapeType;
+import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder;
 import org.junit.Test;
 
 /**
@@ -90,9 +90,9 @@ public final class TestTextShape {
                     assertEquals("Text in a TextBox", rawText);
                     break;
                 case RECT:
-                    if(runType == TextHeaderAtom.OTHER_TYPE) {
+                    if(runType == TextPlaceholder.OTHER.nativeId) {
                         assertEquals("Rectangle", rawText);
-                    } else if(runType == TextHeaderAtom.TITLE_TYPE) {
+                    } else if(runType == TextPlaceholder.TITLE.nativeId) {
                         assertEquals("Title Placeholder", rawText);
                     }
                     break;
index 64fb8563b3de2c60f3fece1b38b377f3282731ea..1e2b464015a4e0fdaf08a76f00a80a262bf6d59a 100644 (file)
@@ -20,44 +20,22 @@ package org.apache.poi.hwmf;
 import static org.apache.poi.POITestCase.assertContains;
 import static org.junit.Assert.assertEquals;
 
-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.File;
-import java.io.FileFilter;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.URL;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
-import java.util.Locale;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-
-import javax.imageio.ImageIO;
 
 import org.apache.poi.POIDataSamples;
-import org.apache.poi.hwmf.record.HwmfFill.HwmfImageRecord;
 import org.apache.poi.hwmf.record.HwmfFont;
 import org.apache.poi.hwmf.record.HwmfRecord;
 import org.apache.poi.hwmf.record.HwmfRecordType;
 import org.apache.poi.hwmf.record.HwmfText;
-import org.apache.poi.hwmf.usermodel.HwmfEmbedded;
 import org.apache.poi.hwmf.usermodel.HwmfPicture;
-import org.apache.poi.sl.usermodel.PictureData;
-import org.apache.poi.sl.usermodel.PictureData.PictureType;
-import org.apache.poi.sl.usermodel.SlideShow;
-import org.apache.poi.sl.usermodel.SlideShowFactory;
 import org.apache.poi.util.LocaleUtil;
 import org.apache.poi.util.RecordFormatException;
-import org.apache.poi.util.Units;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -65,6 +43,9 @@ public class TestHwmfParsing {
 
     private static final POIDataSamples samples = POIDataSamples.getSlideShowInstance();
 
+    // ******************************************************************************
+    // for manual mass parsing and rendering tests of .wmfs use HemfPictureTest.paint() !
+    // ******************************************************************************
 
     @Test
     public void parse() throws IOException {
@@ -82,158 +63,6 @@ public class TestHwmfParsing {
         }
     }
 
-    @Test
-    @Ignore("This is work-in-progress and not a real unit test ...")
-    public void paint() throws IOException {
-        boolean dumpEmbedded = true;
-        boolean dumpRecords = false;
-
-        File f = new File("testme.wmf");
-        FileInputStream fis = new FileInputStream(f);
-        HwmfPicture wmf = new HwmfPicture(fis);
-        fis.close();
-        
-        Dimension2D dim = wmf.getSize();
-        double width = Units.pointsToPixel(dim.getWidth());
-        // keep aspect ratio for height
-        double height = Units.pointsToPixel(dim.getHeight());
-        double scale = (width > height) ? 1500 / width : 1500 / width;
-        width = Math.abs(width * scale);
-        height = Math.abs(height * scale);
-
-        BufferedImage bufImg = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_ARGB);
-        Graphics2D g = bufImg.createGraphics();
-        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
-        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
-        g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
-        
-        wmf.draw(g, new Rectangle2D.Double(0,0,width,height));
-
-        g.dispose();
-        
-        ImageIO.write(bufImg, "PNG", new File("bla.png"));
-
-        if (dumpEmbedded) {
-            int embIdx = 0;
-            for (HwmfEmbedded emb : wmf.getEmbeddings()) {
-                final File embName = new File("build/tmp", "emb_"+embIdx + emb.getEmbeddedType().extension);
-                try (FileOutputStream fos = new FileOutputStream(embName)) {
-                    fos.write(emb.getRawData());
-                }
-                embIdx++;
-            }
-        }
-
-        if (dumpRecords) {
-            try (FileWriter fw = new FileWriter("wmf-records.log")) {
-                for (HwmfRecord r : wmf.getRecords()) {
-                    fw.write(r.getWmfRecordType().name());
-                    fw.write(":");
-                    fw.write(r.toString());
-                    fw.write("\n");
-                }
-            }
-        }
-    }
-
-    @Test
-    @Ignore("This is work-in-progress and not a real unit test ...")
-    public void fetchWmfFromGovdocs() throws IOException {
-        URL url = new URL("http://digitalcorpora.org/corpora/files/govdocs1/by_type/ppt.zip");
-        File outdir = new File("build/ppt");
-        outdir.mkdirs();
-        ZipInputStream zis = new ZipInputStream(url.openStream());
-        ZipEntry ze;
-        while ((ze = zis.getNextEntry()) != null) {
-            String basename = ze.getName().replaceAll(".*?([^/]+)\\.wmf", "$1");
-            FilterInputStream fis = new FilterInputStream(zis){
-                @Override
-                public void close() throws IOException {}
-            };
-            try {
-                SlideShow<?,?> ss = SlideShowFactory.create(fis);
-                int wmfIdx = 1;
-                for (PictureData pd : ss.getPictureData()) {
-                    if (pd.getType() != PictureType.WMF) {
-                        continue;
-                    }
-                    byte[] wmfData = pd.getData();
-                    String filename = String.format(Locale.ROOT, "%s-%04d.wmf", basename, wmfIdx);
-                    FileOutputStream fos = new FileOutputStream(new File(outdir, filename));
-                    fos.write(wmfData);
-                    fos.close();
-                    wmfIdx++;
-                }
-                ss.close();
-            } catch (Exception e) {
-                System.out.println(ze.getName()+" ignored.");
-            }
-        }
-    }
-
-    @Test
-    @Ignore("This is work-in-progress and not a real unit test ...")
-    public void parseWmfs() throws IOException {
-        // parse and render the extracted wmfs from the fetchWmfFromGovdocs step
-        boolean outputFiles = false;
-        boolean renderWmf = true;
-        File indir = new File("E:\\project\\poi\\misc\\govdocs-ppt");
-        File outdir = new File("build/wmf");
-        outdir.mkdirs();
-        final String startFile = "";
-        File[] files = indir.listFiles(new FileFilter() {
-            boolean foundStartFile;
-
-            @Override
-            public boolean accept(File pathname) {
-                foundStartFile |= startFile.isEmpty() || pathname.getName().contains(startFile);
-                return foundStartFile && pathname.getName().matches("(?i).*\\.wmf?$");
-            }
-        });
-        for (File f : files) {
-            try {
-                String basename = f.getName().replaceAll(".*?([^/]+)\\.wmf", "$1");
-                FileInputStream fis = new FileInputStream(f);
-                HwmfPicture wmf = new HwmfPicture(fis);
-                fis.close();
-                
-                int bmpIndex = 1;
-                for (HwmfRecord r : wmf.getRecords()) {
-                    if (r instanceof HwmfImageRecord) {
-                        BufferedImage bi = ((HwmfImageRecord)r).getImage();
-                        if (bi != null && outputFiles) {
-                            String filename = String.format(Locale.ROOT, "%s-%04d.png", basename, bmpIndex);
-                            ImageIO.write(bi, "PNG", new File(outdir, filename));
-                        }
-                        bmpIndex++;
-                    }
-                }
-
-                if (renderWmf) {
-                    Dimension2D dim = wmf.getSize();
-                    int width = Units.pointsToPixel(dim.getWidth());
-                    // keep aspect ratio for height
-                    int height = Units.pointsToPixel(dim.getHeight());
-
-                    BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-                    Graphics2D g = bufImg.createGraphics();
-                    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-                    g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
-                    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
-                    g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
-                    
-                    wmf.draw(g);
-
-                    g.dispose();
-                    
-                    ImageIO.write(bufImg, "PNG", new File(outdir, basename+".png"));
-                }
-            } catch (Exception e) {
-                System.out.println(f.getName()+" ignored.");                
-            }
-        }
-    }
 
     @Test
     @Ignore("If we decide we can use common crawl file specified, we can turn this back on")
index dd57d837e690d7df973a027ddb5d32c7c8f2196e..d5264c8339c1f1f0e16ce302e2d6fa6eea62e899 100644 (file)
@@ -161,6 +161,8 @@ public final class TestEscherContainerRecord {
         public String getRecordName() { return ""; }
         @Override
                protected Object[][] getAttributeMap() { return null; }
+               @Override
+               public Enum getGenericRecordType() { return EscherRecordTypes.UNKNOWN; }
        }
 
     @Test