如何使用 ooxml apache poi 通过精确坐标旋转 90 度来创建弯曲连接器
how to create bent connector by rotating 90degrees with exact coordinates using ooxml apache poi
最近我在使用 Apache POI,在精确定位旋转弯曲连接器时遇到问题。我想将弯曲的连接器表示为形状之间的流动水平,但我在可视化方面遇到了问题
所以我的基本问题是在使用 ooxml apache poi 绘制形状连接器时,它被描述如下
当我们旋转90度时,精确位置和坐标不断变化
我期待如下输出,任何人都可以提供解决此问题的指示
ooxml 和 apache poi PPT
为了得到你想要的弯曲连接,默认的BENT_CONNECTOR_3
需要水平翻转然后旋转90度counter-clockwise。
以下完整示例说明了这一点。注意代码中的注释。
import java.io.FileOutputStream;
import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.sl.usermodel.*;
import org.openxmlformats.schemas.presentationml.x2006.main.*;
import org.openxmlformats.schemas.drawingml.x2006.main.*;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.awt.Color;
public class CreatePPTXConnectorShapes {
private static XSLFConnectorShape createConnector(XSLFSlide slide, XSLFAutoShape shape1, XSLFAutoShape shape2) {
XSLFConnectorShape connector = slide.createConnector();
connector.setShapeType(ShapeType.BENT_CONNECTOR_3); // bent connector having one handle
connector.setAnchor(new Rectangle2D.Double( //connector is diagonal in a rectangle
shape1.getAnchor().getCenterX(), // top left x of that rectangle is center x position of shape1
shape1.getAnchor().getMaxY(), // top left y of that rectangle is bottom y of shape1
shape2.getAnchor().getCenterX()-shape1.getAnchor().getCenterX(), // width of that rectanle is center x of shape2 minus center x of shape1
shape2.getAnchor().getY()-shape1.getAnchor().getMaxY() // height of that rectanle is top y of shape2 minus bottom y of shape1
));
// the rectangle needs to be flipped horizontally as the default bent connector having one handle is like so:
// ----+
// |
// +----
connector.setFlipHorizontal(true);
// now it is like so:
// +----
// |
// ----+
// and needs to be rotated 90 deg counter-clockwise
connector.setRotation(-90.00);
// now it is like so:
// |
// +---+
// |
// now we fix the connecting points
CTConnector ctConnector = (CTConnector)connector.getXmlObject();
CTNonVisualConnectorProperties cx = ctConnector.getNvCxnSpPr().getCNvCxnSpPr();
CTConnection start = cx.addNewStCxn();
start.setId(shape1.getShapeId());
start.setIdx(2); // connecting point 2 is center of bottom edge
CTConnection end = cx.addNewEndCxn();
end.setId(shape2.getShapeId());
end.setIdx(0); // connecting point 0 is center of top edge
return connector;
}
public static void main(String[] args) throws Exception {
XMLSlideShow slideShow = new XMLSlideShow();
XSLFSlide slide = slideShow.createSlide();
XSLFAutoShape shape1 = slide.createAutoShape();
shape1.setShapeType(ShapeType.RECT);
shape1.setFillColor(Color.BLUE);
shape1.setAnchor(new Rectangle(50, 50, 150, 50));
XSLFAutoShape shape2 = slide.createAutoShape();
shape2.setShapeType(ShapeType.RECT);
shape2.setFillColor(Color.BLUE);
shape2.setAnchor(new Rectangle(150, 200, 150, 50));
// create connector
XSLFConnectorShape connector1 = createConnector(slide, shape1, shape2);
FileOutputStream out = new FileOutputStream("CreatePPTXConnectorShapes.pptx");
slideShow.write(out);
out.close();
}
}
它产生:
很明显,如果以连接器为对角线的矩形是正方形,那么旋转很容易。这就是为什么上面的代码有效并显示了主要原理。我将把它留在答案中,以免一开始就使它复杂化,并有代码显示当包含连接器作为对角线的矩形是 不是 正方形时的问题。
如果以connector为对角线的矩形不是正方形,那么anchor的设置需要考虑旋转时左上角位置和维度的变化。我们有 TL1,旋转后所需的位置,可以从形状的位置和尺寸计算得到。我们需要 TL0,它是旋转前的左上角点。对于尺寸,需要考虑旋转后宽度和高度交换。
TL0*-------+
| . |
| . |
| . |
TL1*---------------------------+
| | . | |
h- - - - - - -+rp- - - - - -|
| | . | |
+-------------w-------------+
| . |
| . |
| . |
+-------+
we have TL1, which can get calculated from the shape position and dimension
TL1X, TL1Y, w: width, h: height
we need TL0, which is the top left point before rotating (rp = rotation point)
TL0X = TL1X + (w/2 - h/2) = TL1X + (w - h) / 2
TL0Y = TL1Y - (w/2 - h/2) = TL1Y - (w - h) / 2
width and height simply get swapped
如果形状 2 位于形状 1 的右下方,则以下内容应该有效。连接器是从形状 1 的底部边缘中心到形状 2 的顶部边缘中心。
import java.io.FileOutputStream;
import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.sl.usermodel.*;
import org.openxmlformats.schemas.presentationml.x2006.main.*;
import org.openxmlformats.schemas.drawingml.x2006.main.*;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.awt.Color;
public class CreatePPTXConnectorShapes2 {
private static XSLFConnectorShape createConnector(XSLFSlide slide, XSLFAutoShape shape1, XSLFAutoShape shape2) {
XSLFConnectorShape connector = slide.createConnector();
// if shape2 is on the right below shape1; connector is from center of bottom edge of shape1 to center of top edge of shape2
if (shape2.getAnchor().getX() >= shape1.getAnchor().getCenterX() && shape2.getAnchor().getY() >= shape1.getAnchor().getMaxY()) {
connector.setShapeType(ShapeType.BENT_CONNECTOR_3); // bent connector having one handle
// the rectangle needs to be flipped horizontally as the default bent connector having one handle is like so:
// ----+
// |
// +----
connector.setFlipHorizontal(true);
// now it is like so:
// +----
// |
// ----+
// and needs to be rotated 90 deg counter-clockwise
connector.setRotation(-90.00);
// now it is like so:
// |
// |
// +---+
// |
// |
// set the anchor after rotating
//connector is diagonal in a rectangle
//before rotating it would be:
double topLeftX = shape1.getAnchor().getCenterX(); // top left x of that rectangle is center x position of shape1
double topLeftY = shape1.getAnchor().getMaxY(); // top left y of that rectangle is bottom y of shape1
double width = shape2.getAnchor().getCenterX()-shape1.getAnchor().getCenterX(); // width of that rectanle is center x of shape2 minus center x of shape1
double height = shape2.getAnchor().getY()-shape1.getAnchor().getMaxY(); // height of that rectanle is top y of shape2 minus bottom y of shape1
//considering the rotation it is:
topLeftX = topLeftX + (width - height) / 2;
topLeftY = topLeftY - (width - height) / 2;
double w = width;
width = height;
height = w;
connector.setAnchor(new Rectangle2D.Double(topLeftX, topLeftY, width, height));
// now it is like so:
// |
// +-------+
// |
// now we fix the connecting points
CTConnector ctConnector = (CTConnector)connector.getXmlObject();
CTNonVisualConnectorProperties cx = ctConnector.getNvCxnSpPr().getCNvCxnSpPr();
CTConnection start = cx.addNewStCxn();
start.setId(shape1.getShapeId());
start.setIdx(2); // connecting point 2 is center of bottom edge
CTConnection end = cx.addNewEndCxn();
end.setId(shape2.getShapeId());
end.setIdx(0); // connecting point 0 is center of top edge
}
return connector;
}
public static void main(String[] args) throws Exception {
XMLSlideShow slideShow = new XMLSlideShow();
XSLFSlide slide = slideShow.createSlide();
XSLFAutoShape shape1 = slide.createAutoShape();
shape1.setShapeType(ShapeType.RECT);
shape1.setFillColor(Color.BLUE);
shape1.setAnchor(new Rectangle(50, 50, 150, 50));
XSLFAutoShape shape2 = slide.createAutoShape();
shape2.setShapeType(ShapeType.RECT);
shape2.setFillColor(Color.BLUE);
shape2.setAnchor(new Rectangle(150, 200, 150, 50));
XSLFAutoShape shape3 = slide.createAutoShape();
shape3.setShapeType(ShapeType.RECT);
shape3.setFillColor(Color.BLUE);
shape3.setAnchor(new Rectangle(400, 200, 150, 50));
// create connectors
XSLFConnectorShape connector1 = createConnector(slide, shape1, shape2);
XSLFConnectorShape connector2 = createConnector(slide, shape1, shape3);
FileOutputStream out = new FileOutputStream("CreatePPTXConnectorShapes.pptx");
slideShow.write(out);
out.close();
}
}
它产生:
免责声明:正如我在这里的所有代码示例一样,这 不是 一个适合所有可用于生产环境的解决方案,但只是一个展示原则的工作草案。为了创建一个可以有效使用的解决方案,您必须自己努力。
最近我在使用 Apache POI,在精确定位旋转弯曲连接器时遇到问题。我想将弯曲的连接器表示为形状之间的流动水平,但我在可视化方面遇到了问题 所以我的基本问题是在使用 ooxml apache poi 绘制形状连接器时,它被描述如下
我期待如下输出,任何人都可以提供解决此问题的指示
ooxml 和 apache poi PPT
为了得到你想要的弯曲连接,默认的BENT_CONNECTOR_3
需要水平翻转然后旋转90度counter-clockwise。
以下完整示例说明了这一点。注意代码中的注释。
import java.io.FileOutputStream;
import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.sl.usermodel.*;
import org.openxmlformats.schemas.presentationml.x2006.main.*;
import org.openxmlformats.schemas.drawingml.x2006.main.*;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.awt.Color;
public class CreatePPTXConnectorShapes {
private static XSLFConnectorShape createConnector(XSLFSlide slide, XSLFAutoShape shape1, XSLFAutoShape shape2) {
XSLFConnectorShape connector = slide.createConnector();
connector.setShapeType(ShapeType.BENT_CONNECTOR_3); // bent connector having one handle
connector.setAnchor(new Rectangle2D.Double( //connector is diagonal in a rectangle
shape1.getAnchor().getCenterX(), // top left x of that rectangle is center x position of shape1
shape1.getAnchor().getMaxY(), // top left y of that rectangle is bottom y of shape1
shape2.getAnchor().getCenterX()-shape1.getAnchor().getCenterX(), // width of that rectanle is center x of shape2 minus center x of shape1
shape2.getAnchor().getY()-shape1.getAnchor().getMaxY() // height of that rectanle is top y of shape2 minus bottom y of shape1
));
// the rectangle needs to be flipped horizontally as the default bent connector having one handle is like so:
// ----+
// |
// +----
connector.setFlipHorizontal(true);
// now it is like so:
// +----
// |
// ----+
// and needs to be rotated 90 deg counter-clockwise
connector.setRotation(-90.00);
// now it is like so:
// |
// +---+
// |
// now we fix the connecting points
CTConnector ctConnector = (CTConnector)connector.getXmlObject();
CTNonVisualConnectorProperties cx = ctConnector.getNvCxnSpPr().getCNvCxnSpPr();
CTConnection start = cx.addNewStCxn();
start.setId(shape1.getShapeId());
start.setIdx(2); // connecting point 2 is center of bottom edge
CTConnection end = cx.addNewEndCxn();
end.setId(shape2.getShapeId());
end.setIdx(0); // connecting point 0 is center of top edge
return connector;
}
public static void main(String[] args) throws Exception {
XMLSlideShow slideShow = new XMLSlideShow();
XSLFSlide slide = slideShow.createSlide();
XSLFAutoShape shape1 = slide.createAutoShape();
shape1.setShapeType(ShapeType.RECT);
shape1.setFillColor(Color.BLUE);
shape1.setAnchor(new Rectangle(50, 50, 150, 50));
XSLFAutoShape shape2 = slide.createAutoShape();
shape2.setShapeType(ShapeType.RECT);
shape2.setFillColor(Color.BLUE);
shape2.setAnchor(new Rectangle(150, 200, 150, 50));
// create connector
XSLFConnectorShape connector1 = createConnector(slide, shape1, shape2);
FileOutputStream out = new FileOutputStream("CreatePPTXConnectorShapes.pptx");
slideShow.write(out);
out.close();
}
}
它产生:
很明显,如果以连接器为对角线的矩形是正方形,那么旋转很容易。这就是为什么上面的代码有效并显示了主要原理。我将把它留在答案中,以免一开始就使它复杂化,并有代码显示当包含连接器作为对角线的矩形是 不是 正方形时的问题。
如果以connector为对角线的矩形不是正方形,那么anchor的设置需要考虑旋转时左上角位置和维度的变化。我们有 TL1,旋转后所需的位置,可以从形状的位置和尺寸计算得到。我们需要 TL0,它是旋转前的左上角点。对于尺寸,需要考虑旋转后宽度和高度交换。
TL0*-------+
| . |
| . |
| . |
TL1*---------------------------+
| | . | |
h- - - - - - -+rp- - - - - -|
| | . | |
+-------------w-------------+
| . |
| . |
| . |
+-------+
we have TL1, which can get calculated from the shape position and dimension
TL1X, TL1Y, w: width, h: height
we need TL0, which is the top left point before rotating (rp = rotation point)
TL0X = TL1X + (w/2 - h/2) = TL1X + (w - h) / 2
TL0Y = TL1Y - (w/2 - h/2) = TL1Y - (w - h) / 2
width and height simply get swapped
如果形状 2 位于形状 1 的右下方,则以下内容应该有效。连接器是从形状 1 的底部边缘中心到形状 2 的顶部边缘中心。
import java.io.FileOutputStream;
import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.sl.usermodel.*;
import org.openxmlformats.schemas.presentationml.x2006.main.*;
import org.openxmlformats.schemas.drawingml.x2006.main.*;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.awt.Color;
public class CreatePPTXConnectorShapes2 {
private static XSLFConnectorShape createConnector(XSLFSlide slide, XSLFAutoShape shape1, XSLFAutoShape shape2) {
XSLFConnectorShape connector = slide.createConnector();
// if shape2 is on the right below shape1; connector is from center of bottom edge of shape1 to center of top edge of shape2
if (shape2.getAnchor().getX() >= shape1.getAnchor().getCenterX() && shape2.getAnchor().getY() >= shape1.getAnchor().getMaxY()) {
connector.setShapeType(ShapeType.BENT_CONNECTOR_3); // bent connector having one handle
// the rectangle needs to be flipped horizontally as the default bent connector having one handle is like so:
// ----+
// |
// +----
connector.setFlipHorizontal(true);
// now it is like so:
// +----
// |
// ----+
// and needs to be rotated 90 deg counter-clockwise
connector.setRotation(-90.00);
// now it is like so:
// |
// |
// +---+
// |
// |
// set the anchor after rotating
//connector is diagonal in a rectangle
//before rotating it would be:
double topLeftX = shape1.getAnchor().getCenterX(); // top left x of that rectangle is center x position of shape1
double topLeftY = shape1.getAnchor().getMaxY(); // top left y of that rectangle is bottom y of shape1
double width = shape2.getAnchor().getCenterX()-shape1.getAnchor().getCenterX(); // width of that rectanle is center x of shape2 minus center x of shape1
double height = shape2.getAnchor().getY()-shape1.getAnchor().getMaxY(); // height of that rectanle is top y of shape2 minus bottom y of shape1
//considering the rotation it is:
topLeftX = topLeftX + (width - height) / 2;
topLeftY = topLeftY - (width - height) / 2;
double w = width;
width = height;
height = w;
connector.setAnchor(new Rectangle2D.Double(topLeftX, topLeftY, width, height));
// now it is like so:
// |
// +-------+
// |
// now we fix the connecting points
CTConnector ctConnector = (CTConnector)connector.getXmlObject();
CTNonVisualConnectorProperties cx = ctConnector.getNvCxnSpPr().getCNvCxnSpPr();
CTConnection start = cx.addNewStCxn();
start.setId(shape1.getShapeId());
start.setIdx(2); // connecting point 2 is center of bottom edge
CTConnection end = cx.addNewEndCxn();
end.setId(shape2.getShapeId());
end.setIdx(0); // connecting point 0 is center of top edge
}
return connector;
}
public static void main(String[] args) throws Exception {
XMLSlideShow slideShow = new XMLSlideShow();
XSLFSlide slide = slideShow.createSlide();
XSLFAutoShape shape1 = slide.createAutoShape();
shape1.setShapeType(ShapeType.RECT);
shape1.setFillColor(Color.BLUE);
shape1.setAnchor(new Rectangle(50, 50, 150, 50));
XSLFAutoShape shape2 = slide.createAutoShape();
shape2.setShapeType(ShapeType.RECT);
shape2.setFillColor(Color.BLUE);
shape2.setAnchor(new Rectangle(150, 200, 150, 50));
XSLFAutoShape shape3 = slide.createAutoShape();
shape3.setShapeType(ShapeType.RECT);
shape3.setFillColor(Color.BLUE);
shape3.setAnchor(new Rectangle(400, 200, 150, 50));
// create connectors
XSLFConnectorShape connector1 = createConnector(slide, shape1, shape2);
XSLFConnectorShape connector2 = createConnector(slide, shape1, shape3);
FileOutputStream out = new FileOutputStream("CreatePPTXConnectorShapes.pptx");
slideShow.write(out);
out.close();
}
}
它产生:
免责声明:正如我在这里的所有代码示例一样,这 不是 一个适合所有可用于生产环境的解决方案,但只是一个展示原则的工作草案。为了创建一个可以有效使用的解决方案,您必须自己努力。