* Construct a SpreadsheetML slide from a package part
*
* @param part the package part holding the slide data,
- * the content type must be <code>application/vnd.openxmlformats-officedocument.slide+xml</code>
+ * the content type must be {@code application/vnd.openxmlformats-officedocument.slide+xml}
*
* @since POI 3.14-Beta1
*/
draw.draw(graphics);
}
- @Override
- public boolean getDisplayPlaceholder(Placeholder placeholder) {
- return false;
- }
-
-
@Override
public void setHidden(boolean hidden) {
CTSlide sld = getXmlObject();
public HeadersFooters(HSLFSheet sheet, short headerFooterType) {
_sheet = sheet;
-
+
@SuppressWarnings("resource")
HSLFSlideShow ppt = _sheet.getSlideShow();
Document doc = ppt.getDocumentRecord();
-
+
// detect if this ppt was saved in Office2007
String tag = ppt.getSlideMasters().get(0).getProgrammableTag();
_ppt2007 = _ppt2007tag.equals(tag);
}
}
}
-
+
if (hdd == null) {
hdd = new HeadersFootersContainer(headerFooterType);
Record lst = doc.findFirstOfType(RecordTypes.List.typeID);
return isVisible(HeadersFootersAtom.fHasUserDate, Placeholder.DATETIME);
}
+ public CString getHeaderAtom() {
+ return _container.getHeaderAtom();
+ }
+
+ public CString getFooterAtom() {
+ return _container.getFooterAtom();
+ }
+
+ public CString getUserDateAtom() {
+ return _container.getUserDateAtom();
+ }
+
/**
* whether the date is displayed in the footer.
*/
setFlag(HeadersFootersAtom.fHasUserDate, flag);
}
+ /**
+ * whether today's date is used.
+ */
+ public boolean isTodayDateVisible(){
+ return isVisible(HeadersFootersAtom.fHasTodayDate, Placeholder.DATETIME);
+ }
+
+ /**
+ * whether the todays date is displayed in the footer.
+ */
+ public void setTodayDateVisible(boolean flag){
+ setFlag(HeadersFootersAtom.fHasTodayDate, flag);
+ }
+
/**
* whether the slide number is displayed in the footer.
*/
public boolean isPpt2007() {
return _ppt2007;
}
+
+ public HeadersFootersContainer getContainer() {
+ return _container;
+ }
}
--- /dev/null
+/* ====================================================================
+ 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 java.util.Arrays;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.apache.poi.util.GenericRecordUtil;
+import org.apache.poi.util.LittleEndian;
+
+public class DateTimeMCAtom extends RecordAtom {
+
+ /**
+ * Record header.
+ */
+ private final byte[] _header;
+
+ /**
+ * A TextPosition that specifies the position of the metacharacter in the corresponding text.
+ */
+ private int position;
+
+ /**
+ * An unsigned byte that specifies the Format ID used to stylize datetime. The identifier specified by
+ * the Format ID is converted based on the LCID [MS-LCID] into a value or string as specified in the
+ * following tables. The LCID is specified in TextSIException.lid. If no valid LCID is found in
+ * TextSIException.lid, TextSIException.altLid (if it exists) is used.
+ * The value MUST be greater than or equal to 0x0 and MUST be less than or equal to 0xC.
+ */
+ private int index;
+
+ private final byte[] unused = new byte[3];
+
+ protected DateTimeMCAtom() {
+ _header = new byte[8];
+ position = 0;
+ index = 0;
+
+ LittleEndian.putShort(_header, 2, (short)getRecordType());
+ LittleEndian.putInt(_header, 4, 8);
+ }
+
+ /**
+ * Constructs the datetime atom 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.
+ */
+ protected DateTimeMCAtom(byte[] source, int start, int len) {
+ // Get the header.
+ _header = Arrays.copyOfRange(source, start, start+8);
+
+ position = LittleEndian.getInt(source, start+8);
+ index = LittleEndian.getUByte(source, start+12);
+ System.arraycopy(source, start+13, unused, 0, 3);
+ }
+
+ /**
+ * Write the contents of the record back, so it can be written
+ * to disk
+ *
+ * @param out the output stream to write to.
+ * @throws IOException if an error occurs.
+ */
+ @Override
+ public void writeOut(OutputStream out) throws IOException {
+ out.write(_header);
+ LittleEndian.putInt(position, out);
+ out.write(index);
+ out.write(unused);
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public void setPosition(int position) {
+ this.position = position;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ /**
+ * Gets the record type.
+ * @return the record type.
+ */
+ @Override
+ public long getRecordType() {
+ return RecordTypes.DateTimeMCAtom.typeID;
+ }
+
+ @Override
+ public Map<String, Supplier<?>> getGenericProperties() {
+ return GenericRecordUtil.getGenericProperties(
+ "position", this::getPosition,
+ "index", this::getIndex
+ );
+ }
+
+}
package org.apache.poi.hslf.record;
import static org.apache.poi.util.GenericRecordUtil.getBitsAsString;
-import static org.apache.poi.util.GenericRecordUtil.safeEnum;
import java.io.IOException;
import java.io.OutputStream;
public final class HeadersFootersAtom extends RecordAtom {
- /** FormatIndex enum without LCID mapping */
- public enum FormatIndex {
- SHORT_DATE,
- LONG_DATE,
- LONG_DATE_WITHOUT_WEEKDAY,
- ALTERNATE_SHORT_DATE,
- ISO_STANDARD_DATE,
- SHORT_DATE_WITH_ABBREVIATED_MONTH,
- SHORT_DATE_WITH_SLASHES,
- ALTERNATE_SHORT_DATE_WITH_ABBREVIATED_MONTH,
- ENGLISH_DATE,
- MONTH_AND_YEAR,
- ABBREVIATED_MONTH_AND_YEAR,
- DATE_AND_HOUR12_TIME,
- DATE_AND_HOUR12_TIME_WITH_SECONDS,
- HOUR12_TIME,
- HOUR12_TIME_WITH_SECONDS,
- HOUR24_TIME,
- HOUR24_TIME_WITH_SECONDS,
- CHINESE1,
- CHINESE2,
- CHINESE3
- }
-
/**
* A bit that specifies whether the date is displayed in the footer.
* @see #getMask()
/**
* Build an instance of {@code HeadersFootersAtom} from on-disk data
*/
- protected HeadersFootersAtom(byte[] source, int start, int len) {
+ HeadersFootersAtom(byte[] source, int start, int len) {
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
return LittleEndian.getShort(_recdata, 0);
}
+
/**
* A signed integer that specifies the format ID to be used to style the datetime.
*
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
- "formatIndex", safeEnum(FormatIndex.values(), this::getFormatId),
+ "formatIndex", this::getFormatId,
"flags", getBitsAsString(this::getMask, PLACEHOLDER_MASKS, PLACEHOLDER_NAMES)
);
}
*/
public static final int PLACEHOLDER_QUARTSIZE = 2;
- private byte[] _header;
+ private final byte[] _header;
private int placementId;
private int placeholderId;
/**
* Build an instance of {@code OEPlaceholderAtom} from on-disk data
*/
- protected OEPlaceholderAtom(byte[] source, int start, int len) {
+ OEPlaceholderAtom(byte[] source, int start, int len) {
_header = Arrays.copyOfRange(source, start, start+8);
int offset = start+8;
* Sets the placeholder Id.<p>
*
* placeholder Id specifies the type of the placeholder shape.
- * The value MUST be one of the static constants defined in this class
+ * The value MUST be one of the static constants defined in {@link Placeholder}
*
* @param id the placeholder Id.
*/
InteractiveInfoAtom(4083,InteractiveInfoAtom::new),
UserEditAtom(4085,UserEditAtom::new),
CurrentUserAtom(4086,null),
- DateTimeMCAtom(4087,null),
+ DateTimeMCAtom(4087,DateTimeMCAtom::new),
GenericDateMCAtom(4088,null),
FooterMCAtom(4090,null),
ExControlAtom(4091,ExControlAtom::new),
}
+ @Override
public boolean isVisible() {
final Placeholder ph = getPlaceholder();
if (ph == null) {
switch (ph) {
case HEADER:
+ case TITLE:
return headersFooters.isHeaderVisible();
case FOOTER:
return headersFooters.isFooterVisible();
case DATETIME:
return headersFooters.isDateTimeVisible();
- case TITLE:
- return headersFooters.isHeaderVisible();
case SLIDE_NUMBER:
return headersFooters.isSlideNumberVisible();
default:
}
}
+ @Override
public void setVisible(final boolean isVisible) {
final Placeholder ph = getPlaceholder();
if (ph == null) {
package org.apache.poi.hslf.usermodel;
+import java.time.format.DateTimeFormatter;
+import java.util.Optional;
+import java.util.stream.Stream;
+
import org.apache.poi.ddf.EscherPropertyTypes;
import org.apache.poi.ddf.EscherSpRecord;
import org.apache.poi.hslf.exceptions.HSLFException;
+import org.apache.poi.hslf.model.HeadersFooters;
+import org.apache.poi.hslf.record.CString;
+import org.apache.poi.hslf.record.DateTimeMCAtom;
+import org.apache.poi.hslf.record.EscherTextboxWrapper;
import org.apache.poi.hslf.record.HSLFEscherClientDataRecord;
+import org.apache.poi.hslf.record.HeadersFootersAtom;
import org.apache.poi.hslf.record.OEPlaceholderAtom;
-import org.apache.poi.hslf.record.Record;
+import org.apache.poi.hslf.record.RecordTypes;
import org.apache.poi.hslf.record.RoundTripHFPlaceholder12;
+import org.apache.poi.hslf.record.TextSpecInfoAtom;
+import org.apache.poi.hslf.record.TextSpecInfoRun;
+import org.apache.poi.hslf.util.LocaleDateFormat;
import org.apache.poi.sl.usermodel.MasterSheet;
import org.apache.poi.sl.usermodel.Placeholder;
+import org.apache.poi.util.LocaleID;
+import org.apache.poi.util.LocaleUtil;
/**
* Extended placeholder details for HSLF shapes
final HSLFSimpleShape shape;
private OEPlaceholderAtom oePlaceholderAtom;
private RoundTripHFPlaceholder12 roundTripHFPlaceholder12;
+ private DateTimeMCAtom localDateTime;
HSLFShapePlaceholderDetails(final HSLFSimpleShape shape) {
}
}
+ @Override
public Placeholder getPlaceholder() {
updatePlaceholderAtom(false);
final int phId;
phId = oePlaceholderAtom.getPlaceholderId();
} else if (roundTripHFPlaceholder12 != null) {
phId = roundTripHFPlaceholder12.getPlaceholderId();
+ } else if (localDateTime != null) {
+ return Placeholder.DATETIME;
} else {
return null;
}
}
}
+ @Override
public void setPlaceholder(final Placeholder placeholder) {
final EscherSpRecord spRecord = shape.getEscherChild(EscherSpRecord.RECORD_ID);
int flags = spRecord.getFlags();
roundTripHFPlaceholder12.setPlaceholderId(phId);
}
+ @Override
public PlaceholderSize getSize() {
final Placeholder ph = getPlaceholder();
if (ph == null) {
return null;
}
- final int size = (oePlaceholderAtom != null)
+ final int size = (oePlaceholderAtom != null)
? oePlaceholderAtom.getPlaceholderSize()
: OEPlaceholderAtom.PLACEHOLDER_HALFSIZE;
-
+
switch (size) {
case OEPlaceholderAtom.PLACEHOLDER_FULLSIZE:
return PlaceholderSize.full;
}
}
+ @Override
public void setSize(final PlaceholderSize size) {
final Placeholder ph = getPlaceholder();
if (ph == null || size == null) {
return;
}
updatePlaceholderAtom(true);
-
+
final byte ph_size;
switch (size) {
case full:
}
private void updatePlaceholderAtom(final boolean create) {
+ localDateTime = null;
+ if (shape instanceof HSLFTextBox) {
+ EscherTextboxWrapper txtBox = ((HSLFTextBox)shape).getEscherTextboxWrapper();
+ if (txtBox != null) {
+ localDateTime = (DateTimeMCAtom)txtBox.findFirstOfType(RecordTypes.DateTimeMCAtom.typeID);
+ }
+ }
+
final HSLFEscherClientDataRecord clientData = shape.getClientData(create);
if (clientData == null) {
oePlaceholderAtom = null;
clientData.addChild(roundTripHFPlaceholder12);
}
}
+
+ @Override
+ public String getUserDate() {
+ HeadersFooters hf = shape.getSheet().getHeadersFooters();
+ CString uda = hf.getUserDateAtom();
+ return hf.isUserDateVisible() && uda != null ? uda.getText() : null;
+ }
+
+ @Override
+ public DateTimeFormatter getDateFormat() {
+ int formatId;
+ if (localDateTime != null) {
+ formatId = localDateTime.getIndex();
+ } else {
+ HeadersFootersAtom hfAtom = shape.getSheet().getHeadersFooters().getContainer().getHeadersFootersAtom();
+ formatId = hfAtom.getFormatId();
+ }
+
+ LocaleID def = LocaleID.lookupByLanguageTag(LocaleUtil.getUserLocale().toLanguageTag());
+
+ // def = LocaleID.EN_US;
+
+ LocaleID lcid =
+ Stream.of(((HSLFTextShape)shape).getTextParagraphs().get(0).getRecords())
+ .filter(r -> r instanceof TextSpecInfoAtom)
+ .findFirst()
+ .map(r -> ((TextSpecInfoAtom)r).getTextSpecInfoRuns()[0])
+ .map(TextSpecInfoRun::getLangId)
+ .flatMap(lid -> Optional.ofNullable(LocaleID.lookupByLcid(lid)))
+ .orElse(def != null ? def : LocaleID.EN_US)
+ ;
+
+ return LocaleDateFormat.map(lcid, formatId, LocaleDateFormat.MapFormatId.PPT);
+ }
}
import org.apache.poi.sl.usermodel.Notes;
import org.apache.poi.sl.usermodel.Placeholder;
import org.apache.poi.sl.usermodel.ShapeType;
+import org.apache.poi.sl.usermodel.SimpleShape;
import org.apache.poi.sl.usermodel.Slide;
import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder;
* @return set of records inside {@code SlideListWithtext} container
* which hold text data for this slide (typically for placeholders).
*/
- protected SlideAtomsSet getSlideAtomsSet() { return _atomSet; }
+ public SlideAtomsSet getSlideAtomsSet() { return _atomSet; }
/**
* Returns master sheet associated with this slide.
(slt == SlideLayoutType.TITLE_SLIDE || slt == SlideLayoutType.TITLE_ONLY || slt == SlideLayoutType.MASTER_TITLE);
switch (placeholder) {
case DATETIME:
- return hf.isDateTimeVisible() && !isTitle;
+ return (hf.isDateTimeVisible() && (hf.isTodayDateVisible() || (hf.isUserDateVisible() && hf.getUserDateAtom() != null))) && !isTitle;
case SLIDE_NUMBER:
return hf.isSlideNumberVisible() && !isTitle;
case HEADER:
- return hf.isHeaderVisible() && !isTitle;
+ return hf.isHeaderVisible() && hf.getHeaderAtom() != null && !isTitle;
case FOOTER:
- return hf.isFooterVisible() && !isTitle;
+ return hf.isFooterVisible() && hf.getFooterAtom() != null && !isTitle;
default:
return false;
}
}
+ @Override
+ public boolean getDisplayPlaceholder(final SimpleShape<?,?> placeholderRef) {
+ Placeholder placeholder = placeholderRef.getPlaceholder();
+ if (placeholder == null) {
+ return false;
+ }
+
+ final HeadersFooters hf = getHeadersFooters();
+ final SlideLayoutType slt = getSlideRecord().getSlideAtom().getSSlideLayoutAtom().getGeometryType();
+ final boolean isTitle =
+ (slt == SlideLayoutType.TITLE_SLIDE || slt == SlideLayoutType.TITLE_ONLY || slt == SlideLayoutType.MASTER_TITLE);
+ switch (placeholder) {
+ case HEADER:
+ return hf.isHeaderVisible() && hf.getHeaderAtom() != null && !isTitle;
+ case FOOTER:
+ return hf.isFooterVisible() && hf.getFooterAtom() != null && !isTitle;
+ case DATETIME:
+ case SLIDE_NUMBER:
+ default:
+ return false;
+ }
+ }
+
@Override
public HSLFMasterSheet getSlideLayout(){
// TODO: find out how we can find the mastersheet base on the slide layout type, i.e.
--- /dev/null
+/* ====================================================================
+ 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.util;
+
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.FormatStyle;
+import java.util.AbstractMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.LocaleID;
+import org.apache.poi.util.SuppressForbidden;
+
+@Internal
+public final class LocaleDateFormat {
+
+ /**
+ * Enum to specify initial remapping of the FormatID based on thd LCID
+ */
+ public enum MapFormatId {
+ NONE, PPT
+ }
+
+ private enum MapFormatPPT {
+ EN_US(LocaleID.EN_US, "MM/dd/yyyy", 1, 8, "MMMM dd, yyyy", 5, 9, 10, 11, 12, 15, 16, "h:mm a", "h:mm:ss a"),
+ EN_AU(LocaleID.EN_AU, 0, 1, "d MMMM, yyy", 2, 5, 9, 10, 11, 12, 15, 16, 13, 14),
+ JA_JP(LocaleID.JA_JP, 4, 8, 7, 3, 0, 9, 5, 11, 12, "HH:mm", "HH:mm:ss", 15, 16),
+ ZH_TW(LocaleID.ZH_TW, 0, 1, 3, 7, 12, 9, 10, 4, 11, "HH:mm", "HH:mm:ss", "H:mm a", "H:mm:ss a"),
+ KO_KR(LocaleID.KO_KR, 0, 1, 6, 3, 4, 10, 7, 12, 11, "HH:mm", "HH:mm:ss", 13, 14 ),
+ AR_SA(LocaleID.AR_SA, 0, 1, 2, 3, 4, 5, 8, 7, 8, 1, 10, 11, 5),
+ HE_IL(LocaleID.HE_IL, 0, 1, 2, 6, 11, 5, 12, 7, 8, 9, 1, 11, 6),
+ SV_SE(LocaleID.SV_SE, 0, 1, 3, 2, 7, 9, 10, 11, 12, 15, 16, 13, 14),
+ ZH_CN(LocaleID.ZH_CN, 0, 1, 2, 2, 4, 9, 5, "yyyy\u5E74M\u6708d\u65E5h\u65F6m\u5206", "yyyy\u5E74M\u6708d\u65E5\u661F\u671fWh\u65F6m\u5206s\u79D2", "HH:mm", "HH:mm:ss", "a h\u65F6m\u5206", "a h\u65F6m\u5206s\u79D2"),
+ ZH_SG(LocaleID.ZH_SG, 0, 1, 3, 2, 4, 9, 5, "yyyy\u5E74M\u6708d\u65E5h\u65F6m\u5206", "yyyy\u5E74M\u6708d\u65E5\u661F\u671fWh\u65F6m\u5206s\u79D2", "HH:mm", "HH:mm:ss", "a h\u65F6m\u5206", "a h\u65F6m\u5206s\u79D2"),
+ ZH_MO(LocaleID.ZH_MO, 0, 1, 3, 2, 4, 9, 5, "yyyy\u5E74M\u6708d\u65E5h\u65F6m\u5206", "yyyy\u5E74M\u6708d\u65E5\u661F\u671fWh\u65F6m\u5206s\u79D2", "HH:mm", "HH:mm:ss", "a h\u65F6m\u5206", "a h\u65F6m\u5206s\u79D2"),
+ ZH_HK(LocaleID.ZH_HK, 0, 1, 3, 2, 4, 9, 5, "yyyy\u5E74M\u6708d\u65E5h\u65F6m\u5206", "yyyy\u5E74M\u6708d\u65E5\u661F\u671fWh\u65F6m\u5206s\u79D2", "HH:mm", "HH:mm:ss", "a h\u65F6m\u5206", "a h\u65F6m\u5206s\u79D2"),
+ TH_TH(LocaleID.TH_TH, 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 13, 14),
+ VI_VN(LocaleID.VI_VN, 0, 1, 2, 3, 5, 6, 10, 11, 12, 13, 14, 15, 16),
+ HI_IN(LocaleID.HI_IN, 1, 2, 3, 5, 7, 11, 13, 0, 1, 5, 10, 11, 14),
+ SYR_SY(LocaleID.SYR_SY, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),
+ NO_MAP(LocaleID.INVALID_O, 0, 1, 3, 2, 5, 9, 10, 11, 12, 15, 16, 13, 14, 4, 6, 7, 8)
+ ;
+
+ private final LocaleID lcid;
+ private final Object[] mapping;
+
+ private static final Map<LocaleID,MapFormatPPT> LCID_LOOKUP =
+ Stream.of(values()).collect(Collectors.toMap(MapFormatPPT::getLocaleID, Function.identity()));
+
+ MapFormatPPT(LocaleID lcid, Object... mapping) {
+ this.lcid = lcid;
+ this.mapping = mapping;
+ }
+
+ public LocaleID getLocaleID() {
+ return lcid;
+ }
+
+ public static Object mapFormatId(LocaleID lcid, int formatId) {
+ Object[] mapping = LCID_LOOKUP.getOrDefault(lcid, NO_MAP).mapping;
+ return (formatId >= 0 && formatId < mapping.length) ? mapping[formatId] : formatId;
+ }
+ }
+
+ private enum MapFormatException {
+ CHINESE(
+ new LocaleID[]{LocaleID.ZH, LocaleID.ZH_HANS, LocaleID.ZH_HANT, LocaleID.ZH_CN, LocaleID.ZH_SG, LocaleID.ZH_MO, LocaleID.ZH_HK, LocaleID.ZH_YUE_HK},
+ 0,
+ 1,
+ "yyyy\u5E74M\u6708d\u65E5\u661F\u671FW",
+ "yyyy\u5E74M\u6708d\u65E5",
+ "yyyy/M/d",
+ "yy.M.d",
+ "yyyy\u5E74M\u6708d\u65E5\u661F\u671FW",
+ "yyyy\u5E74M\u6708d\u65E5",
+ "yyyy\u5E74M\u6708d\u65E5\u661F\u671FW",
+ "yyyy\u5E74M\u6708",
+ "yyyy\u5E74M\u6708",
+ "h\u65F6m\u5206s\u79D2",
+ "h\u65F6m\u5206",
+ "h\u65F6m\u5206",
+ "h\u65F6m\u5206",
+ "ah\u65F6m\u5206",
+ "ah\u65F6m\u5206",
+ // no lunar calendar support
+ "EEEE\u5E74O\u6708A\u65E5",
+ "EEEE\u5E74O\u6708A\u65E5\u661F\u671FW",
+ "EEEE\u5E74O\u6708"
+ ),
+ // no hindu calendar support
+ HINDI(
+ new LocaleID[]{LocaleID.HI, LocaleID.HI_IN},
+ "dd/M/g",
+ "dddd, d MMMM yyyy",
+ "dd MMMM yyyy",
+ "dd/M/yy",
+ "yy-M-dd",
+ "d-MMMM-yyyy",
+ "dd.M.g",
+ "dd MMMM. yy",
+ "dd MMMM yy",
+ "MMMM YY",
+ "MMMM-g",
+ "dd/M/g HH:mm",
+ "dd/M/g HH:mm:ss",
+ "HH:mm a",
+ "HH:mm:ss a",
+ "HH:mm",
+ "HH:mm:ss"
+ ),
+ // https://www.secondsite8.com/customdateformats.htm
+ // aa or gg or o, r, i, c -> lunar calendar not supported
+ // (aaa) -> lower case week names ... not supported
+ JAPANESE(
+ new LocaleID[]{LocaleID.JA, LocaleID.JA_JP, LocaleID.JA_PLOC_JP},
+ 0,
+ 1,
+ "EEEy\u5E74M\u6708d\u65E5",
+ "yyyy\u5E74M\u6708d\u65E5",
+ "yyyy/M/d",
+ "yyyy\u5E74M\u6708d\u65E5",
+ "yy\u5E74M\u6708d\u65E5",
+ "yyyy\u5E74M\u6708d\u65E5",
+ "yyyy\u5E74M\u6708d\u65E5(EEE)",
+ "yyyy\u5E74M\u6708",
+ "yyyy\u5E74M\u6708",
+ "yy/M/d H\u6642m\u5206",
+ "yy/M/d H\u6642m\u5206s\u79D2",
+ "a h\u6642m\u5206",
+ "a h\u6642m\u5206s\u79D2",
+ "H\u6642m\u5206",
+ "H\u6642m\u5206s\u79D2",
+ "yyyy\u5E74M\u6708d\u65E5 EEE\u66DC\u65E5"
+ ),
+ KOREAN(
+ new LocaleID[]{LocaleID.KO,LocaleID.KO_KR},
+ 0,
+ 1,
+ "yyyy\uB144 M\uC6D4 d\uC77C EEE\uC694\uC77C",
+ "yyyy\uB144 M\uC6D4 d\uC77C",
+ "yyyy/M/d",
+ "yyMMdd",
+ "yyyy\uB144 M\uC6D4 d\uC77C",
+ "yyyy\uB144 M\uC6D4",
+ "yyyy\uB144 M\uC6D4 d\uC77C",
+ "yyyy",
+ "yyyy\uB144 M\uC6D4",
+ "yyyy\uB144 M\uC6D4 d\uC77C a h\uC2DC m\uBD84",
+ "yy\uB144 M\uC6D4 d\uC77C H\uC2DC m\uBD84 s\uCD08",
+ "a h\uC2DC m\uBD84",
+ "a h\uC2DC m\uBD84 s\uCD08",
+ "H\uC2DC m\uBD84",
+ "H\uC2DC m\uBD84 S\uCD08"
+ ),
+ HUNGARIAN(
+ new LocaleID[]{LocaleID.HU, LocaleID.HU_HU},
+ 0, 1, 2, 3, 4, 5, 6, "yy. MMM. dd.", "\u2019yy MMM.", "MMMM \u2019yy", 10, 11, 12, "a h:mm", "a h:mm:ss", 15, 16
+ ),
+ BOKMAL(
+ new LocaleID[]{LocaleID.NB_NO},
+ 0, 1, 2, 3, 4, "d. MMM. yyyy", "d/m yyyy", "MMM. yy", "yyyy.mm.dd", 9, "d. MMM.", 11, 12, 13, 14, 15, 16
+ ),
+ CZECH(new LocaleID[]{LocaleID.CS, LocaleID.CS_CZ}, 0, 1, 2, 3, 4, 5, 6, 7, 8, "MMMM \u2019yy", 10, 11, 12, 13, 14, 15, 16),
+ DANISH(new LocaleID[]{LocaleID.DA, LocaleID.DA_DK}, 0, "d. MMMM yyyy", "yy-MM-dd", "yyyy.MM.dd", 4, "MMMM yyyy", "d.M.yy", "d/M yyyy", "dd.MM.yyyy", "d.M.yyyy", "dd/MM yyyy", 11, 12, 13, 14, 15, 16 ),
+ DUTCH(new LocaleID[]{LocaleID.NL,LocaleID.NL_BE,LocaleID.NL_NL}, 0, 1, 2, 3, 4, 5, 6, 7, 8, "MMMM \u2019yy", 10, 11, 12, 13, 14, 15, 16),
+ FINISH(new LocaleID[]{LocaleID.FI, LocaleID.FI_FI}, 0, 1, 2, 3, 4, 5, 6, 7, 8, "MMMM \u2019yy", 10, 11, 12, 13, 14, 15, 16),
+ FRENCH_CANADIAN(new LocaleID[]{LocaleID.FR_CA}, 0, 1, 2, "yy MM dd", 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16),
+ GERMAN(new LocaleID[]{LocaleID.DE,LocaleID.DE_AT,LocaleID.DE_CH,LocaleID.DE_DE,LocaleID.DE_LI,LocaleID.DE_LU}, 0, 1, 2, 3, 4, "yy-MM-dd", 6, "dd. MMM. yyyy", 8, 9, 10, 11, 12, 13, 14, 15, 16),
+ ITALIAN(new LocaleID[]{LocaleID.IT,LocaleID.IT_IT,LocaleID.IT_CH}, 0, 1, 2, 3, 4, "d-MMM.-yy", 6, "d. MMM. yy", "MMM. \u2019yy", "MMMM \u2019yy", 10, 11, 12, 13, 14, 15, 16),
+ NO_MAP(new LocaleID[]{LocaleID.INVALID_O}, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
+ // TODO: add others from [MS-OSHARED] chapter 2.4.4.4
+ ;
+
+
+ private final LocaleID[] lcid;
+ private final Object[] mapping;
+
+ private static final Map<LocaleID, MapFormatException> LCID_LOOKUP =
+ Stream.of(values()).flatMap(m -> Stream.of(m.lcid).map(l -> new AbstractMap.SimpleEntry<>(l, m)))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+
+ MapFormatException(LocaleID[] lcid, Object... mapping) {
+ this.lcid = lcid;
+ this.mapping = mapping;
+ }
+
+ public static Object mapFormatId(LocaleID lcid, int formatId) {
+ Object[] mapping = LCID_LOOKUP.getOrDefault(lcid, NO_MAP).mapping;
+ return (formatId >= 0 && formatId < mapping.length) ? mapping[formatId] : formatId;
+ }
+ }
+
+ /**
+ * This enum lists and describes the format indices that can be used as inputs to the algorithm. The
+ * descriptions given are generalized; the actual format produced can vary from the description,
+ * depending on the input locale.
+ */
+ @SuppressForbidden("DateTimeFormatter::ofLocalizedDate and others will be localized in mapFormatId")
+ private enum MapFormatBase {
+ /** 0 - Base short date **/
+ SHORT_DATE(null, FormatStyle.MEDIUM, DateTimeFormatter::ofLocalizedDate),
+ /** 1 - Base long date. **/
+ LONG_DATE(null, FormatStyle.FULL, DateTimeFormatter::ofLocalizedDate),
+ /**
+ * 2 - Do the following to base long date:
+ * - Remove occurrences of "dddd".
+ * - Remove the comma symbol (0x002C) and space following "dddd" if present.
+ * - Change occurrences of "dd" to "d".
+ **/
+ LONG_DATE_WITHOUT_WEEKDAY("d. MMMM yyyy", null, null),
+ /**
+ * 3 - Do the following to base short date:
+ * - Change occurrences of "yyyy" to "yy".
+ * - Change occurrences of "yy" to "yyyy".
+ */
+ ALTERNATE_SHORT_DATE("dd/MM/yy", null, null),
+ /**
+ * 4 - yyyy-MM-dd
+ */
+ ISO_STANDARD_DATE("yyyy-MM-dd", null, null),
+ /**
+ * 5 - If the symbol "y" occurs before the symbol "M" occurs in the base short date, the format is
+ * "yy-MMM-d". Otherwise, the format is "d-MMM-yy".
+ */
+ SHORT_DATE_WITH_ABBREVIATED_MONTH("d-MMM-yy", null, null),
+ /**
+ * 6 - If the forward slash symbol (0x002F) occurs in the base short date, the slash symbol is the
+ * period symbol (0x002E). Otherwise, the slash symbol is the forward slash (0x002F).
+ * A group is an uninterrupted sequence of qualified symbols where a qualified symbol is "d",
+ * "M", or "Y".
+ * Identify the first three groups that occur in the base short date. The format is formed by
+ * appending the three groups together with single slash symbols separating the groups.
+ */
+ SHORT_DATE_WITH_SLASHES("d/M/y", null, null),
+ /**
+ * 7 - Do the following to base long date:
+ * - Remove occurrences of "dddd".
+ * - Remove the comma symbol (0x002C) and space following "dddd" if present.
+ * - Change occurrences of "dd" to "d".
+ * - For all right-to-left locales and Lao, change a sequence of any length of "M" to "MMM".
+ * - For all other locales, change a sequence of any length of "M" to "MMM".
+ * - Change occurrences of "yyyy" to "yy".
+ */
+ ALTERNATE_SHORT_DATE_WITH_ABBREVIATED_MONTH("d. MMM yy", null, null),
+ /**
+ * 8 - For American English and Arabic, the format is "d MMMM yyyy".
+ * For Hebrew, the format is "d MMMM, yyyy".
+ * For all other locales, the format is the same as format 6 with the following additional step:
+ * Change occurrences of "yyyy" to "yy".
+ */
+ ENGLISH_DATE("d MMMM yyyy", null, null),
+ /**
+ * 9 - Do the following to base long date:
+ * - Remove all symbols that occur before the first occurrence of either the "y" symbol or the "M" symbol.
+ * - Remove all "d" symbols.
+ * - For all locales except Lithuanian, remove all period symbols (0x002E).
+ * - Remove all comma symbols (0x002C).
+ * - Change occurrences of "yyyy" to "yy".
+ */
+ MONTH_AND_YEAR("MMMM yy", null, null),
+ /**
+ * 10 - MMM-yy
+ */
+ ABBREVIATED_MONTH_AND_YEAR("LLL-yy", null, null),
+ /**
+ * 11 - Base short date followed by a space, followed by base time with seconds removed.
+ * Seconds are removed by removing all "s" symbols and any symbol that directly precedes an
+ * "s" symbol that is not an "h" or "m" symbol.
+ */
+ DATE_AND_HOUR12_TIME(null, FormatStyle.MEDIUM, (fs) -> new DateTimeFormatterBuilder().appendLocalized(FormatStyle.SHORT, null).appendLiteral(" ").appendLocalized(null, FormatStyle.SHORT).toFormatter()),
+ /**
+ * 12 - Base short date followed by a space, followed by base time.
+ */
+ DATE_AND_HOUR12_TIME_WITH_SECONDS(null, FormatStyle.MEDIUM, (fs) -> new DateTimeFormatterBuilder().appendLocalized(FormatStyle.SHORT, null).appendLiteral(" ").appendLocalized(null, fs).toFormatter()),
+ /**
+ * 13 - For Hungarian, the format is "am/pm h:mm".
+ * For all other locales, the format is "h:mm am/pm".
+ * In both cases, replace occurrences of the colon symbol (0x003A) with the time separator.
+ */
+ HOUR12_TIME("K:mm", null, null),
+ /**
+ * 14 - For Hungarian, the format is "am/pm h:mm:ss".
+ * For all other locales, the format is "h:mm:ss am/pm".
+ * In both cases, replace occurrences of the colon symbol (0x003A) with the time separator.
+ */
+ HOUR12_TIME_WITH_SECONDS("K:mm:ss", null, null),
+ /**
+ * 15 - "HH" followed by the time separator, followed by "mm".
+ */
+ HOUR24_TIME("HH:mm", null, null),
+ /**
+ * 16 - "HH" followed by the time separator, followed by "mm", followed by the time separator
+ * followed by "ss".
+ */
+ HOUR24_TIME_WITH_SECONDS("HH:mm:ss", null, null),
+ // CHINESE1(null, null, null),
+ // CHINESE2(null, null, null),
+ // CHINESE3(null, null, null)
+ ;
+
+
+ private final String datefmt;
+ private final FormatStyle formatStyle;
+ private final Function<FormatStyle,DateTimeFormatter> formatFct;
+
+ MapFormatBase(String datefmt, FormatStyle formatStyle, Function<FormatStyle,DateTimeFormatter> formatFct) {
+ this.formatStyle = formatStyle;
+ this.datefmt = datefmt;
+ this.formatFct = formatFct;
+ }
+
+ public static DateTimeFormatter mapFormatId(Locale loc, int formatId) {
+ MapFormatBase[] mfb = MapFormatBase.values();
+ if (formatId < 0 || formatId >= mfb.length) {
+ return DateTimeFormatter.BASIC_ISO_DATE;
+ }
+ MapFormatBase mf = mfb[formatId];
+ return (mf.datefmt == null)
+ ? mf.formatFct.apply(mf.formatStyle).withLocale(loc)
+ : DateTimeFormatter.ofPattern(mf.datefmt, loc);
+ }
+ }
+
+ private LocaleDateFormat() {}
+
+ public static DateTimeFormatter map(LocaleID lcid, int formatID, MapFormatId mapFormatId) {
+ final Locale loc = Locale.forLanguageTag(lcid.getLanguageTag());
+ int mappedFormatId = formatID;
+ if (mapFormatId == MapFormatId.PPT) {
+ Object mappedFormat = MapFormatPPT.mapFormatId(lcid, formatID);
+ if (mappedFormat instanceof String) {
+ return DateTimeFormatter.ofPattern((String)mappedFormat,loc);
+ } else {
+ mappedFormatId = (Integer)mappedFormat;
+ }
+ }
+ Object mappedFormat = MapFormatException.mapFormatId(lcid, mappedFormatId);
+ if (mappedFormat instanceof String) {
+ return DateTimeFormatter.ofPattern((String)mappedFormat,loc);
+ } else {
+ return MapFormatBase.mapFormatId(loc, (Integer)mappedFormat);
+ }
+ }
+}
package org.apache.poi.hslf.usermodel;
-import static org.apache.poi.sl.usermodel.BaseTestSlideShow.getColor;
+import static org.apache.poi.hslf.HSLFTestDataSamples.getSlideShow;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.awt.Color;
import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.apache.poi.hslf.HSLFTestDataSamples;
import org.apache.poi.hslf.model.textproperties.TextPropCollection;
+import org.apache.poi.hslf.record.DateTimeMCAtom;
import org.apache.poi.hslf.record.TextBytesAtom;
import org.apache.poi.hslf.record.TextCharsAtom;
-import org.apache.poi.hslf.record.TextHeaderAtom;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
+import org.apache.poi.sl.usermodel.BaseTestSlideShow;
+import org.apache.poi.sl.usermodel.PlaceholderDetails;
+import org.apache.poi.util.LocaleUtil;
import org.junit.jupiter.api.Test;
/**
*/
@SuppressWarnings("UnusedAssignment")
public final class TestTextRun {
- // SlideShow primed on the test data
- private HSLFSlideShow ss;
- private HSLFSlideShow ssRich;
-
- @BeforeEach
- void setUp() throws IOException {
- // Basic (non rich) test file
- ss = HSLFTestDataSamples.getSlideShow("basic_test_ppt_file.ppt");
-
- // Rich test file
- ssRich = HSLFTestDataSamples.getSlideShow("Single_Coloured_Page.ppt");
- }
-
- @AfterEach
- void tearDown() throws IOException {
- ssRich.close();
- ss.close();
- }
-
/**
* Test to ensure that getting the text works correctly
*/
@Test
- void testGetText() {
- HSLFSlide slideOne = ss.getSlides().get(0);
- List<List<HSLFTextParagraph>> textParas = slideOne.getTextParagraphs();
-
- assertEquals(2, textParas.size());
-
- // Get text works with \n
- assertEquals("This is a test title", HSLFTextParagraph.getText(textParas.get(0)));
- assertEquals("This is a test subtitle\nThis is on page 1", HSLFTextParagraph.getText(textParas.get(1)));
+ void testGetText() throws IOException {
+ try (HSLFSlideShow ppt = getSlideShow("basic_test_ppt_file.ppt")) {
+ HSLFSlide slideOne = ppt.getSlides().get(0);
+ List<List<HSLFTextParagraph>> textParas = slideOne.getTextParagraphs();
+
+ // Get text works with \n
+ String[] exp1 = { "This is a test title", "This is a test subtitle\nThis is on page 1" };
+ String[] act1 = textParas.stream().map(HSLFTextParagraph::getText).toArray(String[]::new);
+ assertArrayEquals(exp1, act1);
+
+ // Raw text has \r instead
+ String[] exp2 = { "This is a test title", "This is a test subtitle\rThis is on page 1" };
+ String[] act2 = textParas.stream().map(HSLFTextParagraph::getRawText).toArray(String[]::new);
+ assertArrayEquals(exp2, act2);
+ }
- // Raw text has \r instead
- assertEquals("This is a test title", HSLFTextParagraph.getRawText(textParas.get(0)));
- assertEquals("This is a test subtitle\rThis is on page 1", HSLFTextParagraph.getRawText(textParas.get(1)));
+ // Now check on a rich text run
+ try (HSLFSlideShow ppt = getSlideShow("Single_Coloured_Page.ppt")) {
+ List<List<HSLFTextParagraph>> textParas = ppt.getSlides().get(0).getTextParagraphs();
+ String[] exp1 = { "This is a title, it\u2019s in black", "This is the subtitle, in bold\nThis bit is blue and italic\nThis bit is red (normal)" };
+ String[] act1 = textParas.stream().map(HSLFTextParagraph::getText).toArray(String[]::new);
+ assertArrayEquals(exp1, act1);
- // Now check on a rich text run
- HSLFSlide slideOneR = ssRich.getSlides().get(0);
- textParas = slideOneR.getTextParagraphs();
-
- assertEquals(2, textParas.size());
- assertEquals("This is a title, it\u2019s in black", HSLFTextParagraph.getText(textParas.get(0)));
- assertEquals("This is the subtitle, in bold\nThis bit is blue and italic\nThis bit is red (normal)", HSLFTextParagraph.getText(textParas.get(1)));
- assertEquals("This is a title, it\u2019s in black", HSLFTextParagraph.getRawText(textParas.get(0)));
- assertEquals("This is the subtitle, in bold\rThis bit is blue and italic\rThis bit is red (normal)", HSLFTextParagraph.getRawText(textParas.get(1)));
+ String[] exp2 = { "This is a title, it\u2019s in black", "This is the subtitle, in bold\rThis bit is blue and italic\rThis bit is red (normal)" };
+ String[] act2 = textParas.stream().map(HSLFTextParagraph::getRawText).toArray(String[]::new);
+ assertArrayEquals(exp2, act2);
+ }
}
/**
* Test to ensure changing non rich text bytes->bytes works correctly
*/
@Test
- void testSetText() {
- HSLFSlide slideOne = ss.getSlides().get(0);
- List<List<HSLFTextParagraph>> textRuns = slideOne.getTextParagraphs();
- HSLFTextParagraph run = textRuns.get(0).get(0);
- HSLFTextRun tr = run.getTextRuns().get(0);
-
- // Check current text
- assertEquals("This is a test title", tr.getRawText());
-
- // Change
- String changeTo = "New test title";
- tr.setText(changeTo);
- assertEquals(changeTo, tr.getRawText());
-
- // Ensure trailing \n's are NOT stripped, it is legal to set a text with a trailing '\r'
- tr.setText(changeTo + "\n");
- assertEquals(changeTo + "\r", tr.getRawText());
+ void testSetText() throws IOException {
+ try (HSLFSlideShow ppt = getSlideShow("basic_test_ppt_file.ppt")) {
+ List<List<HSLFTextParagraph>> textRuns = ppt.getSlides().get(0).getTextParagraphs();
+ HSLFTextParagraph run = textRuns.get(0).get(0);
+ HSLFTextRun tr = run.getTextRuns().get(0);
+
+ // Check current text
+ assertEquals("This is a test title", tr.getRawText());
+
+ // Change
+ String changeTo = "New test title";
+ tr.setText(changeTo);
+ assertEquals(changeTo, tr.getRawText());
+
+ // Ensure trailing \n's are NOT stripped, it is legal to set a text with a trailing '\r'
+ tr.setText(changeTo + "\n");
+ assertEquals(changeTo + "\r", tr.getRawText());
+ }
}
/**
*/
@SuppressWarnings("unused")
@Test
- void testAdvancedSetText() {
- HSLFSlide slideOne = ss.getSlides().get(0);
- List<HSLFTextParagraph> paras = slideOne.getTextParagraphs().get(0);
- HSLFTextParagraph para = paras.get(0);
-
- TextHeaderAtom tha = null;
- TextBytesAtom tba = null;
- TextCharsAtom tca = null;
- for ( org.apache.poi.hslf.record.Record r : para.getRecords()) {
- if (r instanceof TextHeaderAtom) tha = (TextHeaderAtom)r;
- else if (r instanceof TextBytesAtom) tba = (TextBytesAtom)r;
- else if (r instanceof TextCharsAtom) tca = (TextCharsAtom)r;
- }
-
- // Bytes -> Bytes
- assertNull(tca);
- assertNotNull(tba);
- // assertFalse(run._isUnicode);
- assertEquals("This is a test title", para.getTextRuns().get(0).getRawText());
-
- String changeBytesOnly = "New Test Title";
- HSLFTextParagraph.setText(paras, changeBytesOnly);
- para = paras.get(0);
- tha = null; tba = null; tca = null;
- for ( org.apache.poi.hslf.record.Record r : para.getRecords()) {
- if (r instanceof TextHeaderAtom) tha = (TextHeaderAtom)r;
- else if (r instanceof TextBytesAtom) tba = (TextBytesAtom)r;
- else if (r instanceof TextCharsAtom) tca = (TextCharsAtom)r;
- }
-
- assertEquals(changeBytesOnly, HSLFTextParagraph.getRawText(paras));
- assertNull(tca);
- assertNotNull(tba);
-
- // Bytes -> Chars
- assertEquals(changeBytesOnly, HSLFTextParagraph.getRawText(paras));
-
- String changeByteChar = "This is a test title with a '\u0121' g with a dot";
- HSLFTextParagraph.setText(paras, changeByteChar);
- para = paras.get(0);
- tha = null; tba = null; tca = null;
- for ( org.apache.poi.hslf.record.Record r : para.getRecords()) {
- if (r instanceof TextHeaderAtom) tha = (TextHeaderAtom)r;
- else if (r instanceof TextBytesAtom) tba = (TextBytesAtom)r;
- else if (r instanceof TextCharsAtom) tca = (TextCharsAtom)r;
- }
-
- assertEquals(changeByteChar, HSLFTextParagraph.getRawText(paras));
- assertNotNull(tca);
- assertNull(tba);
-
- // Chars -> Chars
- assertNotNull(tca);
- assertEquals(changeByteChar, HSLFTextParagraph.getRawText(paras));
-
- String changeCharChar = "This is a test title with a '\u0147' N with a hat";
- HSLFTextParagraph.setText(paras, changeCharChar);
- para = paras.get(0);
- tha = null; tba = null; tca = null;
- for ( org.apache.poi.hslf.record.Record r : para.getRecords()) {
- if (r instanceof TextHeaderAtom) tha = (TextHeaderAtom)r;
- else if (r instanceof TextBytesAtom) tba = (TextBytesAtom)r;
- else if (r instanceof TextCharsAtom) tca = (TextCharsAtom)r;
+ void testAdvancedSetText() throws IOException {
+ try (HSLFSlideShow ppt = getSlideShow("basic_test_ppt_file.ppt")) {
+ List<HSLFTextParagraph> paras = ppt.getSlides().get(0).getTextParagraphs().get(0);
+ final HSLFTextParagraph para = paras.get(0);
+
+ final TextBytesAtom[] tba = { null };
+ final TextCharsAtom[] tca = { null };
+ Runnable extract = () -> {
+ tba[0] = null;
+ tca[0] = null;
+ Stream.of(para.getRecords()).forEach(r -> {
+ if (r instanceof TextBytesAtom) tba[0] = (TextBytesAtom) r;
+ else if (r instanceof TextCharsAtom) tca[0] = (TextCharsAtom) r;
+ });
+ };
+
+ // Bytes -> Bytes
+ extract.run();
+ assertNull(tca[0]);
+ assertNotNull(tba);
+ // assertFalse(run._isUnicode);
+ assertEquals("This is a test title", para.getTextRuns().get(0).getRawText());
+
+ String changeBytesOnly = "New Test Title";
+ HSLFTextParagraph.setText(paras, changeBytesOnly);
+ extract.run();
+ assertEquals(changeBytesOnly, HSLFTextParagraph.getRawText(paras));
+ assertNull(tca[0]);
+ assertNotNull(tba);
+
+ // Bytes -> Chars
+ String changeByteChar = "This is a test title with a '\u0121' g with a dot";
+ HSLFTextParagraph.setText(paras, changeByteChar);
+ extract.run();
+ assertEquals(changeByteChar, HSLFTextParagraph.getRawText(paras));
+ assertNotNull(tca[0]);
+ assertNull(tba[0]);
+
+ // Chars -> Chars
+ String changeCharChar = "This is a test title with a '\u0147' N with a hat";
+ HSLFTextParagraph.setText(paras, changeCharChar);
+ extract.run();
+
+ assertEquals(changeCharChar, HSLFTextParagraph.getRawText(paras));
+ assertNotNull(tca[0]);
+ assertNull(tba[0]);
}
-
- assertEquals(changeCharChar, HSLFTextParagraph.getRawText(paras));
- assertNotNull(tca);
- assertNull(tba);
}
/**
* set up for it
*/
@Test
- void testGetRichTextNonRich() {
- HSLFSlide slideOne = ss.getSlides().get(0);
- List<List<HSLFTextParagraph>> textParass = slideOne.getTextParagraphs();
+ void testGetRichTextNonRich() throws IOException {
+ try (HSLFSlideShow ppt = getSlideShow("basic_test_ppt_file.ppt")) {
+ List<List<HSLFTextParagraph>> textParass = ppt.getSlides().get(0).getTextParagraphs();
- assertEquals(2, textParass.size());
+ assertEquals(2, textParass.size());
- List<HSLFTextParagraph> trA = textParass.get(0);
- List<HSLFTextParagraph> trB = textParass.get(1);
+ List<HSLFTextParagraph> trA = textParass.get(0);
+ List<HSLFTextParagraph> trB = textParass.get(1);
- assertEquals(1, trA.size());
- assertEquals(2, trB.size());
+ assertEquals(1, trA.size());
+ assertEquals(2, trB.size());
- HSLFTextRun rtrA = trA.get(0).getTextRuns().get(0);
- HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
+ HSLFTextRun rtrA = trA.get(0).getTextRuns().get(0);
+ HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
- assertEquals(HSLFTextParagraph.getRawText(trA), rtrA.getRawText());
- assertEquals(HSLFTextParagraph.getRawText(trB.subList(0, 1)), rtrB.getRawText());
+ assertEquals(HSLFTextParagraph.getRawText(trA), rtrA.getRawText());
+ assertEquals(HSLFTextParagraph.getRawText(trB.subList(0, 1)), rtrB.getRawText());
+ }
}
/**
* Tests to ensure that the rich text runs are built up correctly
*/
@Test
- void testGetRichText() {
- HSLFSlide slideOne = ssRich.getSlides().get(0);
- List<List<HSLFTextParagraph>> textParass = slideOne.getTextParagraphs();
+ void testGetRichText() throws IOException {
+ try (HSLFSlideShow ppt = getSlideShow("Single_Coloured_Page.ppt")) {
+ List<List<HSLFTextParagraph>> textParass = ppt.getSlides().get(0).getTextParagraphs();
- assertEquals(2, textParass.size());
+ assertEquals(2, textParass.size());
- List<HSLFTextParagraph> trA = textParass.get(0);
- List<HSLFTextParagraph> trB = textParass.get(1);
+ List<HSLFTextParagraph> trA = textParass.get(0);
+ List<HSLFTextParagraph> trB = textParass.get(1);
- assertEquals(1, trA.size());
- assertEquals(3, trB.size());
+ assertEquals(1, trA.size());
+ assertEquals(3, trB.size());
- HSLFTextRun rtrA = trA.get(0).getTextRuns().get(0);
- HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
- HSLFTextRun rtrC = trB.get(1).getTextRuns().get(0);
- HSLFTextRun rtrD = trB.get(2).getTextRuns().get(0);
+ HSLFTextRun rtrA = trA.get(0).getTextRuns().get(0);
+ HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
+ HSLFTextRun rtrC = trB.get(1).getTextRuns().get(0);
+ HSLFTextRun rtrD = trB.get(2).getTextRuns().get(0);
- assertEquals(HSLFTextParagraph.getRawText(trA), rtrA.getRawText());
+ assertEquals(HSLFTextParagraph.getRawText(trA), rtrA.getRawText());
- String trBstr = HSLFTextParagraph.getRawText(trB);
- assertEquals(trBstr.substring(0, 30), rtrB.getRawText());
- assertEquals(trBstr.substring(30,58), rtrC.getRawText());
- assertEquals(trBstr.substring(58,82), rtrD.getRawText());
+ String trBstr = HSLFTextParagraph.getRawText(trB);
+ assertEquals(trBstr.substring(0, 30), rtrB.getRawText());
+ assertEquals(trBstr.substring(30, 58), rtrC.getRawText());
+ assertEquals(trBstr.substring(58, 82), rtrD.getRawText());
- // Same paragraph styles
- assertEquals(trB.get(0).getParagraphStyle(), trB.get(1).getParagraphStyle());
- assertEquals(trB.get(0).getParagraphStyle(), trB.get(2).getParagraphStyle());
+ // Same paragraph styles
+ assertEquals(trB.get(0).getParagraphStyle(), trB.get(1).getParagraphStyle());
+ assertEquals(trB.get(0).getParagraphStyle(), trB.get(2).getParagraphStyle());
- // Different char styles
- assertNotEquals(rtrB.getCharacterStyle(), rtrC.getCharacterStyle());
- assertNotEquals(rtrB.getCharacterStyle(), rtrD.getCharacterStyle());
- assertNotEquals(rtrC.getCharacterStyle(), rtrD.getCharacterStyle());
+ // Different char styles
+ assertNotEquals(rtrB.getCharacterStyle(), rtrC.getCharacterStyle());
+ assertNotEquals(rtrB.getCharacterStyle(), rtrD.getCharacterStyle());
+ assertNotEquals(rtrC.getCharacterStyle(), rtrD.getCharacterStyle());
+ }
}
/**
* ensuring that everything stays with the same default styling
*/
@Test
- void testSetTextWhereNotRich() {
- HSLFSlide slideOne = ss.getSlides().get(0);
- List<List<HSLFTextParagraph>> textParass = slideOne.getTextParagraphs();
- List<HSLFTextParagraph> trB = textParass.get(0);
- assertEquals(1, trB.size());
-
- HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
- assertEquals(HSLFTextParagraph.getText(trB), rtrB.getRawText());
-
- // Change text via normal
- HSLFTextParagraph.setText(trB, "Test Foo Test");
- rtrB = trB.get(0).getTextRuns().get(0);
- assertEquals("Test Foo Test", HSLFTextParagraph.getRawText(trB));
- assertEquals("Test Foo Test", rtrB.getRawText());
+ void testSetTextWhereNotRich() throws IOException {
+ try (HSLFSlideShow ppt = getSlideShow("basic_test_ppt_file.ppt")) {
+ List<HSLFTextParagraph> trB = ppt.getSlides().get(0).getTextParagraphs().get(0);
+ assertEquals(1, trB.size());
+
+ HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
+ assertEquals(HSLFTextParagraph.getText(trB), rtrB.getRawText());
+
+ // Change text via normal
+ HSLFTextParagraph.setText(trB, "Test Foo Test");
+ rtrB = trB.get(0).getTextRuns().get(0);
+ assertEquals("Test Foo Test", HSLFTextParagraph.getRawText(trB));
+ assertEquals("Test Foo Test", rtrB.getRawText());
+ }
}
/**
* sets everything to the same styling
*/
@Test
- void testSetTextWhereRich() {
- HSLFSlide slideOne = ssRich.getSlides().get(0);
- List<List<HSLFTextParagraph>> textParass = slideOne.getTextParagraphs();
- List<HSLFTextParagraph> trB = textParass.get(1);
- assertEquals(3, trB.size());
-
- HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
- HSLFTextRun rtrC = trB.get(1).getTextRuns().get(0);
- HSLFTextRun rtrD = trB.get(2).getTextRuns().get(0);
- TextPropCollection tpBP = rtrB.getTextParagraph().getParagraphStyle();
- TextPropCollection tpBC = rtrB.getCharacterStyle();
- TextPropCollection tpCP = rtrC.getTextParagraph().getParagraphStyle();
- TextPropCollection tpCC = rtrC.getCharacterStyle();
- TextPropCollection tpDP = rtrD.getTextParagraph().getParagraphStyle();
- TextPropCollection tpDC = rtrD.getCharacterStyle();
+ void testSetTextWhereRich() throws IOException {
+ try (HSLFSlideShow ppt = getSlideShow("Single_Coloured_Page.ppt")) {
+ List<HSLFTextParagraph> trB = ppt.getSlides().get(0).getTextParagraphs().get(1);
+ assertEquals(3, trB.size());
+
+ HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
+ HSLFTextRun rtrC = trB.get(1).getTextRuns().get(0);
+ HSLFTextRun rtrD = trB.get(2).getTextRuns().get(0);
+ TextPropCollection tpBP = rtrB.getTextParagraph().getParagraphStyle();
+ TextPropCollection tpBC = rtrB.getCharacterStyle();
+ TextPropCollection tpCP = rtrC.getTextParagraph().getParagraphStyle();
+ TextPropCollection tpCC = rtrC.getCharacterStyle();
+ TextPropCollection tpDP = rtrD.getTextParagraph().getParagraphStyle();
+ TextPropCollection tpDC = rtrD.getCharacterStyle();
// assertEquals(trB.getRawText().substring(0, 30), rtrB.getRawText());
- assertNotNull(tpBP);
- assertNotNull(tpBC);
- assertNotNull(tpCP);
- assertNotNull(tpCC);
- assertNotNull(tpDP);
- assertNotNull(tpDC);
- assertEquals(tpBP,tpCP);
- assertEquals(tpBP,tpDP);
- assertEquals(tpCP,tpDP);
- assertNotEquals(tpBC,tpCC);
- assertNotEquals(tpBC,tpDC);
- assertNotEquals(tpCC,tpDC);
-
- // Change text via normal
- HSLFTextParagraph.setText(trB, "Test Foo Test");
-
- // Ensure now have first style
- assertEquals(1, trB.get(0).getTextRuns().size());
- rtrB = trB.get(0).getTextRuns().get(0);
- assertEquals("Test Foo Test", HSLFTextParagraph.getRawText(trB));
- assertEquals("Test Foo Test", rtrB.getRawText());
- assertNotNull(rtrB.getCharacterStyle());
- assertNotNull(rtrB.getTextParagraph().getParagraphStyle());
- assertEquals( tpBP, rtrB.getTextParagraph().getParagraphStyle() );
- assertEquals( tpBC, rtrB.getCharacterStyle() );
+ assertNotNull(tpBP);
+ assertNotNull(tpBC);
+ assertNotNull(tpCP);
+ assertNotNull(tpCC);
+ assertNotNull(tpDP);
+ assertNotNull(tpDC);
+ assertEquals(tpBP, tpCP);
+ assertEquals(tpBP, tpDP);
+ assertEquals(tpCP, tpDP);
+ assertNotEquals(tpBC, tpCC);
+ assertNotEquals(tpBC, tpDC);
+ assertNotEquals(tpCC, tpDC);
+
+ // Change text via normal
+ HSLFTextParagraph.setText(trB, "Test Foo Test");
+
+ // Ensure now have first style
+ assertEquals(1, trB.get(0).getTextRuns().size());
+ rtrB = trB.get(0).getTextRuns().get(0);
+ assertEquals("Test Foo Test", HSLFTextParagraph.getRawText(trB));
+ assertEquals("Test Foo Test", rtrB.getRawText());
+ assertNotNull(rtrB.getCharacterStyle());
+ assertNotNull(rtrB.getTextParagraph().getParagraphStyle());
+ assertEquals(tpBP, rtrB.getTextParagraph().getParagraphStyle());
+ assertEquals(tpBC, rtrB.getCharacterStyle());
+ }
}
/**
* in a rich text run, that doesn't happen to actually be rich
*/
@Test
- void testChangeTextInRichTextRunNonRich() {
- HSLFSlide slideOne = ss.getSlides().get(0);
- List<List<HSLFTextParagraph>> textRuns = slideOne.getTextParagraphs();
- List<HSLFTextParagraph> trB = textRuns.get(1);
- assertEquals(1, trB.get(0).getTextRuns().size());
-
- HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
- assertEquals(HSLFTextParagraph.getRawText(trB.subList(0, 1)), rtrB.getRawText());
- assertNotNull(rtrB.getCharacterStyle());
- assertNotNull(rtrB.getTextParagraph().getParagraphStyle());
-
- // Change text via rich
- rtrB.setText("Test Test Test");
- assertEquals("Test Test Test", HSLFTextParagraph.getRawText(trB.subList(0, 1)));
- assertEquals("Test Test Test", rtrB.getRawText());
-
- // Will now have dummy props
- assertNotNull(rtrB.getCharacterStyle());
- assertNotNull(rtrB.getTextParagraph().getParagraphStyle());
+ void testChangeTextInRichTextRunNonRich() throws IOException {
+ try (HSLFSlideShow ppt = getSlideShow("basic_test_ppt_file.ppt")) {
+ List<List<HSLFTextParagraph>> textRuns = ppt.getSlides().get(0).getTextParagraphs();
+ List<HSLFTextParagraph> trB = textRuns.get(1);
+ assertEquals(1, trB.get(0).getTextRuns().size());
+
+ HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
+ assertEquals(HSLFTextParagraph.getRawText(trB.subList(0, 1)), rtrB.getRawText());
+ assertNotNull(rtrB.getCharacterStyle());
+ assertNotNull(rtrB.getTextParagraph().getParagraphStyle());
+
+ // Change text via rich
+ rtrB.setText("Test Test Test");
+ assertEquals("Test Test Test", HSLFTextParagraph.getRawText(trB.subList(0, 1)));
+ assertEquals("Test Test Test", rtrB.getRawText());
+
+ // Will now have dummy props
+ assertNotNull(rtrB.getCharacterStyle());
+ assertNotNull(rtrB.getTextParagraph().getParagraphStyle());
+ }
}
/**
* correctly
*/
@Test
- void testChangeTextInRichTextRun() {
- HSLFSlide slideOne = ssRich.getSlides().get(0);
- List<List<HSLFTextParagraph>> textParass = slideOne.getTextParagraphs();
- List<HSLFTextParagraph> trB = textParass.get(1);
- assertEquals(3, trB.size());
-
- // We start with 3 text runs, each with their own set of styles,
- // but all sharing the same paragraph styles
- HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
- HSLFTextRun rtrC = trB.get(1).getTextRuns().get(0);
- HSLFTextRun rtrD = trB.get(2).getTextRuns().get(0);
- TextPropCollection tpBP = rtrB.getTextParagraph().getParagraphStyle();
- TextPropCollection tpBC = rtrB.getCharacterStyle();
- TextPropCollection tpCP = rtrC.getTextParagraph().getParagraphStyle();
- TextPropCollection tpCC = rtrC.getCharacterStyle();
- TextPropCollection tpDP = rtrD.getTextParagraph().getParagraphStyle();
- TextPropCollection tpDC = rtrD.getCharacterStyle();
-
- // Check text and stylings
- assertEquals(HSLFTextParagraph.getRawText(trB).substring(0, 30), rtrB.getRawText());
- assertNotNull(tpBP);
- assertNotNull(tpBC);
- assertNotNull(tpCP);
- assertNotNull(tpCC);
- assertNotNull(tpDP);
- assertNotNull(tpDC);
- assertEquals(tpBP, tpCP);
- assertEquals(tpBP, tpDP);
- assertEquals(tpCP, tpDP);
- assertNotEquals(tpBC, tpCC);
- assertNotEquals(tpBC, tpDC);
- assertNotEquals(tpCC, tpDC);
-
- // Check text in the rich runs
- assertEquals("This is the subtitle, in bold\r", rtrB.getRawText());
- assertEquals("This bit is blue and italic\r", rtrC.getRawText());
- assertEquals("This bit is red (normal)", rtrD.getRawText());
-
- String newBText = "New Subtitle, will still be bold\n";
- String newCText = "New blue and italic text\n";
- String newDText = "Funky new normal red text";
- rtrB.setText(newBText);
- rtrC.setText(newCText);
- rtrD.setText(newDText);
- HSLFTextParagraph.storeText(trB);
-
- assertEquals(newBText.replace('\n','\r'), rtrB.getRawText());
- assertEquals(newCText.replace('\n','\r'), rtrC.getRawText());
- assertEquals(newDText.replace('\n','\r'), rtrD.getRawText());
-
- assertEquals(newBText.replace('\n','\r') + newCText.replace('\n','\r') + newDText.replace('\n','\r'), HSLFTextParagraph.getRawText(trB));
-
- // The styles should have been updated for the new sizes
- assertEquals(newBText.length(), tpBC.getCharactersCovered());
- assertEquals(newCText.length(), tpCC.getCharactersCovered());
- assertEquals(newDText.length()+1, tpDC.getCharactersCovered()); // Last one is always one larger
-
- // Paragraph style should be sum of text length
- assertEquals(
- newBText.length() + newCText.length() + newDText.length() +1,
- tpBP.getCharactersCovered() + tpCP.getCharactersCovered() + tpDP.getCharactersCovered()
- );
-
- // Check stylings still as expected
- TextPropCollection ntpBC = rtrB.getCharacterStyle();
- TextPropCollection ntpCC = rtrC.getCharacterStyle();
- TextPropCollection ntpDC = rtrD.getCharacterStyle();
- assertEquals(tpBC.getTextPropList(), ntpBC.getTextPropList());
- assertEquals(tpCC.getTextPropList(), ntpCC.getTextPropList());
- assertEquals(tpDC.getTextPropList(), ntpDC.getTextPropList());
+ void testChangeTextInRichTextRun() throws IOException {
+ try (HSLFSlideShow ppt = getSlideShow("Single_Coloured_Page.ppt")) {
+ HSLFSlide slideOne = ppt.getSlides().get(0);
+ List<List<HSLFTextParagraph>> textParass = slideOne.getTextParagraphs();
+ List<HSLFTextParagraph> trB = textParass.get(1);
+ assertEquals(3, trB.size());
+
+ // We start with 3 text runs, each with their own set of styles,
+ // but all sharing the same paragraph styles
+ HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
+ HSLFTextRun rtrC = trB.get(1).getTextRuns().get(0);
+ HSLFTextRun rtrD = trB.get(2).getTextRuns().get(0);
+ TextPropCollection tpBP = rtrB.getTextParagraph().getParagraphStyle();
+ TextPropCollection tpBC = rtrB.getCharacterStyle();
+ TextPropCollection tpCP = rtrC.getTextParagraph().getParagraphStyle();
+ TextPropCollection tpCC = rtrC.getCharacterStyle();
+ TextPropCollection tpDP = rtrD.getTextParagraph().getParagraphStyle();
+ TextPropCollection tpDC = rtrD.getCharacterStyle();
+
+ // Check text and stylings
+ assertEquals(HSLFTextParagraph.getRawText(trB).substring(0, 30), rtrB.getRawText());
+ assertNotNull(tpBP);
+ assertNotNull(tpBC);
+ assertNotNull(tpCP);
+ assertNotNull(tpCC);
+ assertNotNull(tpDP);
+ assertNotNull(tpDC);
+ assertEquals(tpBP, tpCP);
+ assertEquals(tpBP, tpDP);
+ assertEquals(tpCP, tpDP);
+ assertNotEquals(tpBC, tpCC);
+ assertNotEquals(tpBC, tpDC);
+ assertNotEquals(tpCC, tpDC);
+
+ // Check text in the rich runs
+ assertEquals("This is the subtitle, in bold\r", rtrB.getRawText());
+ assertEquals("This bit is blue and italic\r", rtrC.getRawText());
+ assertEquals("This bit is red (normal)", rtrD.getRawText());
+
+ String newBText = "New Subtitle, will still be bold\n";
+ String newCText = "New blue and italic text\n";
+ String newDText = "Funky new normal red text";
+ rtrB.setText(newBText);
+ rtrC.setText(newCText);
+ rtrD.setText(newDText);
+ HSLFTextParagraph.storeText(trB);
+
+ assertEquals(newBText.replace('\n', '\r'), rtrB.getRawText());
+ assertEquals(newCText.replace('\n', '\r'), rtrC.getRawText());
+ assertEquals(newDText.replace('\n', '\r'), rtrD.getRawText());
+
+ assertEquals(newBText.replace('\n', '\r') + newCText.replace('\n', '\r') + newDText.replace('\n', '\r'), HSLFTextParagraph.getRawText(trB));
+
+ // The styles should have been updated for the new sizes
+ assertEquals(newBText.length(), tpBC.getCharactersCovered());
+ assertEquals(newCText.length(), tpCC.getCharactersCovered());
+ assertEquals(newDText.length() + 1, tpDC.getCharactersCovered()); // Last one is always one larger
+
+ // Paragraph style should be sum of text length
+ assertEquals(
+ newBText.length() + newCText.length() + newDText.length() + 1,
+ tpBP.getCharactersCovered() + tpCP.getCharactersCovered() + tpDP.getCharactersCovered()
+ );
+
+ // Check stylings still as expected
+ TextPropCollection ntpBC = rtrB.getCharacterStyle();
+ TextPropCollection ntpCC = rtrC.getCharacterStyle();
+ TextPropCollection ntpDC = rtrD.getCharacterStyle();
+ assertEquals(tpBC.getTextPropList(), ntpBC.getTextPropList());
+ assertEquals(tpCC.getTextPropList(), ntpCC.getTextPropList());
+ assertEquals(tpDC.getTextPropList(), ntpDC.getTextPropList());
+ }
}
*/
@Test
void testBug41015() throws IOException {
- List<HSLFTextRun> rt;
-
- HSLFSlideShow ppt = HSLFTestDataSamples.getSlideShow("bug-41015.ppt");
- HSLFSlide sl = ppt.getSlides().get(0);
- List<List<HSLFTextParagraph>> textParass = sl.getTextParagraphs();
- assertEquals(2, textParass.size());
-
- List<HSLFTextParagraph> textParas = textParass.get(0);
- rt = textParass.get(0).get(0).getTextRuns();
- assertEquals(1, rt.size());
- assertEquals(0, textParass.get(0).get(0).getIndentLevel());
- assertEquals("sdfsdfsdf", rt.get(0).getRawText());
-
- textParas = textParass.get(1);
- String[] texts = {"Sdfsdfsdf\r", "Dfgdfg\r", "Dfgdfgdfg\r", "Sdfsdfs\r", "Sdfsdf\r"};
- int[] indents = {0, 0, 0, 1, 1};
- int i=0;
- for (HSLFTextParagraph p : textParas) {
- assertEquals(texts[i], p.getTextRuns().get(0).getRawText());
- assertEquals(indents[i], p.getIndentLevel());
- i++;
+ try (HSLFSlideShow ppt = getSlideShow("bug-41015.ppt")) {
+ HSLFSlide sl = ppt.getSlides().get(0);
+ List<List<HSLFTextParagraph>> textParass = sl.getTextParagraphs();
+ assertEquals(2, textParass.size());
+
+ List<HSLFTextParagraph> textParas = textParass.get(0);
+ List<HSLFTextRun> rt = textParass.get(0).get(0).getTextRuns();
+ assertEquals(1, rt.size());
+ assertEquals(0, textParass.get(0).get(0).getIndentLevel());
+ assertEquals("sdfsdfsdf", rt.get(0).getRawText());
+
+ textParas = textParass.get(1);
+ String[] texts = {"Sdfsdfsdf\r", "Dfgdfg\r", "Dfgdfgdfg\r", "Sdfsdfs\r", "Sdfsdf\r"};
+ int[] indents = {0, 0, 0, 1, 1};
+ int i = 0;
+ for (HSLFTextParagraph p : textParas) {
+ assertEquals(texts[i], p.getTextRuns().get(0).getRawText());
+ assertEquals(indents[i], p.getIndentLevel());
+ i++;
+ }
}
- ppt.close();
}
/**
*/
@Test
void testAddTextRun() throws IOException {
- HSLFSlideShow ppt = new HSLFSlideShow();
- HSLFSlide slide = ppt.createSlide();
-
- assertEquals(0, slide.getTextParagraphs().size());
-
- HSLFTextBox shape1 = new HSLFTextBox();
- List<HSLFTextParagraph> run1 = shape1.getTextParagraphs();
- shape1.setText("Text 1");
- slide.addShape(shape1);
-
- //The array of Slide's text runs must be updated when new text shapes are added.
- List<List<HSLFTextParagraph>> runs = slide.getTextParagraphs();
- assertNotNull(runs);
- assertSame(run1, runs.get(0));
-
- HSLFTextBox shape2 = new HSLFTextBox();
- List<HSLFTextParagraph> run2 = shape2.getTextParagraphs();
- shape2.setText("Text 2");
- slide.addShape(shape2);
-
- runs = slide.getTextParagraphs();
- assertEquals(2, runs.size());
-
- assertSame(run1, runs.get(0));
- assertSame(run2, runs.get(1));
-
- // as getShapes()
- List<HSLFShape> sh = slide.getShapes();
- assertEquals(2, sh.size());
- assertTrue(sh.get(0) instanceof HSLFTextBox);
- HSLFTextBox box1 = (HSLFTextBox)sh.get(0);
- assertSame(run1, box1.getTextParagraphs());
- HSLFTextBox box2 = (HSLFTextBox)sh.get(1);
- assertSame(run2, box2.getTextParagraphs());
-
- // test Table - a complex group of shapes containing text objects
- HSLFSlide slide2 = ppt.createSlide();
- assertTrue(slide2.getTextParagraphs().isEmpty());
- HSLFTable table = new HSLFTable(2, 2);
- slide2.addShape(table);
- runs = slide2.getTextParagraphs();
- assertNotNull(runs);
- assertEquals(4, runs.size());
- ppt.close();
+ try (HSLFSlideShow ppt = new HSLFSlideShow()) {
+ HSLFSlide slide = ppt.createSlide();
+
+ assertEquals(0, slide.getTextParagraphs().size());
+
+ HSLFTextBox shape1 = new HSLFTextBox();
+ List<HSLFTextParagraph> run1 = shape1.getTextParagraphs();
+ shape1.setText("Text 1");
+ slide.addShape(shape1);
+
+ //The array of Slide's text runs must be updated when new text shapes are added.
+ List<List<HSLFTextParagraph>> runs = slide.getTextParagraphs();
+ assertNotNull(runs);
+ assertSame(run1, runs.get(0));
+
+ HSLFTextBox shape2 = new HSLFTextBox();
+ List<HSLFTextParagraph> run2 = shape2.getTextParagraphs();
+ shape2.setText("Text 2");
+ slide.addShape(shape2);
+
+ runs = slide.getTextParagraphs();
+ assertEquals(2, runs.size());
+
+ assertSame(run1, runs.get(0));
+ assertSame(run2, runs.get(1));
+
+ // as getShapes()
+ List<HSLFShape> sh = slide.getShapes();
+ assertEquals(2, sh.size());
+ assertTrue(sh.get(0) instanceof HSLFTextBox);
+ HSLFTextBox box1 = (HSLFTextBox) sh.get(0);
+ assertSame(run1, box1.getTextParagraphs());
+ HSLFTextBox box2 = (HSLFTextBox) sh.get(1);
+ assertSame(run2, box2.getTextParagraphs());
+
+ // test Table - a complex group of shapes containing text objects
+ HSLFSlide slide2 = ppt.createSlide();
+ assertTrue(slide2.getTextParagraphs().isEmpty());
+ HSLFTable table = new HSLFTable(2, 2);
+ slide2.addShape(table);
+ runs = slide2.getTextParagraphs();
+ assertNotNull(runs);
+ assertEquals(4, runs.size());
+ }
}
@Test
void test48916() throws IOException {
- HSLFSlideShow ppt1 = HSLFTestDataSamples.getSlideShow("SampleShow.ppt");
- List<HSLFSlide> slides = ppt1.getSlides();
- for(HSLFSlide slide : slides){
- for(HSLFShape sh : slide.getShapes()){
- if (!(sh instanceof HSLFTextShape)) continue;
- HSLFTextShape tx = (HSLFTextShape)sh;
- List<HSLFTextParagraph> paras = tx.getTextParagraphs();
- //verify that records cached in TextRun and EscherTextboxWrapper are the same
- org.apache.poi.hslf.record.Record[] runChildren = paras.get(0).getRecords();
- org.apache.poi.hslf.record.Record[] txboxChildren = tx.getEscherTextboxWrapper().getChildRecords();
- assertEquals(runChildren.length, txboxChildren.length);
- for(int i=0; i < txboxChildren.length; i++){
- assertSame(txboxChildren[i], runChildren[i]);
- }
- // caused NPE prior to fix of Bugzilla #48916
- for (HSLFTextParagraph p : paras) {
- for (HSLFTextRun rt : p.getTextRuns()) {
- rt.setBold(true);
- rt.setFontColor(Color.RED);
+ try (HSLFSlideShow ppt1 = getSlideShow("SampleShow.ppt")) {
+ List<HSLFSlide> slides = ppt1.getSlides();
+ for (HSLFSlide slide : slides) {
+ for (HSLFShape sh : slide.getShapes()) {
+ if (!(sh instanceof HSLFTextShape)) continue;
+ HSLFTextShape tx = (HSLFTextShape) sh;
+ List<HSLFTextParagraph> paras = tx.getTextParagraphs();
+ //verify that records cached in TextRun and EscherTextboxWrapper are the same
+ org.apache.poi.hslf.record.Record[] runChildren = paras.get(0).getRecords();
+ org.apache.poi.hslf.record.Record[] txboxChildren = tx.getEscherTextboxWrapper().getChildRecords();
+ assertEquals(runChildren.length, txboxChildren.length);
+ for (int i = 0; i < txboxChildren.length; i++) {
+ assertSame(txboxChildren[i], runChildren[i]);
+ }
+ // caused NPE prior to fix of Bugzilla #48916
+ for (HSLFTextParagraph p : paras) {
+ for (HSLFTextRun rt : p.getTextRuns()) {
+ rt.setBold(true);
+ rt.setFontColor(Color.RED);
+ }
}
+ // tx.storeText();
}
- // tx.storeText();
}
- }
- HSLFSlideShow ppt2 = HSLFTestDataSamples.writeOutAndReadBack(ppt1);
- for(HSLFSlide slide : ppt2.getSlides()){
- for(HSLFShape sh : slide.getShapes()){
- if(sh instanceof HSLFTextShape){
- HSLFTextShape tx = (HSLFTextShape)sh;
- List<HSLFTextParagraph> run = tx.getTextParagraphs();
- HSLFTextRun rt = run.get(0).getTextRuns().get(0);
- assertTrue(rt.isBold());
- assertEquals(Color.RED, getColor(rt.getFontColor()));
- }
+ try (HSLFSlideShow ppt2 = HSLFTestDataSamples.writeOutAndReadBack(ppt1)) {
+ List<HSLFTextRun> runs = ppt2.getSlides().stream()
+ .flatMap(s -> s.getShapes().stream())
+ .filter(s -> s instanceof HSLFTextShape)
+ .map(s -> ((HSLFTextShape) s).getTextParagraphs().get(0).getTextRuns().get(0))
+ .collect(Collectors.toList());
+
+ assertFalse(runs.isEmpty());
+ assertTrue(runs.stream().allMatch(HSLFTextRun::isBold));
+ assertTrue(runs.stream().map(HSLFTextRun::getFontColor)
+ .map(BaseTestSlideShow::getColor).allMatch(Color.RED::equals));
}
}
- ppt2.close();
- ppt1.close();
}
@Test
void test52244() throws IOException {
- HSLFSlideShow ppt = HSLFTestDataSamples.getSlideShow("52244.ppt");
- HSLFSlide slide = ppt.getSlides().get(0);
+ try (HSLFSlideShow ppt = getSlideShow("52244.ppt")) {
+ HSLFSlide slide = ppt.getSlides().get(0);
- int[] sizes = {36, 24, 12, 32, 12, 12};
+ List<HSLFTextRun> runs = slide.getTextParagraphs().stream().map(tp -> tp.get(0).getTextRuns().get(0)).collect(Collectors.toList());
+ assertTrue(runs.stream().map(HSLFTextRun::getFontFamily).allMatch("Arial"::equals));
- int i=0;
- for (List<HSLFTextParagraph> textParas : slide.getTextParagraphs()) {
- HSLFTextRun first = textParas.get(0).getTextRuns().get(0);
- assertEquals("Arial", first.getFontFamily());
- assertNotNull(first.getFontSize());
- assertEquals(sizes[i++], first.getFontSize().intValue());
+ int[] exp = {36, 24, 12, 32, 12, 12};
+ int[] act = runs.stream().map(HSLFTextRun::getFontSize).mapToInt(Double::intValue).toArray();
+ assertArrayEquals(exp, act);
}
- ppt.close();
}
@Test
assertEquals("\npara", title.getText());
}
}
+
+ @Test
+ void datetimeFormats() throws IOException {
+ LocalDateTime ldt = LocalDateTime.of(2012, 3, 4, 23, 45, 26);
+ final Map<Locale, String[]> formats = new HashMap<>();
+ formats.put(Locale.GERMANY, new String[]{
+ "04.03.2012",
+ "Sonntag, 4. M\u00e4rz 2012",
+ "04/03/12",
+ "4. M\u00e4rz 2012",
+ "12-03-04",
+ "M\u00e4rz 12",
+ "M\u00e4r-12",
+ "04.03.12 23:45",
+ "04.03.12 23:45:26",
+ "23:45",
+ "23:45:26",
+ "11:45",
+ "11:45:26"
+ });
+ formats.put(Locale.US, new String[]{
+ "03/04/2012",
+ "Sunday, March 4, 2012",
+ "4 March 2012",
+ "March 04, 2012",
+ "4-Mar-12",
+ "March 12",
+ "Mar-12",
+ "3/4/12 11:45 PM",
+ "3/4/12 11:45:26 PM",
+ "23:45",
+ "23:45:26",
+ "11:45 PM",
+ "11:45:26 PM"
+ });
+
+
+ try (HSLFSlideShow ppt = getSlideShow("datetime.ppt")) {
+ List<HSLFTextShape> shapes = ppt.getSlides().get(0).getShapes()
+ .stream().map(HSLFTextShape.class::cast).collect(Collectors.toList());
+
+ int[] expFormatId = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
+ int[] actFormatId = shapes.stream().flatMap(tp -> Stream.of(tp.getTextParagraphs().get(0).getRecords()))
+ .filter(r -> r instanceof DateTimeMCAtom)
+ .mapToInt(r -> ((DateTimeMCAtom)r).getIndex()).toArray();
+ assertArrayEquals(expFormatId, actFormatId);
+
+ List<HSLFShapePlaceholderDetails> phs = shapes.stream().map(HSLFSimpleShape::getPlaceholderDetails).collect(Collectors.toList());
+
+ for (Map.Entry<Locale,String[]> me : formats.entrySet()) {
+ LocaleUtil.setUserLocale(me.getKey());
+
+ // refresh internal members
+ phs.forEach(PlaceholderDetails::getPlaceholder);
+
+ String[] actDate = phs.stream().map(PlaceholderDetails::getDateFormat).map(ldt::format).toArray(String[]::new);
+ assertArrayEquals(me.getValue(), actDate);
+ }
+ } finally {
+ LocaleUtil.resetUserLocale();
+ }
+ }
}
// in XSLF, slidenumber and date shapes aren't marked as placeholders opposed to HSLF
Placeholder ph = ((SimpleShape<?,?>)shape).getPlaceholder();
if (ph != null) {
- return slide.getDisplayPlaceholder(ph);
+ return slide.getDisplayPlaceholder((SimpleShape<?, ?>)shape);
}
}
return slide.getFollowMasterGraphics();
package org.apache.poi.sl.draw;
+import static org.apache.logging.log4j.util.Unbox.box;
+
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.text.AttributedCharacterIterator;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.AttributedString;
+import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import org.apache.poi.sl.usermodel.Insets2D;
import org.apache.poi.sl.usermodel.PaintStyle;
import org.apache.poi.sl.usermodel.PlaceableShape;
-import org.apache.poi.sl.usermodel.ShapeContainer;
-import org.apache.poi.sl.usermodel.Sheet;
+import org.apache.poi.sl.usermodel.PlaceholderDetails;
+import org.apache.poi.sl.usermodel.SimpleShape;
import org.apache.poi.sl.usermodel.Slide;
import org.apache.poi.sl.usermodel.TextParagraph;
import org.apache.poi.sl.usermodel.TextParagraph.BulletStyle;
import org.apache.poi.util.LocaleUtil;
import org.apache.poi.util.Units;
-import static org.apache.logging.log4j.util.Unbox.box;
-
public class DrawTextParagraph implements Drawable {
private static final Logger LOG = LogManager.getLogger(DrawTextParagraph.class);
}
protected String getRenderableText(Graphics2D graphics, TextRun tr) {
- if (tr.getFieldType() == FieldType.SLIDE_NUMBER) {
- Slide<?,?> slide = (Slide<?,?>)graphics.getRenderingHint(Drawable.CURRENT_SLIDE);
- return (slide == null) ? "" : Integer.toString(slide.getSlideNumber());
+ FieldType ft = tr.getFieldType();
+ if (ft == null) {
+ return getRenderableText(tr);
+ }
+ if (!tr.getRawText().isEmpty()) {
+ switch (ft) {
+ case SLIDE_NUMBER: {
+ Slide<?, ?> slide = (Slide<?, ?>) graphics.getRenderingHint(Drawable.CURRENT_SLIDE);
+ return (slide == null) ? "" : Integer.toString(slide.getSlideNumber());
+ }
+ case DATE_TIME: {
+ PlaceholderDetails pd = ((SimpleShape<?, ?>) this.getParagraphShape()).getPlaceholderDetails();
+ // refresh internal members
+ pd.getPlaceholder();
+ String uda = pd.getUserDate();
+ if (uda != null) {
+ return uda;
+ }
+ Calendar cal = LocaleUtil.getLocaleCalendar();
+ LocalDateTime now = LocalDateTime.ofInstant(cal.toInstant(), cal.getTimeZone().toZoneId());
+ return now.format(pd.getDateFormat());
+ }
+ }
}
- return getRenderableText(tr);
+ return "";
}
@Internal
/**
* Helper method for paint style relative to bounds, e.g. gradient paint
*/
- @SuppressWarnings("rawtypes")
private PlaceableShape<?,?> getParagraphShape() {
- return new PlaceableShape(){
- @Override
- public ShapeContainer<?,?> getParent() { return null; }
- @Override
- public Rectangle2D getAnchor() { return paragraph.getParentShape().getAnchor(); }
- @Override
- public void setAnchor(Rectangle2D anchor) {}
- @Override
- public double getRotation() { return 0; }
- @Override
- public void setRotation(double theta) {}
- @Override
- public void setFlipHorizontal(boolean flip) {}
- @Override
- public void setFlipVertical(boolean flip) {}
- @Override
- public boolean getFlipHorizontal() { return false; }
- @Override
- public boolean getFlipVertical() { return false; }
- @Override
- public Sheet<?,?> getSheet() { return paragraph.getParentShape().getSheet(); }
- };
+ return paragraph.getParentShape();
}
protected List<AttributedStringData> getAttributedString(Graphics2D graphics, StringBuilder text) {
}
/**
- * Processing the glyphs is done in two steps.
- * <li>determine the font group - a text run can have different font groups. Depending on the chars,
- * the correct font group needs to be used
+ * Processing the glyphs is done in two steps:
+ * <ul>
+ * <li>1. determine the font group - a text run can have different font groups.
+ * <li>2. Depending on the chars, the correct font group needs to be used
+ * </ul>
*
* @see <a href="https://blogs.msdn.microsoft.com/officeinteroperability/2013/04/22/office-open-xml-themes-schemes-and-fonts/">Office Open XML Themes, Schemes, and Fonts</a>
*/
package org.apache.poi.sl.usermodel;
+import java.time.format.DateTimeFormatter;
+
/**
* Extended details about placholders
*
enum PlaceholderSize {
quarter, half, full
}
-
+
Placeholder getPlaceholder();
/**
* @param placeholder The shape to use as placeholder or null if no placeholder should be set.
*/
void setPlaceholder(Placeholder placeholder);
-
+
boolean isVisible();
-
+
void setVisible(boolean isVisible);
-
+
PlaceholderSize getSize();
-
+
void setSize(PlaceholderSize size);
/**
* @since POI 4.0.0
*/
void setText(String text);
+
+
+ /**
+ * @return the stored / fixed user specified date
+ *
+ * @since POI 5.2.0
+ */
+ default String getUserDate() {
+ return null;
+ }
+
+ /**
+ * @return Get the date format for the datetime placeholder
+ *
+ * @since POI 5.2.0
+ */
+ default DateTimeFormatter getDateFormat() {
+ return DateTimeFormatter.ISO_LOCAL_DATE;
+ }
}
import java.util.List;
+import org.apache.poi.util.Removal;
+
@SuppressWarnings("unused")
public interface Slide<
S extends Shape<S,P>,
* @param placeholder the placeholder type
* @return {@code true} if the placeholder should be displayed/rendered
* @since POI 3.16-beta2
+ *
+ * @deprecated in POI 5.2.0 - use {@link #getDisplayPlaceholder(SimpleShape)}
+ *
+ */
+ @Deprecated
+ @Removal(version = "6.0.0")
+ default boolean getDisplayPlaceholder(Placeholder placeholder) {
+ return false;
+ }
+
+
+ /**
+ * In XSLF, slidenumber and date shapes aren't marked as placeholders
+ * whereas in HSLF they are activated via a HeadersFooter configuration.
+ * This method is used to generalize that handling.
+ *
+ * @param placeholderRefShape the shape which references to the placeholder
+ * @return {@code true} if the placeholder should be displayed/rendered
+ * @since POI 5.2.0
*/
- boolean getDisplayPlaceholder(Placeholder placeholder);
+ default boolean getDisplayPlaceholder(SimpleShape<?,?> placeholderRefShape) {
+ return false;
+ }
/**
* Sets the slide visibility