mirror of https://github.com/apache/poi.git
#63028 - Provide font embedding for slideshows
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1849898 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
a78bd71fc1
commit
c039da1b94
|
@ -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());
|
||||
|
|
|
@ -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"),
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -37,53 +34,8 @@ import org.apache.poi.util.Internal;
|
|||
this.typeface = typeface;
|
||||
}
|
||||
|
||||
@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.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
private void printTextParagraphs(final List<P> paras, final Consumer<String> consumer) {
|
||||
printTextParagraphs(paras, consumer, "\n");
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
final StringBuilder footer = new StringBuilder("\n");
|
||||
addSheetPlaceholderDatails(sheet, Placeholder.HEADER, sb);
|
||||
addSheetPlaceholderDatails(sheet, Placeholder.FOOTER, footer);
|
||||
addSheetPlaceholderDatails(sheet, Placeholder.HEADER, consumer);
|
||||
addSheetPlaceholderDatails(sheet, Placeholder.FOOTER, footerCon);
|
||||
|
||||
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;
|
||||
}
|
||||
if (!masterByDefault) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return (footer.length() > 1) ? footer.toString() : "";
|
||||
}
|
||||
|
||||
private void addSheetPlaceholderDatails(final Sheet<S,P> sheet, final Placeholder placeholder, final StringBuilder sb) {
|
||||
|
||||
private void addSheetPlaceholderDatails(final Sheet<S,P> sheet, final Placeholder placeholder, final Consumer<String> consumer) {
|
||||
final PlaceholderDetails headerPD = sheet.getPlaceholderDetails(placeholder);
|
||||
if (headerPD == null) {
|
||||
return;
|
||||
final String headerStr = (headerPD != null) ? headerPD.getText() : null;
|
||||
if (headerStr != null && filter.test(headerPD)) {
|
||||
consumer.accept(headerStr);
|
||||
}
|
||||
final String headerStr = headerPD.getText();
|
||||
if (headerStr == null) {
|
||||
return;
|
||||
}
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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]"
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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.clear();
|
||||
if (_presentation.isSetSldMasterIdLst()) {
|
||||
_presentation.getSldMasterIdLst().getSldMasterIdList().forEach(
|
||||
id -> _masters.add(masterMap.get(id.getId2()))
|
||||
);
|
||||
}
|
||||
|
||||
_masters = new ArrayList<>(masterMap.size());
|
||||
for (CTSlideMasterIdListEntry masterId : _presentation.getSldMasterIdLst().getSldMasterIdList()) {
|
||||
XSLFSlideMaster master = masterMap.get(masterId.getId2());
|
||||
_masters.add(master);
|
||||
}
|
||||
|
||||
_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,18 +605,13 @@ public class XMLSlideShow extends POIXMLDocument
|
|||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
@Override
|
||||
public MasterSheet<XSLFShape, XSLFTextParagraph> createMasterSheet() throws IOException {
|
||||
// TODO: implement!
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
@ -628,16 +629,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);
|
||||
|
@ -829,4 +820,9 @@ public class XSLFTextRun implements TextRun {
|
|||
return font;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XSLFTextParagraph getParagraph() {
|
||||
return _p;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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){
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,54 +44,24 @@ 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() {
|
||||
return typeface;
|
||||
}
|
||||
|
||||
@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.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -891,6 +890,21 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap
|
|||
return getDocumentRecord().getEnvironment().getFontCollection().addFont(fontInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -437,51 +439,21 @@ public class HwmfFont implements FontInfo {
|
|||
return FontFamily.valueOf(pitchAndFamily & 0xF);
|
||||
}
|
||||
|
||||
@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+
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue