]> source.dussan.org Git - poi.git/commitdiff
TextShape is a common superclass of all shapes that can hold text. The subclasses...
authorYegor Kozlov <yegor@apache.org>
Tue, 15 Apr 2008 07:54:20 +0000 (07:54 +0000)
committerYegor Kozlov <yegor@apache.org>
Tue, 15 Apr 2008 07:54:20 +0000 (07:54 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@648156 13f79535-47bb-0310-9956-ffa450edef68

16 files changed:
src/scratchpad/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java
src/scratchpad/src/org/apache/poi/hslf/model/AutoShape.java
src/scratchpad/src/org/apache/poi/hslf/model/Line.java
src/scratchpad/src/org/apache/poi/hslf/model/Picture.java
src/scratchpad/src/org/apache/poi/hslf/model/Placeholder.java
src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java
src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java
src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java
src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java
src/scratchpad/src/org/apache/poi/hslf/model/TableCell.java
src/scratchpad/src/org/apache/poi/hslf/model/TextBox.java
src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/data/text_shapes.ppt [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestTextShape.java [new file with mode: 0755]
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java

index 0f28c2a8c921df2085799870490ee67419042506..962349876a0736f1a7a6676fef4774c6b2441964 100755 (executable)
@@ -459,6 +459,7 @@ public class ApacheconEU08 {
         TextBox box2 = new TextBox();\r
         TextRun tr2 = box2.getTextRun();\r
         tr2.setRunType(TextHeaderAtom.BODY_TYPE);\r
+        tr2.getRichTextRuns()[0].setFontSize(32);\r
         tr2.setText(\r
                 "Support for more PowerPoint functionality\r" +\r
                 "Rendering slides into java.awt.Graphics2D");\r
@@ -477,6 +478,7 @@ public class ApacheconEU08 {
         TextBox box4 = new TextBox();\r
         TextRun tr4 = box4.getTextRun();\r
         tr4.setRunType(TextHeaderAtom.BODY_TYPE);\r
+        tr4.getRichTextRuns()[0].setFontSize(32);\r
         tr4.setText(\r
                 "Integration with Apache FOP - Formatting Objects Processor");\r
         box4.setAnchor(new Rectangle(36, 290, 648, 90));\r
index b3ddce37da7961be68a97bdae606324e2671e422..cf65a9b13cb2cf3007cab12b461a81f71925f08f 100644 (file)
@@ -20,11 +20,15 @@ package org.apache.poi.hslf.model;
 import org.apache.poi.ddf.*;
 
 /**
- * Represents a autoshape in a PowerPoint drawing
+ * Represents an AutoShape.
+ * <p>
+ * AutoShapes are drawing objects with a particular shape that may be customized through smart resizing and adjustments.
+ * See {@link ShapeTypes}
+ * </p>
  *
  *  @author Yegor Kozlov
  */
-public class AutoShape extends SimpleShape {
+public class AutoShape extends TextShape {
 
     protected AutoShape(EscherContainerRecord escherRecord, Shape parent){
         super(escherRecord, parent);
@@ -40,23 +44,62 @@ public class AutoShape extends SimpleShape {
     }
 
     protected EscherContainerRecord createSpContainer(int shapeType, boolean isChild){
-        EscherContainerRecord spcont = super.createSpContainer(isChild);
+        _escherContainer = super.createSpContainer(isChild);
 
-        EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID);
-        short type = (short)((shapeType << 4) | 0x2);
-        spRecord.setOptions(type);
+        setShapeType(shapeType);
 
         //set default properties for an autoshape
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(spcont, EscherOptRecord.RECORD_ID);
+        setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x40000);
+        setEscherProperty(EscherProperties.FILL__FILLCOLOR, 0x8000004);
+        setEscherProperty(EscherProperties.FILL__FILLCOLOR, 0x8000004);
+        setEscherProperty(EscherProperties.FILL__FILLBACKCOLOR, 0x8000000);
+        setEscherProperty(EscherProperties.FILL__NOFILLHITTEST, 0x100010);
+        setEscherProperty(EscherProperties.LINESTYLE__COLOR, 0x8000001);
+        setEscherProperty(EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x80008);
+        setEscherProperty(EscherProperties.SHADOWSTYLE__COLOR, 0x8000002);
 
-        opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.FILL__FILLCOLOR, 0x8000004));
-        opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.FILL__FILLBACKCOLOR, 0x8000000));
-        opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.FILL__NOFILLHITTEST, 0x100010));
-        opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__COLOR, 0x8000001));
-        opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x80008));
-        opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.SHADOWSTYLE__COLOR, 0x8000002));
+        return _escherContainer;
+    }
+
+    protected void setDefaultTextProperties(TextRun _txtrun){
+        setVerticalAlignment(TextBox.AnchorMiddle);
+        setHorizontalAlignment(TextBox.AlignCenter);
+        setWordWrap(TextBox.WrapNone);
+    }
 
-        return spcont;
+    /**
+     * Gets adjust value which controls smart resizing of the auto-shape.
+     *
+     * <p>
+     * The adjustment values are given in shape coordinates:
+     * the origin is at the top-left, positive-x is to the right, positive-y is down.
+     * The region from (0,0) to (S,S) maps to the geometry box of the shape (S=21600 is a constant).
+     * </p>
+     *
+     * @param idx the adjust index in the [0, 9] range
+     * @return the adjustment value
+     */
+    public int getAdjustmentValue(int idx){
+        if(idx < 0 || idx > 9) throw new IllegalArgumentException("The index of an adjustment value must be in the [0, 9] range");
+
+        return getEscherProperty((short)(EscherProperties.GEOMETRY__ADJUSTVALUE + idx));
     }
 
+    /**
+     * Sets adjust value which controls smart resizing of the auto-shape.
+     *
+     * <p>
+     * The adjustment values are given in shape coordinates:
+     * the origin is at the top-left, positive-x is to the right, positive-y is down.
+     * The region from (0,0) to (S,S) maps to the geometry box of the shape (S=21600 is a constant).
+     * </p>
+     *
+     * @param idx the adjust index in the [0, 9] range
+     * @param val the adjustment value
+     */
+    public void setAdjustmentValue(int idx, int val){
+        if(idx < 0 || idx > 9) throw new IllegalArgumentException("The index of an adjustment value must be in the [0, 9] range");
+
+        setEscherProperty((short)(EscherProperties.GEOMETRY__ADJUSTVALUE + idx), val);
+    }
 }
index 0d3f616e929352895d6cd9b31d2b118fcb9f92ce..9237183689aef981e9985767d16b3a0a08949d49 100644 (file)
@@ -106,14 +106,14 @@ public class Line extends SimpleShape {
     }
 
     protected EscherContainerRecord createSpContainer(boolean isChild){
-        EscherContainerRecord spcont = super.createSpContainer(isChild);
+        _escherContainer = super.createSpContainer(isChild);
 
-        EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID);
+        EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
         short type = (ShapeTypes.Line << 4) | 0x2;
         spRecord.setOptions(type);
   
         //set default properties for a line
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(spcont, EscherOptRecord.RECORD_ID);
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
 
         //default line properties
         setEscherProperty(opt, EscherProperties.GEOMETRY__SHAPEPATH, 4);
@@ -123,7 +123,7 @@ public class Line extends SimpleShape {
         setEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0xA0008);
         setEscherProperty(opt, EscherProperties.SHADOWSTYLE__COLOR, 0x8000002);
 
-        return spcont;
+        return _escherContainer;
     }
 
 }
index 90efd5f3ee5a1f41348ede304f5097b43861fc97..910d5c850344e79d12dc4aa68cd65e5bcaeb68b7 100644 (file)
@@ -120,20 +120,20 @@ public class Picture extends SimpleShape {
      * @return the create Picture object
      */
     protected EscherContainerRecord createSpContainer(int idx, boolean isChild) {
-        EscherContainerRecord spContainer = super.createSpContainer(isChild);
-        spContainer.setOptions((short)15);
+        _escherContainer = super.createSpContainer(isChild);
+        _escherContainer.setOptions((short)15);
 
-        EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID);
+        EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
         spRecord.setOptions((short)((ShapeTypes.PictureFrame << 4) | 0x2));
 
         //set default properties for a picture
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(spContainer, EscherOptRecord.RECORD_ID);
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
         setEscherProperty(opt, EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 8388736);
 
         //another weird feature of powerpoint: for picture id we must add 0x4000.
         setEscherProperty(opt, (short)(EscherProperties.BLIP__BLIPTODISPLAY + 0x4000), idx);
 
-        return spContainer;
+        return _escherContainer;
     }
 
     /**
index dd0132ebe0926c610ae229cd34c3ff647468b779..f0bc74558fe3d28cfc46c29335c88f9207a7b949 100644 (file)
@@ -49,15 +49,15 @@ public class Placeholder extends TextBox {
      * @return the created <code>EscherContainerRecord</code> which holds shape data
      */
     protected EscherContainerRecord createSpContainer(boolean isChild){
-        EscherContainerRecord spcont = super.createSpContainer(isChild);
+        _escherContainer = super.createSpContainer(isChild);
 
-        EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID);
+        EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID);
         spRecord.setFlags(EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HAVEMASTER);
 
         EscherClientDataRecord cldata = new EscherClientDataRecord();
         cldata.setOptions((short)15);
 
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(spcont, EscherOptRecord.RECORD_ID);
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
 
         //Placeholders can't be grouped
         setEscherProperty(opt, EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 262144);
@@ -86,7 +86,7 @@ public class Placeholder extends TextBox {
         cldata.setRemainingData(out.toByteArray());
 
         //append placeholder container before EscherTextboxRecord
-        List lst = spcont.getChildRecords();
+        List lst = _escherContainer.getChildRecords();
         for (int i = 0; i < lst.size(); i++) {
               EscherRecord rec = (EscherRecord)lst.get(i);
               if(rec.getRecordId() == EscherTextboxRecord.RECORD_ID){
@@ -94,6 +94,6 @@ public class Placeholder extends TextBox {
               }
         }
 
-        return spcont;
+        return _escherContainer;
     }
 }
index 66c95e50cf4c046dfcf52d6969ec80bd514573c4..0c36bf053a3ac40ff28deb46b57e3bee3dd552ce 100644 (file)
@@ -39,12 +39,7 @@ public class ShapeFactory {
         int type = spRecord.getOptions() >> 4;
         switch (type){
             case ShapeTypes.TextBox:
-            case ShapeTypes.Rectangle:
-                EscherTextboxRecord txtbox = (EscherTextboxRecord)Shape.getEscherChild(spContainer, EscherTextboxRecord.RECORD_ID);
-                if (txtbox == null)
-                    shape = new AutoShape(spContainer, parent);
-                else
-                    shape = new TextBox(spContainer, parent);
+                shape = new TextBox(spContainer, parent);
                 break;
             case ShapeTypes.PictureFrame:
                 shape = new Picture(spContainer, parent);
@@ -54,9 +49,13 @@ public class ShapeFactory {
                 break;
             case ShapeTypes.NotPrimitive:
                 if ((spRecord.getFlags() & EscherSpRecord.FLAG_GROUP) != 0)
-                     shape = new ShapeGroup(spContainer, parent);
-                else
+                    //TODO: check if the shape group is a Table 
+                    shape = new ShapeGroup(spContainer, parent);
+                else {
+                    //TODO: check if the shape has GEOMETRY__VERTICES or GEOMETRY__SEGMENTINFO properties.
+                    //if it does, then return Freeform or Polygon
                     shape = new AutoShape(spContainer, parent);
+                }
                 break;
             default:
                 shape = new AutoShape(spContainer, parent);
index dbcc8069c24e04a2702f9858c848f0aeb55055f8..b22ed8d70b1222f0041b2e6a257cb6439383780a 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.poi.hslf.model;
 import org.apache.poi.ddf.*;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.POILogger;
+import org.apache.poi.hslf.record.EscherTextboxWrapper;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -154,9 +155,10 @@ public class ShapeGroup extends Shape{
         shape.setSheet(sheet);
         shape.afterInsert(sheet);
 
-        if(shape instanceof TextBox) {
-            TextBox tbox = (TextBox)shape;
-            getSheet().getPPDrawing().addTextboxWrapper(tbox._txtbox);
+        if (shape instanceof TextShape) {
+            TextShape tbox = (TextShape) shape;
+            EscherTextboxWrapper txWrapper = tbox.getEscherTextboxWrapper();
+            if(txWrapper != null) getSheet().getPPDrawing().addTextboxWrapper(txWrapper);
         }
     }
 
index 135b625f1d7ba7640ebfb7805cec41fe0bfcdb5c..abf2fec5890050779205c21f3edfc7627a46b51f 100644 (file)
@@ -257,11 +257,12 @@ public abstract class Sheet {
         shape.setSheet(this);
         shape.afterInsert(this);
 
-        // If it's a TextBox, we need to tell the PPDrawing, as it has to
+        // If it's a TextShape, we need to tell the PPDrawing, as it has to
         //  track TextboxWrappers specially
-        if (shape instanceof TextBox) {
-            TextBox tbox = (TextBox) shape;
-            ppdrawing.addTextboxWrapper(tbox._txtbox);
+        if (shape instanceof TextShape) {
+            TextShape tbox = (TextShape) shape;
+            EscherTextboxWrapper txWrapper = tbox.getEscherTextboxWrapper();
+            if(txWrapper != null) ppdrawing.addTextboxWrapper(txWrapper);
         }
     }
 
index 85d672fe899aa2757ea020529a04a6a4adfa825a..0831d453b5925b35de5f838cc6b606203051bb4f 100644 (file)
@@ -48,19 +48,19 @@ public class SimpleShape extends Shape {
      * @return the record container which holds this shape
      */
     protected EscherContainerRecord createSpContainer(boolean isChild) {
-        EscherContainerRecord spContainer = new EscherContainerRecord();
-        spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
-        spContainer.setOptions((short)15);
+        _escherContainer = new EscherContainerRecord();
+        _escherContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
+        _escherContainer.setOptions((short)15);
 
         EscherSpRecord sp = new EscherSpRecord();
         int flags = EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE;
         if (isChild) flags |= EscherSpRecord.FLAG_CHILD;
         sp.setFlags(flags);
-        spContainer.addChildRecord(sp);
+        _escherContainer.addChildRecord(sp);
 
         EscherOptRecord opt = new EscherOptRecord();
         opt.setRecordId(EscherOptRecord.RECORD_ID);
-        spContainer.addChildRecord(opt);
+        _escherContainer.addChildRecord(opt);
 
         EscherRecord anchor;
         if(isChild) anchor = new EscherChildAnchorRecord();
@@ -75,9 +75,9 @@ public class SimpleShape extends Shape {
             LittleEndian.putInt(header, 4, 8);
             anchor.fillFields(header, 0, null);
         }
-        spContainer.addChildRecord(anchor);
+        _escherContainer.addChildRecord(anchor);
 
-        return spContainer;
+        return _escherContainer;
     }
 
     /**
index bb93e06b7154f5e31ea7b371b8b8eb393f71af23..36789ad91a2f0462c52b0e95311c9c84e1d400dc 100755 (executable)
@@ -63,15 +63,15 @@ public class TableCell extends TextBox {
     }\r
 \r
     protected EscherContainerRecord createSpContainer(boolean isChild){\r
-        EscherContainerRecord spContainer = super.createSpContainer(isChild);\r
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(spContainer, EscherOptRecord.RECORD_ID);\r
+        _escherContainer = super.createSpContainer(isChild);\r
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
         setEscherProperty(opt, EscherProperties.TEXT__TEXTID, 0);\r
         setEscherProperty(opt, EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x20000);\r
         setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x150001);\r
         setEscherProperty(opt, EscherProperties.SHADOWSTYLE__SHADOWOBSURED, 0x20000);\r
         setEscherProperty(opt, EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x40000);\r
 \r
-        return spContainer;\r
+        return _escherContainer;\r
     }\r
 \r
     protected void anchorBorder(int type, Line line){\r
index 1f9a489a78be29eb2b08babd6e1922e6a487a5f0..a1d45ffbaed9ad44678541b769249b2cfedef6e2 100644 (file)
@@ -38,55 +38,7 @@ import java.io.IOException;
  *
  * @author Yegor Kozlov
  */
-public class TextBox extends SimpleShape {
-
-    /**
-     * How to anchor the text
-     */
-    public static final int AnchorTop = 0;
-    public static final int AnchorMiddle = 1;
-    public static final int AnchorBottom = 2;
-    public static final int AnchorTopCentered = 3;
-    public static final int AnchorMiddleCentered = 4;
-    public static final int AnchorBottomCentered = 5;
-    public static final int AnchorTopBaseline = 6;
-    public static final int AnchorBottomBaseline = 7;
-    public static final int AnchorTopCenteredBaseline = 8;
-    public static final int AnchorBottomCenteredBaseline = 9;
-
-    /**
-     * How to wrap the text
-     */
-    public static final int WrapSquare = 0;
-    public static final int WrapByPoints = 1;
-    public static final int WrapNone = 2;
-    public static final int WrapTopBottom = 3;
-    public static final int WrapThrough = 4;
-
-    /**
-     * How to align the text
-     */
-    public static final int AlignLeft = 0;
-    public static final int AlignCenter = 1;
-    public static final int AlignRight = 2;
-    public static final int AlignJustify = 3;
-
-    /**
-     * Low-level object which holds actual text and format data
-     */
-    protected TextRun _txtrun;
-
-    /**
-     * Escher container which holds text attributes such as
-     * TextHeaderAtom, TextBytesAtom ot TextCharsAtom, StyleTextPropAtom etc.
-     */
-    protected EscherTextboxWrapper _txtbox;
-    
-    /**
-     * Is the TextBox missing the text records which actually
-     *  store the text?
-     */
-    private boolean _missingTextRecords = false;
+public class TextBox extends TextShape {
 
     /**
      * Create a TextBox object and initialize it from the supplied Record container.
@@ -97,8 +49,6 @@ public class TextBox extends SimpleShape {
    protected TextBox(EscherContainerRecord escherRecord, Shape parent){
         super(escherRecord, parent);
 
-        EscherTextboxRecord textbox = (EscherTextboxRecord)Shape.getEscherChild(_escherContainer, EscherTextboxRecord.RECORD_ID);
-        _txtbox = new EscherTextboxWrapper(textbox);
     }
 
     /**
@@ -108,8 +58,7 @@ public class TextBox extends SimpleShape {
      * in a table then the parent is Table.
      */
     public TextBox(Shape parent){
-        super(null, parent);
-        _escherContainer = createSpContainer(parent instanceof ShapeGroup);
+        super(parent);
     }
 
     /**
@@ -121,376 +70,31 @@ public class TextBox extends SimpleShape {
     }
 
     /**
-     * Create a new textBox and initialize internal structures
+     * Create a new TextBox and initialize its internal structures
      *
      * @return the created <code>EscherContainerRecord</code> which holds shape data
      */
     protected EscherContainerRecord createSpContainer(boolean isChild){
-        EscherContainerRecord spcont = super.createSpContainer(isChild);
-
-        EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID);
-        short type = (ShapeTypes.TextBox << 4) | 0x2;
-        spRecord.setOptions(type);
-
-        //set default properties for a textbox
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(spcont, EscherOptRecord.RECORD_ID);
-        setEscherProperty(opt, EscherProperties.TEXT__TEXTID, 0);
-
-        setEscherProperty(opt, EscherProperties.FILL__FILLCOLOR, 0x8000004);
-        setEscherProperty(opt, EscherProperties.FILL__FILLBACKCOLOR, 0x8000000);
-        setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x100000);
-        setEscherProperty(opt, EscherProperties.LINESTYLE__COLOR, 0x8000001);
-        setEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x80000);
-        setEscherProperty(opt, EscherProperties.SHADOWSTYLE__COLOR, 0x8000002);
-
-        //create EscherTextboxWrapper
-        _txtbox = new EscherTextboxWrapper();
-
-        TextHeaderAtom tha = new TextHeaderAtom();
-        tha.setParentRecord(_txtbox); // TextHeaderAtom is parent aware
-        _txtbox.appendChildRecord(tha);
-
-        TextCharsAtom tca = new TextCharsAtom();
-        _txtbox.appendChildRecord(tca);
-
-        StyleTextPropAtom sta = new StyleTextPropAtom(0);
-        _txtbox.appendChildRecord(sta);
-
-        _txtrun = new TextRun(tha,tca,sta);
-        _txtrun.setText("");
-        spcont.addChildRecord(_txtbox.getEscherRecord());
-
-        return spcont;
-    }
-
-    /**
-     * Returns the text contained in this text frame.
-     *
-     * @return the text string for this textbox.
-     */
-     public String getText(){
-        return _txtrun == null ? null : _txtrun.getText();
-    }
-
-    /**
-     * Sets the text contained in this text frame.
-     *
-     * @param text the text string used by this object.
-     */
-    public void setText(String text){
-        _txtrun.setText(text);
-    }
-
-    /**
-     * When a textbox is added to  a sheet we need to tell upper-level
-     * <code>PPDrawing</code> about it.
-     *
-     * @param sh the sheet we are adding to
-     */
-    protected void afterInsert(Sheet sh){
-        PPDrawing ppdrawing = sh.getPPDrawing();
-        ppdrawing.addTextboxWrapper(_txtbox);
-        // Ensure the escher layer knows about the added records 
-        try {
-            _txtbox.writeOut(null);
-        } catch (IOException e){
-            throw new HSLFException(e);
-        }
-        if(getAnchor().equals(new java.awt.Rectangle()) && !"".equals(getText())) resizeToFitText();
-    }
-
-    /**
-     * Adjust the size of the TextBox so it encompasses the text inside it.
-     */
-    public void resizeToFitText(){
-        try{
-        FontRenderContext frc = new FontRenderContext(null, true, true);
-        RichTextRun rt = _txtrun.getRichTextRuns()[0];
-        int size = rt.getFontSize();
-        int style = 0;
-        if (rt.isBold()) style |= Font.BOLD;
-        if (rt.isItalic()) style |= Font.ITALIC;
-        String fntname = rt.getFontName();
-        Font font = new Font(fntname, style, size);
-
-        TextLayout layout = new TextLayout(getText(), font, frc);
-        int width = Math.round(layout.getAdvance());
-        int height = Math.round(layout.getAscent());
-
-        Dimension txsize = new Dimension(width, height);
-        java.awt.Rectangle anchor = getAnchor();
-        anchor.setSize(txsize);
-        setAnchor(anchor);
-        } catch (Exception e){
-            e.printStackTrace();
-
-        }
-    }
-
-    /**
-     * Returns the type of vertical alignment for the text.
-     * One of the <code>Anchor*</code> constants defined in this class.
-     *
-     * @return the type of alignment
-     */
-    public int getVerticalAlignment(){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT);
-        int valign;
-        if (prop == null){
-            int type = getTextRun().getRunType();
-            switch (type){
-                case TextHeaderAtom.TITLE_TYPE:
-                case TextHeaderAtom.CENTER_TITLE_TYPE:
-                    valign = TextBox.AnchorMiddle;
-                    break;
-                default:
-                    valign = TextBox.AnchorTop;
-                    break;
-            }
-        } else {
-            valign = prop.getPropertyValue();
-        }
-        return valign;
-    }
-
-    /**
-     * Sets the type of vertical alignment for the text.
-     * One of the <code>Anchor*</code> constants defined in this class.
-     *
-     * @param align - the type of alignment
-     */
-    public void setVerticalAlignment(int align){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        setEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT, align);
-    }
-
-    public void setHorizontalAlignment(int align){
-        _txtrun.getRichTextRuns()[0].setAlignment(align);
-    }
-    public int getHorizontalAlignment(){
-        return _txtrun.getRichTextRuns()[0].getAlignment();
-    }
-
-    /**
-     * Returns the distance (in points) between the bottom of the text frame
-     * and the bottom of the inscribed rectangle of the shape that contains the text.
-     * Default value is 1/20 inch.
-     *
-     * @return the botom margin
-     */
-    public int getMarginBottom(){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTBOTTOM);
-        int val = prop == null ? EMU_PER_INCH/20 : prop.getPropertyValue();
-        return val/EMU_PER_POINT;
-    }
-
-    /**
-     * Sets the botom margin.
-     * @see #getMarginBottom()
-     *
-     * @param margin    the bottom margin
-     */
-    public void setMarginBottom(int margin){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        setEscherProperty(opt, EscherProperties.TEXT__TEXTBOTTOM, margin*EMU_PER_POINT);
-    }
-
-    /**
-     *  Returns the distance (in EMUs) between the left edge of the text frame
-     *  and the left edge of the inscribed rectangle of the shape that contains
-     *  the text.
-     *  Default value is 1/10 inch.
-     *
-     * @return the left margin
-     */
-    public int getMarginLeft(){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTBOTTOM);
-        int val = prop == null ? EMU_PER_INCH/10 : prop.getPropertyValue();
-        return val/EMU_PER_POINT;
-    }
+        _escherContainer = super.createSpContainer(isChild);
 
-    /**
-     * Sets the left margin.
-     * @see #getMarginLeft()
-     *
-     * @param margin    the left margin
-     */
-    public void setMarginLeft(int margin){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        setEscherProperty(opt, EscherProperties.TEXT__TEXTLEFT, margin*EMU_PER_POINT);
-    }
+        setShapeType(ShapeTypes.TextBox);
 
-    /**
-     *  Returns the distance (in EMUs) between the right edge of the
-     *  text frame and the right edge of the inscribed rectangle of the shape
-     *  that contains the text.
-     *  Default value is 1/10 inch.
-     *
-     * @return the right margin
-     */
-    public int getMarginRight(){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTRIGHT);
-        int val = prop == null ? EMU_PER_INCH/10 : prop.getPropertyValue();
-        return val/EMU_PER_POINT;
-    }
+        //set default properties for a TextBox
+        setEscherProperty(EscherProperties.FILL__FILLCOLOR, 0x8000004);
+        setEscherProperty(EscherProperties.FILL__FILLBACKCOLOR, 0x8000000);
+        setEscherProperty(EscherProperties.FILL__NOFILLHITTEST, 0x100000);
+        setEscherProperty(EscherProperties.LINESTYLE__COLOR, 0x8000001);
+        setEscherProperty(EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x80000);
+        setEscherProperty(EscherProperties.SHADOWSTYLE__COLOR, 0x8000002);
 
-    /**
-     * Sets the right margin.
-     * @see #getMarginRight()
-     *
-     * @param margin    the right margin
-     */
-    public void setMarginRight(int margin){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        setEscherProperty(opt, EscherProperties.TEXT__TEXTRIGHT, margin*EMU_PER_POINT);
-    }
+        _txtrun = createTextRun();
 
-     /**
-     *  Returns the distance (in EMUs) between the top of the text frame
-     *  and the top of the inscribed rectangle of the shape that contains the text.
-     *  Default value is 1/20 inch.
-     *
-     * @return the top margin
-     */
-    public int getMarginTop(){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTTOP);
-        int val = prop == null ? EMU_PER_INCH/20 : prop.getPropertyValue();
-        return val/EMU_PER_POINT;
+        return _escherContainer;
     }
 
-   /**
-     * Sets the top margin.
-     * @see #getMarginTop()
-     *
-     * @param margin    the top margin
-     */
-    public void setMarginTop(int margin){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        setEscherProperty(opt, EscherProperties.TEXT__TEXTTOP, margin*EMU_PER_POINT);
+    protected void setDefaultTextProperties(TextRun _txtrun){
+        setVerticalAlignment(TextBox.AnchorTop);
+        setEscherProperty(EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x20002);
     }
 
-
-    /**
-     * Returns the value indicating word wrap.
-     * One of the <code>Wrap*</code> constants defined in this class.
-     *
-     * @return the value indicating word wrap
-     */
-    public int getWordWrap(){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__WRAPTEXT);
-        return prop == null ? WrapSquare : prop.getPropertyValue();
-    }
-
-    /**
-     *  Specifies how the text should be wrapped
-     *
-     * @param wrap  the value indicating how the text should be wrapped
-     */
-    public void setWordWrap(int wrap){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        setEscherProperty(opt, EscherProperties.TEXT__WRAPTEXT, wrap);
-    }
-
-    /**
-     * @return id for the text.
-     */
-    public int getTextId(){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTID);
-        return prop == null ? 0 : prop.getPropertyValue();
-    }
-
-    /**
-     * Sets text ID
-     *
-     * @param id of the text
-     */
-    public void setTextId(int id){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        setEscherProperty(opt, EscherProperties.TEXT__TEXTID, id);
-    }
-
-    /**
-     * The color used to fill this shape.
-     *
-     * @param color the background color
-     */
-    public void setBackgroundColor(Color color){
-        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);
-        int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB();
-        setEscherProperty(opt, EscherProperties.FILL__FILLBACKCOLOR, rgb);
-    }
-
-    /**
-      * @return the TextRun object for this text box
-      */
-     public TextRun getTextRun(){
-         return _txtrun;
-     }
-
-     public void setSheet(Sheet sheet){
-        _sheet = sheet;
-
-        // Initialize _txtrun object.
-        // (We can't do it in the constructor because the sheet
-        //  is not assigned then, it's only built once we have
-        //  all the records)
-        if(_txtrun == null) initTextRun();
-        if(_txtrun == null) {
-               // No text records found, skip
-               _missingTextRecords = true;
-               return;
-        } else {
-               _missingTextRecords = false;
-        }
-        
-        // Supply the sheet to our child RichTextRuns
-        _txtrun.setSheet(sheet);
-        RichTextRun[] rt = _txtrun.getRichTextRuns();
-        for (int i = 0; i < rt.length; i++) {
-            rt[i].supplySlideShow(_sheet.getSlideShow());
-        }
-    }
-
-    private void initTextRun(){
-        OutlineTextRefAtom ota = null;
-        
-        // Find the interesting child records 
-        Record[] child = _txtbox.getChildRecords();
-        for (int i = 0; i < child.length; i++) {
-            if (child[i] instanceof OutlineTextRefAtom) {
-                ota = (OutlineTextRefAtom)child[i];
-                break;
-            }
-        }
-
-        Sheet sheet = getSheet();
-        TextRun[] runs = sheet.getTextRuns();
-        if (ota != null) {
-            int idx = ota.getTextIndex();
-            for (int i = 0; i < runs.length; i++) {
-                if(runs[i].getIndex() == idx){
-                    _txtrun = runs[i];
-                }
-            }
-            if(_txtrun == null) {
-                logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);
-            }
-        } else {
-            int shapeId = _escherContainer.getChildById(EscherSpRecord.RECORD_ID).getShapeId();
-            if(runs != null) for (int i = 0; i < runs.length; i++) {
-                if(runs[i].getShapeId() == shapeId){
-                    _txtrun = runs[i];
-                    break;
-                }
-            }
-        }
-
-    }
 }
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java
new file mode 100755 (executable)
index 0000000..3c45b34
--- /dev/null
@@ -0,0 +1,516 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+package org.apache.poi.hslf.model;\r
+\r
+import org.apache.poi.ddf.*;\r
+import org.apache.poi.hslf.record.*;\r
+import org.apache.poi.hslf.usermodel.RichTextRun;\r
+import org.apache.poi.hslf.exceptions.HSLFException;\r
+import org.apache.poi.util.POILogger;\r
+\r
+import java.awt.*;\r
+import java.awt.geom.Rectangle2D;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.font.FontRenderContext;\r
+import java.awt.font.TextLayout;\r
+import java.io.IOException;\r
+\r
+/**\r
+ * A common superclass of all shapes that can hold text.\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public abstract class TextShape extends SimpleShape {\r
+\r
+    /**\r
+     * How to anchor the text\r
+     */\r
+    public static final int AnchorTop = 0;\r
+    public static final int AnchorMiddle = 1;\r
+    public static final int AnchorBottom = 2;\r
+    public static final int AnchorTopCentered = 3;\r
+    public static final int AnchorMiddleCentered = 4;\r
+    public static final int AnchorBottomCentered = 5;\r
+    public static final int AnchorTopBaseline = 6;\r
+    public static final int AnchorBottomBaseline = 7;\r
+    public static final int AnchorTopCenteredBaseline = 8;\r
+    public static final int AnchorBottomCenteredBaseline = 9;\r
+\r
+    /**\r
+     * How to wrap the text\r
+     */\r
+    public static final int WrapSquare = 0;\r
+    public static final int WrapByPoints = 1;\r
+    public static final int WrapNone = 2;\r
+    public static final int WrapTopBottom = 3;\r
+    public static final int WrapThrough = 4;\r
+\r
+    /**\r
+     * How to align the text\r
+     */\r
+    public static final int AlignLeft = 0;\r
+    public static final int AlignCenter = 1;\r
+    public static final int AlignRight = 2;\r
+    public static final int AlignJustify = 3;\r
+\r
+    /**\r
+     * TextRun object which holds actual text and format data\r
+     */\r
+    protected TextRun _txtrun;\r
+\r
+    /**\r
+     * Escher container which holds text attributes such as\r
+     * TextHeaderAtom, TextBytesAtom ot TextCharsAtom, StyleTextPropAtom etc.\r
+     */\r
+    protected EscherTextboxWrapper _txtbox;\r
+\r
+    /**\r
+     * Used to calculate text bounds\r
+     */\r
+    protected static final FontRenderContext _frc = new FontRenderContext(null, true, true);\r
+\r
+    /**\r
+     * Create a TextBox object and initialize it from the supplied Record container.\r
+     * \r
+     * @param escherRecord       <code>EscherSpContainer</code> container which holds information about this shape\r
+     * @param parent    the parent of the shape\r
+     */\r
+   protected TextShape(EscherContainerRecord escherRecord, Shape parent){\r
+        super(escherRecord, parent);\r
+\r
+    }\r
+\r
+    /**\r
+     * Create a new TextBox. This constructor is used when a new shape is created.\r
+     *\r
+     * @param parent    the parent of this Shape. For example, if this text box is a cell\r
+     * in a table then the parent is Table.\r
+     */\r
+    public TextShape(Shape parent){\r
+        super(null, parent);\r
+        _escherContainer = createSpContainer(parent instanceof ShapeGroup);\r
+    }\r
+\r
+    /**\r
+     * Create a new TextBox. This constructor is used when a new shape is created.\r
+     *\r
+     */\r
+    public TextShape(){\r
+        this(null);\r
+    }\r
+\r
+    public TextRun createTextRun(){\r
+        _txtbox = getEscherTextboxWrapper();\r
+        if(_txtbox == null) _txtbox = new EscherTextboxWrapper();\r
+\r
+        _txtrun = getTextRun();\r
+        if(_txtrun == null){\r
+            TextHeaderAtom tha = new TextHeaderAtom();\r
+            tha.setParentRecord(_txtbox);\r
+            _txtbox.appendChildRecord(tha);\r
+\r
+            TextCharsAtom tca = new TextCharsAtom();\r
+            _txtbox.appendChildRecord(tca);\r
+\r
+            StyleTextPropAtom sta = new StyleTextPropAtom(0);\r
+            _txtbox.appendChildRecord(sta);\r
+\r
+            _txtrun = new TextRun(tha,tca,sta);\r
+            _txtrun.setText("");\r
+\r
+            _escherContainer.addChildRecord(_txtbox.getEscherRecord());\r
+\r
+            setDefaultTextProperties(_txtrun);\r
+        }\r
+\r
+        return _txtrun;\r
+    }\r
+\r
+    /**\r
+     * Set default properties for the  TextRun.\r
+     * Depending on the text and shape type the defaults are different:\r
+     *   TextBox: align=left, valign=top\r
+     *   AutoShape: align=center, valign=middle\r
+     *\r
+     */\r
+    protected void setDefaultTextProperties(TextRun _txtrun){\r
+\r
+    }\r
+\r
+    /**\r
+     * Returns the text contained in this text frame.\r
+     *\r
+     * @return the text string for this textbox.\r
+     */\r
+     public String getText(){\r
+        TextRun tx = getTextRun();\r
+        return tx == null ? null : tx.getText();\r
+    }\r
+\r
+    /**\r
+     * Sets the text contained in this text frame.\r
+     *\r
+     * @param text the text string used by this object.\r
+     */\r
+    public void setText(String text){\r
+        TextRun tx = getTextRun();\r
+        if(tx == null){\r
+            tx = createTextRun();\r
+        }\r
+        tx.setText(text);\r
+        setTextId(text.hashCode());\r
+    }\r
+\r
+    /**\r
+     * When a textbox is added to  a sheet we need to tell upper-level\r
+     * <code>PPDrawing</code> about it.\r
+     *\r
+     * @param sh the sheet we are adding to\r
+     */\r
+    protected void afterInsert(Sheet sh){\r
+        EscherTextboxWrapper _txtbox = getEscherTextboxWrapper();\r
+        if(_txtbox != null){\r
+            PPDrawing ppdrawing = sh.getPPDrawing();\r
+            ppdrawing.addTextboxWrapper(_txtbox);\r
+            // Ensure the escher layer knows about the added records\r
+            try {\r
+                _txtbox.writeOut(null);\r
+            } catch (IOException e){\r
+                throw new HSLFException(e);\r
+            }\r
+            if(getAnchor().equals(new Rectangle()) && !"".equals(getText())) resizeToFitText();\r
+        }\r
+    }\r
+\r
+    protected EscherTextboxWrapper getEscherTextboxWrapper(){\r
+        if(_txtbox == null){\r
+            EscherTextboxRecord textRecord = (EscherTextboxRecord)Shape.getEscherChild(_escherContainer, EscherTextboxRecord.RECORD_ID);\r
+            if(textRecord != null) _txtbox = new EscherTextboxWrapper(textRecord);\r
+        }\r
+        return _txtbox;\r
+    }\r
+    /**\r
+     * Adjust the size of the TextShape so it encompasses the text inside it.\r
+     *\r
+     * @return a <code>Rectangle2D</code> that is the bounds of this <code>TextShape</code>.\r
+     */\r
+    public Rectangle2D resizeToFitText(){\r
+        String txt = getText();\r
+        if(txt == null || txt.length() == 0) return new Rectangle2D.Float();\r
+\r
+        RichTextRun rt = getTextRun().getRichTextRuns()[0];\r
+        int size = rt.getFontSize();\r
+        int style = 0;\r
+        if (rt.isBold()) style |= Font.BOLD;\r
+        if (rt.isItalic()) style |= Font.ITALIC;\r
+        String fntname = rt.getFontName();\r
+        Font font = new Font(fntname, style, size);\r
+\r
+        float width = 0, height = 0;\r
+        String[] lines = txt.split("\r");\r
+        for (int i = 0; i < lines.length; i++) {\r
+            if(lines[i].length() == 0) continue;\r
+\r
+            TextLayout layout = new TextLayout(lines[i], font, _frc);\r
+\r
+            width = Math.max(width, layout.getAdvance());\r
+\r
+            /**\r
+             * Even if top and bottom margins are set to 0 PowerPoint\r
+             * always sets extra space between the text and its bounding box.\r
+             *\r
+             * The approximation height = ascent*2 works good enough in most cases\r
+             */\r
+            height = Math.max(height, 2*layout.getAscent());\r
+        }\r
+\r
+        width += getMarginLeft() + getMarginRight();\r
+        height += getMarginTop() + getMarginBottom();\r
+\r
+        Rectangle2D anchor = getAnchor2D();\r
+        anchor.setRect(anchor.getX(), anchor.getY(), width, height);\r
+        setAnchor(anchor);\r
+\r
+        return anchor;\r
+    }\r
+\r
+    /**\r
+     * Returns the type of vertical alignment for the text.\r
+     * One of the <code>Anchor*</code> constants defined in this class.\r
+     *\r
+     * @return the type of alignment\r
+     */\r
+    public int getVerticalAlignment(){\r
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
+        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT);\r
+        int valign;\r
+        if (prop == null){\r
+            int type = getTextRun().getRunType();\r
+            switch (type){\r
+                case TextHeaderAtom.TITLE_TYPE:\r
+                case TextHeaderAtom.CENTER_TITLE_TYPE:\r
+                    valign = TextShape.AnchorMiddle;\r
+                    break;\r
+                default:\r
+                    valign = TextShape.AnchorTop;\r
+                    break;\r
+            }\r
+        } else {\r
+            valign = prop.getPropertyValue();\r
+        }\r
+        return valign;\r
+    }\r
+\r
+    /**\r
+     * Sets the type of vertical alignment for the text.\r
+     * One of the <code>Anchor*</code> constants defined in this class.\r
+     *\r
+     * @param align - the type of alignment\r
+     */\r
+    public void setVerticalAlignment(int align){\r
+        setEscherProperty(EscherProperties.TEXT__ANCHORTEXT, align);\r
+    }\r
+\r
+    /**\r
+     * Sets the type of horizontal alignment for the text.\r
+     * One of the <code>Align*</code> constants defined in this class.\r
+     *\r
+     * @param align - the type of horizontal alignment\r
+     */\r
+    public void setHorizontalAlignment(int align){\r
+        TextRun tx = getTextRun();\r
+        if(tx != null) tx.getRichTextRuns()[0].setAlignment(align);\r
+    }\r
+\r
+    /**\r
+     * Gets the type of horizontal alignment for the text.\r
+     * One of the <code>Align*</code> constants defined in this class.\r
+     *\r
+     * @return align - the type of horizontal alignment\r
+     */\r
+    public int getHorizontalAlignment(){\r
+        TextRun tx = getTextRun();\r
+        return tx == null ? -1 : tx.getRichTextRuns()[0].getAlignment();\r
+    }\r
+\r
+    /**\r
+     * Returns the distance (in points) between the bottom of the text frame\r
+     * and the bottom of the inscribed rectangle of the shape that contains the text.\r
+     * Default value is 1/20 inch.\r
+     *\r
+     * @return the botom margin\r
+     */\r
+    public float getMarginBottom(){\r
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
+        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTBOTTOM);\r
+        int val = prop == null ? EMU_PER_INCH/20 : prop.getPropertyValue();\r
+        return (float)val/EMU_PER_POINT;\r
+    }\r
+\r
+    /**\r
+     * Sets the botom margin.\r
+     * @see #getMarginBottom()\r
+     *\r
+     * @param margin    the bottom margin\r
+     */\r
+    public void setMarginBottom(float margin){\r
+        setEscherProperty(EscherProperties.TEXT__TEXTBOTTOM, (int)(margin*EMU_PER_POINT));\r
+    }\r
+\r
+    /**\r
+     *  Returns the distance (in points) between the left edge of the text frame\r
+     *  and the left edge of the inscribed rectangle of the shape that contains\r
+     *  the text.\r
+     *  Default value is 1/10 inch.\r
+     *\r
+     * @return the left margin\r
+     */\r
+    public float getMarginLeft(){\r
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
+        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTBOTTOM);\r
+        int val = prop == null ? EMU_PER_INCH/10 : prop.getPropertyValue();\r
+        return (float)val/EMU_PER_POINT;\r
+    }\r
+\r
+    /**\r
+     * Sets the left margin.\r
+     * @see #getMarginLeft()\r
+     *\r
+     * @param margin    the left margin\r
+     */\r
+    public void setMarginLeft(float margin){\r
+        setEscherProperty(EscherProperties.TEXT__TEXTLEFT, (int)(margin*EMU_PER_POINT));\r
+    }\r
+\r
+    /**\r
+     *  Returns the distance (in points) between the right edge of the\r
+     *  text frame and the right edge of the inscribed rectangle of the shape\r
+     *  that contains the text.\r
+     *  Default value is 1/10 inch.\r
+     *\r
+     * @return the right margin\r
+     */\r
+    public float getMarginRight(){\r
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
+        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTRIGHT);\r
+        int val = prop == null ? EMU_PER_INCH/10 : prop.getPropertyValue();\r
+        return (float)val/EMU_PER_POINT;\r
+    }\r
+\r
+    /**\r
+     * Sets the right margin.\r
+     * @see #getMarginRight()\r
+     *\r
+     * @param margin    the right margin\r
+     */\r
+    public void setMarginRight(float margin){\r
+        setEscherProperty(EscherProperties.TEXT__TEXTRIGHT, (int)(margin*EMU_PER_POINT));\r
+    }\r
+\r
+     /**\r
+     *  Returns the distance (in points) between the top of the text frame\r
+     *  and the top of the inscribed rectangle of the shape that contains the text.\r
+     *  Default value is 1/20 inch.\r
+     *\r
+     * @return the top margin\r
+     */\r
+    public float getMarginTop(){\r
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
+        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTTOP);\r
+        int val = prop == null ? EMU_PER_INCH/20 : prop.getPropertyValue();\r
+        return (float)val/EMU_PER_POINT;\r
+    }\r
+\r
+   /**\r
+     * Sets the top margin.\r
+     * @see #getMarginTop()\r
+     *\r
+     * @param margin    the top margin\r
+     */\r
+    public void setMarginTop(float margin){\r
+        setEscherProperty(EscherProperties.TEXT__TEXTTOP, (int)(margin*EMU_PER_POINT));\r
+    }\r
+\r
+\r
+    /**\r
+     * Returns the value indicating word wrap.\r
+     *\r
+     * @return the value indicating word wrap.\r
+     *  Must be one of the <code>Wrap*</code> constants defined in this class.\r
+     */\r
+    public int getWordWrap(){\r
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
+        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__WRAPTEXT);\r
+        return prop == null ? WrapSquare : prop.getPropertyValue();\r
+    }\r
+\r
+    /**\r
+     *  Specifies how the text should be wrapped\r
+     *\r
+     * @param wrap  the value indicating how the text should be wrapped.\r
+     *  Must be one of the <code>Wrap*</code> constants defined in this class.\r
+     */\r
+    public void setWordWrap(int wrap){\r
+        setEscherProperty(EscherProperties.TEXT__WRAPTEXT, wrap);\r
+    }\r
+\r
+    /**\r
+     * @return id for the text.\r
+     */\r
+    public int getTextId(){\r
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
+        EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTID);\r
+        return prop == null ? 0 : prop.getPropertyValue();\r
+    }\r
+\r
+    /**\r
+     * Sets text ID\r
+     *\r
+     * @param id of the text\r
+     */\r
+    public void setTextId(int id){\r
+        setEscherProperty(EscherProperties.TEXT__TEXTID, id);\r
+    }\r
+\r
+    /**\r
+      * @return the TextRun object for this text box\r
+      */\r
+     public TextRun getTextRun(){\r
+         if(_txtrun == null) initTextRun();\r
+         return _txtrun;\r
+     }\r
+\r
+    public void setSheet(Sheet sheet) {\r
+        _sheet = sheet;\r
+\r
+        // Initialize _txtrun object.\r
+        // (We can't do it in the constructor because the sheet\r
+        //  is not assigned then, it's only built once we have\r
+        //  all the records)\r
+        TextRun tx = getTextRun();\r
+        if (tx != null) {\r
+            // Supply the sheet to our child RichTextRuns\r
+            tx.setSheet(_sheet);\r
+            RichTextRun[] rt = tx.getRichTextRuns();\r
+            for (int i = 0; i < rt.length; i++) {\r
+                rt[i].supplySlideShow(_sheet.getSlideShow());\r
+            }\r
+        }\r
+\r
+    }\r
+\r
+    protected void initTextRun(){\r
+        EscherTextboxWrapper txtbox = getEscherTextboxWrapper();\r
+        Sheet sheet = getSheet();\r
+\r
+        if(sheet == null || txtbox == null) return;\r
+\r
+        OutlineTextRefAtom ota = null;\r
+\r
+        Record[] child = txtbox.getChildRecords();\r
+        for (int i = 0; i < child.length; i++) {\r
+            if (child[i] instanceof OutlineTextRefAtom) {\r
+                ota = (OutlineTextRefAtom)child[i];\r
+                break;\r
+            }\r
+        }\r
+\r
+        TextRun[] runs = _sheet.getTextRuns();\r
+        if (ota != null) {\r
+            int idx = ota.getTextIndex();\r
+            for (int i = 0; i < runs.length; i++) {\r
+                if(runs[i].getIndex() == idx){\r
+                    _txtrun = runs[i];\r
+                    break;\r
+                }\r
+            }\r
+            if(_txtrun == null) {\r
+                logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);\r
+            }\r
+        } else {\r
+            int shapeId = _escherContainer.getChildById(EscherSpRecord.RECORD_ID).getShapeId();\r
+            if(runs != null) for (int i = 0; i < runs.length; i++) {\r
+                if(runs[i].getShapeId() == shapeId){\r
+                    _txtrun = runs[i];\r
+                    break;\r
+                }\r
+            }\r
+        }\r
+    }\r
+}\r
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/text_shapes.ppt b/src/scratchpad/testcases/org/apache/poi/hslf/data/text_shapes.ppt
new file mode 100755 (executable)
index 0000000..c36183a
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/text_shapes.ppt differ
index 1fe8f7e5e6d88d135c6edecabd2cabc31c20e19b..c2e081ee5caa407782fe441800a0f7ba6a040a99 100644 (file)
@@ -218,8 +218,8 @@ public class TestShapes extends TestCase {
             ArrayList lst2 = new ArrayList();
             Shape[] sh = sl[k].getShapes();
             for (int i = 0; i < sh.length; i++) {
-                if (sh[i] instanceof TextBox){
-                    TextBox tbox = (TextBox)sh[i];
+                if (sh[i] instanceof TextShape){
+                    TextShape tbox = (TextShape)sh[i];
                     lst2.add(tbox.getText());
                 }
             }
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTextShape.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTextShape.java
new file mode 100755 (executable)
index 0000000..25a8db9
--- /dev/null
@@ -0,0 +1,160 @@
+\r
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+\r
+\r
+package org.apache.poi.hslf.model;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import java.io.*;\r
+import java.util.ArrayList;\r
+\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.record.TextHeaderAtom;\r
+\r
+/**\r
+ * Verify behavior of <code>TextShape</code> and its sub-classes\r
+ * \r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestTextShape extends TestCase {\r
+    protected String cwd = System.getProperty("HSLF.testdata.path");\r
+\r
+    public void testCreateAutoShape(){\r
+        TextShape shape = new AutoShape(ShapeTypes.Trapezoid);\r
+        assertNull(shape.getTextRun());\r
+        assertNull(shape.getText());\r
+        assertNull(shape.getEscherTextboxWrapper());\r
+\r
+        TextRun run = shape.createTextRun();\r
+        assertNotNull(run);\r
+        assertNotNull(shape.getTextRun());\r
+        assertNotNull(shape.getEscherTextboxWrapper());\r
+        assertEquals("", shape.getText());\r
+        assertSame(run, shape.createTextRun());\r
+\r
+    }\r
+\r
+    public void testCreateTextBox(){\r
+        TextShape shape = new TextBox();\r
+        TextRun run = shape.getTextRun();\r
+        assertNotNull(run);\r
+        assertNotNull(shape.getText());\r
+        assertNotNull(shape.getEscherTextboxWrapper());\r
+\r
+        assertSame(run, shape.createTextRun());\r
+        assertNotNull(shape.getTextRun());\r
+        assertNotNull(shape.getEscherTextboxWrapper());\r
+        assertEquals("", shape.getText());\r
+\r
+    }\r
+\r
+    /**\r
+     * Verify we can get text from TextShape in the following cases:\r
+     *  - placeholders\r
+     *  - normal TextBox object\r
+     *  - text in auto-shapes\r
+     */\r
+    public void testRead() throws IOException {\r
+        FileInputStream is = new FileInputStream(new File(cwd, "text_shapes.ppt"));\r
+        SlideShow ppt = new SlideShow(is);\r
+        is.close();\r
+\r
+        ArrayList lst1 = new ArrayList();\r
+        Slide slide = ppt.getSlides()[0];\r
+        Shape[] shape = slide.getShapes();\r
+        for (int i = 0; i < shape.length; i++) {\r
+            assertTrue("Expected TextShape but found " + shape[i].getClass().getName(), shape[i] instanceof TextShape);\r
+            TextShape tx = (TextShape)shape[i];\r
+            TextRun run = tx.getTextRun();\r
+            assertNotNull(run);\r
+            int runType = run.getRunType();\r
+\r
+            int type = shape[i].getShapeType();\r
+            switch (type){\r
+                case ShapeTypes.TextBox:\r
+                    assertEquals("Text in a TextBox", run.getText());\r
+                    break;\r
+                case ShapeTypes.Rectangle:\r
+                    if(runType == TextHeaderAtom.OTHER_TYPE)\r
+                        assertEquals("Rectangle", run.getText());\r
+                    else if(runType == TextHeaderAtom.TITLE_TYPE)\r
+                        assertEquals("Title Placeholder", run.getText());\r
+                    break;\r
+                case ShapeTypes.Octagon:\r
+                    assertEquals("Octagon", run.getText());\r
+                    break;\r
+                case ShapeTypes.Ellipse:\r
+                    assertEquals("Ellipse", run.getText());\r
+                    break;\r
+                case ShapeTypes.RoundRectangle:\r
+                    assertEquals("RoundRectangle", run.getText());\r
+                    break;\r
+                default:\r
+                    fail("Unexpected shape: " + shape[i].getShapeName());\r
+\r
+            }\r
+            lst1.add(run.getText());\r
+        }\r
+\r
+        ArrayList lst2 = new ArrayList();\r
+        TextRun[] run = slide.getTextRuns();\r
+        for (int i = 0; i < run.length; i++) {\r
+            lst2.add(run[i].getText());\r
+        }\r
+\r
+        assertTrue(lst1.containsAll(lst2));\r
+    }\r
+\r
+    public void testReadWrite() throws IOException {\r
+        SlideShow ppt = new SlideShow();\r
+        Slide slide =  ppt.createSlide();\r
+\r
+        TextShape shape1 = new TextBox();\r
+        TextRun run1 = shape1.createTextRun();\r
+        run1.setText("Hello, World!");\r
+        slide.addShape(shape1);\r
+\r
+        shape1.moveTo(100, 100);\r
+\r
+        TextShape shape2 = new AutoShape(ShapeTypes.Arrow);\r
+        TextRun run2 = shape2.createTextRun();\r
+        run2.setText("Testing TextShape");\r
+        slide.addShape(shape2);\r
+        shape2.moveTo(300, 300);\r
+\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream();\r
+        ppt.write(out);\r
+        out.close();\r
+\r
+        ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray()));\r
+        slide = ppt.getSlides()[0];\r
+        Shape[] shape = slide.getShapes();\r
+\r
+        assertTrue(shape[0] instanceof TextShape);\r
+        shape1 = (TextShape)shape[0];\r
+        assertEquals(ShapeTypes.TextBox, shape1.getShapeType());\r
+        assertEquals("Hello, World!", shape1.getTextRun().getText());\r
+\r
+        assertTrue(shape[1] instanceof TextShape);\r
+        shape1 = (TextShape)shape[1];\r
+        assertEquals(ShapeTypes.Arrow, shape1.getShapeType());\r
+        assertEquals("Testing TextShape", shape1.getTextRun().getText());\r
+    }\r
+\r
+}\r
index 36e45501ec7f94e6985450e11e85deba15471ff5..19e7af64177e1119568846757a8dcff3c8031edb 100644 (file)
@@ -316,8 +316,8 @@ public class TestBugs extends TestCase {
         ArrayList lst = new ArrayList();\r
         Shape[] shape = slide.getShapes();\r
         for (int i = 0; i < shape.length; i++) {\r
-            if( shape[i] instanceof TextBox){\r
-                TextRun textRun = ((TextBox)shape[i]).getTextRun();\r
+            if( shape[i] instanceof TextShape){\r
+                TextRun textRun = ((TextShape)shape[i]).getTextRun();\r
                 if(textRun != null) lst.add(textRun);\r
             }\r
 \r