From 149e87cda0286e73fb4e8960f807c93715030376 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Tue, 15 Apr 2008 07:54:20 +0000 Subject: [PATCH] TextShape is a common superclass of all shapes that can hold text. The subclasses are TextBox and AutoShape. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@648156 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/hslf/examples/ApacheconEU08.java | 2 + .../org/apache/poi/hslf/model/AutoShape.java | 73 ++- .../src/org/apache/poi/hslf/model/Line.java | 8 +- .../org/apache/poi/hslf/model/Picture.java | 10 +- .../apache/poi/hslf/model/Placeholder.java | 10 +- .../apache/poi/hslf/model/ShapeFactory.java | 15 +- .../org/apache/poi/hslf/model/ShapeGroup.java | 8 +- .../src/org/apache/poi/hslf/model/Sheet.java | 9 +- .../apache/poi/hslf/model/SimpleShape.java | 14 +- .../org/apache/poi/hslf/model/TableCell.java | 6 +- .../org/apache/poi/hslf/model/TextBox.java | 430 +-------------- .../org/apache/poi/hslf/model/TextShape.java | 516 ++++++++++++++++++ .../org/apache/poi/hslf/data/text_shapes.ppt | Bin 0 -> 11264 bytes .../org/apache/poi/hslf/model/TestShapes.java | 4 +- .../apache/poi/hslf/model/TestTextShape.java | 160 ++++++ .../apache/poi/hslf/usermodel/TestBugs.java | 4 +- 16 files changed, 798 insertions(+), 471 deletions(-) create mode 100755 src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/data/text_shapes.ppt create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/model/TestTextShape.java diff --git a/src/scratchpad/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java b/src/scratchpad/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java index 0f28c2a8c..962349876 100755 --- a/src/scratchpad/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java +++ b/src/scratchpad/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java @@ -459,6 +459,7 @@ public class ApacheconEU08 { TextBox box2 = new TextBox(); TextRun tr2 = box2.getTextRun(); tr2.setRunType(TextHeaderAtom.BODY_TYPE); + tr2.getRichTextRuns()[0].setFontSize(32); tr2.setText( "Support for more PowerPoint functionality\r" + "Rendering slides into java.awt.Graphics2D"); @@ -477,6 +478,7 @@ public class ApacheconEU08 { TextBox box4 = new TextBox(); TextRun tr4 = box4.getTextRun(); tr4.setRunType(TextHeaderAtom.BODY_TYPE); + tr4.getRichTextRuns()[0].setFontSize(32); tr4.setText( "Integration with Apache FOP - Formatting Objects Processor"); box4.setAnchor(new Rectangle(36, 290, 648, 90)); diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/AutoShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/AutoShape.java index b3ddce37d..cf65a9b13 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/AutoShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/AutoShape.java @@ -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. + *

+ * AutoShapes are drawing objects with a particular shape that may be customized through smart resizing and adjustments. + * See {@link ShapeTypes} + *

* * @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 spcont; + return _escherContainer; } + protected void setDefaultTextProperties(TextRun _txtrun){ + setVerticalAlignment(TextBox.AnchorMiddle); + setHorizontalAlignment(TextBox.AlignCenter); + setWordWrap(TextBox.WrapNone); + } + + /** + * Gets adjust value which controls smart resizing of the auto-shape. + * + *

+ * 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). + *

+ * + * @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. + * + *

+ * 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). + *

+ * + * @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); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Line.java b/src/scratchpad/src/org/apache/poi/hslf/model/Line.java index 0d3f616e9..923718368 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Line.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Line.java @@ -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; } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java index 90efd5f3e..910d5c850 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java @@ -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; } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Placeholder.java b/src/scratchpad/src/org/apache/poi/hslf/model/Placeholder.java index dd0132ebe..f0bc74558 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Placeholder.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Placeholder.java @@ -49,15 +49,15 @@ public class Placeholder extends TextBox { * @return the created EscherContainerRecord 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; } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java index 66c95e50c..0c36bf053 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java @@ -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); diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java index dbcc8069c..b22ed8d70 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java @@ -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); } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java index 135b625f1..abf2fec58 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java @@ -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); } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java index 85d672fe8..0831d453b 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java @@ -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; } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TableCell.java b/src/scratchpad/src/org/apache/poi/hslf/model/TableCell.java index bb93e06b7..36789ad91 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TableCell.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TableCell.java @@ -63,15 +63,15 @@ public class TableCell extends TextBox { } protected EscherContainerRecord createSpContainer(boolean isChild){ - EscherContainerRecord spContainer = super.createSpContainer(isChild); - EscherOptRecord opt = (EscherOptRecord)getEscherChild(spContainer, EscherOptRecord.RECORD_ID); + _escherContainer = super.createSpContainer(isChild); + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); setEscherProperty(opt, EscherProperties.TEXT__TEXTID, 0); setEscherProperty(opt, EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x20000); setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x150001); setEscherProperty(opt, EscherProperties.SHADOWSTYLE__SHADOWOBSURED, 0x20000); setEscherProperty(opt, EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x40000); - return spContainer; + return _escherContainer; } protected void anchorBorder(int type, Line line){ diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextBox.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextBox.java index 1f9a489a7..a1d45ffba 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TextBox.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextBox.java @@ -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 EscherContainerRecord 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); - short type = (ShapeTypes.TextBox << 4) | 0x2; - spRecord.setOptions(type); + setShapeType(ShapeTypes.TextBox); - //set default properties for a textbox - EscherOptRecord opt = (EscherOptRecord)getEscherChild(spcont, EscherOptRecord.RECORD_ID); - setEscherProperty(opt, EscherProperties.TEXT__TEXTID, 0); + //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); - 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); + _txtrun = createTextRun(); - //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; + return _escherContainer; } - /** - * Returns the text contained in this text frame. - * - * @return the text string for this textbox. - */ - public String getText(){ - return _txtrun == null ? null : _txtrun.getText(); + protected void setDefaultTextProperties(TextRun _txtrun){ + setVerticalAlignment(TextBox.AnchorTop); + setEscherProperty(EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x20002); } - /** - * 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 - * PPDrawing 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 Anchor* 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 Anchor* 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; - } - - /** - * 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); - } - - /** - * 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; - } - - /** - * 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); - } - - /** - * 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; - } - - /** - * 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); - } - - - /** - * Returns the value indicating word wrap. - * One of the Wrap* 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 index 000000000..3c45b34b0 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java @@ -0,0 +1,516 @@ + +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hslf.model; + +import org.apache.poi.ddf.*; +import org.apache.poi.hslf.record.*; +import org.apache.poi.hslf.usermodel.RichTextRun; +import org.apache.poi.hslf.exceptions.HSLFException; +import org.apache.poi.util.POILogger; + +import java.awt.*; +import java.awt.geom.Rectangle2D; +import java.awt.geom.AffineTransform; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.io.IOException; + +/** + * A common superclass of all shapes that can hold text. + * + * @author Yegor Kozlov + */ +public abstract class TextShape 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; + + /** + * TextRun 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; + + /** + * Used to calculate text bounds + */ + protected static final FontRenderContext _frc = new FontRenderContext(null, true, true); + + /** + * Create a TextBox object and initialize it from the supplied Record container. + * + * @param escherRecord EscherSpContainer container which holds information about this shape + * @param parent the parent of the shape + */ + protected TextShape(EscherContainerRecord escherRecord, Shape parent){ + super(escherRecord, parent); + + } + + /** + * Create a new TextBox. This constructor is used when a new shape is created. + * + * @param parent the parent of this Shape. For example, if this text box is a cell + * in a table then the parent is Table. + */ + public TextShape(Shape parent){ + super(null, parent); + _escherContainer = createSpContainer(parent instanceof ShapeGroup); + } + + /** + * Create a new TextBox. This constructor is used when a new shape is created. + * + */ + public TextShape(){ + this(null); + } + + public TextRun createTextRun(){ + _txtbox = getEscherTextboxWrapper(); + if(_txtbox == null) _txtbox = new EscherTextboxWrapper(); + + _txtrun = getTextRun(); + if(_txtrun == null){ + TextHeaderAtom tha = new TextHeaderAtom(); + tha.setParentRecord(_txtbox); + _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(""); + + _escherContainer.addChildRecord(_txtbox.getEscherRecord()); + + setDefaultTextProperties(_txtrun); + } + + return _txtrun; + } + + /** + * Set default properties for the TextRun. + * Depending on the text and shape type the defaults are different: + * TextBox: align=left, valign=top + * AutoShape: align=center, valign=middle + * + */ + protected void setDefaultTextProperties(TextRun _txtrun){ + + } + + /** + * Returns the text contained in this text frame. + * + * @return the text string for this textbox. + */ + public String getText(){ + TextRun tx = getTextRun(); + return tx == null ? null : tx.getText(); + } + + /** + * Sets the text contained in this text frame. + * + * @param text the text string used by this object. + */ + public void setText(String text){ + TextRun tx = getTextRun(); + if(tx == null){ + tx = createTextRun(); + } + tx.setText(text); + setTextId(text.hashCode()); + } + + /** + * When a textbox is added to a sheet we need to tell upper-level + * PPDrawing about it. + * + * @param sh the sheet we are adding to + */ + protected void afterInsert(Sheet sh){ + EscherTextboxWrapper _txtbox = getEscherTextboxWrapper(); + if(_txtbox != null){ + 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 Rectangle()) && !"".equals(getText())) resizeToFitText(); + } + } + + protected EscherTextboxWrapper getEscherTextboxWrapper(){ + if(_txtbox == null){ + EscherTextboxRecord textRecord = (EscherTextboxRecord)Shape.getEscherChild(_escherContainer, EscherTextboxRecord.RECORD_ID); + if(textRecord != null) _txtbox = new EscherTextboxWrapper(textRecord); + } + return _txtbox; + } + /** + * Adjust the size of the TextShape so it encompasses the text inside it. + * + * @return a Rectangle2D that is the bounds of this TextShape. + */ + public Rectangle2D resizeToFitText(){ + String txt = getText(); + if(txt == null || txt.length() == 0) return new Rectangle2D.Float(); + + RichTextRun rt = getTextRun().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); + + float width = 0, height = 0; + String[] lines = txt.split("\r"); + for (int i = 0; i < lines.length; i++) { + if(lines[i].length() == 0) continue; + + TextLayout layout = new TextLayout(lines[i], font, _frc); + + width = Math.max(width, layout.getAdvance()); + + /** + * Even if top and bottom margins are set to 0 PowerPoint + * always sets extra space between the text and its bounding box. + * + * The approximation height = ascent*2 works good enough in most cases + */ + height = Math.max(height, 2*layout.getAscent()); + } + + width += getMarginLeft() + getMarginRight(); + height += getMarginTop() + getMarginBottom(); + + Rectangle2D anchor = getAnchor2D(); + anchor.setRect(anchor.getX(), anchor.getY(), width, height); + setAnchor(anchor); + + return anchor; + } + + /** + * Returns the type of vertical alignment for the text. + * One of the Anchor* 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 = TextShape.AnchorMiddle; + break; + default: + valign = TextShape.AnchorTop; + break; + } + } else { + valign = prop.getPropertyValue(); + } + return valign; + } + + /** + * Sets the type of vertical alignment for the text. + * One of the Anchor* constants defined in this class. + * + * @param align - the type of alignment + */ + public void setVerticalAlignment(int align){ + setEscherProperty(EscherProperties.TEXT__ANCHORTEXT, align); + } + + /** + * Sets the type of horizontal alignment for the text. + * One of the Align* constants defined in this class. + * + * @param align - the type of horizontal alignment + */ + public void setHorizontalAlignment(int align){ + TextRun tx = getTextRun(); + if(tx != null) tx.getRichTextRuns()[0].setAlignment(align); + } + + /** + * Gets the type of horizontal alignment for the text. + * One of the Align* constants defined in this class. + * + * @return align - the type of horizontal alignment + */ + public int getHorizontalAlignment(){ + TextRun tx = getTextRun(); + return tx == null ? -1 : tx.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 float 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 (float)val/EMU_PER_POINT; + } + + /** + * Sets the botom margin. + * @see #getMarginBottom() + * + * @param margin the bottom margin + */ + public void setMarginBottom(float margin){ + setEscherProperty(EscherProperties.TEXT__TEXTBOTTOM, (int)(margin*EMU_PER_POINT)); + } + + /** + * Returns the distance (in points) 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 float 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 (float)val/EMU_PER_POINT; + } + + /** + * Sets the left margin. + * @see #getMarginLeft() + * + * @param margin the left margin + */ + public void setMarginLeft(float margin){ + setEscherProperty(EscherProperties.TEXT__TEXTLEFT, (int)(margin*EMU_PER_POINT)); + } + + /** + * Returns the distance (in points) 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 float 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 (float)val/EMU_PER_POINT; + } + + /** + * Sets the right margin. + * @see #getMarginRight() + * + * @param margin the right margin + */ + public void setMarginRight(float margin){ + setEscherProperty(EscherProperties.TEXT__TEXTRIGHT, (int)(margin*EMU_PER_POINT)); + } + + /** + * Returns the distance (in points) 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 float 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 (float)val/EMU_PER_POINT; + } + + /** + * Sets the top margin. + * @see #getMarginTop() + * + * @param margin the top margin + */ + public void setMarginTop(float margin){ + setEscherProperty(EscherProperties.TEXT__TEXTTOP, (int)(margin*EMU_PER_POINT)); + } + + + /** + * Returns the value indicating word wrap. + * + * @return the value indicating word wrap. + * Must be one of the Wrap* constants defined in this class. + */ + 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. + * Must be one of the Wrap* constants defined in this class. + */ + public void setWordWrap(int wrap){ + setEscherProperty(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){ + setEscherProperty(EscherProperties.TEXT__TEXTID, id); + } + + /** + * @return the TextRun object for this text box + */ + public TextRun getTextRun(){ + if(_txtrun == null) initTextRun(); + 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) + TextRun tx = getTextRun(); + if (tx != null) { + // Supply the sheet to our child RichTextRuns + tx.setSheet(_sheet); + RichTextRun[] rt = tx.getRichTextRuns(); + for (int i = 0; i < rt.length; i++) { + rt[i].supplySlideShow(_sheet.getSlideShow()); + } + } + + } + + protected void initTextRun(){ + EscherTextboxWrapper txtbox = getEscherTextboxWrapper(); + Sheet sheet = getSheet(); + + if(sheet == null || txtbox == null) return; + + OutlineTextRefAtom ota = null; + + Record[] child = txtbox.getChildRecords(); + for (int i = 0; i < child.length; i++) { + if (child[i] instanceof OutlineTextRefAtom) { + ota = (OutlineTextRefAtom)child[i]; + break; + } + } + + 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]; + break; + } + } + 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/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 index 0000000000000000000000000000000000000000..c36183ac35f11b33518d2f1ebc0b5bca15ab7b54 GIT binary patch literal 11264 zcmeHN3vg7`8UF9Rn`9F<+$BpCAB77|1(jeV*4j#AkO-(%A_*-xsU%G{$&O1lvsnbR zwu_FBs1j?oO_eaCIOOB zD^pJ9zvulw&-u@P&SuB66ZYKm+vEQ(S*b!SIgrkm+>CYuWr9jUkpK#;A4sRux={qh zqv;6Jz^C9f1mjr%A0P)no96-ifP6p!U>tyUCX7ef24D%u1V9mBBEaiEg2ey7XLYf} z@Rtxr&XYFeaajXP9)3CoE5=hIeL=8uZPPCnJv-t0H{?Ef=;YTjF(t3QboUb*wi|_L zOwxHERfGB}34<<65ygWeRVX*gN?Cy(ay4?AA*NxXo9mZ+j68CnhvOgTqD9J)8bxP? zG$`562fb!ckArptC`V9k(?f^-l0Ns1r-NY!4byldHx^RP5LrjZ&sm`F(LP-Hr-N=8 zm4IZ+Yu3Y1o*}PWGf+<+BeDk@o4_*t0r$U`T>o#_@~$-zmD6OqO;eW1^EUOsep_D5 z`;MCMJG%<`Txc@_ZAMYvwmu}=pAM8UXX3o2_lAV%{Ki} zQY!JKoqwt5qT-*05B-(My*7De*_7kJPNi(JX+5mbPtQR<%@JurE|{-!mZehZg0s@; zw^fVQs9GWv2ug4aM~jYfrKxAIz22(l(oSVXp>K^Gw_`YXv)Y@l=g3T%7?C*S!enY&Y@eObwfhZ zE}vL5HB&nZUY%GgaSlz9w8yrOs`L`Ym?9zH9Mz!dlXfO>OvxP~d0=&*j7x4vGC~mN z#t_YA%S|Dw9YWhw<8t*^4M$Zgr(@xY+#F)NNTE;hyA4Hv4fQTZ(Nix~0uv~6>ks?x zR@;RokzSj}Xl*1B4LjA*hQ@GfEZP)~i`-{p3#v1g>n0yqs00Gj8%o)>d}!^0H!c{E zDwWF^>M9bhvz0dP8s16{6E)OERj7_VIos*82E80vH;5+?`ukdnn&c6 z%NAeidE`ZIyNPxlZ0m5Ior&U)fhk9ll8SZ84T|f`6K*SxmI?0JmPR?>J=MUUqH;g1 zTVH&&Wy=JYn|8fm=TNyMCRQzYSB)X4L7W2~*oiH5HTM+<9PBXO@hKejTSXmva0N1DAdwZPpr zRZ(QrZn&`v%a$rraIKvh)sK&|`*!4>c6=jfr+3Zhv~!sa?d0AR%+?NfN}i!DUQGsE z?U1ZUkInv50QcDRU*5j*N~Z8W`(eg$tmWZ2tzTOEY1BMD6>D8l^XG`9W@k8a| zZqGNhJoHE-%fslvl?O}TaLZbvk4GU9cR6=v_WFJGn$b$+1nY~3N<{SjF-T+%l4$24 zBw7e6o{iL{3{KaRXe~{N*cpDwzs|Vz?wjRyJ$&Hd zMN6X(7T=sjFZIEDj~nf0mM(G-Tr$3{!cDgscchiLCGj@SdIxSzEx3K!@^5PwROm%` zJa@SR-IQdWhX{{;`~r-lA3qnlli|nn;jrn)cVTgHJLNe_g`gn3&k6fQB$L6ljX)o~Q zk(*1jYP1r!gh-E1lqlYnwuq(r_9Y) z388aG5U-Hzpd|T6orJzewhhlpk@+sOlRC zCs0>|Zy*%qF|^Kh&-c6$MA?7K*hLBYx8+Nqx`3iA0j+FNsN(aY(MWqozaZRRNJm0n)X654kqhBhp1GN7I-! zKut1&pf=&Pl)vW&;YQ*5huR*Dnr{+EC{Xv_O}DLc5wcwwR0zOuA9vO zuAMCauAl7yY+AuKS8M5gI;^;8{O6^>WO zB5et$Iv(x_wP(byH4W6p5L6Zb`t#(1nF)(q7B zR5-R0(9=6<9C=~cOO;*$Im=y-FHyg5a4%8r1%MJLf|g;MUw#s7fxx`Wilu{u`DoXi zTX?x;w57IBh=k5i}PXU`)q6SLHdFoeN+aTS;RUIMO@@l_VmM5_=zB!l%`& z!To*(YHp&f*}3x@#qug9YaVk{p2Ed}wwmnfRJr;+_8(2X*_s7TG1YDNQ4LVQ!6#mNK@ER zBs%&#;VTE^a!{tcX8@?TIc}Y0+U2|9cn9NRPp6)K&^0K{!;51afBOGy9C+all;eZO z9ISFYC}iF|4mQ^7D?@>@d{$|+^gk=Q9-bqc{yz0|IT6Av-*(&H4cJowd7uBsu`~0| h#5J`HE29OzJT4AS^_X0VxFaU&I_0V2Xnurg;J+}LZT0{F literal 0 HcmV?d00001 diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java index 1fe8f7e5e..c2e081ee5 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java @@ -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 index 000000000..25a8db918 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTextShape.java @@ -0,0 +1,160 @@ + +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + +package org.apache.poi.hslf.model; + +import junit.framework.TestCase; + +import java.io.*; +import java.util.ArrayList; + +import org.apache.poi.hslf.usermodel.SlideShow; +import org.apache.poi.hslf.record.TextHeaderAtom; + +/** + * Verify behavior of TextShape and its sub-classes + * + * @author Yegor Kozlov + */ +public class TestTextShape extends TestCase { + protected String cwd = System.getProperty("HSLF.testdata.path"); + + public void testCreateAutoShape(){ + TextShape shape = new AutoShape(ShapeTypes.Trapezoid); + assertNull(shape.getTextRun()); + assertNull(shape.getText()); + assertNull(shape.getEscherTextboxWrapper()); + + TextRun run = shape.createTextRun(); + assertNotNull(run); + assertNotNull(shape.getTextRun()); + assertNotNull(shape.getEscherTextboxWrapper()); + assertEquals("", shape.getText()); + assertSame(run, shape.createTextRun()); + + } + + public void testCreateTextBox(){ + TextShape shape = new TextBox(); + TextRun run = shape.getTextRun(); + assertNotNull(run); + assertNotNull(shape.getText()); + assertNotNull(shape.getEscherTextboxWrapper()); + + assertSame(run, shape.createTextRun()); + assertNotNull(shape.getTextRun()); + assertNotNull(shape.getEscherTextboxWrapper()); + assertEquals("", shape.getText()); + + } + + /** + * Verify we can get text from TextShape in the following cases: + * - placeholders + * - normal TextBox object + * - text in auto-shapes + */ + public void testRead() throws IOException { + FileInputStream is = new FileInputStream(new File(cwd, "text_shapes.ppt")); + SlideShow ppt = new SlideShow(is); + is.close(); + + ArrayList lst1 = new ArrayList(); + Slide slide = ppt.getSlides()[0]; + Shape[] shape = slide.getShapes(); + for (int i = 0; i < shape.length; i++) { + assertTrue("Expected TextShape but found " + shape[i].getClass().getName(), shape[i] instanceof TextShape); + TextShape tx = (TextShape)shape[i]; + TextRun run = tx.getTextRun(); + assertNotNull(run); + int runType = run.getRunType(); + + int type = shape[i].getShapeType(); + switch (type){ + case ShapeTypes.TextBox: + assertEquals("Text in a TextBox", run.getText()); + break; + case ShapeTypes.Rectangle: + if(runType == TextHeaderAtom.OTHER_TYPE) + assertEquals("Rectangle", run.getText()); + else if(runType == TextHeaderAtom.TITLE_TYPE) + assertEquals("Title Placeholder", run.getText()); + break; + case ShapeTypes.Octagon: + assertEquals("Octagon", run.getText()); + break; + case ShapeTypes.Ellipse: + assertEquals("Ellipse", run.getText()); + break; + case ShapeTypes.RoundRectangle: + assertEquals("RoundRectangle", run.getText()); + break; + default: + fail("Unexpected shape: " + shape[i].getShapeName()); + + } + lst1.add(run.getText()); + } + + ArrayList lst2 = new ArrayList(); + TextRun[] run = slide.getTextRuns(); + for (int i = 0; i < run.length; i++) { + lst2.add(run[i].getText()); + } + + assertTrue(lst1.containsAll(lst2)); + } + + public void testReadWrite() throws IOException { + SlideShow ppt = new SlideShow(); + Slide slide = ppt.createSlide(); + + TextShape shape1 = new TextBox(); + TextRun run1 = shape1.createTextRun(); + run1.setText("Hello, World!"); + slide.addShape(shape1); + + shape1.moveTo(100, 100); + + TextShape shape2 = new AutoShape(ShapeTypes.Arrow); + TextRun run2 = shape2.createTextRun(); + run2.setText("Testing TextShape"); + slide.addShape(shape2); + shape2.moveTo(300, 300); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ppt.write(out); + out.close(); + + ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray())); + slide = ppt.getSlides()[0]; + Shape[] shape = slide.getShapes(); + + assertTrue(shape[0] instanceof TextShape); + shape1 = (TextShape)shape[0]; + assertEquals(ShapeTypes.TextBox, shape1.getShapeType()); + assertEquals("Hello, World!", shape1.getTextRun().getText()); + + assertTrue(shape[1] instanceof TextShape); + shape1 = (TextShape)shape[1]; + assertEquals(ShapeTypes.Arrow, shape1.getShapeType()); + assertEquals("Testing TextShape", shape1.getTextRun().getText()); + } + +} diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java index 36e45501e..19e7af641 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java @@ -316,8 +316,8 @@ public class TestBugs extends TestCase { ArrayList lst = new ArrayList(); Shape[] shape = slide.getShapes(); for (int i = 0; i < shape.length; i++) { - if( shape[i] instanceof TextBox){ - TextRun textRun = ((TextBox)shape[i]).getTextRun(); + if( shape[i] instanceof TextShape){ + TextRun textRun = ((TextShape)shape[i]).getTextRun(); if(textRun != null) lst.add(textRun); }