From 1d4b05ff60cbb1438d1a369025762bf0d50c7989 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Wed, 11 Sep 2019 21:24:06 +0000 Subject: [PATCH] #63745 - Add traversing and debugging interface git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1866808 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/common/usermodel/GenericRecord.java | 32 ++ .../common/usermodel/fonts/FontHeader.java | 211 ++++++-- .../poi/ddf/AbstractEscherOptRecord.java | 44 +- .../org/apache/poi/ddf/EscherBSERecord.java | 34 +- .../org/apache/poi/ddf/EscherBitmapBlip.java | 21 +- .../org/apache/poi/ddf/EscherBlipRecord.java | 26 +- .../poi/ddf/EscherChildAnchorRecord.java | 29 +- .../poi/ddf/EscherClientAnchorRecord.java | 35 +- .../poi/ddf/EscherClientDataRecord.java | 22 +- .../apache/poi/ddf/EscherContainerRecord.java | 47 +- .../org/apache/poi/ddf/EscherDgRecord.java | 28 +- .../org/apache/poi/ddf/EscherDggRecord.java | 38 +- .../apache/poi/ddf/EscherMetafileBlip.java | 31 +- .../org/apache/poi/ddf/EscherOptRecord.java | 14 +- .../org/apache/poi/ddf/EscherPictBlip.java | 23 +- src/java/org/apache/poi/ddf/EscherRecord.java | 22 +- .../org/apache/poi/ddf/EscherRecordTypes.java | 92 ++++ .../org/apache/poi/ddf/EscherSpRecord.java | 62 ++- .../org/apache/poi/ddf/EscherSpgrRecord.java | 29 +- .../poi/ddf/EscherSplitMenuColorsRecord.java | 29 +- .../poi/ddf/EscherTertiaryOptRecord.java | 12 +- .../apache/poi/ddf/EscherTextboxRecord.java | 22 +- .../apache/poi/ddf/UnknownEscherRecord.java | 16 + .../poi/hssf/usermodel/HSSFShapeFactory.java | 8 +- .../poi/hssf/usermodel/HSSFShapeGroup.java | 34 +- .../org/apache/poi/poifs/crypt/Decryptor.java | 16 +- .../poi/poifs/crypt/EncryptionHeader.java | 24 +- .../poi/poifs/crypt/EncryptionInfo.java | 33 +- .../poi/poifs/crypt/EncryptionVerifier.java | 23 +- .../org/apache/poi/poifs/crypt/Encryptor.java | 13 +- .../apache/poi/sl/draw/DrawPictureShape.java | 4 +- .../apache/poi/sl/draw/DrawSimpleShape.java | 12 +- .../org/apache/poi/sl/draw/ImageRenderer.java | 5 + .../apache/poi/sl/usermodel/TextShape.java | 33 +- .../poi/util/GenericRecordJsonWriter.java | 455 ++++++++++++++++++ .../apache/poi/util/GenericRecordUtil.java | 142 ++++++ .../crypt/agile/AgileEncryptionHeader.java | 21 +- .../org/apache/poi/xslf/util/PPTX2PNG.java | 317 ++++++++++-- .../poi/xslf/usermodel/TestPPTX2PNG.java | 8 +- .../poi/hemf/draw/HemfImageRenderer.java | 6 +- .../poi/hemf/record/emf/HemfComment.java | 110 ++++- .../apache/poi/hemf/record/emf/HemfDraw.java | 233 +++++++-- .../apache/poi/hemf/record/emf/HemfFill.java | 226 +++++++-- .../apache/poi/hemf/record/emf/HemfFont.java | 64 ++- .../poi/hemf/record/emf/HemfHeader.java | 48 +- .../apache/poi/hemf/record/emf/HemfMisc.java | 234 ++++++++- .../poi/hemf/record/emf/HemfPalette.java | 74 +++ .../poi/hemf/record/emf/HemfRecord.java | 17 +- .../apache/poi/hemf/record/emf/HemfText.java | 67 ++- .../poi/hemf/record/emf/HemfWindowing.java | 84 +++- .../record/emf/UnimplementedHemfRecord.java | 2 +- .../hemf/record/emfplus/HemfPlusBrush.java | 182 ++++--- .../poi/hemf/record/emfplus/HemfPlusDraw.java | 150 ++++-- .../poi/hemf/record/emfplus/HemfPlusFont.java | 28 +- .../hemf/record/emfplus/HemfPlusHeader.java | 27 +- .../hemf/record/emfplus/HemfPlusImage.java | 55 ++- .../poi/hemf/record/emfplus/HemfPlusMisc.java | 75 +++ .../hemf/record/emfplus/HemfPlusObject.java | 37 +- .../poi/hemf/record/emfplus/HemfPlusPath.java | 44 +- .../poi/hemf/record/emfplus/HemfPlusPen.java | 96 +++- .../hemf/record/emfplus/HemfPlusRecord.java | 3 +- .../hemf/record/emfplus/HemfPlusRegion.java | 42 +- .../emfplus/UnimplementedHemfPlusRecord.java | 11 + .../poi/hemf/usermodel/HemfPicture.java | 15 +- .../model/textproperties/BitMaskTextProp.java | 16 +- .../textproperties/FontAlignmentProp.java | 29 ++ .../model/textproperties/HSLFTabStop.java | 15 +- .../HSLFTabStopPropCollection.java | 13 +- .../hslf/model/textproperties/IndentProp.java | 15 +- .../textproperties/TextAlignmentProp.java | 32 ++ .../textproperties/TextPFException9.java | 48 +- .../hslf/model/textproperties/TextProp.java | 17 +- .../textproperties/TextPropCollection.java | 17 +- .../poi/hslf/record/AnimationInfoAtom.java | 54 ++- .../org/apache/poi/hslf/record/CString.java | 13 +- .../poi/hslf/record/ColorSchemeAtom.java | 18 + .../apache/poi/hslf/record/Comment2000.java | 3 +- .../poi/hslf/record/Comment2000Atom.java | 13 + .../poi/hslf/record/DocInfoListContainer.java | 8 +- .../apache/poi/hslf/record/DocumentAtom.java | 79 ++- .../hslf/record/DocumentEncryptionAtom.java | 11 +- .../poi/hslf/record/EscherPlaceholder.java | 31 ++ .../poi/hslf/record/EscherTextboxWrapper.java | 17 +- .../apache/poi/hslf/record/ExControlAtom.java | 7 + .../apache/poi/hslf/record/ExEmbedAtom.java | 24 + .../apache/poi/hslf/record/ExHyperlink.java | 5 +- .../poi/hslf/record/ExHyperlinkAtom.java | 8 + .../apache/poi/hslf/record/ExMediaAtom.java | 18 + .../org/apache/poi/hslf/record/ExObjList.java | 2 +- .../apache/poi/hslf/record/ExObjListAtom.java | 10 + .../apache/poi/hslf/record/ExObjRefAtom.java | 11 +- .../apache/poi/hslf/record/ExOleObjAtom.java | 58 ++- .../apache/poi/hslf/record/ExOleObjStg.java | 12 + .../poi/hslf/record/FontCollection.java | 6 + .../poi/hslf/record/FontEmbeddedData.java | 8 + .../poi/hslf/record/FontEntityAtom.java | 31 ++ .../poi/hslf/record/HeadersFootersAtom.java | 59 ++- .../hslf/record/HeadersFootersContainer.java | 41 +- .../poi/hslf/record/InteractiveInfo.java | 2 +- .../poi/hslf/record/InteractiveInfoAtom.java | 59 +++ .../poi/hslf/record/MasterTextPropAtom.java | 10 + .../org/apache/poi/hslf/record/NotesAtom.java | 18 +- .../poi/hslf/record/OEPlaceholderAtom.java | 12 + .../poi/hslf/record/OutlineTextRefAtom.java | 15 +- .../org/apache/poi/hslf/record/PPDrawing.java | 220 ++++----- .../poi/hslf/record/PPDrawingGroup.java | 26 +- .../poi/hslf/record/PersistPtrHolder.java | 16 +- .../org/apache/poi/hslf/record/Record.java | 17 +- .../poi/hslf/record/RecordContainer.java | 7 +- .../apache/poi/hslf/record/RecordTypes.java | 38 +- .../hslf/record/RoundTripHFPlaceholder12.java | 8 + .../poi/hslf/record/SSSlideInfoAtom.java | 39 +- .../org/apache/poi/hslf/record/SlideAtom.java | 15 + .../poi/hslf/record/SlideAtomLayout.java | 13 +- .../poi/hslf/record/SlideListWithText.java | 2 +- .../poi/hslf/record/SlidePersistAtom.java | 83 ++-- .../org/apache/poi/hslf/record/SoundData.java | 8 + .../poi/hslf/record/StyleTextProp9Atom.java | 10 + .../poi/hslf/record/StyleTextPropAtom.java | 17 +- .../apache/poi/hslf/record/TextBytesAtom.java | 10 + .../apache/poi/hslf/record/TextCharsAtom.java | 10 + .../poi/hslf/record/TextHeaderAtom.java | 33 +- .../apache/poi/hslf/record/TextRulerAtom.java | 14 + .../poi/hslf/record/TextSpecInfoAtom.java | 10 + .../poi/hslf/record/TextSpecInfoRun.java | 48 +- .../hslf/record/TxInteractiveInfoAtom.java | 17 +- .../poi/hslf/record/TxMasterStyleAtom.java | 21 +- .../hslf/record/UnknownRecordPlaceholder.java | 8 + .../apache/poi/hslf/record/UserEditAtom.java | 21 +- .../apache/poi/hslf/record/VBAInfoAtom.java | 13 +- .../poi/hslf/record/VBAInfoContainer.java | 2 +- .../apache/poi/hslf/usermodel/HSLFFill.java | 4 +- .../apache/poi/hslf/usermodel/HSLFShape.java | 10 +- .../poi/hslf/usermodel/HSLFShapeFactory.java | 3 +- .../apache/poi/hslf/usermodel/HSLFSlide.java | 20 +- .../poi/hslf/usermodel/HSLFSlideMaster.java | 17 +- .../poi/hslf/usermodel/HSLFSlideShow.java | 14 +- .../apache/poi/hslf/usermodel/HSLFTable.java | 10 +- .../poi/hslf/usermodel/HSLFTextParagraph.java | 19 +- .../poi/hslf/usermodel/HSLFTextShape.java | 50 +- .../apache/poi/hwmf/draw/HwmfGraphics.java | 21 +- .../poi/hwmf/draw/HwmfImageRenderer.java | 7 +- .../apache/poi/hwmf/record/HwmfBitmap16.java | 40 +- .../apache/poi/hwmf/record/HwmfBitmapDib.java | 52 +- .../apache/poi/hwmf/record/HwmfColorRef.java | 15 +- .../org/apache/poi/hwmf/record/HwmfDraw.java | 388 ++++++++------- .../apache/poi/hwmf/record/HwmfEscape.java | 56 ++- .../org/apache/poi/hwmf/record/HwmfFill.java | 328 ++++++++++--- .../org/apache/poi/hwmf/record/HwmfFont.java | 125 +++-- .../apache/poi/hwmf/record/HwmfHeader.java | 28 +- .../org/apache/poi/hwmf/record/HwmfMisc.java | 183 ++++++- .../apache/poi/hwmf/record/HwmfPalette.java | 67 ++- .../apache/poi/hwmf/record/HwmfPenStyle.java | 27 +- .../apache/poi/hwmf/record/HwmfRecord.java | 8 +- .../org/apache/poi/hwmf/record/HwmfText.java | 113 ++++- .../apache/poi/hwmf/record/HwmfWindowing.java | 172 ++++++- .../poi/hwmf/usermodel/HwmfPicture.java | 27 +- .../poi/hslf/model/TestSlideMaster.java | 101 ++-- .../org/apache/poi/hslf/model/TestTable.java | 4 +- .../poi/hslf/record/TestRecordTypes.java | 18 +- .../poi/hslf/record/TestTextHeaderAtom.java | 10 +- .../hslf/record/TestTxMasterStyleAtom.java | 22 +- .../poi/hslf/usermodel/TestTextShape.java | 6 +- .../org/apache/poi/hwmf/TestHwmfParsing.java | 177 +------ .../poi/ddf/TestEscherContainerRecord.java | 2 + 165 files changed, 6091 insertions(+), 1641 deletions(-) create mode 100644 src/java/org/apache/poi/common/usermodel/GenericRecord.java create mode 100644 src/java/org/apache/poi/ddf/EscherRecordTypes.java create mode 100644 src/java/org/apache/poi/util/GenericRecordJsonWriter.java create mode 100644 src/java/org/apache/poi/util/GenericRecordUtil.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 index 0000000000..e029bf965c --- /dev/null +++ b/src/java/org/apache/poi/common/usermodel/GenericRecord.java @@ -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> getGenericProperties(); + + default List getGenericChildren() { return null; } +} diff --git a/src/java/org/apache/poi/common/usermodel/fonts/FontHeader.java b/src/java/org/apache/poi/common/usermodel/fonts/FontHeader.java index 9777f0cc75..2589b16f17 100644 --- a/src/java/org/apache/poi/common/usermodel/fonts/FontHeader.java +++ b/src/java/org/apache/poi/common/usermodel/fonts/FontHeader.java @@ -17,10 +17,18 @@ 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 Embedded OpenType (EOT) File Format */ -@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> getGenericProperties() { + final Map> 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(); + } +} diff --git a/src/java/org/apache/poi/ddf/AbstractEscherOptRecord.java b/src/java/org/apache/poi/ddf/AbstractEscherOptRecord.java index bd1da1db8d..b3643ff172 100644 --- a/src/java/org/apache/poi/ddf/AbstractEscherOptRecord.java +++ b/src/java/org/apache/poi/ddf/AbstractEscherOptRecord.java @@ -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 properties = new ArrayList<>(); + private final List 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() { - @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 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 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "isContainer", this::isContainerRecord + ); + } } diff --git a/src/java/org/apache/poi/ddf/EscherBSERecord.java b/src/java/org/apache/poi/ddf/EscherBSERecord.java index d3e0514be7..c00c0e2812 100644 --- a/src/java/org/apache/poi/ddf/EscherBSERecord.java +++ b/src/java/org/apache/poi/ddf/EscherBSERecord.java @@ -17,6 +17,11 @@ 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> getGenericProperties() { + final Map> 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; + } } diff --git a/src/java/org/apache/poi/ddf/EscherBitmapBlip.java b/src/java/org/apache/poi/ddf/EscherBitmapBlip.java index 05ad9ae66f..04f0843349 100644 --- a/src/java/org/apache/poi/ddf/EscherBitmapBlip.java +++ b/src/java/org/apache/poi/ddf/EscherBitmapBlip.java @@ -17,12 +17,17 @@ 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> getGenericProperties() { + final Map> m = new LinkedHashMap<>(super.getGenericProperties()); + m.put("uid", this::getUID); + m.put("marker", this::getMarker); + return Collections.unmodifiableMap(m); + } + } diff --git a/src/java/org/apache/poi/ddf/EscherBlipRecord.java b/src/java/org/apache/poi/ddf/EscherBlipRecord.java index f49f09f0e8..41464b3c19 100644 --- a/src/java/org/apache/poi/ddf/EscherBlipRecord.java +++ b/src/java/org/apache/poi/ddf/EscherBlipRecord.java @@ -17,6 +17,10 @@ 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> 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; + } } diff --git a/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java b/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java index b7a7a8d78c..152ab213f7 100644 --- a/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java +++ b/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java @@ -18,6 +18,10 @@ 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> 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; + } } diff --git a/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java b/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java index 741ea0fdb4..3662d5bd77 100644 --- a/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java +++ b/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java @@ -17,6 +17,11 @@ 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> getGenericProperties() { + final Map> 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; + } } diff --git a/src/java/org/apache/poi/ddf/EscherClientDataRecord.java b/src/java/org/apache/poi/ddf/EscherClientDataRecord.java index 801098854d..468d229972 100644 --- a/src/java/org/apache/poi/ddf/EscherClientDataRecord.java +++ b/src/java/org/apache/poi/ddf/EscherClientDataRecord.java @@ -18,6 +18,10 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "remainingData", this::getRemainingData + ); + } + + @Override + public Enum getGenericRecordType() { + return EscherRecordTypes.CLIENT_DATA; + } } diff --git a/src/java/org/apache/poi/ddf/EscherContainerRecord.java b/src/java/org/apache/poi/ddf/EscherContainerRecord.java index d7889015d5..bb487a5fc8 100644 --- a/src/java/org/apache/poi/ddf/EscherContainerRecord.java +++ b/src/java/org/apache/poi/ddf/EscherContainerRecord.java @@ -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 { - 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "isContainer", this::isContainerRecord + ); + } + + @Override + public Enum getGenericRecordType() { + return EscherRecordTypes.forTypeID(getRecordId()); + } } diff --git a/src/java/org/apache/poi/ddf/EscherDgRecord.java b/src/java/org/apache/poi/ddf/EscherDgRecord.java index 956f0f6478..3af8c69183 100644 --- a/src/java/org/apache/poi/ddf/EscherDgRecord.java +++ b/src/java/org/apache/poi/ddf/EscherDgRecord.java @@ -18,17 +18,18 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "numShapes", this::getNumShapes, + "lastMSOSPID", this::getLastMSOSPID, + "drawingGroupId", this::getDrawingGroupId + ); + } + + @Override + public Enum getGenericRecordType() { + return EscherRecordTypes.DG; + } } diff --git a/src/java/org/apache/poi/ddf/EscherDggRecord.java b/src/java/org/apache/poi/ddf/EscherDggRecord.java index d8ed75ec67..608947e03c 100644 --- a/src/java/org/apache/poi/ddf/EscherDggRecord.java +++ b/src/java/org/apache/poi/ddf/EscherDggRecord.java @@ -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 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> 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "fileIdClusters", () -> field_5_fileIdClusters, + "shapeIdMax", this::getShapeIdMax, + "numIdClusters", this::getNumIdClusters, + "numShapesSaved", this::getNumShapesSaved, + "drawingsSaved", this::getDrawingsSaved + ); + } } diff --git a/src/java/org/apache/poi/ddf/EscherMetafileBlip.java b/src/java/org/apache/poi/ddf/EscherMetafileBlip.java index fcc6d92b05..fc5affac94 100644 --- a/src/java/org/apache/poi/ddf/EscherMetafileBlip.java +++ b/src/java/org/apache/poi/ddf/EscherMetafileBlip.java @@ -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> getGenericProperties() { + final Map> 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); + } } diff --git a/src/java/org/apache/poi/ddf/EscherOptRecord.java b/src/java/org/apache/poi/ddf/EscherOptRecord.java index f941c672e8..f1ac813aa1 100644 --- a/src/java/org/apache/poi/ddf/EscherOptRecord.java +++ b/src/java/org/apache/poi/ddf/EscherOptRecord.java @@ -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; + } } diff --git a/src/java/org/apache/poi/ddf/EscherPictBlip.java b/src/java/org/apache/poi/ddf/EscherPictBlip.java index 69cf60138f..44bfe1eca0 100644 --- a/src/java/org/apache/poi/ddf/EscherPictBlip.java +++ b/src/java/org/apache/poi/ddf/EscherPictBlip.java @@ -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> getGenericProperties() { + final Map> 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); + } } diff --git a/src/java/org/apache/poi/ddf/EscherRecord.java b/src/java/org/apache/poi/ddf/EscherRecord.java index 45fce36427..ff97b87ce3 100644 --- a/src/java/org/apache/poi/ddf/EscherRecord.java +++ b/src/java/org/apache/poi/ddf/EscherRecord.java @@ -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 getGenericChildren() { + return getChildRecords(); + } + + @Override + public Map> 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 index 0000000000..0080c23a7b --- /dev/null +++ b/src/java/org/apache/poi/ddf/EscherRecordTypes.java @@ -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 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; + } + +} diff --git a/src/java/org/apache/poi/ddf/EscherSpRecord.java b/src/java/org/apache/poi/ddf/EscherSpRecord.java index b08ff4dde8..ad1ce992e3 100644 --- a/src/java/org/apache/poi/ddf/EscherSpRecord.java +++ b/src/java/org/apache/poi/ddf/EscherSpRecord.java @@ -17,6 +17,12 @@ 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> 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; + } } diff --git a/src/java/org/apache/poi/ddf/EscherSpgrRecord.java b/src/java/org/apache/poi/ddf/EscherSpgrRecord.java index 199439cedc..49979e5a05 100644 --- a/src/java/org/apache/poi/ddf/EscherSpgrRecord.java +++ b/src/java/org/apache/poi/ddf/EscherSpgrRecord.java @@ -17,6 +17,10 @@ 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> 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; + } } diff --git a/src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java b/src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java index 25071c0ea6..b6d2ba4af0 100644 --- a/src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java +++ b/src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java @@ -17,6 +17,10 @@ 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> 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; + } } diff --git a/src/java/org/apache/poi/ddf/EscherTertiaryOptRecord.java b/src/java/org/apache/poi/ddf/EscherTertiaryOptRecord.java index 6182942119..0677f15c2e 100644 --- a/src/java/org/apache/poi/ddf/EscherTertiaryOptRecord.java +++ b/src/java/org/apache/poi/ddf/EscherTertiaryOptRecord.java @@ -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; } } diff --git a/src/java/org/apache/poi/ddf/EscherTextboxRecord.java b/src/java/org/apache/poi/ddf/EscherTextboxRecord.java index fbf37f49b0..16213b8a3e 100644 --- a/src/java/org/apache/poi/ddf/EscherTextboxRecord.java +++ b/src/java/org/apache/poi/ddf/EscherTextboxRecord.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "isContainer", this::isContainerRecord, + "extraData", this::getData + ); + } } diff --git a/src/java/org/apache/poi/ddf/UnknownEscherRecord.java b/src/java/org/apache/poi/ddf/UnknownEscherRecord.java index 38475be818..8db83cd46f 100644 --- a/src/java/org/apache/poi/ddf/UnknownEscherRecord.java +++ b/src/java/org/apache/poi/ddf/UnknownEscherRecord.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "data", this::getData + ); + } + + @Override + public Enum getGenericRecordType() { + return EscherRecordTypes.UNKNOWN; + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java index 5659545745..64a027be1f 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeFactory.java @@ -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: diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java index 69036df6f1..1787baaccd 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java @@ -17,13 +17,28 @@ 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 iterator = shapes.iterator(); iterator.hasNext(); ) { - HSSFShape shape = iterator.next(); + for (HSSFShape shape : shapes) { count += shape.countOfAllChildren(); } return count; diff --git a/src/java/org/apache/poi/poifs/crypt/Decryptor.java b/src/java/org/apache/poi/poifs/crypt/Decryptor.java index 6e9e362319..07c5666e6b 100644 --- a/src/java/org/apache/poi/poifs/crypt/Decryptor.java +++ b/src/java/org/apache/poi/poifs/crypt/Decryptor.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "secretKey", secretKey::getEncoded, + "verifier", this::getVerifier, + "integrityHmacKey", this::getIntegrityHmacKey, + "integrityHmacValue", this::getIntegrityHmacValue + ); + } } \ No newline at end of file diff --git a/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java b/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java index a00ac44c82..6518281d6f 100644 --- a/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java +++ b/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java @@ -16,13 +16,19 @@ ==================================================================== */ 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> getGenericProperties() { + final Map> 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); + } } diff --git a/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java b/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java index abd955a870..826b57e419 100644 --- a/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java +++ b/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java @@ -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> getGenericProperties() { + final Map> 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 diff --git a/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java b/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java index 3601e01022..0f3e44b10b 100644 --- a/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java +++ b/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java @@ -16,10 +16,17 @@ ==================================================================== */ package org.apache.poi.poifs.crypt; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Supplier; + +import org.apache.poi.common.usermodel.GenericRecord; + /** * Used when checking if a key is valid for a document */ -public abstract class EncryptionVerifier implements Cloneable { +public abstract class EncryptionVerifier implements Cloneable, GenericRecord { private byte[] salt; private byte[] encryptedVerifier; private byte[] encryptedVerifierHash; @@ -105,4 +112,18 @@ public abstract class EncryptionVerifier implements Cloneable { other.encryptedKey = (encryptedKey == null) ? null : encryptedKey.clone(); return other; } + + @Override + public Map> getGenericProperties() { + final Map> 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); + } } diff --git a/src/java/org/apache/poi/poifs/crypt/Encryptor.java b/src/java/org/apache/poi/poifs/crypt/Encryptor.java index b62ad862fe..8c241be780 100644 --- a/src/java/org/apache/poi/poifs/crypt/Encryptor.java +++ b/src/java/org/apache/poi/poifs/crypt/Encryptor.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "secretKey", secretKey::getEncoded + ); + } } diff --git a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java index 2a1d1effe9..83b50c22db 100644 --- a/src/java/org/apache/poi/sl/draw/DrawPictureShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawPictureShape.java @@ -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; } diff --git a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java index c77cf32d08..cc50ed737c 100644 --- a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java @@ -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) { diff --git a/src/java/org/apache/poi/sl/draw/ImageRenderer.java b/src/java/org/apache/poi/sl/draw/ImageRenderer.java index 4703483b51..c657bbadfd 100644 --- a/src/java/org/apache/poi/sl/draw/ImageRenderer.java +++ b/src/java/org/apache/poi/sl/draw/ImageRenderer.java @@ -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 diff --git a/src/java/org/apache/poi/sl/usermodel/TextShape.java b/src/java/org/apache/poi/sl/usermodel/TextShape.java index 9342607434..8219836281 100644 --- a/src/java/org/apache/poi/sl/usermodel/TextShape.java +++ b/src/java/org/apache/poi/sl/usermodel/TextShape.java @@ -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 index 0000000000..1598d58ae3 --- /dev/null +++ b/src/java/org/apache/poi/util/GenericRecordJsonWriter.java @@ -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>> 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 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> prop = record.getGenericProperties(); + if (prop != null) { + final int oldChildIndex = childIndex; + childIndex = 0; + prop.forEach(this::writeProp); + childIndex = oldChildIndex; + } + + fw.println(); + List 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> + getGenericProperties(String val1, Supplier sup1) { + return Collections.unmodifiableMap(Collections.singletonMap(val1, sup1)); + } + + public static Map> 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> 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> 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> 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> 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> 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>)baseMap); + } else { + m.put(vals[i], sups[i]); + } + } + + return Collections.unmodifiableMap(m); + } + + public static Supplier safeEnum(T[] values, Supplier ordinal) { + return safeEnum(values, ordinal, null); + } + + public static Supplier safeEnum(T[] values, Supplier ordinal, T defaultVal) { + int ord = ordinal.get().intValue(); + return () -> (0 <= ord && ord < values.length) ? values[ord] : defaultVal; + } + + public static Supplier getBitsAsString(Supplier flags, final int[] masks, final String[] names) { + return () -> new AnnotatedFlag(flags, masks, names); + } + + public static class AnnotatedFlag { + private final Supplier value; + private final Map masks = new LinkedHashMap<>(); + + AnnotatedFlag(Supplier value, int[] masks, String[] names) { + assert(masks.length == names.length); + + this.value = value; + for (int i=0; i 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; + } + } +} diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java b/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java index ed058d1df0..d943014bc0 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java @@ -16,16 +16,19 @@ ==================================================================== */ package org.apache.poi.poifs.crypt.agile; -import org.apache.poi.EncryptedDocumentException; -import org.apache.poi.poifs.crypt.ChainingMode; -import org.apache.poi.poifs.crypt.CipherAlgorithm; -import org.apache.poi.poifs.crypt.EncryptionHeader; -import org.apache.poi.poifs.crypt.HashAlgorithm; +import java.util.Map; +import java.util.function.Supplier; import com.microsoft.schemas.office.x2006.encryption.CTDataIntegrity; import com.microsoft.schemas.office.x2006.encryption.CTKeyData; import com.microsoft.schemas.office.x2006.encryption.EncryptionDocument; import com.microsoft.schemas.office.x2006.encryption.STCipherChaining; +import org.apache.poi.EncryptedDocumentException; +import org.apache.poi.poifs.crypt.ChainingMode; +import org.apache.poi.poifs.crypt.CipherAlgorithm; +import org.apache.poi.poifs.crypt.EncryptionHeader; +import org.apache.poi.poifs.crypt.HashAlgorithm; +import org.apache.poi.util.GenericRecordUtil; public class AgileEncryptionHeader extends EncryptionHeader implements Cloneable { private byte[] encryptedHmacKey; @@ -132,4 +135,12 @@ public class AgileEncryptionHeader extends EncryptionHeader implements Cloneable return other; } + @Override + public Map> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "encryptedHmacKey", this::getEncryptedHmacKey, + "encryptedHmacValue", this::getEncryptedHmacValue + ); + } } diff --git a/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java b/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java index b8bea4aea0..43c7f398e1 100644 --- a/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java +++ b/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java @@ -19,24 +19,45 @@ package org.apache.poi.xslf.util; -import java.awt.Dimension; +import static java.util.Spliterator.NONNULL; +import static java.util.Spliterator.ORDERED; + +import java.awt.AlphaComposite; import java.awt.Graphics2D; import java.awt.RenderingHints; +import java.awt.geom.Dimension2D; +import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; +import java.io.Closeable; import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.lang.ref.WeakReference; -import java.util.List; +import java.util.Collections; import java.util.Locale; import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators.AbstractSpliterator; import java.util.TreeSet; +import java.util.function.Consumer; +import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; import javax.imageio.ImageIO; +import org.apache.poi.common.usermodel.GenericRecord; +import org.apache.poi.sl.draw.DrawPictureShape; import org.apache.poi.sl.draw.Drawable; +import org.apache.poi.sl.draw.ImageRenderer; +import org.apache.poi.sl.usermodel.PictureData; import org.apache.poi.sl.usermodel.Slide; import org.apache.poi.sl.usermodel.SlideShow; import org.apache.poi.sl.usermodel.SlideShowFactory; +import org.apache.poi.util.GenericRecordJsonWriter; /** * An utility to convert slides of a .pptx slide show to a PNG image @@ -57,12 +78,14 @@ public class PPTX2PNG { (error == null ? "" : ("Error: "+error+"\n")) + "Options:\n" + " -scale scale factor\n" + + " -fixSide specify side (long,short,width,height) to fix - use as amount of pixels\n" + " -slide 1-based index of a slide to render\n" + " -format png,gif,jpg (,null for testing)\n" + " -outdir output directory, defaults to origin of the ppt/pptx file\n" + " -outfile output filename, defaults to '"+OUTPUT_PAT_REGEX+"'\n" + " -outpat output filename pattern, defaults to '"+OUTPUT_PAT_REGEX+"'\n" + " patterns: basename, slideno, format, ext\n" + + " -dump 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(" 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> slides = ss.getSlides(); - Set slidenum = slideIndexes(slides.size(), slidenumStr); + try (MFProxy proxy = initProxy(file)) { + final Set 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 slideIndexes(final int slideCount, String range) { - Set slideIdx = new TreeSet<>(); - if ("-1".equals(range)) { - for (int i=0; i getEmbeddings(); + Dimension2D getSize(); + + default void setSlideNo(int slideNo) {} + + String getTitle(); + void draw(Graphics2D ctx); + + default int getSlideCount() { return 1; } + + default Set 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 = "(^|,)(?\\d+)?(-(?\\d+))?"; + + @Override + public Set slideIndexes(String range) { + final Matcher matcher = Pattern.compile(RANGE_PATTERN).matcher(range); + Spliterator sp = new AbstractSpliterator(range.length(), ORDERED|NONNULL){ + @Override + public boolean tryAdvance(Consumer 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 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; + } + } + } diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java index c471ae1717..3002dd2832 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java @@ -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); diff --git a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java index 39db61492b..9d32e2d053 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java +++ b/src/scratchpad/src/org/apache/poi/hemf/draw/HemfImageRenderer.java @@ -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; + } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java index 1bd5bd6c25..70df41135a 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfComment.java @@ -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> 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> 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> getGenericProperties() { + return null; + } + + @Override + public List 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> 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> getGenericProperties() { + return null; + } } public static class EmfCommentDataMultiformats implements EmfCommentData { @@ -369,6 +426,20 @@ public class HemfComment { public List getFormats() { return Collections.unmodifiableList(formats); } + + public Rectangle2D getBounds() { + return bounds; + } + + @Override + public Map> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("bounds", this::getBounds); + } + + @Override + public List 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> 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 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> 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 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> getGenericProperties() { + return null; + } } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java index ae0fbd2d07..77f2b0d4b6 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfDraw.java @@ -17,11 +17,10 @@ package org.apache.poi.hemf.record.emf; -import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString; import static org.apache.poi.hwmf.record.HwmfDraw.normalizeBounds; +import static org.apache.poi.util.GenericRecordUtil.getBitsAsString; import java.awt.Shape; -import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; import java.awt.geom.Dimension2D; import java.awt.geom.Path2D; @@ -29,13 +28,17 @@ import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.IntStream; import org.apache.poi.hemf.draw.HemfDrawProperties; import org.apache.poi.hemf.draw.HemfGraphics; import org.apache.poi.hwmf.draw.HwmfGraphics.FillDrawStyle; import org.apache.poi.hwmf.record.HwmfDraw; import org.apache.poi.hwmf.record.HwmfDraw.WmfSelectObject; -import org.apache.poi.util.Internal; +import org.apache.poi.util.GenericRecordJsonWriter; +import org.apache.poi.util.GenericRecordUtil; import org.apache.poi.util.LittleEndianConsts; import org.apache.poi.util.LittleEndianInputStream; @@ -47,26 +50,30 @@ public class HemfDraw { */ public static class EmfSelectObject extends WmfSelectObject implements HemfRecord { - private static final String[] STOCK_IDS = { - "0x80000000 /* WHITE_BRUSH */", - "0x80000001 /* LTGRAY_BRUSH */", - "0x80000002 /* GRAY_BRUSH */", - "0x80000003 /* DKGRAY_BRUSH */", - "0x80000004 /* BLACK_BRUSH */", - "0x80000005 /* NULL_BRUSH */", - "0x80000006 /* WHITE_PEN */", - "0x80000007 /* BLACK_PEN */", - "0x80000008 /* NULL_PEN */", - "0x8000000A /* OEM_FIXED_FONT */", - "0x8000000B /* ANSI_FIXED_FONT */", - "0x8000000C /* ANSI_VAR_FONT */", - "0x8000000D /* SYSTEM_FONT */", - "0x8000000E /* DEVICE_DEFAULT_FONT */", - "0x8000000F /* DEFAULT_PALETTE */", - "0x80000010 /* SYSTEM_FIXED_FONT */", - "0x80000011 /* DEFAULT_GUI_FONT */", - "0x80000012 /* DC_BRUSH */", - "0x80000013 /* DC_PEN */" + private static final int[] IDX_MASKS = IntStream.rangeClosed(0x80000000,0x80000013).toArray(); + + private static final String[] IDX_NAMES = { + "WHITE_BRUSH", + "LTGRAY_BRUSH", + "GRAY_BRUSH", + "DKGRAY_BRUSH", + "BLACK_BRUSH", + "NULL_BRUSH", + "WHITE_PEN", + "BLACK_PEN", + "NULL_PEN", + // 0x80000009 is not a valid stock object + "INVALID", + "OEM_FIXED_FONT", + "ANSI_FIXED_FONT", + "ANSI_VAR_FONT", + "SYSTEM_FONT", + "DEVICE_DEFAULT_FONT", + "DEFAULT_PALETTE", + "SYSTEM_FIXED_FONT", + "DEFAULT_GUI_FONT", + "DC_BRUSH", + "DC_PEN" }; @Override @@ -84,12 +91,20 @@ public class HemfDraw { @Override public String toString() { - return "{ index: "+ - (((objectIndex & 0x80000000) != 0 && (objectIndex & 0x3FFFFFFF) <= 13 ) - ? STOCK_IDS[objectIndex & 0x3FFFFFFF] - : objectIndex)+" }"; + return GenericRecordJsonWriter.marshal(this); } + @Override + public Map> 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> 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> 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 path.append(shape, false), getFillDrawStyle()); } + + public Rectangle2D getBounds() { + return bounds; + } + + @Override + public Map> 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> 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> 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()+" }"; - } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java index 6979a37b71..514db1d309 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFill.java @@ -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> 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> 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 getRgnRects() { + return rgnRects; + } + + @Override + public Map> 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 getRgnRects() { + return rgnRects; + } + + @Override + public Map> 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 getRgnRects() { + return rgnRects; + } + + @Override + public Map> 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 getRgnRects() { + return rgnRects; + } + + @Override + public Map> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "regionMode", this::getRegionMode, + "rgnRects", this::getRgnRects + ); } } @@ -539,6 +657,23 @@ public class HemfFill { return size; } + + @Override + public Map> getGenericProperties() { + final Map> 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> 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(); diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java index dd42fe63be..f7106053a7 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfFont.java @@ -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> getGenericProperties() { + final Map> 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "fullname", () -> fullname, + "style", () -> style, + "script", () -> script, + "details", () -> details + ); } @Override diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfHeader.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfHeader.java index 6c1cf5cadd..52a4055029 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfHeader.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfHeader.java @@ -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> getGenericProperties() { + final Map> 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); + } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java index 36b82946f4..e32fde84e9 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfMisc.java @@ -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 getPalette() { + return palette; + } + + @Override + public Map> 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> 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> 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> 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> 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> 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> 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> 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> 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "penIndex", this::getPenIndex, + "colorUsage", this::getColorUsage, + "bitmap", this::getBitmap + ); } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPalette.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPalette.java index 3d002f0044..5c5f9283ac 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPalette.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfPalette.java @@ -18,12 +18,16 @@ 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> 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> 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> 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("icmMode", this::getIcmMode); + } } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java index 41e9f30442..cd33b10ca0 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfRecord.java @@ -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> getGenericProperties() { + return null; + } + } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.java index 7e785315c0..f806185d25 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfText.java @@ -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> 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> 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; } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java index a312dc116e..d95b9d94a0 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/HemfWindowing.java @@ -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> 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 diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emf/UnimplementedHemfRecord.java b/src/scratchpad/src/org/apache/poi/hemf/record/emf/UnimplementedHemfRecord.java index 12034376c4..6a2fb0e6c9 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emf/UnimplementedHemfRecord.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emf/UnimplementedHemfRecord.java @@ -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; diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java index 36d7b90ec7..5a2a1ee254 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusBrush.java @@ -17,26 +17,24 @@ 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> 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> 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> 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> getGenericProperties() { + final Map> 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> getGenericProperties() { + final Map> 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> 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(",", "{", "}"))); - } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java index 036b544fdc..e254d91816 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusDraw.java @@ -17,10 +17,7 @@ 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> 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 getRectData() { + return rectData; + } + + @Override + public Map> 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> getGenericProperties() { + final Map> 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> 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> 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> 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "flags", this::getFlags, + "rectData", () -> rectData + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusFont.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusFont.java index a2201320b4..e0a2fc759f 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusFont.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusFont.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "graphicsVersion", () -> graphicsVersion, + "emSize", () -> emSize, + "sizeUnit", () -> sizeUnit, + "styleFlags", () -> styleFlags, + "family", () -> family + ); + } + + @Override + public EmfPlusObjectType getGenericRecordType() { + return EmfPlusObjectType.FONT; + } } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java index aa93853173..36209f289e 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusHeader.java @@ -18,12 +18,18 @@ 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> 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 { diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusImage.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusImage.java index ffa9d9c8e9..0748ec717a 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusImage.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusImage.java @@ -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> getGenericProperties() { + final Map> 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 continuedObjectData) { } + + @Override + public String toString() { + return GenericRecordJsonWriter.marshal(this); + } + + @Override + public Map> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "graphicsVersion", this::getGraphicsVersion, + "wrapMode", this::getWrapMode, + "clampColor", this::getClampColor, + "objectClamp", this::getObjectClamp + ); + } + + @Override + public EmfPlusObjectType getGenericRecordType() { + return EmfPlusObjectType.IMAGE_ATTRIBUTES; + } } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java index 2d006563ab..94566a92b1 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusMisc.java @@ -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> 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> 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> 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> 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> 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "flags", this::getFlags, + "origin", this::getOrigin + ); + } } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java index 4d23bab6e0..6b0c754788 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusObject.java @@ -17,11 +17,15 @@ 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 getContinuedObject() { return continuedObjectData; } + + @Override + public Map> 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 continuedObjectData); @@ -251,5 +269,18 @@ public class HemfPlusObject { public EmfPlusGraphicsVersion getGraphicsVersion() { return graphicsVersion; } + + @Override + public EmfPlusObjectType getGenericRecordType() { + return objectType; + } + + @Override + public Map> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "graphicsVersion", this::getGraphicsVersion, + "objectDataBytes", () -> objectDataBytes + ); + } } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java index c8405fece6..71d71b73eb 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPath.java @@ -17,13 +17,20 @@ 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 continuedObjectData) { @@ -184,8 +200,32 @@ public class HemfPlusPath { } } + @Override + public EmfPlusObjectType getGenericRecordType() { + return EmfPlusObjectType.PATH; + } - } + @Override + public Map> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "graphicsVersion", this::getGraphicsVersion, + "flags", getBitsAsString(this::getFlags, FLAGS_MASKS, FLAGS_NAMES), + "points", this::getGenericPoints + ); + } + private List 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) + ); + } + } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java index c412b425a7..4540aab3b0 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusPen.java @@ -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> getGenericProperties() { + final Map> 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> getGenericProperties() { + final Map> 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> getGenericProperties() { + final Map> 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); + } } } diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java index c074b66704..608bba7e7f 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRecord.java @@ -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(); diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java index 7caeff1f3b..f8e43fc646 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/HemfPlusRegion.java @@ -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> 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> 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> 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> 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "left", () -> left, + "right", () -> right + ); + } + } private static long readNode(LittleEndianInputStream leis, Consumer con) throws IOException { // A 32-bit unsigned integer that specifies the type of data in the RegionNodeData field. diff --git a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/UnimplementedHemfPlusRecord.java b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/UnimplementedHemfPlusRecord.java index bc7152cd7e..9ca1b375eb 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/UnimplementedHemfPlusRecord.java +++ b/src/scratchpad/src/org/apache/poi/hemf/record/emfplus/UnimplementedHemfPlusRecord.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "flags", this::getFlags, + "recordBytes", () -> recordBytes + ); + } } \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java b/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java index b6cacdbaa9..92030fa02c 100644 --- a/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java +++ b/src/scratchpad/src/org/apache/poi/hemf/usermodel/HemfPicture.java @@ -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 { +public class HemfPicture implements Iterable, GenericRecord { private final LittleEndianInputStream stream; private final List records = new ArrayList<>(); private boolean isParsed = false; @@ -179,4 +182,14 @@ public class HemfPicture implements Iterable { public Iterable getEmbeddings() { return () -> new HemfEmbeddedIterator(HemfPicture.this); } + + @Override + public List getGenericChildren() { + return getRecords(); + } + + @Override + public Map> getGenericProperties() { + return null; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/BitMaskTextProp.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/BitMaskTextProp.java index ec01408930..e28c0d713a 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/BitMaskTextProp.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/BitMaskTextProp.java @@ -17,6 +17,12 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "flags", getBitsAsString(this::getValue, subPropMasks, subPropNames) + ); + } } \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/FontAlignmentProp.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/FontAlignmentProp.java index 904feeedf8..7c87b5d16a 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/FontAlignmentProp.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/FontAlignmentProp.java @@ -17,6 +17,12 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "fontAlign", this::getFontAlign + ); + } } \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java index 78cc3fab4d..5f747c109f 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java @@ -17,12 +17,17 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "type", this::getType, + "position", this::getPosition + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java index dca666903b..92b6abbf81 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "tabStops", this::getTabStops + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/IndentProp.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/IndentProp.java index 40adf46d8f..e62b4b9b63 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/IndentProp.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/IndentProp.java @@ -17,7 +17,12 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "charactersCovered", this::getCharactersCovered, + "indentLevel", this::getIndentLevel + ); + } } \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextAlignmentProp.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextAlignmentProp.java index 82430f0de2..62fc80cf0e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextAlignmentProp.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextAlignmentProp.java @@ -17,6 +17,12 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "textAlign", this::getTextAlign + ); + } } \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPFException9.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPFException9.java index 128bc83653..0ad60d0e7a 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPFException9.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPFException9.java @@ -21,7 +21,12 @@ */ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "bulletBlipRef", this::getBulletBlipRef, + "bulletHasAutoNumber", this::hasBulletAutoNumber, + "autoNumberScheme", this::getAutoNumberScheme, + "autoNumberStartNumber", this::getAutoNumberStartNumber + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java index dfb1a1e544..4dd8b52c09 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java @@ -18,6 +18,11 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "sizeOfDataBlock", this::getSize, + "propName", this::getName, + "dataValue", this::getValue, + "maskInHeader", this::getMask + ); + } } \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java index b859a4c099..d564241e11 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java @@ -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> getGenericProperties() { + Map> 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); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java index d9451d95b2..b7636d2da0 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java @@ -17,9 +17,14 @@ 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> 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 + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/CString.java b/src/scratchpad/src/org/apache/poi/hslf/record/CString.java index f126a81904..5f98cc8a8c 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/CString.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/CString.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("text", this::getText); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ColorSchemeAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ColorSchemeAtom.java index 02f3c28b66..9bd51ed8ad 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ColorSchemeAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ColorSchemeAtom.java @@ -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> getGenericProperties() { + final Map> 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); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/Comment2000.java b/src/scratchpad/src/org/apache/poi/hslf/record/Comment2000.java index b64134b0d4..9dc836bf53 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/Comment2000.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/Comment2000.java @@ -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); } - } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/Comment2000Atom.java b/src/scratchpad/src/org/apache/poi/hslf/record/Comment2000Atom.java index f73b436280..de01e4f9aa 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/Comment2000Atom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/Comment2000Atom.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "number", this::getNumber, + "date", this::getDate, + "xOffset", this::getXOffset, + "yOffset", this::getYOffset + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/DocInfoListContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/DocInfoListContainer.java index eed1de23f7..e046161f41 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/DocInfoListContainer.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/DocInfoListContainer.java @@ -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> getGenericProperties() { + return null; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/DocumentAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/DocumentAtom.java index cf01f0b2c9..c0195d7a27 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/DocumentAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/DocumentAtom.java @@ -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> getGenericProperties() { + final Map> 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); } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java index 7637408326..abcf03628e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java @@ -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 oldToNewReferencesLookup) { // nothing to update } + + @Override + public Map> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "encryptionInfo", this::getEncryptionInfo + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/EscherPlaceholder.java b/src/scratchpad/src/org/apache/poi/hslf/record/EscherPlaceholder.java index 9d984180eb..a3d29b01da 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/EscherPlaceholder.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/EscherPlaceholder.java @@ -17,9 +17,13 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "base", super::getGenericProperties, + "position", this::getPosition, + "placementId", this::getPlacementId, + "size", this::getSize + ); + } + + @Override + public Enum getGenericRecordType() { + return RecordTypes.OEPlaceholderAtom; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java b/src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java index 10744e49d7..783c59e496 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java @@ -17,11 +17,14 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "shapeId", this::getShapeId, + "escherRecord", this::getEscherRecord + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java index b5540cee5a..93942674af 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("slideId", this::getSlideId); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbedAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbedAtom.java index 5ebb2a43c6..e72f052347 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbedAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbedAtom.java @@ -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> 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"; + } + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java index ca9d68d788..da25e248bf 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlink.java @@ -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; diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlinkAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlinkAtom.java index 648f2aaa57..bc99430998 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlinkAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExHyperlinkAtom.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("number", this::getNumber); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java index 1ed5829532..97e2594ad1 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java @@ -17,9 +17,14 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "objectId", this::getObjectId, + "flags", getBitsAsString(this::getMask, FLAG_MASKS, FLAG_NAMES) + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExObjList.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExObjList.java index 11e48fa1f1..ced5508a9d 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExObjList.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExObjList.java @@ -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; diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExObjListAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExObjListAtom.java index e1224ef0bc..0c345a286b 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExObjListAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExObjListAtom.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "objectIDSeed", this::getObjectIDSeed + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExObjRefAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExObjRefAtom.java index 1381e6ab6c..930f54c380 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExObjRefAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExObjRefAtom.java @@ -17,9 +17,13 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("exObjIdRef", this::getExObjIdRef); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java index 7e679f27aa..0b0b66ebab 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java @@ -17,16 +17,21 @@ 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. * - * - * - * @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> 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 + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java index 3e62549d9e..3fdfa04ecd 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java @@ -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 oldToNewReferencesLookup) { // nothing to update } + + @Override + public Map> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "compressed", this::isCompressed, + "persistId", this::getPersistId, + "dataLength", this::getDataLength, + "data", this::getData + ); + } } \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java b/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java index bddb79efc6..db2ffcf6d9 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java @@ -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 getFonts() { return new ArrayList<>(fonts.values()); } + + @Override + public Map> getGenericProperties() { + return null; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/FontEmbeddedData.java b/src/scratchpad/src/org/apache/poi/hslf/record/FontEmbeddedData.java index f60ba4a697..f28bb3f990 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/FontEmbeddedData.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/FontEmbeddedData.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("fontHeader", this::getFontHeader); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java index 2d172d7b5e..0e9f732833 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java @@ -17,11 +17,18 @@ 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> 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()) + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersAtom.java index 9e9140578e..ee6e645ee1 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersAtom.java @@ -17,10 +17,17 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "formatIndex", safeEnum(FormatIndex.values(), this::getFormatId), + "flags", getBitsAsString(this::getMask, PLACEHOLDER_MASKS, PLACEHOLDER_NAMES) + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersContainer.java index 9f2a0eecd7..c7aa32876b 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersContainer.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/HeadersFootersContainer.java @@ -17,12 +17,12 @@ 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. *

@@ -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) { diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfo.java b/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfo.java index 8ca722d856..9c0962c644 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfo.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfo.java @@ -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; diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfoAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfoAtom.java index 15dc05b50d..71a5adc123 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfoAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfoAtom.java @@ -17,9 +17,15 @@ 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> 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) + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/MasterTextPropAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/MasterTextPropAtom.java index 9d76556e86..0e1b4dcab1 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/MasterTextPropAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/MasterTextPropAtom.java @@ -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 getIndents() { return Collections.unmodifiableList(indents); } + + @Override + public Map> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "indents", this::getIndents + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/NotesAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/NotesAtom.java index c4e284098b..40b4554955 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/NotesAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/NotesAtom.java @@ -17,10 +17,14 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "slideId", this::getSlideID, + "followMasterObjects", this::getFollowMasterObjects, + "followMasterScheme", this::getFollowMasterScheme, + "followMasterBackground", this::getFollowMasterBackground + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java index e91ddd0421..f25164d2a2 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "placementId", this::getPlacementId, + "placeholderId", this::getPlaceholderId, + "placeholderSize", this::getPlaceholderSize + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/OutlineTextRefAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/OutlineTextRefAtom.java index e44b360a58..e0c368825b 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/OutlineTextRefAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/OutlineTextRefAtom.java @@ -17,10 +17,13 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "textIndex", this::getTextIndex + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java index 7f6f6ab0d5..c2bceecd81 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java @@ -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 { //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 getEscherRecords() { return childRecords; } + @Override + public Iterator 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 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 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 getTextboxHelper(EscherContainerRecord spContainer) { + Optional 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 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 findInSpContainer(final EscherContainerRecord spContainer) { + Optional 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 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 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 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 children = parent.getChildContainers(); - final List 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 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("escherRecords", this::getEscherRecords); } - - @SuppressWarnings("unchecked") - private static T getChildRecord(List children, RecordTypes type) { - for (Record r : children) { - if (r.getRecordType() == type.typeID) { - return (T)r; - } - } - return null; - } + + private static Predicate sameHSLF(RecordTypes type) { + return (p) -> p.getRecordType() == type.typeID; + } + + private static Predicate sameEscher(EscherRecordTypes type) { + return (p) -> p.getRecordId() == type.typeID; + } + + @SuppressWarnings("unchecked") + private static Optional firstEscherRecord(Iterable container, EscherRecordTypes type) { + return StreamSupport.stream(container.spliterator(), false).filter(sameEscher(type)).map(o -> (T)o).findFirst(); + } + + private static Function> findEscherContainer(EscherRecordTypes type) { + return (r) -> r.getChildContainers().stream().filter(sameEscher(type)); + } + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawingGroup.java b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawingGroup.java index 694f2ba148..8f244607b0 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawingGroup.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawingGroup.java @@ -17,22 +17,25 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "dggContainer", this::getDggContainer + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java b/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java index f2e7c05ce0..841d49d9e1 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/PersistPtrHolder.java @@ -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 _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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "slideLocations", this::getSlideLocationsLookup + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/Record.java b/src/scratchpad/src/org/apache/poi/hslf/record/Record.java index bec722cb8d..c4ec8c983f 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/Record.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/Record.java @@ -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 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; } + + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java index a714f1950b..f7cf20107b 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java @@ -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> getGenericProperties() { + return null; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java index a1697d511e..c57003c89b 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java @@ -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 { diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RoundTripHFPlaceholder12.java b/src/scratchpad/src/org/apache/poi/hslf/record/RoundTripHFPlaceholder12.java index e843f1dd73..f2c8f03b82 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/RoundTripHFPlaceholder12.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RoundTripHFPlaceholder12.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("placeholderId", this::getPlaceholderId ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SSSlideInfoAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/SSSlideInfoAtom.java index f07542ee06..e0d28e5dfa 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/SSSlideInfoAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SSSlideInfoAtom.java @@ -17,9 +17,14 @@ 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> 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 + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java index 5d9b347bce..e5b85a7e00 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "masterID", this::getMasterID, + "notesID", this::getNotesID, + "followMasterObjects", this::getFollowMasterObjects, + "followMasterScheme", this::getFollowMasterScheme, + "followMasterBackground", this::getFollowMasterBackground, + "layoutAtom", this::getSSlideLayoutAtom + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtomLayout.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtomLayout.java index c6bd795757..9b27fe2282 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtomLayout.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtomLayout.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "geometry", this::getGeometryType, + "placeholderIDs", () -> placeholderIDs + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java index 08ed2e8468..0c6e825d9b 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java @@ -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; diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlidePersistAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlidePersistAtom.java index 7c4907f939..34a5560253 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/SlidePersistAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SlidePersistAtom.java @@ -17,45 +17,61 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "refID", this::getRefID, + "flags", getBitsAsString(() -> flags, FLAGS_MASKS, FLAGS_NAMES), + "numPlaceholderTexts", this::getNumPlaceholderTexts, + "slideIdentifier", this::getSlideIdentifier + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SoundData.java b/src/scratchpad/src/org/apache/poi/hslf/record/SoundData.java index a4676f63a6..09f9d5c1a7 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/SoundData.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SoundData.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("data", this::getData); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextProp9Atom.java b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextProp9Atom.java index 3f1ec4b18a..d11f406fae 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextProp9Atom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextProp9Atom.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "autoNumberSchemes", this::getAutoNumberTypes + ); + } } \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java index 096e35c89b..372c52d9ad 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java @@ -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 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> getGenericProperties() { + return !initialised ? null : GenericRecordUtil.getGenericProperties( + "paragraphStyles", this::getParagraphStyles, + "characterStyles", this::getCharacterStyles + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java index 5e9eeea795..e5f8d4f4dc 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "text", this::getText + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextCharsAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextCharsAtom.java index 349ee8166b..0e8b95b3ab 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextCharsAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextCharsAtom.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "text", this::getText + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java index 05c61a667d..3264b78793 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "index", this::getIndex, + "textType", this::getTextTypeEnum + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java index b8ef54ad10..3b194b7cc0 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "defaultTabSize", this::getDefaultTabSize, + "numLevels", this::getNumberOfLevels, + "tabStops", this::getTabStops, + "leftMargins", () -> leftMargin, + "indents", () -> indent + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoAtom.java index d506f48282..115dc21e4c 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoAtom.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "charactersCovered", this::getCharactersCovered, + "textSpecInfoRuns", this::getTextSpecInfoRuns + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoRun.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoRun.java index 8a046c1613..9c890a2b56 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextSpecInfoRun.java @@ -17,15 +17,23 @@ 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> getGenericProperties() { + final Map> 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); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TxInteractiveInfoAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TxInteractiveInfoAtom.java index 66ec91b566..469de32a56 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TxInteractiveInfoAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TxInteractiveInfoAtom.java @@ -17,12 +17,15 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "startIndex", this::getStartIndex, + "endIndex", this::getEndIndex + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TxMasterStyleAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TxMasterStyleAtom.java index 70b3221df6..a706d0d33b 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TxMasterStyleAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TxMasterStyleAtom.java @@ -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. *

- * - * @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 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= 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "paragraphStyles", this::getParagraphStyles, + "charStyles", this::getCharacterStyles + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/UnknownRecordPlaceholder.java b/src/scratchpad/src/org/apache/poi/hslf/record/UnknownRecordPlaceholder.java index 4790ae1ee6..237027b088 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/UnknownRecordPlaceholder.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/UnknownRecordPlaceholder.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("contents", () -> _contents); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java index 54a0e5033a..287da27798 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/UserEditAtom.java @@ -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> getGenericProperties() { + final Map> 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); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/VBAInfoAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/VBAInfoAtom.java index d847c38539..eb4d337383 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/VBAInfoAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/VBAInfoAtom.java @@ -17,10 +17,12 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "persistIdRef", this::getPersistIdRef, + "hasMacros", this::isHasMacros, + "version", this::getVersion + ); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/VBAInfoContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/VBAInfoContainer.java index 03577653ed..013ace9fed 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/VBAInfoContainer.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/VBAInfoContainer.java @@ -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 diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java index 806c206fb5..52952e2191 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java @@ -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); diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java index c10daef5e9..8f538d65ec 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java @@ -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 { /** * @since POI 3.14-Beta2 */ - public static T getEscherChild(EscherContainerRecord owner, RecordTypes recordId){ + public static T getEscherChild(EscherContainerRecord owner, EscherRecordTypes recordId){ return getEscherChild(owner, recordId.typeID); } @@ -252,7 +252,7 @@ public abstract class HSLFShape implements Shape { /** * @since POI 3.14-Beta2 */ - public T getEscherChild(RecordTypes recordId){ + public T getEscherChild(EscherRecordTypes recordId){ return getEscherChild(recordId.typeID); } @@ -577,9 +577,9 @@ public abstract class HSLFShape implements Shape { } 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; } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeFactory.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeFactory.java index 58e216a4f5..7ff8404e7f 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeFactory.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeFactory.java @@ -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 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(); diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlide.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlide.java index 673b72009a..14d85b56f8 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlide.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlide.java @@ -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, Closeable { +public final class HSLFSlideShow implements SlideShow, 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> getGenericProperties() { + return null; + } + + @Override + public List getGenericChildren() { + return Arrays.asList(_hslfSlideShow.getRecords()); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java index 6271002037..16f039d7d8 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java @@ -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 { 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 { } // 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 { } 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++) { diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java index 49b114512b..c46881d8fc 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java @@ -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 { /* 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 { 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 { 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 { @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()); } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java index 0a3eecd00c..e17811086c 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfGraphics.java @@ -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 handler; + + FillDrawStyle(BiConsumer 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 propStack = new LinkedList<>(); diff --git a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java index bc96032f03..ed34e355a9 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/draw/HwmfImageRenderer.java @@ -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; + } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmap16.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmap16.java index 09bbb9f16e..cc103b2c06 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmap16.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmap16.java @@ -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> getGenericProperties() { + final Map> 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); + } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java index 2ef22a407a..a21eb5a7ef 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfBitmapDib.java @@ -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> getGenericProperties() { + final Map> 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); diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java index a757617752..cd78ccdc8a 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfColorRef.java @@ -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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("color", colorRef::getRGB); } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java index 6f8b3aa045..38089ca7ab 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfDraw.java @@ -18,28 +18,35 @@ 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> 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> 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> 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> 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> 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 getPolyList() { + return polyList; + } + + @Override + public Map> 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> 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> 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> 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> 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> 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 diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfEscape.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfEscape.java index a2a7082a35..788ad62534 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfEscape.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfEscape.java @@ -18,13 +18,16 @@ 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> 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> 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> getGenericProperties() { + final Map> 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); + } } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java index 20c48b8ee1..5e50e2e48d 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFill.java @@ -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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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; + } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java index 211c9fc263..14432432db 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java @@ -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> 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> getGenericProperties() { + final Map> 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); + } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfHeader.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfHeader.java index 811caa0d20..2f1ada526b 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfHeader.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfHeader.java @@ -18,12 +18,21 @@ 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> getGenericProperties() { + final Map> 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); + } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java index bd9264b6ac..45ccf645fa 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfMisc.java @@ -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> getGenericProperties() { + return null; + } } /** @@ -74,6 +84,11 @@ public class HwmfMisc { public void draw(HwmfGraphics ctx) { } + + @Override + public Map> 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> 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> 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("bkMode", this::getBkMode); } } @@ -219,6 +261,11 @@ public class HwmfMisc { public void draw(HwmfGraphics ctx) { } + + @Override + public Map> 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> 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> 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> 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> 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> 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> 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> 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> 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "brushStyle", this::getBrushStyle, + "colorRef", this::getColorRef, + "brushHatch", this::getBrushHatch + ); } } } \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java index cef75f698d..efaa774497 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPalette.java @@ -17,25 +17,35 @@ 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> 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 getPaletteCopy() { + List getPaletteCopy() { List 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> 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> 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("paletteIndex", this::getPaletteIndex); + } } /** @@ -281,6 +327,11 @@ public class HwmfPalette { public void draw(HwmfGraphics ctx) { } + + @Override + public Map> getGenericProperties() { + return null; + } } /** @@ -306,7 +357,7 @@ public class HwmfPalette { HwmfDrawProperties props = ctx.getProperties(); List dest = props.getPalette(); List src = getPaletteCopy(); - int start = getPaletteStart(); + final int start = getPaletteStart(); if (dest == null) { dest = new ArrayList<>(); } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java index 776d48a3e7..261b60a439 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfPenStyle.java @@ -18,9 +18,14 @@ 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties( + "lineCap", this::getLineCap, + "lineJoin", this::getLineJoin, + "lineDash", this::getLineDash, + "lineDashes", this::getLineDashes, + "alternateDash", this::isAlternateDash, + "geometric", this::isGeometric + ); } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecord.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecord.java index 2673e80783..b68af18460 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecord.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfRecord.java @@ -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(); + } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java index 6af61b81ac..533c85a821 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfText.java @@ -17,28 +17,31 @@ 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> 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("colorRef", this::getColorRef); } } @@ -141,6 +158,14 @@ public class HwmfText { public void draw(HwmfGraphics ctx) { } + + @Override + public Map> 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> 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> 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> 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> 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> getGenericProperties() { + return GenericRecordUtil.getGenericProperties("font", this::getFont); } } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java index 442884e78c..ebe819155f 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfWindowing.java @@ -17,10 +17,7 @@ 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> getGenericProperties() { + final Map> 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); + } } } diff --git a/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java b/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java index cb4e92133d..c5a4bad522 100644 --- a/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java +++ b/src/scratchpad/src/org/apache/poi/hwmf/usermodel/HwmfPicture.java @@ -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, 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 getEmbeddings() { return () -> new HwmfEmbeddedIterator(HwmfPicture.this); } + + @Override + public Iterator iterator() { + return getRecords().iterator(); + } + + @Override + public Spliterator spliterator() { + return getRecords().spliterator(); + } + + @Override + public Map> getGenericProperties() { + return null; + } + + @Override + public List getGenericChildren() { + return getRecords(); + } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java index e03bded11d..eb4c368bac 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java @@ -17,11 +17,12 @@ 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 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 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 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 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 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 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(); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java index 3c7bc9c9a5..2dddf74950 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java @@ -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); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestRecordTypes.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestRecordTypes.java index dbf89692bb..be224a9d24 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestRecordTypes.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestRecordTypes.java @@ -18,10 +18,11 @@ 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); - } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextHeaderAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextHeaderAtom.java index 8ac7453405..fb3c47dc28 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextHeaderAtom.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextHeaderAtom.java @@ -18,9 +18,11 @@ 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 { diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTxMasterStyleAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTxMasterStyleAtom.java index 7e7c9f4d37..30c177f086 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTxMasterStyleAtom.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTxMasterStyleAtom.java @@ -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); } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextShape.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextShape.java index 8499ed7b48..94ee89002a 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextShape.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextShape.java @@ -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; diff --git a/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java b/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java index 64fb8563b3..1e2b464015 100644 --- a/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java +++ b/src/scratchpad/testcases/org/apache/poi/hwmf/TestHwmfParsing.java @@ -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") diff --git a/src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java index dd57d837e6..d5264c8339 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java @@ -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 -- 2.39.5