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;
/**
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; }
/** 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;
}
/** 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();
- int pos = 0;
- for(int i=0; i<textPropList.size(); i++) {
- TextProp curProp = textPropList.get(i);
- if(textProp.getMask() > curProp.getMask()) {
- pos++;
- }
- }
- textPropList.add(pos, textProp);
+ 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;
+ 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++;
+ }
+ }
+
+ if(!found) {
+ String err = "TextProp with name " + textProp.getName() + " doesn't belong to this collection.";
+ throw new IllegalArgumentException(err);
+ }
+ }
/**
* For an existing set of text properties, build the list of
* 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
}
prop.setValue(val);
bytesPassed += prop.getSize();
- textPropList.add(prop);
+ addProp(prop);
}
}
* 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);
}
/**
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);
}
}
/**
* 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);
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);
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();
+ }
}
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);
+ }
}
// 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);
* 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;
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
// 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
* 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);
- }
-
- // Now, we do the character ones
- for(int i=0; i<charStyles.size(); i++) {
- TextPropCollection tpc = charStyles.get(i);
- tpc.writeOut(baos, characterTextPropTypes);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ // First up, we need to serialise the paragraph properties
+ 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(TextPropCollection tpc : charStyles) {
+ // ditto for the char flags
+ tpc.addWithName(CharFlagsTextProp.NAME);
+ tpc.writeOut(baos);
+ }
+
+ rawContents = baos.toByteArray();
}
-
- 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);
}
/**
public void clearStyles() {
paragraphStyles.clear();
charStyles.clear();
+ reserved = new byte[0];
+ initialised = true;
}
/**
* @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;
}
* @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;
}
} 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(" 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(pr);
}
+
+ 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();
}
package org.apache.poi.hslf.usermodel;\r
\r
import java.awt.Color;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
import java.util.*;\r
\r
import org.apache.poi.hslf.model.PPFont;\r
\r
public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {\r
protected static POILogger logger = POILogFactory.getLogger(HSLFTextParagraph.class);\r
- \r
+\r
/**\r
* How to align the text\r
*/\r
/* package */ static final int AlignCenter = 1;\r
/* package */ static final int AlignRight = 2;\r
/* package */ static final int AlignJustify = 3;\r
- \r
- \r
+\r
+\r
// Note: These fields are protected to help with unit testing\r
// Other classes shouldn't really go playing with them!\r
private final TextHeaderAtom _headerAtom;\r
- private final TextBytesAtom _byteAtom;\r
- private final TextCharsAtom _charAtom;\r
+ private TextBytesAtom _byteAtom;\r
+ private TextCharsAtom _charAtom;\r
private StyleTextPropAtom _styleAtom;\r
- private TextPropCollection _paragraphStyle = new TextPropCollection(1);\r
- \r
+ private TextPropCollection _paragraphStyle = new TextPropCollection(1, StyleTextPropAtom.paragraphTextPropTypes);\r
+\r
protected TextRulerAtom _ruler;\r
protected List<HSLFTextRun> _runs = new ArrayList<HSLFTextRun>();\r
protected HSLFTextShape _parentShape;\r
\r
/**\r
* Constructs a Text Run from a Unicode text block.\r
- * Either a {@link TextCharsAtom} or a {@link TextBytesAtom} needs to be provided. \r
+ * Either a {@link TextCharsAtom} or a {@link TextBytesAtom} needs to be provided.\r
*\r
* @param tha the TextHeaderAtom that defines what's what\r
* @param tba the TextBytesAtom containing the text or null if {@link TextCharsAtom} is provided\r
public void addTextRun(HSLFTextRun run) {\r
_runs.add(run);\r
}\r
- \r
+\r
/**\r
* Fetch the rich text runs (runs of text with the same styling) that\r
* are contained within this block of text\r
public TextPropCollection getParagraphStyle() {\r
return _paragraphStyle;\r
}\r
- \r
+\r
public void setParagraphStyle(TextPropCollection paragraphStyle) {\r
_paragraphStyle = paragraphStyle;\r
}\r
- \r
+\r
/**\r
* Supply the Sheet we belong to, which might have an assigned SlideShow\r
* Also passes it on to our child RichTextRuns\r
}\r
\r
/**\r
- * Sets the index of the paragraph in the SLWT container \r
+ * Sets the index of the paragraph in the SLWT container\r
*\r
* @param index\r
*/\r
protected void setIndex(int index) {\r
if (_headerAtom != null) _headerAtom.setIndex(index);\r
}\r
- \r
+\r
/**\r
* Returns the type of the text, from the TextHeaderAtom.\r
* Possible values can be seen from TextHeaderAtom\r
public int getRunType() {\r
return (_headerAtom != null) ? _headerAtom.getTextType() : -1;\r
}\r
- \r
+\r
/**\r
* Is this Text Run one from a {@link PPDrawing}, or is it\r
* one from the {@link SlideListWithText}?\r
public Record[] getRecords(){\r
return _records;\r
}\r
- \r
+\r
/** Numbered List info */\r
public void setStyleTextProp9Atom(final StyleTextProp9Atom styleTextProp9Atom) {\r
this.styleTextProp9Atom = styleTextProp9Atom;\r
}\r
- \r
+\r
/** Numbered List info */\r
public StyleTextProp9Atom getStyleTextProp9Atom() {\r
return this.styleTextProp9Atom;\r
\r
/** Characters covered */\r
public StyleTextPropAtom getStyleTextPropAtom() {\r
- return this._styleAtom; \r
+ return this._styleAtom;\r
}\r
\r
/**\r
public void setParaTextPropVal(String propName, int val) {\r
// Ensure we have the StyleTextProp atom we're going to need\r
if(_paragraphStyle == null) {\r
- ensureStyleAtomPresent();\r
- // paragraphStyle will now be defined\r
+ _styleAtom = findStyleAtomPresent(_headerAtom, -1);\r
+ _paragraphStyle = _styleAtom.getParagraphStyles().get(0);\r
}\r
\r
assert(_paragraphStyle!=null);\r
TextProp tp = fetchOrAddTextProp(_paragraphStyle, propName);\r
tp.setValue(val);\r
}\r
- \r
- /**\r
- * Ensure a StyleTextPropAtom is present for this run,\r
- * by adding if required. Normally for internal TextRun use.\r
- */\r
- protected StyleTextPropAtom ensureStyleAtomPresent() {\r
- if (_styleAtom != null) {\r
- return _styleAtom;\r
- }\r
- \r
- _styleAtom = ensureStyleAtomPresent(_headerAtom, _byteAtom, _charAtom);\r
- _paragraphStyle = _styleAtom.getParagraphStyles().get(0);\r
- \r
- return _styleAtom;\r
- }\r
-\r
- protected static StyleTextPropAtom ensureStyleAtomPresent(TextHeaderAtom header, TextBytesAtom tbytes, TextCharsAtom tchars) {\r
- RecordContainer wrapper = header.getParentRecord();\r
- StyleTextPropAtom styleAtom = null;\r
- \r
- boolean afterHeader = false;\r
- for (Record record : wrapper.getChildRecords()) {\r
- if (afterHeader && record.getRecordType() == RecordTypes.TextHeaderAtom.typeID) break;\r
- afterHeader |= (header == record);\r
- if (afterHeader && record.getRecordType() == RecordTypes.StyleTextPropAtom.typeID) {\r
- styleAtom = (StyleTextPropAtom)record;\r
- break;\r
- }\r
- }\r
- \r
- if (styleAtom != null) return styleAtom;\r
- \r
- String rawText = (tchars != null) ? tchars.getText() : tbytes.getText();\r
- \r
- // Create a new one at the right size\r
- styleAtom = new StyleTextPropAtom(rawText.length()+1);\r
-\r
- // Add the new StyleTextPropAtom after the TextCharsAtom / TextBytesAtom\r
- wrapper.addChildAfter(styleAtom, (tbytes == null ? tchars : tbytes));\r
-\r
- return styleAtom;\r
- }\r
\r
@Override\r
public Iterator<HSLFTextRun> iterator() {\r
int val = (int)(leftMargin*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI);\r
setParaTextPropVal("text.offset", val);\r
}\r
- \r
+\r
@Override\r
public double getRightMargin() {\r
// TODO: find out, how to determine this value\r
public void setRightMargin(double rightMargin) {\r
// TODO: find out, how to set this value\r
}\r
- \r
+\r
@Override\r
public double getIndent() {\r
int val = getParaTextPropVal("bullet.offset");\r
}\r
setParaTextPropVal("alignment", alignInt);\r
}\r
- \r
+\r
@Override\r
public org.apache.poi.sl.usermodel.TextParagraph.TextAlign getTextAlign() {\r
switch (getParaTextPropVal("alignment")) {\r
@Override\r
public BulletStyle getBulletStyle() {\r
if (getBulletChar() == 0) return null;\r
- \r
+\r
return new BulletStyle() {\r
public String getBulletCharacter() {\r
char chr = HSLFTextParagraph.this.getBulletChar();\r
_parentShape = parentShape;\r
}\r
\r
- \r
+\r
/**\r
*\r
* @return indentation level\r
int val = getParaTextPropVal("spaceafter");\r
return val == -1 ? 0 : val;\r
}\r
- \r
+\r
/**\r
* Returns the named TextProp, either by fetching it (if it exists) or adding it\r
* (if it didn't)\r
protected void setFlag(int index, boolean value) {\r
// Ensure we have the StyleTextProp atom we're going to need\r
if(_paragraphStyle == null) {\r
- _paragraphStyle = new TextPropCollection(1);\r
+ _paragraphStyle = new TextPropCollection(1, StyleTextPropAtom.paragraphTextPropTypes);\r
}\r
\r
BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(_paragraphStyle, ParagraphFlagsTextProp.NAME);\r
}\r
lastRun = ltr.get(ltr.size()-1);\r
assert(lastRun.getRawText() != null);\r
- } \r
+ }\r
}\r
- \r
+\r
+ /**\r
+ * Search for a StyleTextPropAtom is for this text header (list of paragraphs)\r
+ * \r
+ * @param header the header\r
+ * @param textLen the length of the rawtext, or -1 if the length is not known\r
+ */\r
+ private static StyleTextPropAtom findStyleAtomPresent(TextHeaderAtom header, int textLen) {\r
+ boolean afterHeader = false;\r
+ StyleTextPropAtom style = null;\r
+ for (Record record : header.getParentRecord().getChildRecords()) {\r
+ if (afterHeader && record.getRecordType() == RecordTypes.TextHeaderAtom.typeID) {\r
+ // already on the next header, quit searching\r
+ break;\r
+ }\r
+ afterHeader |= (header == record);\r
+ if (afterHeader && record.getRecordType() == RecordTypes.StyleTextPropAtom.typeID) {\r
+ // found it\r
+ style = (StyleTextPropAtom)record;\r
+ }\r
+ }\r
+\r
+ if (style == null) {\r
+ logger.log(POILogger.INFO, "styles atom doesn't exist. Creating dummy record for later saving.");\r
+ style = new StyleTextPropAtom((textLen < 0) ? 1 : textLen);\r
+ } else {\r
+ if (textLen >= 0) {\r
+ style.setParentTextSize(textLen);\r
+ }\r
+ }\r
+ \r
+ return style;\r
+ }\r
+\r
+\r
/**\r
* Saves the modified paragraphs/textrun to the records.\r
* Also updates the styles to the correct text length.\r
*/\r
protected static void storeText(List<HSLFTextParagraph> paragraphs) {\r
fixLineEndings(paragraphs);\r
- \r
+\r
String rawText = toInternalString(getRawText(paragraphs));\r
\r
// Will it fit in a 8 bit atom?\r
boolean isUnicode = StringUtil.hasMultibyte(rawText);\r
- \r
+ // isUnicode = true;\r
+\r
TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;\r
TextBytesAtom byteAtom = paragraphs.get(0)._byteAtom;\r
TextCharsAtom charAtom = paragraphs.get(0)._charAtom;\r
+ StyleTextPropAtom styleAtom = findStyleAtomPresent(headerAtom, rawText.length());\r
\r
// Store in the appropriate record\r
Record oldRecord = null, newRecord = null;\r
- if (isUnicode) { \r
- if (byteAtom != null) {\r
+ if (isUnicode) {\r
+ if (byteAtom != null || charAtom == null) {\r
oldRecord = byteAtom;\r
- newRecord = charAtom = new TextCharsAtom();\r
+ charAtom = new TextCharsAtom();\r
}\r
+ newRecord = charAtom;\r
charAtom.setText(rawText);\r
} else {\r
- if (charAtom != null) {\r
+ if (charAtom != null || byteAtom == null) {\r
oldRecord = charAtom;\r
- newRecord = byteAtom = new TextBytesAtom();\r
+ byteAtom = new TextBytesAtom();\r
}\r
+ newRecord = byteAtom;\r
byte[] byteText = new byte[rawText.length()];\r
StringUtil.putCompressedUnicode(rawText,byteText,0);\r
byteAtom.setText(byteText);\r
}\r
-\r
+ assert(newRecord != null);\r
+ \r
RecordContainer _txtbox = headerAtom.getParentRecord();\r
+ Record[] cr = _txtbox.getChildRecords();\r
+ int headerIdx = -1, textIdx = -1, styleIdx = -1;\r
+ for (int i=0; i<cr.length; i++) {\r
+ Record r = cr[i];\r
+ if (r == headerAtom) headerIdx = i;\r
+ else if (r == oldRecord || r == newRecord) textIdx = i;\r
+ else if (r == styleAtom) styleIdx = i;\r
+ }\r
+\r
+ if (textIdx == -1) {\r
+ // the old record was never registered, ignore it\r
+ _txtbox.addChildAfter(newRecord, headerAtom);\r
+ textIdx = headerIdx+1;\r
+ } else {\r
+ // swap not appropriated records - noop if unchanged\r
+ cr[textIdx] = newRecord;\r
+ }\r
+\r
+ if (styleIdx == -1) {\r
+ // Add the new StyleTextPropAtom after the TextCharsAtom / TextBytesAtom\r
+ _txtbox.addChildAfter(styleAtom, newRecord);\r
+ }\r
\r
- if (oldRecord != null) {\r
- // swap not appropriated records\r
- Record[] cr = _txtbox.getChildRecords();\r
- int idx=0;\r
- for (Record r : cr) {\r
- if(r.equals(oldRecord)) break;\r
- idx++;\r
- }\r
- if (idx >= cr.length) {\r
- throw new RuntimeException("child record not found - malformed container record");\r
- }\r
- cr[idx] = newRecord;\r
- \r
+ for (HSLFTextParagraph p : paragraphs) {\r
if (newRecord == byteAtom) {\r
- charAtom = null;\r
+ p._charAtom = null;\r
} else {\r
- byteAtom = null;\r
+ p._byteAtom = null;\r
}\r
}\r
-\r
- // Ensure a StyleTextPropAtom is present, adding if required\r
- StyleTextPropAtom styleAtom = ensureStyleAtomPresent(headerAtom, byteAtom, charAtom);\r
-\r
+ \r
// Update the text length for its Paragraph and Character stylings\r
- // If it's shared:\r
- // * calculate the new length based on the run's old text\r
- // * this should leave in any +1's for the end of block if needed\r
- // If it isn't shared:\r
// * reset the length, to the new string's length\r
// * add on +1 if the last block\r
- // The last run needs its stylings to be 1 longer than the raw\r
- // text is. This is to define the stylings that any new text\r
- // that is added will inherit\r
- \r
+\r
styleAtom.clearStyles();\r
- \r
+\r
TextPropCollection lastPTPC = null, lastRTPC = null, ptpc = null, rtpc = null;\r
for (HSLFTextParagraph para : paragraphs) {\r
ptpc = para.getParagraphStyle();\r
lastRTPC.updateTextSize(lastRTPC.getCharactersCovered()+len);\r
}\r
}\r
- \r
+\r
assert(lastPTPC != null && lastRTPC != null && ptpc != null && rtpc != null);\r
ptpc.updateTextSize(ptpc.getCharactersCovered()+1);\r
rtpc.updateTextSize(rtpc.getCharactersCovered()+1);\r
lastPTPC.updateTextSize(lastPTPC.getCharactersCovered()+1);\r
lastRTPC.updateTextSize(lastRTPC.getCharactersCovered()+1);\r
- \r
+\r
/**\r
* If TextSpecInfoAtom is present, we must update the text size in it,\r
* otherwise the ppt will be corrupted\r
/**\r
* Adds the supplied text onto the end of the TextParagraphs,\r
* creating a new RichTextRun for it to sit in.\r
- * \r
+ *\r
* @param text the text string used by this object.\r
*/\r
protected static HSLFTextRun appendText(List<HSLFTextParagraph> paragraphs, String text, boolean newParagraph) {\r
\r
// check paragraphs\r
assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());\r
- \r
+\r
HSLFTextParagraph htp = paragraphs.get(paragraphs.size()-1);\r
HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size()-1);\r
\r
- if (newParagraph) {\r
- htr.setText(htr.getRawText()+"\n");\r
- }\r
- \r
boolean isFirst = !newParagraph;\r
for (String rawText : text.split("(?<=\r)")) {\r
if (!isFirst) {\r
paragraphs.add(htp);\r
isFirst = false;\r
}\r
+ \r
TextPropCollection tpc = htr.getCharacterStyle();\r
// special case, last text run is empty, we will reuse it\r
if (htr.getLength() > 0) {\r
}\r
htr.setText(rawText);\r
}\r
-\r
- storeText(paragraphs);\r
\r
+ storeText(paragraphs);\r
+\r
return htr;\r
}\r
- \r
+\r
/**\r
* Sets (overwrites) the current text.\r
* Uses the properties of the first paragraph / textrun\r
- * \r
+ *\r
* @param text the text string used by this object.\r
*/\r
public static HSLFTextRun setText(List<HSLFTextParagraph> paragraphs, String text) {\r
- text = HSLFTextParagraph.toInternalString(text);\r
- \r
// check paragraphs\r
assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());\r
\r
paraIter.next();\r
paraIter.remove();\r
}\r
- \r
+\r
Iterator<HSLFTextRun> runIter = htp.getTextRuns().iterator();\r
HSLFTextRun htr = runIter.next();\r
htr.setText("");\r
runIter.next();\r
runIter.remove();\r
}\r
- \r
+\r
return appendText(paragraphs, text, false);\r
}\r
- \r
+\r
public static String getRawText(List<HSLFTextParagraph> paragraphs) {\r
StringBuilder sb = new StringBuilder();\r
for (HSLFTextParagraph p : paragraphs) {\r
sb.append(r.getRawText());\r
}\r
}\r
- return sb.toString(); \r
+ return sb.toString();\r
}\r
- \r
+\r
/**\r
* Returns a new string with line breaks converted into internal ppt\r
* representation\r
* @param found vector to add any found to\r
*/\r
protected static List<List<HSLFTextParagraph>> findTextParagraphs(final Record[] records) {\r
- return findTextParagraphs(records, null); \r
+ return findTextParagraphs(records, null);\r
}\r
/**\r
* Scans through the supplied record array, looking for\r
*/\r
protected static List<List<HSLFTextParagraph>> findTextParagraphs(Record[] records, StyleTextProp9Atom styleTextProp9Atom) {\r
List<List<HSLFTextParagraph>> paragraphCollection = new ArrayList<List<HSLFTextParagraph>>();\r
- \r
+\r
if (records == null) {\r
throw new NullPointerException("records need to be filled.");\r
}\r
- \r
+\r
int recordIdx;\r
for (recordIdx = 0; recordIdx < records.length; recordIdx++) {\r
if (records[recordIdx] instanceof TextHeaderAtom) break;\r
}\r
- \r
+\r
if (recordIdx == records.length) {\r
logger.log(POILogger.INFO, "No text records found.");\r
return paragraphCollection;\r
}\r
- \r
+\r
for (int slwtIndex = 0; recordIdx < records.length; slwtIndex++) {\r
List<HSLFTextParagraph> paragraphs = new ArrayList<HSLFTextParagraph>();\r
paragraphCollection.add(paragraphs);\r
- \r
+\r
TextHeaderAtom header = (TextHeaderAtom)records[recordIdx++];\r
TextBytesAtom tbytes = null;\r
TextCharsAtom tchars = null;\r
- StyleTextPropAtom styles = null;\r
TextRulerAtom ruler = null;\r
MasterTextPropAtom indents = null;\r
- \r
+\r
List<Record> otherRecordList = new ArrayList<Record>();\r
- \r
+\r
for (; recordIdx < records.length; recordIdx++) {\r
Record r = records[recordIdx];\r
long rt = r.getRecordType();\r
if (RecordTypes.TextHeaderAtom.typeID == rt) break;\r
else if (RecordTypes.TextBytesAtom.typeID == rt) tbytes = (TextBytesAtom)r;\r
else if (RecordTypes.TextCharsAtom.typeID == rt) tchars = (TextCharsAtom)r;\r
- else if (RecordTypes.StyleTextPropAtom.typeID == rt) styles = (StyleTextPropAtom)r;\r
+ // don't search for RecordTypes.StyleTextPropAtom.typeID here ... see findStyleAtomPresent below\r
else if (RecordTypes.TextRulerAtom.typeID == rt) ruler = (TextRulerAtom)r;\r
else if (RecordTypes.MasterTextPropAtom.typeID == rt) {\r
indents = (MasterTextPropAtom)r;\r
otherRecordList.add(r);\r
}\r
}\r
- \r
+\r
assert(header != null);\r
if (header.getParentRecord() instanceof SlideListWithText) {\r
// runs found in PPDrawing are not linked with SlideListWithTexts\r
header.setIndex(slwtIndex);\r
}\r
- \r
+\r
Record otherRecords[] = otherRecordList.toArray(new Record[otherRecordList.size()]);\r
- \r
+\r
if (tbytes == null && tchars == null) {\r
tbytes = new TextBytesAtom();\r
- header.getParentRecord().addChildAfter(tbytes, header);\r
+ // header.getParentRecord().addChildAfter(tbytes, header);\r
logger.log(POILogger.INFO, "bytes nor chars atom doesn't exist. Creating dummy record for later saving.");\r
}\r
- \r
+\r
String rawText = (tchars != null) ? tchars.getText() : tbytes.getText();\r
- \r
+ StyleTextPropAtom styles = findStyleAtomPresent(header, rawText.length());\r
+\r
// split, but keep delimiter\r
for (String para : rawText.split("(?<=\r)")) {\r
HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars, styles);\r
tpara._ruler = ruler;\r
tpara._records = otherRecords;\r
tpara.getParagraphStyle().updateTextSize(para.length());\r
- \r
+\r
HSLFTextRun trun = new HSLFTextRun(tpara);\r
tpara.addTextRun(trun);\r
trun.setText(para);\r
}\r
- \r
- if (styles == null) {\r
- styles = ensureStyleAtomPresent(header, tbytes, tchars);\r
- } else {\r
- styles.setParentTextSize(rawText.length());\r
- }\r
- \r
+\r
applyCharacterStyles(paragraphs, styles.getCharacterStyles());\r
applyParagraphStyles(paragraphs, styles.getParagraphStyles());\r
if (indents != null) {\r
applyParagraphIndents(paragraphs, indents.getIndents());\r
}\r
}\r
- \r
+\r
return paragraphCollection;\r
}\r
\r
protected static void applyCharacterStyles(List<HSLFTextParagraph> paragraphs, List<TextPropCollection> charStyles) {\r
int paraIdx = 0, runIdx = 0;\r
HSLFTextRun trun;\r
- \r
+\r
for (int csIdx=0; csIdx<charStyles.size(); csIdx++) {\r
TextPropCollection p = charStyles.get(csIdx);\r
for (int ccRun = 0, ccStyle = p.getCharactersCovered(); ccRun < ccStyle; ) {\r
List<HSLFTextRun> runs = para.getTextRuns();\r
trun = runs.get(runIdx);\r
int len = trun.getLength();\r
- \r
+\r
if (ccRun+len <= ccStyle) {\r
ccRun += len;\r
} else {\r
String text = trun.getRawText();\r
trun.setText(text.substring(0,ccStyle-ccRun));\r
- \r
+\r
HSLFTextRun nextRun = new HSLFTextRun(para);\r
nextRun.setText(text.substring(ccStyle-ccRun));\r
runs.add(runIdx+1, nextRun);\r
- \r
+\r
ccRun += ccStyle-ccRun;\r
}\r
\r
- TextPropCollection pCopy = new TextPropCollection(0);\r
+ TextPropCollection pCopy = new TextPropCollection(0, StyleTextPropAtom.characterTextPropTypes);\r
pCopy.copy(p);\r
trun.setCharacterStyle(pCopy);\r
\r
}\r
}\r
pCopy.updateTextSize(len);\r
- \r
+\r
// need to compare it again, in case a run has been added after\r
if (++runIdx == runs.size()) {\r
paraIdx++;\r
}\r
}\r
}\r
- \r
+\r
protected static void applyParagraphStyles(List<HSLFTextParagraph> paragraphs, List<TextPropCollection> paraStyles) {\r
int paraIdx = 0;\r
for (TextPropCollection p : paraStyles) {\r
for (int ccPara = 0, ccStyle = p.getCharactersCovered(); ccPara < ccStyle; paraIdx++) {\r
if (paraIdx >= paragraphs.size() || ccPara >= ccStyle-1) return;\r
HSLFTextParagraph htp = paragraphs.get(paraIdx);\r
- TextPropCollection pCopy = new TextPropCollection(0);\r
+ TextPropCollection pCopy = new TextPropCollection(0, StyleTextPropAtom.paragraphTextPropTypes);\r
pCopy.copy(p);\r
htp.setParagraphStyle(pCopy);\r
int len = 0;\r
}\r
}\r
}\r
- \r
+\r
protected static void applyParagraphIndents(List<HSLFTextParagraph> paragraphs, List<IndentProp> paraStyles) {\r
int paraIdx = 0;\r
for (IndentProp p : paraStyles) {\r
}\r
}\r
}\r
- \r
+\r
protected static List<HSLFTextParagraph> createEmptyParagraph() {\r
EscherTextboxWrapper wrapper = new EscherTextboxWrapper();\r
- \r
+\r
TextHeaderAtom tha = new TextHeaderAtom();\r
tha.setParentRecord(wrapper);\r
wrapper.appendChildRecord(tha);\r
- \r
+\r
TextBytesAtom tba = new TextBytesAtom();\r
tba.setText("\r".getBytes());\r
wrapper.appendChildRecord(tba);\r
- \r
+\r
StyleTextPropAtom sta = new StyleTextPropAtom(1);\r
TextPropCollection paraStyle = sta.addParagraphTextPropCollection(1);\r
TextPropCollection charStyle = sta.addCharacterTextPropCollection(1);\r
wrapper.appendChildRecord(sta);\r
- \r
+\r
HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null, sta);\r
htp.setParagraphStyle(paraStyle);\r
htp._records = new Record[0];\r
htp.setLeftMargin(0);\r
htp.setIndent(0);\r
// set wrap flags\r
- \r
+\r
HSLFTextRun htr = new HSLFTextRun(htp);\r
htr.setCharacterStyle(charStyle);\r
htr.setText("\r");\r
htr.setFontColor(Color.black);\r
htp.addTextRun(htr);\r
- \r
+\r
return Arrays.asList(htp);\r
}\r
- \r
+\r
public EscherTextboxWrapper getTextboxWrapper() {\r
return (EscherTextboxWrapper)_headerAtom.getParentRecord();\r
}\r