您好:我使用aspose-words-21.5.0-jdk17.jar版本生成word。生成的word包含header和footer,还有body。分别是三个table,每个table的的cell都设置了段落。然后把header、body和footer中某一个段落放入到集合中,遍历集合move到对应段落,然后插入对应段落的shape。以下代码注释了计算坐标的代码就可以正常运行。
但是现在有这样的问题:我需要在插入shape的时候重新计算shape的大小和相对位置,因为有可能会超过当前的word页面,所以我需要计算需要插入shape的段落的坐标(x,y)。然后去计算新的相对位置。出入body中的段落计算没有问题,但是传入header和footer中的段落,计算的时候就报错了。不清楚师什么原因。请帮忙指出错误原因?并且提供计算header和footer中段落的坐标(x, y)。感谢。
如有问题随时给我留言,感谢。
package com.edetek.conform.solution.util;
import java.awt.Color;
import java.awt.geom.Rectangle2D;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import com.aspose.words.Document;
import com.aspose.words.DocumentBuilder;
import com.aspose.words.HeaderFooterType;
import com.aspose.words.LayoutCollector;
import com.aspose.words.LayoutEnumerator;
import com.aspose.words.License;
import com.aspose.words.Node;
import com.aspose.words.NodeType;
import com.aspose.words.Paragraph;
import com.aspose.words.RelativeHorizontalPosition;
import com.aspose.words.RelativeVerticalPosition;
import com.aspose.words.Run;
import com.aspose.words.Shape;
import com.aspose.words.ShapeType;
import com.edetek.conform.common.exception.ToastMessageException;
/**
* @author DongZhou
* @since 2025/8/11 15:29
*/
public class DD {
private static final String LICENSE_RESOURCE_NAME = "license.xml";
protected static final License ASPOSE_LICENSE = new License();
static {
try (InputStream is = InitAsposeLicense.class.getClassLoader().getResourceAsStream(LICENSE_RESOURCE_NAME)) {
ASPOSE_LICENSE.setLicense(is);
} catch (Exception e) {
throw new ToastMessageException("The aspose license is not found");
}
}
public static class ParaPosition {
public int pageIndex;
public double x;
public double y;
public double width;
public double height;
@Override
public String toString() {
return String.format("Page=%d, X=%.2fpt, Y=%.2fpt, W=%.2fpt, H=%.2fpt",
pageIndex, x, y, width, height);
}
}
public static void main(String[] args) throws Exception {
// 创建文档和文档构建器
Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);
List<Paragraph> paragraphs = new ArrayList<>();
builder.moveToHeaderFooter(HeaderFooterType.HEADER_PRIMARY);
// 插入一个表格
builder.startTable();
// ---- 表头: 2行3列 ----
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
builder.insertCell();
builder.write(String.format("Header R%dC%d", i + 1, j + 1));
if (i == 1 && j == 2) {
// 在表头第一行第一列插入一个段落
Paragraph headerPara = builder.getCurrentParagraph();
Run run = new Run(doc, "Header Paragraph");
headerPara.appendChild(run);
paragraphs.add(headerPara); // 保存段落引用
}
}
builder.endRow();
}
builder.endTable();
builder.moveToDocumentStart(); // 或 moveToDocumentEnd()
builder.startTable();
// ---- 正文: 5行5列 ----
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
builder.insertCell();
builder.write(String.format("Body R%dC%d", i + 1, j + 1));
if (i == 2 && j == 3) {
// 在正文第三行第四列插入一个段落
Paragraph bodyPara = builder.getCurrentParagraph();
Run run = new Run(doc, "Body Paragraph");
bodyPara.appendChild(run);
paragraphs.add(bodyPara); // 保存段落引用
}
}
builder.endRow();
}
builder.endTable();
builder.moveToHeaderFooter(HeaderFooterType.FOOTER_PRIMARY);
builder.startTable();
// ---- 表尾: 2行3列 ----
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
builder.insertCell();
builder.write(String.format("Footer R%dC%d", i + 1, j + 1));
if (i == 1 && j == 0) {
// 在表尾第二行第一列插入一个段落
Paragraph footerPara = builder.getCurrentParagraph();
Run run = new Run(doc, "Footer Paragraph");
footerPara.appendChild(run);
paragraphs.add(footerPara); // 保存段落引用
}
}
builder.endRow();
}
builder.endTable();
for (Paragraph para : paragraphs) {
// 计算para的坐标
ParaPosition paragraphPagePosition = getParagraphPagePosition(para, doc);
System.out.println("Paragraph Position: " + paragraphPagePosition);
// 在每个段落后插入一个Shape
insertShapeAtParagraph(builder, para, 50, 20, 100, 20, Color.YELLOW, "Shape Text");
}
String basePath = "D:\\temp\\path\\";
// 保存文档
doc.save(basePath + "TableWithShapes.docx");
}
public static DD.ParaPosition getParagraphPagePosition(Paragraph paragraph, Document doc) throws Exception {
if (paragraph == null || doc == null) {
throw new IllegalArgumentException("Parameters 'paragraph' and 'doc' is null.");
}
doc.updatePageLayout();
// Initialize LayoutCollector and update page layout to ensure layout info is up-to-date
LayoutCollector collector = new LayoutCollector(doc);
return getBodyParagraphPosition(paragraph, collector);
}
private static DD.ParaPosition getBodyParagraphPosition(Paragraph paragraph, LayoutCollector collector) throws Exception {
LayoutEnumerator enumerator = new LayoutEnumerator(collector.getDocument());
// Try to get any layout entity related to the paragraph
Object layoutEntity = getAnyLayoutEntityFromParagraphChildren(paragraph, collector);
if (layoutEntity == null) {
throw new IllegalStateException("Unable to obtain layout entity for body paragraph.");
}
enumerator.setCurrent(layoutEntity);
// If the paragraph is inside a table, move up to the Cell level to get correct coordinates
Node ancestorCell = paragraph.getAncestor(com.aspose.words.Cell.class);
if (ancestorCell != null) {
Object cellEntity = collector.getEntity(ancestorCell);
if (cellEntity != null) {
enumerator.setCurrent(cellEntity);
}
} else {
// Otherwise, move up to the paragraph level
while (enumerator.getType() != NodeType.PARAGRAPH && enumerator.moveParent()) {
// empty loop
}
}
Rectangle2D.Float rect = enumerator.getRectangle();
if (rect == null) {
throw new IllegalStateException("Unable to get bounding rectangle for body paragraph.");
}
DD.ParaPosition pos = new DD.ParaPosition();
pos.pageIndex = collector.getStartPageIndex(paragraph);
pos.x = rect.getX();
pos.y = rect.getY();
pos.width = rect.getWidth();
pos.height = rect.getHeight();
return pos;
}
private static Object getAnyLayoutEntityFromParagraphChildren(Paragraph paragraph,
LayoutCollector collector) throws Exception {
for (Node child = paragraph.getFirstChild(); child != null; child = child.getNextSibling()) {
Object entity = collector.getEntity(child);
if (entity != null) return entity;
}
for (Run run : paragraph.getRuns()) {
Object entity = collector.getEntity(run);
if (entity != null) return entity;
}
return collector.getEntity(paragraph);
}
/**
* 使用DocumentBuilder移动到段落,插入Shape,设置相对位置
*
* @param builder DocumentBuilder对象
* @param para 目标段落
* @param offsetX 相对段落的X偏移(磅)
* @param offsetY 相对段落的Y偏移(磅)
* @param width Shape宽度
* @param height Shape高度
* @param color Shape颜色
* @param text Shape内显示文字
*/
private static void insertShapeAtParagraph(DocumentBuilder builder, Paragraph para,
double offsetX, double offsetY,
double width, double height,
Color color, String text) throws Exception {
builder.moveTo(para);
// 插入shape,设置相对段落偏移
Shape shape = new Shape(builder.getDocument(), ShapeType.RECTANGLE);
shape.setWidth(width);
shape.setHeight(height);
shape.setRelativeHorizontalPosition(RelativeHorizontalPosition.COLUMN);
shape.setRelativeVerticalPosition(RelativeVerticalPosition.PARAGRAPH);
shape.setLeft(offsetX);
shape.setTop(offsetY);
shape.getFill().setColor(color);
shape.getStroke().setColor(Color.BLACK);
// 插入形状内文本
Paragraph shapePara = new Paragraph(builder.getDocument());
Run run = new Run(builder.getDocument(), text);
shapePara.appendChild(run);
shape.appendChild(shapePara);
builder.insertNode(shape);
}
}