#54210 When saving PPT to PNG, some text is rendered backwards
#53189 Shapes drawn wrongly when ppt file converted to image fix line decoration (HSLF) git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1714290 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
67b4477a7e
commit
a0e06add7a
@ -48,6 +48,7 @@ import org.apache.poi.sl.draw.geom.CustomGeometry;
|
||||
import org.apache.poi.sl.draw.geom.Outline;
|
||||
import org.apache.poi.sl.draw.geom.Path;
|
||||
import org.apache.poi.sl.usermodel.LineDecoration;
|
||||
import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape;
|
||||
import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize;
|
||||
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
|
||||
import org.apache.poi.sl.usermodel.Shadow;
|
||||
@ -60,6 +61,8 @@ import org.apache.poi.util.Units;
|
||||
|
||||
public class DrawSimpleShape extends DrawShape {
|
||||
|
||||
private static final double DECO_SIZE_POW = 1.5d;
|
||||
|
||||
public DrawSimpleShape(SimpleShape<?,?> shape) {
|
||||
super(shape);
|
||||
}
|
||||
@ -131,8 +134,17 @@ public class DrawSimpleShape extends DrawShape {
|
||||
}
|
||||
|
||||
protected Outline getTailDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {
|
||||
if (deco == null || stroke == null) {
|
||||
return null;
|
||||
}
|
||||
DecorationSize tailLength = deco.getTailLength();
|
||||
if (tailLength == null) {
|
||||
tailLength = DecorationSize.MEDIUM;
|
||||
}
|
||||
DecorationSize tailWidth = deco.getTailWidth();
|
||||
if (tailWidth == null) {
|
||||
tailWidth = DecorationSize.MEDIUM;
|
||||
}
|
||||
|
||||
double lineWidth = Math.max(2.5, stroke.getLineWidth());
|
||||
|
||||
@ -146,9 +158,16 @@ public class DrawSimpleShape extends DrawShape {
|
||||
java.awt.Shape tailShape = null;
|
||||
Path p = null;
|
||||
Rectangle2D bounds;
|
||||
final double scaleY = Math.pow(2, tailWidth.ordinal()+1);
|
||||
final double scaleX = Math.pow(2, tailLength.ordinal()+1);
|
||||
switch (deco.getTailShape()) {
|
||||
final double scaleY = Math.pow(DECO_SIZE_POW, tailWidth.ordinal()+1);
|
||||
final double scaleX = Math.pow(DECO_SIZE_POW, tailLength.ordinal()+1);
|
||||
|
||||
DecorationShape tailShapeEnum = deco.getTailShape();
|
||||
|
||||
if (tailShapeEnum == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (tailShapeEnum) {
|
||||
case OVAL:
|
||||
p = new Path();
|
||||
tailShape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
|
||||
@ -189,8 +208,17 @@ public class DrawSimpleShape extends DrawShape {
|
||||
}
|
||||
|
||||
protected Outline getHeadDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {
|
||||
if (deco == null || stroke == null) {
|
||||
return null;
|
||||
}
|
||||
DecorationSize headLength = deco.getHeadLength();
|
||||
if (headLength == null) {
|
||||
headLength = DecorationSize.MEDIUM;
|
||||
}
|
||||
DecorationSize headWidth = deco.getHeadWidth();
|
||||
if (headWidth == null) {
|
||||
headWidth = DecorationSize.MEDIUM;
|
||||
}
|
||||
|
||||
double lineWidth = Math.max(2.5, stroke.getLineWidth());
|
||||
|
||||
@ -204,9 +232,15 @@ public class DrawSimpleShape extends DrawShape {
|
||||
java.awt.Shape headShape = null;
|
||||
Path p = null;
|
||||
Rectangle2D bounds;
|
||||
final double scaleY = Math.pow(2, headWidth.ordinal()+1);
|
||||
final double scaleX = Math.pow(2, headLength.ordinal()+1);
|
||||
switch (deco.getHeadShape()) {
|
||||
final double scaleY = Math.pow(DECO_SIZE_POW, headWidth.ordinal()+1);
|
||||
final double scaleX = Math.pow(DECO_SIZE_POW, headLength.ordinal()+1);
|
||||
DecorationShape headShapeEnum = deco.getHeadShape();
|
||||
|
||||
if (headShapeEnum == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (headShapeEnum) {
|
||||
case OVAL:
|
||||
p = new Path();
|
||||
headShape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
|
||||
|
@ -37,8 +37,10 @@ public class DrawTextShape extends DrawSimpleShape {
|
||||
public void drawContent(Graphics2D graphics) {
|
||||
fixFonts(graphics);
|
||||
|
||||
Rectangle2D anchor = DrawShape.getAnchor(graphics, getShape());
|
||||
Insets2D insets = getShape().getInsets();
|
||||
TextShape<?,?> s = getShape();
|
||||
|
||||
Rectangle2D anchor = DrawShape.getAnchor(graphics, s);
|
||||
Insets2D insets = s.getInsets();
|
||||
double x = anchor.getX() + insets.left;
|
||||
double y = anchor.getY();
|
||||
|
||||
@ -50,39 +52,42 @@ public class DrawTextShape extends DrawSimpleShape {
|
||||
// (see DrawShape#applyTransform ), but we need to restore it to avoid painting "upside down".
|
||||
// See Bugzilla 54210.
|
||||
|
||||
if(getShape().getFlipVertical()){
|
||||
graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
|
||||
graphics.scale(1, -1);
|
||||
graphics.translate(-anchor.getX(), -anchor.getY());
|
||||
|
||||
// text in vertically flipped shapes is rotated by 180 degrees
|
||||
double centerX = anchor.getX() + anchor.getWidth()/2;
|
||||
double centerY = anchor.getY() + anchor.getHeight()/2;
|
||||
graphics.translate(centerX, centerY);
|
||||
graphics.rotate(Math.toRadians(180));
|
||||
graphics.translate(-centerX, -centerY);
|
||||
}
|
||||
boolean vertFlip = s.getFlipVertical();
|
||||
boolean horzFlip = s.getFlipHorizontal();
|
||||
ShapeContainer<?,?> sc = s.getParent();
|
||||
while (sc instanceof PlaceableShape) {
|
||||
PlaceableShape<?,?> ps = (PlaceableShape<?,?>)sc;
|
||||
vertFlip ^= ps.getFlipVertical();
|
||||
horzFlip ^= ps.getFlipHorizontal();
|
||||
sc = ps.getParent();
|
||||
};
|
||||
|
||||
// Horizontal flipping applies only to shape outline and not to the text in the shape.
|
||||
// Applying flip second time restores the original not-flipped transform
|
||||
if(getShape().getFlipHorizontal()){
|
||||
if (horzFlip ^ vertFlip) {
|
||||
graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
|
||||
graphics.scale(-1, 1);
|
||||
graphics.translate(-anchor.getX(), -anchor.getY());
|
||||
}
|
||||
|
||||
Double textRot = s.getTextRotation();
|
||||
if (textRot != null) {
|
||||
graphics.translate(anchor.getCenterX(), anchor.getCenterY());
|
||||
graphics.rotate(Math.toRadians(textRot));
|
||||
graphics.translate(-anchor.getCenterX(), -anchor.getCenterY());
|
||||
}
|
||||
|
||||
// first dry-run to calculate the total height of the text
|
||||
double textHeight = getShape().getTextHeight();
|
||||
double textHeight = s.getTextHeight();
|
||||
|
||||
switch (getShape().getVerticalAlignment()){
|
||||
switch (s.getVerticalAlignment()){
|
||||
default:
|
||||
case TOP:
|
||||
y += insets.top;
|
||||
break;
|
||||
case BOTTOM:
|
||||
y += anchor.getHeight() - textHeight - insets.bottom;
|
||||
break;
|
||||
default:
|
||||
case MIDDLE:
|
||||
double delta = anchor.getHeight() - textHeight - insets.top - insets.bottom;
|
||||
y += insets.top + delta/2;
|
||||
|
@ -22,19 +22,28 @@ public interface LineDecoration {
|
||||
* Represents the shape decoration that appears at the ends of lines.
|
||||
*/
|
||||
enum DecorationShape {
|
||||
NONE(1),
|
||||
TRIANGLE(2),
|
||||
STEALTH(3),
|
||||
DIAMOND(4),
|
||||
OVAL(5),
|
||||
ARROW(6);
|
||||
NONE(0,1),
|
||||
TRIANGLE(1,2),
|
||||
STEALTH(2,3),
|
||||
DIAMOND(3,4),
|
||||
OVAL(4,5),
|
||||
ARROW(5,6);
|
||||
|
||||
public final int nativeId;
|
||||
public final int ooxmlId;
|
||||
|
||||
DecorationShape(int ooxmlId) {
|
||||
DecorationShape(int nativeId, int ooxmlId) {
|
||||
this.nativeId = nativeId;
|
||||
this.ooxmlId = ooxmlId;
|
||||
}
|
||||
|
||||
public static DecorationShape fromNativeId(int nativeId) {
|
||||
for (DecorationShape ld : values()) {
|
||||
if (ld.nativeId == nativeId) return ld;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static DecorationShape fromOoxmlId(int ooxmlId) {
|
||||
for (DecorationShape ds : values()) {
|
||||
if (ds.ooxmlId == ooxmlId) return ds;
|
||||
@ -44,16 +53,25 @@ public interface LineDecoration {
|
||||
}
|
||||
|
||||
enum DecorationSize {
|
||||
SMALL(1),
|
||||
MEDIUM(2),
|
||||
LARGE(3);
|
||||
SMALL(0, 1),
|
||||
MEDIUM(1, 2),
|
||||
LARGE(2, 3);
|
||||
|
||||
public final int nativeId;
|
||||
public final int ooxmlId;
|
||||
|
||||
DecorationSize(int ooxmlId) {
|
||||
DecorationSize(int nativeId, int ooxmlId) {
|
||||
this.nativeId = nativeId;
|
||||
this.ooxmlId = ooxmlId;
|
||||
}
|
||||
|
||||
public static DecorationSize fromNativeId(int nativeId) {
|
||||
for (DecorationSize ld : values()) {
|
||||
if (ld.nativeId == nativeId) return ld;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static DecorationSize fromOoxmlId(int ooxmlId) {
|
||||
for (DecorationSize ds : values()) {
|
||||
if (ds.ooxmlId == ooxmlId) return ds;
|
||||
|
@ -200,6 +200,28 @@ public interface TextShape<
|
||||
*/
|
||||
TextDirection getTextDirection();
|
||||
|
||||
/**
|
||||
* sets the vertical orientation
|
||||
* @param orientation vertical orientation of the text
|
||||
*/
|
||||
void setTextDirection(TextDirection orientation);
|
||||
|
||||
/**
|
||||
* The text rotation can be independent specified from the shape rotation.
|
||||
* For XSLF this can be an arbitrary degree, for HSLF the degree is given in steps of 90 degrees
|
||||
*
|
||||
* @return text rotation in degrees, returns null if no rotation is given
|
||||
*/
|
||||
Double getTextRotation();
|
||||
|
||||
/**
|
||||
* Sets the text rotation.
|
||||
* For XSLF this can ben an arbitrary degree, for HSLF the rotation is rounded to next 90 degree step
|
||||
*
|
||||
* @param rotation the text rotation, or null to unset the rotation
|
||||
*/
|
||||
void setTextRotation(Double rotation);
|
||||
|
||||
/**
|
||||
* Sets the text placeholder
|
||||
*/
|
||||
|
@ -161,7 +161,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
|
||||
@Override
|
||||
public void setVerticalAlignment(VerticalAlignment anchor){
|
||||
CTTextBodyProperties bodyPr = getTextBodyPr();
|
||||
CTTextBodyProperties bodyPr = getTextBodyPr(true);
|
||||
if (bodyPr != null) {
|
||||
if(anchor == null) {
|
||||
if(bodyPr.isSetAnchor()) bodyPr.unsetAnchor();
|
||||
@ -189,7 +189,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
|
||||
@Override
|
||||
public void setHorizontalCentered(Boolean isCentered){
|
||||
CTTextBodyProperties bodyPr = getTextBodyPr();
|
||||
CTTextBodyProperties bodyPr = getTextBodyPr(true);
|
||||
if (bodyPr != null) {
|
||||
if (isCentered == null) {
|
||||
if (bodyPr.isSetAnchorCtr()) bodyPr.unsetAnchorCtr();
|
||||
@ -214,12 +214,9 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
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();
|
||||
@ -229,9 +226,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return vertical orientation of the text
|
||||
*/
|
||||
@Override
|
||||
public TextDirection getTextDirection(){
|
||||
CTTextBodyProperties bodyPr = getTextBodyPr();
|
||||
if (bodyPr != null) {
|
||||
@ -243,6 +238,23 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
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
|
||||
@ -341,7 +353,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
* @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));
|
||||
@ -355,7 +367,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
* @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));
|
||||
@ -369,7 +381,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
* @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));
|
||||
@ -383,7 +395,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
* @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));
|
||||
@ -421,7 +433,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
|
||||
@Override
|
||||
public void setWordWrap(boolean wrap){
|
||||
CTTextBodyProperties bodyPr = getTextBodyPr();
|
||||
CTTextBodyProperties bodyPr = getTextBodyPr(true);
|
||||
if (bodyPr != null) {
|
||||
bodyPr.setWrap(wrap ? STTextWrappingType.SQUARE : STTextWrappingType.NONE);
|
||||
}
|
||||
@ -435,7 +447,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
* @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();
|
||||
@ -464,8 +476,19 @@ public abstract class XSLFTextShape extends XSLFSimpleShape
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -26,6 +26,7 @@ import org.apache.poi.hslf.record.*;
|
||||
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;
|
||||
@ -463,31 +464,99 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
|
||||
};
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -54,8 +54,6 @@ import org.apache.poi.util.Units;
|
||||
|
||||
/**
|
||||
* A common superclass of all shapes that can hold text.
|
||||
*
|
||||
* @author Yegor Kozlov
|
||||
*/
|
||||
public abstract class HSLFTextShape extends HSLFSimpleShape
|
||||
implements TextShape<HSLFShape,HSLFTextParagraph> {
|
||||
@ -63,16 +61,37 @@ 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
|
||||
@ -120,7 +139,7 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
|
||||
*
|
||||
* @see <a href=""></a>
|
||||
*/
|
||||
boolean alignToBaseline = false;
|
||||
// boolean alignToBaseline = false;
|
||||
|
||||
/**
|
||||
* Used to calculate text bounds
|
||||
@ -279,38 +298,40 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
@ -319,26 +340,21 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
|
||||
* 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
|
||||
* @param isCentered horizontal centered?
|
||||
* @param vAlign vertical alignment
|
||||
* @param baseline aligned to baseline?
|
||||
*/
|
||||
/* 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};
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -346,8 +362,7 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
|
||||
* this is only used for older versions less equals Office 2003
|
||||
*/
|
||||
public boolean isAlignToBaseline() {
|
||||
getAlignment();
|
||||
return alignToBaseline;
|
||||
return getAlignment().baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -356,51 +371,27 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -583,13 +574,6 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
|
||||
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) {
|
||||
@ -615,68 +599,6 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
*
|
||||
@ -755,9 +677,35 @@ implements TextShape<HSLFShape,HSLFTextParagraph> {
|
||||
@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
|
||||
|
Loading…
Reference in New Issue
Block a user