mirror of https://github.com/apache/poi.git
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
This commit is contained in:
parent
f98e88e706
commit
95326239ee
|
@ -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));
|
||||
|
|
|
@ -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 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.
|
||||
*
|
||||
* <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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,11 +39,6 @@ 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);
|
||||
break;
|
||||
case ShapeTypes.PictureFrame:
|
||||
|
@ -54,9 +49,13 @@ public class ShapeFactory {
|
|||
break;
|
||||
case ShapeTypes.NotPrimitive:
|
||||
if ((spRecord.getFlags() & EscherSpRecord.FLAG_GROUP) != 0)
|
||||
//TODO: check if the shape group is a Table
|
||||
shape = new ShapeGroup(spContainer, parent);
|
||||
else
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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){
|
||||
|
|
|
@ -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);
|
||||
_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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <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;
|
||||
}
|
||||
}
|
||||
protected void setDefaultTextProperties(TextRun _txtrun){
|
||||
setVerticalAlignment(TextBox.AnchorTop);
|
||||
setEscherProperty(EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x20002);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <code>EscherSpContainer</code> 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
|
||||
* <code>PPDrawing</code> 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 <code>Rectangle2D</code> that is the bounds of this <code>TextShape</code>.
|
||||
*/
|
||||
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 <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 = 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 <code>Anchor*</code> 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 <code>Align*</code> 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 <code>Align*</code> 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 <code>Wrap*</code> 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 <code>Wrap*</code> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <code>TextShape</code> 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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue