mirror of https://github.com/apache/poi.git
#62381 - Fix rendering of AutoShapes
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1831743 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
1af53bbf7f
commit
e371ce46cc
|
@ -45,8 +45,8 @@ public class DrawShape implements Drawable {
|
||||||
* @param shape the shape to render
|
* @param shape the shape to render
|
||||||
* @return {@code true} if HSLF implementation is used
|
* @return {@code true} if HSLF implementation is used
|
||||||
*/
|
*/
|
||||||
protected static boolean isHSLF(Shape<?,?> shape) {
|
static boolean isHSLF(Object shape) {
|
||||||
return shape.getClass().getCanonicalName().toLowerCase(Locale.ROOT).contains("hslf");
|
return shape.getClass().getName().toLowerCase(Locale.ROOT).contains("hslf");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,21 +60,19 @@ public class DrawShape implements Drawable {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaceableShape<?,?> ps = (PlaceableShape<?,?>)shape;
|
final PlaceableShape<?,?> ps = (PlaceableShape<?,?>)shape;
|
||||||
final boolean isHSLF = isHSLF(shape);
|
final boolean isHSLF = isHSLF(shape);
|
||||||
AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
|
|
||||||
if (tx == null) {
|
final Rectangle2D anchor = getAnchor(graphics, ps);
|
||||||
tx = new AffineTransform();
|
|
||||||
|
if (shape.getShapeName().startsWith("rotate")) {
|
||||||
|
System.out.println(String.format("%s x: %.3f y: %.3f cx: %.3f cy: %.3f / x: %.3f y: %.3f cx: %.3f cy: %.3f ",
|
||||||
|
shape.getShapeName(), shape.getAnchor().getX(), shape.getAnchor().getY(),
|
||||||
|
shape.getAnchor().getWidth(), shape.getAnchor().getHeight(),
|
||||||
|
anchor.getX(), anchor.getY(), anchor.getWidth(), anchor.getHeight()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// we saw one document failing here, probably the format is slightly broken, but
|
|
||||||
// maybe better to try to handle it more gracefully
|
|
||||||
java.awt.Shape transformedShape = tx.createTransformedShape(ps.getAnchor());
|
|
||||||
if(transformedShape == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Rectangle2D anchor = transformedShape.getBounds2D();
|
|
||||||
|
|
||||||
char cmds[] = isHSLF ? new char[]{ 'h','v','r' } : new char[]{ 'r','h','v' };
|
char cmds[] = isHSLF ? new char[]{ 'h','v','r' } : new char[]{ 'r','h','v' };
|
||||||
for (char ch : cmds) {
|
for (char ch : cmds) {
|
||||||
|
@ -103,61 +101,10 @@ public class DrawShape implements Drawable {
|
||||||
double centerX = anchor.getCenterX();
|
double centerX = anchor.getCenterX();
|
||||||
double centerY = anchor.getCenterY();
|
double centerY = anchor.getCenterY();
|
||||||
|
|
||||||
// normalize rotation
|
|
||||||
rotation %= 360.;
|
|
||||||
if (rotation < 0) {
|
|
||||||
rotation += 360.;
|
|
||||||
}
|
|
||||||
|
|
||||||
int quadrant = (((int)rotation+45)/90)%4;
|
|
||||||
double scaleX = 1.0, scaleY = 1.0;
|
|
||||||
|
|
||||||
// scale to bounding box (bug #53176)
|
|
||||||
if (quadrant == 1 || quadrant == 3) {
|
|
||||||
// In quadrant 1 and 3, which is basically a shape in a more or less portrait orientation
|
|
||||||
// (45-135 degrees and 225-315 degrees), we need to first rotate the shape by a multiple
|
|
||||||
// of 90 degrees and then resize the bounding box to its original bbox. After that we can
|
|
||||||
// rotate the shape to the exact rotation amount.
|
|
||||||
// It's strange that you'll need to rotate the shape back and forth again, but you can
|
|
||||||
// think of it, as if you paint the shape on a canvas. First you rotate the canvas, which might
|
|
||||||
// be already (differently) scaled, so you can paint the shape in its default orientation
|
|
||||||
// and later on, turn it around again to compare it with its original size ...
|
|
||||||
|
|
||||||
AffineTransform txs;
|
|
||||||
if (isHSLF) {
|
|
||||||
txs = new AffineTransform(tx);
|
|
||||||
} else {
|
|
||||||
// this handling is only based on try and error ... not sure why xslf is handled differently.
|
|
||||||
txs = new AffineTransform();
|
|
||||||
txs.translate(centerX, centerY);
|
|
||||||
txs.rotate(Math.PI/2.); // actually doesn't matter if +/- 90 degrees
|
|
||||||
txs.translate(-centerX, -centerY);
|
|
||||||
txs.concatenate(tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
txs.translate(centerX, centerY);
|
|
||||||
txs.rotate(Math.PI/2.);
|
|
||||||
txs.translate(-centerX, -centerY);
|
|
||||||
|
|
||||||
Rectangle2D anchor2 = txs.createTransformedShape(ps.getAnchor()).getBounds2D();
|
|
||||||
|
|
||||||
scaleX = safeScale(anchor.getWidth(), anchor2.getWidth());
|
|
||||||
scaleY = safeScale(anchor.getHeight(), anchor2.getHeight());
|
|
||||||
} else {
|
|
||||||
quadrant = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// transformation is applied reversed ...
|
// transformation is applied reversed ...
|
||||||
graphics.translate(centerX, centerY);
|
graphics.translate(centerX, centerY);
|
||||||
double rot = Math.toRadians(rotation-quadrant*90.);
|
graphics.rotate(Math.toRadians(rotation));
|
||||||
if (rot != 0) {
|
|
||||||
graphics.rotate(rot);
|
|
||||||
}
|
|
||||||
graphics.scale(scaleX, scaleY);
|
|
||||||
rot = Math.toRadians(quadrant*90.);
|
|
||||||
if (rot != 0) {
|
|
||||||
graphics.rotate(rot);
|
|
||||||
}
|
|
||||||
graphics.translate(-centerX, -centerY);
|
graphics.translate(-centerX, -centerY);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -183,7 +130,85 @@ public class DrawShape implements Drawable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Rectangle2D getAnchor(Graphics2D graphics, PlaceableShape<?,?> shape) {
|
public static Rectangle2D getAnchor(Graphics2D graphics, PlaceableShape<?,?> shape) {
|
||||||
return getAnchor(graphics, shape.getAnchor());
|
// return getAnchor(graphics, shape.getAnchor());
|
||||||
|
|
||||||
|
final boolean isHSLF = isHSLF(shape);
|
||||||
|
AffineTransform tx = graphics == null ? null : (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
|
||||||
|
if (tx == null) {
|
||||||
|
tx = new AffineTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
final double rotation = ((shape.getRotation() % 360.) + 360.) % 360.;
|
||||||
|
final int quadrant = (((int)rotation+45)/90)%4;
|
||||||
|
|
||||||
|
final Rectangle2D normalizedShape;
|
||||||
|
|
||||||
|
// scale to bounding box (bug #53176)
|
||||||
|
if (quadrant == 1 || quadrant == 3) {
|
||||||
|
// In a rotated quadrant 1 (=45-135 degrees) and 3 (=225-315 degrees), which is basically a shape in a
|
||||||
|
// more or less portrait orientation, Powerpoint doesn't use the normal shape anchor,
|
||||||
|
// but rotate it 90 degress and apply the group transformations.
|
||||||
|
// We try to revert that distortion and return the normalized anchor.
|
||||||
|
// It's strange that you'll need to rotate the shape back and forth again, but you can
|
||||||
|
// think of it, as if you paint the shape on a canvas. First you rotate the canvas, which might
|
||||||
|
// be already (differently) scaled, so you can paint the shape in its default orientation
|
||||||
|
// and later on, turn it around again to compare it with its original size ...
|
||||||
|
|
||||||
|
|
||||||
|
final Rectangle2D shapeAnchor = shape.getAnchor();
|
||||||
|
final Rectangle2D anchorO = tx.createTransformedShape(shapeAnchor).getBounds2D();
|
||||||
|
|
||||||
|
final Rectangle2D anchorT;
|
||||||
|
{
|
||||||
|
final double centerX = anchorO.getCenterX();
|
||||||
|
final double centerY = anchorO.getCenterY();
|
||||||
|
final AffineTransform txs2 = new AffineTransform();
|
||||||
|
|
||||||
|
// this handling is only based on try and error ... not sure why h/xslf is handled differently.
|
||||||
|
|
||||||
|
if (!isHSLF) {
|
||||||
|
txs2.translate(centerX, centerY);
|
||||||
|
txs2.quadrantRotate(1);
|
||||||
|
txs2.translate(-centerX, -centerY);
|
||||||
|
txs2.concatenate(tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
txs2.translate(centerX, centerY);
|
||||||
|
txs2.quadrantRotate(3);
|
||||||
|
txs2.translate(-centerX, -centerY);
|
||||||
|
|
||||||
|
if (isHSLF) {
|
||||||
|
txs2.concatenate(tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
anchorT = txs2.createTransformedShape(shapeAnchor).getBounds2D();
|
||||||
|
}
|
||||||
|
|
||||||
|
final double scaleX2 = safeScale(anchorO.getWidth(), anchorT.getWidth());
|
||||||
|
final double scaleY2 = safeScale(anchorO.getHeight(), anchorT.getHeight());
|
||||||
|
|
||||||
|
{
|
||||||
|
double centerX = shapeAnchor.getCenterX();
|
||||||
|
double centerY = shapeAnchor.getCenterY();
|
||||||
|
final AffineTransform txs2 = new AffineTransform();
|
||||||
|
txs2.translate(centerX, centerY);
|
||||||
|
// no need to rotate back and forth, just apply scaling inverted
|
||||||
|
txs2.scale(scaleY2, scaleX2);
|
||||||
|
txs2.translate(-centerX, -centerY);
|
||||||
|
|
||||||
|
normalizedShape = txs2.createTransformedShape(shapeAnchor).getBounds2D();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
normalizedShape = shape.getAnchor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx.isIdentity()) {
|
||||||
|
return normalizedShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final java.awt.Shape anc = tx.createTransformedShape(normalizedShape);
|
||||||
|
return (anc != null) ? anc.getBounds2D() : normalizedShape;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Rectangle2D getAnchor(Graphics2D graphics, Rectangle2D anchor) {
|
public static Rectangle2D getAnchor(Graphics2D graphics, Rectangle2D anchor) {
|
||||||
|
|
|
@ -44,7 +44,8 @@ public class Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guide getAdjustValue(String name){
|
public Guide getAdjustValue(String name){
|
||||||
return _props.getAdjustValue(name);
|
// ignore HSLF props for now ... the results with default value are usually better - see #59004
|
||||||
|
return (_props.getClass().getName().contains("hslf")) ? null : _props.getAdjustValue(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getValue(String key){
|
public double getValue(String key){
|
||||||
|
|
|
@ -65,7 +65,7 @@ implements XSLFShapeContainer, GroupShape<XSLFShape,XSLFTextParagraph> {
|
||||||
|
|
||||||
protected XSLFGroupShape(CTGroupShape shape, XSLFSheet sheet){
|
protected XSLFGroupShape(CTGroupShape shape, XSLFSheet sheet){
|
||||||
super(shape,sheet);
|
super(shape,sheet);
|
||||||
_shapes = XSLFSheet.buildShapes(shape, sheet);
|
_shapes = XSLFSheet.buildShapes(shape, this);
|
||||||
_grpSpPr = shape.getGrpSpPr();
|
_grpSpPr = shape.getGrpSpPr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,9 @@ implements XSLFShapeContainer, Sheet<XSLFShape,XSLFTextParagraph> {
|
||||||
throw new IllegalStateException("SlideShow was not found");
|
throw new IllegalStateException("SlideShow was not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static List<XSLFShape> buildShapes(CTGroupShape spTree, XSLFSheet sheet){
|
protected static List<XSLFShape> buildShapes(CTGroupShape spTree, XSLFShapeContainer parent){
|
||||||
|
final XSLFSheet sheet = (parent instanceof XSLFSheet) ? (XSLFSheet)parent : ((XSLFShape)parent).getSheet();
|
||||||
|
|
||||||
List<XSLFShape> shapes = new ArrayList<>();
|
List<XSLFShape> shapes = new ArrayList<>();
|
||||||
XmlCursor cur = spTree.newCursor();
|
XmlCursor cur = spTree.newCursor();
|
||||||
try {
|
try {
|
||||||
|
@ -133,7 +135,7 @@ implements XSLFShapeContainer, Sheet<XSLFShape,XSLFTextParagraph> {
|
||||||
if (cur.toChild(PackageNamespaces.MARKUP_COMPATIBILITY, "Choice") && cur.toFirstChild()) {
|
if (cur.toChild(PackageNamespaces.MARKUP_COMPATIBILITY, "Choice") && cur.toFirstChild()) {
|
||||||
try {
|
try {
|
||||||
CTGroupShape grp = CTGroupShape.Factory.parse(cur.newXMLStreamReader());
|
CTGroupShape grp = CTGroupShape.Factory.parse(cur.newXMLStreamReader());
|
||||||
shapes.addAll(buildShapes(grp, sheet));
|
shapes.addAll(buildShapes(grp, parent));
|
||||||
} catch (XmlException e) {
|
} catch (XmlException e) {
|
||||||
LOG.log(POILogger.DEBUG, "unparsable alternate content", e);
|
LOG.log(POILogger.DEBUG, "unparsable alternate content", e);
|
||||||
}
|
}
|
||||||
|
@ -145,6 +147,10 @@ implements XSLFShapeContainer, Sheet<XSLFShape,XSLFTextParagraph> {
|
||||||
cur.dispose();
|
cur.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (final XSLFShape s : shapes) {
|
||||||
|
s.setParent(parent);
|
||||||
|
}
|
||||||
|
|
||||||
return shapes;
|
return shapes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue