package cn.iocoder.yudao.module.qcreport.service.render;
|
|
import cn.iocoder.yudao.module.qcreport.engine.Numbers;
|
import cn.iocoder.yudao.module.qcreport.engine.PageSetting;
|
import cn.iocoder.yudao.module.qcreport.engine.PageSizes;
|
import cn.iocoder.yudao.module.qcreport.engine.ResolvedPage;
|
|
/**
|
* 打印参数:把纸张配置换算成 Chromium 打印 PDF 需要的几何与样式。
|
* <p>
|
* 纯函数、不依赖 Spring 与 Playwright,便于单测。
|
* <p>
|
* 几何**一律**从 {@link PageSizes#resolve} 取,与 {@code HtmlRenderer} 写进 HTML 的那行
|
* {@code @page { size: …; margin: …; }} 同一个来源、同一个数字格式化函数
|
* ({@link Numbers#toString(double)}),因此不存在「PDF 纸面与页面预览对不上」的可能。
|
* 用 {@code String.format} 会引入 locale 差异并输出 {@code 210.0} 这类与前端不一致的文本。
|
*/
|
public record PdfPrintOptions(String widthCss, String heightCss,
|
String marginTopCss, String marginRightCss,
|
String marginBottomCss, String marginLeftCss,
|
String footerTemplate, String printCss) {
|
|
/**
|
* 仅供打印的加固样式,由 Playwright {@code addStyleTag} 注入。
|
* <p>
|
* <b>不能写进渲染引擎的 BASE_CSS</b>:产物 HTML 与前端冻结样例是逐字比对的,
|
* 动它会让存量报告的「重新生成」结果变样。
|
* <p>
|
* <b>{@code break-inside: avoid} 绝不能加在 {@code table} 上</b>:那是「整张表不许跨页」,
|
* 长表在一页放不下时会被整体搬到下一页,第 1 页只剩页眉与大片空白(已实际观测到,
|
* 60 行检验项的表就是如此)。长表跨页靠的是 {@code thead} 在每页重复表头 + 行内不断页,
|
* 所以名单里保留 {@code tr/td/th/img}、去掉 {@code table}。
|
*/
|
public static final String PRINT_CSS = """
|
thead { display: table-header-group; }
|
tfoot { display: table-footer-group; }
|
tr, td, th, img { break-inside: avoid; page-break-inside: avoid; }
|
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }""";
|
|
/**
|
* 页脚模板。Playwright 的 header/footer 模板不进浏览器默认样式,
|
* 必须自带内联样式与字号,否则是 0 号字看不见。
|
*/
|
private static final String FOOTER_TEMPLATE = """
|
<div style="width:100%;font-size:9px;font-family:'Microsoft YaHei',sans-serif;\
|
color:#333;text-align:center;padding:0 10mm;">第 <span class="pageNumber"></span> \
|
页 / 共 <span class="totalPages"></span> 页</div>""";
|
|
/**
|
* 页眉模板:一个空 div。
|
* <p>
|
* 必须显式给一个<b>非 null</b> 的值。开了 {@code displayHeaderFooter} 却把 headerTemplate
|
* 留空时,Chromium 会用它自己的默认页眉 —— 左上角打印日期({@code 2026/9/18 21:34})、
|
* 右上角文档标题。那两串东西没人要过,页眉时间还容易被误当成报告出具时间,
|
* 所以这里用一个空 div 把它顶掉,页面上只保留我们自己的页脚页码。
|
*/
|
public static final String HEADER_TEMPLATE = "<div></div>";
|
|
public static PdfPrintOptions of(PageSetting page, boolean withPageNumber) {
|
ResolvedPage resolved = PageSizes.resolve(page);
|
return new PdfPrintOptions(
|
mm(resolved.widthMm()),
|
mm(resolved.heightMm()),
|
mm(resolved.marginTopMm()),
|
mm(resolved.marginRightMm()),
|
mm(resolved.marginBottomMm()),
|
mm(resolved.marginLeftMm()),
|
withPageNumber ? FOOTER_TEMPLATE : "",
|
PRINT_CSS);
|
}
|
|
private static String mm(double value) {
|
return Numbers.toString(value) + "mm";
|
}
|
|
}
|