import org.apache.poi.sl.draw.geom.Outline;\r
import org.apache.poi.sl.draw.geom.Path;\r
import org.apache.poi.sl.usermodel.LineDecoration;\r
+import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape;\r
import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize;\r
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;\r
import org.apache.poi.sl.usermodel.Shadow;\r
\r
\r
public class DrawSimpleShape extends DrawShape {\r
+ \r
+ private static final double DECO_SIZE_POW = 1.5d;\r
\r
public DrawSimpleShape(SimpleShape<?,?> shape) {\r
super(shape);\r
graphics.setStroke(stroke);\r
\r
Collection<Outline> elems = computeOutlines(graphics);\r
- \r
+\r
// first paint the shadow\r
drawShadow(graphics, elems, fill, line);\r
- \r
+\r
// then fill the shape interior\r
if (fill != null) {\r
graphics.setPaint(fill);\r
java.awt.Shape s = o.getOutline();\r
graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);\r
graphics.fill(s);\r
- } \r
+ }\r
}\r
}\r
- \r
+\r
// then draw any content within this shape (text, image, etc.)\r
drawContent(graphics);\r
\r
}\r
}\r
}\r
- \r
+\r
// draw line decorations\r
drawDecoration(graphics, line, stroke);\r
}\r
protected void drawDecoration(Graphics2D graphics, Paint line, BasicStroke stroke) {\r
if(line == null) return;\r
graphics.setPaint(line);\r
- \r
+\r
List<Outline> lst = new ArrayList<Outline>();\r
LineDecoration deco = getShape().getLineDecoration();\r
Outline head = getHeadDecoration(graphics, deco, stroke);\r
if (head != null) lst.add(head);\r
Outline tail = getTailDecoration(graphics, deco, stroke);\r
if (tail != null) lst.add(tail);\r
- \r
- \r
+\r
+\r
for(Outline o : lst){\r
java.awt.Shape s = o.getOutline();\r
Path p = o.getPath();\r
graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);\r
- \r
+\r
if(p.isFilled()) graphics.fill(s);\r
if(p.isStroked()) graphics.draw(s);\r
}\r
}\r
\r
protected Outline getTailDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {\r
+ if (deco == null || stroke == null) {\r
+ return null;\r
+ }\r
DecorationSize tailLength = deco.getTailLength();\r
+ if (tailLength == null) {\r
+ tailLength = DecorationSize.MEDIUM;\r
+ }\r
DecorationSize tailWidth = deco.getTailWidth();\r
- \r
+ if (tailWidth == null) {\r
+ tailWidth = DecorationSize.MEDIUM;\r
+ }\r
+\r
double lineWidth = Math.max(2.5, stroke.getLineWidth());\r
- \r
+\r
Rectangle2D anchor = getAnchor(graphics, getShape());\r
double x2 = anchor.getX() + anchor.getWidth(),\r
y2 = anchor.getY() + anchor.getHeight();\r
- \r
+\r
double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());\r
- \r
+\r
AffineTransform at = new AffineTransform();\r
java.awt.Shape tailShape = null;\r
Path p = null;\r
Rectangle2D bounds;\r
- final double scaleY = Math.pow(2, tailWidth.ordinal()+1);\r
- final double scaleX = Math.pow(2, tailLength.ordinal()+1);\r
- switch (deco.getTailShape()) {\r
+ final double scaleY = Math.pow(DECO_SIZE_POW, tailWidth.ordinal()+1);\r
+ final double scaleX = Math.pow(DECO_SIZE_POW, tailLength.ordinal()+1);\r
+\r
+ DecorationShape tailShapeEnum = deco.getTailShape();\r
+\r
+ if (tailShapeEnum == null) {\r
+ return null;\r
+ }\r
+\r
+ switch (tailShapeEnum) {\r
case OVAL:\r
p = new Path();\r
tailShape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);\r
default:\r
break;\r
}\r
- \r
+\r
if (tailShape != null) {\r
tailShape = at.createTransformedShape(tailShape);\r
}\r
return tailShape == null ? null : new Outline(tailShape, p);\r
}\r
- \r
+\r
protected Outline getHeadDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {\r
+ if (deco == null || stroke == null) {\r
+ return null;\r
+ }\r
DecorationSize headLength = deco.getHeadLength();\r
+ if (headLength == null) {\r
+ headLength = DecorationSize.MEDIUM;\r
+ }\r
DecorationSize headWidth = deco.getHeadWidth();\r
- \r
+ if (headWidth == null) {\r
+ headWidth = DecorationSize.MEDIUM;\r
+ }\r
+\r
double lineWidth = Math.max(2.5, stroke.getLineWidth());\r
- \r
+\r
Rectangle2D anchor = getAnchor(graphics, getShape());\r
double x1 = anchor.getX(),\r
y1 = anchor.getY();\r
- \r
+\r
double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());\r
- \r
+\r
AffineTransform at = new AffineTransform();\r
java.awt.Shape headShape = null;\r
Path p = null;\r
Rectangle2D bounds;\r
- final double scaleY = Math.pow(2, headWidth.ordinal()+1);\r
- final double scaleX = Math.pow(2, headLength.ordinal()+1);\r
- switch (deco.getHeadShape()) {\r
+ final double scaleY = Math.pow(DECO_SIZE_POW, headWidth.ordinal()+1);\r
+ final double scaleX = Math.pow(DECO_SIZE_POW, headLength.ordinal()+1);\r
+ DecorationShape headShapeEnum = deco.getHeadShape();\r
+\r
+ if (headShapeEnum == null) {\r
+ return null;\r
+ }\r
+\r
+ switch (headShapeEnum) {\r
case OVAL:\r
p = new Path();\r
headShape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);\r
default:\r
break;\r
}\r
- \r
+\r
if (headShape != null) {\r
headShape = at.createTransformedShape(headShape);\r
}\r
return headShape == null ? null : new Outline(headShape, p);\r
}\r
- \r
+\r
public BasicStroke getStroke() {\r
StrokeStyle strokeStyle = getShape().getStrokeStyle();\r
- \r
+\r
float lineWidth = (float) strokeStyle.getLineWidth();\r
if (lineWidth == 0.0f) lineWidth = 0.25f; // Both PowerPoint and OOo draw zero-length lines as 0.25pt\r
\r
\r
SolidPaint shadowPaint = shadow.getFillStyle();\r
Color shadowColor = DrawPaint.applyColorTransform(shadowPaint.getSolidColor());\r
- \r
+\r
double shapeRotation = getShape().getRotation();\r
if(getShape().getFlipVertical()) {\r
shapeRotation += 180;\r
double dist = shadow.getDistance();\r
double dx = dist * Math.cos(Math.toRadians(angle));\r
double dy = dist * Math.sin(Math.toRadians(angle));\r
- \r
+\r
graphics.translate(dx, dy);\r
- \r
+\r
for(Outline o : outlines){\r
java.awt.Shape s = o.getOutline();\r
Path p = o.getPath();\r
graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);\r
graphics.setPaint(shadowColor);\r
- \r
+\r
if(fill != null && p.isFilled()){\r
graphics.fill(s);\r
} else if (line != null && p.isStroked()) {\r
\r
graphics.translate(-dx, -dy);\r
}\r
- \r
+\r
protected static CustomGeometry getCustomGeometry(String name) {\r
return getCustomGeometry(name, null);\r
}\r
- \r
+\r
protected static CustomGeometry getCustomGeometry(String name, Graphics2D graphics) {\r
@SuppressWarnings("unchecked")\r
Map<String, CustomGeometry> presets = (graphics == null)\r
? null\r
: (Map<String, CustomGeometry>)graphics.getRenderingHint(Drawable.PRESET_GEOMETRY_CACHE);\r
- \r
+\r
if (presets == null) {\r
presets = new HashMap<String,CustomGeometry>();\r
if (graphics != null) {\r
graphics.setRenderingHint(Drawable.PRESET_GEOMETRY_CACHE, presets);\r
}\r
- \r
+\r
String packageName = "org.apache.poi.sl.draw.binding";\r
InputStream presetIS = Drawable.class.getResourceAsStream("presetShapeDefinitions.xml");\r
- \r
+\r
// StAX:\r
EventFilter startElementFilter = new EventFilter() {\r
@Override\r
return event.isStartElement();\r
}\r
};\r
- \r
+\r
try {\r
XMLInputFactory staxFactory = XMLInputFactory.newInstance();\r
XMLEventReader staxReader = staxFactory.createXMLEventReader(presetIS);\r
// JAXB:\r
JAXBContext jaxbContext = JAXBContext.newInstance(packageName);\r
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();\r
- \r
+\r
while (staxFiltRd.peek() != null) {\r
StartElement evRoot = (StartElement)staxFiltRd.peek();\r
String cusName = evRoot.getName().getLocalPart();\r
// XMLEvent ev = staxReader.nextEvent();\r
JAXBElement<org.apache.poi.sl.draw.binding.CTCustomGeometry2D> el = unmarshaller.unmarshal(staxReader, CTCustomGeometry2D.class);\r
CTCustomGeometry2D cusGeom = el.getValue();\r
- \r
+\r
presets.put(cusName, new CustomGeometry(cusGeom));\r
}\r
} catch (Exception e) {\r
}\r
}\r
}\r
- \r
+\r
return presets.get(name);\r
}\r
- \r
+\r
protected Collection<Outline> computeOutlines(Graphics2D graphics) {\r
\r
List<Outline> lst = new ArrayList<Outline>();\r
public void drawContent(Graphics2D graphics) {\r
fixFonts(graphics);\r
\r
- Rectangle2D anchor = DrawShape.getAnchor(graphics, getShape());\r
- Insets2D insets = getShape().getInsets();\r
+ TextShape<?,?> s = getShape();\r
+ \r
+ Rectangle2D anchor = DrawShape.getAnchor(graphics, s);\r
+ Insets2D insets = s.getInsets();\r
double x = anchor.getX() + insets.left;\r
double y = anchor.getY();\r
\r
// (see DrawShape#applyTransform ), but we need to restore it to avoid painting "upside down".\r
// See Bugzilla 54210.\r
\r
- if(getShape().getFlipVertical()){\r
- graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());\r
- graphics.scale(1, -1);\r
- graphics.translate(-anchor.getX(), -anchor.getY());\r
-\r
- // text in vertically flipped shapes is rotated by 180 degrees\r
- double centerX = anchor.getX() + anchor.getWidth()/2;\r
- double centerY = anchor.getY() + anchor.getHeight()/2;\r
- graphics.translate(centerX, centerY);\r
- graphics.rotate(Math.toRadians(180));\r
- graphics.translate(-centerX, -centerY);\r
- }\r
-\r
+ boolean vertFlip = s.getFlipVertical();\r
+ boolean horzFlip = s.getFlipHorizontal();\r
+ ShapeContainer<?,?> sc = s.getParent();\r
+ while (sc instanceof PlaceableShape) {\r
+ PlaceableShape<?,?> ps = (PlaceableShape<?,?>)sc;\r
+ vertFlip ^= ps.getFlipVertical();\r
+ horzFlip ^= ps.getFlipHorizontal();\r
+ sc = ps.getParent();\r
+ };\r
+ \r
// Horizontal flipping applies only to shape outline and not to the text in the shape.\r
// Applying flip second time restores the original not-flipped transform\r
- if(getShape().getFlipHorizontal()){\r
+ if (horzFlip ^ vertFlip) {\r
graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());\r
graphics.scale(-1, 1);\r
- graphics.translate(-anchor.getX() , -anchor.getY());\r
+ graphics.translate(-anchor.getX(), -anchor.getY());\r
+ }\r
+ \r
+ Double textRot = s.getTextRotation();\r
+ if (textRot != null) {\r
+ graphics.translate(anchor.getCenterX(), anchor.getCenterY());\r
+ graphics.rotate(Math.toRadians(textRot));\r
+ graphics.translate(-anchor.getCenterX(), -anchor.getCenterY());\r
}\r
-\r
\r
// first dry-run to calculate the total height of the text\r
- double textHeight = getShape().getTextHeight();\r
+ double textHeight = s.getTextHeight();\r
\r
- switch (getShape().getVerticalAlignment()){\r
+ switch (s.getVerticalAlignment()){\r
+ default:\r
case TOP:\r
y += insets.top;\r
break;\r
case BOTTOM:\r
y += anchor.getHeight() - textHeight - insets.bottom;\r
break;\r
- default:\r
case MIDDLE:\r
double delta = anchor.getHeight() - textHeight - insets.top - insets.bottom;\r
y += insets.top + delta/2;\r
* Represents the shape decoration that appears at the ends of lines.\r
*/\r
enum DecorationShape {\r
- NONE(1),\r
- TRIANGLE(2),\r
- STEALTH(3),\r
- DIAMOND(4),\r
- OVAL(5),\r
- ARROW(6);\r
- \r
+ NONE(0,1),\r
+ TRIANGLE(1,2),\r
+ STEALTH(2,3),\r
+ DIAMOND(3,4),\r
+ OVAL(4,5),\r
+ ARROW(5,6);\r
+\r
+ public final int nativeId;\r
public final int ooxmlId;\r
- \r
- DecorationShape(int ooxmlId) {\r
+\r
+ DecorationShape(int nativeId, int ooxmlId) {\r
+ this.nativeId = nativeId;\r
this.ooxmlId = ooxmlId;\r
}\r
- \r
+\r
+ public static DecorationShape fromNativeId(int nativeId) {\r
+ for (DecorationShape ld : values()) {\r
+ if (ld.nativeId == nativeId) return ld;\r
+ }\r
+ return null;\r
+ }\r
+\r
public static DecorationShape fromOoxmlId(int ooxmlId) {\r
for (DecorationShape ds : values()) {\r
if (ds.ooxmlId == ooxmlId) return ds;\r
return null;\r
}\r
}\r
- \r
+\r
enum DecorationSize {\r
- SMALL(1),\r
- MEDIUM(2),\r
- LARGE(3);\r
- \r
+ SMALL(0, 1),\r
+ MEDIUM(1, 2),\r
+ LARGE(2, 3);\r
+\r
+ public final int nativeId;\r
public final int ooxmlId;\r
- \r
- DecorationSize(int ooxmlId) {\r
+\r
+ DecorationSize(int nativeId, int ooxmlId) {\r
+ this.nativeId = nativeId;\r
this.ooxmlId = ooxmlId;\r
}\r
- \r
+\r
+ public static DecorationSize fromNativeId(int nativeId) {\r
+ for (DecorationSize ld : values()) {\r
+ if (ld.nativeId == nativeId) return ld;\r
+ }\r
+ return null;\r
+ }\r
+\r
public static DecorationSize fromOoxmlId(int ooxmlId) {\r
for (DecorationSize ds : values()) {\r
if (ds.ooxmlId == ooxmlId) return ds;\r
return null;\r
}\r
}\r
- \r
+\r
/**\r
* @return the line start shape\r
*/\r
DecorationShape getHeadShape();\r
- \r
+\r
/**\r
* @return the width of the start shape\r
*/\r
DecorationSize getHeadWidth();\r
- \r
+\r
/**\r
* @return the length of the start shape\r
*/\r
DecorationSize getHeadLength();\r
- \r
+\r
/**\r
* @return the line end shape\r
*/\r
DecorationShape getTailShape();\r
- \r
+\r
/**\r
* @return the width of the end shape\r
*/\r
DecorationSize getTailWidth();\r
- \r
+\r
/**\r
* @return the length of the end shape\r
*/\r
* @return vertical orientation of the text\r
*/\r
TextDirection getTextDirection();\r
+\r
+ /**\r
+ * sets the vertical orientation\r
+ * @param orientation vertical orientation of the text\r
+ */\r
+ void setTextDirection(TextDirection orientation);\r
+\r
+ /**\r
+ * The text rotation can be independent specified from the shape rotation.\r
+ * For XSLF this can be an arbitrary degree, for HSLF the degree is given in steps of 90 degrees\r
+ * \r
+ * @return text rotation in degrees, returns null if no rotation is given\r
+ */\r
+ Double getTextRotation();\r
+ \r
+ /**\r
+ * Sets the text rotation.\r
+ * For XSLF this can ben an arbitrary degree, for HSLF the rotation is rounded to next 90 degree step\r
+ * \r
+ * @param rotation the text rotation, or null to unset the rotation\r
+ */\r
+ void setTextRotation(Double rotation);\r
\r
/**\r
* Sets the text placeholder\r
@Override
public void setVerticalAlignment(VerticalAlignment anchor){
- CTTextBodyProperties bodyPr = getTextBodyPr();
+ CTTextBodyProperties bodyPr = getTextBodyPr(true);
if (bodyPr != null) {
if(anchor == null) {
if(bodyPr.isSetAnchor()) bodyPr.unsetAnchor();
@Override
public void setHorizontalCentered(Boolean isCentered){
- CTTextBodyProperties bodyPr = getTextBodyPr();
+ CTTextBodyProperties bodyPr = getTextBodyPr(true);
if (bodyPr != null) {
if (isCentered == null) {
if (bodyPr.isSetAnchorCtr()) bodyPr.unsetAnchorCtr();
return fetcher.getValue() == null ? false : fetcher.getValue();
}
- /**
- *
- * @param orientation vertical orientation of the text
- */
+ @Override
public void setTextDirection(TextDirection orientation){
- CTTextBodyProperties bodyPr = getTextBodyPr();
+ CTTextBodyProperties bodyPr = getTextBodyPr(true);
if (bodyPr != null) {
if(orientation == null) {
if(bodyPr.isSetVert()) bodyPr.unsetVert();
}
}
- /**
- * @return vertical orientation of the text
- */
+ @Override
public TextDirection getTextDirection(){
CTTextBodyProperties bodyPr = getTextBodyPr();
if (bodyPr != null) {
return TextDirection.HORIZONTAL;
}
-
+ @Override
+ public Double getTextRotation() {
+ CTTextBodyProperties bodyPr = getTextBodyPr();
+ if (bodyPr != null && bodyPr.isSetRot()) {
+ return bodyPr.getRot() / 60000.;
+ }
+ return null;
+ }
+
+ @Override
+ public void setTextRotation(Double rotation) {
+ CTTextBodyProperties bodyPr = getTextBodyPr(true);
+ if (bodyPr != null) {
+ bodyPr.setRot((int)(rotation * 60000.));
+ }
+ }
+
+
/**
* 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.
* @param margin the bottom margin
*/
public void setBottomInset(double margin){
- CTTextBodyProperties bodyPr = getTextBodyPr();
+ CTTextBodyProperties bodyPr = getTextBodyPr(true);
if (bodyPr != null) {
if(margin == -1) bodyPr.unsetBIns();
else bodyPr.setBIns(Units.toEMU(margin));
* @param margin the left margin
*/
public void setLeftInset(double margin){
- CTTextBodyProperties bodyPr = getTextBodyPr();
+ CTTextBodyProperties bodyPr = getTextBodyPr(true);
if (bodyPr != null) {
if(margin == -1) bodyPr.unsetLIns();
else bodyPr.setLIns(Units.toEMU(margin));
* @param margin the right margin
*/
public void setRightInset(double margin){
- CTTextBodyProperties bodyPr = getTextBodyPr();
+ CTTextBodyProperties bodyPr = getTextBodyPr(true);
if (bodyPr != null) {
if(margin == -1) bodyPr.unsetRIns();
else bodyPr.setRIns(Units.toEMU(margin));
* @param margin the top margin
*/
public void setTopInset(double margin){
- CTTextBodyProperties bodyPr = getTextBodyPr();
+ CTTextBodyProperties bodyPr = getTextBodyPr(true);
if (bodyPr != null) {
if(margin == -1) bodyPr.unsetTIns();
else bodyPr.setTIns(Units.toEMU(margin));
@Override
public void setWordWrap(boolean wrap){
- CTTextBodyProperties bodyPr = getTextBodyPr();
+ CTTextBodyProperties bodyPr = getTextBodyPr(true);
if (bodyPr != null) {
bodyPr.setWrap(wrap ? STTextWrappingType.SQUARE : STTextWrappingType.NONE);
}
* @param value type of autofit
*/
public void setTextAutofit(TextAutofit value){
- CTTextBodyProperties bodyPr = getTextBodyPr();
+ CTTextBodyProperties bodyPr = getTextBodyPr(true);
if (bodyPr != null) {
if(bodyPr.isSetSpAutoFit()) bodyPr.unsetSpAutoFit();
if(bodyPr.isSetNoAutofit()) bodyPr.unsetNoAutofit();
}
protected CTTextBodyProperties getTextBodyPr(){
- CTTextBody textBody = getTextBody(false);
- return textBody == null ? null : textBody.getBodyPr();
+ return getTextBodyPr(false);
}
+ protected CTTextBodyProperties getTextBodyPr(boolean create) {
+ CTTextBody textBody = getTextBody(create);
+ if (textBody == null) {
+ return null;
+ }
+ CTTextBodyProperties textBodyPr = textBody.getBodyPr();
+ if (textBodyPr == null && create) {
+ textBodyPr = textBody.addNewBodyPr();
+ }
+ return textBodyPr;
+ }
+
protected abstract CTTextBody getTextBody(boolean create);
@Override
import org.apache.poi.sl.draw.DrawPaint;
import org.apache.poi.sl.draw.geom.*;
import org.apache.poi.sl.usermodel.*;
+import org.apache.poi.sl.usermodel.LineDecoration.*;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
import org.apache.poi.sl.usermodel.StrokeStyle.LineCap;
import org.apache.poi.sl.usermodel.StrokeStyle.LineCompound;
};
}
+
+ public DecorationShape getLineHeadDecoration(){
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWHEAD);
+ return (prop == null) ? null : DecorationShape.fromNativeId(prop.getPropertyValue());
+ }
+
+ public void setLineHeadDecoration(DecorationShape decoShape){
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWHEAD, decoShape == null ? -1 : decoShape.nativeId);
+ }
+
+ public DecorationSize getLineHeadWidth(){
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWWIDTH);
+ return (prop == null) ? null : DecorationSize.fromNativeId(prop.getPropertyValue());
+ }
+
+ public void setLineHeadWidth(DecorationSize decoSize){
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWWIDTH, decoSize == null ? -1 : decoSize.nativeId);
+ }
+
+ public DecorationSize getLineHeadLength(){
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWLENGTH);
+ return (prop == null) ? null : DecorationSize.fromNativeId(prop.getPropertyValue());
+ }
+
+ public void setLineHeadLength(DecorationSize decoSize){
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWLENGTH, decoSize == null ? -1 : decoSize.nativeId);
+ }
+
+ public DecorationShape getLineTailDecoration(){
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWHEAD);
+ return (prop == null) ? null : DecorationShape.fromNativeId(prop.getPropertyValue());
+ }
+
+ public void setLineTailDecoration(DecorationShape decoShape){
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWHEAD, decoShape == null ? -1 : decoShape.nativeId);
+ }
+
+ public DecorationSize getLineTailWidth(){
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWWIDTH);
+ return (prop == null) ? null : DecorationSize.fromNativeId(prop.getPropertyValue());
+ }
+ public void setLineTailWidth(DecorationSize decoSize){
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWWIDTH, decoSize == null ? -1 : decoSize.nativeId);
+ }
+
+ public DecorationSize getLineTailLength(){
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWLENGTH);
+ return (prop == null) ? null : DecorationSize.fromNativeId(prop.getPropertyValue());
+ }
+
+ public void setLineTailLength(DecorationSize decoSize){
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWLENGTH, decoSize == null ? -1 : decoSize.nativeId);
+ }
+
+
+
public LineDecoration getLineDecoration() {
return new LineDecoration() {
public DecorationShape getHeadShape() {
- return DecorationShape.NONE;
+ return HSLFSimpleShape.this.getLineHeadDecoration();
}
public DecorationSize getHeadWidth() {
- return DecorationSize.MEDIUM;
+ return HSLFSimpleShape.this.getLineHeadWidth();
}
public DecorationSize getHeadLength() {
- return DecorationSize.MEDIUM;
+ return HSLFSimpleShape.this.getLineHeadLength();
}
public DecorationShape getTailShape() {
- return DecorationShape.NONE;
+ return HSLFSimpleShape.this.getLineTailDecoration();
}
public DecorationSize getTailWidth() {
- return DecorationSize.MEDIUM;
+ return HSLFSimpleShape.this.getLineTailWidth();
}
public DecorationSize getTailLength() {
- return DecorationSize.MEDIUM;
+ return HSLFSimpleShape.this.getLineTailLength();
}
};
}
/**
* A common superclass of all shapes that can hold text.
- *
- * @author Yegor Kozlov
*/
public abstract class HSLFTextShape extends HSLFSimpleShape
implements TextShape<HSLFShape,HSLFTextParagraph> {
/**
* How to anchor the text
*/
- /* package */ static final int AnchorTop = 0;
- /* package */ static final int AnchorMiddle = 1;
- /* package */ static final int AnchorBottom = 2;
- /* package */ static final int AnchorTopCentered = 3;
- /* package */ static final int AnchorMiddleCentered = 4;
- /* package */ static final int AnchorBottomCentered = 5;
- /* package */ static final int AnchorTopBaseline = 6;
- /* package */ static final int AnchorBottomBaseline = 7;
- /* package */ static final int AnchorTopCenteredBaseline = 8;
- /* package */ static final int AnchorBottomCenteredBaseline = 9;
+ private enum HSLFTextAnchor {
+ TOP (0, VerticalAlignment.TOP, false, false),
+ MIDDLE (1, VerticalAlignment.MIDDLE, false, false),
+ BOTTOM (2, VerticalAlignment.BOTTOM, false, false),
+ TOP_CENTER (3, VerticalAlignment.TOP, true, false),
+ MIDDLE_CENTER (4, VerticalAlignment.MIDDLE, true, null),
+ BOTTOM_CENTER (5, VerticalAlignment.BOTTOM, true, false),
+ TOP_BASELINE (6, VerticalAlignment.TOP, false, true),
+ BOTTOM_BASELINE (7, VerticalAlignment.BOTTOM, false, true),
+ TOP_CENTER_BASELINE (8, VerticalAlignment.TOP, true, true),
+ BOTTOM_CENTER_BASELINE(9, VerticalAlignment.BOTTOM, true, true);
+
+ public final int nativeId;
+ public final VerticalAlignment vAlign;
+ public final boolean centered;
+ public final Boolean baseline;
+
+ HSLFTextAnchor(int nativeId, VerticalAlignment vAlign, boolean centered, Boolean baseline) {
+ this.nativeId = nativeId;
+ this.vAlign = vAlign;
+ this.centered = centered;
+ this.baseline = baseline;
+ }
+
+ static HSLFTextAnchor fromNativeId(int nativeId) {
+ for (HSLFTextAnchor ta : values()) {
+ if (ta.nativeId == nativeId) return ta;
+ }
+ return null;
+ }
+ }
/**
* Specifies that a line of text will continue on subsequent lines instead
*
* @see <a href=""></a>
*/
- boolean alignToBaseline = false;
+// boolean alignToBaseline = false;
/**
* Used to calculate text bounds
*
* @return the type of alignment
*/
- /* package */ int getAlignment(){
+ /* package */ HSLFTextAnchor getAlignment(){
AbstractEscherOptRecord opt = getEscherOptRecord();
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT);
- int align = HSLFTextShape.AnchorTop;
+ HSLFTextAnchor align = HSLFTextAnchor.TOP;
if (prop == null){
/**
* If vertical alignment was not found in the shape properties then try to
* fetch the master shape and search for the align property there.
*/
int type = getRunType();
- if(getSheet() != null && getSheet().getMasterSheet() != null){
- HSLFMasterSheet master = getSheet().getMasterSheet();
- HSLFTextShape masterShape = master.getPlaceholderByTextType(type);
- if(masterShape != null) align = masterShape.getAlignment();
+ HSLFSheet sh = getSheet();
+ HSLFMasterSheet master = (sh != null) ? sh.getMasterSheet() : null;
+ HSLFTextShape masterShape = (master != null) ? master.getPlaceholderByTextType(type) : null;
+ if (masterShape != null && type != TextHeaderAtom.OTHER_TYPE) {
+ align = masterShape.getAlignment();
} else {
//not found in the master sheet. Use the hardcoded defaults.
switch (type){
- case org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE:
- case org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE:
- align = HSLFTextShape.AnchorMiddle;
+ case TextHeaderAtom.TITLE_TYPE:
+ case TextHeaderAtom.CENTER_TITLE_TYPE:
+ align = HSLFTextAnchor.MIDDLE;
break;
default:
- align = HSLFTextShape.AnchorTop;
+ align = HSLFTextAnchor.TOP;
break;
}
}
} else {
- align = prop.getPropertyValue();
+ align = HSLFTextAnchor.fromNativeId(prop.getPropertyValue());
}
- alignToBaseline = (align == AnchorBottomBaseline || align == AnchorBottomCenteredBaseline
- || align == AnchorTopBaseline || align == AnchorTopCenteredBaseline);
+ if (align == null) {
+ align = HSLFTextAnchor.TOP;
+ }
return align;
}
* Sets the type of alignment for the text.
* One of the <code>Anchor*</code> constants defined in this class.
*
- * @param align - the type of alignment
- */
- /* package */ void setAlignment(Boolean isCentered, VerticalAlignment vAlign) {
- int align[];
- switch (vAlign) {
- case TOP:
- align = new int[]{AnchorTop, AnchorTopCentered, AnchorTopBaseline, AnchorTopCenteredBaseline};
- break;
- default:
- case MIDDLE:
- align = new int[]{AnchorMiddle, AnchorMiddleCentered, AnchorMiddle, AnchorMiddleCentered};
- break;
- case BOTTOM:
- align = new int[]{AnchorBottom, AnchorBottomCentered, AnchorBottomBaseline, AnchorBottomCenteredBaseline};
- break;
+ * @param isCentered horizontal centered?
+ * @param vAlign vertical alignment
+ * @param baseline aligned to baseline?
+ */
+ /* package */ void setAlignment(Boolean isCentered, VerticalAlignment vAlign, boolean baseline) {
+ for (HSLFTextAnchor hta : HSLFTextAnchor.values()) {
+ if (
+ (hta.centered == (isCentered != null && isCentered)) &&
+ (hta.vAlign == vAlign) &&
+ (hta.baseline == null || hta.baseline == baseline)
+ ) {
+ setEscherProperty(EscherProperties.TEXT__ANCHORTEXT, hta.nativeId);
+ break;
+ }
}
-
- int align2 = align[(isCentered ? 1 : 0)+(alignToBaseline ? 2 : 0)];
-
- setEscherProperty(EscherProperties.TEXT__ANCHORTEXT, align2);
}
/**
* this is only used for older versions less equals Office 2003
*/
public boolean isAlignToBaseline() {
- getAlignment();
- return alignToBaseline;
+ return getAlignment().baseline;
}
/**
* @param alignToBaseline if true, vertical alignment is relative to baseline
*/
public void setAlignToBaseline(boolean alignToBaseline) {
- this.alignToBaseline = alignToBaseline;
- setAlignment(isHorizontalCentered(), getVerticalAlignment());
+ setAlignment(isHorizontalCentered(), getVerticalAlignment(), alignToBaseline);
}
@Override
public boolean isHorizontalCentered() {
- int va = getAlignment();
- switch (va) {
- case AnchorTopCentered:
- case AnchorTopCenteredBaseline:
- case AnchorBottomCentered:
- case AnchorBottomCenteredBaseline:
- case AnchorMiddleCentered:
- return true;
- default:
- return false;
- }
+ return getAlignment().centered;
}
@Override
public void setHorizontalCentered(Boolean isCentered) {
- setAlignment(isCentered, getVerticalAlignment());
+ setAlignment(isCentered, getVerticalAlignment(), getAlignment().baseline);
}
@Override
public VerticalAlignment getVerticalAlignment() {
- int va = getAlignment();
- switch (va) {
- case AnchorTop:
- case AnchorTopCentered:
- case AnchorTopBaseline:
- case AnchorTopCenteredBaseline: return VerticalAlignment.TOP;
- case AnchorBottom:
- case AnchorBottomCentered:
- case AnchorBottomBaseline:
- case AnchorBottomCenteredBaseline: return VerticalAlignment.BOTTOM;
- default:
- case AnchorMiddle:
- case AnchorMiddleCentered: return VerticalAlignment.MIDDLE;
- }
+ return getAlignment().vAlign;
}
@Override
public void setVerticalAlignment(VerticalAlignment vAlign) {
- setAlignment(isHorizontalCentered(), vAlign);
+ setAlignment(isHorizontalCentered(), vAlign, getAlignment().baseline);
}
/**
if (_paragraphs.isEmpty()) {
logger.log(POILogger.WARN, "TextRecord didn't contained any text lines");
}
-// initParagraphsFromSheetRecords();
-// if (_paragraphs.isEmpty()) {
-// List<List<HSLFTextParagraph>> llhtp = HSLFTextParagraph.findTextParagraphs(_txtbox);
-// if (!llhtp.isEmpty()) {
-// _paragraphs.addAll(llhtp.get(0));
-// }
-// }
}
for (HSLFTextParagraph p : _paragraphs) {
}
}
-// protected void initParagraphsFromSheetRecords(){
-// EscherTextboxWrapper txtbox = getEscherTextboxWrapper();
-// HSLFSheet sheet = getSheet();
-//
-// if (sheet == null || txtbox == null) return;
-// List<List<HSLFTextParagraph>> sheetRuns = _sheet.getTextParagraphs();
-// if (sheetRuns == null) return;
-//
-// _paragraphs.clear();
-// OutlineTextRefAtom ota = (OutlineTextRefAtom)txtbox.findFirstOfType(OutlineTextRefAtom.typeID);
-//
-// if (ota != null) {
-// int idx = ota.getTextIndex();
-// for (List<HSLFTextParagraph> r : sheetRuns) {
-// if (r.isEmpty()) continue;
-// int ridx = r.get(0).getIndex();
-// if (ridx > idx) break;
-// if (ridx == idx) _paragraphs.addAll(r);
-// }
-// if(_paragraphs.isEmpty()) {
-// logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);
-// }
-// } else {
-// int shapeId = getShapeId();
-// for (List<HSLFTextParagraph> r : sheetRuns) {
-// if (r.isEmpty()) continue;
-// if (r.get(0).getShapeId() == shapeId) _paragraphs.addAll(r);
-// }
-// }
-//
-// // ensure the same references child records of TextRun - see #48916
-//// if(_txtrun != null) {
-//// for (int i = 0; i < child.length; i++) {
-//// for (Record r : _txtrun.getRecords()) {
-//// if (child[i].getRecordType() == r.getRecordType()) {
-//// child[i] = r;
-//// }
-//// }
-//// }
-//// }
-// }
-
- /*
- // 0xB acts like cariage return in page titles and like blank in the others
- char replChr;
- switch(tha == null ? -1 : tha.getTextType()) {
- case -1:
- case TextHeaderAtom.TITLE_TYPE:
- case TextHeaderAtom.CENTER_TITLE_TYPE:
- replChr = '\n';
- break;
- default:
- replChr = ' ';
- break;
- }
-
- // PowerPoint seems to store files with \r as the line break
- // The messes things up on everything but a Mac, so translate
- // them to \n
- String text = rawText.replace('\r','\n').replace('\u000b', replChr);
- */
-
/**
* Return <code>OEPlaceholderAtom</code>, the atom that describes a placeholder.
*
@Override
public TextDirection getTextDirection() {
// TODO: determine vertical text setting
+ // see 2.3.22.10 Geometry Text Boolean Properties
return TextDirection.HORIZONTAL;
}
+ @Override
+ public void setTextDirection(TextDirection orientation) {
+ // TODO: determine vertical text setting
+ // see 2.3.22.10 Geometry Text Boolean Properties / gtextFVertical [MS-ODRAW]
+ }
+
+ @Override
+ public Double getTextRotation() {
+ // see 2.4.6 MSOCDIR
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__FONTROTATION);
+ return (prop == null) ? null : (90. * prop.getPropertyValue());
+ }
+
+ @Override
+ public void setTextRotation(Double rotation) {
+ AbstractEscherOptRecord opt = getEscherOptRecord();
+ if (rotation == null) {
+ opt.removeEscherProperty(EscherProperties.TEXT__FONTROTATION);
+ } else {
+ int rot = (int)(Math.round(rotation / 90.) % 4L);
+ setEscherProperty(EscherProperties.TEXT__FONTROTATION, rot);
+ }
+ }
+
/**
* Returns the raw text content of the shape. This hasn't had any
* changes applied to it, and so is probably unlikely to print