<changes>
<release version="3.9-beta1" date="2012-??-??">
+ <action dev="poi-developers" type="add">53979 - Support fetching properties of Numbered Lists from PPT files</action>
<action dev="poi-developers" type="add">53784 - Partial HSMF support for fixed sized properties</action>
<action dev="poi-developers" type="add">53943 - added method processSymbol() to allow converting word symbols </action>
<action dev="poi-developers" type="fix">53763 - avoid style mess when using HSSFOptimiser </action>
*/
public static TextRun[] findTextRuns(PPDrawing ppdrawing) {
final List<TextRun> runsV = new ArrayList<TextRun>();
- EscherTextboxWrapper[] wrappers = ppdrawing.getTextboxWrappers();
+ final EscherTextboxWrapper[] wrappers = ppdrawing.getTextboxWrappers();
for (int i = 0; i < wrappers.length; i++) {
int s1 = runsV.size();
// propagate parents to parent-aware records
RecordContainer.handleParentAwareRecords(wrappers[i]);
- findTextRuns(wrappers[i].getChildRecords(), runsV);
+ findTextRuns(wrappers[i], runsV);
int s2 = runsV.size();
if (s2 != s1){
TextRun t = runsV.get(runsV.size()-1);
}
return runsV.toArray(new TextRun[runsV.size()]);
}
-
/**
* Scans through the supplied record array, looking for
* a TextHeaderAtom followed by one of a TextBytesAtom or
* @param records the records to build from
* @param found vector to add any found to
*/
- protected static void findTextRuns(Record[] records, List<TextRun> found) {
- // Look for a TextHeaderAtom
+ protected static void findTextRuns(final Record[] records, final List<TextRun> found) {
+ findTextRuns(records, found, null);
+ }
+ /**
+ * Scans through the supplied record array, looking for
+ * a TextHeaderAtom followed by one of a TextBytesAtom or
+ * a TextCharsAtom. Builds up TextRuns from these
+ *
+ * @param wrapper an EscherTextboxWrapper
+ * @param found vector to add any found to
+ */
+ protected static void findTextRuns(final EscherTextboxWrapper wrapper, final List<TextRun> found) {
+ findTextRuns(wrapper.getChildRecords(), found, wrapper.getStyleTextProp9Atom());
+ }
+ /**
+ * Scans through the supplied record array, looking for
+ * a TextHeaderAtom followed by one of a TextBytesAtom or
+ * a TextCharsAtom. Builds up TextRuns from these
+ *
+ * @param records the records to build from
+ * @param found vector to add any found to
+ * @param styleTextProp9Atom a StyleTextProp9Atom with numbered lists info
+ */
+ protected static void findTextRuns(final Record[] records, final List<TextRun> found, final StyleTextProp9Atom styleTextProp9Atom) {
for (int i = 0, slwtIndex=0; i < (records.length - 1); i++) {
if (records[i] instanceof TextHeaderAtom) {
TextRun trun = null;
TextBytesAtom tba = (TextBytesAtom) records[i + 1];
trun = new TextRun(tha, tba, stpa);
} else if (records[i + 1].getRecordType() == 4001l) {
- // StyleTextPropAtom - Safe to ignore
+ stpa = (StyleTextPropAtom) records[i + 1];
} else if (records[i + 1].getRecordType() == 4010l) {
// TextSpecInfoAtom - Safe to ignore
} else {
}
if (trun != null) {
- ArrayList lst = new ArrayList();
+ List<Record> lst = new ArrayList<Record>();
for (int j = i; j < records.length; j++) {
if(j > i && records[j] instanceof TextHeaderAtom) break;
lst.add(records[j]);
lst.toArray(recs);
trun._records = recs;
trun.setIndex(slwtIndex);
-
+ trun.setStyleTextProp9Atom(styleTextProp9Atom);
found.add(trun);
i++;
} else {
package org.apache.poi.hslf.model;
import java.awt.Graphics2D;
-import java.util.Vector;
+import java.util.LinkedList;
+import java.util.List;
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherDgRecord;
import org.apache.poi.ddf.EscherSpRecord;
import org.apache.poi.hslf.record.ColorSchemeAtom;
import org.apache.poi.hslf.record.Comment2000;
+import org.apache.poi.hslf.record.EscherTextboxWrapper;
import org.apache.poi.hslf.record.HeadersFootersContainer;
import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.RecordContainer;
import org.apache.poi.hslf.record.RecordTypes;
import org.apache.poi.hslf.record.SlideAtom;
+import org.apache.poi.hslf.record.StyleTextProp9Atom;
import org.apache.poi.hslf.record.TextHeaderAtom;
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
/**
* Constructs a Slide from the Slide record, and the SlideAtomsSet
* containing the text.
- * Initialises TextRuns, to provide easier access to the text
+ * Initializes TextRuns, to provide easier access to the text
*
* @param slide the Slide record we're based on
* @param notes the Notes sheet attached to us
// For the text coming in from the SlideAtomsSet:
// Build up TextRuns from pairs of TextHeaderAtom and
// one of TextBytesAtom or TextCharsAtom
- Vector textRuns = new Vector();
+ final List<TextRun> textRuns = new LinkedList<TextRun>();
if(_atomSet != null) {
findTextRuns(_atomSet.getSlideRecords(),textRuns);
} else {
_runs = tmp;
}
}
+
+ /** This will return an atom per TextBox, so if the page has two text boxes the method should return two atoms. */
+ public StyleTextProp9Atom[] getNumberedListInfo() {
+ return this.getPPDrawing().getNumberedListInfo();
+ }
+
+ public EscherTextboxWrapper[] getTextboxWrappers() {
+ return this.getPPDrawing().getTextboxWrappers();
+ }
}
* (there can be misc InteractiveInfo, TxInteractiveInfo and other records)
*/
protected Record[] _records;
+ private StyleTextPropAtom styleTextPropAtom;
+ private StyleTextProp9Atom styleTextProp9Atom;
/**
* Constructs a Text Run from a Unicode text block
public Record[] getRecords(){
return _records;
}
+ /** Numbered List info */
+ public void setStyleTextProp9Atom(final StyleTextProp9Atom styleTextProp9Atom) {
+ this.styleTextProp9Atom = styleTextProp9Atom;
+ }
+ /** Numbered List info */
+ public StyleTextProp9Atom getStyleTextProp9Atom() {
+ return this.styleTextProp9Atom;
+ }
+
+ /** Characters covered */
+ public StyleTextPropAtom getStyleTextPropAtom() {
+ return this._styleAtom;
+ }
}
--- /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.
+==================================================================== */
+
+/**
+ * A structure that specifies additional paragraph-level formatting
+ * such as Bullet Auto Number Scheme.
+ */
+package org.apache.poi.hslf.model.textproperties;
+
+import org.apache.poi.hslf.record.TextAutoNumberSchemeEnum;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * This structure store text autonumber scheme and start number.
+ * If a paragraph has an autonumber(fBulletHasAutoNumber = 0x0001) but start number and scheme are empty,
+ * this means the default values will be used: statNumber=1 and sheme=ANM_ArabicPeriod
+ * @see http://social.msdn.microsoft.com/Forums/mr-IN/os_binaryfile/thread/650888db-fabd-4b95-88dc-f0455f6e2d28
+ *
+ * @author Alex Nikiforov [mailto:anikif@gmail.com]
+ *
+ */
+public class TextPFException9 {
+ //private final byte mask1;
+ //private final byte mask2;
+ private final byte mask3;
+ private final byte mask4;
+ private final Short bulletBlipRef;
+ private final Short fBulletHasAutoNumber;
+ private final TextAutoNumberSchemeEnum autoNumberScheme;
+ private final static TextAutoNumberSchemeEnum DEFAULT_AUTONUMBER_SHEME = TextAutoNumberSchemeEnum.ANM_ArabicPeriod;
+ private final Short autoNumberStartNumber;
+ private final static Short DEFAULT_START_NUMBER = new Short((short)1);
+ private final int recordLength;
+ public TextPFException9(final byte[] source, final int startIndex) {
+ //this.mask1 = source[startIndex];
+ //this.mask2 = source[startIndex + 1];
+ this.mask3 = source[startIndex + 2];
+ this.mask4 = source[startIndex + 3];
+ int length = 4;
+ int index = startIndex + 4;
+ if (0 == (mask3 & (byte)0x80 )) {
+ this.bulletBlipRef = null;
+ } else {
+ this.bulletBlipRef = LittleEndian.getShort(source, index);
+ index +=2;
+ length = 6;
+ }
+ if (0 == (mask4 & 2)) {
+ this.fBulletHasAutoNumber = null;
+ } else {
+ this.fBulletHasAutoNumber = LittleEndian.getShort(source, index);
+ index +=2;
+ length +=2;
+ }
+ if (0 == (mask4 & 1)) {
+ this.autoNumberScheme = null;
+ this.autoNumberStartNumber = null;
+ } else {
+ this.autoNumberScheme = TextAutoNumberSchemeEnum.valueOf(LittleEndian.getShort(source, index));
+ index +=2;
+ this.autoNumberStartNumber = LittleEndian.getShort(source, index);
+ index +=2;
+ length +=4;
+ }
+ this.recordLength = length;
+ }
+ public Short getBulletBlipRef() {
+ return bulletBlipRef;
+ }
+ public Short getfBulletHasAutoNumber() {
+ return fBulletHasAutoNumber;
+ }
+ public TextAutoNumberSchemeEnum getAutoNumberScheme() {
+ if (null != this.autoNumberScheme) {
+ return this.autoNumberScheme;
+ }
+ if (null != this.fBulletHasAutoNumber && 1 == this.fBulletHasAutoNumber.shortValue()) {
+ return DEFAULT_AUTONUMBER_SHEME;
+ }
+ return null;
+ }
+ public Short getAutoNumberStartNumber() {
+ if (null != this.autoNumberStartNumber) {
+ return this.autoNumberStartNumber;
+ }
+ if (null != this.fBulletHasAutoNumber && 1 == this.fBulletHasAutoNumber.shortValue()) {
+ return DEFAULT_START_NUMBER;
+ }
+ return null;
+ }
+ public int getRecordLength() {
+ return recordLength;
+ }
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("Record length: ").append(this.recordLength).append(" bytes\n");
+ sb.append("bulletBlipRef: ").append(this.bulletBlipRef).append("\n");
+ sb.append("fBulletHasAutoNumber: ").append(this.fBulletHasAutoNumber).append("\n");
+ sb.append("autoNumberScheme: ").append(this.autoNumberScheme).append("\n");
+ sb.append("autoNumberStartNumber: ").append(this.autoNumberStartNumber).append("\n");
+ return sb.toString();
+ }
+}
--- /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 org.apache.poi.util.LittleEndian;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * If we come across a record we know has children of (potential)
+ * interest, but where the record itself is boring, but where other
+ * records may care about where this one lives, we create one
+ * of these. It allows us to get at the children, and track where on
+ * disk this is, but not much else.
+ * Anything done using this should quite quickly be transitioned to its
+ * own proper record class!
+ *
+ * @author Nick Burch
+ */
+
+public final class BinaryTagDataBlob extends PositionDependentRecordContainer
+{
+ private byte[] _header;
+ private long _type;
+
+ /**
+ * Create a new holder for a boring record with children, but with
+ * position dependent characteristics
+ */
+ protected BinaryTagDataBlob(byte[] source, int start, int len) {
+ // Just grab the header, not the whole contents
+ _header = new byte[8];
+ System.arraycopy(source,start,_header,0,8);
+ _type = LittleEndian.getUShort(_header,2);
+
+ // Find our children
+ _children = Record.findChildRecords(source,start+8,len-8);
+ }
+
+ /**
+ * Return the value we were given at creation
+ */
+ public long getRecordType() { return _type; }
+
+ /**
+ * Write the contents of the record back, so it can be written
+ * to disk
+ */
+ public void writeOut(OutputStream out) throws IOException {
+ writeOut(_header[0],_header[1],_type,_children,out);
+ }
+}
private EscherTextboxRecord _escherRecord;
private long _type;
private int shapeId;
+ private StyleTextPropAtom styleTextPropAtom;
+ private StyleTextProp9Atom styleTextProp9Atom;
/**
* Returns the underlying DDF Escher Record
// Find the child records in the escher data
byte[] data = _escherRecord.getData();
_children = Record.findChildRecords(data,0,data.length);
+ for (Record r : this._children) {
+ if (r instanceof StyleTextPropAtom) { this.styleTextPropAtom = (StyleTextPropAtom) r; }
+ }
}
/**
public void setShapeId(int id){
shapeId = id;
}
+
+ public StyleTextPropAtom getStyleTextPropAtom() {
+ return styleTextPropAtom;
+ }
+
+ public void setStyleTextProp9Atom(final StyleTextProp9Atom nineAtom) {
+ this.styleTextProp9Atom = nineAtom;
+ }
+ public StyleTextProp9Atom getStyleTextProp9Atom() {
+ return this.styleTextProp9Atom;
+ }
}
import java.io.IOException;
import java.io.OutputStream;
+import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.Iterator;
_type = LittleEndian.getUShort(_header,2);
// Get the contents for now
- byte[] contents = new byte[len];
+ final byte[] contents = new byte[len];
System.arraycopy(source,start,contents,0,len);
-
// Build up a tree of Escher records contained within
- DefaultEscherRecordFactory erf = new DefaultEscherRecordFactory();
- Vector escherChildren = new Vector();
- findEscherChildren(erf,contents,8,len-8,escherChildren);
-
- childRecords = new EscherRecord[escherChildren.size()];
- for(int i=0; i<childRecords.length; i++) {
- childRecords[i] = (EscherRecord)escherChildren.get(i);
+ final DefaultEscherRecordFactory erf = new DefaultEscherRecordFactory();
+ final Vector<EscherRecord> escherChildren = new Vector<EscherRecord>();
+ findEscherChildren(erf, contents, 8, len-8, escherChildren);
+ this.childRecords = (EscherRecord[]) escherChildren.toArray(new EscherRecord[escherChildren.size()]);
+
+ if (1 == this.childRecords.length && (short)0xf002 == this.childRecords[0].getRecordId() && this.childRecords[0] instanceof EscherContainerRecord) {
+ this.textboxWrappers = findInDgContainer((EscherContainerRecord) this.childRecords[0]);
+ } else {
+ // Find and EscherTextboxRecord's, and wrap them up
+ final Vector<EscherTextboxWrapper> textboxes = new Vector<EscherTextboxWrapper>();
+ findEscherTextboxRecord(childRecords, textboxes);
+ this.textboxWrappers = (EscherTextboxWrapper[]) textboxes.toArray(new EscherTextboxWrapper[textboxes.size()]);
}
-
- // Find and EscherTextboxRecord's, and wrap them up
- Vector textboxes = new Vector();
- findEscherTextboxRecord(childRecords, textboxes);
- textboxWrappers = new EscherTextboxWrapper[textboxes.size()];
- for(int i=0; i<textboxWrappers.length; i++) {
- textboxWrappers[i] = (EscherTextboxWrapper)textboxes.get(i);
+ }
+ private EscherTextboxWrapper[] findInDgContainer(final EscherContainerRecord escherContainerF002) {
+ final List<EscherTextboxWrapper> found = new LinkedList<EscherTextboxWrapper>();
+ final EscherContainerRecord SpgrContainer = findFirstEscherContainerRecordOfType((short)0xf003, escherContainerF002);
+ final EscherContainerRecord[] escherContainersF004 = findAllEscherContainerRecordOfType((short)0xf004, SpgrContainer);
+ for (EscherContainerRecord spContainer : escherContainersF004) {
+ StyleTextProp9Atom nineAtom = findInSpContainer(spContainer);
+ EscherSpRecord sp = null;
+ final EscherRecord escherContainerF00A = findFirstEscherRecordOfType((short)0xf00a, spContainer);
+ if (null != escherContainerF00A) {
+ if (escherContainerF00A instanceof EscherSpRecord) {
+ sp = (EscherSpRecord) escherContainerF00A;
+ }
+ }
+ final EscherRecord escherContainerF00D = findFirstEscherRecordOfType((short)0xf00d, spContainer);
+ if (null == escherContainerF00D) { continue; }
+ if (escherContainerF00D instanceof EscherTextboxRecord) {
+ EscherTextboxRecord tbr = (EscherTextboxRecord) escherContainerF00D;
+ EscherTextboxWrapper w = new EscherTextboxWrapper(tbr);
+ w.setStyleTextProp9Atom(nineAtom);
+ if (null != sp) {
+ w.setShapeId(sp.getShapeId());
+ }
+ found.add(w);
+ }
}
+ return (EscherTextboxWrapper[]) found.toArray(new EscherTextboxWrapper[found.size()]);
+ }
+ private StyleTextProp9Atom findInSpContainer(final EscherContainerRecord spContainer) {
+ final EscherContainerRecord escherContainerF011 = findFirstEscherContainerRecordOfType((short)0xf011, spContainer);
+ if (null == escherContainerF011) { return null; }
+ final EscherContainerRecord escherContainer1388 = findFirstEscherContainerRecordOfType((short)0x1388, escherContainerF011);
+ if (null == escherContainer1388) { return null; }
+ final EscherContainerRecord escherContainer138A = findFirstEscherContainerRecordOfType((short)0x138A, escherContainer1388);
+ if (null == escherContainer138A) { return null; }
+ int size = escherContainer138A.getChildRecords().size();
+ if (2 != size) { return null; }
+ final Record r0 = buildFromUnknownEscherRecord((UnknownEscherRecord) escherContainer138A.getChild(0));
+ final Record r1 = buildFromUnknownEscherRecord((UnknownEscherRecord) escherContainer138A.getChild(1));
+ if (!(r0 instanceof CString)) { return null; }
+ if (!("___PPT9".equals(((CString) r0).getText()))) { return null; };
+ if (!(r1 instanceof BinaryTagDataBlob )) { return null; }
+ final BinaryTagDataBlob blob = (BinaryTagDataBlob) r1;
+ if (1 != blob.getChildRecords().length) { return null; }
+ return (StyleTextProp9Atom) blob.findFirstOfType(0x0FACL);
}
-
/**
* Creates a new, empty, PPDrawing (typically for use with a new Slide
* or Notes)
/**
* Tree walking way of finding Escher Child Records
*/
- private void findEscherChildren(DefaultEscherRecordFactory erf, byte[] source, int startPos, int lenToGo, Vector found) {
+ private void findEscherChildren(DefaultEscherRecordFactory erf, byte[] source, int startPos, int lenToGo, Vector<EscherRecord> found) {
int escherBytes = LittleEndian.getInt( source, startPos + 4 ) + 8;
/**
* Sanity check. Always advance the cursor by the correct value.
*
- * getRecordSize() must return exatcly the same number of bytes that was written in fillFields.
+ * getRecordSize() must return exactly the same number of bytes that was written in fillFields.
* Sometimes it is not so, see an example in bug #44770. Most likely reason is that one of ddf records calculates wrong size.
*/
if(size != escherBytes){
/**
* Look for EscherTextboxRecords
*/
- private void findEscherTextboxRecord(EscherRecord[] toSearch, Vector found) {
+ private void findEscherTextboxRecord(EscherRecord[] toSearch, Vector<EscherTextboxWrapper> found) {
for(int i=0; i<toSearch.length; i++) {
if(toSearch[i] instanceof EscherTextboxRecord) {
EscherTextboxRecord tbr = (EscherTextboxRecord)toSearch[i];
}
return dg;
}
+
+ protected EscherContainerRecord findFirstEscherContainerRecordOfType(short type, EscherContainerRecord parent) {
+ if (null == parent) { return null; }
+ final List<EscherContainerRecord> children = parent.getChildContainers();
+ for (EscherContainerRecord child : children) {
+ if (type == child.getRecordId()) {
+ return child;
+ }
+ }
+ return null;
+ }
+ protected EscherRecord findFirstEscherRecordOfType(short type, EscherContainerRecord parent) {
+ if (null == parent) { return null; }
+ final List<EscherRecord> children = parent.getChildRecords();
+ for (EscherRecord child : children) {
+ if (type == child.getRecordId()) {
+ return child;
+ }
+ }
+ return null;
+ }
+ protected EscherContainerRecord[] findAllEscherContainerRecordOfType(short type, EscherContainerRecord parent) {
+ if (null == parent) { return new EscherContainerRecord[0]; }
+ final List<EscherContainerRecord> children = parent.getChildContainers();
+ final List<EscherContainerRecord> result = new LinkedList<EscherContainerRecord>();
+ for (EscherContainerRecord child : children) {
+ if (type == child.getRecordId()) {
+ result.add(child);
+ }
+ }
+ return (EscherContainerRecord[]) result.toArray(new EscherContainerRecord[result.size()]);
+ }
+ protected Record buildFromUnknownEscherRecord(UnknownEscherRecord unknown) {
+ byte[] bingo = unknown.getData();
+ byte[] restoredRecord = new byte[8 + bingo.length];
+ System.arraycopy(bingo, 0, restoredRecord, 8, bingo.length);
+ short recordVersion = unknown.getVersion();
+ short recordId = unknown.getRecordId();
+ int recordLength = unknown.getRecordSize();
+ LittleEndian.putShort(restoredRecord, 0, recordVersion);
+ LittleEndian.putShort(restoredRecord, 2, recordId);
+ LittleEndian.putInt(restoredRecord, 4, recordLength);
+ return Record.createRecordForType(recordId, restoredRecord, 0, restoredRecord.length);
+ }
+
+ public StyleTextProp9Atom[] getNumberedListInfo() {
+ final List<StyleTextProp9Atom> result = new LinkedList<StyleTextProp9Atom>();
+ EscherRecord[] escherRecords = this.getEscherRecords();
+ for (EscherRecord escherRecord : escherRecords) {
+ if (escherRecord instanceof EscherContainerRecord && (short)0xf002 == escherRecord.getRecordId()) {
+ EscherContainerRecord escherContainerF002 = (EscherContainerRecord) escherRecord;
+ final EscherContainerRecord escherContainerF003 = findFirstEscherContainerRecordOfType((short)0xf003, escherContainerF002);
+ final EscherContainerRecord[] escherContainersF004 = findAllEscherContainerRecordOfType((short)0xf004, escherContainerF003);
+ for (EscherContainerRecord containerF004 : escherContainersF004) {
+ final EscherContainerRecord escherContainerF011 = findFirstEscherContainerRecordOfType((short)0xf011, containerF004);
+ if (null == escherContainerF011) { continue; }
+ final EscherContainerRecord escherContainer1388 = findFirstEscherContainerRecordOfType((short)0x1388, escherContainerF011);
+ if (null == escherContainer1388) { continue; }
+ final EscherContainerRecord escherContainer138A = findFirstEscherContainerRecordOfType((short)0x138A, escherContainer1388);
+ if (null == escherContainer138A) { continue; }
+ int size = escherContainer138A.getChildRecords().size();
+ if (2 != size) { continue; }
+ final Record r0 = buildFromUnknownEscherRecord((UnknownEscherRecord) escherContainer138A.getChild(0));
+ final Record r1 = buildFromUnknownEscherRecord((UnknownEscherRecord) escherContainer138A.getChild(1));
+ if (!(r0 instanceof CString)) { continue; }
+ if (!("___PPT9".equals(((CString) r0).getText()))) { continue; };
+ if (!(r1 instanceof BinaryTagDataBlob )) { continue; }
+ final BinaryTagDataBlob blob = (BinaryTagDataBlob) r1;
+ if (1 != blob.getChildRecords().length) { continue; }
+ result.add((StyleTextProp9Atom) blob.findFirstOfType(0x0FACL));
+ }
+ }
+ }
+ return (StyleTextProp9Atom[]) result.toArray(new StyleTextProp9Atom[result.size()]);
+ }
}
public static final Type OutlineTextRefAtom = new Type(3998,OutlineTextRefAtom.class);
public static final Type TextHeaderAtom = new Type(3999,TextHeaderAtom.class);
public static final Type TextCharsAtom = new Type(4000,TextCharsAtom.class);
- public static final Type StyleTextPropAtom = new Type(4001,StyleTextPropAtom.class);
+ public static final Type StyleTextPropAtom = new Type(4001, StyleTextPropAtom.class);//0x0fa1 RT_StyleTextPropAtom
public static final Type BaseTextPropAtom = new Type(4002,null);
public static final Type TxMasterStyleAtom = new Type(4003,TxMasterStyleAtom.class);
public static final Type TxCFStyleAtom = new Type(4004,null);
public static final Type TxSIStyleAtom = new Type(4009,null);
public static final Type TextSpecInfoAtom = new Type(4010, TextSpecInfoAtom.class);
public static final Type DefaultRulerAtom = new Type(4011,null);
+ public static final Type StyleTextProp9Atom = new Type(4012, StyleTextProp9Atom.class); //0x0FAC RT_StyleTextProp9Atom
public static final Type FontEntityAtom = new Type(4023,FontEntityAtom.class);
public static final Type FontEmbeddedData = new Type(4024,null);
public static final Type CString = new Type(4026,CString.class);
public static final Type ProgTags = new Type(5000,DummyPositionSensitiveRecordWithChildren.class);
public static final Type ProgStringTag = new Type(5001,null);
public static final Type ProgBinaryTag = new Type(5002,DummyPositionSensitiveRecordWithChildren.class);
- public static final Type BinaryTagData = new Type(5003,DummyPositionSensitiveRecordWithChildren.class);
+ public static final Type BinaryTagData = new Type(5003, BinaryTagDataBlob.class);//0x138b RT_BinaryTagDataBlob
public static final Type PrpublicintOptions = new Type(6000,null);
public static final Type PersistPtrFullBlock = new Type(6001,PersistPtrHolder.class);
public static final Type PersistPtrIncrementalBlock = new Type(6002,PersistPtrHolder.class);
--- /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.LinkedList;
+import java.util.List;
+
+import org.apache.poi.hslf.model.textproperties.TextPFException9;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * The atom record that specifies additional text formatting.
+ *
+ * @author Alex Nikiforov [mailto:anikif@gmail.com]
+ */
+public final class StyleTextProp9Atom extends RecordAtom {
+ private final TextPFException9[] autoNumberSchemes;
+ /** Record header. */
+ private byte[] header;
+ /** Record data. */
+ private byte[] data;
+ private short version;
+ private short recordId;
+ private int length;
+
+ /**
+ * Constructs the link related 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 StyleTextProp9Atom(byte[] source, int start, int len) {
+ // Get the header.
+ final List<TextPFException9> schemes = new LinkedList<TextPFException9>();
+ header = new byte[8];
+ System.arraycopy(source,start, header,0,8);
+ this.version = LittleEndian.getShort(header, 0);
+ this.recordId = LittleEndian.getShort(header, 2);
+ this.length = LittleEndian.getInt(header, 4);
+
+ // Get the record data.
+ data = new byte[len-8];
+ System.arraycopy(source, start+8, data, 0, len-8);
+ for (int i = 0; i < data.length; ) {
+ final TextPFException9 item = new TextPFException9(data, i);
+ schemes.add(item);
+ i += item.getRecordLength();
+ //int textCfException9 = LittleEndian.getInt(data, i );
+ //TODO analyze textCfException when have some test data
+ i += 4;
+ int textSiException = LittleEndian.getInt(data, i );
+ i += + 4;//TextCFException9 + SIException
+ if (0 != (textSiException & 0x40)) {
+ i += 2; //skip fBidi
+ }
+ if (i >= data.length) {
+ break;
+ }
+ }
+ this.autoNumberSchemes = (TextPFException9[]) schemes.toArray(new TextPFException9[schemes.size()]);
+ }
+
+ /**
+ * Gets the record type.
+ * @return the record type.
+ */
+ public long getRecordType() { return this.recordId; }
+
+ public short getVersion() {
+ return version;
+ }
+
+ public int getLength() {
+ return length;
+ }
+ public TextPFException9[] getAutoNumberTypes() {
+ return this.autoNumberSchemes;
+ }
+
+ /**
+ * Write the contents of the record back, so it can be written
+ * to disk
+ *
+ * @param out the output stream to write to.
+ * @throws java.io.IOException if an error occurs.
+ */
+ public void writeOut(OutputStream out) throws IOException {
+ out.write(header);
+ out.write(data);
+ }
+
+ /**
+ * Update the text length
+ *
+ * @param size the text length
+ */
+ public void setTextSize(int size){
+ LittleEndian.putInt(data, 0, size);
+ }
+
+ /**
+ * Reset the content to one info run with the default values
+ * @param size the site of parent text
+ */
+ public void reset(int size){
+ data = new byte[10];
+ // 01 00 00 00
+ LittleEndian.putInt(data, 0, size);
+ // 01 00 00 00
+ LittleEndian.putInt(data, 4, 1); //mask
+ // 00 00
+ LittleEndian.putShort(data, 8, (short)0); //langId
+
+ // Update the size (header bytes 5-8)
+ LittleEndian.putInt(header, 4, data.length);
+ }
+}
--- /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;
+
+public enum TextAutoNumberSchemeEnum {
+ //Name Value Meaning
+ ANM_AlphaLcPeriod ((short) 0x0000), // "Lowercase Latin character followed by a period. Example: a., b., c., ..."),
+ ANM_AlphaUcPeriod ((short) 0x0001), // "Uppercase Latin character followed by a period. Example: A., B., C., ..."),
+ ANM_ArabicParenRight ((short) 0x0002), // "Arabic numeral followed by a closing parenthesis. Example: 1), 2), 3), ..."),
+ ANM_ArabicPeriod ((short) 0x0003), // "Arabic numeral followed by a period. Example: 1., 2., 3., ..."),
+ ANM_RomanLcParenBoth ((short) 0x0004), // "Lowercase Roman numeral enclosed in parentheses. Example: (i), (ii), (iii), ..."),
+ ANM_RomanLcParenRight ((short) 0x0005), // "Lowercase Roman numeral followed by a closing parenthesis. Example: i), ii), iii), ..."),
+ ANM_RomanLcPeriod ((short) 0x0006), // "Lowercase Roman numeral followed by a period. Example: i., ii., iii., ..."),
+ ANM_RomanUcPeriod ((short) 0x0007), // "Uppercase Roman numeral followed by a period. Example: I., II., III., ..."),
+ ANM_AlphaLcParenBoth ((short) 0x0008), // "Lowercase alphabetic character enclosed in parentheses. Example: (a), (b), (c), ..."),
+ ANM_AlphaLcParenRight ((short) 0x0009), // "Lowercase alphabetic character followed by a closing parenthesis. Example: a), b), c), ..."),
+ ANM_AlphaUcParenBoth ((short) 0x000A), // "Uppercase alphabetic character enclosed in parentheses. Example: (A), (B), (C), ..."),
+ ANM_AlphaUcParenRight ((short) 0x000B), // "Uppercase alphabetic character followed by a closing parenthesis. Example: A), B), C), ..."),
+ ANM_ArabicParenBoth ((short) 0x000C), // "Arabic numeral enclosed in parentheses. Example: (1), (2), (3), ..."),
+ ANM_ArabicPlain ((short) 0x000D), // "Arabic numeral. Example: 1, 2, 3, ..."),
+ ANM_RomanUcParenBoth ((short) 0x000E), // "Uppercase Roman numeral enclosed in parentheses. Example: (I), (II), (III), ..."),
+ ANM_RomanUcParenRight ((short) 0x000F), // "Uppercase Roman numeral followed by a closing parenthesis. Example: I), II), III), ..."),
+ ANM_ChsPlain ((short) 0x0010), // "Simplified Chinese."),
+ ANM_ChsPeriod ((short) 0x0011), // "Simplified Chinese with single-byte period."),
+ ANM_CircleNumDBPlain ((short) 0x0012), // "Double byte circle numbers."),
+ ANM_CircleNumWDBWhitePlain ((short) 0x0013), // "Wingdings white circle numbers."),
+ ANM_CircleNumWDBBlackPlain ((short) 0x0014), // "Wingdings black circle numbers."),
+ ANM_ChtPlain ((short) 0x0015), // "Traditional Chinese."),
+ ANM_ChtPeriod ((short) 0x0016), // "Traditional Chinese with single-byte period."),
+ ANM_Arabic1Minus ((short) 0x0017), // "Bidi Arabic 1 (AraAlpha) with ANSI minus symbol."),
+ ANM_Arabic2Minus ((short) 0x0018), // "Bidi Arabic 2 (AraAbjad) with ANSI minus symbol."),
+ ANM_Hebrew2Minus ((short) 0x0019), // "Bidi Hebrew 2 with ANSI minus symbol."),
+ ANM_JpnKorPlain ((short) 0x001A), // "Japanese/Korean."),
+ ANM_JpnKorPeriod ((short) 0x001B), // "Japanese/Korean with single-byte period."),
+ ANM_ArabicDbPlain ((short) 0x001C), // "Double-byte Arabic numbers."),
+ ANM_ArabicDbPeriod ((short) 0x001D), // "Double-byte Arabic numbers with double-byte period."),
+ ANM_ThaiAlphaPeriod ((short) 0x001E), // "Thai alphabetic character followed by a period."),
+ ANM_ThaiAlphaParenRight ((short) 0x001F), // "Thai alphabetic character followed by a closing parenthesis."),
+ ANM_ThaiAlphaParenBoth ((short) 0x0020), // "Thai alphabetic character enclosed by parentheses."),
+ ANM_ThaiNumPeriod ((short) 0x0021), // "Thai numeral followed by a period."),
+ ANM_ThaiNumParenRight ((short) 0x0022), // "Thai numeral followed by a closing parenthesis."),
+ ANM_ThaiNumParenBoth ((short) 0x0023), // "Thai numeral enclosed in parentheses."),
+ ANM_HindiAlphaPeriod ((short) 0x0024), // "Hindi alphabetic character followed by a period."),
+ ANM_HindiNumPeriod ((short) 0x0025), // "Hindi numeric character followed by a period."),
+ ANM_JpnChsDBPeriod ((short) 0x0026), // "Japanese with double-byte period."),
+ ANM_HindiNumParenRight ((short) 0x0027), // "Hindi numeric character followed by a closing parenthesis."),
+ ANM_HindiAlpha1Period ((short) 0x0028); // "Hindi alphabetic character followed by a period.");
+
+ private final short value;
+ private TextAutoNumberSchemeEnum(final short code) {
+ this.value = code;
+ }
+ private short getValue() { return value; }
+ public String getDescription() {
+ return TextAutoNumberSchemeEnum.getDescription(this);
+ }
+ public static String getDescription(final TextAutoNumberSchemeEnum code) {
+ switch (code) {
+ case ANM_AlphaLcPeriod : return "Lowercase Latin character followed by a period. Example: a., b., c., ...";
+ case ANM_AlphaUcPeriod : return "Uppercase Latin character followed by a period. Example: A., B., C., ...";
+ case ANM_ArabicParenRight : return "Arabic numeral followed by a closing parenthesis. Example: 1), 2), 3), ...";
+ case ANM_ArabicPeriod : return "Arabic numeral followed by a period. Example: 1., 2., 3., ...";
+ case ANM_RomanLcParenBoth : return "Lowercase Roman numeral enclosed in parentheses. Example: (i), (ii), (iii), ...";
+ case ANM_RomanLcParenRight : return "Lowercase Roman numeral followed by a closing parenthesis. Example: i), ii), iii), ...";
+ case ANM_RomanLcPeriod : return "Lowercase Roman numeral followed by a period. Example: i., ii., iii., ...";
+ case ANM_RomanUcPeriod : return "Uppercase Roman numeral followed by a period. Example: I., II., III., ...";
+ case ANM_AlphaLcParenBoth : return "Lowercase alphabetic character enclosed in parentheses. Example: (a), (b), (c), ...";
+ case ANM_AlphaLcParenRight : return "Lowercase alphabetic character followed by a closing parenthesis. Example: a), b), c), ...";
+ case ANM_AlphaUcParenBoth : return "Uppercase alphabetic character enclosed in parentheses. Example: (A), (B), (C), ...";
+ case ANM_AlphaUcParenRight : return "Uppercase alphabetic character followed by a closing parenthesis. Example: A), B), C), ...";
+ case ANM_ArabicParenBoth : return "Arabic numeral enclosed in parentheses. Example: (1), (2), (3), ...";
+ case ANM_ArabicPlain : return "Arabic numeral. Example: 1, 2, 3, ...";
+ case ANM_RomanUcParenBoth : return "Uppercase Roman numeral enclosed in parentheses. Example: (I), (II), (III), ...";
+ case ANM_RomanUcParenRight : return "Uppercase Roman numeral followed by a closing parenthesis. Example: I), II), III), ...";
+ case ANM_ChsPlain : return "Simplified Chinese.";
+ case ANM_ChsPeriod : return "Simplified Chinese with single-byte period.";
+ case ANM_CircleNumDBPlain : return "Double byte circle numbers.";
+ case ANM_CircleNumWDBWhitePlain : return "Wingdings white circle numbers.";
+ case ANM_CircleNumWDBBlackPlain : return "Wingdings black circle numbers.";
+ case ANM_ChtPlain : return "Traditional Chinese.";
+ case ANM_ChtPeriod : return "Traditional Chinese with single-byte period.";
+ case ANM_Arabic1Minus : return "Bidi Arabic 1 (AraAlpha) with ANSI minus symbol.";
+ case ANM_Arabic2Minus : return "Bidi Arabic 2 (AraAbjad) with ANSI minus symbol.";
+ case ANM_Hebrew2Minus : return "Bidi Hebrew 2 with ANSI minus symbol.";
+ case ANM_JpnKorPlain : return "Japanese/Korean.";
+ case ANM_JpnKorPeriod : return "Japanese/Korean with single-byte period.";
+ case ANM_ArabicDbPlain : return "Double-byte Arabic numbers.";
+ case ANM_ArabicDbPeriod : return "Double-byte Arabic numbers with double-byte period.";
+ case ANM_ThaiAlphaPeriod : return "Thai alphabetic character followed by a period.";
+ case ANM_ThaiAlphaParenRight : return "Thai alphabetic character followed by a closing parenthesis.";
+ case ANM_ThaiAlphaParenBoth : return "Thai alphabetic character enclosed by parentheses.";
+ case ANM_ThaiNumPeriod : return "Thai numeral followed by a period.";
+ case ANM_ThaiNumParenRight : return "Thai numeral followed by a closing parenthesis.";
+ case ANM_ThaiNumParenBoth : return "Thai numeral enclosed in parentheses.";
+ case ANM_HindiAlphaPeriod : return "Hindi alphabetic character followed by a period.";
+ case ANM_HindiNumPeriod : return "Hindi numeric character followed by a period.";
+ case ANM_JpnChsDBPeriod : return "Japanese with double-byte period.";
+ case ANM_HindiNumParenRight : return "Hindi numeric character followed by a closing parenthesis.";
+ case ANM_HindiAlpha1Period : return "Hindi alphabetic character followed by a period.";
+ default : return "Unknown Numbered Scheme";
+ }
+ }
+ public static TextAutoNumberSchemeEnum valueOf(short autoNumberScheme) {
+ for (TextAutoNumberSchemeEnum item: TextAutoNumberSchemeEnum.values()) {
+ if (autoNumberScheme == item.getValue()) {
+ return item;
+ }
+ }
+ return null;
+ }
+}
--- /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.usermodel;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hslf.model.Slide;
+import org.apache.poi.hslf.model.TextRun;
+import org.apache.poi.hslf.model.textproperties.TextPFException9;
+import org.apache.poi.hslf.model.textproperties.TextPropCollection;
+import org.apache.poi.hslf.record.EscherTextboxWrapper;
+import org.apache.poi.hslf.record.StyleTextProp9Atom;
+import org.apache.poi.hslf.record.StyleTextPropAtom;
+import org.apache.poi.hslf.record.TextAutoNumberSchemeEnum;
+import org.apache.poi.POIDataSamples;
+
+
+/**
+ * Test that checks numbered list functionality.
+ *
+ * @author Alex Nikiforov [mailto:anikif@gmail.com]
+ */
+public final class TestNumberedList extends TestCase {
+ private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance();
+
+ protected void setUp() throws Exception {
+ }
+
+ public void testNumberedList() throws Exception {
+ SlideShow ppt = new SlideShow(_slTests.openResourceAsStream("numbers.ppt"));
+ assertTrue("No Exceptions while reading file", true);
+
+ final Slide[] slides = ppt.getSlides();
+ assertEquals(2, slides.length);
+ checkSlide0(slides[0]);
+ checkSlide1(slides[1]);
+ }
+ private void checkSlide0(final Slide s) {
+ final StyleTextProp9Atom[] numberedListArray = s.getNumberedListInfo();
+ assertNotNull(numberedListArray);
+ assertEquals(1, numberedListArray.length);//Just one text box here
+ final StyleTextProp9Atom numberedListInfo = numberedListArray[0];
+ assertNotNull(numberedListInfo);
+ final TextPFException9[] autoNumbers = numberedListInfo.getAutoNumberTypes();
+ assertNotNull(autoNumbers);
+ assertEquals(4, autoNumbers.length);
+ assertTrue(4 == autoNumbers[0].getAutoNumberStartNumber());
+ assertNull(autoNumbers[1].getAutoNumberStartNumber());
+ assertTrue(3 == autoNumbers[2].getAutoNumberStartNumber());
+ assertTrue(TextAutoNumberSchemeEnum.ANM_ArabicPeriod == autoNumbers[0].getAutoNumberScheme());
+ assertNull(autoNumbers[1].getAutoNumberScheme());
+ assertTrue(TextAutoNumberSchemeEnum.ANM_AlphaLcParenRight == autoNumbers[2].getAutoNumberScheme());
+
+ TextRun[] textRuns = s.getTextRuns();
+ assertEquals(2, textRuns.length);
+
+ RichTextRun textRun = textRuns[0].getRichTextRuns()[0];
+ assertEquals("titTe", textRun.getRawText());
+ assertEquals(1, textRuns[0].getRichTextRuns().length);
+ assertFalse(textRun.isBullet());
+
+ assertEquals("This is a text placeholder that \rfollows the design pattern\rJust a test\rWithout any paragraph\rSecond paragraph first line c) ;\rSecond paragraph second line d) . \r", textRuns[1].getRawText());
+
+ final EscherTextboxWrapper[] styleAtoms = s.getTextboxWrappers();
+ assertEquals(textRuns.length, styleAtoms.length);
+ final EscherTextboxWrapper wrapper = styleAtoms[1];
+ final StyleTextPropAtom styleTextPropAtom = wrapper.getStyleTextPropAtom();
+ final List<TextPropCollection> textProps = styleTextPropAtom.getCharacterStyles();
+ final TextPropCollection[] props = (TextPropCollection[]) textProps.toArray(new TextPropCollection[textProps.size()]);
+ assertEquals(60, props[0].getCharactersCovered());
+ assertEquals(34, props[1].getCharactersCovered());
+ assertEquals(68, props[2].getCharactersCovered());
+ }
+ private void checkSlide1(final Slide s) {
+ final StyleTextProp9Atom[] numberedListArray = s.getNumberedListInfo();
+ assertNotNull(numberedListArray);
+ assertEquals(1, numberedListArray.length);//Just one text box here
+ final StyleTextProp9Atom numberedListInfo = numberedListArray[0];
+ assertNotNull(numberedListInfo);
+ final TextPFException9[] autoNumbers = numberedListInfo.getAutoNumberTypes();
+ assertNotNull(autoNumbers);
+ assertEquals(4, autoNumbers.length);
+ assertTrue(9 == autoNumbers[0].getAutoNumberStartNumber());
+ assertNull(autoNumbers[1].getAutoNumberStartNumber());
+ assertTrue(3 == autoNumbers[2].getAutoNumberStartNumber());
+ assertTrue(TextAutoNumberSchemeEnum.ANM_ArabicParenRight == autoNumbers[0].getAutoNumberScheme());
+ assertNull(autoNumbers[1].getAutoNumberScheme());
+ assertTrue(TextAutoNumberSchemeEnum.ANM_AlphaUcPeriod == autoNumbers[2].getAutoNumberScheme());
+
+ final TextRun[] textRuns = s.getTextRuns();
+ assertEquals(2, textRuns.length);
+
+ RichTextRun textRun = textRuns[0].getRichTextRuns()[0];
+ assertEquals("Second Slide Title", textRun.getRawText());
+ assertEquals(1, textRuns[0].getRichTextRuns().length);
+ assertFalse(textRun.isBullet());
+
+ assertEquals("This is a text placeholder that \rfollows the design pattern\rJust a test\rWithout any paragraph\rSecond paragraph first line c) ;\rSecond paragraph second line d) . \r", textRuns[1].getRawText());
+
+ final EscherTextboxWrapper[] styleAtoms = s.getTextboxWrappers();
+ assertEquals(textRuns.length, styleAtoms.length);
+ final EscherTextboxWrapper wrapper = styleAtoms[1];
+ final StyleTextPropAtom styleTextPropAtom = wrapper.getStyleTextPropAtom();
+ final List<TextPropCollection> textProps = styleTextPropAtom.getCharacterStyles();
+
+ final TextPropCollection[] props = (TextPropCollection[]) textProps.toArray(new TextPropCollection[textProps.size()]);
+ assertEquals(33, props[0].getCharactersCovered());
+ assertEquals(61, props[1].getCharactersCovered());
+ assertEquals(68, props[2].getCharactersCovered());
+ }
+}
--- /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.usermodel;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hslf.model.Slide;
+import org.apache.poi.hslf.model.TextRun;
+import org.apache.poi.hslf.model.textproperties.TextPFException9;
+import org.apache.poi.hslf.model.textproperties.TextPropCollection;
+import org.apache.poi.hslf.record.EscherTextboxWrapper;
+import org.apache.poi.hslf.record.StyleTextProp9Atom;
+import org.apache.poi.hslf.record.StyleTextPropAtom;
+import org.apache.poi.hslf.record.TextAutoNumberSchemeEnum;
+import org.apache.poi.POIDataSamples;
+
+
+/**
+ * Test that checks numbered list functionality.
+ * if a paragraph has autonumber ()
+ * @see <a href="http://social.msdn.microsoft.com/Forums/mr-IN/os_binaryfile/thread/650888db-fabd-4b95-88dc-f0455f6e2d28">
+ * PPT: Missing TextAutoNumberScheme structure providing the style of the number bullets</a>
+ *
+ * @author Alex Nikiforov [mailto:anikif@gmail.com]
+ */
+public final class TestNumberedList2 extends TestCase {
+ private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance();
+
+ protected void setUp() throws Exception {
+ }
+
+ public void testNumberedList() throws Exception {
+ SlideShow ppt = new SlideShow(_slTests.openResourceAsStream("numbers2.ppt"));
+ assertTrue("No Exceptions while reading file", true);
+
+ final Slide[] slides = ppt.getSlides();
+ assertEquals(2, slides.length);
+ checkSlide0(slides[0]);
+ checkSlide1(slides[1]);
+ }
+ private void checkSlide0(final Slide s) {
+ final StyleTextProp9Atom[] numberedListArray = s.getNumberedListInfo();
+ assertNotNull(numberedListArray);
+ assertEquals(2, numberedListArray.length);
+ final StyleTextProp9Atom numberedListInfoForTextBox0 = numberedListArray[0];
+ final StyleTextProp9Atom numberedListInfoForTextBox1 = numberedListArray[1];
+ assertNotNull(numberedListInfoForTextBox0);
+ assertNotNull(numberedListInfoForTextBox1);
+ final TextPFException9[] autoNumbersOfTextBox0 = numberedListInfoForTextBox0.getAutoNumberTypes();
+ assertEquals(Short.valueOf((short)1), autoNumbersOfTextBox0[0].getfBulletHasAutoNumber());
+ assertEquals(Short.valueOf((short)1), autoNumbersOfTextBox0[0].getAutoNumberStartNumber());//Default value = 1 will be used
+ assertTrue(TextAutoNumberSchemeEnum.ANM_ArabicPeriod == autoNumbersOfTextBox0[0].getAutoNumberScheme());
+ final TextPFException9[] autoNumbersOfTextBox1 = numberedListInfoForTextBox1.getAutoNumberTypes();
+ assertEquals(Short.valueOf((short)1), autoNumbersOfTextBox1[0].getfBulletHasAutoNumber());
+ assertEquals(Short.valueOf((short)6), autoNumbersOfTextBox1[0].getAutoNumberStartNumber());//Default value = 1 will be used
+ assertTrue(TextAutoNumberSchemeEnum.ANM_ArabicPeriod == autoNumbersOfTextBox1[0].getAutoNumberScheme());
+
+
+ TextRun[] textRuns = s.getTextRuns();
+ assertEquals(2, textRuns.length);
+
+ RichTextRun textRun = textRuns[0].getRichTextRuns()[0];
+ assertEquals("List Item One\rList Item Two\rList Item Three", textRun.getRawText());
+ assertEquals(1, textRuns[0].getRichTextRuns().length);
+ assertTrue(textRun.isBullet());
+
+ assertEquals("A numbered list may start at any number \rThis would be used as a continuation list on another page\rThis list should start with #6", textRuns[1].getRawText());
+
+ final EscherTextboxWrapper[] styleAtoms = s.getTextboxWrappers();
+ assertEquals(textRuns.length, styleAtoms.length);
+ checkSingleRunWrapper(44, styleAtoms[0]);
+ checkSingleRunWrapper(130, styleAtoms[1]);
+ }
+ private void checkSlide1(final Slide s) {
+ final StyleTextProp9Atom[] numberedListArray = s.getNumberedListInfo();
+ assertNotNull(numberedListArray);
+ assertEquals(1, numberedListArray.length);
+ final StyleTextProp9Atom numberedListInfoForTextBox = numberedListArray[0];
+ assertNotNull(numberedListInfoForTextBox);
+ final TextPFException9[] autoNumbersOfTextBox = numberedListInfoForTextBox.getAutoNumberTypes();
+ assertEquals(Short.valueOf((short)1), autoNumbersOfTextBox[0].getfBulletHasAutoNumber());
+ assertEquals(Short.valueOf((short)1), autoNumbersOfTextBox[0].getAutoNumberStartNumber());//Default value = 1 will be used
+ assertTrue(TextAutoNumberSchemeEnum.ANM_ArabicPeriod == autoNumbersOfTextBox[0].getAutoNumberScheme());
+
+ TextRun[] textRuns = s.getTextRuns();
+ assertEquals(3, textRuns.length);
+
+ RichTextRun textRun = textRuns[0].getRichTextRuns()[0];
+ assertEquals("Bulleted list\rMore bullets", textRun.getRawText());
+ assertEquals(1, textRuns[0].getRichTextRuns().length);
+ assertTrue(textRun.isBullet());
+
+ assertEquals("Numbered list between two bulleted lists\rSecond numbered list item", textRuns[1].getRawText());
+ assertEquals("Second bulleted list \u2013 should appear after numbered list\rMore bullets", textRuns[2].getRawText());
+
+ final EscherTextboxWrapper[] styleAtoms = s.getTextboxWrappers();
+ assertEquals(textRuns.length, styleAtoms.length);
+ checkSingleRunWrapper(27, styleAtoms[0]);
+ checkSingleRunWrapper(67, styleAtoms[1]);
+ checkSingleRunWrapper(70, styleAtoms[2]);
+ }
+ private void checkSingleRunWrapper(final int exceptedLength, final EscherTextboxWrapper wrapper) {
+ final StyleTextPropAtom styleTextPropAtom = wrapper.getStyleTextPropAtom();
+ final List<TextPropCollection> textProps = styleTextPropAtom.getCharacterStyles();
+ assertEquals(1, textProps.size());
+ final TextPropCollection[] props = (TextPropCollection[]) textProps.toArray(new TextPropCollection[textProps.size()]);
+ assertEquals(exceptedLength, props[0].getCharactersCovered());
+ }
+}
--- /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.usermodel;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.hslf.model.Slide;
+import org.apache.poi.hslf.model.TextRun;
+import org.apache.poi.hslf.model.textproperties.TextPFException9;
+import org.apache.poi.hslf.model.textproperties.TextPropCollection;
+import org.apache.poi.hslf.record.EscherTextboxWrapper;
+import org.apache.poi.hslf.record.StyleTextProp9Atom;
+import org.apache.poi.hslf.record.StyleTextPropAtom;
+import org.apache.poi.hslf.record.TextAutoNumberSchemeEnum;
+import org.apache.poi.POIDataSamples;
+
+
+/**
+ * Test that checks numbered list functionality.
+ * if a paragraph has autonumber ()
+ * @see <a href="http://social.msdn.microsoft.com/Forums/mr-IN/os_binaryfile/thread/650888db-fabd-4b95-88dc-f0455f6e2d28">
+ * PPT: Missing TextAutoNumberScheme structure providing the style of the number bullets</a>
+ *
+ * @author Alex Nikiforov [mailto:anikif@gmail.com]
+ */
+public final class TestNumberedList3 extends TestCase {
+ private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance();
+
+ protected void setUp() throws Exception {
+ }
+
+ public void testNumberedList() throws Exception {
+ SlideShow ppt = new SlideShow(_slTests.openResourceAsStream("numbers3.ppt"));
+ assertTrue("No Exceptions while reading file", true);
+
+ final Slide[] slides = ppt.getSlides();
+ assertEquals(1, slides.length);
+ final Slide slide = slides[0];
+ checkSlide(slide);
+ }
+ private void checkSlide(final Slide s) {
+ final StyleTextProp9Atom[] numberedListArray = s.getNumberedListInfo();
+ assertNotNull(numberedListArray);
+ assertEquals(1, numberedListArray.length);
+ final StyleTextProp9Atom numberedListInfoForTextBox = numberedListArray[0];
+ assertNotNull(numberedListInfoForTextBox);
+ final TextPFException9[] autoNumbersOfTextBox0 = numberedListInfoForTextBox.getAutoNumberTypes();
+ assertEquals(Short.valueOf((short)1), autoNumbersOfTextBox0[0].getfBulletHasAutoNumber());
+ assertEquals(Short.valueOf((short)1), autoNumbersOfTextBox0[0].getAutoNumberStartNumber());//Default value = 1 will be used
+ assertTrue(TextAutoNumberSchemeEnum.ANM_ArabicPeriod == autoNumbersOfTextBox0[0].getAutoNumberScheme());
+
+ final TextRun[] textRuns = s.getTextRuns();
+ assertEquals(3, textRuns.length);
+ assertEquals("Bulleted list\rMore bullets\rNo bullets here", textRuns[0].getRawText());
+ assertEquals("Numbered list between two bulleted lists\rSecond numbered list item", textRuns[1].getRawText());
+ assertEquals("Second bulleted list \u2013 should appear after numbered list\rMore bullets", textRuns[2].getRawText());
+ assertEquals(2, textRuns[0].getRichTextRuns().length);
+ assertEquals(1, textRuns[1].getRichTextRuns().length);
+ assertEquals(1, textRuns[2].getRichTextRuns().length);
+ assertNull(textRuns[0].getStyleTextProp9Atom());
+ assertNotNull(textRuns[1].getStyleTextProp9Atom());
+ assertNull(textRuns[2].getStyleTextProp9Atom());
+ final TextPFException9[] autoNumbers = textRuns[1].getStyleTextProp9Atom().getAutoNumberTypes();
+ assertEquals(1, autoNumbers.length);
+ assertEquals(Short.valueOf((short)1), autoNumbers[0].getfBulletHasAutoNumber());
+ assertEquals(Short.valueOf((short)1), autoNumbers[0].getAutoNumberStartNumber());//Default value = 1 will be used
+ assertTrue(TextAutoNumberSchemeEnum.ANM_ArabicPeriod == autoNumbersOfTextBox0[0].getAutoNumberScheme());
+
+ final List<TextPropCollection> textProps = textRuns[1].getStyleTextPropAtom().getCharacterStyles();
+ assertEquals(1, textProps.size());
+ final TextPropCollection textProp = textProps.get(0);
+ assertEquals(67, textProp.getCharactersCovered());
+
+
+ RichTextRun textRun = textRuns[0].getRichTextRuns()[0];
+ assertTrue(textRun.isBullet());
+
+
+ final EscherTextboxWrapper[] styleAtoms = s.getTextboxWrappers();
+ assertEquals(textRuns.length, styleAtoms.length);
+ checkSingleRunWrapper(43, styleAtoms[0]);
+ checkSingleRunWrapper(67, styleAtoms[1]);
+ }
+ private void checkSingleRunWrapper(final int exceptedLength, final EscherTextboxWrapper wrapper) {
+ final StyleTextPropAtom styleTextPropAtom = wrapper.getStyleTextPropAtom();
+ final List<TextPropCollection> textProps = styleTextPropAtom.getCharacterStyles();
+ assertEquals(1, textProps.size());
+ final TextPropCollection[] props = (TextPropCollection[]) textProps.toArray(new TextPropCollection[textProps.size()]);
+ assertEquals(exceptedLength, props[0].getCharactersCovered());
+ }
+}