- lib/print/pavo/:PavoApiW_x64.dll 的 dart:ffi 绑定(结构体布局对齐 C#,W 后缀导出名回退兼容) - pavo_printer.dart:枚举(UTF-8/UTF-16 自动解码)/状态/色带信息/单卡直打/忙碌轮询(3 分钟超时)/LayerHolder 非托管内存管理 - pavo_backend.dart:全部 ffi 调用与像素转换跑在 Isolate.run,主 isolate HTTP 服务零阻塞 - lib/image/card_image.dart:PNG data URL → 纵横比选卡面 → 手动 alpha 铺白底 → BGR24/Gray8 紧凑像素(image 包 compositeImage 对全透明像素非 src-over 语义,改为逐像素混合对齐 C#) - windows/ 目录:从 Flutter SDK windows.tmpl 手动实例化(mac 上 flutter create 拒绝生成的绕过),窗口标题"饭卡打印助手" - windows/vendor/:收录 SDK 的 PavoApiW_x64.dll/PavoCardSDK_x64.dll/HeatTB3.dll/heatthcd.bin,CMake POST_BUILD 自动拷到 exe 旁 - backend.dart 工厂接通 Windows 分支;analyze 零问题、单测 12 个全过
99 lines
3.9 KiB
Dart
99 lines
3.9 KiB
Dart
import 'dart:convert';
|
||
import 'dart:typed_data';
|
||
|
||
import 'package:desktop_plugin/image/card_image.dart';
|
||
import 'package:desktop_plugin/print/pavo/pavo_ffi.dart';
|
||
import 'package:desktop_plugin/print/pavo/pavo_printer.dart';
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:image/image.dart';
|
||
|
||
String dataUrl(Image img) => 'data:image/png;base64,${base64.encode(encodePng(img))}';
|
||
|
||
void main() {
|
||
group('CardImage 像素层转换', () {
|
||
test('横向图缩放到 1014×642,纯红转 BGR 字节序正确', () {
|
||
final img = Image(width: 100, height: 50);
|
||
fill(img, color: ColorRgb8(255, 0, 0));
|
||
final card = CardImage.fromDataUrl(dataUrl(img));
|
||
expect(card.isLandscape, isTrue);
|
||
expect(card.width, 1014);
|
||
expect(card.height, 642);
|
||
final bgr = card.toBgr24();
|
||
expect(bgr.length, 1014 * 642 * 3);
|
||
// 红色像素 → BGR 序 [B=0, G=0, R=255]
|
||
expect(bgr[0], 0);
|
||
expect(bgr[1], 0);
|
||
expect(bgr[2], 255);
|
||
// 中间像素一致(无行对齐错位/跨行污染)
|
||
final mid = (642 ~/ 2) * 1014 * 3 + (1014 ~/ 2) * 3;
|
||
expect(bgr[mid], 0);
|
||
expect(bgr[mid + 1], 0);
|
||
expect(bgr[mid + 2], 255);
|
||
});
|
||
|
||
test('纵向图缩放到 642×1014,纯蓝转 BGR 正确', () {
|
||
final img = Image(width: 50, height: 100);
|
||
fill(img, color: ColorRgb8(0, 0, 255));
|
||
final card = CardImage.fromDataUrl(dataUrl(img));
|
||
expect(card.isLandscape, isFalse);
|
||
expect(card.width, 642);
|
||
expect(card.height, 1014);
|
||
final bgr = card.toBgr24();
|
||
// 蓝色像素 → BGR 序 [B=255, G=0, R=0]
|
||
expect(bgr[0], 255);
|
||
expect(bgr[1], 0);
|
||
expect(bgr[2], 0);
|
||
});
|
||
|
||
test('透明像素铺白,避免黑底', () {
|
||
// 注意:image v4 的 Image() 默认是 3 通道不透明黑,透明图必须显式 numChannels: 4
|
||
final img = Image(width: 100, height: 50, numChannels: 4); // RGBA(0,0,0,0) 全透明
|
||
final card = CardImage.fromDataUrl(dataUrl(img));
|
||
final bgr = card.toBgr24();
|
||
expect(bgr[0], 255);
|
||
expect(bgr[1], 255);
|
||
expect(bgr[2], 255);
|
||
});
|
||
|
||
test('灰度转换:红 ≈ 76、白 = 255(K 层白底黑字语义)', () {
|
||
final red = Image(width: 100, height: 50);
|
||
fill(red, color: ColorRgb8(255, 0, 0));
|
||
final card = CardImage.fromDataUrl(dataUrl(red));
|
||
final gray = card.toGray8();
|
||
expect(gray.length, 1014 * 642);
|
||
expect(gray[0], 255 * 299 ~/ 1000); // = 76
|
||
expect(gray.every((g) => g == 76), isTrue);
|
||
|
||
final white = Image(width: 100, height: 50);
|
||
fill(white, color: ColorRgb8(255, 255, 255));
|
||
expect(CardImage.fromDataUrl(dataUrl(white)).toGray8()[0], 255);
|
||
});
|
||
|
||
test('空 data URL 报错', () {
|
||
expect(() => CardImage.fromDataUrl(''), throwsArgumentError);
|
||
});
|
||
});
|
||
|
||
group('PavoPrinter 纯 Dart 工具', () {
|
||
test('decodeAuto 识别 UTF-8 / UTF-16 打印机名', () {
|
||
// 纯 ASCII:UTF-8 解码即可正确还原(NUL 计入可打印率,两种编码都可能命中 UTF-8 路径)
|
||
final ascii = Uint8List.fromList('HiTi CS-200e'.codeUnits);
|
||
expect(PavoPrinter.decodeAuto(ascii).startsWith('HiTi CS-200e'), isTrue);
|
||
// 纯中文:UTF-8 解码得乱码(可打印率低),走 UTF-16 路径正确还原
|
||
// (ASCII 混中文时 NUL 也计入可打印率,可能仍走 UTF-8 路径——与 C# 行为一致)
|
||
final name = '证卡打印机';
|
||
final bytes = <int>[];
|
||
for (final c in name.codeUnits) {
|
||
bytes..add(c & 0xff)..add(c >> 8);
|
||
}
|
||
expect(PavoPrinter.decodeAuto(Uint8List.fromList(bytes)), name);
|
||
});
|
||
|
||
test('statusTextOf 状态文案', () {
|
||
expect(PavoPrinter.statusTextOf(0), '就绪');
|
||
expect(PavoPrinter.statusTextOf(PavoApi.dsOutOfRibbon), '色带已用尽,请更换色带');
|
||
expect(PavoPrinter.statusTextOf(0x12345678), contains('设备状态异常'));
|
||
});
|
||
});
|
||
}
|