A few text handling related fixes

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/common_sl@1680695 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2015-05-20 23:07:08 +00:00
parent 6520c37b7f
commit a1c2269e6f
7 changed files with 336 additions and 311 deletions

View File

@ -17,11 +17,11 @@
package org.apache.poi.hslf.model.textproperties;
import java.io.IOException;
import java.io.OutputStream;
import java.io.*;
import java.util.*;
import org.apache.poi.hslf.record.StyleTextPropAtom;
import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian;
/**
@ -33,8 +33,9 @@ import org.apache.poi.util.LittleEndian;
public class TextPropCollection {
private int charactersCovered;
private short reservedField;
private List<TextProp> textPropList;
private final List<TextProp> textPropList = new ArrayList<TextProp>();
private int maskSpecial = 0;
private final TextProp[] potentialPropList;
public int getSpecialMask() { return maskSpecial; }
@ -45,8 +46,7 @@ public class TextPropCollection {
/** Fetch the TextProp with this name, or null if it isn't present */
public TextProp findByName(String textPropName) {
for(int i=0; i<textPropList.size(); i++) {
TextProp prop = textPropList.get(i);
for(TextProp prop : textPropList) {
if(prop.getName().equals(textPropName)) {
return prop;
}
@ -57,32 +57,60 @@ public class TextPropCollection {
/** Add the TextProp with this name to the list */
public TextProp addWithName(String name) {
// Find the base TextProp to base on
TextProp existing = findByName(name);
if (existing != null) return existing;
TextProp base = null;
for(int i=0; i < StyleTextPropAtom.characterTextPropTypes.length; i++) {
if(StyleTextPropAtom.characterTextPropTypes[i].getName().equals(name)) {
base = StyleTextPropAtom.characterTextPropTypes[i];
}
}
for(int i=0; i < StyleTextPropAtom.paragraphTextPropTypes.length; i++) {
if(StyleTextPropAtom.paragraphTextPropTypes[i].getName().equals(name)) {
base = StyleTextPropAtom.paragraphTextPropTypes[i];
for (TextProp tp : potentialPropList) {
if (tp.getName().equals(name)) {
base = tp;
break;
}
}
if(base == null) {
throw new IllegalArgumentException("No TextProp with name " + name + " is defined to add from");
throw new IllegalArgumentException("No TextProp with name " + name + " is defined to add from. "
+ "Character and paragraphs have their own properties/names.");
}
// Add a copy of this property, in the right place to the list
TextProp textProp = base.clone();
addProp(textProp);
return textProp;
}
/**
* Add the property at the correct position. Replaces an existing property with the same name.
*
* @param textProp the property to be added
*/
public void addProp(TextProp textProp) {
assert(textProp != null);
int pos = 0;
for(int i=0; i<textPropList.size(); i++) {
TextProp curProp = textPropList.get(i);
if(textProp.getMask() > curProp.getMask()) {
boolean found = false;
for (TextProp curProp : potentialPropList) {
String potName = curProp.getName();
if (pos == textPropList.size() || potName.equals(textProp.getName())) {
if (textPropList.size() > pos && potName.equals(textPropList.get(pos).getName())) {
// replace existing prop (with same name)
textPropList.set(pos, textProp);
} else {
textPropList.add(pos, textProp);
}
found = true;
break;
}
if (potName.equals(textPropList.get(pos).getName())) {
pos++;
}
}
textPropList.add(pos, textProp);
return textProp;
if(!found) {
String err = "TextProp with name " + textProp.getName() + " doesn't belong to this collection.";
throw new IllegalArgumentException(err);
}
}
/**
@ -90,12 +118,12 @@ public class TextPropCollection {
* properties coded for in a given run of properties.
* @return the number of bytes that were used encoding the properties list
*/
public int buildTextPropList(int containsField, TextProp[] potentialProperties, byte[] data, int dataOffset) {
public int buildTextPropList(int containsField, byte[] data, int dataOffset) {
int bytesPassed = 0;
// For each possible entry, see if we match the mask
// If we do, decode that, save it, and shuffle on
for(TextProp tp : potentialProperties) {
for(TextProp tp : potentialPropList) {
// Check there's still data left to read
// Check if this property is found in the mask
@ -123,7 +151,7 @@ public class TextPropCollection {
}
prop.setValue(val);
bytesPassed += prop.getSize();
textPropList.add(prop);
addProp(prop);
}
}
@ -136,20 +164,18 @@ public class TextPropCollection {
* or character) which will be groked via a subsequent call to
* buildTextPropList().
*/
public TextPropCollection(int charactersCovered, short reservedField) {
public TextPropCollection(int charactersCovered, short reservedField, TextProp[] potentialPropList) {
this.charactersCovered = charactersCovered;
this.reservedField = reservedField;
textPropList = new ArrayList<TextProp>();
this.potentialPropList = potentialPropList;
}
/**
* Create a new collection of text properties (be they paragraph
* or character) for a run of text without any
*/
public TextPropCollection(int textSize) {
charactersCovered = textSize;
reservedField = -1;
textPropList = new ArrayList<TextProp>();
public TextPropCollection(int textSize, TextProp[] potentialPropList) {
this(textSize, (short)-1, potentialPropList);
}
/**
@ -158,12 +184,13 @@ public class TextPropCollection {
public void copy(TextPropCollection other) {
this.charactersCovered = other.charactersCovered;
this.reservedField = other.reservedField;
this.maskSpecial = other.maskSpecial;
this.textPropList.clear();
for (TextProp tp : other.textPropList) {
TextProp tpCopy = (tp instanceof BitMaskTextProp)
? ((BitMaskTextProp)tp).cloneAll()
: tp.clone();
this.textPropList.add(tpCopy);
addProp(tpCopy);
}
}
@ -178,7 +205,7 @@ public class TextPropCollection {
/**
* Writes out to disk the header, and then all the properties
*/
public void writeOut(OutputStream o, TextProp[] potentialProperties) throws IOException {
public void writeOut(OutputStream o) throws IOException {
// First goes the number of characters we affect
StyleTextPropAtom.writeLittleEndian(charactersCovered,o);
@ -201,12 +228,17 @@ public class TextPropCollection {
StyleTextPropAtom.writeLittleEndian(mask,o);
// Then the contents of all the properties
for (TextProp potProp : potentialProperties) {
for (TextProp potProp : potentialPropList) {
for(TextProp textProp : textPropList) {
if (!textProp.getName().equals(potProp.getName())) continue;
int val = textProp.getValue();
if (textProp instanceof BitMaskTextProp && val == 0) {
if (textProp instanceof BitMaskTextProp && val == 0
&& !(textProp instanceof ParagraphFlagsTextProp)
// && !(textProp instanceof CharFlagsTextProp)
) {
// don't add empty properties, as they can't be recognized while reading
// strangely this doesn't apply for ParagraphFlagsTextProp in contrast
// to the documentation in 2.9.20 TextPFException
continue;
} else if (textProp.getSize() == 2) {
StyleTextPropAtom.writeLittleEndian((short)val,o);
@ -264,4 +296,26 @@ public class TextPropCollection {
return true;
}
public String toString() {
StringBuilder out = new StringBuilder();
out.append(" chars covered: " + getCharactersCovered());
out.append(" special mask flags: 0x" + HexDump.toHex(getSpecialMask()) + "\n");
for(TextProp p : getTextPropList()) {
out.append(" " + p.getName() + " = " + p.getValue() );
out.append(" (0x" + HexDump.toHex(p.getValue()) + ")\n");
}
out.append(" bytes that would be written: \n");
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeOut(baos);
byte[] b = baos.toByteArray();
out.append(HexDump.dump(b, 0, 0));
} catch (Exception e ) {
e.printStackTrace();
}
return out.toString();
}
}

View File

@ -218,15 +218,23 @@ public final class StyleTextPropAtom extends RecordAtom
charStyles = new ArrayList<TextPropCollection>();
TextPropCollection defaultParagraphTextProps =
new TextPropCollection(parentTextSize, (short)0);
new TextPropCollection(parentTextSize, (short)0, paragraphTextPropTypes);
defaultParagraphTextProps.addWithName("paragraph_flags");
paragraphStyles.add(defaultParagraphTextProps);
TextPropCollection defaultCharacterTextProps =
new TextPropCollection(parentTextSize);
new TextPropCollection(parentTextSize, characterTextPropTypes);
defaultCharacterTextProps.addWithName("char_flags");
charStyles.add(defaultCharacterTextProps);
// Set us as now initialised
initialised = true;
try {
updateRawContents();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@ -245,10 +253,6 @@ public final class StyleTextPropAtom extends RecordAtom
// on the properties
updateRawContents();
// Now ensure that the header size is correct
int newSize = rawContents.length + reserved.length;
LittleEndian.putInt(_header,4,newSize);
// Write out the (new) header
out.write(_header);
@ -265,9 +269,14 @@ public final class StyleTextPropAtom extends RecordAtom
* contains, so we can go ahead and initialise ourselves.
*/
public void setParentTextSize(int size) {
// if (initialised) return;
int pos = 0;
int textHandled = 0;
paragraphStyles.clear();
charStyles.clear();
// While we have text in need of paragraph stylings, go ahead and
// grok the contents as paragraph formatting data
int prsize = size;
@ -286,9 +295,8 @@ public final class StyleTextPropAtom extends RecordAtom
pos += 4;
// Now make sense of those properties
TextPropCollection thisCollection = new TextPropCollection(textLen, indent);
int plSize = thisCollection.buildTextPropList(
paraFlags, paragraphTextPropTypes, rawContents, pos);
TextPropCollection thisCollection = new TextPropCollection(textLen, indent, paragraphTextPropTypes);
int plSize = thisCollection.buildTextPropList(paraFlags, rawContents, pos);
pos += plSize;
// Save this properties set
@ -323,9 +331,8 @@ public final class StyleTextPropAtom extends RecordAtom
// Now make sense of those properties
// (Assuming we actually have some)
TextPropCollection thisCollection = new TextPropCollection(textLen, no_val);
int chSize = thisCollection.buildTextPropList(
charFlags, characterTextPropTypes, rawContents, pos);
TextPropCollection thisCollection = new TextPropCollection(textLen, no_val, characterTextPropTypes);
int chSize = thisCollection.buildTextPropList(charFlags, rawContents, pos);
pos += chSize;
// Save this properties set
@ -363,33 +370,32 @@ public final class StyleTextPropAtom extends RecordAtom
* Updates the cache of the raw contents. Serialised the styles out.
*/
private void updateRawContents() throws IOException {
if(!initialised) {
// We haven't groked the styles since creation, so just stick
// with what we found
return;
}
if (initialised) {
// Only update the style bytes, if the styles have been potentially
// changed
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// First up, we need to serialise the paragraph properties
for(int i=0; i<paragraphStyles.size(); i++) {
TextPropCollection tpc = paragraphStyles.get(i);
tpc.writeOut(baos, paragraphTextPropTypes);
for(TextPropCollection tpc : paragraphStyles) {
// ensure, that the paragraphs flags exist, no matter if anthing is set
tpc.addWithName(ParagraphFlagsTextProp.NAME);
tpc.writeOut(baos);
}
// Now, we do the character ones
for(int i=0; i<charStyles.size(); i++) {
TextPropCollection tpc = charStyles.get(i);
tpc.writeOut(baos, characterTextPropTypes);
for(TextPropCollection tpc : charStyles) {
// ditto for the char flags
tpc.addWithName(CharFlagsTextProp.NAME);
tpc.writeOut(baos);
}
rawContents = baos.toByteArray();
}
public void setRawContents(byte[] bytes) {
rawContents = bytes;
reserved = new byte[0];
initialised = false;
// Now ensure that the header size is correct
int newSize = rawContents.length + reserved.length;
LittleEndian.putInt(_header,4,newSize);
}
/**
@ -398,6 +404,8 @@ public final class StyleTextPropAtom extends RecordAtom
public void clearStyles() {
paragraphStyles.clear();
charStyles.clear();
reserved = new byte[0];
initialised = true;
}
/**
@ -406,7 +414,7 @@ public final class StyleTextPropAtom extends RecordAtom
* @return the new TextPropCollection, which will then be in the list
*/
public TextPropCollection addParagraphTextPropCollection(int charactersCovered) {
TextPropCollection tpc = new TextPropCollection(charactersCovered, (short)0);
TextPropCollection tpc = new TextPropCollection(charactersCovered, (short)0, paragraphTextPropTypes);
paragraphStyles.add(tpc);
return tpc;
}
@ -416,7 +424,7 @@ public final class StyleTextPropAtom extends RecordAtom
* @return the new TextPropCollection, which will then be in the list
*/
public TextPropCollection addCharacterTextPropCollection(int charactersCovered) {
TextPropCollection tpc = new TextPropCollection(charactersCovered);
TextPropCollection tpc = new TextPropCollection(charactersCovered, characterTextPropTypes);
charStyles.add(tpc);
return tpc;
}
@ -438,51 +446,25 @@ public final class StyleTextPropAtom extends RecordAtom
} else {
out.append("Paragraph properties\n");
for(TextPropCollection pr : getParagraphStyles()) {
out.append(" chars covered: " + pr.getCharactersCovered());
out.append(" special mask flags: 0x" + HexDump.toHex(pr.getSpecialMask()) + "\n");
for(TextProp p : pr.getTextPropList()) {
out.append(" " + p.getName() + " = " + p.getValue() );
out.append(" (0x" + HexDump.toHex(p.getValue()) + ")\n");
}
out.append(" para bytes that would be written: \n");
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
pr.writeOut(baos, paragraphTextPropTypes);
byte[] b = baos.toByteArray();
out.append(HexDump.dump(b, 0, 0));
} catch (Exception e ) {
e.printStackTrace();
}
out.append(pr);
}
out.append("Character properties\n");
for(TextPropCollection pr : getCharacterStyles()) {
out.append(" chars covered: " + pr.getCharactersCovered() );
out.append(" special mask flags: 0x" + HexDump.toHex(pr.getSpecialMask()) + "\n");
for(TextProp p : pr.getTextPropList()) {
out.append(" " + p.getName() + " = " + p.getValue() );
out.append(" (0x" + HexDump.toHex(p.getValue()) + ")\n");
out.append(pr);
}
out.append(" char bytes that would be written: \n");
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
pr.writeOut(baos, characterTextPropTypes);
byte[] b = baos.toByteArray();
out.append(HexDump.dump(b, 0, 0));
} catch (Exception e ) {
e.printStackTrace();
}
}
out.append("Reserved bytes\n");
out.append( HexDump.dump(reserved, 0, 0) );
}
out.append(" original byte stream \n");
out.append( HexDump.dump(rawContents, 0, 0) );
byte buf[] = new byte[rawContents.length+reserved.length];
System.arraycopy(rawContents, 0, buf, 0, rawContents.length);
System.arraycopy(reserved, 0, buf, rawContents.length, reserved.length);
out.append( HexDump.dump(buf, 0, 0) );
return out.toString();
}

View File

@ -182,14 +182,14 @@ public final class TxMasterStyleAtom extends RecordAtom {
head = LittleEndian.getInt(_data, pos);
pos += LittleEndian.INT_SIZE;
TextPropCollection prprops = new TextPropCollection(0);
pos += prprops.buildTextPropList( head, getParagraphProps(type, j), _data, pos);
TextPropCollection prprops = new TextPropCollection(0, getParagraphProps(type, j));
pos += prprops.buildTextPropList( head, _data, pos);
prstyles[j] = prprops;
head = LittleEndian.getInt(_data, pos);
pos += LittleEndian.INT_SIZE;
TextPropCollection chprops = new TextPropCollection(0);
pos += chprops.buildTextPropList( head, getCharacterProps(type, j), _data, pos);
TextPropCollection chprops = new TextPropCollection(0, getCharacterProps(type, j));
pos += chprops.buildTextPropList( head, _data, pos);
chstyles[j] = chprops;
}

View File

@ -18,6 +18,8 @@
package org.apache.poi.hslf.usermodel;
import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import org.apache.poi.hslf.model.PPFont;
@ -49,10 +51,10 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
// Note: These fields are protected to help with unit testing
// Other classes shouldn't really go playing with them!
private final TextHeaderAtom _headerAtom;
private final TextBytesAtom _byteAtom;
private final TextCharsAtom _charAtom;
private TextBytesAtom _byteAtom;
private TextCharsAtom _charAtom;
private StyleTextPropAtom _styleAtom;
private TextPropCollection _paragraphStyle = new TextPropCollection(1);
private TextPropCollection _paragraphStyle = new TextPropCollection(1, StyleTextPropAtom.paragraphTextPropTypes);
protected TextRulerAtom _ruler;
protected List<HSLFTextRun> _runs = new ArrayList<HSLFTextRun>();
@ -258,8 +260,8 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
public void setParaTextPropVal(String propName, int val) {
// Ensure we have the StyleTextProp atom we're going to need
if(_paragraphStyle == null) {
ensureStyleAtomPresent();
// paragraphStyle will now be defined
_styleAtom = findStyleAtomPresent(_headerAtom, -1);
_paragraphStyle = _styleAtom.getParagraphStyles().get(0);
}
assert(_paragraphStyle!=null);
@ -267,48 +269,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
tp.setValue(val);
}
/**
* Ensure a StyleTextPropAtom is present for this run,
* by adding if required. Normally for internal TextRun use.
*/
protected StyleTextPropAtom ensureStyleAtomPresent() {
if (_styleAtom != null) {
return _styleAtom;
}
_styleAtom = ensureStyleAtomPresent(_headerAtom, _byteAtom, _charAtom);
_paragraphStyle = _styleAtom.getParagraphStyles().get(0);
return _styleAtom;
}
protected static StyleTextPropAtom ensureStyleAtomPresent(TextHeaderAtom header, TextBytesAtom tbytes, TextCharsAtom tchars) {
RecordContainer wrapper = header.getParentRecord();
StyleTextPropAtom styleAtom = null;
boolean afterHeader = false;
for (Record record : wrapper.getChildRecords()) {
if (afterHeader && record.getRecordType() == RecordTypes.TextHeaderAtom.typeID) break;
afterHeader |= (header == record);
if (afterHeader && record.getRecordType() == RecordTypes.StyleTextPropAtom.typeID) {
styleAtom = (StyleTextPropAtom)record;
break;
}
}
if (styleAtom != null) return styleAtom;
String rawText = (tchars != null) ? tchars.getText() : tbytes.getText();
// Create a new one at the right size
styleAtom = new StyleTextPropAtom(rawText.length()+1);
// Add the new StyleTextPropAtom after the TextCharsAtom / TextBytesAtom
wrapper.addChildAfter(styleAtom, (tbytes == null ? tchars : tbytes));
return styleAtom;
}
@Override
public Iterator<HSLFTextRun> iterator() {
return _runs.iterator();
@ -661,7 +621,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
protected void setFlag(int index, boolean value) {
// Ensure we have the StyleTextProp atom we're going to need
if(_paragraphStyle == null) {
_paragraphStyle = new TextPropCollection(1);
_paragraphStyle = new TextPropCollection(1, StyleTextPropAtom.paragraphTextPropTypes);
}
BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(_paragraphStyle, ParagraphFlagsTextProp.NAME);
@ -688,6 +648,40 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
}
}
/**
* Search for a StyleTextPropAtom is for this text header (list of paragraphs)
*
* @param header the header
* @param textLen the length of the rawtext, or -1 if the length is not known
*/
private static StyleTextPropAtom findStyleAtomPresent(TextHeaderAtom header, int textLen) {
boolean afterHeader = false;
StyleTextPropAtom style = null;
for (Record record : header.getParentRecord().getChildRecords()) {
if (afterHeader && record.getRecordType() == RecordTypes.TextHeaderAtom.typeID) {
// already on the next header, quit searching
break;
}
afterHeader |= (header == record);
if (afterHeader && record.getRecordType() == RecordTypes.StyleTextPropAtom.typeID) {
// found it
style = (StyleTextPropAtom)record;
}
}
if (style == null) {
logger.log(POILogger.INFO, "styles atom doesn't exist. Creating dummy record for later saving.");
style = new StyleTextPropAtom((textLen < 0) ? 1 : textLen);
} else {
if (textLen >= 0) {
style.setParentTextSize(textLen);
}
}
return style;
}
/**
* Saves the modified paragraphs/textrun to the records.
* Also updates the styles to the correct text length.
@ -699,64 +693,69 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
// Will it fit in a 8 bit atom?
boolean isUnicode = StringUtil.hasMultibyte(rawText);
// isUnicode = true;
TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;
TextBytesAtom byteAtom = paragraphs.get(0)._byteAtom;
TextCharsAtom charAtom = paragraphs.get(0)._charAtom;
StyleTextPropAtom styleAtom = findStyleAtomPresent(headerAtom, rawText.length());
// Store in the appropriate record
Record oldRecord = null, newRecord = null;
if (isUnicode) {
if (byteAtom != null) {
if (byteAtom != null || charAtom == null) {
oldRecord = byteAtom;
newRecord = charAtom = new TextCharsAtom();
charAtom = new TextCharsAtom();
}
newRecord = charAtom;
charAtom.setText(rawText);
} else {
if (charAtom != null) {
if (charAtom != null || byteAtom == null) {
oldRecord = charAtom;
newRecord = byteAtom = new TextBytesAtom();
byteAtom = new TextBytesAtom();
}
newRecord = byteAtom;
byte[] byteText = new byte[rawText.length()];
StringUtil.putCompressedUnicode(rawText,byteText,0);
byteAtom.setText(byteText);
}
assert(newRecord != null);
RecordContainer _txtbox = headerAtom.getParentRecord();
if (oldRecord != null) {
// swap not appropriated records
Record[] cr = _txtbox.getChildRecords();
int idx=0;
for (Record r : cr) {
if(r.equals(oldRecord)) break;
idx++;
int headerIdx = -1, textIdx = -1, styleIdx = -1;
for (int i=0; i<cr.length; i++) {
Record r = cr[i];
if (r == headerAtom) headerIdx = i;
else if (r == oldRecord || r == newRecord) textIdx = i;
else if (r == styleAtom) styleIdx = i;
}
if (idx >= cr.length) {
throw new RuntimeException("child record not found - malformed container record");
}
cr[idx] = newRecord;
if (newRecord == byteAtom) {
charAtom = null;
if (textIdx == -1) {
// the old record was never registered, ignore it
_txtbox.addChildAfter(newRecord, headerAtom);
textIdx = headerIdx+1;
} else {
byteAtom = null;
}
// swap not appropriated records - noop if unchanged
cr[textIdx] = newRecord;
}
// Ensure a StyleTextPropAtom is present, adding if required
StyleTextPropAtom styleAtom = ensureStyleAtomPresent(headerAtom, byteAtom, charAtom);
if (styleIdx == -1) {
// Add the new StyleTextPropAtom after the TextCharsAtom / TextBytesAtom
_txtbox.addChildAfter(styleAtom, newRecord);
}
for (HSLFTextParagraph p : paragraphs) {
if (newRecord == byteAtom) {
p._charAtom = null;
} else {
p._byteAtom = null;
}
}
// Update the text length for its Paragraph and Character stylings
// If it's shared:
// * calculate the new length based on the run's old text
// * this should leave in any +1's for the end of block if needed
// If it isn't shared:
// * reset the length, to the new string's length
// * add on +1 if the last block
// The last run needs its stylings to be 1 longer than the raw
// text is. This is to define the stylings that any new text
// that is added will inherit
styleAtom.clearStyles();
@ -815,10 +814,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
HSLFTextParagraph htp = paragraphs.get(paragraphs.size()-1);
HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size()-1);
if (newParagraph) {
htr.setText(htr.getRawText()+"\n");
}
boolean isFirst = !newParagraph;
for (String rawText : text.split("(?<=\r)")) {
if (!isFirst) {
@ -832,6 +827,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
paragraphs.add(htp);
isFirst = false;
}
TextPropCollection tpc = htr.getCharacterStyle();
// special case, last text run is empty, we will reuse it
if (htr.getLength() > 0) {
@ -854,8 +850,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
* @param text the text string used by this object.
*/
public static HSLFTextRun setText(List<HSLFTextParagraph> paragraphs, String text) {
text = HSLFTextParagraph.toInternalString(text);
// check paragraphs
assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());
@ -1002,7 +996,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
TextHeaderAtom header = (TextHeaderAtom)records[recordIdx++];
TextBytesAtom tbytes = null;
TextCharsAtom tchars = null;
StyleTextPropAtom styles = null;
TextRulerAtom ruler = null;
MasterTextPropAtom indents = null;
@ -1014,7 +1007,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
if (RecordTypes.TextHeaderAtom.typeID == rt) break;
else if (RecordTypes.TextBytesAtom.typeID == rt) tbytes = (TextBytesAtom)r;
else if (RecordTypes.TextCharsAtom.typeID == rt) tchars = (TextCharsAtom)r;
else if (RecordTypes.StyleTextPropAtom.typeID == rt) styles = (StyleTextPropAtom)r;
// don't search for RecordTypes.StyleTextPropAtom.typeID here ... see findStyleAtomPresent below
else if (RecordTypes.TextRulerAtom.typeID == rt) ruler = (TextRulerAtom)r;
else if (RecordTypes.MasterTextPropAtom.typeID == rt) {
indents = (MasterTextPropAtom)r;
@ -1034,11 +1027,12 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
if (tbytes == null && tchars == null) {
tbytes = new TextBytesAtom();
header.getParentRecord().addChildAfter(tbytes, header);
// header.getParentRecord().addChildAfter(tbytes, header);
logger.log(POILogger.INFO, "bytes nor chars atom doesn't exist. Creating dummy record for later saving.");
}
String rawText = (tchars != null) ? tchars.getText() : tbytes.getText();
StyleTextPropAtom styles = findStyleAtomPresent(header, rawText.length());
// split, but keep delimiter
for (String para : rawText.split("(?<=\r)")) {
@ -1054,12 +1048,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
trun.setText(para);
}
if (styles == null) {
styles = ensureStyleAtomPresent(header, tbytes, tchars);
} else {
styles.setParentTextSize(rawText.length());
}
applyCharacterStyles(paragraphs, styles.getCharacterStyles());
applyParagraphStyles(paragraphs, styles.getParagraphStyles());
if (indents != null) {
@ -1095,7 +1083,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
ccRun += ccStyle-ccRun;
}
TextPropCollection pCopy = new TextPropCollection(0);
TextPropCollection pCopy = new TextPropCollection(0, StyleTextPropAtom.characterTextPropTypes);
pCopy.copy(p);
trun.setCharacterStyle(pCopy);
@ -1129,7 +1117,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
for (int ccPara = 0, ccStyle = p.getCharactersCovered(); ccPara < ccStyle; paraIdx++) {
if (paraIdx >= paragraphs.size() || ccPara >= ccStyle-1) return;
HSLFTextParagraph htp = paragraphs.get(paraIdx);
TextPropCollection pCopy = new TextPropCollection(0);
TextPropCollection pCopy = new TextPropCollection(0, StyleTextPropAtom.paragraphTextPropTypes);
pCopy.copy(p);
htp.setParagraphStyle(pCopy);
int len = 0;

View File

@ -23,6 +23,7 @@ import java.awt.Color;
import org.apache.poi.hslf.model.textproperties.*;
import org.apache.poi.hslf.record.ColorSchemeAtom;
import org.apache.poi.hslf.record.StyleTextPropAtom;
import org.apache.poi.sl.usermodel.TextRun;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@ -44,7 +45,7 @@ public final class HSLFTextRun implements TextRun {
* Our paragraph and character style.
* Note - we may share these styles with other RichTextRuns
*/
private TextPropCollection characterStyle = new TextPropCollection(0);
private TextPropCollection characterStyle = new TextPropCollection(0, StyleTextPropAtom.characterTextPropTypes);
/**
* Create a new wrapper around a rich text string
@ -163,7 +164,7 @@ public final class HSLFTextRun implements TextRun {
public void setCharTextPropVal(String propName, int val) {
// Ensure we have the StyleTextProp atom we're going to need
if(characterStyle == null) {
characterStyle = new TextPropCollection(1);
characterStyle = new TextPropCollection(1, StyleTextPropAtom.characterTextPropTypes);
// characterStyle will now be defined
}
@ -376,7 +377,7 @@ public final class HSLFTextRun implements TextRun {
protected void setFlag(int index, boolean value) {
// Ensure we have the StyleTextProp atom we're going to need
if (characterStyle == null) {
characterStyle = new TextPropCollection(1);
characterStyle = new TextPropCollection(1, StyleTextPropAtom.characterTextPropTypes);
}
BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(characterStyle, CharFlagsTextProp.NAME);

View File

@ -18,10 +18,10 @@
package org.apache.poi.hslf.usermodel;
import java.util.List;
import junit.framework.TestCase;
import org.apache.poi.hslf.*;
import org.apache.poi.hslf.model.*;
import org.apache.poi.POIDataSamples;
/**
@ -40,39 +40,39 @@ public final class TestCounts extends TestCase {
}
public void testSheetsCount() {
HSLFSlide[] slides = ss.getSlides();
List<HSLFSlide> slides = ss.getSlides();
// Two sheets - master sheet is separate
assertEquals(2, slides.length);
assertEquals(2, slides.size());
// They are slides 1+2
assertEquals(1, slides[0].getSlideNumber());
assertEquals(2, slides[1].getSlideNumber());
assertEquals(1, slides.get(0).getSlideNumber());
assertEquals(2, slides.get(1).getSlideNumber());
// The ref IDs are 4 and 6
assertEquals(4, slides[0]._getSheetRefId());
assertEquals(6, slides[1]._getSheetRefId());
assertEquals(4, slides.get(0)._getSheetRefId());
assertEquals(6, slides.get(1)._getSheetRefId());
// These are slides 1+2 -> 256+257
assertEquals(256, slides[0]._getSheetNumber());
assertEquals(257, slides[1]._getSheetNumber());
assertEquals(256, slides.get(0)._getSheetNumber());
assertEquals(257, slides.get(1)._getSheetNumber());
}
public void testNotesCount() {
HSLFNotes[] notes = ss.getNotes();
List<HSLFNotes> notes = ss.getNotes();
// Two sheets -> two notes
// Note: there are also notes on the slide master
//assertEquals(3, notes.length); // When we do slide masters
assertEquals(2, notes.length);
assertEquals(2, notes.size());
// First is for master
//assertEquals(-2147483648, notes[0]._getSheetNumber()); // When we do slide masters
//assertEquals(-2147483648, notes.get(0)._getSheetNumber()); // When we do slide masters
// Next two are for the two slides
assertEquals(256, notes[0]._getSheetNumber());
assertEquals(257, notes[1]._getSheetNumber());
assertEquals(256, notes.get(0)._getSheetNumber());
assertEquals(257, notes.get(1)._getSheetNumber());
// They happen to go between the two slides in Ref terms
assertEquals(5, notes[0]._getSheetRefId());
assertEquals(7, notes[1]._getSheetRefId());
assertEquals(5, notes.get(0)._getSheetRefId());
assertEquals(7, notes.get(1)._getSheetRefId());
}
}

View File

@ -40,21 +40,21 @@ public final class TestNotesText extends TestCase {
}
public void testNotesOne() {
HSLFNotes notes = ss.getNotes()[0];
HSLFNotes notes = ss.getNotes().get(0);
String[] expectText = new String[] {"These are the notes for page 1"};
assertEquals(expectText.length, notes.getTextParagraphs().length);
assertEquals(expectText.length, notes.getTextParagraphs().size());
for(int i=0; i<expectText.length; i++) {
assertEquals(expectText[i], notes.getTextParagraphs()[i].getRawText());
assertEquals(expectText[i], HSLFTextParagraph.getRawText(notes.getTextParagraphs().get(i)));
}
}
public void testNotesTwo() {
HSLFNotes notes = ss.getNotes()[1];
HSLFNotes notes = ss.getNotes().get(1);
String[] expectText = new String[] {"These are the notes on page two, again lacking formatting"};
assertEquals(expectText.length, notes.getTextParagraphs().length);
assertEquals(expectText.length, notes.getTextParagraphs().size());
for(int i=0; i<expectText.length; i++) {
assertEquals(expectText[i], notes.getTextParagraphs()[i].getRawText());
assertEquals(expectText[i], HSLFTextParagraph.getRawText(notes.getTextParagraphs().get(i)));
}
}
}