如何使用 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();
 }
}

它产生:

免责声明:正如我在这里的所有代码示例一样,这 不是 一个适合所有可用于生产环境的解决方案,但只是一个展示原则的工作草案。为了创建一个可以有效使用的解决方案,您必须自己努力。