PAYATHON 2026

Flying Saucer 生成 PDF 时正文与页脚重叠,如何解决?

数据阿明

应将页脚放在 @page 的底部页边距区域,并用 margin-bottom 预留空间。根据现有代码,重叠主要是因为 .footer 使用了 position: fixed,正文分页时不会自动为页脚留出位置。

Flying Saucer 支持用于页眉、页脚的 margin boxes 和 running elements。可以用 position: running(...) 配合 content: element(...) 实现重复页脚。Flying Saucer 用户指南

为什么给 body 增加底部空白没有解决问题?

body 是跨页的文档元素。给它设置底部 margin 或 padding,不能代替为每页正文区域设置底部页边距。因此,padding-bottom: 60mm 可能只在文档末尾增加空白,正文仍可能进入固定页脚所在的区域。Flying Saucer 的指南也区分了 body 边距与 @page 页边距。分页与页边距说明

移除 position: fixed 后,页脚会回到普通文档流,跟在全部正文之后,因此只在最后一页出现。这与描述中的现象一致。

1. 把页脚模板放到正文之前

将主模板调整为:

{{footer}}

{{table}}

<div class="disclaimer-section">
  <ul>
    {{disclaimer}}
  </ul>
</div>

{{footer}} 仍然展开为原来的 <div class="footer">...</div>,内部表格不需要重写。把 running element 放在正文之前,可以让第一页布局时取得页脚内容,避免它直到文档末尾才被定义。

2. 用页边距区域承载页脚

删除原来的 .footer 固定定位规则,以及 @media print 中重新设置固定定位的规则,再加入以下 CSS:

@page {
  margin: 15mm 12mm 40mm 12mm;

  @bottom-center {
    content: element(pdfFooter);
    vertical-align: top;
  }
}

body {
  margin: 0;
  padding: 0;
  font-family: "Microsoft JhengHei";
  font-size: 10px;
  color: #333;
  line-height: 1.4;
}

.footer {
  position: running(pdfFooter);
  width: 100%;
  font-size: 8pt;
  line-height: 1.4;
  background-color: transparent;
}

.footer-remark {
  margin: 0 0 4px 0;
  text-align: left;
  font-size: 8pt;
}

.footer-table,
.footer-right-table {
  width: 100%;
  border-collapse: collapse;
}

.page-counter::before {
  content: counter(page) " / " counter(pages);
}

pdfFooter 是自定义名称,running(pdfFooter) 与 element(pdfFooter) 必须一致。其余页脚单元格样式可以保留。

示例中的 40mm 是底部页边距的起始配置,并非根据现有材料测得的页脚高度。应根据实际页脚高度和所需间隔调整,尤其要考虑 {{footerRemark}} 换行后占用的空间。vertical-align: top 让页脚从底部页边距区域的上端开始排列。如果需要额外间隔,可以给 .footer 增加适量的 padding-top,并把它计入预留高度。

3. 清理合并后的 CSS 覆盖

这些 HTML、CSS 片段拼入同一个文档后,选择器会共同参与样式计算。表格片段中的 *、body 规则并不只作用于正文表格。

建议把字体和正文基础样式集中到主模板,表格片段只保留 .projection-table 及其后代选择器。重点检查最终样式中是否还存在:

  • .footer { position: fixed; },包括 @media print 中的规则。
  • 原来用于补偿页脚的 body 底部 margin、padding。
  • 其他 @page 规则覆盖了底部页边距。

还要检查模板替换后的完整 XHTML。原始主 CSS 中出现了 {{table}};如果它实际被替换成 HTML 表格,就不应放在 <style> 内。如果它代表 CSS 片段,需要确认替换结果是有效 CSS。

表格的 page-break-inside 应该怎样处理?

page-break-inside: avoid 控制元素是否允许在内部分页,不负责给页脚预留空间。不要依靠给整张长表格设置它来解决重叠;表格超过一页时,避免分页的要求本身就无法满足。Flying Saucer 会放弃无法满足的分页约束。分页行为说明

如果还需要尽量避免单行被拆开,可以单独尝试:

table.projection-table tr {
  page-break-inside: avoid;
}

这仍然以单行能够放进一页的正文区域为前提。

如何确认调整有效?

用足以生成多页的表格和实际可能出现的较长 footerRemark 生成 PDF,检查第一页、中间页和最后一页,确认页脚重复显示、正文在底部页边距之前结束,且页码和总页数正确。

原问题没有提供 Flying Saucer 版本、最终拼装后的 XHTML 或可查看的重叠图片,因此无法确认是否还有其他样式影响。如果调整后仍然重叠,应先核对上述规则是否实际生效,再检查页脚是否高于预留的底部页边距。

备注:内容仅供参考。