|
|
@@ -45,8 +45,8 @@ public class DrawShape implements Drawable { |
|
|
|
* @param shape the shape to render |
|
|
|
* @return {@code true} if HSLF implementation is used |
|
|
|
*/ |
|
|
|
protected static boolean isHSLF(Shape<?,?> shape) { |
|
|
|
return shape.getClass().getCanonicalName().toLowerCase(Locale.ROOT).contains("hslf"); |
|
|
|
static boolean isHSLF(Object shape) { |
|
|
|
return shape.getClass().getName().toLowerCase(Locale.ROOT).contains("hslf"); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
@@ -60,21 +60,19 @@ public class DrawShape implements Drawable { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
PlaceableShape<?,?> ps = (PlaceableShape<?,?>)shape; |
|
|
|
final PlaceableShape<?,?> ps = (PlaceableShape<?,?>)shape; |
|
|
|
final boolean isHSLF = isHSLF(shape); |
|
|
|
AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM); |
|
|
|
if (tx == null) { |
|
|
|
tx = new AffineTransform(); |
|
|
|
} |
|
|
|
|
|
|
|
// 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 = getAnchor(graphics, ps); |
|
|
|
|
|
|
|
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() |
|
|
|
)); |
|
|
|
} |
|
|
|
|
|
|
|
final Rectangle2D anchor = transformedShape.getBounds2D(); |
|
|
|
|
|
|
|
char cmds[] = isHSLF ? new char[]{ 'h','v','r' } : new char[]{ 'r','h','v' }; |
|
|
|
for (char ch : cmds) { |
|
|
@@ -103,61 +101,10 @@ public class DrawShape implements Drawable { |
|
|
|
double centerX = anchor.getCenterX(); |
|
|
|
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 ... |
|
|
|
graphics.translate(centerX, centerY); |
|
|
|
double rot = Math.toRadians(rotation-quadrant*90.); |
|
|
|
if (rot != 0) { |
|
|
|
graphics.rotate(rot); |
|
|
|
} |
|
|
|
graphics.scale(scaleX, scaleY); |
|
|
|
rot = Math.toRadians(quadrant*90.); |
|
|
|
if (rot != 0) { |
|
|
|
graphics.rotate(rot); |
|
|
|
} |
|
|
|
graphics.rotate(Math.toRadians(rotation)); |
|
|
|
graphics.translate(-centerX, -centerY); |
|
|
|
} |
|
|
|
break; |
|
|
@@ -183,7 +130,85 @@ public class DrawShape implements Drawable { |
|
|
|
} |
|
|
|
|
|
|
|
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) { |