You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FOP PDF生成:基于表格内容实现表格并排显示需求

基于Apache XML Graphics 2.7实现表格自适应并排/换行

核心逻辑

要达成「单元格内容超页面50%宽度时表格换行,否则并排」的效果,核心是先计算表格实际宽度,再根据阈值控制布局位置,利用Apache XML Graphics的文本测量和布局API实现动态定位。

具体实现步骤

1. 计算页面阈值与表格实际宽度

首先获取页面总宽度,算出50%的阈值宽度;然后遍历表格单元格,用TextLayout精准计算文本宽度,累加得到表格的实际总宽度(含边框、内边距、外边距)。

import org.apache.xmlgraphics.java2d.TextLayout;
import org.apache.xmlgraphics.util.UnitConv;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.flow.Table;
import org.apache.fop.fo.flow.TableCell;

// 初始化FOP环境(Apache XML Graphics常用FOP组件生成PDF)
FOUserAgent foUserAgent = FopFactory.newInstance().newFOUserAgent();
// 获取页面宽度(转换为PT单位)
float pageWidthPt = UnitConv.mm2pt(foUserAgent.getPageWidth().getNumericValue().getValue());
float halfPageWidthPt = pageWidthPt * 0.5f;

// 计算单个表格的实际宽度
private float calculateTableTotalWidth(Table table) {
    float totalWidth = 0;
    // 遍历表格列,计算每列的最大内容宽度
    for (var col : table.getColumns()) {
        float maxColWidth = 0;
        for (TableCell cell : col.getCells()) {
            String cellText = cell.getContent().getStringValue();
            // 计算文本的实际宽度
            TextLayout textLayout = new TextLayout(cellText, cell.getFontInfo().getFont(), new FontRenderContext(null, false, false));
            float textWidth = textLayout.getAdvance();
            // 加上单元格的内边距和边框宽度
            textWidth += cell.getPaddingLeft().getValue(UnitConv.MM2PT) + cell.getPaddingRight().getValue(UnitConv.MM2PT);
            textWidth += cell.getBorderLeftWidth().getValue(UnitConv.MM2PT) + cell.getBorderRightWidth().getValue(UnitConv.MM2PT);
            maxColWidth = Math.max(maxColWidth, textWidth);
        }
        totalWidth += maxColWidth;
    }
    // 加上表格的外边距
    totalWidth += table.getMarginLeft().getValue(UnitConv.MM2PT) + table.getMarginRight().getValue(UnitConv.MM2PT);
    return totalWidth;
}

2. 动态控制表格布局

维护当前行的剩余宽度和Y轴位置,遍历表格时根据宽度判断是否需要换行:

  • 若表格宽度超过50%阈值:直接换行渲染
  • 若表格宽度未超阈值,但当前行剩余空间不足:换行渲染
  • 否则:在当前行并排渲染,更新剩余空间
import org.apache.fop.fo.flow.Table;

List<Table> tables = getTargetTables(); // 待渲染的表格列表
float currentRemainingWidth = pageWidthPt;
// 初始Y坐标:页面顶部减去上边距
float currentY = UnitConv.mm2pt(foUserAgent.getPageHeight().getNumericValue().getValue()) 
                 - UnitConv.mm2pt(foUserAgent.getMarginTop().getValue());

for (Table table : tables) {
    float tableWidth = calculateTableTotalWidth(table);
    boolean needNewLine = false;

    // 判断是否需要换行
    if (tableWidth > halfPageWidthPt) {
        needNewLine = true;
    } else if (tableWidth > currentRemainingWidth) {
        needNewLine = true;
    }

    if (needNewLine) {
        // 计算表格高度(需自行实现类似宽度的计算逻辑)
        float tableHeight = calculateTableTotalHeight(table);
        // 跳到新行,调整Y坐标
        currentY -= tableHeight + UnitConv.mm2pt(foUserAgent.getMarginBottom().getValue());
        // 重置当前行剩余宽度
        currentRemainingWidth = pageWidthPt - tableWidth;
        // 表格左对齐
        table.setMarginLeft("0mm");
    } else {
        // 设置表格左边距,实现并排
        float marginLeftPt = currentRemainingWidth - tableWidth;
        table.setMarginLeft(UnitConv.pt2mm(marginLeftPt) + "mm");
        currentRemainingWidth -= tableWidth;
    }

    // 渲染表格到PDF
    renderTable(table, foUserAgent, currentY);
}

3. 关键注意点

  • 表格高度计算:可参照宽度计算逻辑,用TextLayout.getAscent()和getDescent()累加单元格高度,加上边框、内边距得到总高度
  • XSL-FO模板配合:若通过XSL生成FO文件,可在模板中动态传递宽度参数,结合Java逻辑实现更灵活的布局
  • 分页处理:需判断当前Y坐标是否超出页面底部边距,若不足则触发分页,重置Y坐标到新页面顶部

内容的提问来源于stack exchange,提问作者hungrygik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 21:37:32