git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1401652 13f79535-47bb-0310-9956-ffa450edef68tags/3.10-beta1
@@ -34,6 +34,7 @@ | |||
<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> |
@@ -122,13 +122,13 @@ public abstract class Sheet { | |||
*/ | |||
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); | |||
@@ -137,7 +137,6 @@ public abstract class Sheet { | |||
} | |||
return runsV.toArray(new TextRun[runsV.size()]); | |||
} | |||
/** | |||
* Scans through the supplied record array, looking for | |||
* a TextHeaderAtom followed by one of a TextBytesAtom or | |||
@@ -146,8 +145,30 @@ public abstract class Sheet { | |||
* @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; | |||
@@ -169,7 +190,7 @@ public abstract class Sheet { | |||
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 { | |||
@@ -177,7 +198,7 @@ public abstract class Sheet { | |||
} | |||
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]); | |||
@@ -186,7 +207,7 @@ public abstract class Sheet { | |||
lst.toArray(recs); | |||
trun._records = recs; | |||
trun.setIndex(slwtIndex); | |||
trun.setStyleTextProp9Atom(styleTextProp9Atom); | |||
found.add(trun); | |||
i++; | |||
} else { |
@@ -18,7 +18,8 @@ | |||
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; | |||
@@ -26,11 +27,13 @@ import org.apache.poi.ddf.EscherDggRecord; | |||
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; | |||
@@ -53,7 +56,7 @@ public final class Slide extends Sheet | |||
/** | |||
* 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 | |||
@@ -72,7 +75,7 @@ public final class Slide extends Sheet | |||
// 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 { | |||
@@ -476,4 +479,13 @@ public final class Slide extends Sheet | |||
_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(); | |||
} | |||
} |
@@ -54,6 +54,8 @@ public final class TextRun | |||
* (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 | |||
@@ -702,5 +704,18 @@ public final class TextRun | |||
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; | |||
} | |||
} |
@@ -0,0 +1,117 @@ | |||
/* ==================================================================== | |||
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(); | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
/* ==================================================================== | |||
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); | |||
} | |||
} |
@@ -36,6 +36,8 @@ public final class EscherTextboxWrapper extends RecordContainer { | |||
private EscherTextboxRecord _escherRecord; | |||
private long _type; | |||
private int shapeId; | |||
private StyleTextPropAtom styleTextPropAtom; | |||
private StyleTextProp9Atom styleTextProp9Atom; | |||
/** | |||
* Returns the underlying DDF Escher Record | |||
@@ -52,6 +54,9 @@ public final class EscherTextboxWrapper extends RecordContainer { | |||
// 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; } | |||
} | |||
} | |||
/** | |||
@@ -104,4 +109,15 @@ public final class EscherTextboxWrapper extends RecordContainer { | |||
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; | |||
} | |||
} |
@@ -25,6 +25,7 @@ import org.apache.poi.hslf.model.ShapeTypes; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Vector; | |||
import java.util.Iterator; | |||
@@ -78,29 +79,69 @@ public final class PPDrawing extends RecordAtom { | |||
_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) | |||
@@ -118,7 +159,7 @@ public final class PPDrawing extends RecordAtom { | |||
/** | |||
* 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; | |||
@@ -138,7 +179,7 @@ public final class PPDrawing extends RecordAtom { | |||
/** | |||
* 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){ | |||
@@ -155,7 +196,7 @@ public final class PPDrawing extends RecordAtom { | |||
/** | |||
* 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]; | |||
@@ -313,4 +354,79 @@ public final class PPDrawing extends RecordAtom { | |||
} | |||
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()]); | |||
} | |||
} |
@@ -84,7 +84,7 @@ public final class RecordTypes { | |||
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); | |||
@@ -95,6 +95,7 @@ public final class RecordTypes { | |||
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); | |||
@@ -146,7 +147,7 @@ public final class RecordTypes { | |||
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); |
@@ -0,0 +1,136 @@ | |||
/* ==================================================================== | |||
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); | |||
} | |||
} |
@@ -0,0 +1,128 @@ | |||
/* | |||
* ==================================================================== | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,130 @@ | |||
/* | |||
* ==================================================================== | |||
* 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()); | |||
} | |||
} |
@@ -0,0 +1,128 @@ | |||
/* | |||
* ==================================================================== | |||
* 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()); | |||
} | |||
} |
@@ -0,0 +1,110 @@ | |||
/* | |||
* ==================================================================== | |||
* 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()); | |||
} | |||
} |