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
相关产品推荐
相关产品推荐

