aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndreas Beeker <kiwiwings@apache.org>2018-12-28 23:43:31 +0000
committerAndreas Beeker <kiwiwings@apache.org>2018-12-28 23:43:31 +0000
commitc039da1b94a24224bcd7d6a09b1828782a2acb00 (patch)
treea6e4f58d3565bdec89873d6582f0bb9afc32c8aa /src
parenta78bd71fc1f3bd60af434774744b5dfacfced403 (diff)
downloadpoi-c039da1b94a24224bcd7d6a09b1828782a2acb00.tar.gz
poi-c039da1b94a24224bcd7d6a09b1828782a2acb00.zip
#63028 - Provide font embedding for slideshows
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1849898 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/integrationtest/org/apache/poi/TestAllFiles.java2
-rw-r--r--src/java/org/apache/poi/common/usermodel/fonts/FontCharset.java2
-rw-r--r--src/java/org/apache/poi/common/usermodel/fonts/FontFacet.java75
-rw-r--r--src/java/org/apache/poi/common/usermodel/fonts/FontHeader.java227
-rw-r--r--src/java/org/apache/poi/common/usermodel/fonts/FontInfo.java68
-rw-r--r--src/java/org/apache/poi/sl/draw/DrawFontInfo.java48
-rw-r--r--src/java/org/apache/poi/sl/extractor/SlideShowExtractor.java305
-rw-r--r--src/java/org/apache/poi/sl/usermodel/FontCollection.java22
-rw-r--r--src/java/org/apache/poi/sl/usermodel/Resources.java29
-rw-r--r--src/java/org/apache/poi/sl/usermodel/Slide.java3
-rw-r--r--src/java/org/apache/poi/sl/usermodel/SlideShow.java29
-rw-r--r--src/java/org/apache/poi/sl/usermodel/TextRun.java8
-rw-r--r--src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java144
-rw-r--r--src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFontData.java84
-rw-r--r--src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFontInfo.java285
-rw-r--r--src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java7
-rw-r--r--src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java16
-rw-r--r--src/ooxml/testcases/org/apache/poi/xslf/extractor/TestXSLFPowerPointExtractor.java14
-rw-r--r--src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java32
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/record/Document.java36
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/record/DocumentAtom.java59
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java76
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/record/FontEmbeddedData.java116
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java8
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java26
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfo.java27
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfoPredefined.java30
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java27
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java30
-rw-r--r--src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java36
-rw-r--r--src/scratchpad/testcases/org/apache/poi/hslf/extractor/TestExtractor.java65
-rw-r--r--src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestHSLFSlideShow.java10
-rw-r--r--src/testcases/org/apache/poi/sl/usermodel/BaseTestSlideShow.java91
33 files changed, 1481 insertions, 556 deletions
diff --git a/src/integrationtest/org/apache/poi/TestAllFiles.java b/src/integrationtest/org/apache/poi/TestAllFiles.java
index 2255e8bbf9..33bd696195 100644
--- a/src/integrationtest/org/apache/poi/TestAllFiles.java
+++ b/src/integrationtest/org/apache/poi/TestAllFiles.java
@@ -187,11 +187,11 @@ public class TestAllFiles {
HANDLERS.put(".tif", new NullFileHandler());
HANDLERS.put(".tiff", new NullFileHandler());
HANDLERS.put(".wav", new NullFileHandler());
- HANDLERS.put(".pfx", new NullFileHandler());
HANDLERS.put(".xml", new NullFileHandler());
HANDLERS.put(".csv", new NullFileHandler());
HANDLERS.put(".ods", new NullFileHandler());
HANDLERS.put(".ttf", new NullFileHandler());
+ HANDLERS.put(".fntdata", new NullFileHandler());
// VBA source files
HANDLERS.put(".vba", new NullFileHandler());
HANDLERS.put(".bas", new NullFileHandler());
diff --git a/src/java/org/apache/poi/common/usermodel/fonts/FontCharset.java b/src/java/org/apache/poi/common/usermodel/fonts/FontCharset.java
index aeeca9284c..32915149f2 100644
--- a/src/java/org/apache/poi/common/usermodel/fonts/FontCharset.java
+++ b/src/java/org/apache/poi/common/usermodel/fonts/FontCharset.java
@@ -70,7 +70,7 @@ public enum FontCharset {
/** Specifies the Russian Cyrillic character set. */
RUSSIAN(0x000000CC, "Cp1251"),
/** Specifies the Thai character set. */
- THAI_(0x000000DE, "x-windows-874"),
+ THAI(0x000000DE, "x-windows-874"),
/** Specifies a Eastern European character set. */
EASTEUROPE(0x000000EE, "Cp1250"),
/**
diff --git a/src/java/org/apache/poi/common/usermodel/fonts/FontFacet.java b/src/java/org/apache/poi/common/usermodel/fonts/FontFacet.java
new file mode 100644
index 0000000000..5fb16908cd
--- /dev/null
+++ b/src/java/org/apache/poi/common/usermodel/fonts/FontFacet.java
@@ -0,0 +1,75 @@
+/* ====================================================================
+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.fonts;
+
+import org.apache.poi.util.Beta;
+
+/**
+ * A FontFacet holds the font data for a shape of a font, i.e. a regular,
+ * italic, bold or bold-italic version of a Font.
+ */
+@SuppressWarnings("unused")
+@Beta
+public interface FontFacet {
+ /**
+ * Get the font weight.<p>
+ *
+ * The weight of the font in the range 0 through 1000.
+ * For example, 400 is normal and 700 is bold.
+ * If this value is zero, a default weight is used.
+ *
+ * @return the font weight
+ *
+ * @since POI 4.1.0
+ */
+ default int getWeight() {
+ return FontHeader.REGULAR_WEIGHT;
+ }
+
+ /**
+ * Set the font weight
+ *
+ * @param weight the font weight
+ */
+ default void setWeight(int weight) {
+ throw new UnsupportedOperationException("FontFacet is read-only.");
+ }
+
+ /**
+ * @return {@code true}, if the font is italic
+ */
+ default boolean isItalic() {
+ return false;
+ }
+
+ /**
+ * Set the font posture
+ *
+ * @param italic {@code true} for italic, {@code false} for regular
+ */
+ default void setItalic(boolean italic) {
+ throw new UnsupportedOperationException("FontFacet is read-only.");
+ }
+
+ /**
+ * @return the wrapper object holding the font data
+ */
+ default Object getFontData() {
+ 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
new file mode 100644
index 0000000000..9777f0cc75
--- /dev/null
+++ b/src/java/org/apache/poi/common/usermodel/fonts/FontHeader.java
@@ -0,0 +1,227 @@
+/* ====================================================================
+ 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.fonts;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
+import org.apache.poi.util.LittleEndianInput;
+import org.apache.poi.util.LittleEndianInputStream;
+
+
+/**
+ * The header data of an EOT font.<p>
+ *
+ * Currently only version 1 fields are read to identify a stream to be embedded.
+ *
+ * @see <a href="http://www.w3.org/Submission/EOT">Embedded OpenType (EOT) File Format</a>
+ */
+@SuppressWarnings({"FieldCanBeLocal", "unused", "Duplicates"})
+public class FontHeader implements FontInfo {
+ /**
+ * Fonts with a font weight of 400 are regarded as regular weighted.
+ * Higher font weights (up to 1000) are bold - lower weights are thin.
+ */
+ public static final int REGULAR_WEIGHT = 400;
+
+ private int eotSize;
+ private int fontDataSize;
+ private int version;
+ private int flags;
+ private final byte[] panose = new byte[10];
+ private byte charset;
+ private byte italic;
+ private int weight;
+ private int fsType;
+ private int magic;
+ private int unicodeRange1;
+ private int unicodeRange2;
+ private int unicodeRange3;
+ private int unicodeRange4;
+ private int codePageRange1;
+ private int codePageRange2;
+ private int checkSumAdjustment;
+ private String familyName;
+ private String styleName;
+ private String versionName;
+ private String fullName;
+
+ public void init(byte[] source, int offset, int length) {
+ init(new LittleEndianByteArrayInputStream(source, offset, length));
+ }
+
+ public void init(LittleEndianInput leis) {
+ eotSize = leis.readInt();
+ fontDataSize = leis.readInt();
+ version = leis.readInt();
+ if (version != 0x00010000 && version != 0x00020001 && version != 0x00020002) {
+ throw new RuntimeException("not a EOT font data stream");
+ }
+ flags = leis.readInt();
+ leis.readFully(panose);
+ charset = leis.readByte();
+ italic = leis.readByte();
+ weight = leis.readInt();
+ fsType = leis.readUShort();
+ magic = leis.readUShort();
+ if (magic != 0x504C) {
+ throw new RuntimeException("not a EOT font data stream");
+ }
+ unicodeRange1 = leis.readInt();
+ unicodeRange2 = leis.readInt();
+ unicodeRange3 = leis.readInt();
+ unicodeRange4 = leis.readInt();
+ codePageRange1 = leis.readInt();
+ codePageRange2 = leis.readInt();
+ checkSumAdjustment = leis.readInt();
+ int reserved1 = leis.readInt();
+ int reserved2 = leis.readInt();
+ int reserved3 = leis.readInt();
+ int reserved4 = leis.readInt();
+ familyName = readName(leis);
+ styleName = readName(leis);
+ versionName = readName(leis);
+ fullName = readName(leis);
+
+ }
+
+ public InputStream bufferInit(InputStream fontStream) throws IOException {
+ LittleEndianInputStream is = new LittleEndianInputStream(fontStream);
+ is.mark(1000);
+ init(is);
+ is.reset();
+ return is;
+ }
+
+ private String readName(LittleEndianInput leis) {
+ // padding
+ leis.readShort();
+ int nameSize = leis.readUShort();
+ byte[] nameBuf = IOUtils.safelyAllocate(nameSize, 1000);
+ leis.readFully(nameBuf);
+ // may be 0-terminated, just trim it away
+ return new String(nameBuf, 0, nameSize, StandardCharsets.UTF_16LE).trim();
+ }
+
+ public boolean isItalic() {
+ return italic != 0;
+ }
+
+ public int getWeight() {
+ return weight;
+ }
+
+ public boolean isBold() {
+ return getWeight() > REGULAR_WEIGHT;
+ }
+
+ public byte getCharsetByte() {
+ return charset;
+ }
+
+ public FontCharset getCharset() {
+ return FontCharset.valueOf(getCharsetByte());
+ }
+
+ public FontPitch getPitch() {
+ byte familyKind = panose[0];
+ switch (familyKind) {
+ default:
+ // Any
+ case 0:
+ // No Fit
+ case 1:
+ return FontPitch.VARIABLE;
+
+ // Latin Text
+ case 2:
+ // Latin Decorative
+ case 4:
+ byte proportion = panose[3];
+ return proportion == 9 ? FontPitch.FIXED : FontPitch.VARIABLE;
+
+ // Latin Hand Written
+ case 3:
+ // Latin Symbol
+ case 5:
+ byte spacing = panose[3];
+ return spacing == 3 ? FontPitch.FIXED : FontPitch.VARIABLE;
+ }
+
+ }
+
+ public FontFamily getFamily() {
+ switch (panose[0]) {
+ // Any
+ case 0:
+ // No Fit
+ case 1:
+ return FontFamily.FF_DONTCARE;
+ // Latin Text
+ case 2:
+ byte serifStyle = panose[1];
+ return (10 <= serifStyle && serifStyle <= 15)
+ ? FontFamily.FF_SWISS : FontFamily.FF_ROMAN;
+ // Latin Hand Written
+ case 3:
+ return FontFamily.FF_SCRIPT;
+ // Latin Decorative
+ default:
+ case 4:
+ return FontFamily.FF_DECORATIVE;
+ // Latin Symbol
+ case 5:
+ return FontFamily.FF_MODERN;
+ }
+ }
+
+ public String getFamilyName() {
+ return familyName;
+ }
+
+ public String getStyleName() {
+ return styleName;
+ }
+
+ public String getVersionName() {
+ return versionName;
+ }
+
+ public String getFullName() {
+ return fullName;
+ }
+
+ public byte[] getPanose() {
+ return panose;
+ }
+
+ @Override
+ public String getTypeface() {
+ return getFamilyName();
+ }
+
+ public int getFlags() {
+ return flags;
+ }
+}
+
+
+
diff --git a/src/java/org/apache/poi/common/usermodel/fonts/FontInfo.java b/src/java/org/apache/poi/common/usermodel/fonts/FontInfo.java
index ecb5a69687..b47b02e53b 100644
--- a/src/java/org/apache/poi/common/usermodel/fonts/FontInfo.java
+++ b/src/java/org/apache/poi/common/usermodel/fonts/FontInfo.java
@@ -17,6 +17,11 @@ limitations under the License.
package org.apache.poi.common.usermodel.fonts;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.poi.util.Beta;
+
/**
* A FontInfo object holds information about a font configuration.
* It is roughly an equivalent to the LOGFONT structure in Windows GDI.<p>
@@ -30,6 +35,7 @@ package org.apache.poi.common.usermodel.fonts;
*
* @see <a href="https://msdn.microsoft.com/en-us/library/dd145037.aspx">LOGFONT structure</a>
*/
+@SuppressWarnings("unused")
public interface FontInfo {
/**
@@ -37,7 +43,9 @@ public interface FontInfo {
* @return unique index number of the underlying record this Font represents
* (probably you don't care unless you're comparing which one is which)
*/
- Integer getIndex();
+ default Integer getIndex() {
+ return null;
+ }
/**
* Sets the index within the collection of Font objects
@@ -46,7 +54,9 @@ public interface FontInfo {
*
* @throws UnsupportedOperationException if unsupported
*/
- void setIndex(int index);
+ default void setIndex(int index) {
+ throw new UnsupportedOperationException("FontInfo is read-only.");
+ }
/**
@@ -60,36 +70,48 @@ public interface FontInfo {
* @param typeface the full name of the font, when {@code null} removes the font definition -
* removal is implementation specific
*/
- void setTypeface(String typeface);
+ default void setTypeface(String typeface) {
+ throw new UnsupportedOperationException("FontInfo is read-only.");
+ }
/**
* @return the font charset
*/
- FontCharset getCharset();
+ default FontCharset getCharset() {
+ return FontCharset.ANSI;
+ }
/**
* Sets the charset
*
* @param charset the charset
*/
- void setCharset(FontCharset charset);
+ default void setCharset(FontCharset charset) {
+ throw new UnsupportedOperationException("FontInfo is read-only.");
+ }
/**
* @return the family class
*/
- FontFamily getFamily();
+ default FontFamily getFamily() {
+ return FontFamily.FF_DONTCARE;
+ }
/**
* Sets the font family class
*
* @param family the font family class
*/
- void setFamily(FontFamily family);
+ default void setFamily(FontFamily family) {
+ throw new UnsupportedOperationException("FontInfo is read-only.");
+ }
/**
* @return the font pitch or {@code null} if unsupported
*/
- FontPitch getPitch();
+ default FontPitch getPitch() {
+ return null;
+ }
/**
* Set the font pitch
@@ -98,5 +120,33 @@ public interface FontInfo {
*
* @throws UnsupportedOperationException if unsupported
*/
- void setPitch(FontPitch pitch);
+ default void setPitch(FontPitch pitch) {
+ throw new UnsupportedOperationException("FontInfo is read-only.");
+ }
+
+ /**
+ * @return panose info in binary form or {@code null} if unknown
+ */
+ default byte[] getPanose() {
+ return null;
+ }
+
+ /**
+ * Set the panose in binary form
+ * @param panose the panose bytes
+ */
+ default void setPanose(byte[] panose) {
+ throw new UnsupportedOperationException("FontInfo is read-only.");
+ }
+
+
+ /**
+ * If font facets are embedded in the document, return the list of embedded facets.
+ * The font embedding is experimental, therefore the API can change.
+ * @return the list of embedded EOT font data
+ */
+ @Beta
+ default List<? extends FontFacet> getFacets() {
+ return Collections.emptyList();
+ }
} \ No newline at end of file
diff --git a/src/java/org/apache/poi/sl/draw/DrawFontInfo.java b/src/java/org/apache/poi/sl/draw/DrawFontInfo.java
index dc7afb4e24..da1979cb9e 100644
--- a/src/java/org/apache/poi/sl/draw/DrawFontInfo.java
+++ b/src/java/org/apache/poi/sl/draw/DrawFontInfo.java
@@ -19,10 +19,7 @@
package org.apache.poi.sl.draw;
-import org.apache.poi.common.usermodel.fonts.FontCharset;
-import org.apache.poi.common.usermodel.fonts.FontFamily;
import org.apache.poi.common.usermodel.fonts.FontInfo;
-import org.apache.poi.common.usermodel.fonts.FontPitch;
import org.apache.poi.util.Internal;
/**
@@ -38,52 +35,7 @@ import org.apache.poi.util.Internal;
}
@Override
- public Integer getIndex() {
- return null;
- }
-
- @Override
- public void setIndex(int index) {
- throw new UnsupportedOperationException("DrawFontManagers FontInfo can't be changed.");
- }
-
- @Override
public String getTypeface() {
return typeface;
}
-
- @Override
- public void setTypeface(String typeface) {
- throw new UnsupportedOperationException("DrawFontManagers FontInfo can't be changed.");
- }
-
- @Override
- public FontCharset getCharset() {
- return FontCharset.ANSI;
- }
-
- @Override
- public void setCharset(FontCharset charset) {
- throw new UnsupportedOperationException("DrawFontManagers FontInfo can't be changed.");
- }
-
- @Override
- public FontFamily getFamily() {
- return FontFamily.FF_SWISS;
- }
-
- @Override
- public void setFamily(FontFamily family) {
- throw new UnsupportedOperationException("DrawFontManagers FontInfo can't be changed.");
- }
-
- @Override
- public FontPitch getPitch() {
- return FontPitch.VARIABLE;
- }
-
- @Override
- public void setPitch(FontPitch pitch) {
- throw new UnsupportedOperationException("DrawFontManagers FontInfo can't be changed.");
- }
}
diff --git a/src/java/org/apache/poi/sl/extractor/SlideShowExtractor.java b/src/java/org/apache/poi/sl/extractor/SlideShowExtractor.java
index dee4d44a03..7173c24e97 100644
--- a/src/java/org/apache/poi/sl/extractor/SlideShowExtractor.java
+++ b/src/java/org/apache/poi/sl/extractor/SlideShowExtractor.java
@@ -18,10 +18,14 @@
package org.apache.poi.sl.extractor;
import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.LinkedList;
import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
import org.apache.poi.extractor.POITextExtractor;
-import org.apache.poi.sl.usermodel.Comment;
import org.apache.poi.sl.usermodel.MasterSheet;
import org.apache.poi.sl.usermodel.Notes;
import org.apache.poi.sl.usermodel.ObjectShape;
@@ -52,6 +56,10 @@ public class SlideShowExtractor<
> extends POITextExtractor {
private static final POILogger LOG = POILogFactory.getLogger(SlideShowExtractor.class);
+ // placeholder text for slide numbers
+ private static final String SLIDE_NUMBER_PH = "‹#›";
+
+
private SlideShow<S,P> slideshow;
private boolean slidesByDefault = true;
@@ -59,7 +67,8 @@ public class SlideShowExtractor<
private boolean commentsByDefault;
private boolean masterByDefault;
-
+ private Predicate<Object> filter = o -> true;
+
public SlideShowExtractor(final SlideShow<S,P> slideshow) {
setFilesystem(slideshow);
this.slideshow = slideshow;
@@ -115,9 +124,8 @@ public class SlideShowExtractor<
@Override
public String getText() {
final StringBuilder sb = new StringBuilder();
-
for (final Slide<S, P> slide : slideshow.getSlides()) {
- sb.append(getText(slide));
+ getText(slide, sb::append);
}
return sb.toString();
@@ -125,34 +133,37 @@ public class SlideShowExtractor<
public String getText(final Slide<S,P> slide) {
final StringBuilder sb = new StringBuilder();
+ getText(slide, sb::append);
+ return sb.toString();
+ }
+
+ private void getText(final Slide<S,P> slide, final Consumer<String> consumer) {
if (slidesByDefault) {
- printShapeText(slide, sb);
+ printShapeText(slide, consumer);
}
if (masterByDefault) {
final MasterSheet<S,P> ms = slide.getMasterSheet();
- printSlideMaster(ms, sb);
+ printSlideMaster(ms, consumer);
// only print slide layout, if it's a different instance
final MasterSheet<S,P> sl = slide.getSlideLayout();
if (sl != ms) {
- printSlideMaster(sl, sb);
+ printSlideMaster(sl, consumer);
}
}
if (commentsByDefault) {
- printComments(slide, sb);
+ printComments(slide, consumer);
}
if (notesByDefault) {
- printNotes(slide, sb);
+ printNotes(slide, consumer);
}
-
- return sb.toString();
}
- private void printSlideMaster(final MasterSheet<S,P> master, final StringBuilder sb) {
+ private void printSlideMaster(final MasterSheet<S,P> master, final Consumer<String> consumer) {
if (master == null) {
return;
}
@@ -163,163 +174,140 @@ public class SlideShowExtractor<
if (text == null || text.isEmpty() || "*".equals(text)) {
continue;
}
+
if (ts.isPlaceholder()) {
// don't bother about boiler plate text on master sheets
LOG.log(POILogger.INFO, "Ignoring boiler plate (placeholder) text on slide master:", text);
continue;
}
- sb.append(text);
- if (!text.endsWith("\n")) {
- sb.append("\n");
- }
+ printTextParagraphs(ts.getTextParagraphs(), consumer);
}
}
}
- private String printHeaderReturnFooter(final Sheet<S,P> sheet, final StringBuilder sb) {
- final Sheet<S, P> m = (sheet instanceof Slide) ? sheet.getMasterSheet() : sheet;
- final StringBuilder footer = new StringBuilder("\n");
- addSheetPlaceholderDatails(sheet, Placeholder.HEADER, sb);
- addSheetPlaceholderDatails(sheet, Placeholder.FOOTER, footer);
+ private void printTextParagraphs(final List<P> paras, final Consumer<String> consumer) {
+ printTextParagraphs(paras, consumer, "\n");
+ }
- if (masterByDefault) {
- // write header texts and determine footer text
- for (Shape<S, P> s : m) {
- if (!(s instanceof TextShape)) {
- continue;
- }
- final TextShape<S, P> ts = (TextShape<S, P>) s;
- final PlaceholderDetails pd = ts.getPlaceholderDetails();
- if (pd == null || !pd.isVisible() || pd.getPlaceholder() == null) {
- continue;
- }
- switch (pd.getPlaceholder()) {
- case HEADER:
- sb.append(ts.getText());
- sb.append('\n');
- break;
- case SLIDE_NUMBER:
- if (sheet instanceof Slide) {
- footer.append(ts.getText().replace("‹#›", Integer.toString(((Slide<S, P>) sheet).getSlideNumber() + 1)));
- footer.append('\n');
- }
- break;
- case FOOTER:
- footer.append(ts.getText());
- footer.append('\n');
- break;
- case DATETIME:
- // currently not supported
- default:
- break;
+
+ private void printTextParagraphs(final List<P> paras, final Consumer<String> consumer, String trailer) {
+ printTextParagraphs(paras, consumer, trailer, SlideShowExtractor::replaceTextCap);
+ }
+
+ private void printTextParagraphs(final List<P> paras, final Consumer<String> consumer, String trailer, final Function<TextRun,String> converter) {
+ for (P p : paras) {
+ for (TextRun r : p) {
+ if (filter.test(r)) {
+ consumer.accept(converter.apply(r));
}
}
+ if (!trailer.isEmpty() && filter.test(trailer)) {
+ consumer.accept(trailer);
+ }
}
-
- return (footer.length() > 1) ? footer.toString() : "";
}
- private void addSheetPlaceholderDatails(final Sheet<S,P> sheet, final Placeholder placeholder, final StringBuilder sb) {
- final PlaceholderDetails headerPD = sheet.getPlaceholderDetails(placeholder);
- if (headerPD == null) {
+ private void printHeaderFooter(final Sheet<S,P> sheet, final Consumer<String> consumer, final Consumer<String> footerCon) {
+ final Sheet<S, P> m = (sheet instanceof Slide) ? sheet.getMasterSheet() : sheet;
+ addSheetPlaceholderDatails(sheet, Placeholder.HEADER, consumer);
+ addSheetPlaceholderDatails(sheet, Placeholder.FOOTER, footerCon);
+
+ if (!masterByDefault) {
return;
}
- final String headerStr = headerPD.getText();
- if (headerStr == null) {
- return;
+
+ // write header texts and determine footer text
+ for (Shape<S, P> s : m) {
+ if (!(s instanceof TextShape)) {
+ continue;
+ }
+ final TextShape<S, P> ts = (TextShape<S, P>) s;
+ final PlaceholderDetails pd = ts.getPlaceholderDetails();
+ if (pd == null || !pd.isVisible() || pd.getPlaceholder() == null) {
+ continue;
+ }
+ switch (pd.getPlaceholder()) {
+ case HEADER:
+ printTextParagraphs(ts.getTextParagraphs(), consumer);
+ break;
+ case FOOTER:
+ printTextParagraphs(ts.getTextParagraphs(), footerCon);
+ break;
+ case SLIDE_NUMBER:
+ printTextParagraphs(ts.getTextParagraphs(), footerCon, "\n", SlideShowExtractor::replaceSlideNumber);
+ break;
+ case DATETIME:
+ // currently not supported
+ default:
+ break;
+ }
}
- sb.append(headerStr);
}
- private void printShapeText(final Sheet<S,P> sheet, final StringBuilder sb) {
- final String footer = printHeaderReturnFooter(sheet, sb);
- printShapeText((ShapeContainer<S,P>)sheet, sb);
- sb.append(footer);
+
+ private void addSheetPlaceholderDatails(final Sheet<S,P> sheet, final Placeholder placeholder, final Consumer<String> consumer) {
+ final PlaceholderDetails headerPD = sheet.getPlaceholderDetails(placeholder);
+ final String headerStr = (headerPD != null) ? headerPD.getText() : null;
+ if (headerStr != null && filter.test(headerPD)) {
+ consumer.accept(headerStr);
+ }
+ }
+
+ private void printShapeText(final Sheet<S,P> sheet, final Consumer<String> consumer) {
+ final List<String> footer = new LinkedList<>();
+ printHeaderFooter(sheet, consumer, footer::add);
+ printShapeText((ShapeContainer<S,P>)sheet, consumer);
+ footer.forEach(consumer);
}
@SuppressWarnings("unchecked")
- private void printShapeText(final ShapeContainer<S,P> container, final StringBuilder sb) {
+ private void printShapeText(final ShapeContainer<S,P> container, final Consumer<String> consumer) {
for (Shape<S,P> shape : container) {
if (shape instanceof TextShape) {
- printShapeText((TextShape<S,P>)shape, sb);
+ printTextParagraphs(((TextShape<S,P>)shape).getTextParagraphs(), consumer);
} else if (shape instanceof TableShape) {
- printShapeText((TableShape<S,P>)shape, sb);
+ printShapeText((TableShape<S,P>)shape, consumer);
} else if (shape instanceof ShapeContainer) {
- printShapeText((ShapeContainer<S,P>)shape, sb);
+ printShapeText((ShapeContainer<S,P>)shape, consumer);
}
}
}
- private void printShapeText(final TextShape<S,P> shape, final StringBuilder sb) {
- final List<P> paraList = shape.getTextParagraphs();
- if (paraList.isEmpty()) {
- sb.append('\n');
- return;
- }
- for (final P para : paraList) {
- for (final TextRun tr : para) {
- final String str = tr.getRawText().replace("\r", "");
- final String newStr;
- switch (tr.getTextCap()) {
- case ALL:
- newStr = str.toUpperCase(LocaleUtil.getUserLocale());
- break;
- case SMALL:
- newStr = str.toLowerCase(LocaleUtil.getUserLocale());
- break;
- default:
- case NONE:
- newStr = str;
- break;
- }
- sb.append(newStr);
- }
- sb.append('\n');
- }
- }
-
@SuppressWarnings("Duplicates")
- private void printShapeText(final TableShape<S,P> shape, final StringBuilder sb) {
+ private void printShapeText(final TableShape<S,P> shape, final Consumer<String> consumer) {
final int nrows = shape.getNumberOfRows();
final int ncols = shape.getNumberOfColumns();
- for (int row = 0; row < nrows; row++){
+ for (int row = 0; row < nrows; row++) {
+ String trailer = "";
for (int col = 0; col < ncols; col++){
TableCell<S, P> cell = shape.getCell(row, col);
//defensive null checks; don't know if they're necessary
- if (cell != null){
- String txt = cell.getText();
- txt = (txt == null) ? "" : txt;
- sb.append(txt);
- if (col < ncols-1){
- sb.append('\t');
- }
+ if (cell != null) {
+ trailer = col < ncols-1 ? "\t" : "\n";
+ printTextParagraphs(cell.getTextParagraphs(), consumer, trailer);
}
}
- sb.append('\n');
+ if (!trailer.equals("\n") && filter.test("\n")) {
+ consumer.accept("\n");
+ }
}
}
- private void printComments(final Slide<S,P> slide, final StringBuilder sb) {
- for (final Comment comment : slide.getComments()) {
- sb.append(comment.getAuthor());
- sb.append(" - ");
- sb.append(comment.getText());
- sb.append("\n");
- }
+ private void printComments(final Slide<S,P> slide, final Consumer<String> consumer) {
+ slide.getComments().stream().filter(filter).map(c -> c.getAuthor()+" - "+c.getText()).forEach(consumer);
}
- private void printNotes(final Slide<S,P> slide, final StringBuilder sb) {
+ private void printNotes(final Slide<S,P> slide, final Consumer<String> consumer) {
final Notes<S, P> notes = slide.getNotes();
if (notes == null) {
return;
}
- final String footer = printHeaderReturnFooter(notes, sb);
-
- printShapeText(notes, sb);
-
- sb.append(footer);
+ List<String> footer = new LinkedList<>();
+ printHeaderFooter(notes, consumer, footer::add);
+ printShapeText(notes, consumer);
+ footer.forEach(consumer);
}
public List<? extends ObjectShape<S,P>> getOLEShapes() {
@@ -342,4 +330,83 @@ public class SlideShowExtractor<
}
}
}
+
+ private static String replaceSlideNumber(TextRun tr) {
+ String raw = tr.getRawText();
+
+ if (!raw.contains(SLIDE_NUMBER_PH)) {
+ return raw;
+ }
+
+ TextParagraph tp = tr.getParagraph();
+ TextShape ps = (tp != null) ? tp.getParentShape() : null;
+ Sheet sh = (ps != null) ? ps.getSheet() : null;
+ String slideNr = (sh instanceof Slide) ? Integer.toString(((Slide)sh).getSlideNumber() + 1) : "";
+
+ return raw.replace(SLIDE_NUMBER_PH, slideNr);
+ }
+
+ private static String replaceTextCap(TextRun tr) {
+ final TextParagraph tp = tr.getParagraph();
+ final TextShape sh = (tp != null) ? tp.getParentShape() : null;
+ final Placeholder ph = (sh != null) ? sh.getPlaceholder() : null;
+
+ // 0xB acts like cariage return in page titles and like blank in the others
+ final char sep = (
+ ph == Placeholder.TITLE ||
+ ph == Placeholder.CENTERED_TITLE ||
+ ph == Placeholder.SUBTITLE
+ ) ? '\n' : ' ';
+
+ // PowerPoint seems to store files with \r as the line break
+ // The messes things up on everything but a Mac, so translate them to \n
+ String txt = tr.getRawText();
+ txt = txt.replace('\r', '\n');
+ txt = txt.replace((char) 0x0B, sep);
+
+ switch (tr.getTextCap()) {
+ case ALL:
+ txt = txt.toUpperCase(LocaleUtil.getUserLocale());
+ case SMALL:
+ txt = txt.toLowerCase(LocaleUtil.getUserLocale());
+ }
+
+ return txt;
+ }
+
+ /**
+ * Extract the used codepoints for font embedding / subsetting
+ * @param typeface the typeface/font family of the textruns to examine
+ * @param italic use {@code true} for italic TextRuns, {@code false} for non-italic ones and
+ * {@code null} if it doesn't matter
+ * @param bold use {@code true} for bold TextRuns, {@code false} for non-bold ones and
+ * {@code null} if it doesn't matter
+ * @return a bitset with the marked/used codepoints
+ */
+ public BitSet getCodepoints(String typeface, Boolean italic, Boolean bold) {
+ final BitSet glyphs = new BitSet();
+
+ Predicate<Object> filterOld = filter;
+ try {
+ filter = o -> filterFonts(o, typeface, italic, bold);
+ slideshow.getSlides().forEach(slide ->
+ getText(slide, s -> s.codePoints().forEach(glyphs::set))
+ );
+ } finally {
+ filter = filterOld;
+ }
+
+ return glyphs;
+ }
+
+ private static boolean filterFonts(Object o, String typeface, Boolean italic, Boolean bold) {
+ if (!(o instanceof TextRun)) {
+ return false;
+ }
+ TextRun tr = (TextRun)o;
+ return
+ typeface.equalsIgnoreCase(tr.getFontFamily()) &&
+ (italic == null || tr.isItalic() == italic) &&
+ (bold == null || tr.isBold() == bold);
+ }
}
diff --git a/src/java/org/apache/poi/sl/usermodel/FontCollection.java b/src/java/org/apache/poi/sl/usermodel/FontCollection.java
deleted file mode 100644
index 61278f4618..0000000000
--- a/src/java/org/apache/poi/sl/usermodel/FontCollection.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/* ====================================================================
- 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.sl.usermodel;
-
-public interface FontCollection {
-
-}
diff --git a/src/java/org/apache/poi/sl/usermodel/Resources.java b/src/java/org/apache/poi/sl/usermodel/Resources.java
deleted file mode 100644
index 96170e50bd..0000000000
--- a/src/java/org/apache/poi/sl/usermodel/Resources.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/* ====================================================================
- 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.sl.usermodel;
-
-/**
- * Common SlideShow resources, such as fonts, pictures
- * and multimedia data
- */
-public interface Resources {
- public FontCollection getFontCollection();
-
- public PictureData[] getPictureData();
- public int addPictureData(PictureData pict);
-}
diff --git a/src/java/org/apache/poi/sl/usermodel/Slide.java b/src/java/org/apache/poi/sl/usermodel/Slide.java
index 91b80f107e..7c0d566138 100644
--- a/src/java/org/apache/poi/sl/usermodel/Slide.java
+++ b/src/java/org/apache/poi/sl/usermodel/Slide.java
@@ -19,6 +19,7 @@ package org.apache.poi.sl.usermodel;
import java.util.List;
+@SuppressWarnings("unused")
public interface Slide<
S extends Shape<S,P>,
P extends TextParagraph<S,P,? extends TextRun>
@@ -82,7 +83,7 @@ public interface Slide<
*
* @since POI 4.0.0
*/
- MasterSheet getSlideLayout();
+ MasterSheet<S,P> getSlideLayout();
/**
* @return the slide name, defaults to "Slide[slideNumber]"
diff --git a/src/java/org/apache/poi/sl/usermodel/SlideShow.java b/src/java/org/apache/poi/sl/usermodel/SlideShow.java
index 175ad2b00e..751379de92 100644
--- a/src/java/org/apache/poi/sl/usermodel/SlideShow.java
+++ b/src/java/org/apache/poi/sl/usermodel/SlideShow.java
@@ -25,6 +25,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.extractor.POITextExtractor;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
@@ -44,8 +45,6 @@ public interface SlideShow<
*/
List<? extends MasterSheet<S,P>> getSlideMasters();
- Resources getResources();
-
/**
* Returns the current page size
*
@@ -135,4 +134,30 @@ public interface SlideShow<
* @since POI 4.0.0
*/
Object getPersistDocument();
+
+ /**
+ * Add an EOT font to the slideshow.
+ * An EOT or MTX font is a transformed True-Type (.ttf) or Open-Type (.otf) font.
+ * To transform a True-Type font use the sfntly library (see "see also" below)<p>
+ *
+ * (Older?) Powerpoint versions handle embedded fonts by converting them to .ttf files
+ * and put them into the Windows fonts directory. If the user is not allowed to install
+ * fonts, the slideshow can't be opened. While the slideshow is opened, its possible
+ * to copy the extracted .ttfs from the fonts directory. When the slideshow is closed,
+ * they will be removed.
+ *
+ * @param fontData the EOT font as stream
+ * @return the font info object containing the new font data
+ * @throws IOException if the fontData can't be saved or if the fontData is no EOT font
+ *
+ * @see <a href="http://www.w3.org/Submission/EOT">EOT specification</a>
+ * @see <a href="https://github.com/googlei18n/sfntly">googles sfntly library</a>
+ * @see <a href="https://github.com/kiwiwings/poi-font-mbender">Example on how to subset and embed fonts</a>
+ */
+ FontInfo addFont(InputStream fontData) throws IOException;
+
+ /**
+ * @return a list of registered fonts
+ */
+ List<? extends FontInfo> getFonts();
}
diff --git a/src/java/org/apache/poi/sl/usermodel/TextRun.java b/src/java/org/apache/poi/sl/usermodel/TextRun.java
index 394166071c..7dfd4933d8 100644
--- a/src/java/org/apache/poi/sl/usermodel/TextRun.java
+++ b/src/java/org/apache/poi/sl/usermodel/TextRun.java
@@ -27,6 +27,7 @@ import org.apache.poi.util.Internal;
/**
* Some text.
*/
+@SuppressWarnings("unused")
public interface TextRun {
/**
* Type of text capitals
@@ -243,4 +244,11 @@ public interface TextRun {
*/
@Internal
FieldType getFieldType();
+
+ /**
+ * @return the paragraph which contains this TextRun
+ *
+ * @since POI 4.1.0
+ */
+ TextParagraph<?,?,?> getParagraph();
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java
index d596993fe8..ff12d6aad2 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java
@@ -30,19 +30,20 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.OptionalLong;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ooxml.POIXMLException;
import org.apache.poi.ooxml.extractor.POIXMLPropertiesTextExtractor;
import org.apache.poi.ooxml.util.PackageHelper;
-import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.sl.usermodel.MasterSheet;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
-import org.apache.poi.sl.usermodel.Resources;
import org.apache.poi.sl.usermodel.SlideShow;
import org.apache.poi.util.Beta;
import org.apache.poi.util.IOUtils;
@@ -60,7 +61,6 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesMasterIdListE
import org.openxmlformats.schemas.presentationml.x2006.main.CTPresentation;
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdList;
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdListEntry;
-import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterIdListEntry;
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideSize;
import org.openxmlformats.schemas.presentationml.x2006.main.PresentationDocument;
@@ -70,6 +70,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.PresentationDocument
* they are reading or writing a slideshow. It is also the
* top level object for creating new slides/etc.
*/
+@SuppressWarnings("WeakerAccess")
@Beta
public class XMLSlideShow extends POIXMLDocument
implements SlideShow<XSLFShape, XSLFTextParagraph> {
@@ -78,10 +79,10 @@ public class XMLSlideShow extends POIXMLDocument
private static final int MAX_RECORD_LENGTH = 1_000_000;
private CTPresentation _presentation;
- private List<XSLFSlide> _slides;
- private List<XSLFSlideMaster> _masters;
- private List<XSLFPictureData> _pictures;
- private List<XSLFChart> _charts;
+ private final List<XSLFSlide> _slides = new ArrayList<>();
+ private final List<XSLFSlideMaster> _masters = new ArrayList<>();
+ private final List<XSLFPictureData> _pictures = new ArrayList<>();
+ private final List<XSLFChart> _charts = new ArrayList<>();
private XSLFTableStyles _tableStyles;
private XSLFNotesMaster _notesMaster;
private XSLFCommentAuthors _commentAuthors;
@@ -153,27 +154,26 @@ public class XMLSlideShow extends POIXMLDocument
}
}
- _charts = new ArrayList<>(chartMap.size());
- for (XSLFChart chart : chartMap.values()) {
- _charts.add(chart);
- }
+ _charts.clear();
+ _charts.addAll(chartMap.values());
- _masters = new ArrayList<>(masterMap.size());
- for (CTSlideMasterIdListEntry masterId : _presentation.getSldMasterIdLst().getSldMasterIdList()) {
- XSLFSlideMaster master = masterMap.get(masterId.getId2());
- _masters.add(master);
+ _masters.clear();
+ if (_presentation.isSetSldMasterIdLst()) {
+ _presentation.getSldMasterIdLst().getSldMasterIdList().forEach(
+ id -> _masters.add(masterMap.get(id.getId2()))
+ );
}
- _slides = new ArrayList<>(shIdMap.size());
+ _slides.clear();
if (_presentation.isSetSldIdLst()) {
- for (CTSlideIdListEntry slId : _presentation.getSldIdLst().getSldIdList()) {
- XSLFSlide sh = shIdMap.get(slId.getId2());
+ _presentation.getSldIdLst().getSldIdList().forEach(id -> {
+ XSLFSlide sh = shIdMap.get(id.getId2());
if (sh == null) {
- LOG.log(POILogger.WARN, "Slide with r:id " + slId.getId() + " was defined, but didn't exist in package, skipping");
- continue;
+ LOG.log(POILogger.WARN, "Slide with r:id " + id.getId() + " was defined, but didn't exist in package, skipping");
+ } else {
+ _slides.add(sh);
}
- _slides.add(sh);
- }
+ });
}
} catch (XmlException e) {
throw new POIXMLException(e);
@@ -192,7 +192,7 @@ public class XMLSlideShow extends POIXMLDocument
* Get the document's embedded files.
*/
@Override
- public List<PackagePart> getAllEmbeddedParts() throws OpenXML4JException {
+ public List<PackagePart> getAllEmbeddedParts() {
return Collections.unmodifiableList(
getPackage().getPartsByName(Pattern.compile("/ppt/embeddings/.*?"))
);
@@ -200,14 +200,12 @@ public class XMLSlideShow extends POIXMLDocument
@Override
public List<XSLFPictureData> getPictureData() {
- if (_pictures == null) {
- List<PackagePart> mediaParts = getPackage().getPartsByName(Pattern.compile("/ppt/media/.*?"));
- _pictures = new ArrayList<>(mediaParts.size());
- for (PackagePart part : mediaParts) {
+ if (_pictures.isEmpty()) {
+ getPackage().getPartsByName(Pattern.compile("/ppt/media/.*?")).forEach(part -> {
XSLFPictureData pd = new XSLFPictureData(part);
pd.setIndex(_pictures.size());
_pictures.add(pd);
- }
+ });
}
return Collections.unmodifiableList(_pictures);
}
@@ -219,20 +217,16 @@ public class XMLSlideShow extends POIXMLDocument
* @return created slide
*/
public XSLFSlide createSlide(XSLFSlideLayout layout) {
- int slideNumber = 256, cnt = 1;
- CTSlideIdList slideList;
- XSLFRelation relationType = XSLFRelation.SLIDE;
- if (!_presentation.isSetSldIdLst()) {
- slideList = _presentation.addNewSldIdLst();
- } else {
- slideList = _presentation.getSldIdLst();
- for (CTSlideIdListEntry slideId : slideList.getSldIdArray()) {
- slideNumber = (int) Math.max(slideId.getId() + 1, slideNumber);
- cnt++;
- }
+ CTSlideIdList slideList = _presentation.isSetSldIdLst()
+ ? _presentation.getSldIdLst() : _presentation.addNewSldIdLst();
- cnt = findNextAvailableFileNameIndex(relationType, cnt);
- }
+ @SuppressWarnings("deprecation")
+ OptionalLong maxId = Stream.of(slideList.getSldIdArray())
+ .mapToLong(CTSlideIdListEntry::getId).max();
+
+ final XSLFRelation relationType = XSLFRelation.SLIDE;
+ final int slideNumber = (int)(Math.max(maxId.orElse(0),255)+1);
+ final int cnt = findNextAvailableFileNameIndex(relationType);
RelationPart rp = createRelationship
(relationType, XSLFFactory.getInstance(), cnt, false);
@@ -250,33 +244,14 @@ public class XMLSlideShow extends POIXMLDocument
return slide;
}
- private int findNextAvailableFileNameIndex(XSLFRelation relationType, int idx) {
+ private int findNextAvailableFileNameIndex(XSLFRelation relationType) {
// Bug 55791: We also need to check that the resulting file name is not already taken
// this can happen when removing/adding slides, notes or charts
- while (true) {
- String fileName = relationType.getFileName(idx);
- boolean found = false;
- for (POIXMLDocumentPart relation : getRelations()) {
- if (relation.getPackagePart() != null &&
- fileName.equals(relation.getPackagePart().getPartName().getName())) {
- // name is taken => try next one
- found = true;
- break;
- }
- }
-
- if (!found &&
- getPackage().getPartsByName(Pattern.compile(Pattern.quote(fileName))).size() > 0) {
- // name is taken => try next one
- found = true;
- }
-
- if (!found) {
- break;
- }
- idx++;
+ try {
+ return getPackage().getUnusedPartIndex(relationType.getDefaultFileName());
+ } catch (InvalidFormatException e) {
+ throw new RuntimeException(e);
}
- return idx;
}
/**
@@ -313,8 +288,8 @@ public class XMLSlideShow extends POIXMLDocument
* @since POI 4.1.0
*/
public XSLFChart createChart() {
- int chartIdx = findNextAvailableFileNameIndex(XSLFRelation.CHART, _charts.size() + 1);
- XSLFChart chart = (XSLFChart) createRelationship(XSLFRelation.CHART, XSLFFactory.getInstance(), chartIdx, true).getDocumentPart();
+ int chartIdx = findNextAvailableFileNameIndex(XSLFRelation.CHART);
+ XSLFChart chart = createRelationship(XSLFRelation.CHART, XSLFFactory.getInstance(), chartIdx, true).getDocumentPart();
chart.setChartIndex(chartIdx);
_charts.add(chart);
return chart;
@@ -341,10 +316,8 @@ public class XMLSlideShow extends POIXMLDocument
createNotesMaster();
}
- int slideIndex = XSLFRelation.SLIDE.getFileNameIndex(slide);
-
XSLFRelation relationType = XSLFRelation.NOTES;
- slideIndex = findNextAvailableFileNameIndex(relationType, slideIndex);
+ int slideIndex = findNextAvailableFileNameIndex(relationType);
// add notes slide to presentation
XSLFNotes notesSlide = (XSLFNotes) createRelationship
@@ -453,6 +426,7 @@ public class XMLSlideShow extends POIXMLDocument
// fix ordering in the low-level xml
CTSlideIdList sldIdLst = _presentation.getSldIdLst();
+ @SuppressWarnings("deprecation")
CTSlideIdListEntry[] entries = sldIdLst.getSldIdArray();
CTSlideIdListEntry oldEntry = entries[oldIndex];
if (oldIndex < newIndex) {
@@ -517,14 +491,21 @@ public class XMLSlideShow extends POIXMLDocument
return img;
}
- int imageNumber = _pictures.size();
+
XSLFRelation relType = XSLFPictureData.getRelationForType(format);
if (relType == null) {
throw new IllegalArgumentException("Picture type " + format + " is not supported.");
}
- img = createRelationship(relType, XSLFFactory.getInstance(), imageNumber + 1, true).getDocumentPart();
- img.setIndex(imageNumber);
+ int imageNumber;
+ try {
+ imageNumber = getPackage().getUnusedPartIndex("/ppt/media/image#\\..+");
+ } catch (InvalidFormatException e) {
+ imageNumber = _pictures.size() + 1;
+ }
+
+ img = createRelationship(relType, XSLFFactory.getInstance(), imageNumber, true).getDocumentPart();
+ img.setIndex(_pictures.size());
_pictures.add(img);
try (OutputStream out = img.getPackagePart().getOutputStream()) {
@@ -624,6 +605,7 @@ public class XMLSlideShow extends POIXMLDocument
return null;
}
+ @SuppressWarnings("RedundantThrows")
@Override
public MasterSheet<XSLFShape, XSLFTextParagraph> createMasterSheet() throws IOException {
// TODO: implement!
@@ -631,12 +613,6 @@ public class XMLSlideShow extends POIXMLDocument
}
@Override
- public Resources getResources() {
- // TODO: implement!
- throw new UnsupportedOperationException();
- }
-
- @Override
public POIXMLPropertiesTextExtractor getMetadataTextExtractor() {
return new POIXMLPropertiesTextExtractor(this);
}
@@ -645,4 +621,14 @@ public class XMLSlideShow extends POIXMLDocument
public Object getPersistDocument() {
return this;
}
+
+ @Override
+ public XSLFFontInfo addFont(InputStream fontStream) throws IOException {
+ return XSLFFontInfo.addFontToSlideShow(this, fontStream);
+ }
+
+ @Override
+ public List<XSLFFontInfo> getFonts() {
+ return XSLFFontInfo.getFonts(this);
+ }
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFontData.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFontData.java
new file mode 100644
index 0000000000..8b2cb09ebc
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFontData.java
@@ -0,0 +1,84 @@
+/*
+ * ====================================================================
+ * 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.xslf.usermodel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.poi.ooxml.POIXMLDocumentPart;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.util.Beta;
+
+/**
+ * A container for fontdata files, i.e. MTX fonts derived from
+ * true (TTF) or open (OTF) type fonts.
+ *
+ * @since POI 4.1.0
+ */
+@Beta
+public class XSLFFontData extends POIXMLDocumentPart {
+ /**
+ * Create a new XSLFFontData node
+ */
+ @SuppressWarnings("unused")
+ protected XSLFFontData() {
+ super();
+ }
+
+ /**
+ * Construct XSLFFontData from a package part
+ *
+ * @param part the package part holding the ole data
+ */
+ @SuppressWarnings("unused")
+ public XSLFFontData(final PackagePart part) {
+ super(part);
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return getPackagePart().getInputStream();
+ }
+
+ public OutputStream getOutputStream() {
+ final PackagePart pp = getPackagePart();
+ pp.clear();
+ return pp.getOutputStream();
+ }
+
+ /**
+ * XSLFFontData objects store the actual content in the part directly without keeping a
+ * copy like all others therefore we need to handle them differently.
+ */
+ @Override
+ protected void prepareForCommit() {
+ // do not clear the part here
+ }
+
+
+ public void setData(final byte[] data) throws IOException {
+ try (final OutputStream os = getPackagePart().getOutputStream()) {
+ os.write(data);
+ }
+ }
+
+
+
+}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFontInfo.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFontInfo.java
new file mode 100644
index 0000000000..1e7462113e
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFontInfo.java
@@ -0,0 +1,285 @@
+/*
+ * ====================================================================
+ * 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.xslf.usermodel;
+
+import java.awt.Font;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.poi.common.usermodel.fonts.FontCharset;
+import org.apache.poi.common.usermodel.fonts.FontFacet;
+import org.apache.poi.common.usermodel.fonts.FontFamily;
+import org.apache.poi.common.usermodel.fonts.FontHeader;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
+import org.apache.poi.common.usermodel.fonts.FontPitch;
+import org.apache.poi.ooxml.POIXMLDocumentPart;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.util.IOUtils;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;
+import org.openxmlformats.schemas.presentationml.x2006.main.CTEmbeddedFontDataId;
+import org.openxmlformats.schemas.presentationml.x2006.main.CTEmbeddedFontList;
+import org.openxmlformats.schemas.presentationml.x2006.main.CTEmbeddedFontListEntry;
+import org.openxmlformats.schemas.presentationml.x2006.main.CTPresentation;
+
+@SuppressWarnings("WeakerAccess")
+public class XSLFFontInfo implements FontInfo {
+ final XMLSlideShow ppt;
+ final String typeface;
+ final CTEmbeddedFontListEntry fontListEntry;
+
+ public XSLFFontInfo(XMLSlideShow ppt, String typeface) {
+ this.ppt = ppt;
+ this.typeface = typeface;
+
+ final CTPresentation pres = ppt.getCTPresentation();
+ CTEmbeddedFontList fontList = pres.isSetEmbeddedFontLst()
+ ? pres.getEmbeddedFontLst() : pres.addNewEmbeddedFontLst();
+
+ for (CTEmbeddedFontListEntry fe : fontList.getEmbeddedFontArray()) {
+ if (typeface.equalsIgnoreCase(fe.getFont().getTypeface())) {
+ fontListEntry = fe;
+ return;
+ }
+ }
+
+ fontListEntry = fontList.addNewEmbeddedFont();
+ fontListEntry.addNewFont().setTypeface(typeface);
+ }
+
+ public XSLFFontInfo(XMLSlideShow ppt, CTEmbeddedFontListEntry fontListEntry) {
+ this.ppt = ppt;
+ this.typeface = fontListEntry.getFont().getTypeface();
+ this.fontListEntry = fontListEntry;
+ }
+
+ @Override
+ public String getTypeface() {
+ return getFont().getTypeface();
+ }
+
+ @Override
+ public void setTypeface(String typeface) {
+ getFont().setTypeface(typeface);
+ }
+
+ @Override
+ public FontCharset getCharset() {
+ return FontCharset.valueOf(getFont().getCharset());
+ }
+
+ @Override
+ public void setCharset(FontCharset charset) {
+ getFont().setCharset((byte)charset.getNativeId());
+ }
+
+ @Override
+ public FontFamily getFamily() {
+ return FontFamily.valueOfPitchFamily(getFont().getPitchFamily());
+ }
+
+ @Override
+ public void setFamily(FontFamily family) {
+ byte pitchAndFamily = getFont().getPitchFamily();
+ FontPitch pitch = FontPitch.valueOfPitchFamily(pitchAndFamily);
+ getFont().setPitchFamily(FontPitch.getNativeId(pitch, family));
+ }
+
+ @Override
+ public FontPitch getPitch() {
+ return FontPitch.valueOfPitchFamily(getFont().getPitchFamily());
+ }
+
+ @Override
+ public void setPitch(FontPitch pitch) {
+ byte pitchAndFamily = getFont().getPitchFamily();
+ FontFamily family = FontFamily.valueOfPitchFamily(pitchAndFamily);
+ getFont().setPitchFamily(FontPitch.getNativeId(pitch, family));
+ }
+
+ @Override
+ public byte[] getPanose() {
+ return getFont().getPanose();
+ }
+
+ @Override
+ public List<FontFacet> getFacets() {
+ List<FontFacet> facetList = new ArrayList<>();
+ if (fontListEntry.isSetRegular()) {
+ facetList.add(new XSLFFontFacet((fontListEntry.getRegular())));
+ }
+ if (fontListEntry.isSetItalic()) {
+ facetList.add(new XSLFFontFacet((fontListEntry.getItalic())));
+ }
+ if (fontListEntry.isSetBold()) {
+ facetList.add(new XSLFFontFacet((fontListEntry.getBold())));
+ }
+ if (fontListEntry.isSetBoldItalic()) {
+ facetList.add(new XSLFFontFacet((fontListEntry.getBoldItalic())));
+ }
+ return facetList;
+ }
+
+ public FontFacet addFacet(InputStream fontData) throws IOException {
+ FontHeader header = new FontHeader();
+ InputStream is = header.bufferInit(fontData);
+
+ final CTPresentation pres = ppt.getCTPresentation();
+ pres.setEmbedTrueTypeFonts(true);
+ pres.setSaveSubsetFonts(true);
+
+ final CTEmbeddedFontDataId dataId;
+ final int style =
+ (header.getWeight() > 400 ? Font.BOLD : Font.PLAIN) |
+ (header.isItalic() ? Font.ITALIC : Font.PLAIN);
+ switch (style) {
+ case Font.PLAIN:
+ dataId = fontListEntry.isSetRegular()
+ ? fontListEntry.getRegular() : fontListEntry.addNewRegular();
+ break;
+ case Font.BOLD:
+ dataId = fontListEntry.isSetBold()
+ ? fontListEntry.getBold() : fontListEntry.addNewBold();
+ break;
+ case Font.ITALIC:
+ dataId = fontListEntry.isSetItalic()
+ ? fontListEntry.getItalic() : fontListEntry.addNewItalic();
+ break;
+ default:
+ dataId = fontListEntry.isSetBoldItalic()
+ ? fontListEntry.getBoldItalic() : fontListEntry.addNewBoldItalic();
+ break;
+ }
+
+ XSLFFontFacet facet = new XSLFFontFacet(dataId);
+ facet.setFontData(is);
+ return facet;
+ }
+
+ private final class XSLFFontFacet implements FontFacet {
+ private final CTEmbeddedFontDataId fontEntry;
+ private final FontHeader header = new FontHeader();
+
+ private XSLFFontFacet(CTEmbeddedFontDataId fontEntry) {
+ this.fontEntry = fontEntry;
+ }
+
+ @Override
+ public int getWeight() {
+ init();
+ return header.getWeight();
+ }
+
+ @Override
+ public boolean isItalic() {
+ init();
+ return header.isItalic();
+ }
+
+ @Override
+ public XSLFFontData getFontData() {
+ return ppt.getRelationPartById(fontEntry.getId()).getDocumentPart();
+ }
+
+ void setFontData(InputStream is) throws IOException {
+ final XSLFRelation fntRel = XSLFRelation.FONT;
+ final String relId = fontEntry.getId();
+ final XSLFFontData fntData;
+ if (relId == null || relId.isEmpty()) {
+ final int fntDataIdx;
+ try {
+ fntDataIdx = ppt.getPackage().getUnusedPartIndex(fntRel.getDefaultFileName());
+ } catch (InvalidFormatException e) {
+ throw new RuntimeException(e);
+ }
+
+ POIXMLDocumentPart.RelationPart rp = ppt.createRelationship(fntRel, XSLFFactory.getInstance(), fntDataIdx, false);
+ fntData = rp.getDocumentPart();
+ fontEntry.setId(rp.getRelationship().getId());
+ } else {
+ fntData = (XSLFFontData)ppt.getRelationById(relId);
+ }
+
+ assert (fntData != null);
+ try (OutputStream os = fntData.getOutputStream()) {
+ IOUtils.copy(is, os);
+ }
+ }
+
+ private void init() {
+ if (header.getFamilyName() == null) {
+ try (InputStream is = getFontData().getInputStream()) {
+ byte[] buf = IOUtils.toByteArray(is, 1000);
+ header.init(buf, 0, buf.length);
+ } catch (IOException e) {
+ // TODO: better exception class
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+
+ private CTTextFont getFont() {
+ return fontListEntry.getFont();
+ }
+
+
+
+ /**
+ * Adds or updates a (MTX-) font
+ * @param ppt the slideshow which will contain the font
+ * @param fontStream the (MTX) font data as stream
+ * @return a font data object
+ * @throws IOException if the font data can't be stored
+ *
+ * @since POI 4.1.0
+ */
+ public static XSLFFontInfo addFontToSlideShow(XMLSlideShow ppt, InputStream fontStream)
+ throws IOException {
+ FontHeader header = new FontHeader();
+ InputStream is = header.bufferInit(fontStream);
+
+ XSLFFontInfo fontInfo = new XSLFFontInfo(ppt, header.getFamilyName());
+ fontInfo.addFacet(is);
+ return fontInfo;
+ }
+
+ /**
+ * Return all registered fonts
+ * @param ppt the slideshow containing the fonts
+ * @return the list of registered fonts
+ */
+ public static List<XSLFFontInfo> getFonts(XMLSlideShow ppt) {
+ final CTPresentation pres = ppt.getCTPresentation();
+
+ //noinspection deprecation
+ return pres.isSetEmbeddedFontLst()
+ ? Stream.of(pres.getEmbeddedFontLst().getEmbeddedFontArray())
+ .map(fe -> new XSLFFontInfo(ppt, fe)).collect(Collectors.toList())
+ : Collections.emptyList();
+ }
+
+}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java
index 9b9fc9c844..ca2d9d7ba2 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java
@@ -260,6 +260,13 @@ public final class XSLFRelation extends POIXMLRelation {
XSLFObjectData.class
);
+ public static final XSLFRelation FONT = new XSLFRelation(
+ "application/x-fontdata",
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/font",
+ "/ppt/fonts/font#.fntdata",
+ XSLFFontData.class
+ );
+
private XSLFRelation(String type, String rel, String defaultName, Class<? extends POIXMLDocumentPart> cls) {
super(type, rel, defaultName, cls);
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
index be714cbd85..6d2d215181 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
@@ -28,6 +28,7 @@ import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.sl.draw.DrawPaint;
import org.apache.poi.sl.usermodel.PaintStyle;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
+import org.apache.poi.sl.usermodel.TextParagraph;
import org.apache.poi.sl.usermodel.TextRun;
import org.apache.poi.util.Beta;
import org.apache.poi.util.POILogFactory;
@@ -629,16 +630,6 @@ public class XSLFTextRun implements TextRun {
}
@Override
- public Integer getIndex() {
- return null;
- }
-
- @Override
- public void setIndex(int index) {
- throw new UnsupportedOperationException("setIndex not supported by XSLFFontInfo.");
- }
-
- @Override
public String getTypeface() {
CTTextFont tf = getXmlObject(false);
return (tf != null && tf.isSetTypeface()) ? tf.getTypeface() : null;
@@ -829,4 +820,9 @@ public class XSLFTextRun implements TextRun {
return font;
}
}
+
+ @Override
+ public XSLFTextParagraph getParagraph() {
+ return _p;
+ }
}
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/extractor/TestXSLFPowerPointExtractor.java b/src/ooxml/testcases/org/apache/poi/xslf/extractor/TestXSLFPowerPointExtractor.java
index e63762d290..3f71533dd5 100644
--- a/src/ooxml/testcases/org/apache/poi/xslf/extractor/TestXSLFPowerPointExtractor.java
+++ b/src/ooxml/testcases/org/apache/poi/xslf/extractor/TestXSLFPowerPointExtractor.java
@@ -59,7 +59,7 @@ public class TestXSLFPowerPointExtractor {
// Check Basics
assertStartsWith(text, "Lorem ipsum dolor sit amet\n");
- assertContains(text, "amet\n\n");
+ assertContains(text, "amet\n");
// Our placeholder master text
// This shouldn't show up in the output
@@ -96,7 +96,7 @@ public class TestXSLFPowerPointExtractor {
extractor.setSlidesByDefault(false);
extractor.setNotesByDefault(true);
text = extractor.getText();
- assertEquals("\n\n1\n\n\n2\n", text);
+ assertEquals("\n1\n\n2\n", text);
// Both
extractor.setSlidesByDefault(true);
@@ -105,14 +105,14 @@ public class TestXSLFPowerPointExtractor {
String bothText =
"Lorem ipsum dolor sit amet\n" +
"Nunc at risus vel erat tempus posuere. Aenean non ante.\n" +
- "\n\n\n1\n" +
+ "\n\n1\n" +
"Lorem ipsum dolor sit amet\n" +
"Lorem\n" +
"ipsum\n" +
"dolor\n" +
"sit\n" +
"amet\n" +
- "\n\n\n2\n";
+ "\n\n2\n";
assertEquals(bothText, text);
// With Slides and Master Text
@@ -141,21 +141,21 @@ public class TestXSLFPowerPointExtractor {
String snmText =
"Lorem ipsum dolor sit amet\n" +
"Nunc at risus vel erat tempus posuere. Aenean non ante.\n" +
- "\n\n\n1\n" +
+ "\n\n1\n" +
"Lorem ipsum dolor sit amet\n" +
"Lorem\n" +
"ipsum\n" +
"dolor\n" +
"sit\n" +
"amet\n" +
- "\n\n\n2\n";
+ "\n\n2\n";
assertEquals(snmText, text);
// Via set defaults
extractor.setSlidesByDefault(false);
extractor.setNotesByDefault(true);
text = extractor.getText();
- assertEquals("\n\n1\n\n\n2\n", text);
+ assertEquals("\n1\n\n2\n", text);
}
}
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java
index afaf8d65b5..bb6167ee2f 100644
--- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java
+++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java
@@ -38,7 +38,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesMasterIdListE
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdListEntry;
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterIdListEntry;
-public class TestXMLSlideShow extends BaseTestSlideShow {
+public class TestXMLSlideShow extends BaseTestSlideShow<XSLFShape,XSLFTextParagraph> {
private OPCPackage pack;
@Override
@@ -81,6 +81,7 @@ public class TestXMLSlideShow extends BaseTestSlideShow {
xml.close();
}
+ @SuppressWarnings("deprecation")
@Test
public void testSlideBasics() throws IOException {
XMLSlideShow xml = new XMLSlideShow(pack);
@@ -136,7 +137,7 @@ public class TestXMLSlideShow extends BaseTestSlideShow {
assertEquals(0, xml.getProperties().getExtendedProperties().getUnderlyingProperties().getCharacters());
assertEquals(0, xml.getProperties().getExtendedProperties().getUnderlyingProperties().getLines());
- assertEquals(null, xml.getProperties().getCoreProperties().getTitle());
+ assertNull(xml.getProperties().getCoreProperties().getTitle());
assertFalse(xml.getProperties().getCoreProperties().getUnderlyingProperties().getSubjectProperty().isPresent());
xml.close();
@@ -146,8 +147,8 @@ public class TestXMLSlideShow extends BaseTestSlideShow {
public void testComments() throws Exception {
// Default sample file has none
XMLSlideShow xml = new XMLSlideShow(pack);
-
- assertEquals(null, xml.getCommentAuthors());
+
+ assertNull(xml.getCommentAuthors());
for (XSLFSlide slide : xml.getSlides()) {
assertTrue(slide.getComments().isEmpty());
@@ -186,19 +187,16 @@ public class TestXMLSlideShow extends BaseTestSlideShow {
xml.close();
}
- public SlideShow<?, ?> reopen(SlideShow<?, ?> show) {
- return reopen((XMLSlideShow)show);
- }
-
- private static XMLSlideShow reopen(XMLSlideShow show) {
- try {
- BufAccessBAOS bos = new BufAccessBAOS();
- show.write(bos);
- return new XMLSlideShow(new ByteArrayInputStream(bos.getBuf()));
- } catch (IOException e) {
- fail(e.getMessage());
- return null;
- }
+ @Override
+ public XMLSlideShow reopen(SlideShow<XSLFShape,XSLFTextParagraph> show) {
+ try {
+ BufAccessBAOS bos = new BufAccessBAOS();
+ show.write(bos);
+ return new XMLSlideShow(new ByteArrayInputStream(bos.getBuf()));
+ } catch (IOException e) {
+ fail(e.getMessage());
+ return null;
+ }
}
private static class BufAccessBAOS extends ByteArrayOutputStream {
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/Document.java b/src/scratchpad/src/org/apache/poi/hslf/record/Document.java
index 5e4019b001..4fd37a9b35 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/Document.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/Document.java
@@ -87,11 +87,11 @@ public final class Document extends PositionDependentRecordContainer
* Master Slides
*/
public SlideListWithText getMasterSlideListWithText() {
- for (int i = 0; i < slwts.length; i++) {
- if(slwts[i].getInstance() == SlideListWithText.MASTER) {
- return slwts[i];
- }
- }
+ for (SlideListWithText slwt : slwts) {
+ if (slwt.getInstance() == SlideListWithText.MASTER) {
+ return slwt;
+ }
+ }
return null;
}
@@ -100,11 +100,11 @@ public final class Document extends PositionDependentRecordContainer
* Slides, or null if there isn't one
*/
public SlideListWithText getSlideSlideListWithText() {
- for (int i = 0; i < slwts.length; i++) {
- if(slwts[i].getInstance() == SlideListWithText.SLIDES) {
- return slwts[i];
- }
- }
+ for (SlideListWithText slwt : slwts) {
+ if (slwt.getInstance() == SlideListWithText.SLIDES) {
+ return slwt;
+ }
+ }
return null;
}
/**
@@ -112,11 +112,11 @@ public final class Document extends PositionDependentRecordContainer
* notes, or null if there isn't one
*/
public SlideListWithText getNotesSlideListWithText() {
- for (int i = 0; i < slwts.length; i++) {
- if(slwts[i].getInstance() == SlideListWithText.NOTES) {
- return slwts[i];
- }
- }
+ for (SlideListWithText slwt : slwts) {
+ if (slwt.getInstance() == SlideListWithText.NOTES) {
+ return slwt;
+ }
+ }
return null;
}
@@ -124,7 +124,7 @@ public final class Document extends PositionDependentRecordContainer
/**
* Set things up, and find our more interesting children
*/
- protected Document(byte[] source, int start, int len) {
+ /* package */ Document(byte[] source, int start, int len) {
// Grab the header
_header = new byte[8];
System.arraycopy(source,start,_header,0,8);
@@ -186,7 +186,7 @@ public final class Document extends PositionDependentRecordContainer
// The new SlideListWithText should go in
// just before the EndDocumentRecord
Record endDoc = _children[_children.length - 1];
- if(endDoc.getRecordType() == RecordTypes.RoundTripCustomTableStyles12Atom.typeID) {
+ if(endDoc.getRecordType() == RecordTypes.RoundTripCustomTableStyles12.typeID) {
// last record can optionally be a RoundTripCustomTableStyles12Atom
endDoc = _children[_children.length - 2];
}
@@ -213,7 +213,7 @@ public final class Document extends PositionDependentRecordContainer
removeChild(slwt);
}
}
- slwts = lst.toArray(new SlideListWithText[lst.size()]);
+ slwts = lst.toArray(new SlideListWithText[0]);
}
/**
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 e2c49a9f35..cf01f0b2c9 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/DocumentAtom.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/DocumentAtom.java
@@ -17,25 +17,25 @@
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 org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
+
/**
* A Document Atom (type 1001). Holds misc information on the PowerPoint
* document, lots of them size and scale related.
- *
- * @author Nick Burch
*/
+@SuppressWarnings({"WeakerAccess", "unused"})
public final class DocumentAtom extends RecordAtom
{
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
- private byte[] _header;
- private static long _type = 1001l;
+ private final byte[] _header = new byte[8];
+ private static long _type = RecordTypes.DocumentAtom.typeID;
private long slideSizeX; // PointAtom, assume 1st 4 bytes = X
private long slideSizeY; // PointAtom, assume 2nd 4 bytes = Y
@@ -87,6 +87,11 @@ public final class DocumentAtom extends RecordAtom
return saveWithFonts != 0;
}
+ /** Set the font embedding state */
+ public void setSaveWithFonts(boolean saveWithFonts) {
+ this.saveWithFonts = (byte)(saveWithFonts ? 1 : 0);
+ }
+
/** Have the placeholders on the title slide been omitted? */
public boolean getOmitTitlePlace() {
return omitTitlePlace != 0;
@@ -108,41 +113,41 @@ public final class DocumentAtom extends RecordAtom
/**
* For the Document Atom
*/
- protected DocumentAtom(byte[] source, int start, int len) {
- // Sanity Checking
- if(len < 48) { len = 48; }
+ /* package */ DocumentAtom(byte[] source, int start, int len) {
+ final int maxLen = Math.max(len, 48);
+ LittleEndianByteArrayInputStream leis =
+ new LittleEndianByteArrayInputStream(source, start, maxLen);
// Get the header
- _header = new byte[8];
- System.arraycopy(source,start,_header,0,8);
+ leis.readFully(_header);
// Get the sizes and zoom ratios
- slideSizeX = LittleEndian.getInt(source,start+0+8);
- slideSizeY = LittleEndian.getInt(source,start+4+8);
- notesSizeX = LittleEndian.getInt(source,start+8+8);
- notesSizeY = LittleEndian.getInt(source,start+12+8);
- serverZoomFrom = LittleEndian.getInt(source,start+16+8);
- serverZoomTo = LittleEndian.getInt(source,start+20+8);
+ slideSizeX = leis.readInt();
+ slideSizeY = leis.readInt();
+ notesSizeX = leis.readInt();
+ notesSizeY = leis.readInt();
+ serverZoomFrom = leis.readInt();
+ serverZoomTo = leis.readInt();
// Get the master persists
- notesMasterPersist = LittleEndian.getInt(source,start+24+8);
- handoutMasterPersist = LittleEndian.getInt(source,start+28+8);
+ notesMasterPersist = leis.readInt();
+ handoutMasterPersist = leis.readInt();
// Get the ID of the first slide
- firstSlideNum = LittleEndian.getShort(source,start+32+8);
+ firstSlideNum = leis.readShort();
// Get the slide size type
- slideSizeType = LittleEndian.getShort(source,start+34+8);
+ slideSizeType = leis.readShort();
// Get the booleans as bytes
- saveWithFonts = source[start+36+8];
- omitTitlePlace = source[start+37+8];
- rightToLeft = source[start+38+8];
- showComments = source[start+39+8];
+ saveWithFonts = leis.readByte();
+ omitTitlePlace = leis.readByte();
+ rightToLeft = leis.readByte();
+ showComments = leis.readByte();
// If there's any other bits of data, keep them about
- reserved = IOUtils.safelyAllocate(len-40-8, MAX_RECORD_LENGTH);
- System.arraycopy(source,start+48,reserved,0,reserved.length);
+ reserved = IOUtils.safelyAllocate(maxLen-48, MAX_RECORD_LENGTH);
+ leis.readFully(reserved);
}
/**
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 fef1e797d4..bddb79efc6 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java
@@ -18,13 +18,19 @@
package org.apache.poi.hslf.record;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
+import org.apache.poi.common.usermodel.fonts.FontHeader;
import org.apache.poi.common.usermodel.fonts.FontInfo;
+import org.apache.poi.common.usermodel.fonts.FontPitch;
import org.apache.poi.hslf.usermodel.HSLFFontInfo;
import org.apache.poi.hslf.usermodel.HSLFFontInfoPredefined;
+import org.apache.poi.util.IOUtils;
import org.apache.poi.util.POILogger;
/**
@@ -32,11 +38,12 @@ import org.apache.poi.util.POILogger;
* about all the fonts in the presentation.
*/
+@SuppressWarnings("WeakerAccess")
public final class FontCollection extends RecordContainer {
private final Map<String,HSLFFontInfo> fonts = new LinkedHashMap<>();
private byte[] _header;
- protected FontCollection(byte[] source, int start, int len) {
+ /* package */ FontCollection(byte[] source, int start, int len) {
_header = new byte[8];
System.arraycopy(source,start,_header,0,8);
@@ -44,8 +51,13 @@ public final class FontCollection extends RecordContainer {
for (Record r : _children){
if(r instanceof FontEntityAtom) {
- HSLFFontInfo fi = new HSLFFontInfo((FontEntityAtom)r);
- fonts.put(fi.getTypeface(), fi);
+ HSLFFontInfo fi = new HSLFFontInfo((FontEntityAtom) r);
+ fonts.put(fi.getTypeface(), fi);
+ } else if (r instanceof FontEmbeddedData) {
+ FontEmbeddedData fed = (FontEmbeddedData)r;
+ FontHeader fontHeader = fed.getFontHeader();
+ HSLFFontInfo fi = addFont(fontHeader);
+ fi.addFacet(fed);
} else {
logger.log(POILogger.WARN, "Warning: FontCollection child wasn't a FontEntityAtom, was " + r.getClass().getSimpleName());
}
@@ -88,7 +100,7 @@ public final class FontCollection extends RecordContainer {
fi = new HSLFFontInfo(fontInfo);
fi.setIndex(fonts.size());
fonts.put(fi.getTypeface(), fi);
-
+
FontEntityAtom fnt = fi.createRecord();
// Append new child to the end
@@ -98,6 +110,58 @@ public final class FontCollection extends RecordContainer {
return fi;
}
+ public HSLFFontInfo addFont(InputStream fontData) throws IOException {
+ FontHeader fontHeader = new FontHeader();
+ InputStream is = fontHeader.bufferInit(fontData);
+
+ HSLFFontInfo fi = addFont(fontHeader);
+
+ // always overwrite the font info properties when a font data given
+ // as the font info properties are assigned generically when only a typeface is given
+ FontEntityAtom fea = fi.getFontEntityAtom();
+ assert (fea != null);
+ fea.setCharSet(fontHeader.getCharsetByte());
+ fea.setPitchAndFamily(FontPitch.getNativeId(fontHeader.getPitch(),fontHeader.getFamily()));
+
+ // always activate subsetting
+ fea.setFontFlags(1);
+ // true type font and no font substitution
+ fea.setFontType(12);
+
+ Record after = fea;
+
+ final int insertIdx = getFacetIndex(fontHeader.isItalic(), fontHeader.isBold());
+
+ FontEmbeddedData newChild = null;
+ for (FontEmbeddedData fed : fi.getFacets()) {
+ FontHeader fh = fed.getFontHeader();
+ final int curIdx = getFacetIndex(fh.isItalic(), fh.isBold());
+
+ if (curIdx == insertIdx) {
+ newChild = fed;
+ break;
+ } else if (curIdx > insertIdx) {
+ // the new facet needs to be inserted before the current facet
+ break;
+ }
+
+ after = fed;
+ }
+
+ if (newChild == null) {
+ newChild = new FontEmbeddedData();
+ addChildAfter(newChild, after);
+ fi.addFacet(newChild);
+ }
+
+ newChild.setFontData(IOUtils.toByteArray(is));
+ return fi;
+ }
+
+ private static int getFacetIndex(boolean isItalic, boolean isBold) {
+ return (isItalic ? 2 : 0) | (isBold ? 1 : 0);
+ }
+
/**
* Lookup a FontInfo object by its typeface
@@ -132,4 +196,8 @@ public final class FontCollection extends RecordContainer {
public int getNumberOfFonts() {
return fonts.size();
}
+
+ public List<HSLFFontInfo> getFonts() {
+ return new ArrayList<>(fonts.values());
+ }
}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/FontEmbeddedData.java b/src/scratchpad/src/org/apache/poi/hslf/record/FontEmbeddedData.java
new file mode 100644
index 0000000000..f60ba4a697
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/FontEmbeddedData.java
@@ -0,0 +1,116 @@
+/* ====================================================================
+ 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.hslf.record;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.poi.common.usermodel.fonts.FontFacet;
+import org.apache.poi.common.usermodel.fonts.FontHeader;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndian;
+
+@SuppressWarnings("WeakerAccess")
+public class FontEmbeddedData extends RecordAtom implements FontFacet {
+ //arbitrarily selected; may need to increase
+ private static final int MAX_RECORD_LENGTH = 1_000_000;
+
+ /**
+ * Record header.
+ */
+ private byte[] _header;
+
+ /**
+ * Record data - An EOT Font
+ */
+ private byte[] _data;
+
+ /**
+ * Constructs a brand new font embedded record.
+ */
+ /* package */ FontEmbeddedData() {
+ _header = new byte[8];
+ _data = new byte[4];
+
+ LittleEndian.putShort(_header, 2, (short)getRecordType());
+ LittleEndian.putInt(_header, 4, _data.length);
+ }
+
+ /**
+ * Constructs the font embedded record from its source data.
+ *
+ * @param source the source data as a byte array.
+ * @param start the start offset into the byte array.
+ * @param len the length of the slice in the byte array.
+ */
+ /* package */ FontEmbeddedData(byte[] source, int start, int len) {
+ // Get the header.
+ _header = new byte[8];
+ System.arraycopy(source,start,_header,0,8);
+
+ // Get the record data.
+ _data = IOUtils.safelyAllocate(len-8, MAX_RECORD_LENGTH);
+ System.arraycopy(source,start+8,_data,0,len-8);
+
+ // Must be at least 4 bytes long
+ if(_data.length < 4) {
+ throw new IllegalArgumentException("The length of the data for a ExObjListAtom must be at least 4 bytes, but was only " + _data.length);
+ }
+ }
+
+ @Override
+ public long getRecordType() {
+ return RecordTypes.FontEmbeddedData.typeID;
+ }
+
+ @Override
+ public void writeOut(OutputStream out) throws IOException {
+ out.write(_header);
+ out.write(_data);
+ }
+
+ public void setFontData(byte[] fontData) {
+ _data = fontData.clone();
+ LittleEndian.putInt(_header, 4, _data.length);
+ }
+
+ public FontHeader getFontHeader() {
+ FontHeader h = new FontHeader();
+ h.init(_data, 0, _data.length);
+ return h;
+ }
+
+ @Override
+ public int getWeight() {
+ return getFontHeader().getWeight();
+ }
+
+ @Override
+ public boolean isItalic() {
+ return getFontHeader().isItalic();
+ }
+
+ public String getTypeface() {
+ return getFontHeader().getFamilyName();
+ }
+
+ @Override
+ public Object getFontData() {
+ return this;
+ }
+}
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 4f3cdd89bc..2d172d7b5e 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java
@@ -43,7 +43,7 @@ public final class FontEntityAtom extends RecordAtom {
/**
* record header
*/
- private byte[] _header;
+ private final byte[] _header = new byte[8];
/**
* record data
@@ -53,9 +53,8 @@ public final class FontEntityAtom extends RecordAtom {
/**
* Build an instance of <code>FontEntityAtom</code> from on-disk data
*/
- protected FontEntityAtom(byte[] source, int start, int len) {
+ /* package */ FontEntityAtom(byte[] source, int start, int len) {
// Get the header
- _header = new byte[8];
System.arraycopy(source,start,_header,0,8);
// Grab the record data
@@ -69,7 +68,6 @@ public final class FontEntityAtom extends RecordAtom {
public FontEntityAtom() {
_recdata = new byte[68];
- _header = new byte[8];
LittleEndian.putShort(_header, 2, (short)getRecordType());
LittleEndian.putInt(_header, 4, _recdata.length);
}
@@ -108,7 +106,7 @@ public final class FontEntityAtom extends RecordAtom {
byte[] bytes = StringUtil.getToUnicodeLE(name);
System.arraycopy(bytes, 0, _recdata, 0, bytes.length);
// null the remaining bytes
- Arrays.fill(_recdata, 64-bytes.length, 64, (byte)0);
+ Arrays.fill(_recdata, bytes.length, 64, (byte)0);
}
public void setFontIndex(int idx){
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 233d54a67f..a1697d511e 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java
@@ -62,7 +62,18 @@ public enum RecordTypes {
NamedShow(1041,null),
NamedShowSlides(1042,null),
SheetProperties(1044,null),
- RoundTripCustomTableStyles12Atom(1064,null),
+ OriginalMainMasterId(1052,null),
+ CompositeMasterId(1052,null),
+ RoundTripContentMasterInfo12(1054,null),
+ RoundTripShapeId12(1055,null),
+ RoundTripHFPlaceholder12(1056,RoundTripHFPlaceholder12::new),
+ RoundTripContentMasterId(1058,null),
+ RoundTripOArtTextStyles12(1059,null),
+ RoundTripShapeCheckSumForCustomLayouts12(1062,null),
+ RoundTripNotesMasterTextStyles12(1063,null),
+ RoundTripCustomTableStyles12(1064,null),
+
+
List(2000,DocInfoListContainer::new),
FontCollection(2005,FontCollection::new),
BookmarkCollection(2019,null),
@@ -92,7 +103,7 @@ public enum RecordTypes {
DefaultRulerAtom(4011,null),
StyleTextProp9Atom(4012, StyleTextProp9Atom::new), //0x0FAC RT_StyleTextProp9Atom
FontEntityAtom(4023,FontEntityAtom::new),
- FontEmbeddedData(4024,null),
+ FontEmbeddedData(4024,FontEmbeddedData::new),
CString(4026,CString::new),
MetaFile(4033,null),
ExOleObjAtom(4035,ExOleObjAtom::new),
@@ -159,17 +170,6 @@ public enum RecordTypes {
// Records ~12050 seem to be related to Document Encryption
DocumentEncryptionAtom(12052,DocumentEncryptionAtom::new),
- OriginalMainMasterId(1052,null),
- CompositeMasterId(1052,null),
- RoundTripContentMasterInfo12(1054,null),
- RoundTripShapeId12(1055,null),
- RoundTripHFPlaceholder12(1056,RoundTripHFPlaceholder12::new),
- RoundTripContentMasterId(1058,null),
- RoundTripOArtTextStyles12(1059,null),
- RoundTripShapeCheckSumForCustomLayouts12(1062,null),
- RoundTripNotesMasterTextStyles12(1063,null),
- RoundTripCustomTableStyles12(1064,null),
-
// records greater then 0xF000 belong to with Microsoft Office Drawing format also known as Escher
EscherDggContainer(0xF000,null),
EscherDgg(0xf006,null),
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfo.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfo.java
index 57c473173a..3b509276ca 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfo.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfo.java
@@ -17,13 +17,18 @@
package org.apache.poi.hslf.usermodel;
+import java.util.ArrayList;
+import java.util.List;
+
import org.apache.poi.common.usermodel.fonts.FontCharset;
import org.apache.poi.common.usermodel.fonts.FontFamily;
import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.common.usermodel.fonts.FontPitch;
+import org.apache.poi.hslf.record.FontEmbeddedData;
import org.apache.poi.hslf.record.FontEntityAtom;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.Internal;
/**
* Represents a Font used in a presentation.<p>
@@ -32,6 +37,7 @@ import org.apache.poi.util.BitFieldFactory;
*
* @since POI 3.17-beta2
*/
+@SuppressWarnings("WeakerAccess")
public class HSLFFontInfo implements FontInfo {
public enum FontRenderType {
@@ -53,6 +59,8 @@ public class HSLFFontInfo implements FontInfo {
private FontPitch pitch = FontPitch.VARIABLE;
private boolean isSubsetted;
private boolean isSubstitutable = true;
+ private final List<FontEmbeddedData> facets = new ArrayList<>();
+ private FontEntityAtom fontEntityAtom;
/**
* Creates a new instance of HSLFFontInfo with more or sensible defaults.<p>
@@ -70,6 +78,7 @@ public class HSLFFontInfo implements FontInfo {
* Creates a new instance of HSLFFontInfo and initialize it from the supplied font atom
*/
public HSLFFontInfo(FontEntityAtom fontAtom){
+ fontEntityAtom = fontAtom;
setIndex(fontAtom.getFontIndex());
setTypeface(fontAtom.getFontName());
setCharset(FontCharset.valueOf(fontAtom.getCharSet()));
@@ -187,7 +196,11 @@ public class HSLFFontInfo implements FontInfo {
}
public FontEntityAtom createRecord() {
+ assert(fontEntityAtom == null);
+
FontEntityAtom fnt = new FontEntityAtom();
+ fontEntityAtom = fnt;
+
fnt.setFontIndex(getIndex() << 4);
fnt.setFontName(getTypeface());
fnt.setCharSet(getCharset().getNativeId());
@@ -212,4 +225,18 @@ public class HSLFFontInfo implements FontInfo {
fnt.setPitchAndFamily(FontPitch.getNativeId(pitch, family));
return fnt;
}
+
+ public void addFacet(FontEmbeddedData facet) {
+ facets.add(facet);
+ }
+
+ @Override
+ public List<FontEmbeddedData> getFacets() {
+ return facets;
+ }
+
+ @Internal
+ public FontEntityAtom getFontEntityAtom() {
+ return fontEntityAtom;
+ }
}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfoPredefined.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfoPredefined.java
index 1f016f99e5..d2dc44095e 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfoPredefined.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFontInfoPredefined.java
@@ -44,16 +44,6 @@ public enum HSLFFontInfoPredefined implements FontInfo {
this.pitch = pitch;
this.family = family;
}
-
- @Override
- public Integer getIndex() {
- return -1;
- }
-
- @Override
- public void setIndex(int index) {
- throw new UnsupportedOperationException("Predefined enum can't be changed.");
- }
@Override
public String getTypeface() {
@@ -61,37 +51,17 @@ public enum HSLFFontInfoPredefined implements FontInfo {
}
@Override
- public void setTypeface(String typeface) {
- throw new UnsupportedOperationException("Predefined enum can't be changed.");
- }
-
- @Override
public FontCharset getCharset() {
return charset;
}
@Override
- public void setCharset(FontCharset charset) {
- throw new UnsupportedOperationException("Predefined enum can't be changed.");
- }
-
- @Override
public FontFamily getFamily() {
return family;
}
@Override
- public void setFamily(FontFamily family) {
- throw new UnsupportedOperationException("Predefined enum can't be changed.");
- }
-
- @Override
public FontPitch getPitch() {
return pitch;
}
-
- @Override
- public void setPitch(FontPitch pitch) {
- throw new UnsupportedOperationException("Predefined enum can't be changed.");
- }
}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java
index de90334840..2343ccc489 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java
@@ -50,7 +50,6 @@ import org.apache.poi.poifs.filesystem.Ole10Native;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.sl.usermodel.MasterSheet;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
-import org.apache.poi.sl.usermodel.Resources;
import org.apache.poi.sl.usermodel.SlideShow;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal;
@@ -892,6 +891,21 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
}
/**
+ * Add a font in this presentation and also embed its font data
+ *
+ * @param fontData the EOT font data as stream
+ *
+ * @return the registered HSLFFontInfo - the font info object is unique based on the typeface
+ *
+ * @since POI 4.1.0
+ */
+ public HSLFFontInfo addFont(InputStream fontData) throws IOException {
+ Document doc = getDocumentRecord();
+ doc.getDocumentAtom().setSaveWithFonts(true);
+ return doc.getEnvironment().getFontCollection().addFont(fontData);
+ }
+
+ /**
* Get a font by index
*
* @param idx
@@ -912,6 +926,11 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
return getDocumentRecord().getEnvironment().getFontCollection().getNumberOfFonts();
}
+ @Override
+ public List<HSLFFontInfo> getFonts() {
+ return getDocumentRecord().getEnvironment().getFontCollection().getFonts();
+ }
+
/**
* Return Header / Footer settings for slides
*
@@ -1127,12 +1146,6 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
return null;
}
- @Override
- public Resources getResources() {
- // TODO implement or throw exception if not supported
- return null;
- }
-
/**
* @return the handler class which holds the hslf records
*/
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
index a444a5b2b6..94cea55ab2 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
@@ -44,8 +44,9 @@ import org.apache.poi.util.POILogger;
* Represents a run of text, all with the same style
*
*/
+@SuppressWarnings({"WeakerAccess", "Duplicates", "unused"})
public final class HSLFTextRun implements TextRun {
- protected POILogger logger = POILogFactory.getLogger(this.getClass());
+ private static final POILogger logger = POILogFactory.getLogger(HSLFTextRun.class);
/** The TextRun we belong to */
private HSLFTextParagraph parentParagraph;
@@ -132,17 +133,17 @@ public final class HSLFTextRun implements TextRun {
return getFlag(index);
}
- protected boolean getFlag(int index) {
+ boolean getFlag(int index) {
BitMaskTextProp prop = (characterStyle == null) ? null : characterStyle.findByName(CharFlagsTextProp.NAME);
if (prop == null || !prop.getSubPropMatches()[index]) {
- prop = getMasterProp(CharFlagsTextProp.NAME);
+ prop = getMasterProp();
}
- return prop == null ? false : prop.getSubValue(index);
+ return prop != null && prop.getSubValue(index);
}
- private <T extends TextProp> T getMasterProp(final String name) {
+ private <T extends TextProp> T getMasterProp() {
final int txtype = parentParagraph.getRunType();
final HSLFSheet sheet = parentParagraph.getSheet();
if (sheet == null) {
@@ -155,7 +156,8 @@ public final class HSLFTextRun implements TextRun {
logger.log(POILogger.WARN, "MasterSheet is not available");
return null;
}
-
+
+ String name = CharFlagsTextProp.NAME;
final TextPropCollection col = master.getPropCollection(txtype, parentParagraph.getIndentLevel(), name, true);
return (col == null) ? null : col.findByName(name);
}
@@ -302,7 +304,7 @@ public final class HSLFTextRun implements TextRun {
@Override
public void setFontFamily(String typeface) {
- setFontInfo(new HSLFFontInfo(typeface), FontGroup.LATIN);
+ setFontFamily(typeface, FontGroup.LATIN);
}
@Override
@@ -330,7 +332,7 @@ public final class HSLFTextRun implements TextRun {
switch (fg) {
default:
case LATIN:
- propName = "font.index";
+ propName = "ansi.font.index";
break;
case COMPLEX_SCRIPT:
// TODO: implement TextCFException10 structure
@@ -350,6 +352,7 @@ public final class HSLFTextRun implements TextRun {
}
+ setCharTextPropVal("font.index", fontIdx);
setCharTextPropVal(propName, fontIdx);
}
@@ -435,8 +438,8 @@ public final class HSLFTextRun implements TextRun {
setFontColor(rgb);
}
- protected void setFlag(int index, boolean value) {
- BitMaskTextProp prop = (BitMaskTextProp)characterStyle.addWithName(CharFlagsTextProp.NAME);
+ private void setFlag(int index, boolean value) {
+ BitMaskTextProp prop = characterStyle.addWithName(CharFlagsTextProp.NAME);
prop.setSubValue(value, index);
}
@@ -469,7 +472,7 @@ public final class HSLFTextRun implements TextRun {
*
* @param link the hyperlink
*/
- protected void setHyperlink(HSLFHyperlink link) {
+ /* package */ void setHyperlink(HSLFHyperlink link) {
this.link = link;
}
@@ -521,4 +524,9 @@ public final class HSLFTextRun implements TextRun {
private FontGroup safeFontGroup(FontGroup fontGroup) {
return (fontGroup != null) ? fontGroup : FontGroup.getFontGroupFirst(getRawText());
}
+
+ @Override
+ public HSLFTextParagraph getParagraph() {
+ return parentParagraph;
+ }
}
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 621fa6a095..211c9fc263 100644
--- a/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java
+++ b/src/scratchpad/src/org/apache/poi/hwmf/record/HwmfFont.java
@@ -22,6 +22,7 @@ import java.nio.charset.StandardCharsets;
import org.apache.poi.common.usermodel.fonts.FontCharset;
import org.apache.poi.common.usermodel.fonts.FontFamily;
+import org.apache.poi.common.usermodel.fonts.FontHeader;
import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.common.usermodel.fonts.FontPitch;
import org.apache.poi.util.BitField;
@@ -32,6 +33,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 {
/**
@@ -289,7 +291,7 @@ public class HwmfFont implements FontInfo {
/**
* An 8-bit unsigned integer that defines the character set.
- * It SHOULD be set to a value in the {@link WmfCharset} Enumeration.
+ * It SHOULD be set to a value in the {@link FontCharset} Enumeration.
*
* The DEFAULT_CHARSET value MAY be used to allow the name and size of a font to fully
* describe the logical font. If the specified font name does not exist, a font in another character
@@ -373,7 +375,7 @@ public class HwmfFont implements FontInfo {
height = -12;
width = 0;
escapement = 0;
- weight = 400;
+ weight = FontHeader.REGULAR_WEIGHT;
italic = false;
underline = false;
strikeOut = false;
@@ -438,51 +440,21 @@ public class HwmfFont implements FontInfo {
}
@Override
- public void setFamily(FontFamily family) {
- throw new UnsupportedOperationException("setCharset not supported by HwmfFont.");
- }
-
- @Override
public FontPitch getPitch() {
return FontPitch.valueOf((pitchAndFamily >>> 6) & 3);
}
@Override
- public void setPitch(FontPitch pitch) {
- throw new UnsupportedOperationException("setPitch not supported by HwmfFont.");
- }
-
- @Override
- public Integer getIndex() {
- return null;
- }
-
- @Override
- public void setIndex(int index) {
- throw new UnsupportedOperationException("setIndex not supported by HwmfFont.");
- }
-
- @Override
public String getTypeface() {
return facename;
}
@Override
- public void setTypeface(String typeface) {
- throw new UnsupportedOperationException("setTypeface not supported by HwmfFont.");
- }
-
- @Override
public FontCharset getCharset() {
return charSet;
}
@Override
- public void setCharset(FontCharset charset) {
- throw new UnsupportedOperationException("setCharset not supported by HwmfFont.");
- }
-
- @Override
public String toString() {
return "{ height: "+height+
", width: "+width+
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/extractor/TestExtractor.java b/src/scratchpad/testcases/org/apache/poi/hslf/extractor/TestExtractor.java
index 78ca26ee3b..9fa47b4107 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/extractor/TestExtractor.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/extractor/TestExtractor.java
@@ -29,7 +29,9 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.util.BitSet;
import java.util.List;
+import java.util.stream.Collectors;
import org.apache.poi.POIDataSamples;
import org.apache.poi.hslf.usermodel.HSLFObjectShape;
@@ -52,12 +54,22 @@ public final class TestExtractor {
/**
* Extractor primed on the 2 page basic test data
*/
- private static final String expectText = "This is a test title\nThis is a test subtitle\nThis is on page 1\nThis is the title on page 2\nThis is page two\nIt has several blocks of text\nNone of them have formatting\n";
+ private static final String EXPECTED_PAGE1 =
+ "This is a test title\n" +
+ "This is a test subtitle\n\n" +
+ "This is on page 1\n";
- /**
- * Extractor primed on the 1 page but text-box'd test data
- */
- private static final String expectText2 = "Hello, World!!!\nI am just a poor boy\nThis is Times New Roman\nPlain Text \n";
+ private static final String EXPECTED_PAGE2 =
+ "This is the title on page 2\n" +
+ "This is page two\n\n" +
+ "It has several blocks of text\n\n" +
+ "None of them have formatting\n";
+
+ private static final String NOTES_PAGE1 =
+ "\nThese are the notes for page 1\n";
+
+ private static final String NOTES_PAGE2 =
+ "\nThese are the notes on page two, again lacking formatting\n";
/**
* Where our embeded files live
@@ -75,9 +87,16 @@ public final class TestExtractor {
public void testReadSheetText() throws IOException {
// Basic 2 page example
try (SlideShowExtractor ppe = openExtractor("basic_test_ppt_file.ppt")) {
- assertEquals(expectText, ppe.getText());
+ assertEquals(EXPECTED_PAGE1+EXPECTED_PAGE2, ppe.getText());
}
+ // Extractor primed on the 1 page but text-box'd test data
+ final String expectText2 =
+ "Hello, World!!!\n" +
+ "I am just a poor boy\n" +
+ "This is Times New Roman\n" +
+ "Plain Text \n";
+
// 1 page example with text boxes
try (SlideShowExtractor ppe = openExtractor("with_textbox.ppt")) {
assertEquals(expectText2, ppe.getText());
@@ -92,8 +111,7 @@ public final class TestExtractor {
ppe.setSlidesByDefault(false);
ppe.setMasterByDefault(false);
String notesText = ppe.getText();
- String expText = "\nThese are the notes for page 1\n\nThese are the notes on page two, again lacking formatting\n";
- assertEquals(expText, notesText);
+ assertEquals(NOTES_PAGE1+NOTES_PAGE2, notesText);
}
// Other one doesn't have notes
@@ -109,14 +127,8 @@ public final class TestExtractor {
@Test
public void testReadBoth() throws IOException {
- String[] slText = new String[]{
- "This is a test title\nThis is a test subtitle\nThis is on page 1\n",
- "This is the title on page 2\nThis is page two\nIt has several blocks of text\nNone of them have formatting\n"
- };
- String[] ntText = new String[]{
- "\nThese are the notes for page 1\n",
- "\nThese are the notes on page two, again lacking formatting\n"
- };
+ String[] slText = { EXPECTED_PAGE1, EXPECTED_PAGE2 };
+ String[] ntText = { NOTES_PAGE1, NOTES_PAGE2 };
try (SlideShowExtractor ppe = openExtractor("basic_test_ppt_file.ppt")) {
ppe.setSlidesByDefault(true);
@@ -165,8 +177,8 @@ public final class TestExtractor {
final DirectoryNode root = fs.getRoot();
final String[] TEST_SET = {
- "MBD0000A3B6", "Sample PowerPoint file\nThis is the 1st file\nNot much too it\n",
- "MBD0000A3B3", "Sample PowerPoint file\nThis is the 2nd file\nNot much too it either\n"
+ "MBD0000A3B6", "Sample PowerPoint file\nThis is the 1st file\n\nNot much too it\n",
+ "MBD0000A3B3", "Sample PowerPoint file\nThis is the 2nd file\n\nNot much too it either\n"
};
for (int i=0; i<TEST_SET.length; i+=2) {
@@ -386,7 +398,7 @@ public final class TestExtractor {
// Open directly
try (SlideShow<?,?> ppt = SlideShowFactory.create(npoifs.getRoot());
SlideShowExtractor<?,?> extractor = new SlideShowExtractor<>(ppt)) {
- assertEquals(expectText, extractor.getText());
+ assertEquals(EXPECTED_PAGE1+EXPECTED_PAGE2, extractor.getText());
}
}
}
@@ -457,4 +469,19 @@ public final class TestExtractor {
private static int countMatches(final String base, final String find) {
return base.split(find).length-1;
}
+
+ @Test
+ public void glyphCounting() throws IOException {
+ String[] expected = {
+ "Times New Roman", "\t\n ,-./01234679:ABDEFGILMNOPRSTVWabcdefghijklmnoprstuvwxyz\u00F3\u201C\u201D",
+ "Arial", " Lacdilnost"
+ };
+ try (SlideShowExtractor ppt = openExtractor("45543.ppt")) {
+ for (int i=0; i<expected.length; i+=2) {
+ BitSet l = ppt.getCodepoints(expected[i], null, null);
+ String s = l.stream().mapToObj(Character::toChars).map(String::valueOf).collect(Collectors.joining());
+ assertEquals(expected[i+1], s);
+ }
+ }
+ }
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestHSLFSlideShow.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestHSLFSlideShow.java
index 7c2f7fe473..8e1ad7a928 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestHSLFSlideShow.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestHSLFSlideShow.java
@@ -27,7 +27,7 @@ import org.apache.poi.sl.usermodel.BaseTestSlideShow;
import org.apache.poi.sl.usermodel.SlideShow;
import org.junit.Test;
-public class TestHSLFSlideShow extends BaseTestSlideShow {
+public class TestHSLFSlideShow extends BaseTestSlideShow<HSLFShape,HSLFTextParagraph> {
@Override
public HSLFSlideShow createSlideShow() {
return new HSLFSlideShow();
@@ -39,11 +39,7 @@ public class TestHSLFSlideShow extends BaseTestSlideShow {
assertNotNull(createSlideShow());
}
- public SlideShow<?, ?> reopen(SlideShow<?, ?> show) {
- return reopen((HSLFSlideShow)show);
- }
-
- public static HSLFSlideShow reopen(HSLFSlideShow show) {
+ public HSLFSlideShow reopen(SlideShow<HSLFShape,HSLFTextParagraph> show) {
try {
BufAccessBAOS bos = new BufAccessBAOS();
show.write(bos);
@@ -55,7 +51,7 @@ public class TestHSLFSlideShow extends BaseTestSlideShow {
}
private static class BufAccessBAOS extends ByteArrayOutputStream {
- public byte[] getBuf() {
+ byte[] getBuf() {
return buf;
}
}
diff --git a/src/testcases/org/apache/poi/sl/usermodel/BaseTestSlideShow.java b/src/testcases/org/apache/poi/sl/usermodel/BaseTestSlideShow.java
index c4daa50b3f..c12c96559f 100644
--- a/src/testcases/org/apache/poi/sl/usermodel/BaseTestSlideShow.java
+++ b/src/testcases/org/apache/poi/sl/usermodel/BaseTestSlideShow.java
@@ -17,6 +17,7 @@
package org.apache.poi.sl.usermodel;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -29,20 +30,24 @@ import java.io.InputStream;
import java.util.List;
import org.apache.poi.POIDataSamples;
+import org.apache.poi.common.usermodel.fonts.FontInfo;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
import org.apache.poi.sl.usermodel.TabStop.TabStopType;
import org.junit.Test;
-public abstract class BaseTestSlideShow {
+public abstract class BaseTestSlideShow<
+ S extends Shape<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+> {
protected static final POIDataSamples slTests = POIDataSamples.getSlideShowInstance();
- public abstract SlideShow<?, ?> createSlideShow();
+ public abstract SlideShow<S,P> createSlideShow();
- public abstract SlideShow<?, ?> reopen(SlideShow<?, ?> show);
+ public abstract SlideShow<S,P> reopen(SlideShow<S,P> show);
@Test
public void addPicture_File() throws IOException {
- SlideShow<?,?> show = createSlideShow();
+ SlideShow<S,P> show = createSlideShow();
File f = slTests.getFile("clock.jpg");
assertEquals(0, show.getPictureData().size());
@@ -55,26 +60,18 @@ public abstract class BaseTestSlideShow {
@Test
public void addPicture_Stream() throws IOException {
- SlideShow<?,?> show = createSlideShow();
- try {
- InputStream stream = slTests.openResourceAsStream("clock.jpg");
- try {
- assertEquals(0, show.getPictureData().size());
- PictureData picture = show.addPicture(stream, PictureType.JPEG);
- assertEquals(1, show.getPictureData().size());
- assertSame(picture, show.getPictureData().get(0));
-
- } finally {
- stream.close();
- }
- } finally {
- show.close();
+ try (SlideShow<S,P> show = createSlideShow();
+ InputStream stream = slTests.openResourceAsStream("clock.jpg")) {
+ assertEquals(0, show.getPictureData().size());
+ PictureData picture = show.addPicture(stream, PictureType.JPEG);
+ assertEquals(1, show.getPictureData().size());
+ assertSame(picture, show.getPictureData().get(0));
}
}
@Test
public void addPicture_ByteArray() throws IOException {
- SlideShow<?,?> show = createSlideShow();
+ SlideShow<S,P> show = createSlideShow();
byte[] data = slTests.readFile("clock.jpg");
assertEquals(0, show.getPictureData().size());
@@ -87,7 +84,7 @@ public abstract class BaseTestSlideShow {
@Test
public void findPicture() throws IOException {
- SlideShow<?,?> show = createSlideShow();
+ SlideShow<S,P> show = createSlideShow();
byte[] data = slTests.readFile("clock.jpg");
assertNull(show.findPictureData(data));
@@ -101,11 +98,11 @@ public abstract class BaseTestSlideShow {
@Test
public void addTabStops() throws IOException {
- try (final SlideShow<?,?> show1 = createSlideShow()) {
+ try (final SlideShow<S,P> show1 = createSlideShow()) {
// first set the TabStops in the Master sheet
- final MasterSheet<?, ?> master1 = show1.getSlideMasters().get(0);
- final AutoShape<?, ?> master1_as = (AutoShape<?,?>)master1.getPlaceholder(Placeholder.BODY);
- final TextParagraph<?, ?, ? extends TextRun> master1_tp = master1_as.getTextParagraphs().get(0);
+ final MasterSheet<S,P> master1 = show1.getSlideMasters().get(0);
+ final AutoShape<S,P> master1_as = (AutoShape<S,P>)master1.getPlaceholder(Placeholder.BODY);
+ final P master1_tp = master1_as.getTextParagraphs().get(0);
master1_tp.clearTabStops();
int i1 = 0;
for (final TabStopType tst : TabStopType.values()) {
@@ -114,11 +111,11 @@ public abstract class BaseTestSlideShow {
}
// then set it on a normal slide
- final Slide<?,?> slide1 = show1.createSlide();
- final AutoShape<?, ?> slide1_as = slide1.createAutoShape();
+ final Slide<S,P> slide1 = show1.createSlide();
+ final AutoShape<S,P> slide1_as = slide1.createAutoShape();
slide1_as.setText("abc");
slide1_as.setAnchor(new Rectangle2D.Double(100,100,100,100));
- final TextParagraph<?, ?, ? extends TextRun> slide1_tp = slide1_as.getTextParagraphs().get(0);
+ final P slide1_tp = slide1_as.getTextParagraphs().get(0);
slide1_tp.getTextRuns().get(0).setFontColor(new Color(0x563412));
slide1_tp.clearTabStops();
int i2 = 0;
@@ -127,10 +124,10 @@ public abstract class BaseTestSlideShow {
i2++;
}
- try (final SlideShow<?, ?> show2 = reopen(show1)) {
- final MasterSheet<?, ?> master2 = show2.getSlideMasters().get(0);
- final AutoShape<?, ?> master2_as = (AutoShape<?,?>)master2.getPlaceholder(Placeholder.BODY);
- final TextParagraph<?, ?, ? extends TextRun> master2_tp = master2_as.getTextParagraphs().get(0);
+ try (final SlideShow<S,P> show2 = reopen(show1)) {
+ final MasterSheet<S,P> master2 = show2.getSlideMasters().get(0);
+ final AutoShape<S,P> master2_as = (AutoShape<S,P>)master2.getPlaceholder(Placeholder.BODY);
+ final P master2_tp = master2_as.getTextParagraphs().get(0);
final List<? extends TabStop> master2_tabStops = master2_tp.getTabStops();
assertNotNull(master2_tabStops);
int i3 = 0;
@@ -142,9 +139,10 @@ public abstract class BaseTestSlideShow {
}
- final Slide<?,?> slide2 = show2.getSlides().get(0);
- final AutoShape<?,?> slide2_as = (AutoShape<?,?>)slide2.getShapes().get(0);
- final TextParagraph<?, ?, ? extends TextRun> slide2_tp = slide2_as.getTextParagraphs().get(0);
+ final Slide<S,P> slide2 = show2.getSlides().get(0);
+ @SuppressWarnings("unchecked")
+ final AutoShape<S,P> slide2_as = (AutoShape<S,P>)slide2.getShapes().get(0);
+ final P slide2_tp = slide2_as.getTextParagraphs().get(0);
final List<? extends TabStop> slide2_tabStops = slide2_tp.getTabStops();
assertNotNull(slide2_tabStops);
int i4 = 0;
@@ -162,18 +160,35 @@ public abstract class BaseTestSlideShow {
public void shapeAndSlideName() throws IOException {
final String file = "SampleShow.ppt"+(getClass().getSimpleName().contains("XML")?"x":"");
try (final InputStream is = slTests.openResourceAsStream(file);
- final SlideShow<? extends Shape, ?> ppt = SlideShowFactory.create(is)) {
- final List<? extends Shape> shapes1 = ppt.getSlides().get(0).getShapes();
+ final SlideShow<S,P> ppt = SlideShowFactory.create(is)) {
+ final List<S> shapes1 = ppt.getSlides().get(0).getShapes();
assertEquals("The Title", shapes1.get(0).getShapeName());
assertEquals("Another Subtitle", shapes1.get(1).getShapeName());
- final List<? extends Shape> shapes2 = ppt.getSlides().get(1).getShapes();
+ final List<S> shapes2 = ppt.getSlides().get(1).getShapes();
assertEquals("Title 1", shapes2.get(0).getShapeName());
assertEquals("Content Placeholder 2", shapes2.get(1).getShapeName());
- for (final Slide<?,?> slide : ppt.getSlides()) {
+ for (final Slide<S,P> slide : ppt.getSlides()) {
final String expected = slide.getSlideNumber()==1 ? "FirstSlide" : "Slide2";
assertEquals(expected, slide.getSlideName());
}
}
}
+
+ @Test
+ public void addFont() throws IOException {
+ try (SlideShow<S,P> ppt = createSlideShow()) {
+ ppt.createSlide();
+ try (InputStream fontData = slTests.openResourceAsStream("font.fntdata")) {
+ ppt.addFont(fontData);
+ }
+
+ try (SlideShow<S, P> ppt2 = reopen(ppt)) {
+ List<? extends FontInfo> fonts = ppt2.getFonts();
+ assertFalse(fonts.isEmpty());
+ FontInfo fi = fonts.get(fonts.size()-1);
+ assertEquals("Harlow Solid Italic", fi.getTypeface());
+ }
+ }
+ }
}