diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java index ca369ea95..aef03197d 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java @@ -490,27 +490,7 @@ public class XSLFTextParagraph implements TextParagraph { tabStops.addNewTab().setPos(Units.toEMU(value)); } - /** - * This element specifies the vertical line spacing that is to be used within a paragraph. - * This may be specified in two different ways, percentage spacing and font point spacing: - *

- * If linespacing >= 0, then linespacing is a percentage of normal line height - * If linespacing < 0, the absolute value of linespacing is the spacing in points - *

- * Examples: - *

-     *      // spacing will be 120% of the size of the largest text on each line
-     *      paragraph.setLineSpacing(120);
-     *
-     *      // spacing will be 200% of the size of the largest text on each line
-     *      paragraph.setLineSpacing(200);
-     *
-     *      // spacing will be 48 points
-     *      paragraph.setLineSpacing(-48.0);
-     * 
- * - * @param linespacing the vertical line spacing - */ + @Override public void setLineSpacing(double linespacing){ CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); @@ -519,16 +499,7 @@ public class XSLFTextParagraph implements TextParagraph { pr.setLnSpc(spc); } - /** - * Returns the vertical line spacing that is to be used within a paragraph. - * This may be specified in two different ways, percentage spacing and font point spacing: - *

- * If linespacing >= 0, then linespacing is a percentage of normal line height. - * If linespacing < 0, the absolute value of linespacing is the spacing in points - *

- * - * @return the vertical line spacing. - */ + @Override public double getLineSpacing(){ ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){ public boolean fetch(CTTextParagraphProperties props){ diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/BitMaskTextProp.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/BitMaskTextProp.java index 0f8bab418..e688f9959 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/BitMaskTextProp.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/BitMaskTextProp.java @@ -82,13 +82,9 @@ public abstract class BitMaskTextProp extends TextProp implements Cloneable { * Set the true/false status of the subproperty with the given index */ public void setSubValue(boolean value, int idx) { - if(subPropMatches[idx] == value) { return; } - if(value) { - dataValue += subPropMasks[idx]; - } else { - dataValue -= subPropMasks[idx]; - } - subPropMatches[idx] = value; + if (subPropMatches[idx] == value) return; + subPropMatches[idx] = value; + dataValue ^= subPropMasks[idx]; } @Override @@ -101,4 +97,8 @@ public abstract class BitMaskTextProp extends TextProp implements Cloneable { return newObj; } + + public BitMaskTextProp cloneAll(){ + return (BitMaskTextProp)super.clone(); + } } \ No newline at end of file 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 ac34bf1ff..d856614eb 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 @@ -160,14 +160,9 @@ public class TextPropCollection { this.reservedField = other.reservedField; this.textPropList.clear(); for (TextProp tp : other.textPropList) { - TextProp tpCopy = tp.clone(); - if (tpCopy instanceof BitMaskTextProp) { - BitMaskTextProp bmt = (BitMaskTextProp)tpCopy; - boolean matches[] = ((BitMaskTextProp)tp).getSubPropMatches(); - for (int i=0; i paragraphStyles; - public LinkedList getParagraphStyles() { return paragraphStyles; } + private List paragraphStyles; + public List getParagraphStyles() { return paragraphStyles; } /** * Updates the link list of TextPropCollections which make up the * paragraph stylings */ - public void setParagraphStyles(LinkedList ps) { paragraphStyles = ps; } + public void setParagraphStyles(List ps) { paragraphStyles = ps; } /** * The list of all the different character stylings we code for. * Each entry is a TextPropCollection, which tells you how many * Characters the character styling covers, and also contains the * TextProps that actually define the styling of the characters. */ - private LinkedList charStyles; - public LinkedList getCharacterStyles() { return charStyles; } + private List charStyles; + public List getCharacterStyles() { return charStyles; } /** * Updates the link list of TextPropCollections which make up the * character stylings */ - public void setCharacterStyles(LinkedList cs) { charStyles = cs; } + public void setCharacterStyles(List cs) { charStyles = cs; } /** * Returns how many characters the paragraph's @@ -105,7 +102,7 @@ public final class StyleTextPropAtom extends RecordAtom public int getCharacterTextLengthCovered() { return getCharactersCovered(charStyles); } - private int getCharactersCovered(LinkedList styles) { + private int getCharactersCovered(List styles) { int length = 0; for(TextPropCollection tpc : styles) { length += tpc.getCharactersCovered(); @@ -197,9 +194,9 @@ public final class StyleTextPropAtom extends RecordAtom System.arraycopy(source,start+8,rawContents,0,rawContents.length); reserved = new byte[0]; - // Set empty linked lists, ready for when they call setParentTextSize - paragraphStyles = new LinkedList(); - charStyles = new LinkedList(); + // Set empty lists, ready for when they call setParentTextSize + paragraphStyles = new ArrayList(); + charStyles = new ArrayList(); } @@ -217,8 +214,8 @@ public final class StyleTextPropAtom extends RecordAtom LittleEndian.putInt(_header,4,10); // Set empty paragraph and character styles - paragraphStyles = new LinkedList(); - charStyles = new LinkedList(); + paragraphStyles = new ArrayList(); + charStyles = new ArrayList(); TextPropCollection defaultParagraphTextProps = new TextPropCollection(parentTextSize, (short)0); @@ -377,13 +374,13 @@ public final class StyleTextPropAtom extends RecordAtom // First up, we need to serialise the paragraph properties for(int i=0; i { * @param shape */ protected void onAddTextShape(HSLFTextShape shape) { - } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java index c6a1e12bd..7d76efe8d 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java @@ -553,30 +553,22 @@ public final class HSLFTextParagraph implements TextParagraph { return getParaTextPropVal("bullet.font"); } - /** - * Sets the line spacing. - *

- * If linespacing >= 0, then linespacing is a percentage of normal line height. - * If linespacing < 0, the absolute value of linespacing is the spacing in master coordinates. - *

- */ - public void setLineSpacing(int val) { - setParaTextPropVal("linespacing", val); + @Override + public void setLineSpacing(double lineSpacing) { + // if lineSpacing < 0, we need to convert points (common interface) to master units (hslf) + if (lineSpacing < 0) { + lineSpacing = (lineSpacing*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI); + } + setParaTextPropVal("linespacing", (int)lineSpacing); } - /** - * Returns the line spacing - *

- * If linespacing >= 0, then linespacing is a percentage of normal line height. - * If linespacing < 0, the absolute value of linespacing is the spacing in master coordinates. - *

- * - * @return the spacing between lines - */ @Override public double getLineSpacing() { - int val = getParaTextPropVal("linespacing"); - return val == -1 ? 0 : val; + double val = getParaTextPropVal("linespacing"); + // if lineSpacing < 0, we need to convert master units (hslf) to points (common interface) + if (val == -1) return 0; + if (val < -1) val *= HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI); + return val; } /** @@ -722,6 +714,12 @@ public final class HSLFTextParagraph implements TextParagraph { throw new RuntimeException("child record not found - malformed container record"); } cr[idx] = newRecord; + + if (newRecord == byteAtom) { + charAtom = null; + } else { + byteAtom = null; + } } // Ensure a StyleTextPropAtom is present, adding if required @@ -750,6 +748,7 @@ public final class HSLFTextParagraph implements TextParagraph { } for (HSLFTextRun tr : para.getTextRuns()) { TextPropCollection rtpc = tr.getCharacterStyle(); + rtpc.updateTextSize(0); if (!rtpc.equals(lastRTPC)) { lastRTPC = styleAtom.addCharacterTextPropCollection(0); lastRTPC.copy(rtpc); @@ -783,19 +782,43 @@ public final class HSLFTextParagraph implements TextParagraph { * * @param text the text string used by this object. */ - protected static void appendText(List paragraphs, String text, boolean newParagraph) { + protected static HSLFTextRun appendText(List paragraphs, String text, boolean newParagraph) { text = toInternalString(text); + + // check paragraphs + assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty()); - // init paragraphs - assert(!paragraphs.isEmpty()); + HSLFTextParagraph htp = paragraphs.get(paragraphs.size()-1); + HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size()-1); + + if (newParagraph) { + htr.setText(htr.getRawText()+"\n"); + } - HSLFTextParagraph lastHTP = paragraphs.get(paragraphs.size()-1); - HSLFTextRun lastHTR = lastHTP.getTextRuns().get(lastHTP.getTextRuns().size()-1); - HSLFTextParagraph htp = (newParagraph) ? new HSLFTextParagraph(lastHTP) : lastHTP; - HSLFTextRun htr = new HSLFTextRun(htp); - htr.setText(text); - htr.getCharacterStyle().copy(lastHTR.getCharacterStyle()); - htp.addTextRun(htr); + boolean isFirst = !newParagraph; + for (String rawText : text.split("(?<=\r)")) { + if (!isFirst) { + TextPropCollection tpc = htp.getParagraphStyle(); + HSLFTextParagraph prevHtp = htp; + htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom, htp._styleAtom); + htp.getParagraphStyle().copy(tpc); + htp.setParentShape(prevHtp.getParentShape()); + htp.setShapeId(prevHtp.getShapeId()); + htp.supplySheet(prevHtp.getSheet()); + paragraphs.add(htp); + isFirst = false; + } + TextPropCollection tpc = htr.getCharacterStyle(); + // special case, last text run is empty, we will reuse it + if (htr.getLength() > 0) { + htr = new HSLFTextRun(htp); + htr.getCharacterStyle().copy(tpc); + htp.addTextRun(htr); + } + htr.setText(rawText); + } + + return htr; } /** @@ -804,29 +827,30 @@ public final class HSLFTextParagraph implements TextParagraph { * * @param text the text string used by this object. */ - public static void setText(List paragraphs, String text) { + public static HSLFTextRun setText(List paragraphs, String text) { text = HSLFTextParagraph.toInternalString(text); - // init paragraphs - assert(!paragraphs.isEmpty()); + // check paragraphs + assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty()); Iterator paraIter = paragraphs.iterator(); - HSLFTextParagraph firstHTP = paraIter.next(); // keep first - assert(firstHTP != null); + HSLFTextParagraph htp = paraIter.next(); // keep first + assert(htp != null); while (paraIter.hasNext()) { paraIter.next(); paraIter.remove(); } - Iterator runIter = firstHTP.getTextRuns().iterator(); - HSLFTextRun firstHTR = runIter.next(); - assert(firstHTR != null); + Iterator runIter = htp.getTextRuns().iterator(); + HSLFTextRun htr = runIter.next(); + htr.setText(""); + assert(htr != null); while (runIter.hasNext()) { runIter.next(); runIter.remove(); } - firstHTR.setText(text); + return appendText(paragraphs, text, false); } public static String getRawText(List paragraphs) { @@ -835,9 +859,7 @@ public final class HSLFTextParagraph implements TextParagraph { for (HSLFTextRun r : p.getTextRuns()) { sb.append(r.getRawText()); } - sb.append("\r"); } - sb.deleteCharAt(sb.length()-1); // remove last line break return sb.toString(); } @@ -947,7 +969,7 @@ public final class HSLFTextParagraph implements TextParagraph { return paragraphCollection; } - for (int slwtIndex = 0; recordIdx < records.length-2; slwtIndex++) { + for (int slwtIndex = 0; recordIdx < records.length; slwtIndex++) { List paragraphs = new ArrayList(); paragraphCollection.add(paragraphs); @@ -985,12 +1007,14 @@ public final class HSLFTextParagraph implements TextParagraph { if (tbytes == null && tchars == null) { tbytes = new TextBytesAtom(); + 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(); - for (String para : rawText.split("\r")) { + // split, but keep delimiter + for (String para : rawText.split("(?<=\r)")) { HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars, styles); paragraphs.add(tpara); tpara.setStyleTextProp9Atom(styleTextProp9Atom); @@ -1021,29 +1045,21 @@ public final class HSLFTextParagraph implements TextParagraph { protected static void applyCharacterStyles(List paragraphs, List charStyles) { int paraIdx = 0, runIdx = 0; - for (TextPropCollection p : charStyles) { + HSLFTextRun trun; + + for (int csIdx=0; csIdx runs = para.getTextRuns(); - HSLFTextRun trun = runs.get(runIdx); + trun = runs.get(runIdx); int len = trun.getLength(); - if (runIdx+1 >= runs.size()) { - // need to add +1 to the last run of the paragraph - len++; - } - - TextPropCollection pCopy = new TextPropCollection(1); - pCopy.copy(p); if (ccRun+len <= ccStyle) { - trun.setCharacterStyle(pCopy); - pCopy.updateTextSize(len); ccRun += len; } else { String text = trun.getRawText(); trun.setText(text.substring(0,ccStyle-ccRun)); - pCopy.updateTextSize(ccStyle-ccRun); - trun.setCharacterStyle(pCopy); HSLFTextRun nextRun = new HSLFTextRun(para); nextRun.setText(text.substring(ccStyle-ccRun)); @@ -1052,8 +1068,27 @@ public final class HSLFTextParagraph implements TextParagraph { ccRun += ccStyle-ccRun; } - // need to compare it again, in case a run has been added afer - if (++runIdx >= runs.size()) { + TextPropCollection pCopy = new TextPropCollection(0); + pCopy.copy(p); + trun.setCharacterStyle(pCopy); + + len = trun.getLength(); + if (paraIdx == paragraphs.size()-1 && runIdx == runs.size()-1) { + if (csIdx < charStyles.size()-1) { + // special case, empty trailing text run + HSLFTextRun nextRun = new HSLFTextRun(para); + nextRun.setText(""); + runs.add(nextRun); + } else { + // need to add +1 to the last run of the last paragraph + len++; + ccRun++; + } + } + pCopy.updateTextSize(len); + + // need to compare it again, in case a run has been added after + if (++runIdx == runs.size()) { paraIdx++; runIdx = 0; } @@ -1065,16 +1100,18 @@ public final class HSLFTextParagraph implements TextParagraph { int paraIdx = 0; for (TextPropCollection p : paraStyles) { for (int ccPara = 0, ccStyle = p.getCharactersCovered(); ccPara < ccStyle; paraIdx++) { - HSLFTextParagraph para = paragraphs.get(paraIdx); - TextPropCollection pCopy = new TextPropCollection(1); + if (paraIdx >= paragraphs.size() || ccPara >= ccStyle-1) return; + HSLFTextParagraph htp = paragraphs.get(paraIdx); + TextPropCollection pCopy = new TextPropCollection(0); pCopy.copy(p); + htp.setParagraphStyle(pCopy); int len = 0; - for (HSLFTextRun trun : para.getTextRuns()) { + for (HSLFTextRun trun : htp.getTextRuns()) { len += trun.getLength(); } - pCopy.updateTextSize(len+1); - para.setParagraphStyle(pCopy); - ccPara += len+1; + if (paraIdx == paragraphs.size()-1) len++; + pCopy.updateTextSize(len); + ccPara += len; } } } @@ -1101,15 +1138,28 @@ public final class HSLFTextParagraph implements TextParagraph { tha.setParentRecord(wrapper); wrapper.appendChildRecord(tha); - TextCharsAtom tca = new TextCharsAtom(); - wrapper.appendChildRecord(tca); + TextBytesAtom tba = new TextBytesAtom(); + tba.setText("\r".getBytes()); + wrapper.appendChildRecord(tba); - StyleTextPropAtom sta = new StyleTextPropAtom(0); + StyleTextPropAtom sta = new StyleTextPropAtom(1); + TextPropCollection paraStyle = sta.addParagraphTextPropCollection(1); + TextPropCollection charStyle = sta.addCharacterTextPropCollection(1); wrapper.appendChildRecord(sta); - HSLFTextParagraph htp = new HSLFTextParagraph(tha, null, tca, sta); + HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null, sta); + htp.setParagraphStyle(paraStyle); htp._records = new Record[0]; + htp.setBullet(false); + htp.setLineSpacing(100); + 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); 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 9a10a9b12..e94c84464 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java @@ -37,14 +37,14 @@ public final class HSLFTextRun implements TextRun { /** The TextRun we belong to */ private HSLFTextParagraph parentParagraph; - private String _runText = "\r"; + private String _runText = ""; private String _fontname; /** * Our paragraph and character style. * Note - we may share these styles with other RichTextRuns */ - private TextPropCollection characterStyle = new TextPropCollection(1); + private TextPropCollection characterStyle = new TextPropCollection(0); /** * Create a new wrapper around a rich text string diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java index 2c4e73b69..7b541fdf4 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java @@ -22,14 +22,11 @@ import static org.apache.poi.hslf.record.RecordTypes.*; import java.awt.Rectangle; import java.awt.font.FontRenderContext; import java.awt.geom.Rectangle2D; -import java.awt.geom.Rectangle2D.Double; import java.io.IOException; import java.util.*; -import org.apache.poi.POIXMLException; import org.apache.poi.ddf.*; import org.apache.poi.hslf.exceptions.HSLFException; -import org.apache.poi.hslf.model.textproperties.TextPropCollection; import org.apache.poi.hslf.record.*; import org.apache.poi.sl.draw.DrawFactory; import org.apache.poi.sl.draw.DrawTextShape; @@ -139,8 +136,12 @@ public abstract class HSLFTextShape extends HSLFSimpleShape implements TextShape protected void afterInsert(HSLFSheet sh){ super.afterInsert(sh); + storeText(); + EscherTextboxWrapper _txtbox = getEscherTextboxWrapper(); if(_txtbox != null){ + _escherContainer.addChildRecord(_txtbox.getEscherRecord()); + PPDrawing ppdrawing = sh.getPPDrawing(); ppdrawing.addTextboxWrapper(_txtbox); // Ensure the escher layer knows about the added records @@ -712,10 +713,10 @@ public abstract class HSLFTextShape extends HSLFSimpleShape implements TextShape * * @param text the text string used by this object. */ - public void appendText(String text, boolean newParagraph) { + public HSLFTextRun appendText(String text, boolean newParagraph) { // init paragraphs List paras = getTextParagraphs(); - HSLFTextParagraph.appendText(paras, text, newParagraph); + return HSLFTextParagraph.appendText(paras, text, newParagraph); } /** @@ -723,12 +724,15 @@ public abstract class HSLFTextShape extends HSLFSimpleShape implements TextShape * Uses the properties of the first paragraph / textrun * * @param text the text string used by this object. + * + * @return the last text run of the splitted text */ - public void setText(String text) { + public HSLFTextRun setText(String text) { // init paragraphs List paras = getTextParagraphs(); - HSLFTextParagraph.setText(paras, text); + HSLFTextRun htr = HSLFTextParagraph.setText(paras, text); setTextId(text.hashCode()); + return htr; } /** diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java b/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java index f0b4ff42b..247c7c184 100644 --- a/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java +++ b/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java @@ -104,8 +104,8 @@ public class DrawTextShape extends Iterable { * This may be specified in two different ways, percentage spacing and font point spacing: *

* If spaceBefore >= 0, then space is a percentage of normal line height. - * If spaceBefore < 0, the absolute value of linespacing is the spacing in points + * If spaceBefore < 0, the absolute value in points *

* * @return the vertical white space before the paragraph @@ -174,6 +174,29 @@ public interface TextParagraph extends Iterable { * @return the vertical line spacing. */ double getLineSpacing(); + + /** + * This element specifies the vertical line spacing that is to be used within a paragraph. + * This may be specified in two different ways, percentage spacing and font point spacing: + *

+ * If linespacing >= 0, then linespacing is a percentage of normal line height + * If linespacing < 0, the absolute value of linespacing is the spacing in points + *

+ * Examples: + *

+     *      // spacing will be 120% of the size of the largest text on each line
+     *      paragraph.setLineSpacing(120);
+     *
+     *      // spacing will be 200% of the size of the largest text on each line
+     *      paragraph.setLineSpacing(200);
+     *
+     *      // spacing will be 48 points
+     *      paragraph.setLineSpacing(-48.0);
+     * 
+ * + * @param linespacing the vertical line spacing + */ + void setLineSpacing(double lineSpacing); String getDefaultFontFamily(); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java index bf93fa79d..c807c2011 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java @@ -20,6 +20,8 @@ package org.apache.poi.hslf.model; import static org.junit.Assert.*; import java.awt.*; +import java.awt.geom.Rectangle2D; +import java.awt.geom.Rectangle2D.Double; import java.io.*; import java.util.ArrayList; import java.util.List; @@ -136,6 +138,48 @@ public final class TestShapes { } } + @Test + public void testParagraphs() throws Exception { + HSLFSlideShow ppt = new HSLFSlideShow(); + HSLFSlide slide = ppt.createSlide(); + HSLFTextBox shape = new HSLFTextBox(); + HSLFTextRun p1r1 = shape.setText("para 1 run 1. "); + HSLFTextRun p1r2 = shape.appendText("para 1 run 2.", false); + HSLFTextRun p2r1 = shape.appendText("para 2 run 1. ", true); + HSLFTextRun p2r2 = shape.appendText("para 2 run 2. ", false); + p1r1.setFontColor(Color.black); + p1r2.setFontColor(Color.red); + p2r1.setFontColor(Color.yellow); + p2r2.setStrikethrough(true); + // run 3 has same text properties as run 2 and will be merged when saving + HSLFTextRun p2r3 = shape.appendText("para 2 run 3.", false); + shape.setAnchor(new Rectangle2D.Double(100,100,100,10)); + slide.addShape(shape); + shape.resizeToFitText(); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ppt.write(bos); + + ppt = new HSLFSlideShow(new ByteArrayInputStream(bos.toByteArray())); + slide = ppt.getSlides().get(0); + HSLFTextBox tb = (HSLFTextBox)slide.getShapes().get(0); + List para = tb.getTextParagraphs(); + HSLFTextRun tr = para.get(0).getTextRuns().get(0); + assertEquals("para 1 run 1. ", tr.getRawText()); + assertEquals(Color.black, tr.getFontColor()); + tr = para.get(0).getTextRuns().get(1); + assertEquals("para 1 run 2.\r", tr.getRawText()); + assertEquals(Color.red, tr.getFontColor()); + tr = para.get(1).getTextRuns().get(0); + assertEquals("para 2 run 1. ", tr.getRawText()); + assertEquals(Color.yellow, tr.getFontColor()); + tr = para.get(1).getTextRuns().get(1); + assertEquals("para 2 run 2. para 2 run 3.", tr.getRawText()); + assertEquals(Color.black, tr.getFontColor()); + assertTrue(tr.isStrikethrough()); + } + + /** * Verify that we can add TextBox shapes to a slide * and set some of the style attributes @@ -235,7 +279,11 @@ public final class TestShapes { for (HSLFShape sh : sld.getShapes()) { if (sh instanceof HSLFTextShape){ HSLFTextShape tbox = (HSLFTextShape)sh; - lst2.add(tbox.getText()); + for (HSLFTextParagraph p : tbox.getTextParagraphs()) { + for (HSLFTextRun r : p) { + lst2.add(r.getRawText()); + } + } } } assertTrue(lst1.containsAll(lst2));