财务室的凭证纸是预印刷的,格子、边框、编号都印好了,打印内容必须精确落在框里。
第一次打,整体右移 3mm;改完 CSS,往下偏 1.5mm;再改,右边对上了但底部又压线。折腾一下午,废了三十多张纸。
套打偏移看着像玄学,其实只有四个源头。把四个都量出来,一次就能调对。
一、偏移的四个来源
1. 走纸误差。 打印机进纸时把纸吸进去,第一张和第十张的位置能差 0.5mm。针式机尤其明显。
2. 缩放。 打印对话框里"适应纸张"没关,内容被缩到 97%,边距全部错位。
3. 坐标原点。 CSS 的 @page margin 是相对纸张可打印区,元素定位是相对内容区,两个原点差了一个边距。
4. 打印机自身偏移。 同一个模板,HP 的机器和兄弟的机器打出来能差 2mm,因为两者的可打印区基准不同。
前三个是代码问题,第四个是硬件问题------第四个才是套打真正难的地方。
配图1:偏移来自四个方向,必须分别量,一起调必然越调越乱 
二、校准流程:先打标尺,再调参数
别凭感觉挪,先打一张标尺页:
html
<div style="position:absolute;top:0;left:0">
<div style="position:absolute;left:0;top:0;width:1mm;height:100mm;background:#000"></div>
<div style="position:absolute;left:0;top:0;height:1mm;width:100mm;background:#000"></div>
<span style="position:absolute;left:10mm;top:2mm;font-size:8pt">10mm</span>
<span style="position:absolute;left:50mm;top:2mm;font-size:8pt">50mm</span>
</div>
打出来拿尺子量:标尺的 10mm 刻度如果落在纸上 8mm 处,说明整体左偏 2mm,offsetX = +2。
量三张取平均------单张受走纸误差影响,不可信。
三、偏移量按打印机型号存
这是最关键的一条:偏移不能是一套全局值。
json
{
"printerProfiles": {
"EPSON-LQ-630K": { "offsetX": 1.8, "offsetY": 2.4, "scale": 0.998 },
"HP-LaserJet-M405": { "offsetX": -0.6, "offsetY": 0.9, "scale": 1.0 },
"TSC-TTP-244": { "offsetX": 0, "offsetY": 0, "scale": 1.0 }
}
}
渲染时按当前打印机取对应偏移:
javascript
function applyOffset(root, profile) {
root.style.transform =
`translate(${profile.offsetX}mm, ${profile.offsetY}mm) scale(${profile.scale})`
}
scale 这个字段容易被忽略。有些打印机把 100% 渲染成 99.6%,打 A4 竖版时底部会差 1mm 左右,只有加一个细微缩放才能对上。
这套结构在 vue-print-designer 的模板里就是 paper 下的字段:偏移、纸张、边距都跟着模板走,换机器只换 profile。
四、三个最常见的调错方向
误区一:用 padding 调偏移。
css
/* 错:padding 会挤压内容区,改了它整版都变 */
.paper { padding-left: 3mm; }
/* 对:用 transform 或外层 margin,不影响内部布局 */
.paper { transform: translateX(3mm); }
padding 改变的是可用宽度,原本刚好放下的表格会因为这 3mm 折行。用 transform 只挪位置,不动布局。
误区二:改 @page margin 想调位置。
@page 的 margin 影响的是内容区大小,改大了内容会重新排版,不是平移。想平移就老老实实用 transform。
误区三:只在一台机器上调。
开发机上调好了,客户现场换台打印机又偏。正确做法是从一开始就把偏移做成配置,每台机器首次使用时跑一次标尺校准,把值存进配置。
配图2:预印刷单据的边距余量通常只有 2-3mm,偏移超过就压线 
五、多联针式的校准更麻烦
针式机打多联时,偏移不是一套值。第一联(最上面)和第二联(复写层)的位置能差 0.8mm,因为打印针穿过纸张时有厚度衰减。
实测数据(EPSON LQ-630K,三联单):
| 层 | 水平偏差 | 垂直偏差 |
|---|---|---|
| 第一联 | 0 | 0 |
| 第二联 | +0.4mm | +0.6mm |
| 第三联 | +0.7mm | +1.1mm |
如果三联都要进各自的框,只能取中间值折中,或者干脆按最差的一联调。业务上通常只要求第一联对齐(存档联),后两联允许 1mm 误差。
还有一个针式特有的坑:色带磨损会让字迹变淡,但位置不变。所以"看起来偏了"和"字淡了"是两件事,别混着调。
六、校准功能要做进产品
如果每个客户都要你远程调一次偏移,这个产品就没法规模化。校准应该做成界面:
javascript
// 打印一张带十字线的测试页,让用户填偏差
function calibrate(printerName) {
printTestPage() // 打测试页
const dx = Number(prompt('水平偏差(mm),右偏为正'))
const dy = Number(prompt('垂直偏差(mm),下偏为正'))
saveProfile(printerName, { offsetX: dx, offsetY: dy })
}
交互糙一点没关系,关键是让客户自己完成,而不是每次都找你。
更友好的做法是给 8 个方向键,用户边打边微调,每次 0.2mm:
javascript
let offset = { x: 0, y: 0 }
function nudge(dx, dy) {
offset.x += dx * 0.2
offset.y += dy * 0.2
repaint(offset) // 只重绘预览,不重打
}
预览对上了再打样张确认,能省一半纸。
六、验收标准
套打验收不能只看"大概齐"。建议按这三条:
- 十字线落在框内:内容边界与预印刷框的偏差 ≤ 1mm;
- 连打十张稳定:第 1 张和第 10 张的偏差 ≤ 0.5mm;
- 换纸不跑偏:同一批纸抽 3 张,位置一致。
第二条最容易挂。针式机连打时走纸会累积误差,解决办法是每张之间留 200ms 间隔,别让队列堆积。
配图3:套打对位要在真机上验证,屏幕上再准也不作数 
七、模板化是套打的前提
套打对精度的要求,决定了它不能靠手写 CSS 硬调。坐标必须是数据:
json
{
"paper": { "width": 240, "height": 140, "unit": "mm", "margin": 0 },
"calibration": { "offsetX": 1.8, "offsetY": 2.4, "profile": "EPSON-LQ-630K" },
"elements": [
{ "type": "text", "x": 12.5, "y": 8.0, "binding": "invoiceNo" },
{ "type": "text", "x": 12.5, "y": 16.0, "binding": "customerName" }
]
}
坐标写死在 CSS 里,换一台机就要改一次代码;写成数据,换机只换 calibration。
这也是 vue-print-designer 把 paper 和元素坐标都放进模板的原因------套打场景下,"版式"和"机器校准"必须分开维护。改版式的人不该关心打印机型号,反之亦然。
一个团队如果在套打上反复返工,八成是因为这两个东西混在一起:模板里既写了业务字段,又写了某台机器的偏移,换机时只能整份复制再改。
八、一句话总结
套打偏了不要急着挪,先量清楚是走纸、缩放、原点、机器里的哪一个。四个来源混在一起调,一定越调越偏。
量准了之后,把偏移存成按机型区分的配置,让客户自己能校准------这是套打能不能规模化的分界线。