接入指南

打印组件接入文档

将传思网"打印模板"能力嵌入到您的网站,让您的用户在线设计模板、下发到本地客户端并触发打印。共三步:完成身份核验(申请 API Key 并换取 Token)→ 安装本地 ClientPrinter 客户端 → 引入 wacp.js SDK 调用「设计 / 下发 / 打印」。

在线设计 → 下发到本地 → 本地打印

1身份核验(申请 API Key + 换取 Token)

使用 wacp.js 的设计器与打印能力前,需要一个有效的 API Key 并换取 Poster 服务 Token(打印模板与海报共用同一类型 Token)。详细步骤请参阅:

概要:注册账号 → 申请 API Key → 用 ApiKey + serviceType=Poster 换取 Token(有效期 1 小时)→ 把 Token 传给 SDK。
注意:OCR / ASR / TTS 的 Token 不可用于打印模板接入,必须使用 serviceType=Poster。
Token 使用范围:Token 仅在调用 wacp.openDesigner 打开设计器时使用(设计器打开前会先校验 Token 有效性)。
后续 wacp.sendTemplate(下发模板)和 wacp.print(打印)完全走本机链路,无需 Token、断网亦可继续操作。

2安装本地打印客户端 ClientPrinter

打印动作发生在用户的本机(依赖系统打印机驱动),因此需要在每台需要打印的电脑上安装 ClientPrinter。它兼容 Windows 7 / 10 / 11(32 位 / 64 位)。

安装后的客户端为 SDK 提供两项能力:

  • 注册 acp:// 自定义协议,浏览器可自动唤起客户端,无需用户手动启动;
  • 持久化已下发的模板,断网仍可继续打印;客户端不持久化任何 Token。
首次接入须知:
• 点击上方按钮下载安装包后,双击运行并按提示完成系统打印机驱动安装;
• 安装后需允许其开机自启,否则用户每次需手动启动;
• 浏览器首次唤起 acp:// 时会弹出"是否允许打开 ClientPrinter",请勾选"始终允许"以避免反复确认;
• 未检测到客户端运行时,SDK 会自动弹出下载提示。

3引入 SDK 并完成三段式调用

3.1 引入 wacp.js

在您的网站页面引入 wacp.js:

html
<script src="https://your-webapi.com/printtemplate/wacp.js"></script>
<script>
  var wacp = new Wacp();
</script>

3.2 打开模板设计器(openDesigner)

调用 wacp.openDesigner(token, options, onReady) 在新窗口打开设计器。Token 会被同步校验(调用服务端 API 验证有效性),校验通过后才加载设计器。用户保存模板时通过 onReady 回调拿到 templateContent(模板完整内容),由三方自行存储。

参数 必填 类型 说明
token 是 string Poster 服务 Token。打开前会先调用服务端 API 校验有效性,校验失败则拒绝加载设计器。
options.templateData 否 object 三方自行存储的模板内容对象。不传 则创建空白模板;传入 则加载已有模板用于继续编辑。
options.dataSourceFields 否 object 设计器内可绑定的数据源字段映射,{ 英文键: '中文显示名' },例如 { userName: '姓名', orderId: '工号' }。
options.dpi 否 number 新建模板时画布的初始 DPI,常用 203(热敏标签/小票)/ 300(激光)/ 600(照片)。仅未传 templateData 时生效;不传时设计器默认 203,用户仍可在工具栏修改。详见下方「DPI 参数怎么填」。
options.width / options.height 否 number 新建模板时画布初始尺寸(单位:像素)。仅未传 templateData 时生效,未传时设计器自带默认。
onReady 否 function 设计器保存或退出时回调 { templateContent, error, type },templateContent 为模板完整内容(对象),由三方自行持久化存储。
js
// 打开设计器(独立窗口)
wacp.openDesigner(
  'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6',   // Token(必填,服务端校验通过后才加载设计器)
  {
    templateData: myTplContent,            // 传入已有模板内容进行编辑;留空则新建
    dpi: 203,                              // 新建画布初始 DPI(203 / 300 / 600)
    dataSourceFields: {
      userName: '姓名',
      orderId:  '工号'
    }
  },
  function (ev) {
    if (ev.error) {
      console.error('[wacp] 设计器返回错误:', ev.error);
      return;
    }
    // ev.templateContent 为模板完整内容,由三方自行存储
    localStorage.setItem('myTplContent', JSON.stringify(ev.templateContent));
    console.log('模板已保存,templateContent:', ev.templateContent);
  }
);
设计器要求:必须在「打印参数」面板中填写 widthMm(毫米宽)/ heightMm(毫米高)/ dpi(建议 203 / 300),保存时会强校验,否则拒绝保存。
关于模板存储:模板内容由三方自行存储,SDK 不保留任何云端副本。保存回调返回的 templateContent 即为完整模板数据,建议业务侧序列化为 JSON 存储到自己的数据库。

3.3 下发模板到本地(sendTemplate)

用户首次打印(或模板更新后)需把模板内容下发到本地 ClientPrinter。与旧版 downloadTemplate 不同,sendTemplate 直接通过本机 WebSocket 链路传递模板内容,无需 Token、无需联网。模板 ID 由三方自行生成和管理,作为 ClientPrinter 本地的唯一标识。

参数 必填 类型 说明
templateId 是 string 模板 ID,由三方自行生成和管理,用于 ClientPrinter 本地唯一标识。建议使用有意义的命名,如 工号牌_2026版 或业务主键。
templateData 是 object|string 模板内容,openDesigner 保存时回调返回的 templateContent 对象,或其 JSON 字符串。
name 否 string 本地显示名称,仅用于客户端「查看本机模板」下拉列表展示;允许重复,本地仍以 templateId 唯一。未传时默认使用 templateId 作为名称。
js
// 不传名称:本地以 templateId 作为显示名
wacp.sendTemplate('my_template_id', myTplContent)
  .then(function (info) {
    // info = { templateId, name }
    console.log('模板已下发到本地:', info.templateId, info.name);
  })
  .catch(function (err) {
    // 常见错误:本地 ClientPrinter 未运行(acp:// 唤起失败)/ templateData 为空
    alert('下发失败:' + err.message);
  });

// 传入中文名称:客户端下拉列表中会显示"工号牌-2026版"
wacp.sendTemplate('my_template_id', myTplContent, '工号牌-2026版');
无需 Token:sendTemplate 完全走本机 WebSocket 链路,不需要 Token,断网也可正常操作。这是与旧版 downloadTemplate(需走服务端中转)的核心区别。
关于名称重复:本地客户端「查看本机模板」的下拉框仅展示 name,不拼接 ID。如果多个模板使用同名称,用户从视觉上无法区分,但选中后系统仍会按唯一的 templateId 加载;建议业务侧在名称中附加可识别后缀(如「工号牌-A版」/「工号牌-B版」)。

3.4 触发本地打印(print)

打印动作完全在用户本机执行,无需联网。print 支持两种模式:单步打印(直接传入模板内容,SDK 自动下发并打印)和分步打印(先 sendTemplate 下发,再 print 仅传 templateId)。一次调用支持多条数据 + 多份份数。

参数 必填 类型 说明
templateId 是 string 模板 ID,由三方自行生成。若同时传入 templateData,SDK 会自动先下发再打印;若已通过 sendTemplate 下发过,可仅传 templateId。
payload.rows 是 object[] 非空数组,每个对象一条打印记录。键名必须与设计器中绑定的 dataSourceFields 一致。
payload.copies 否 number 每条记录的打印份数,默认 1。
templateData 是 object|string 模板内容,openDesigner 保存时回调返回的 templateContent 对象,或其 JSON 字符串。SDK 会自动调用 sendTemplate 下发后再打印。
js
// 单步打印:直接传入模板内容,SDK 自动下发 + 打印
wacp.print('my_template_id', {
  rows: [
    { userName: '张三', orderId: 'A001' },
    { userName: '李四', orderId: 'A002' }
  ],
  copies: 1
}, myTplContent)
.then(function (res) {
  console.log('已下达打印任务:', res);
})
.catch(function (err) {
  console.error('打印失败:', err.message);
});

// 分步打印:先 sendTemplate 下发,后续打印仅传 templateId
wacp.sendTemplate('my_template_id', myTplContent).then(function () {
  return wacp.print('my_template_id', { rows: [{ userName: '张三', orderId: 'A001' }], copies: 1 }, myTplContent);
});
单步 vs 分步:每次调用 print 传入 templateData 时,SDK 会自动先执行 sendTemplate 再打印,确保模板为最新版本。如果模板未变更且已下发过,可使用分步模式减少网络开销(仅走本机链路)。

3.5 事件订阅

事件 触发时机
connectedSDK 与本地 ClientPrinter 建链成功。
disconnected本地连接断开(用户关闭客户端、网络异常等)。
printed本地完成一次打印,payload 含 taskCount 等统计字段。
error调用链路中的任何错误(连接超时、token 过期、模板不存在)。
idCardRead / idCardConnected /
idCardDisconnected / idCardError /
idCardReaderSwitched / idCardReaderEnabled
身份证读卡器推送事件,详见下文「3.7 身份证读卡器」。
js
wacp.on('connected', function () { console.log('客户端已连接'); });
wacp.on('printed',   function (e) { console.log('打印完成', e); });
wacp.on('error',     function (e) { console.error('SDK 错误', e); });

扩展能力(本地存取 / 读卡器 / 系统)

除「设计 / 下发 / 打印」主链路外,wacp.js 还封装了 ClientPrinter 在本机提供的应用能力,可调用本机键值存取、身份证读卡器、系统动作等,本节能力全部走本机链路,不需 token。

3.6 本机键值对存取 (setItems / getItems / removeItems)

将 UI 常用参数(如部门名、上次选择、设备号)写入本机 Settings 表,断电不丢;同一台机器上不同页面/会话都能读到。存储 value 统一以字符串语义调度。

js
// 写入(同名键会被覆盖)
wacp.setItems({
  deptName: '行政部',
  userName: '张三',
  lastTplId: 'tpl_20260520_xxx'
});

// 读取:返回只包含存在的 key
wacp.getItems(['deptName', 'userName']).then(function (m) {
  console.log(m.deptName, m.userName); // 行政部 张三
});

// 删除
wacp.removeItems(['deptName']);
与浏览器 localStorage 的区别:setItems 写入的是本机 ClientPrinter,与浏览器、域名均无关;同一台电脑上的任何三方网站都能读取。

3.7 身份证读卡器

ClientPrinter 内置三种读卡器驱动:HS100N(中控 IDM40,USB 免驱)、SerialPort(串口类通用读卡器)、IDR210(明泰 iDR210 USB-HID,与 CardPrint 同款)。SDK 提供读卡器列表查询、状态查询 / 切换 / 开关控制以及 推送式读卡事件。

方法 说明
getIDCardReaderList()查询 ClientPrinter 当前支持的所有读卡器型号。返回 { readers: [{ type, name }], current, enabled }。推荐前端用这个列表动态渲染下拉选项,而不是硬编码。
switchIDCardReader(type)切换型号,type 为 'HS100N' / 'SerialPort' / 'IDR210'。返回 { readerType }。
getIDCardReaderType()查询当前型号。返回 { readerType }。
setIDCardReaderEnabled(enabled)开启 / 关闭读卡。返回 { enabled, readerType }。
getIDCardReaderEnabled()查询读卡开关状态。返回 { enabled, readerType }。

推送事件(服务端主动广播):

事件名 触发时机 / payload
idCardRead读到身份证。ev.data 为身份证信息对象,字段详见下表。
idCardConnected读卡器设备已连接(USB 插入 / 串口打开)。
idCardDisconnected读卡器设备已拔出,插回后会自动重新广播 connected。
idCardError读卡过程异常(SDK 异常、设备占用等)。
idCardReaderSwitched调用 switchIDCardReader 后会同时返回 Promise,也会广播该事件。
idCardReaderEnabled读卡开关变更(包括在 ClientPrinter 本机 UI 手动切换)。

idCardRead 的 ev.data 字段:

字段名 类型 说明
Namestring姓名
Genderstring性别("男" / "女")
Ethnicitystring民族
BirthDatestring出生日期(yyyyMMdd,如 19900101)
Addressstring住址
IDNumberstring18 位身份证号
IssueAuthoritystring签发机关
ValidFromstring有效期起始(yyyyMMdd)
ValidTostring有效期截止(yyyyMMdd;SerialPort 读卡器此字段为"起止-截止"拼接)
PhotoBase64string证件照的 Base64 字符串(不含 data:image/bmp;base64, 前缀,可直接赋值给 img.src = 'data:image/bmp;base64,' + PhotoBase64)。仅 IDR210 支持,HS100N / SerialPort 固定为空字符串。
RawDatastring原始数据(当前版本为空字符串,保留字段)
ReadTimedatetimeClientPrinter 本机的读卡时间
ReaderTypestring本次读卡使用的读卡器型号:'HS100N' / 'SerialPort' / 'IDR210'
js
// 订阅读卡事件(在调用 setIDCardReaderEnabled(true) 之前订阅以免丢事件)
wacp.on('idCardRead', function (ev) {
  // ev.data 示例:
  // {
  //   Name: '张三', Gender: '男', Ethnicity: '汉', BirthDate: '19900101',
  //   Address: '北京市海淀区中关村大街1号',
  //   IDNumber: '110108199001011234',
  //   IssueAuthority: '北京市公安局海淀分局',
  //   ValidFrom: '20200101', ValidTo: '20400101',
  //   PhotoBase64: '/9j/4AAQSkZJRgABAQ...'   // 仅 IDR210 有值,其他型号为空字符串
  //   ReadTime: '2026-10-17 10:30:00',
  //   ReaderType: 'IDR210'
  // }
  console.log('读到身份证', ev.data);

  // 渲染头像(PhotoBase64 不含 data: 前缀,需要自己拼)
  if (ev.data.PhotoBase64) {
    var img = document.getElementById('avatar');
    img.src = 'data:image/bmp;base64,' + ev.data.PhotoBase64;
  } else {
    console.warn('当前读卡器(' + ev.data.ReaderType + ')不支持头像读取');
  }
});
wacp.on('idCardError',        function (ev) { console.error('读卡错误', ev.data); });
wacp.on('idCardDisconnected', function () { /* 提示用户重插 */ });

// 1) 获取本机支持的读卡器列表(前端动态渲染下拉框时用)
wacp.getIDCardReaderList().then(function (r) {
  // r.readers: [{ type: 'IDR210', name: '明泰 iDR210' }, ...]
  // r.current: 当前型号;r.enabled: 当前开关
  console.log('支持的读卡器', r.readers, '当前:', r.current, '开关:', r.enabled);
});

// 2) 选择型号 + 开启读卡(用列表返回的 type 字段切换)
wacp.switchIDCardReader('IDR210')
    .then(function () { return wacp.setIDCardReaderEnabled(true); })
    .then(function (r) { console.log('读卡已开启', r); });
头像(PhotoBase64)设备支持:
• ✅ IDR210(明泰 iDR210):SDK 在读卡时自动将证件照保存为 BMP 文件并读取,返回完整的 Base64 字符串;
• ❌ HS100N(中控 IDM40):经实测,该设备 SDK 无法正确返回证件照,PhotoBase64 固定为空字符串;
• ❌ SerialPort(串口类读卡器):协议层面仅传输文字信息,不支持照片;
• PhotoBase64 为 BMP 格式原始字节的 Base64 编码,不含 data:image/bmp;base64, 前缀,前端渲染时需自行拼接。
设备要求:
• HS100N(中控 IDM40)需插入 USB 读卡器,ClientPrinter 会自动检测;
• IDR210(明泰 iDR210)需插入 USB 读卡器,ClientPrinter 会自动检测;
• SerialPort(串口类读卡器)需要使用对应串口线连接,ClientPrinter 会自动探测 COM 口;
• 设备拔插后会自动推送 idCardConnected / idCardDisconnected,业务侧只需代码订阅事件即可。

3.8 系统能力

方法 说明
createDesktopShortcut(name, url)在用户桌面创建一个网页快捷方式,使用默认浏览器打开。按机器头一次使用时快速引导。
getClientNum()获取本机 ClientPrinter 的唯一通道号 ClientNum,返回 { clientNum }。
clearPrintTasks()清空本机打印任务队列,不影响已下发给驱动的任务。
clearTemplates()清空本机所有已下发的模板(谨慎),后续需重新 sendTemplate。
connect()显式建立 WebSocket 连接。默认采用懒建连,等首次业务调用时自动建连。
js
// 1)创建桌面快捷方式
wacp.createDesktopShortcut('打印组件 Demo',
    'https://tools.eainfor.com/printtemplate/demo-printer.html');

// 2)查询本机通道号
wacp.getClientNum().then(function (r) { console.log(r.clientNum); });

// 3)集中作业中零凌时清队
wacp.clearPrintTasks();

3.9 屏幕截图(screenshot)

调用本机 ClientPrinter 内置的截图工具,用户在屏幕上框选区域、添加标注后,点击截图工具栏「上传」按钮,SDK 会把截图以 JPEG Base64 形式(含 data:image/jpeg;base64, 前缀)返回给三方,便于直接上传到自己的服务器或做本地预览。由于需要用户交互操作,超时时间为 120 秒。

前置条件:截图功能需要用户本机已安装并运行 ClientPrinter 客户端。调用前建议先监听 connected 事件,确保 WebSocket 链路已建立。
js
// 发起截图(会弹出本机截图工具,用户框选 → 标注 → 上传)
wacp.screenshot()
  .then(function (result) {
    // result.imageBase64 为 JPEG Base64(含 data:image/jpeg;base64, 前缀)
    if (!result.imageBase64) {
      console.warn('截图返回空数据');
      return;
    }

    // 1)直接预览
    var img = document.createElement('img');
    img.src = result.imageBase64;
    document.body.appendChild(img);

    // 2)或上传到自己的服务器
    var base64Only = result.imageBase64;
    if (base64Only.indexOf('data:') === 0) {
      base64Only = base64Only.split(',')[1];
    }
    var binary = atob(base64Only);
    var bytes = new Uint8Array(binary.length);
    for (var i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    var blob = new Blob([bytes], { type: 'image/jpeg' });
    var fd = new FormData();
    fd.append('file', blob, 'screenshot.jpg');

    fetch('/api/upload', { method: 'POST', body: fd })
      .then(function (resp) { return resp.json(); })
      .then(function (data) { console.log('上传成功', data); })
      .catch(function (err) { console.error('上传失败', err); });
  })
  .catch(function (err) {
    // 超时:用户 120 秒内未完成操作
    // 取消:用户在截图工具栏点击「取消」
    console.error('截图失败:', err.message);
  });

参数与返回值:

项目 说明
方法签名 wacp.screenshot()
入参 无。
返回值 Promise<{ imageBase64: string }> 截图 JPEG Base64,含 data:image/jpeg;base64, 前缀。
超时 120 秒。超时或用户主动取消时 Promise reject,错误信息含「截图超时」或「用户取消」。
前置条件 ClientPrinter 已启动并与 wacp.js 建立 WebSocket 连接(监听 connected 事件确认)。
注意事项:
• screenshot() 走本机链路,不需要 token,但需要 ClientPrinter 在运行。
• 截图完成后用户点击工具栏「上传」按钮才会返回结果;点击「取消」会 reject。
• 返回的 Base64 可直接赋值给 &lt;img src&amp;gt; 预览,或解码为二进制后上传到服务器。
• 建议在截图期间给用户一个「截图中...」的加载提示,完成后隐藏。
js
// 推荐:配合 connected 事件,确保客户端就绪后再发起截图
var ready = false;
wacp.on('connected', function () { ready = true; });

function takeScreenshot() {
  if (!ready) {
    alert('请先启动 ClientPrinter 客户端');
    return;
  }
  var btn = document.getElementById('btnScreenshot');
  btn.textContent = '⏳ 截图中...';
  btn.disabled = true;

  wacp.screenshot()
    .then(function (r) {
      btn.textContent = '📷 发起截图';
      btn.disabled = false;
      if (r.imageBase64) {
        console.log('截图成功,数据长度:', r.imageBase64.length);
      }
    })
    .catch(function (err) {
      btn.textContent = '📷 发起截图';
      btn.disabled = false;
      console.error('截图失败', err.message);
    });
}

3.10 Chrome Kiosk 全屏窗口(openNativeWebView / closeNativeWebView)

让 ClientPrinter 在本机启动 Google Chrome 浏览器,以 --kiosk --app=URL 参数模式真全屏打开指定页面。Kiosk 模式是 Chrome 专门为自助终端设计的:无标题栏、无边框、无地址栏、无标签页、无任务栏,整个屏幕只有你的网页内容。

Chrome 里的页面同样引用 wacp.js,所有 SDK 能力(设计器 / 下发 / 打印 / 读卡器 / 截图等)自动可用——因为它们共用同一个本机 WebSocket 服务(ws://127.0.0.1:8080/)。

为什么不自己嵌入 WebView2:ClientPrinter 必须保持 .NET Framework 4.0,而 WebView2 最低要求 net462。改用 Chrome Kiosk 无需任何 .NET 框架升级,Chrome 自带现代 Chromium 内核,自动跟随系统更新。

打开窗口 openNativeWebView:

参数 必填 类型 说明
opts.url 否 string 要加载的 URL。不传 则自动取当前页面地址(window.location.href),让同一个页面在 Chrome Kiosk 里再次打开。
opts.closeBrowser 否 boolean Chrome Kiosk 成功启动后,是否尝试关闭当前浏览器页面。默认 true。浏览器安全策略可能阻止 window.close(),此时会自动降级为跳转到 about:blank。

返回 Promise:

status 值 含义
'1'✅ Chrome Kiosk 已成功启动。
'-2'❌ 本机未检测到 Google Chrome 浏览器,用户取消安装提示。
其他❌ 启动失败。

关闭窗口 closeNativeWebView:

方法 说明
wacp.closeNativeWebView()让 ClientPrinter kill Chrome 进程来关闭 Kiosk 窗口(Kiosk 模式下 Chrome 没有窗口关闭按钮,也无法用 CloseMainWindow 优雅退出)。返回 status='1' 表示已触发关闭;status='0' 表示当前没有打开的 Kiosk 窗口。
js
// 场景 A:打开当前页面的 Chrome Kiosk 全屏版,浏览器自动关闭
wacp.openNativeWebView().then(
  function () { console.log('✅ Chrome Kiosk 已启动'); },
  function (err) {
    // 常见错误:Chrome 未安装 / ClientPrinter 未运行
    alert('打开失败:' + err.message);
  }
);

// 场景 B:打开指定的 Kiosk 页面,保留浏览器窗口(用于调试)
wacp.openNativeWebView({
  url: 'https://your-site.com/kiosk/employee-checkin',
  closeBrowser: false
});

// 场景 C:Kiosk 里的页面自己调 closeNativeWebView 退出
var wacp = new Wacp();
document.getElementById('btnExit').onclick = function () {
  // 可选:先做自己的密码校验(Kiosk 里无关闭按钮,密码由网页自己管)
  var pwd = prompt('请输入退出密码');
  if (pwd === 'exit2026') {
    wacp.closeNativeWebView();  // ClientPrinter 会 kill Chrome 进程
  }
};
关闭方式:
Kiosk 模式下 Chrome 完全锁死,没有关闭按钮、没有 ESC/F11 快捷键、Alt+F4 也无效。
唯一的关闭方式:页面调 wacp.closeNativeWebView()(由网页完全控制生命周期)。
这也意味着你可以在网页侧自行加密码校验(如场景 C 所示)。
前置条件:Edge 或 Chrome(Chromium 内核浏览器)
• ClientPrinter 优先使用 Edge(Win10/11 系统自带,100% 机器都有),Edge 不存在时回退到 Chrome;
• Chrome 允许自定义安装目录,ClientPrinter 通过 注册表(Uninstall 键的 InstallLocation)读取真实路径,不依赖 PATH 环境变量,也不假设一定装在 C 盘;
• 如果系统上 Edge 和 Chrome 都没装,ClientPrinter 会弹出提示并引导到 Chrome 官方下载页;
• ClientPrinter 启动 Kiosk 前会 先 kill 掉所有已运行的该浏览器进程(Chromium 复用同一 profile 时会丢弃 --kiosk 参数,必须关掉重开才能生效)。
单例保护:同一台电脑上同一时刻只能打开 一个 Chrome Kiosk 窗口。重复调用 openNativeWebView 会先 kill 旧的 Chrome 进程,再启动新的。

在线体验

我们提供了一个集成 Demo 启动页,覆盖「换 Token / 设计模板 / 下发到本地 / 触发打印」全链路,可在不写代码的情况下走通流程:

建议先在 Demo 启动页跑通一次完整链路(确认本机 ClientPrinter 已安装、能成功唤起 + 出纸),再按上面的示例代码集成到您自己的网站。

DPI 参数怎么填

设计器顶部工具栏的 DPI 输入框(默认 203)决定模板的 毫米 ↔ 像素 折算关系:

formula
画布像素宽 = widthMm × dpi ÷ 25.4
画布像素高 = heightMm × dpi ÷ 25.4

所以 DPI 必须与目标打印机的实际 DPI 一致,否则本地客户端会按比例缩放,导致出纸尺寸偏大或偏小。

推荐取值

打印机类型 推荐 DPI
热敏标签机(条码、胸卡)203
热敏小票机203
普通激光 / 喷墨打印机300
高精度照片打印600
快速判断(标签机/小票机):型号末尾常带数字。例如 XX-203 即为 203 dpi,XX-300 即为 300 dpi。

如何在 Windows 查看打印机 DPI

打开 控制面板 → 设备和打印机,右键目标打印机 → 打印机属性,然后按下表查找:

选项卡 查看位置
常规 部分驱动会在"型号 / Model"行显示 203 dpi 等字样。
高级 点 打印默认值... → 图形 或 质量,会有"分辨率"下拉框,列出 203 dpi / 300 dpi 等可选值。
设备设置 标签机驱动通常在这里有"打印头分辨率 / Print Head Resolution"选项。
注意:办公激光 / 喷墨打印机的"打印质量"里可能列出 600 dpi / 1200 dpi,那是最大输出分辨率,不是物理 DPI。普通办公场景按 300 填即可,更高反而可能引起缩放问题。

实在查不到?用试错法

如果没有规格书也找不到驱动属性,按推荐值填一个,然后通过设计器右上角的 🖨️ 打印测试页 试打一张,再用尺子量纸面:

  • 量出来比设计的 大 → 填的 DPI 小了,调高(如 203 → 300);
  • 量出来比设计的 小 → 填的 DPI 大了,调低(如 300 → 203);
  • 量出来一致 → DPI 就是对的。

常见问题

Q1:浏览器一直提示"无法连接到本地 ClientPrinter"
• 确认本机已安装并运行 ClientPrinter(任务栏托盘有对应图标);未安装请点击 下载 ClientPrinter;
• 确认浏览器允许 acp:// 协议唤起(Chrome 首次需勾选"始终允许");
• 默认监听端口为 8080,若被占用可在 ClientPrinter 配置中修改,并同步传入 new Wacp({ wsUrl: 'ws://127.0.0.1:新端口/' })。
Q2:保存模板提示"模板缺少 print 节"
• 设计器右侧「打印参数」面板必须填写 widthMm / heightMm / dpi(均为正数);
• dpi 推荐:热敏标签 203 / 热敏小票 203 / 普通激光打印机 300。
Q3:打印出纸为空白或字段未替换
• 检查 rows[i] 的键名是否与设计器中绑定的 dataSourceFields 一致(区分大小写);
• 若设计器使用 {{key}} 占位符,请确保 key 已加入 dataSourceFields 并在 rows 中提供值。
Q4:打印物理尺寸与设计器预览不一致
• print.dpi 必须与目标打印机实际 DPI 匹配,否则会被等比缩放;
• 横向打印机请将 print.rotate90 设为 true,仅做长宽交换,无需重新设计内容。
Q5:Token 报错"服务类型不匹配"或"Token 校验失败"
• 换取 Token 时 serviceType 必须为 Poster;
• OCR / ASR / TTS 的 Token 不可用于打印模板接入,需要单独换取;
• Token 仅在调用 wacp.openDesigner 时使用(会同步校验有效性),sendTemplate 和 print 无需 Token。
Q6:sendTemplate 或 print 报"templateId 不能为空"
• templateId 由三方自行生成和管理,不再由 SDK 自动生成;
• 建议使用有意义的命名(如业务主键或模板名称),确保同一模板在 ClientPrinter 中唯一;
• 示例:EMP_CARD_2026、工号牌_2026版。