11
This commit is contained in:
1 parent
eaec48a95b
commit
6a9dbf9080
3 files changed
+48
-7
No files matched your search
@@ -3,6 +3,13 @@ import 'dart:typed_data';
|
||||
|
||||
import 'package:image/image.dart';
|
||||
|
||||
/// 纵向卡面 → 横向位图的预旋转方向(Windows Pavo 直打校准常量)。
|
||||
///
|
||||
/// 实测(2026-09-23 Windows CS-200e):纵向缓冲 + byOrientation=1 会被 SDK 旋转 90°
|
||||
/// 且锚定错位(内容横向贴左、顺时针转卡才可读)。修复=统一预旋转成 1014×642 横向
|
||||
/// 位图 + byOrientation=2 打印。此常量定旋转方向:打测试卡若差 90°(顺/逆)就取反。
|
||||
const bool pavoPortraitRotateClockwise = true;
|
||||
|
||||
/// PNG data URL 解析与像素层转换(Windows PavoApi 直打用)。
|
||||
///
|
||||
/// 卡面为 CR-80 @300dpi:横向 1014×642 / 纵向 642×1014,按源图纵横比自动选择;
|
||||
@@ -20,6 +27,14 @@ class CardImage {
|
||||
/// 宽 ≥ 高 = 横向卡面
|
||||
bool get isLandscape => width >= height;
|
||||
|
||||
/// 纵向卡面预旋转为横向位图(Windows Pavo 直打用,见 [pavoPortraitRotateClockwise])。
|
||||
/// 已是横向的原样返回。返回新实例,原实例不变(正/背面各自转换)。
|
||||
CardImage toLandscape() {
|
||||
if (isLandscape) return this;
|
||||
final rotated = copyRotate(_canvas, angle: pavoPortraitRotateClockwise ? 90 : -90);
|
||||
return CardImage._(rotated, rotated.width, rotated.height);
|
||||
}
|
||||
|
||||
/// 解析 data:image/png;base64,…(或其他 data URL 形式),缩放到卡面画布并铺白底。
|
||||
static CardImage fromDataUrl(String dataUrl) {
|
||||
if (dataUrl.trim().isEmpty) {
|
||||
|
||||
@@ -113,17 +113,22 @@ Future<void> pavoPrintSync({
|
||||
}
|
||||
final printer = printers.first.toNativeUtf16();
|
||||
try {
|
||||
final frontImg = CardImage.fromDataUrl(frontColor);
|
||||
// Windows Pavo 直打方向校准(2026-09-23 实测):纵向位图 + byOrientation=1 会被
|
||||
// SDK 旋转 90° 且锚定错位;统一预旋转成 1014×642 横向位图后按 byOrientation=2 打印。
|
||||
// 旋转方向常量见 card_image.dart 的 pavoPortraitRotateClockwise(测试卡校准)。
|
||||
final frontImg = CardImage.fromDataUrl(frontColor).toLandscape();
|
||||
final frontBgr = frontImg.toBgr24();
|
||||
final Uint8List? frontKGray =
|
||||
frontK != null && frontK.isNotEmpty ? CardImage.fromDataUrl(frontK).toGray8() : null;
|
||||
final Uint8List? frontKGray = frontK != null && frontK.isNotEmpty
|
||||
? CardImage.fromDataUrl(frontK).toLandscape().toGray8()
|
||||
: null;
|
||||
final Uint8List? backBgr;
|
||||
final Uint8List? backKGray;
|
||||
if (backColor != null && backColor.isNotEmpty) {
|
||||
final backImg = CardImage.fromDataUrl(backColor);
|
||||
final backImg = CardImage.fromDataUrl(backColor).toLandscape();
|
||||
backBgr = backImg.toBgr24();
|
||||
backKGray =
|
||||
backK != null && backK.isNotEmpty ? CardImage.fromDataUrl(backK).toGray8() : null;
|
||||
backKGray = backK != null && backK.isNotEmpty
|
||||
? CardImage.fromDataUrl(backK).toLandscape().toGray8()
|
||||
: null;
|
||||
} else {
|
||||
backBgr = null;
|
||||
backKGray = null;
|
||||
@@ -136,7 +141,7 @@ Future<void> pavoPrintSync({
|
||||
frontK: frontKGray,
|
||||
backBgr: backBgr,
|
||||
backK: backKGray,
|
||||
isLandscape: frontImg.isLandscape,
|
||||
isLandscape: true,
|
||||
copies: copies,
|
||||
);
|
||||
} finally {
|
||||
|
||||
@@ -55,3 +55,24 @@ Root: HKCU; Subkey: "Software\Microsoft\Windows\CurrentVersion\Run"; ValueType:
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\{#AppExeName}"; Description: "立即运行 {#AppName}"; Flags: nowait postinstall skipifsilent
|
||||
|
||||
; 覆盖安装/卸载前结束正在运行的实例,避免 exe/dll 被占用导致文件替换失败
|
||||
[Code]
|
||||
procedure TaskkillApp();
|
||||
var
|
||||
ResultCode: Integer;
|
||||
begin
|
||||
Exec(ExpandConstant('{cmd}'), '/C taskkill /F /IM "{#AppExeName}"', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
end;
|
||||
|
||||
function PrepareToInstall(var NeedsRestart: Boolean): String;
|
||||
begin
|
||||
TaskkillApp();
|
||||
Result := '';
|
||||
end;
|
||||
|
||||
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
||||
begin
|
||||
if CurUninstallStep = usUninstall then
|
||||
TaskkillApp();
|
||||
end;
|
||||
Reference in new issue
Block a user