经纬志
首页 / 实战手册 / 样式与渲染

打印出的图比屏幕小一圈,96 与 72 DPI 混用了

先看结论

屏幕上刚好铺满 A4 的地图,打印出来只占左上角四分之三,比例尺也对不上。

最后验证 2026-11-03 · 多技术栈 · 更新 2026-10-29 · 引用本文

现象

地图容器设置为 width: 794px; height: 1123px(按 96 DPI 算的 A4)。 浏览器截图导出 PNG,再插入 A4 PDF 打印: 地图只占页面的 75%,比例尺标注的 1 公里量出来是 0.75 公里, 右下角留出大片空白。

原因

存在两套换算:

场景DPIA4 像素尺寸
CSS / 屏幕96794 × 1123
传统印刷72595 × 842
打印输出3002480 × 3508

截图是 794 像素宽的位图,按 72 DPI 解释,物理宽度变成 794 / 72 = 11.03 英寸,而 A4 只有 8.27 英寸, 图像被整体缩放到 8.27 / 11.03 = 75%。

反过来,如果导出服务按 96 DPI 写入 PDF 元数据,打印就会正好铺满。 问题不在浏览器截图,而在插入 PDF 时声明的分辨率。

排查过程

  1. 看导出的位图实际像素尺寸,与 A4 在 96 DPI 下的 794×1123 比较。
  2. 用图像工具查看位图的 DPI 元数据,是否为 72。
  3. 在 PDF 里量地图占页面的比例,与 72/96 的比值对照。
  4. 检查 puppeteer 的 deviceScaleFactor 与 viewport 设置。
# 查看图片 DPI 元数据
magick identify -verbose map.png | Select-String -Pattern "Resolution"

# 794 px 在 72 DPI 下 = 11.03 in,A4 宽 8.27 in -> 缩放到 75%

解决方案

导出时明确指定 DPI,并按目标 DPI 计算像素尺寸。 打印 300 DPI 的 A4,需要 8.27 × 300 = 2481 像素宽。

// puppeteer:按 300 DPI 导出 A4
const A4_W = 8.27, A4_H = 11.69, DPI = 300;

await page.setViewport({
  width: Math.round(A4_W * 96),      // 794,CSS 布局仍按 96
  height: Math.round(A4_H * 96),
  deviceScaleFactor: DPI / 96        // 3.125,位图放大到 2481×3509
});

await page.screenshot({
  path: 'map-a4.png',
  clip: { x: 0, y: 0, width: 794, height: 1123 },
  type: 'png'
});
# 插入 PDF 时声明正确的 DPI
magick map-a4.png -units PixelsPerInch -density 300 map-a4-300dpi.png

# 或者直接把 DPI 写进位图
magick map.png -density 96 -units PixelsPerInch map-96dpi.png

屏幕上的地图要配合打印,比例尺控件必须用 L.control.scale({ imperial: false, metric: true }), 并在导出前按 DPI 换算一次。

// 比例尺显示的是屏幕像素,导出前需按 DPI 修正
const dpi = 300, cssDpi = 96;
const scale = dpi / cssDpi;           // 3.125
// 图上 1 厘米对应实地 = 100 / (scale × 每厘米像素) ...
// 实际做法:让比例尺文字随导出倍率一起缩放
map.getContainer().style.transform = `scale(${1 / scale})`;

验证

导出的 PNG 为 2481×3509,插入 A4 PDF 后铺满整页; 用尺子量图中比例尺的 1 公里段,与 PDF 上标注的长度一致。

console.assert(Math.round(8.27 * 300) === 2481, '像素宽不符');
// 打印后比例尺校验:图上 1 km 应为 8.27 * (1000 / 地图实地宽度) 英寸

适用版本

工具版本说明
CSS 规范—96 px = 1 in
puppeteer21 及以上deviceScaleFactor 控制位图倍率
ImageMagick7.x-density 写入 DPI 元数据
Leaflet1.9.xL.control.scale 基于屏幕像素
引用本文

经纬志(2026)。《打印出的图比屏幕小一圈,96 与 72 DPI 混用了》。https://www.jingweizhi.com/cookbook/print-dpi-misalign.html(访问日期 2026-11-03)。