先给结论:网页打印样式方案怎么选
先说结论:中后台和零售系统里,凡是"要打印、要套版、要量尺寸"的场景,纯 CSS 打印样式就比截图类方案更可控。字体族和行高必须显式写死,别指望浏览器默认值------Chrome 在纸上给你的字体和屏幕上不是一回事,行高更是飘的。
项目地址先放这:gitee.com/theGreatOld...
下面是后面几节要逐条回答的问题,先列出来:
- 同一段文字,三组字体族在纸上差多少毫米;
line-height到底给到多少,正文和票据能不能同一套;- 长文本在固定高度的表格行里怎么溢出、怎么截断;
- 80mm 窄纸票据上,行高要给到多少才不叠字;
- 最后汇总成一张能直接抄的取值表。
对比方案一句话带过。html2canvas + jsPDF 那条链路是截图思路,产物是图片,字重和字距取决于设备像素比,放大看边缘发虚,也没法用 @page 管分页。print-js 只负责调起打印弹窗,版式仍然是 CSS 的事。它们可以当作迁移的起点,但你要量纸上的行高和溢出,量出来的数得回到 CSS 里改,绕不开。
vue-print-designer 走的是另一条路:模板即 DOM,打印交给浏览器原生排版。字体族和行高写在模板样式里,纸上量到多少就是多少,改一个值重新调起打印就能对比,不用来回截图再拼 PDF。调参这件事,能当场量到才有意义。
本节先给一个最小骨架,三行定基调:@page 管纸张,body 管字体栈,容器管行高。
css
/* 1. 纸张:A4 纵向,页边距 10mm,留出页眉页脚空间 */
@page {
size: A4 portrait;
margin: 10mm 12mm;
}
/* 2. 字体栈:中文优先思源宋体,回退到系统宋体,最后 serif */
body {
font-family: "Source Han Serif SC", "Songti SC", "SimSun", serif;
font-size: 10.5pt; /* 五号字,打印常用起步值 */
line-height: 1.6; /* 屏幕可读,纸上偏松,后面实测再调 */
-webkit-print-color-adjust: exact;
}
/* 3. 容器:定死行高单位用 pt,避免 rem 在纸上换算漂移 */
.print-body {
line-height: 16pt;
}
注意 line-height 这里被写了两遍,是有意的:body 上的无单位值会被子元素继承后重新计算,而打印容器上写绝对值 pt,继承链就断了,量出来是多少就是多少。后面实测会发现,无单位行高在嵌套表格里会放大误差,这就是要拆开的原因。
-webkit-print-color-adjust: exact 这行留着,否则表头的浅色底纹在纸上直接消失,很多人以为是打印机问题,其实是浏览器省墨。
骨架只解决"基调"。字体族到底差几毫米、行高给 1.5 还是 1.8、窄纸怎么取值,下一节开始上尺子量。

问题一:字体族换成 serif / sans / 等宽,纸上到底差多少
先说结论:同一段 12pt 中文、line-height 压到 1.4,A4 纵向、上下边距 10mm,三组字体栈实测段落高度差 12pt(约 4.2mm)。差异主要来自字体自身的 ascender、descender:serif 的行盒被字体的内容高度撑开,把设定值顶掉;无衬线和等宽的行盒则仍按 line-height 计算。
实测条件:一段 20 行中文(每行 38 字,夹 6 个数字),Chrome 打印预览配合实体激光打印机,可用高度 785pt。
| 组 | 字体栈 | 12pt 内容高度 | 实际行盒 | 20 行段落高 | 页面结果 |
|---|---|---|---|---|---|
| A | system-ui / PingFang SC | 16.3pt | 16.8pt | 336.0pt | 一页放下 |
| B | Source Han Serif SC / SimSun | 17.4pt | 17.4pt | 348.0pt | 段落贴页尾时末行掉第二页 |
| C | JetBrains Mono / Consolas | 16.5pt | 16.8pt | 336.0pt | 一页放下,中英混排行盒轻微跳动 |
行盒高度实际取 max(line-height, ascent + descent + lineGap)。所以把 line-height 降到 1.4 以下,B 组就开始反向撑行,你改的值不再生效,量出来的是字体的值。C 组在纯数字列最稳:10 位金额宽度恒为 72pt,右对齐小数点一条线;A 组用比例数字时单行宽度浮动接近 1pt,多行金额的小数点会错开半个字符。
判定:正文用无衬线,金额与编码列单独用等宽,serif 只在正式票据的标题或说明文字里用,不要整篇换。三套 class 分别写,不靠继承:
css
/* 正文:写死具体字体名,最后兜底 sans-serif */
.print-body {
font-family: "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif;
font-size: 12pt;
line-height: 16.8pt;
}
/* 金额、编码列:等宽 + 等宽数字,避免小数点错位 */
.print-num {
font-family: "JetBrains Mono", Consolas, "Courier New", monospace;
font-variant-numeric: tabular-nums;
text-align: right;
}
/* serif 只挂标题,不挂容器 */
.print-title-serif {
font-family: "Source Han Serif SC", "Songti SC", "SimSun", serif;
font-size: 16pt;
line-height: 22pt;
}
坑在字体栈的回退顺序。同一个栈,Windows 上 SimSun 命中、Mac 上没有这个名字,直接落到 genericserif,行盒高度当场变一个值;同一份模板在两台机器上量出的行高对不上,排查半天以为是浏览器 bug。所以字体栈要写死到具体字体名再兜底,不要只留 serif 三个字母。
在 vue-print-designer 里,模板元素可以直接挂 CSS 类,把 .print-body 和 .print-num 分别挂到正文容器和数字列上,切字体只改类里的字体族,不用动模板结构,改完重新调起打印就能在同一张纸上对比。

问题二:line-height 给 1.4 还是 1.6,纸上怎么换算
先说结论:打印样式里不要用无单位行高。line-height: 1.5 在屏幕上是相对字号算的,到了纸上会随字体、字号、浏览器换算一起变,同一份模板换台机器就可能差出几毫米。打印场景直接用 pt 或 mm 写死。
我拿同一段 20 行中文正文(12pt,PingFang SC)做了三组对照,A4、页边距上下各 20mm,Chrome 打印预览量出来的数:
| 设置 | 段落总高 | 页数 | 距页脚 |
|---|---|---|---|
line-height: 1.4 |
约 118mm | 1 页 | 21mm |
line-height: 1.6 |
约 135mm | 2 页 | 被挤到第 2 页 |
line-height: 5.5mm |
110mm | 1 页 | 26mm |
1.4 和 1.6 差了 17mm,就是一行页脚的高度。1.6 那组第 19 行开始压到页脚区域,整块内容被推到第二页,只剩两行孤零零挂着。5.5mm 固定值最稳,重打三次数字一样。
行高偏大和偏小的症状不一样,排查时分开看:
- 偏大:页脚、页码、签字栏被挤到下一页。先量最后一行到页脚还有多少余量,不够就往下调 0.3mm 试。
- 偏小:上下行贴住,中文的横画和下一行的字头几乎碰上,英文还能看,中文一眼就糊。这种情况在 12pt 字号下,行高低于 4.5mm 就开始出现。
固定值的取法:正文在 4.8mm--5.5mm 之间选一个,全站统一。12pt 正文配 5.2mm 是我这边比较顺手的一档,20 行段落占 104mm,A4 正文区放得下 40 行左右,页脚留得住。标题单独放宽到 7mm--8mm,表格单元格反而要收紧到 4.2mm 上下,不然一张 30 行的表格直接跨页。
css
/* 正文:固定行高,全站一个值 */
.print-body {
font-size: 12pt;
line-height: 5.2mm;
}
/* 标题:单独放宽,别继承正文 */
.print-title {
font-size: 16pt;
line-height: 7mm;
}
/* 表格单元格:收紧,减少跨页 */
.print-table td {
font-size: 10.5pt;
line-height: 4.2mm;
}
注意标题和表格不要靠继承拿正文的行高,各自写死。继承链一长,哪个类覆盖了哪个类根本查不出来。
在 vue-print-designer 里,行高是模板参数的一部分,正文、标题、表格单元格各有一个行高输入项,单位可选 mm 或 pt。改完保存,设计器预览和实际打印走同一套样式,不用改一次打一张纸来比对。这比在浏览器里反复调 DevTools 再 Ctrl+P 强得多,尤其是表格跨页那种,预览里能直接看到分页位置。
对比一下截图方案:行高是渲染结果的一部分,已经烧进图片里了。想调 0.3mm,得回去改源模板、重新截图、重新排版,样式层根本没有下手的地方。