From a1c2269e6f653468b848c8fe7d8afdfeee853c47 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Wed, 20 May 2015 23:07:08 +0000 Subject: [PATCH] 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 --- .../textproperties/TextPropCollection.java | 126 +++++-- .../poi/hslf/record/StyleTextPropAtom.java | 134 ++++--- .../poi/hslf/record/TxMasterStyleAtom.java | 8 +- .../poi/hslf/usermodel/HSLFTextParagraph.java | 326 +++++++++--------- .../poi/hslf/usermodel/HSLFTextRun.java | 7 +- .../apache/poi/hslf/usermodel/TestCounts.java | 34 +- .../poi/hslf/usermodel/TestNotesText.java | 12 +- 7 files changed, 336 insertions(+), 311 deletions(-) diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java index d856614eb..a5af73b3d 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java @@ -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 textPropList; + private final List textPropList = new ArrayList(); 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 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 @@ -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(); + 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(); + 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(); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java index 0e3474468..e2f765723 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java @@ -218,15 +218,23 @@ public final class StyleTextPropAtom extends RecordAtom charStyles = new ArrayList(); 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(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(); } - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - // First up, we need to serialise the paragraph properties - for(int i=0; i { protected static POILogger logger = POILogFactory.getLogger(HSLFTextParagraph.class); - + /** * How to align the text */ @@ -44,16 +46,16 @@ public final class HSLFTextParagraph implements TextParagraph { /* package */ static final int AlignCenter = 1; /* package */ static final int AlignRight = 2; /* package */ static final int AlignJustify = 3; - - + + // 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 _runs = new ArrayList(); protected HSLFTextShape _parentShape; @@ -70,7 +72,7 @@ public final class HSLFTextParagraph implements TextParagraph { /** * Constructs a Text Run from a Unicode text block. - * Either a {@link TextCharsAtom} or a {@link TextBytesAtom} needs to be provided. + * Either a {@link TextCharsAtom} or a {@link TextBytesAtom} needs to be provided. * * @param tha the TextHeaderAtom that defines what's what * @param tba the TextBytesAtom containing the text or null if {@link TextCharsAtom} is provided @@ -105,7 +107,7 @@ public final class HSLFTextParagraph implements TextParagraph { public void addTextRun(HSLFTextRun run) { _runs.add(run); } - + /** * Fetch the rich text runs (runs of text with the same styling) that * are contained within this block of text @@ -117,11 +119,11 @@ public final class HSLFTextParagraph implements TextParagraph { public TextPropCollection getParagraphStyle() { return _paragraphStyle; } - + public void setParagraphStyle(TextPropCollection paragraphStyle) { _paragraphStyle = paragraphStyle; } - + /** * Supply the Sheet we belong to, which might have an assigned SlideShow * Also passes it on to our child RichTextRuns @@ -161,14 +163,14 @@ public final class HSLFTextParagraph implements TextParagraph { } /** - * Sets the index of the paragraph in the SLWT container + * Sets the index of the paragraph in the SLWT container * * @param index */ protected void setIndex(int index) { if (_headerAtom != null) _headerAtom.setIndex(index); } - + /** * Returns the type of the text, from the TextHeaderAtom. * Possible values can be seen from TextHeaderAtom @@ -177,7 +179,7 @@ public final class HSLFTextParagraph implements TextParagraph { public int getRunType() { return (_headerAtom != null) ? _headerAtom.getTextType() : -1; } - + /** * Is this Text Run one from a {@link PPDrawing}, or is it * one from the {@link SlideListWithText}? @@ -208,12 +210,12 @@ public final class HSLFTextParagraph implements TextParagraph { 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; @@ -221,7 +223,7 @@ public final class HSLFTextParagraph implements TextParagraph { /** Characters covered */ public StyleTextPropAtom getStyleTextPropAtom() { - return this._styleAtom; + return this._styleAtom; } /** @@ -258,56 +260,14 @@ public final class HSLFTextParagraph implements TextParagraph { 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); TextProp tp = fetchOrAddTextProp(_paragraphStyle, propName); 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 iterator() { @@ -325,7 +285,7 @@ public final class HSLFTextParagraph implements TextParagraph { int val = (int)(leftMargin*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI); setParaTextPropVal("text.offset", val); } - + @Override public double getRightMargin() { // TODO: find out, how to determine this value @@ -336,7 +296,7 @@ public final class HSLFTextParagraph implements TextParagraph { public void setRightMargin(double rightMargin) { // TODO: find out, how to set this value } - + @Override public double getIndent() { int val = getParaTextPropVal("bullet.offset"); @@ -378,7 +338,7 @@ public final class HSLFTextParagraph implements TextParagraph { } setParaTextPropVal("alignment", alignInt); } - + @Override public org.apache.poi.sl.usermodel.TextParagraph.TextAlign getTextAlign() { switch (getParaTextPropVal("alignment")) { @@ -408,7 +368,7 @@ public final class HSLFTextParagraph implements TextParagraph { @Override public BulletStyle getBulletStyle() { if (getBulletChar() == 0) return null; - + return new BulletStyle() { public String getBulletCharacter() { char chr = HSLFTextParagraph.this.getBulletChar(); @@ -441,7 +401,7 @@ public final class HSLFTextParagraph implements TextParagraph { _parentShape = parentShape; } - + /** * * @return indentation level @@ -622,7 +582,7 @@ public final class HSLFTextParagraph implements TextParagraph { int val = getParaTextPropVal("spaceafter"); return val == -1 ? 0 : val; } - + /** * Returns the named TextProp, either by fetching it (if it exists) or adding it * (if it didn't) @@ -661,7 +621,7 @@ public final class HSLFTextParagraph implements TextParagraph { 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); @@ -685,81 +645,120 @@ public final class HSLFTextParagraph implements TextParagraph { } lastRun = ltr.get(ltr.size()-1); assert(lastRun.getRawText() != null); - } + } } - + + /** + * 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. */ protected static void storeText(List paragraphs) { fixLineEndings(paragraphs); - + String rawText = toInternalString(getRawText(paragraphs)); // 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 (isUnicode) { + 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); } - - RecordContainer _txtbox = headerAtom.getParentRecord(); + assert(newRecord != null); - if (oldRecord != null) { - // swap not appropriated records - Record[] cr = _txtbox.getChildRecords(); - int idx=0; - for (Record r : cr) { - if(r.equals(oldRecord)) break; - idx++; - } - if (idx >= cr.length) { - throw new RuntimeException("child record not found - malformed container record"); - } - cr[idx] = newRecord; - - if (newRecord == byteAtom) { - charAtom = null; - } else { - byteAtom = null; - } + RecordContainer _txtbox = headerAtom.getParentRecord(); + Record[] cr = _txtbox.getChildRecords(); + int headerIdx = -1, textIdx = -1, styleIdx = -1; + for (int i=0; i { lastRTPC.updateTextSize(lastRTPC.getCharactersCovered()+len); } } - + assert(lastPTPC != null && lastRTPC != null && ptpc != null && rtpc != null); ptpc.updateTextSize(ptpc.getCharactersCovered()+1); rtpc.updateTextSize(rtpc.getCharactersCovered()+1); lastPTPC.updateTextSize(lastPTPC.getCharactersCovered()+1); lastRTPC.updateTextSize(lastRTPC.getCharactersCovered()+1); - + /** * If TextSpecInfoAtom is present, we must update the text size in it, * otherwise the ppt will be corrupted @@ -803,7 +802,7 @@ public final class HSLFTextParagraph implements TextParagraph { /** * Adds the supplied text onto the end of the TextParagraphs, * creating a new RichTextRun for it to sit in. - * + * * @param text the text string used by this object. */ protected static HSLFTextRun appendText(List paragraphs, String text, boolean newParagraph) { @@ -811,14 +810,10 @@ public final class HSLFTextParagraph implements TextParagraph { // check paragraphs assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty()); - + 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 { paragraphs.add(htp); isFirst = false; } + TextPropCollection tpc = htr.getCharacterStyle(); // special case, last text run is empty, we will reuse it if (htr.getLength() > 0) { @@ -841,21 +837,19 @@ public final class HSLFTextParagraph implements TextParagraph { } htr.setText(rawText); } - - storeText(paragraphs); + storeText(paragraphs); + return htr; } - + /** * Sets (overwrites) the current text. * Uses the properties of the first paragraph / textrun - * + * * @param text the text string used by this object. */ public static HSLFTextRun setText(List paragraphs, String text) { - text = HSLFTextParagraph.toInternalString(text); - // check paragraphs assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty()); @@ -866,7 +860,7 @@ public final class HSLFTextParagraph implements TextParagraph { paraIter.next(); paraIter.remove(); } - + Iterator runIter = htp.getTextRuns().iterator(); HSLFTextRun htr = runIter.next(); htr.setText(""); @@ -875,10 +869,10 @@ public final class HSLFTextParagraph implements TextParagraph { runIter.next(); runIter.remove(); } - + return appendText(paragraphs, text, false); } - + public static String getRawText(List paragraphs) { StringBuilder sb = new StringBuilder(); for (HSLFTextParagraph p : paragraphs) { @@ -886,9 +880,9 @@ public final class HSLFTextParagraph implements TextParagraph { sb.append(r.getRawText()); } } - return sb.toString(); + return sb.toString(); } - + /** * Returns a new string with line breaks converted into internal ppt * representation @@ -948,7 +942,7 @@ public final class HSLFTextParagraph implements TextParagraph { * @param found vector to add any found to */ protected static List> findTextParagraphs(final Record[] records) { - return findTextParagraphs(records, null); + return findTextParagraphs(records, null); } /** * Scans through the supplied record array, looking for @@ -980,41 +974,40 @@ public final class HSLFTextParagraph implements TextParagraph { */ protected static List> findTextParagraphs(Record[] records, StyleTextProp9Atom styleTextProp9Atom) { List> paragraphCollection = new ArrayList>(); - + if (records == null) { throw new NullPointerException("records need to be filled."); } - + int recordIdx; for (recordIdx = 0; recordIdx < records.length; recordIdx++) { if (records[recordIdx] instanceof TextHeaderAtom) break; } - + if (recordIdx == records.length) { logger.log(POILogger.INFO, "No text records found."); return paragraphCollection; } - + for (int slwtIndex = 0; recordIdx < records.length; slwtIndex++) { List paragraphs = new ArrayList(); paragraphCollection.add(paragraphs); - + TextHeaderAtom header = (TextHeaderAtom)records[recordIdx++]; TextBytesAtom tbytes = null; TextCharsAtom tchars = null; - StyleTextPropAtom styles = null; TextRulerAtom ruler = null; MasterTextPropAtom indents = null; - + List otherRecordList = new ArrayList(); - + for (; recordIdx < records.length; recordIdx++) { Record r = records[recordIdx]; long rt = r.getRecordType(); 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; @@ -1023,23 +1016,24 @@ public final class HSLFTextParagraph implements TextParagraph { otherRecordList.add(r); } } - + assert(header != null); if (header.getParentRecord() instanceof SlideListWithText) { // runs found in PPDrawing are not linked with SlideListWithTexts header.setIndex(slwtIndex); } - + Record otherRecords[] = otherRecordList.toArray(new Record[otherRecordList.size()]); - + 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)")) { HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars, styles); @@ -1048,32 +1042,26 @@ public final class HSLFTextParagraph implements TextParagraph { tpara._ruler = ruler; tpara._records = otherRecords; tpara.getParagraphStyle().updateTextSize(para.length()); - + HSLFTextRun trun = new HSLFTextRun(tpara); tpara.addTextRun(trun); 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) { applyParagraphIndents(paragraphs, indents.getIndents()); } } - + return paragraphCollection; } protected static void applyCharacterStyles(List paragraphs, List charStyles) { int paraIdx = 0, runIdx = 0; HSLFTextRun trun; - + for (int csIdx=0; csIdx { List runs = para.getTextRuns(); trun = runs.get(runIdx); int len = trun.getLength(); - + if (ccRun+len <= ccStyle) { ccRun += len; } else { String text = trun.getRawText(); trun.setText(text.substring(0,ccStyle-ccRun)); - + HSLFTextRun nextRun = new HSLFTextRun(para); nextRun.setText(text.substring(ccStyle-ccRun)); runs.add(runIdx+1, nextRun); - + ccRun += ccStyle-ccRun; } - TextPropCollection pCopy = new TextPropCollection(0); + TextPropCollection pCopy = new TextPropCollection(0, StyleTextPropAtom.characterTextPropTypes); pCopy.copy(p); trun.setCharacterStyle(pCopy); @@ -1113,7 +1101,7 @@ public final class HSLFTextParagraph implements TextParagraph { } } pCopy.updateTextSize(len); - + // need to compare it again, in case a run has been added after if (++runIdx == runs.size()) { paraIdx++; @@ -1122,14 +1110,14 @@ public final class HSLFTextParagraph implements TextParagraph { } } } - + protected static void applyParagraphStyles(List paragraphs, List paraStyles) { int paraIdx = 0; for (TextPropCollection p : paraStyles) { 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; @@ -1142,7 +1130,7 @@ public final class HSLFTextParagraph implements TextParagraph { } } } - + protected static void applyParagraphIndents(List paragraphs, List paraStyles) { int paraIdx = 0; for (IndentProp p : paraStyles) { @@ -1157,23 +1145,23 @@ public final class HSLFTextParagraph implements TextParagraph { } } } - + protected static List createEmptyParagraph() { EscherTextboxWrapper wrapper = new EscherTextboxWrapper(); - + TextHeaderAtom tha = new TextHeaderAtom(); tha.setParentRecord(wrapper); wrapper.appendChildRecord(tha); - + TextBytesAtom tba = new TextBytesAtom(); tba.setText("\r".getBytes()); wrapper.appendChildRecord(tba); - + StyleTextPropAtom sta = new StyleTextPropAtom(1); TextPropCollection paraStyle = sta.addParagraphTextPropCollection(1); TextPropCollection charStyle = sta.addCharacterTextPropCollection(1); wrapper.appendChildRecord(sta); - + HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null, sta); htp.setParagraphStyle(paraStyle); htp._records = new Record[0]; @@ -1182,16 +1170,16 @@ public final class HSLFTextParagraph implements TextParagraph { htp.setLeftMargin(0); htp.setIndent(0); // set wrap flags - + HSLFTextRun htr = new HSLFTextRun(htp); htr.setCharacterStyle(charStyle); htr.setText("\r"); htr.setFontColor(Color.black); htp.addTextRun(htr); - + return Arrays.asList(htp); } - + public EscherTextboxWrapper getTextboxWrapper() { return (EscherTextboxWrapper)_headerAtom.getParentRecord(); } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java index d06715b17..b4da12c4d 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java @@ -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); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestCounts.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestCounts.java index dbee82742..cb51595f5 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestCounts.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestCounts.java @@ -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 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 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()); } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestNotesText.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestNotesText.java index 570fb5883..8713c09d0 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestNotesText.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestNotesText.java @@ -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