Bug 53191 - Problems with line style when converting ppt to png
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1765196 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
e3633daac5
commit
bf95861693
@ -18,6 +18,8 @@
|
|||||||
package org.apache.poi.sl.draw;
|
package org.apache.poi.sl.draw;
|
||||||
|
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -32,14 +34,23 @@ public class DrawFreeformShape extends DrawAutoShape {
|
|||||||
public DrawFreeformShape(FreeformShape<?,?> shape) {
|
public DrawFreeformShape(FreeformShape<?,?> shape) {
|
||||||
super(shape);
|
super(shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Collection<Outline> computeOutlines(Graphics2D graphics) {
|
protected Collection<Outline> computeOutlines(Graphics2D graphics) {
|
||||||
List<Outline> lst = new ArrayList<Outline>();
|
List<Outline> lst = new ArrayList<Outline>();
|
||||||
java.awt.Shape sh = getShape().getPath();
|
FreeformShape<?,?> fsh = getShape();
|
||||||
FillStyle fs = getShape().getFillStyle();
|
Path2D sh = fsh.getPath();
|
||||||
StrokeStyle ss = getShape().getStrokeStyle();
|
|
||||||
|
AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
|
||||||
|
if (tx == null) {
|
||||||
|
tx = new AffineTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
java.awt.Shape canvasShape = tx.createTransformedShape(sh);
|
||||||
|
|
||||||
|
FillStyle fs = fsh.getFillStyle();
|
||||||
|
StrokeStyle ss = fsh.getStrokeStyle();
|
||||||
Path path = new Path(fs != null, ss != null);
|
Path path = new Path(fs != null, ss != null);
|
||||||
lst.add(new Outline(sh, path));
|
lst.add(new Outline(canvasShape, path));
|
||||||
return lst;
|
return lst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,85 +47,95 @@ public class DrawShape implements Drawable {
|
|||||||
if (!(shape instanceof PlaceableShape)) return;
|
if (!(shape instanceof PlaceableShape)) return;
|
||||||
|
|
||||||
PlaceableShape<?,?> ps = (PlaceableShape<?,?>)shape;
|
PlaceableShape<?,?> ps = (PlaceableShape<?,?>)shape;
|
||||||
|
final boolean isHSLF = ps.getClass().getCanonicalName().toLowerCase(Locale.ROOT).contains("hslf");
|
||||||
AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
|
AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
|
||||||
if (tx == null) tx = new AffineTransform();
|
if (tx == null) tx = new AffineTransform();
|
||||||
final Rectangle2D anchor = tx.createTransformedShape(ps.getAnchor()).getBounds2D();
|
final Rectangle2D anchor = tx.createTransformedShape(ps.getAnchor()).getBounds2D();
|
||||||
|
|
||||||
// rotation
|
char cmds[] = isHSLF ? new char[]{ 'h','v','r' } : new char[]{ 'r','h','v' };
|
||||||
double rotation = ps.getRotation();
|
for (char ch : cmds) {
|
||||||
if (rotation != 0.) {
|
switch (ch) {
|
||||||
// PowerPoint rotates shapes relative to the geometric center
|
case 'h':
|
||||||
double centerX = anchor.getCenterX();
|
//flip horizontal
|
||||||
double centerY = anchor.getCenterY();
|
if (ps.getFlipHorizontal()) {
|
||||||
|
graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
|
||||||
// normalize rotation
|
graphics.scale(-1, 1);
|
||||||
rotation %= 360.;
|
graphics.translate(-anchor.getX(), -anchor.getY());
|
||||||
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 (ps.getClass().getCanonicalName().toLowerCase(Locale.ROOT).contains("hslf")) {
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
//flip vertical
|
||||||
|
if (ps.getFlipVertical()) {
|
||||||
|
graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
|
||||||
|
graphics.scale(1, -1);
|
||||||
|
graphics.translate(-anchor.getX(), -anchor.getY());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
// rotation
|
||||||
|
double rotation = ps.getRotation();
|
||||||
|
if (rotation != 0.) {
|
||||||
|
// PowerPoint rotates shapes relative to the geometric center
|
||||||
|
double centerX = anchor.getCenterX();
|
||||||
|
double centerY = anchor.getCenterY();
|
||||||
|
|
||||||
txs.translate(centerX, centerY);
|
// normalize rotation
|
||||||
txs.rotate(Math.PI/2.);
|
rotation %= 360.;
|
||||||
txs.translate(-centerX, -centerY);
|
if (rotation < 0) rotation += 360.;
|
||||||
|
|
||||||
Rectangle2D anchor2 = txs.createTransformedShape(ps.getAnchor()).getBounds2D();
|
int quadrant = (((int)rotation+45)/90)%4;
|
||||||
|
double scaleX = 1.0, scaleY = 1.0;
|
||||||
|
|
||||||
scaleX = safeScale(anchor.getWidth(), anchor2.getWidth());
|
// scale to bounding box (bug #53176)
|
||||||
scaleY = safeScale(anchor.getHeight(), anchor2.getHeight());
|
if (quadrant == 1 || quadrant == 3) {
|
||||||
} else {
|
// In quadrant 1 and 3, which is basically a shape in a more or less portrait orientation
|
||||||
quadrant = 0;
|
// (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.translate(-centerX, -centerY);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.translate(-centerX, -centerY);
|
|
||||||
}
|
|
||||||
|
|
||||||
//flip horizontal
|
|
||||||
if (ps.getFlipHorizontal()) {
|
|
||||||
graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
|
|
||||||
graphics.scale(-1, 1);
|
|
||||||
graphics.translate(-anchor.getX(), -anchor.getY());
|
|
||||||
}
|
|
||||||
|
|
||||||
//flip vertical
|
|
||||||
if (ps.getFlipVertical()) {
|
|
||||||
graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
|
|
||||||
graphics.scale(1, -1);
|
|
||||||
graphics.translate(-anchor.getX(), -anchor.getY());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,12 +230,12 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
|
|||||||
EscherArrayProperty verticesProp = new EscherArrayProperty((short)(EscherProperties.GEOMETRY__VERTICES + 0x4000), false, null);
|
EscherArrayProperty verticesProp = new EscherArrayProperty((short)(EscherProperties.GEOMETRY__VERTICES + 0x4000), false, null);
|
||||||
verticesProp.setNumberOfElementsInArray(pntInfo.size());
|
verticesProp.setNumberOfElementsInArray(pntInfo.size());
|
||||||
verticesProp.setNumberOfElementsInMemory(pntInfo.size());
|
verticesProp.setNumberOfElementsInMemory(pntInfo.size());
|
||||||
verticesProp.setSizeOfElements(0xFFF0);
|
verticesProp.setSizeOfElements(8);
|
||||||
for (int i = 0; i < pntInfo.size(); i++) {
|
for (int i = 0; i < pntInfo.size(); i++) {
|
||||||
Point2D.Double pnt = pntInfo.get(i);
|
Point2D.Double pnt = pntInfo.get(i);
|
||||||
byte[] data = new byte[4];
|
byte[] data = new byte[8];
|
||||||
LittleEndian.putShort(data, 0, (short)Units.pointsToMaster(pnt.getX() - bounds.getX()));
|
LittleEndian.putInt(data, 0, Units.pointsToMaster(pnt.getX() - bounds.getX()));
|
||||||
LittleEndian.putShort(data, 2, (short)Units.pointsToMaster(pnt.getY() - bounds.getY()));
|
LittleEndian.putInt(data, 4, Units.pointsToMaster(pnt.getY() - bounds.getY()));
|
||||||
verticesProp.setElement(i, data);
|
verticesProp.setElement(i, data);
|
||||||
}
|
}
|
||||||
opt.addEscherProperty(verticesProp);
|
opt.addEscherProperty(verticesProp);
|
||||||
@ -282,6 +282,7 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
|
|||||||
|
|
||||||
Iterator<byte[]> vertIter = verticesProp.iterator();
|
Iterator<byte[]> vertIter = verticesProp.iterator();
|
||||||
Iterator<byte[]> segIter = segmentsProp.iterator();
|
Iterator<byte[]> segIter = segmentsProp.iterator();
|
||||||
|
double xyPoints[] = new double[2];
|
||||||
|
|
||||||
while (vertIter.hasNext() && segIter.hasNext()) {
|
while (vertIter.hasNext() && segIter.hasNext()) {
|
||||||
byte[] segElem = segIter.next();
|
byte[] segElem = segIter.next();
|
||||||
@ -292,30 +293,30 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case moveTo: {
|
case moveTo: {
|
||||||
byte[] p = vertIter.next();
|
fillPoint(vertIter.next(), xyPoints);
|
||||||
double x = Units.masterToPoints(LittleEndian.getShort(p, 0));
|
double x = xyPoints[0];
|
||||||
double y = Units.masterToPoints(LittleEndian.getShort(p, 2));
|
double y = xyPoints[1];
|
||||||
path.moveTo(x,y);
|
path.moveTo(x,y);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case curveTo: {
|
case curveTo: {
|
||||||
byte[] p1 = vertIter.next();
|
fillPoint(vertIter.next(), xyPoints);
|
||||||
double x1 = Units.masterToPoints(LittleEndian.getShort(p1, 0));
|
double x1 = xyPoints[0];
|
||||||
double y1 = Units.masterToPoints(LittleEndian.getShort(p1, 2));
|
double y1 = xyPoints[1];
|
||||||
byte[] p2 = vertIter.next();
|
fillPoint(vertIter.next(), xyPoints);
|
||||||
double x2 = Units.masterToPoints(LittleEndian.getShort(p2, 0));
|
double x2 = xyPoints[0];
|
||||||
double y2 = Units.masterToPoints(LittleEndian.getShort(p2, 2));
|
double y2 = xyPoints[1];
|
||||||
byte[] p3 = vertIter.next();
|
fillPoint(vertIter.next(), xyPoints);
|
||||||
double x3 = Units.masterToPoints(LittleEndian.getShort(p3, 0));
|
double x3 = xyPoints[0];
|
||||||
double y3 = Units.masterToPoints(LittleEndian.getShort(p3, 2));
|
double y3 = xyPoints[1];
|
||||||
path.curveTo(x1,y1,x2,y2,x3,y3);
|
path.curveTo(x1,y1,x2,y2,x3,y3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case lineTo:
|
case lineTo:
|
||||||
if (vertIter.hasNext()) {
|
if (vertIter.hasNext()) {
|
||||||
byte[] p = vertIter.next();
|
fillPoint(vertIter.next(), xyPoints);
|
||||||
double x = Units.masterToPoints(LittleEndian.getShort(p, 0));
|
double x = xyPoints[0];
|
||||||
double y = Units.masterToPoints(LittleEndian.getShort(p, 2));
|
double y = xyPoints[1];
|
||||||
path.lineTo(x,y);
|
path.lineTo(x,y);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -344,6 +345,25 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
|
|||||||
return new Path2D.Double(at.createTransformedShape(path));
|
return new Path2D.Double(at.createTransformedShape(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillPoint(byte xyMaster[], double xyPoints[]) {
|
||||||
|
if (xyMaster == null || xyPoints == null || (xyMaster.length != 4 && xyMaster.length != 8) || xyPoints.length != 2) {
|
||||||
|
logger.log(POILogger.WARN, "Invalid number of master bytes for a single point - ignore point");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x, y;
|
||||||
|
if (xyMaster.length == 4) {
|
||||||
|
x = LittleEndian.getShort(xyMaster, 0);
|
||||||
|
y = LittleEndian.getShort(xyMaster, 2);
|
||||||
|
} else {
|
||||||
|
x = LittleEndian.getInt(xyMaster, 0);
|
||||||
|
y = LittleEndian.getInt(xyMaster, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
xyPoints[0] = Units.masterToPoints(x);
|
||||||
|
xyPoints[1] = Units.masterToPoints(y);
|
||||||
|
}
|
||||||
|
|
||||||
private static <T extends EscherProperty> T getShapeProp(AbstractEscherOptRecord opt, int propId) {
|
private static <T extends EscherProperty> T getShapeProp(AbstractEscherOptRecord opt, int propId) {
|
||||||
T prop = getEscherProperty(opt, (short)(propId + 0x4000));
|
T prop = getEscherProperty(opt, (short)(propId + 0x4000));
|
||||||
if (prop == null) {
|
if (prop == null) {
|
||||||
|
Loading…
Reference in New Issue
Block a user