feat:卡片打印机

This commit is contained in:
dzc committed 2026-09-23 16:49:42 +08:00
1 parent 726210edcb
commit eaec48a95b
1 file changed
+46 -4
+46 -4
View File
@@ -114,23 +114,65 @@ class CupsPrintBackend implements PrintBackend {
try {
final back = request.back;
if (back != null && back.color.isNotEmpty) {
backFile = await _writeTempPng(back.color);
backFile = await _writeTempPng(request.back!.color);
}
final copies = request.copies < 1 ? 1 : request.copies;
for (var i = 0; i < copies; i++) {
await runCommand('lp', ['-d', queue.name, frontFile]);
await _submitAndTrack(queue.name, frontFile);
if (backFile != null) {
await runCommand('lp', ['-d', queue.name, backFile]);
await _submitAndTrack(queue.name, backFile);
}
}
_log('已提交到 CUPS 队列 ${queue.name}(份数 $copies${backFile != null ? ',含背面' : ''})');
_log('打印完成:CUPS 队列 ${queue.name}(份数 $copies${backFile != null ? ',含背面' : ''})');
} finally {
await _deleteQuiet(frontFile);
await _deleteQuiet(backFile);
}
}
/// CUPS 作业跟踪超时(秒):单卡物理打印约 20-30s,留足余量;
/// 超时视为卡纸/缺卡/暂停等异常,任务转失败而非假装完成。
static const _jobTrackTimeoutSec = 170;
/// lp 提交并跟踪到**物理打印完成**(作业离开 CUPS 活动队列)才返回。
///
/// lp 返回只代表"已入队"(约 0.1s),若不跟踪,任务瞬间变"完成",
/// 前端批量进度会整体提前跑满(第一张还没打页面就 100%)。
/// 解析 lp 输出的作业号,轮询 `lpstat -o`(仅列未完成作业)直到作业号消失。
Future<void> _submitAndTrack(String queueName, String file) async {
final out = await runCommand('lp', ['-d', queueName, file]);
// lp 输出被系统本地化(中文"请求id是X(1个文件)",LC_ALL=C 压不住,同 lpstat 的坑),
// 作业号取到空白或中英文括号为止,避免把"(1个文件)"尾巴带进作业号
final m = RegExp(r'request id is\s+([^\s((]+)|请求id是\s*([^\s((]+)').firstMatch(out);
final jobId = (m?.group(1) ?? m?.group(2) ?? '').trim();
if (jobId.isEmpty) {
// 输出格式异常时退化为旧行为(提交即完成),不影响出卡
_log('lp 已提交(未解析到作业号,跳过完成跟踪):${out.trim()}');
return;
}
_log('CUPS 作业 $jobId 已入队,等待物理打印完成…');
final deadline = DateTime.now().add(const Duration(seconds: _jobTrackTimeoutSec));
while (DateTime.now().isBefore(deadline)) {
await Future<void>.delayed(const Duration(seconds: 1));
String active;
try {
active = await runCommand('lpstat', ['-o'], tolerateExit: true);
} on CommandException {
return; // lpstat 不可用,放弃跟踪(作业仍会打印)
}
if (!active.contains(jobId)) {
_log('CUPS 作业 $jobId 打印完成');
return;
}
}
throw StateError('打印超时:作业 $jobId 长时间未完成,请检查打印机(卡纸/缺卡/被暂停)');
}
/// data URL PNG 落临时文件供 lp 提交。
///
/// 2026-09-23 方向验证:打印机链路做上下翻转,此处曾加 flipVertical 抵消并打出
/// 正向成品(白卡实证);当前按用户要求**临时去掉翻转做消融测试**——预测:上下颠倒。
/// 验证后若需恢复,把 out 改回 img.encodePng(img.flipVertical(decoded))。
Future<String> _writeTempPng(String dataUrl) async {
final comma = dataUrl.indexOf(',');
final base64Part = comma >= 0 ? dataUrl.substring(comma + 1) : dataUrl;