]> source.dussan.org Git - poi.git/commitdiff
fixed HSLF text handling
authorAndreas Beeker <kiwiwings@apache.org>
Sun, 3 May 2015 23:42:42 +0000 (23:42 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Sun, 3 May 2015 23:42:42 +0000 (23:42 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/common_sl@1677496 13f79535-47bb-0310-9956-ffa450edef68

src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/BitMaskTextProp.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java
src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java
src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java
src/scratchpad/src/org/apache/poi/sl/usermodel/TextParagraph.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java

index ca369ea9574e74076361e5f1b112ef8391908857..aef03197dd9c0c6ec18e80bdecf9fa4470c07ffa 100644 (file)
@@ -490,27 +490,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
         tabStops.addNewTab().setPos(Units.toEMU(value));\r
     }\r
 \r
-    /**\r
-     * This element specifies the vertical line spacing that is to be used within a paragraph.\r
-     * This may be specified in two different ways, percentage spacing and font point spacing:\r
-     * <p>\r
-     * If linespacing >= 0, then linespacing is a percentage of normal line height\r
-     * If linespacing < 0, the absolute value of linespacing is the spacing in points\r
-     * </p>\r
-     * Examples:\r
-     * <pre><code>\r
-     *      // spacing will be 120% of the size of the largest text on each line\r
-     *      paragraph.setLineSpacing(120);\r
-     *\r
-     *      // spacing will be 200% of the size of the largest text on each line\r
-     *      paragraph.setLineSpacing(200);\r
-     *\r
-     *      // spacing will be 48 points\r
-     *      paragraph.setLineSpacing(-48.0);\r
-     * </code></pre>\r
-     * \r
-     * @param linespacing the vertical line spacing\r
-     */\r
+    @Override\r
     public void setLineSpacing(double linespacing){\r
         CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
         CTTextSpacing spc = CTTextSpacing.Factory.newInstance();\r
@@ -519,16 +499,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
         pr.setLnSpc(spc);\r
     }\r
 \r
-    /**\r
-     * Returns the vertical line spacing that is to be used within a paragraph.\r
-     * This may be specified in two different ways, percentage spacing and font point spacing:\r
-     * <p>\r
-     * If linespacing >= 0, then linespacing is a percentage of normal line height.\r
-     * If linespacing < 0, the absolute value of linespacing is the spacing in points\r
-     * </p>\r
-     *\r
-     * @return the vertical line spacing.\r
-     */\r
+    @Override\r
     public double getLineSpacing(){\r
         ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){\r
             public boolean fetch(CTTextParagraphProperties props){\r
index 0f8bab418c9e825e4300e820e6cc6eed664ee688..e688f9959de0d098c03545b70af38f960e798e60 100644 (file)
@@ -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
index ac34bf1ffe1b4cdf086bc6389a3ab35b832b1f46..d856614eb3ebc3ce4855f296e2c333ca2cbdc8a0 100644 (file)
@@ -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<matches.length; i++) {
-                    bmt.setSubValue(matches[i], i);
-                }
-            }
+            TextProp tpCopy = (tp instanceof BitMaskTextProp)
+                ? ((BitMaskTextProp)tp).cloneAll()
+                : tp.clone();
             this.textPropList.add(tpCopy);
         }
        }
@@ -183,7 +178,7 @@ public class TextPropCollection {
        /**
         * Writes out to disk the header, and then all the properties
         */
-       public void writeOut(OutputStream o) throws IOException {
+       public void writeOut(OutputStream o, TextProp[] potentialProperties) throws IOException {
                // First goes the number of characters we affect
                StyleTextPropAtom.writeLittleEndian(charactersCovered,o);
 
@@ -194,10 +189,8 @@ public class TextPropCollection {
 
                // Then the mask field
                int mask = maskSpecial;
-               for(int i=0; i<textPropList.size(); i++) {
-                       TextProp textProp = textPropList.get(i);
+               for(TextProp textProp : textPropList) {
             //sometimes header indicates that the bitmask is present but its value is 0
-
             if (textProp instanceof BitMaskTextProp) {
                 if(mask == 0) mask |=  textProp.getWriteMask();
             }
@@ -208,14 +201,19 @@ public class TextPropCollection {
                StyleTextPropAtom.writeLittleEndian(mask,o);
 
                // Then the contents of all the properties
-               for(int i=0; i<textPropList.size(); i++) {
-                       TextProp textProp = textPropList.get(i);
-                       int val = textProp.getValue();
-                       if(textProp.getSize() == 2) {
-                               StyleTextPropAtom.writeLittleEndian((short)val,o);
-                       } else if(textProp.getSize() == 4){
-                               StyleTextPropAtom.writeLittleEndian(val,o);
-                       }
+               for (TextProp potProp : potentialProperties) {
+               for(TextProp textProp : textPropList) {
+                   if (!textProp.getName().equals(potProp.getName())) continue;
+                int val = textProp.getValue();
+                if (textProp instanceof BitMaskTextProp && val == 0) {
+                    // don't add empty properties, as they can't be recognized while reading
+                    continue;
+                } else if (textProp.getSize() == 2) {
+                               StyleTextPropAtom.writeLittleEndian((short)val,o);
+                       } else if (textProp.getSize() == 4) {
+                               StyleTextPropAtom.writeLittleEndian(val,o);
+                       }
+               }
                }
        }
 
index ad43c4dce8a111400e9f86caa1e752d310e77ed2..0e347446821962deb74b4489341cb81be0b6ad66 100644 (file)
 
 package org.apache.poi.hslf.record;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.LinkedList;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
 
 import org.apache.poi.hslf.model.textproperties.*;
-import org.apache.poi.util.HexDump;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.POILogger;
+import org.apache.poi.util.*;
 
 /**
  * A StyleTextPropAtom (type 4001). Holds basic character properties
@@ -64,26 +61,26 @@ public final class StyleTextPropAtom extends RecordAtom
      *  Characters the paragraph covers, and also contains the TextProps
      *  that actually define the styling of the paragraph.
      */
-    private LinkedList<TextPropCollection> paragraphStyles;
-    public LinkedList<TextPropCollection> getParagraphStyles() { return paragraphStyles; }
+    private List<TextPropCollection> paragraphStyles;
+    public List<TextPropCollection> getParagraphStyles() { return paragraphStyles; }
     /**
      * Updates the link list of TextPropCollections which make up the
      *  paragraph stylings
      */
-    public void setParagraphStyles(LinkedList<TextPropCollection> ps) { paragraphStyles = ps; }
+    public void setParagraphStyles(List<TextPropCollection> 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<TextPropCollection> charStyles;
-    public LinkedList<TextPropCollection> getCharacterStyles() { return charStyles; }
+    private List<TextPropCollection> charStyles;
+    public List<TextPropCollection> getCharacterStyles() { return charStyles; }
     /**
      * Updates the link list of TextPropCollections which make up the
      *  character stylings
      */
-    public void setCharacterStyles(LinkedList<TextPropCollection> cs) { charStyles = cs; }
+    public void setCharacterStyles(List<TextPropCollection> 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<TextPropCollection> styles) {
+    private int getCharactersCovered(List<TextPropCollection> 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<TextPropCollection>();
-        charStyles = new LinkedList<TextPropCollection>();
+        // Set empty lists, ready for when they call setParentTextSize
+        paragraphStyles = new ArrayList<TextPropCollection>();
+        charStyles = new ArrayList<TextPropCollection>();
     }
 
 
@@ -217,8 +214,8 @@ public final class StyleTextPropAtom extends RecordAtom
         LittleEndian.putInt(_header,4,10);
 
         // Set empty paragraph and character styles
-        paragraphStyles = new LinkedList<TextPropCollection>();
-        charStyles = new LinkedList<TextPropCollection>();
+        paragraphStyles = new ArrayList<TextPropCollection>();
+        charStyles = new ArrayList<TextPropCollection>();
 
         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<paragraphStyles.size(); i++) {
             TextPropCollection tpc = paragraphStyles.get(i);
-            tpc.writeOut(baos);
+            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);
+            tpc.writeOut(baos, characterTextPropTypes);
         }
 
         rawContents = baos.toByteArray();
@@ -454,7 +451,7 @@ public final class StyleTextPropAtom extends RecordAtom
 
                 try {
                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                    pr.writeOut(baos);
+                    pr.writeOut(baos, paragraphTextPropTypes);
                     byte[] b = baos.toByteArray();
                     out.append(HexDump.dump(b, 0, 0));
                 } catch (Exception e ) {
@@ -475,7 +472,7 @@ public final class StyleTextPropAtom extends RecordAtom
 
                 try {
                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                    pr.writeOut(baos);
+                    pr.writeOut(baos, characterTextPropTypes);
                     byte[] b = baos.toByteArray();
                     out.append(HexDump.dump(b, 0, 0));
                 } catch (Exception e ) {
index e5b704b777d10e01b2ee48322c1505802f411e8b..ddc6e130685362346a87e663e49c26948733b389 100644 (file)
@@ -270,7 +270,6 @@ public abstract class HSLFSheet implements Sheet<HSLFShape,HSLFSlideShow> {
      * @param shape
      */
     protected void onAddTextShape(HSLFTextShape shape) {
-
     }
 
     /**
index c6a1e12bd6822620e9644e782f09440d39cc7e8e..7d76efe8daa4a89ef8c8b3cdcec2b8b3132deac5 100644 (file)
@@ -553,30 +553,22 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
        return getParaTextPropVal("bullet.font");\r
    }\r
 \r
-   /**\r
-    * Sets the line spacing.\r
-    * <p>\r
-    * If linespacing >= 0, then linespacing is a percentage of normal line height.\r
-    * If linespacing < 0, the absolute value of linespacing is the spacing in master coordinates.\r
-    * </p>\r
-    */\r
-   public void setLineSpacing(int val) {\r
-       setParaTextPropVal("linespacing", val);\r
+   @Override\r
+   public void setLineSpacing(double lineSpacing) {\r
+       // if lineSpacing < 0, we need to convert points (common interface) to master units (hslf)\r
+       if (lineSpacing < 0) {\r
+           lineSpacing = (lineSpacing*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI);\r
+       }\r
+       setParaTextPropVal("linespacing", (int)lineSpacing);\r
    }\r
 \r
-   /**\r
-    * Returns the line spacing\r
-    * <p>\r
-    * If linespacing >= 0, then linespacing is a percentage of normal line height.\r
-    * If linespacing < 0, the absolute value of linespacing is the spacing in master coordinates.\r
-    * </p>\r
-    *\r
-    * @return the spacing between lines\r
-    */\r
    @Override\r
    public double getLineSpacing() {\r
-       int val = getParaTextPropVal("linespacing");\r
-       return val == -1 ? 0 : val;\r
+       double val = getParaTextPropVal("linespacing");\r
+       // if lineSpacing < 0, we need to convert master units (hslf) to points (common interface)\r
+       if (val == -1) return 0;\r
+       if (val < -1) val *= HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI);\r
+       return val;\r
    }\r
 \r
    /**\r
@@ -722,6 +714,12 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
                throw new RuntimeException("child record not found - malformed container record");\r
            }\r
            cr[idx] = newRecord;\r
+           \r
+           if (newRecord == byteAtom) {\r
+               charAtom = null;\r
+           } else {\r
+               byteAtom = null;\r
+           }\r
        }\r
 \r
        // Ensure a StyleTextPropAtom is present, adding if required\r
@@ -750,6 +748,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
            }\r
            for (HSLFTextRun tr : para.getTextRuns()) {\r
                TextPropCollection rtpc = tr.getCharacterStyle();\r
+               rtpc.updateTextSize(0);\r
                if (!rtpc.equals(lastRTPC)) {\r
                    lastRTPC = styleAtom.addCharacterTextPropCollection(0);\r
                    lastRTPC.copy(rtpc);\r
@@ -783,19 +782,43 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
     * \r
     * @param text the text string used by this object.\r
     */\r
-   protected static void appendText(List<HSLFTextParagraph> paragraphs, String text, boolean newParagraph) {\r
+   protected static HSLFTextRun appendText(List<HSLFTextParagraph> paragraphs, String text, boolean newParagraph) {\r
        text = toInternalString(text);\r
+\r
+       // check paragraphs\r
+       assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());\r
        \r
-       // init paragraphs\r
-       assert(!paragraphs.isEmpty());\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
-       HSLFTextParagraph lastHTP = paragraphs.get(paragraphs.size()-1);\r
-       HSLFTextRun lastHTR = lastHTP.getTextRuns().get(lastHTP.getTextRuns().size()-1);\r
-       HSLFTextParagraph htp = (newParagraph) ? new HSLFTextParagraph(lastHTP) : lastHTP;\r
-       HSLFTextRun htr = new HSLFTextRun(htp);\r
-       htr.setText(text);\r
-       htr.getCharacterStyle().copy(lastHTR.getCharacterStyle());\r
-       htp.addTextRun(htr);\r
+       boolean isFirst = !newParagraph;\r
+       for (String rawText : text.split("(?<=\r)")) {\r
+           if (!isFirst) {\r
+               TextPropCollection tpc = htp.getParagraphStyle();\r
+               HSLFTextParagraph prevHtp = htp;\r
+               htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom, htp._styleAtom);\r
+               htp.getParagraphStyle().copy(tpc);\r
+               htp.setParentShape(prevHtp.getParentShape());\r
+               htp.setShapeId(prevHtp.getShapeId());\r
+               htp.supplySheet(prevHtp.getSheet());\r
+               paragraphs.add(htp);\r
+               isFirst = false;\r
+           }\r
+           TextPropCollection tpc = htr.getCharacterStyle();\r
+           // special case, last text run is empty, we will reuse it\r
+           if (htr.getLength() > 0) {\r
+               htr = new HSLFTextRun(htp);\r
+               htr.getCharacterStyle().copy(tpc);\r
+               htp.addTextRun(htr);\r
+           }\r
+           htr.setText(rawText);\r
+       }\r
+       \r
+       return htr;\r
    }\r
    \r
    /**\r
@@ -804,29 +827,30 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
     * \r
     * @param text the text string used by this object.\r
     */\r
-   public static void setText(List<HSLFTextParagraph> paragraphs, String text) {\r
+   public static HSLFTextRun setText(List<HSLFTextParagraph> paragraphs, String text) {\r
        text = HSLFTextParagraph.toInternalString(text);\r
        \r
-       // init paragraphs\r
-       assert(!paragraphs.isEmpty());\r
+       // check paragraphs\r
+       assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());\r
 \r
        Iterator<HSLFTextParagraph> paraIter = paragraphs.iterator();\r
-       HSLFTextParagraph firstHTP = paraIter.next(); // keep first\r
-       assert(firstHTP != null);\r
+       HSLFTextParagraph htp = paraIter.next(); // keep first\r
+       assert(htp != null);\r
        while (paraIter.hasNext()) {\r
            paraIter.next();\r
            paraIter.remove();\r
        }\r
        \r
-       Iterator<HSLFTextRun> runIter = firstHTP.getTextRuns().iterator();\r
-       HSLFTextRun firstHTR = runIter.next();\r
-       assert(firstHTR != null);\r
+       Iterator<HSLFTextRun> runIter = htp.getTextRuns().iterator();\r
+       HSLFTextRun htr = runIter.next();\r
+       htr.setText("");\r
+       assert(htr != null);\r
        while (runIter.hasNext()) {\r
            runIter.next();\r
            runIter.remove();\r
        }\r
        \r
-       firstHTR.setText(text);\r
+       return appendText(paragraphs, text, false);\r
    }\r
    \r
    public static String getRawText(List<HSLFTextParagraph> paragraphs) {\r
@@ -835,9 +859,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
            for (HSLFTextRun r : p.getTextRuns()) {\r
                sb.append(r.getRawText());\r
            }\r
-           sb.append("\r");\r
        }\r
-       sb.deleteCharAt(sb.length()-1); // remove last line break\r
        return sb.toString();        \r
    }\r
    \r
@@ -947,7 +969,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
             return paragraphCollection;\r
         }\r
         \r
-        for (int slwtIndex = 0; recordIdx < records.length-2; slwtIndex++) {\r
+        for (int slwtIndex = 0; recordIdx < records.length; slwtIndex++) {\r
             List<HSLFTextParagraph> paragraphs = new ArrayList<HSLFTextParagraph>();\r
             paragraphCollection.add(paragraphs);\r
             \r
@@ -985,12 +1007,14 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
             \r
             if (tbytes == null && tchars == null) {\r
                 tbytes = new TextBytesAtom();\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
             String rawText = (tchars != null) ? tchars.getText() : tbytes.getText();\r
         \r
-            for (String para : rawText.split("\r")) {\r
+            // split, but keep delimiter\r
+            for (String para : rawText.split("(?<=\r)")) {\r
                 HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars, styles);\r
                 paragraphs.add(tpara);\r
                 tpara.setStyleTextProp9Atom(styleTextProp9Atom);\r
@@ -1021,29 +1045,21 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
 \r
     protected static void applyCharacterStyles(List<HSLFTextParagraph> paragraphs, List<TextPropCollection> charStyles) {\r
         int paraIdx = 0, runIdx = 0;\r
-        for (TextPropCollection p : charStyles) {\r
+        HSLFTextRun trun;\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
                 HSLFTextParagraph para = paragraphs.get(paraIdx);\r
                 List<HSLFTextRun> runs = para.getTextRuns();\r
-                HSLFTextRun trun = runs.get(runIdx);\r
+                trun = runs.get(runIdx);\r
                 int len = trun.getLength();\r
                 \r
-                if (runIdx+1 >= runs.size()) {\r
-                    // need to add +1 to the last run of the paragraph\r
-                    len++;\r
-                }\r
-                \r
-                TextPropCollection pCopy = new TextPropCollection(1);\r
-                pCopy.copy(p);\r
                 if (ccRun+len <= ccStyle) {\r
-                    trun.setCharacterStyle(pCopy);\r
-                    pCopy.updateTextSize(len);\r
                     ccRun += len;\r
                 } else {\r
                     String text = trun.getRawText();\r
                     trun.setText(text.substring(0,ccStyle-ccRun));\r
-                    pCopy.updateTextSize(ccStyle-ccRun);\r
-                    trun.setCharacterStyle(pCopy);\r
                     \r
                     HSLFTextRun nextRun = new HSLFTextRun(para);\r
                     nextRun.setText(text.substring(ccStyle-ccRun));\r
@@ -1052,8 +1068,27 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
                     ccRun += ccStyle-ccRun;\r
                 }\r
 \r
-                // need to compare it again, in case a run has been added afer\r
-                if (++runIdx >= runs.size()) {\r
+                TextPropCollection pCopy = new TextPropCollection(0);\r
+                pCopy.copy(p);\r
+                trun.setCharacterStyle(pCopy);\r
+\r
+                len = trun.getLength();\r
+                if (paraIdx == paragraphs.size()-1 && runIdx == runs.size()-1) {\r
+                    if (csIdx < charStyles.size()-1) {\r
+                        // special case, empty trailing text run\r
+                        HSLFTextRun nextRun = new HSLFTextRun(para);\r
+                        nextRun.setText("");\r
+                        runs.add(nextRun);\r
+                    } else {\r
+                        // need to add +1 to the last run of the last paragraph\r
+                        len++;\r
+                        ccRun++;\r
+                    }\r
+                }\r
+                pCopy.updateTextSize(len);\r
+                \r
+                // need to compare it again, in case a run has been added after\r
+                if (++runIdx == runs.size()) {\r
                     paraIdx++;\r
                     runIdx = 0;\r
                 }\r
@@ -1065,16 +1100,18 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
         int paraIdx = 0;\r
         for (TextPropCollection p : paraStyles) {\r
             for (int ccPara = 0, ccStyle = p.getCharactersCovered(); ccPara < ccStyle; paraIdx++) {\r
-                HSLFTextParagraph para = paragraphs.get(paraIdx);\r
-                TextPropCollection pCopy = new TextPropCollection(1);\r
+                if (paraIdx >= paragraphs.size() || ccPara >= ccStyle-1) return;\r
+                HSLFTextParagraph htp = paragraphs.get(paraIdx);\r
+                TextPropCollection pCopy = new TextPropCollection(0);\r
                 pCopy.copy(p);\r
+                htp.setParagraphStyle(pCopy);\r
                 int len = 0;\r
-                for (HSLFTextRun trun : para.getTextRuns()) {\r
+                for (HSLFTextRun trun : htp.getTextRuns()) {\r
                     len += trun.getLength();\r
                 }\r
-                pCopy.updateTextSize(len+1);\r
-                para.setParagraphStyle(pCopy);\r
-                ccPara += len+1;\r
+                if (paraIdx == paragraphs.size()-1) len++;\r
+                pCopy.updateTextSize(len);\r
+                ccPara += len;\r
             }\r
         }\r
     }\r
@@ -1101,15 +1138,28 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
         tha.setParentRecord(wrapper);\r
         wrapper.appendChildRecord(tha);\r
     \r
-        TextCharsAtom tca = new TextCharsAtom();\r
-        wrapper.appendChildRecord(tca);\r
+        TextBytesAtom tba = new TextBytesAtom();\r
+        tba.setText("\r".getBytes());\r
+        wrapper.appendChildRecord(tba);\r
     \r
-        StyleTextPropAtom sta = new StyleTextPropAtom(0);\r
+        StyleTextPropAtom sta = new StyleTextPropAtom(1);\r
+        TextPropCollection paraStyle = sta.addParagraphTextPropCollection(1);\r
+        TextPropCollection charStyle = sta.addCharacterTextPropCollection(1);\r
         wrapper.appendChildRecord(sta);\r
     \r
-        HSLFTextParagraph htp = new HSLFTextParagraph(tha, null, tca, sta);\r
+        HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null, sta);\r
+        htp.setParagraphStyle(paraStyle);\r
         htp._records = new Record[0];\r
+        htp.setBullet(false);\r
+        htp.setLineSpacing(100);\r
+        htp.setLeftMargin(0);\r
+        htp.setIndent(0);\r
+        // set wrap flags\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
         return Arrays.asList(htp);\r
index 9a10a9b125d311310f89ef0dd1e584e9f863b321..e94c8446491263fabc55cdaad3c3195370d3a991 100644 (file)
@@ -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
index 2c4e73b69053110233e0c74ab77384724bb42421..7b541fdf450400b03adae6bb7d1c59acbd76cf0b 100644 (file)
@@ -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<HSLFTextParagraph> 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<HSLFTextParagraph> paras = getTextParagraphs();
-          HSLFTextParagraph.setText(paras, text);
+          HSLFTextRun htr = HSLFTextParagraph.setText(paras, text);
           setTextId(text.hashCode());
+          return htr;
       }
       
       /**
index f0b4ff42b345ac3c898414ca2db57df753e9ede7..247c7c1845b2454eb3f1399fc265cd788503f867 100644 (file)
@@ -104,8 +104,8 @@ public class DrawTextShape<T extends TextShape<? extends TextParagraph<? extends
                     // negative value means the absolute spacing in points\r
                     y += -spaceBefore;\r
                 }\r
-                isFirstLine = false;\r
             }\r
+            isFirstLine = false;\r
             \r
             dp.setPosition(x, y);\r
             dp.draw(graphics);\r
index 102dbae2bcc61253dcaed07e3a9b70e1167bfbae..14eb03c842d0a06c32b3b851430db84e79fb64bb 100644 (file)
@@ -113,7 +113,7 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
      * This may be specified in two different ways, percentage spacing and font point spacing:\r
      * <p>\r
      * If spaceBefore >= 0, then space is a percentage of normal line height.\r
-     * If spaceBefore < 0, the absolute value of linespacing is the spacing in points\r
+     * If spaceBefore < 0, the absolute value in points\r
      * </p>\r
      *\r
      * @return the vertical white space before the paragraph\r
@@ -174,6 +174,29 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
      * @return the vertical line spacing.\r
      */\r
     double getLineSpacing();\r
+    \r
+    /**\r
+     * This element specifies the vertical line spacing that is to be used within a paragraph.\r
+     * This may be specified in two different ways, percentage spacing and font point spacing:\r
+     * <p>\r
+     * If linespacing >= 0, then linespacing is a percentage of normal line height\r
+     * If linespacing < 0, the absolute value of linespacing is the spacing in points\r
+     * </p>\r
+     * Examples:\r
+     * <pre><code>\r
+     *      // spacing will be 120% of the size of the largest text on each line\r
+     *      paragraph.setLineSpacing(120);\r
+     *\r
+     *      // spacing will be 200% of the size of the largest text on each line\r
+     *      paragraph.setLineSpacing(200);\r
+     *\r
+     *      // spacing will be 48 points\r
+     *      paragraph.setLineSpacing(-48.0);\r
+     * </code></pre>\r
+     * \r
+     * @param linespacing the vertical line spacing\r
+     */\r
+    void setLineSpacing(double lineSpacing);\r
 \r
     String getDefaultFontFamily();\r
     \r
index bf93fa79d3f69c39023c8986a22d44ed895957c7..c807c201160c6e0bdddcbbb402d8c2cadf91e7a2 100644 (file)
@@ -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<HSLFTextParagraph> 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));