@delicity/capacitor-thermal-printer 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DelicityThermalPrinter.podspec +28 -0
- package/LICENSE +21 -0
- package/README.md +649 -0
- package/android/build.gradle +122 -0
- package/android/src/main/AndroidManifest.xml +38 -0
- package/android/src/main/java/com/delicity/thermalprinter/Logger.kt +50 -0
- package/android/src/main/java/com/delicity/thermalprinter/ThermalPrinterEngine.kt +528 -0
- package/android/src/main/java/com/delicity/thermalprinter/ThermalPrinterPlugin.kt +334 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/BleAdapter.kt +125 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/BrotherAdapter.kt +206 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/EpsonAdapter.kt +384 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/EscPosAdapter.kt +160 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/EscPosCommands.kt +42 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/EscPosTextEncoder.kt +138 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/PrinterAdapter.kt +95 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/RawTcpAdapter.kt +96 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/SdkContract.kt +158 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/SdkReflect.kt +104 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/StarAdapter.kt +322 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/UsbAdapter.kt +248 -0
- package/android/src/main/java/com/delicity/thermalprinter/adapters/ZebraAdapter.kt +207 -0
- package/android/src/main/java/com/delicity/thermalprinter/discovery/AdapterPriority.kt +39 -0
- package/android/src/main/java/com/delicity/thermalprinter/discovery/BleScanner.kt +70 -0
- package/android/src/main/java/com/delicity/thermalprinter/discovery/BluetoothClassicScanner.kt +112 -0
- package/android/src/main/java/com/delicity/thermalprinter/discovery/DiscoveryManager.kt +136 -0
- package/android/src/main/java/com/delicity/thermalprinter/discovery/TcpScanner.kt +96 -0
- package/android/src/main/java/com/delicity/thermalprinter/image/ImageCache.kt +88 -0
- package/android/src/main/java/com/delicity/thermalprinter/image/ImageProcessor.kt +220 -0
- package/android/src/main/java/com/delicity/thermalprinter/image/TextRasterizer.kt +99 -0
- package/android/src/main/java/com/delicity/thermalprinter/model/Models.kt +206 -0
- package/android/src/main/java/com/delicity/thermalprinter/model/PrintItem.kt +100 -0
- package/android/src/main/java/com/delicity/thermalprinter/store/PrinterStore.kt +71 -0
- package/android/src/main/java/com/delicity/thermalprinter/transport/BleGattClient.kt +201 -0
- package/android/src/main/java/com/delicity/thermalprinter/transport/BluetoothSppTransport.kt +110 -0
- package/android/src/main/java/com/delicity/thermalprinter/transport/ByteTransport.kt +18 -0
- package/android/src/main/java/com/delicity/thermalprinter/transport/TcpTransport.kt +83 -0
- package/dist/esm/adapters/dedup.d.ts +26 -0
- package/dist/esm/adapters/dedup.js +66 -0
- package/dist/esm/adapters/dedup.js.map +1 -0
- package/dist/esm/adapters/priority.d.ts +29 -0
- package/dist/esm/adapters/priority.js +55 -0
- package/dist/esm/adapters/priority.js.map +1 -0
- package/dist/esm/core/enums.d.ts +61 -0
- package/dist/esm/core/enums.js +25 -0
- package/dist/esm/core/enums.js.map +1 -0
- package/dist/esm/core/errors.d.ts +16 -0
- package/dist/esm/core/errors.js +53 -0
- package/dist/esm/core/errors.js.map +1 -0
- package/dist/esm/core/escpos-text.d.ts +33 -0
- package/dist/esm/core/escpos-text.js +239 -0
- package/dist/esm/core/escpos-text.js.map +1 -0
- package/dist/esm/core/imaging.d.ts +91 -0
- package/dist/esm/core/imaging.js +184 -0
- package/dist/esm/core/imaging.js.map +1 -0
- package/dist/esm/core/models.d.ts +131 -0
- package/dist/esm/core/models.js +2 -0
- package/dist/esm/core/models.js.map +1 -0
- package/dist/esm/core/options.d.ts +154 -0
- package/dist/esm/core/options.js +2 -0
- package/dist/esm/core/options.js.map +1 -0
- package/dist/esm/core/text.d.ts +138 -0
- package/dist/esm/core/text.js +14 -0
- package/dist/esm/core/text.js.map +1 -0
- package/dist/esm/definitions.d.ts +155 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +15 -0
- package/dist/esm/index.js +18 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +63 -0
- package/dist/esm/web.js +112 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +224 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +227 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Plugin/Adapters/BrotherAdapter.swift +139 -0
- package/ios/Plugin/Adapters/EpsonAdapter.swift +131 -0
- package/ios/Plugin/Adapters/EscPosAdapter.swift +106 -0
- package/ios/Plugin/Adapters/EscPosCommands.swift +32 -0
- package/ios/Plugin/Adapters/EscPosTextEncoder.swift +115 -0
- package/ios/Plugin/Adapters/PrinterAdapter.swift +44 -0
- package/ios/Plugin/Adapters/RawTcpAdapter.swift +70 -0
- package/ios/Plugin/Adapters/StarAdapter.swift +305 -0
- package/ios/Plugin/Adapters/ZebraAdapter.swift +119 -0
- package/ios/Plugin/Discovery/AdapterPriority.swift +21 -0
- package/ios/Plugin/Discovery/BonjourScanner.swift +51 -0
- package/ios/Plugin/Discovery/DiscoveryManager.swift +86 -0
- package/ios/Plugin/Image/ImageCache.swift +73 -0
- package/ios/Plugin/Image/ImageProcessor.swift +168 -0
- package/ios/Plugin/Image/TextRasterizer.swift +81 -0
- package/ios/Plugin/Logger.swift +33 -0
- package/ios/Plugin/Model/Models.swift +174 -0
- package/ios/Plugin/Model/PrintItem.swift +111 -0
- package/ios/Plugin/Store/PrinterStore.swift +51 -0
- package/ios/Plugin/ThermalPrinterEngine.swift +395 -0
- package/ios/Plugin/ThermalPrinterPlugin.m +22 -0
- package/ios/Plugin/ThermalPrinterPlugin.swift +258 -0
- package/ios/Plugin/Transport/TcpTransport.swift +89 -0
- package/package.json +96 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedup.js","sourceRoot":"","sources":["../../../src/adapters/dedup.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,aAAa,CAAC,SAA2B,EAAE,UAAkB;IAC3E,MAAM,IAAI,GAAG,gBAAgB,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACrD,OAAO,GAAG,SAAS,IAAI,IAAI,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,gBAAgB,CAAC,SAA2B,EAAE,OAAe;IACpE,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,MAAM,CAAC;QACZ,KAAK,UAAU;YACb,2BAA2B;YAC3B,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3D,KAAK,WAAW,CAAC;QACjB,KAAK,KAAK;YACR,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACtC,KAAK,KAAK;YACR,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACtC;YACE,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACxC,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAA6B,EAC7B,WAA6C;;IAE7C,MAAM,IAAI,GAAG,IAAI,GAAG,EAA6B,CAAC;IAElD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,kCAAO,OAAO,KAAE,YAAY,EAAE,aAAa,CAAC,OAAO,CAAC,IAAG,CAAC;YAC3E,SAAS;QACX,CAAC;QAED,uCAAuC;QACvC,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAC9B,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,MAAA,QAAQ,CAAC,YAAY,mCAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAA,OAAO,CAAC,YAAY,mCAAI,EAAE,CAAC,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAChH,CAAC;QAEF,uCAAuC;QACvC,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;QACjF,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,kCACd,MAAM;YACT,uDAAuD;YACvD,YAAY,gDAAO,QAAQ,CAAC,YAAY,GAAK,OAAO,CAAC,YAAY,GAAK,MAAM,CAAC,YAAY,GACzF,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,EAC7D,YAAY,EAAE,aAAa,EAC3B,SAAS,EAAE,QAAQ,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,EAClD,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,IACxD,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,aAAa,CAAC,CAAoB;;IACzC,MAAM,IAAI,GAAG,MAAA,CAAC,CAAC,YAAY,mCAAI,EAAE,CAAC;IAClC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC","sourcesContent":["import type { PrinterTransport } from '../core/enums';\nimport type { DiscoveredPrinter } from '../core/models';\n\n/**\n * Construit un identifiant interne stable pour une imprimante.\n *\n * On veut le MÊME id quelle que soit la source de découverte afin de fusionner\n * les doublons (ex: une TM-m30 vue à la fois par le SDK Epson et par le scan TCP).\n *\n * Clé de stabilité par transport :\n * - réseau (wifi/ethernet) : adresse IP normalisée (sans port) — l'imprimante a\n * une seule IP même si plusieurs ports répondent.\n * - bluetooth/ble : adresse MAC (Android) ou UUID périphérique (iOS).\n * - usb : vendorId:productId.\n *\n * Le préfixe transport évite les collisions improbables entre familles.\n */\nexport function buildStableId(transport: PrinterTransport, rawAddress: string): string {\n const norm = normalizeAddress(transport, rawAddress);\n return `${transport}:${norm}`;\n}\n\nfunction normalizeAddress(transport: PrinterTransport, address: string): string {\n switch (transport) {\n case 'wifi':\n case 'ethernet':\n // retire un éventuel :port\n return address.replace(/:\\d+$/, '').trim().toLowerCase();\n case 'bluetooth':\n case 'ble':\n return address.trim().toUpperCase();\n case 'usb':\n return address.trim().toLowerCase();\n default:\n return address.trim().toLowerCase();\n }\n}\n\n/**\n * Fusionne une liste de découvertes brutes en une liste dédoublonnée.\n * En cas de doublon (même id), on conserve l'entrée au meilleur adapter\n * (déjà résolu par `resolveBestAdapter`) et on fusionne `discoveredBy`.\n *\n * @param incoming découvertes brutes (chaque source peut produire des doublons)\n * @param adapterRank fonction de classement (score) déjà appliquée au champ adapter\n */\nexport function mergeDiscoveries(\n incoming: DiscoveredPrinter[],\n adapterRank: (p: DiscoveredPrinter) => number,\n): DiscoveredPrinter[] {\n const byId = new Map<string, DiscoveredPrinter>();\n\n for (const printer of incoming) {\n const existing = byId.get(printer.id);\n if (!existing) {\n byId.set(printer.id, { ...printer, discoveredBy: dedupeSources(printer) });\n continue;\n }\n\n // Fusionner les sources de découverte.\n const mergedSources = Array.from(\n new Set([...(existing.discoveredBy ?? []), ...(printer.discoveredBy ?? []), printer.adapter, existing.adapter]),\n );\n\n // Garder l'entrée au meilleur adapter.\n const winner = adapterRank(printer) > adapterRank(existing) ? printer : existing;\n byId.set(printer.id, {\n ...winner,\n // capacités: union (on garde le plus d'infos possible)\n capabilities: { ...existing.capabilities, ...printer.capabilities, ...winner.capabilities },\n lastSeenAt: Math.max(existing.lastSeenAt, printer.lastSeenAt),\n discoveredBy: mergedSources,\n isDefault: existing.isDefault || printer.isDefault,\n isConnected: existing.isConnected || printer.isConnected,\n });\n }\n\n return Array.from(byId.values());\n}\n\nfunction dedupeSources(p: DiscoveredPrinter): typeof p.discoveredBy {\n const base = p.discoveredBy ?? [];\n return Array.from(new Set([...base, p.adapter]));\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PrinterAdapterId, PrinterTransport } from '../core/enums';
|
|
2
|
+
/**
|
|
3
|
+
* Indice de découverte brut avant résolution d'adapter.
|
|
4
|
+
* Chaque source de découverte produit un ou plusieurs `AdapterCandidate`.
|
|
5
|
+
*/
|
|
6
|
+
export interface AdapterCandidate {
|
|
7
|
+
adapter: PrinterAdapterId;
|
|
8
|
+
transport: PrinterTransport;
|
|
9
|
+
/** Marque détectée par la source (sert à arbitrer). */
|
|
10
|
+
brand?: string;
|
|
11
|
+
/** La source est un SDK officiel du fabricant (Epson/Star/Brother/Zebra). */
|
|
12
|
+
fromVendorSdk: boolean;
|
|
13
|
+
/** L'imprimante a répondu à un handshake ESC/POS (statut DLE EOT, etc.). */
|
|
14
|
+
escposVerified?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Score de priorité d'adapter. Plus le score est élevé, plus l'adapter est préféré.
|
|
18
|
+
*
|
|
19
|
+
* Règles produit demandées :
|
|
20
|
+
* 1. Un SDK officiel reconnaissant l'imprimante prime sur tout.
|
|
21
|
+
* 2. Zebra => ZebraAdapter, JAMAIS escpos par défaut (langage ZPL/CPCL).
|
|
22
|
+
* 3. Wi-Fi/TCP + ESC/POS confirmé => escpos ; sinon rawTcp en dernier recours.
|
|
23
|
+
* 4. Bluetooth classique Android + ESC/POS => escpos.
|
|
24
|
+
*/
|
|
25
|
+
export declare function scoreCandidate(c: AdapterCandidate): number;
|
|
26
|
+
/**
|
|
27
|
+
* Résout le meilleur adapter parmi plusieurs candidats pour une même imprimante.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveBestAdapter(candidates: AdapterCandidate[]): AdapterCandidate;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Score de priorité d'adapter. Plus le score est élevé, plus l'adapter est préféré.
|
|
3
|
+
*
|
|
4
|
+
* Règles produit demandées :
|
|
5
|
+
* 1. Un SDK officiel reconnaissant l'imprimante prime sur tout.
|
|
6
|
+
* 2. Zebra => ZebraAdapter, JAMAIS escpos par défaut (langage ZPL/CPCL).
|
|
7
|
+
* 3. Wi-Fi/TCP + ESC/POS confirmé => escpos ; sinon rawTcp en dernier recours.
|
|
8
|
+
* 4. Bluetooth classique Android + ESC/POS => escpos.
|
|
9
|
+
*/
|
|
10
|
+
export function scoreCandidate(c) {
|
|
11
|
+
var _a;
|
|
12
|
+
// Zebra est un cas spécial : son langage n'est pas ESC/POS.
|
|
13
|
+
const isZebra = ((_a = c.brand) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes('zebra')) || c.adapter === 'zebra';
|
|
14
|
+
if (isZebra) {
|
|
15
|
+
return c.adapter === 'zebra' ? 1000 : -1000; // bannir escpos/rawTcp pour Zebra
|
|
16
|
+
}
|
|
17
|
+
// SDK officiel = priorité maximale (statut, coupe, reconnexion gérés nativement).
|
|
18
|
+
if (c.fromVendorSdk) {
|
|
19
|
+
switch (c.adapter) {
|
|
20
|
+
case 'epson':
|
|
21
|
+
return 900;
|
|
22
|
+
case 'star':
|
|
23
|
+
return 890;
|
|
24
|
+
case 'brother':
|
|
25
|
+
return 880;
|
|
26
|
+
default:
|
|
27
|
+
return 850;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// ESC/POS générique confirmé.
|
|
31
|
+
if (c.adapter === 'escpos') {
|
|
32
|
+
// BT classic vérifié et TCP vérifié sont très fiables.
|
|
33
|
+
if (c.escposVerified)
|
|
34
|
+
return 700;
|
|
35
|
+
// ESC/POS non vérifié mais transport exploitable.
|
|
36
|
+
return c.transport === 'bluetooth' ? 620 : 600;
|
|
37
|
+
}
|
|
38
|
+
// BLE générique : exploitable seulement si service GATT connu.
|
|
39
|
+
if (c.transport === 'ble')
|
|
40
|
+
return 500;
|
|
41
|
+
// rawTcp : filet de sécurité réseau, on tente l'ESC/POS brut.
|
|
42
|
+
if (c.adapter === 'rawTcp')
|
|
43
|
+
return 300;
|
|
44
|
+
return 100;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Résout le meilleur adapter parmi plusieurs candidats pour une même imprimante.
|
|
48
|
+
*/
|
|
49
|
+
export function resolveBestAdapter(candidates) {
|
|
50
|
+
if (candidates.length === 0) {
|
|
51
|
+
throw new Error('resolveBestAdapter: aucun candidat');
|
|
52
|
+
}
|
|
53
|
+
return [...candidates].sort((a, b) => scoreCandidate(b) - scoreCandidate(a))[0];
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=priority.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"priority.js","sourceRoot":"","sources":["../../../src/adapters/priority.ts"],"names":[],"mappings":"AAiBA;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,CAAmB;;IAChD,4DAA4D;IAC5D,MAAM,OAAO,GAAG,CAAA,MAAA,CAAC,CAAC,KAAK,0CAAE,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAI,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC;IAClF,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,kCAAkC;IACjF,CAAC;IAED,kFAAkF;IAClF,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;QACpB,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;YAClB,KAAK,OAAO;gBACV,OAAO,GAAG,CAAC;YACb,KAAK,MAAM;gBACT,OAAO,GAAG,CAAC;YACb,KAAK,SAAS;gBACZ,OAAO,GAAG,CAAC;YACb;gBACE,OAAO,GAAG,CAAC;QACf,CAAC;IACH,CAAC;IAED,8BAA8B;IAC9B,IAAI,CAAC,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC3B,uDAAuD;QACvD,IAAI,CAAC,CAAC,cAAc;YAAE,OAAO,GAAG,CAAC;QACjC,kDAAkD;QAClD,OAAO,CAAC,CAAC,SAAS,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACjD,CAAC;IAED,+DAA+D;IAC/D,IAAI,CAAC,CAAC,SAAS,KAAK,KAAK;QAAE,OAAO,GAAG,CAAC;IAEtC,8DAA8D;IAC9D,IAAI,CAAC,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IAEvC,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAA8B;IAC/D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClF,CAAC","sourcesContent":["import type { PrinterAdapterId, PrinterTransport } from '../core/enums';\n\n/**\n * Indice de découverte brut avant résolution d'adapter.\n * Chaque source de découverte produit un ou plusieurs `AdapterCandidate`.\n */\nexport interface AdapterCandidate {\n adapter: PrinterAdapterId;\n transport: PrinterTransport;\n /** Marque détectée par la source (sert à arbitrer). */\n brand?: string;\n /** La source est un SDK officiel du fabricant (Epson/Star/Brother/Zebra). */\n fromVendorSdk: boolean;\n /** L'imprimante a répondu à un handshake ESC/POS (statut DLE EOT, etc.). */\n escposVerified?: boolean;\n}\n\n/**\n * Score de priorité d'adapter. Plus le score est élevé, plus l'adapter est préféré.\n *\n * Règles produit demandées :\n * 1. Un SDK officiel reconnaissant l'imprimante prime sur tout.\n * 2. Zebra => ZebraAdapter, JAMAIS escpos par défaut (langage ZPL/CPCL).\n * 3. Wi-Fi/TCP + ESC/POS confirmé => escpos ; sinon rawTcp en dernier recours.\n * 4. Bluetooth classique Android + ESC/POS => escpos.\n */\nexport function scoreCandidate(c: AdapterCandidate): number {\n // Zebra est un cas spécial : son langage n'est pas ESC/POS.\n const isZebra = c.brand?.toLowerCase().includes('zebra') || c.adapter === 'zebra';\n if (isZebra) {\n return c.adapter === 'zebra' ? 1000 : -1000; // bannir escpos/rawTcp pour Zebra\n }\n\n // SDK officiel = priorité maximale (statut, coupe, reconnexion gérés nativement).\n if (c.fromVendorSdk) {\n switch (c.adapter) {\n case 'epson':\n return 900;\n case 'star':\n return 890;\n case 'brother':\n return 880;\n default:\n return 850;\n }\n }\n\n // ESC/POS générique confirmé.\n if (c.adapter === 'escpos') {\n // BT classic vérifié et TCP vérifié sont très fiables.\n if (c.escposVerified) return 700;\n // ESC/POS non vérifié mais transport exploitable.\n return c.transport === 'bluetooth' ? 620 : 600;\n }\n\n // BLE générique : exploitable seulement si service GATT connu.\n if (c.transport === 'ble') return 500;\n\n // rawTcp : filet de sécurité réseau, on tente l'ESC/POS brut.\n if (c.adapter === 'rawTcp') return 300;\n\n return 100;\n}\n\n/**\n * Résout le meilleur adapter parmi plusieurs candidats pour une même imprimante.\n */\nexport function resolveBestAdapter(candidates: AdapterCandidate[]): AdapterCandidate {\n if (candidates.length === 0) {\n throw new Error('resolveBestAdapter: aucun candidat');\n }\n return [...candidates].sort((a, b) => scoreCandidate(b) - scoreCandidate(a))[0];\n}\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transports physiques possibles vers une imprimante.
|
|
3
|
+
*
|
|
4
|
+
* - `wifi` : imprimante sur réseau Wi-Fi (TCP, généralement port 9100 / RAW).
|
|
5
|
+
* - `ethernet` : imprimante filaire réseau (même pile TCP que le Wi-Fi).
|
|
6
|
+
* - `bluetooth` : Bluetooth Classic / SPP (Android uniquement pour le générique ESC/POS).
|
|
7
|
+
* - `ble` : Bluetooth Low Energy (iOS + Android, uniquement si l'imprimante
|
|
8
|
+
* expose un service GATT exploitable).
|
|
9
|
+
* - `usb` : USB host (Android uniquement).
|
|
10
|
+
*/
|
|
11
|
+
export type PrinterTransport = 'wifi' | 'ethernet' | 'bluetooth' | 'ble' | 'usb';
|
|
12
|
+
/**
|
|
13
|
+
* Adapter logiciel utilisé pour parler à l'imprimante.
|
|
14
|
+
* Le choix de l'adapter est fait par le moteur de priorité (voir AdapterPriority).
|
|
15
|
+
*/
|
|
16
|
+
export type PrinterAdapterId = 'escpos' | 'epson' | 'star' | 'brother' | 'zebra' | 'rawTcp';
|
|
17
|
+
/**
|
|
18
|
+
* Codes d'erreur normalisés renvoyés par le plugin sur toutes les plateformes.
|
|
19
|
+
* Ils sont stables et destinés à être mappés dans l'UI / le support.
|
|
20
|
+
*/
|
|
21
|
+
export declare enum PrintErrorCode {
|
|
22
|
+
PRINTER_NOT_FOUND = "PRINTER_NOT_FOUND",
|
|
23
|
+
PRINTER_OFFLINE = "PRINTER_OFFLINE",
|
|
24
|
+
CONNECTION_FAILED = "CONNECTION_FAILED",
|
|
25
|
+
PERMISSION_DENIED = "PERMISSION_DENIED",
|
|
26
|
+
BLUETOOTH_DISABLED = "BLUETOOTH_DISABLED",
|
|
27
|
+
WIFI_NOT_CONNECTED = "WIFI_NOT_CONNECTED",
|
|
28
|
+
PAIRING_REQUIRED = "PAIRING_REQUIRED",
|
|
29
|
+
UNSUPPORTED_TRANSPORT = "UNSUPPORTED_TRANSPORT",
|
|
30
|
+
UNSUPPORTED_PRINTER = "UNSUPPORTED_PRINTER",
|
|
31
|
+
IMAGE_INVALID = "IMAGE_INVALID",
|
|
32
|
+
IMAGE_TOO_LARGE = "IMAGE_TOO_LARGE",
|
|
33
|
+
PRINT_FAILED = "PRINT_FAILED",
|
|
34
|
+
PAPER_EMPTY = "PAPER_EMPTY",
|
|
35
|
+
COVER_OPEN = "COVER_OPEN",
|
|
36
|
+
SDK_NOT_AVAILABLE = "SDK_NOT_AVAILABLE",
|
|
37
|
+
TIMEOUT = "TIMEOUT",
|
|
38
|
+
UNKNOWN = "UNKNOWN"
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* État de connexion logique d'une imprimante connue du plugin.
|
|
42
|
+
*/
|
|
43
|
+
export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'error';
|
|
44
|
+
/**
|
|
45
|
+
* Niveau de papier rapporté par le statut imprimante (si supporté).
|
|
46
|
+
*/
|
|
47
|
+
export type PaperStatus = 'ok' | 'near_end' | 'empty' | 'unknown';
|
|
48
|
+
/**
|
|
49
|
+
* État d'un job d'impression, suivi en temps réel via l'event `printJobStatus`.
|
|
50
|
+
*
|
|
51
|
+
* - `pending` : job accepté, en file (connexion/rendu en cours).
|
|
52
|
+
* - `printing` : octets/commandes en cours d'envoi ou d'impression physique.
|
|
53
|
+
* - `hold` : impression suspendue par l'imprimante (attente papier, capot
|
|
54
|
+
* ouvert, tampon plein). Le job reprend dès résolution.
|
|
55
|
+
* - `completed` : impression physique terminée (best-effort selon transport/SDK).
|
|
56
|
+
* - `failed` : échec définitif (voir errorCode).
|
|
57
|
+
* - `canceled` : annulé par l'app.
|
|
58
|
+
*/
|
|
59
|
+
export type JobState = 'pending' | 'printing' | 'hold' | 'completed' | 'failed' | 'canceled';
|
|
60
|
+
/** Raison d'un état `hold`. */
|
|
61
|
+
export type HoldReason = 'paper_empty' | 'paper_near_end' | 'cover_open' | 'buffer_full' | 'offline' | 'unknown';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codes d'erreur normalisés renvoyés par le plugin sur toutes les plateformes.
|
|
3
|
+
* Ils sont stables et destinés à être mappés dans l'UI / le support.
|
|
4
|
+
*/
|
|
5
|
+
export var PrintErrorCode;
|
|
6
|
+
(function (PrintErrorCode) {
|
|
7
|
+
PrintErrorCode["PRINTER_NOT_FOUND"] = "PRINTER_NOT_FOUND";
|
|
8
|
+
PrintErrorCode["PRINTER_OFFLINE"] = "PRINTER_OFFLINE";
|
|
9
|
+
PrintErrorCode["CONNECTION_FAILED"] = "CONNECTION_FAILED";
|
|
10
|
+
PrintErrorCode["PERMISSION_DENIED"] = "PERMISSION_DENIED";
|
|
11
|
+
PrintErrorCode["BLUETOOTH_DISABLED"] = "BLUETOOTH_DISABLED";
|
|
12
|
+
PrintErrorCode["WIFI_NOT_CONNECTED"] = "WIFI_NOT_CONNECTED";
|
|
13
|
+
PrintErrorCode["PAIRING_REQUIRED"] = "PAIRING_REQUIRED";
|
|
14
|
+
PrintErrorCode["UNSUPPORTED_TRANSPORT"] = "UNSUPPORTED_TRANSPORT";
|
|
15
|
+
PrintErrorCode["UNSUPPORTED_PRINTER"] = "UNSUPPORTED_PRINTER";
|
|
16
|
+
PrintErrorCode["IMAGE_INVALID"] = "IMAGE_INVALID";
|
|
17
|
+
PrintErrorCode["IMAGE_TOO_LARGE"] = "IMAGE_TOO_LARGE";
|
|
18
|
+
PrintErrorCode["PRINT_FAILED"] = "PRINT_FAILED";
|
|
19
|
+
PrintErrorCode["PAPER_EMPTY"] = "PAPER_EMPTY";
|
|
20
|
+
PrintErrorCode["COVER_OPEN"] = "COVER_OPEN";
|
|
21
|
+
PrintErrorCode["SDK_NOT_AVAILABLE"] = "SDK_NOT_AVAILABLE";
|
|
22
|
+
PrintErrorCode["TIMEOUT"] = "TIMEOUT";
|
|
23
|
+
PrintErrorCode["UNKNOWN"] = "UNKNOWN";
|
|
24
|
+
})(PrintErrorCode || (PrintErrorCode = {}));
|
|
25
|
+
//# sourceMappingURL=enums.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enums.js","sourceRoot":"","sources":["../../../src/core/enums.ts"],"names":[],"mappings":"AAwBA;;;GAGG;AACH,MAAM,CAAN,IAAY,cAkBX;AAlBD,WAAY,cAAc;IACxB,yDAAuC,CAAA;IACvC,qDAAmC,CAAA;IACnC,yDAAuC,CAAA;IACvC,yDAAuC,CAAA;IACvC,2DAAyC,CAAA;IACzC,2DAAyC,CAAA;IACzC,uDAAqC,CAAA;IACrC,iEAA+C,CAAA;IAC/C,6DAA2C,CAAA;IAC3C,iDAA+B,CAAA;IAC/B,qDAAmC,CAAA;IACnC,+CAA6B,CAAA;IAC7B,6CAA2B,CAAA;IAC3B,2CAAyB,CAAA;IACzB,yDAAuC,CAAA;IACvC,qCAAmB,CAAA;IACnB,qCAAmB,CAAA;AACrB,CAAC,EAlBW,cAAc,KAAd,cAAc,QAkBzB","sourcesContent":["/**\n * Transports physiques possibles vers une imprimante.\n *\n * - `wifi` : imprimante sur réseau Wi-Fi (TCP, généralement port 9100 / RAW).\n * - `ethernet` : imprimante filaire réseau (même pile TCP que le Wi-Fi).\n * - `bluetooth` : Bluetooth Classic / SPP (Android uniquement pour le générique ESC/POS).\n * - `ble` : Bluetooth Low Energy (iOS + Android, uniquement si l'imprimante\n * expose un service GATT exploitable).\n * - `usb` : USB host (Android uniquement).\n */\nexport type PrinterTransport = 'wifi' | 'ethernet' | 'bluetooth' | 'ble' | 'usb';\n\n/**\n * Adapter logiciel utilisé pour parler à l'imprimante.\n * Le choix de l'adapter est fait par le moteur de priorité (voir AdapterPriority).\n */\nexport type PrinterAdapterId =\n | 'escpos' // ESC/POS générique (raster GS v 0) — SPP, TCP, BLE\n | 'epson' // SDK Epson ePOS2 / ePOS-Print\n | 'star' // SDK StarXpand / StarIO10\n | 'brother' // SDK Brother Print SDK\n | 'zebra' // SDK Zebra Link-OS (ZPL / CPCL)\n | 'rawTcp'; // Flux TCP brut, fallback réseau quand l'imprimante n'est pas identifiée\n\n/**\n * Codes d'erreur normalisés renvoyés par le plugin sur toutes les plateformes.\n * Ils sont stables et destinés à être mappés dans l'UI / le support.\n */\nexport enum PrintErrorCode {\n PRINTER_NOT_FOUND = 'PRINTER_NOT_FOUND',\n PRINTER_OFFLINE = 'PRINTER_OFFLINE',\n CONNECTION_FAILED = 'CONNECTION_FAILED',\n PERMISSION_DENIED = 'PERMISSION_DENIED',\n BLUETOOTH_DISABLED = 'BLUETOOTH_DISABLED',\n WIFI_NOT_CONNECTED = 'WIFI_NOT_CONNECTED',\n PAIRING_REQUIRED = 'PAIRING_REQUIRED',\n UNSUPPORTED_TRANSPORT = 'UNSUPPORTED_TRANSPORT',\n UNSUPPORTED_PRINTER = 'UNSUPPORTED_PRINTER',\n IMAGE_INVALID = 'IMAGE_INVALID',\n IMAGE_TOO_LARGE = 'IMAGE_TOO_LARGE',\n PRINT_FAILED = 'PRINT_FAILED',\n PAPER_EMPTY = 'PAPER_EMPTY',\n COVER_OPEN = 'COVER_OPEN',\n SDK_NOT_AVAILABLE = 'SDK_NOT_AVAILABLE',\n TIMEOUT = 'TIMEOUT',\n UNKNOWN = 'UNKNOWN',\n}\n\n/**\n * État de connexion logique d'une imprimante connue du plugin.\n */\nexport type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'error';\n\n/**\n * Niveau de papier rapporté par le statut imprimante (si supporté).\n */\nexport type PaperStatus = 'ok' | 'near_end' | 'empty' | 'unknown';\n\n/**\n * État d'un job d'impression, suivi en temps réel via l'event `printJobStatus`.\n *\n * - `pending` : job accepté, en file (connexion/rendu en cours).\n * - `printing` : octets/commandes en cours d'envoi ou d'impression physique.\n * - `hold` : impression suspendue par l'imprimante (attente papier, capot\n * ouvert, tampon plein). Le job reprend dès résolution.\n * - `completed` : impression physique terminée (best-effort selon transport/SDK).\n * - `failed` : échec définitif (voir errorCode).\n * - `canceled` : annulé par l'app.\n */\nexport type JobState = 'pending' | 'printing' | 'hold' | 'completed' | 'failed' | 'canceled';\n\n/** Raison d'un état `hold`. */\nexport type HoldReason = 'paper_empty' | 'paper_near_end' | 'cover_open' | 'buffer_full' | 'offline' | 'unknown';\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PrintErrorCode } from './enums';
|
|
2
|
+
import type { NormalizedErrorShape } from './options';
|
|
3
|
+
/**
|
|
4
|
+
* Erreur typée exposée côté JS. Les rejets de promesse du plugin sont
|
|
5
|
+
* normalisés vers cette classe via `toPrinterError`.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PrinterError extends Error implements NormalizedErrorShape {
|
|
8
|
+
readonly code: PrintErrorCode;
|
|
9
|
+
readonly detail?: string;
|
|
10
|
+
readonly retryable: boolean;
|
|
11
|
+
constructor(shape: NormalizedErrorShape);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Convertit une erreur Capacitor (qui porte souvent `code` et `message`) en PrinterError.
|
|
15
|
+
*/
|
|
16
|
+
export declare function toPrinterError(err: unknown): PrinterError;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { PrintErrorCode } from './enums';
|
|
2
|
+
/**
|
|
3
|
+
* Erreur typée exposée côté JS. Les rejets de promesse du plugin sont
|
|
4
|
+
* normalisés vers cette classe via `toPrinterError`.
|
|
5
|
+
*/
|
|
6
|
+
export class PrinterError extends Error {
|
|
7
|
+
constructor(shape) {
|
|
8
|
+
var _a;
|
|
9
|
+
super(shape.message);
|
|
10
|
+
this.name = 'PrinterError';
|
|
11
|
+
this.code = shape.code;
|
|
12
|
+
this.detail = shape.detail;
|
|
13
|
+
this.retryable = (_a = shape.retryable) !== null && _a !== void 0 ? _a : defaultRetryable(shape.code);
|
|
14
|
+
Object.setPrototypeOf(this, PrinterError.prototype);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Convertit une erreur Capacitor (qui porte souvent `code` et `message`) en PrinterError.
|
|
19
|
+
*/
|
|
20
|
+
export function toPrinterError(err) {
|
|
21
|
+
var _a;
|
|
22
|
+
if (err instanceof PrinterError)
|
|
23
|
+
return err;
|
|
24
|
+
const anyErr = err;
|
|
25
|
+
const code = mapToErrorCode(anyErr === null || anyErr === void 0 ? void 0 : anyErr.code);
|
|
26
|
+
return new PrinterError({
|
|
27
|
+
code,
|
|
28
|
+
message: (_a = anyErr === null || anyErr === void 0 ? void 0 : anyErr.message) !== null && _a !== void 0 ? _a : 'Erreur imprimante inconnue',
|
|
29
|
+
detail: anyErr === null || anyErr === void 0 ? void 0 : anyErr.detail,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function mapToErrorCode(raw) {
|
|
33
|
+
if (!raw)
|
|
34
|
+
return PrintErrorCode.UNKNOWN;
|
|
35
|
+
const upper = raw.toUpperCase();
|
|
36
|
+
if (upper in PrintErrorCode) {
|
|
37
|
+
return PrintErrorCode[upper];
|
|
38
|
+
}
|
|
39
|
+
return PrintErrorCode.UNKNOWN;
|
|
40
|
+
}
|
|
41
|
+
/** Les erreurs réseau/connexion/timeout justifient un retry automatique. */
|
|
42
|
+
function defaultRetryable(code) {
|
|
43
|
+
switch (code) {
|
|
44
|
+
case PrintErrorCode.CONNECTION_FAILED:
|
|
45
|
+
case PrintErrorCode.PRINTER_OFFLINE:
|
|
46
|
+
case PrintErrorCode.TIMEOUT:
|
|
47
|
+
case PrintErrorCode.WIFI_NOT_CONNECTED:
|
|
48
|
+
return true;
|
|
49
|
+
default:
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGzC;;;GAGG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IAKrC,YAAY,KAA2B;;QACrC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,MAAA,KAAK,CAAC,SAAS,mCAAI,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjE,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IACtD,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,GAAY;;IACzC,IAAI,GAAG,YAAY,YAAY;QAAE,OAAO,GAAG,CAAC;IAE5C,MAAM,MAAM,GAAG,GAA2D,CAAC;IAC3E,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,IAAI,CAAC,CAAC;IAC1C,OAAO,IAAI,YAAY,CAAC;QACtB,IAAI;QACJ,OAAO,EAAE,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,OAAO,mCAAI,4BAA4B;QACxD,MAAM,EAAE,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,MAAM;KACvB,CAAC,CAAC;AACL,CAAC;AAED,SAAS,cAAc,CAAC,GAAY;IAClC,IAAI,CAAC,GAAG;QAAE,OAAO,cAAc,CAAC,OAAO,CAAC;IACxC,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,IAAI,KAAK,IAAI,cAAc,EAAE,CAAC;QAC5B,OAAO,cAAc,CAAC,KAAoC,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,cAAc,CAAC,OAAO,CAAC;AAChC,CAAC;AAED,4EAA4E;AAC5E,SAAS,gBAAgB,CAAC,IAAoB;IAC5C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,cAAc,CAAC,iBAAiB,CAAC;QACtC,KAAK,cAAc,CAAC,eAAe,CAAC;QACpC,KAAK,cAAc,CAAC,OAAO,CAAC;QAC5B,KAAK,cAAc,CAAC,kBAAkB;YACpC,OAAO,IAAI,CAAC;QACd;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC","sourcesContent":["import { PrintErrorCode } from './enums';\nimport type { NormalizedErrorShape } from './options';\n\n/**\n * Erreur typée exposée côté JS. Les rejets de promesse du plugin sont\n * normalisés vers cette classe via `toPrinterError`.\n */\nexport class PrinterError extends Error implements NormalizedErrorShape {\n public readonly code: PrintErrorCode;\n public readonly detail?: string;\n public readonly retryable: boolean;\n\n constructor(shape: NormalizedErrorShape) {\n super(shape.message);\n this.name = 'PrinterError';\n this.code = shape.code;\n this.detail = shape.detail;\n this.retryable = shape.retryable ?? defaultRetryable(shape.code);\n Object.setPrototypeOf(this, PrinterError.prototype);\n }\n}\n\n/**\n * Convertit une erreur Capacitor (qui porte souvent `code` et `message`) en PrinterError.\n */\nexport function toPrinterError(err: unknown): PrinterError {\n if (err instanceof PrinterError) return err;\n\n const anyErr = err as { code?: string; message?: string; detail?: string };\n const code = mapToErrorCode(anyErr?.code);\n return new PrinterError({\n code,\n message: anyErr?.message ?? 'Erreur imprimante inconnue',\n detail: anyErr?.detail,\n });\n}\n\nfunction mapToErrorCode(raw?: string): PrintErrorCode {\n if (!raw) return PrintErrorCode.UNKNOWN;\n const upper = raw.toUpperCase();\n if (upper in PrintErrorCode) {\n return PrintErrorCode[upper as keyof typeof PrintErrorCode];\n }\n return PrintErrorCode.UNKNOWN;\n}\n\n/** Les erreurs réseau/connexion/timeout justifient un retry automatique. */\nfunction defaultRetryable(code: PrintErrorCode): boolean {\n switch (code) {\n case PrintErrorCode.CONNECTION_FAILED:\n case PrintErrorCode.PRINTER_OFFLINE:\n case PrintErrorCode.TIMEOUT:\n case PrintErrorCode.WIFI_NOT_CONNECTED:\n return true;\n default:\n return false;\n }\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type BarcodeItem, type CodePage, type PrintItem, type QrCodeItem, type TextStyle } from './text';
|
|
2
|
+
export interface EscPosTextOptions {
|
|
3
|
+
/** Page de code par défaut (français : 'WPC1252'). */
|
|
4
|
+
defaultCodePage?: CodePage;
|
|
5
|
+
/** Nb de colonnes en police A (déduit largeur). Défaut 48 (80mm) / 32 (58mm). */
|
|
6
|
+
columns?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Encode une chaîne vers une page de code mono-octet.
|
|
10
|
+
* Pour WPC1252/Latin-1, le code-point Unicode == octet pour 0x00..0xFF (couvre FR).
|
|
11
|
+
* Les caractères hors plage sont remplacés par '?'.
|
|
12
|
+
*/
|
|
13
|
+
export declare function encodeString(value: string): number[];
|
|
14
|
+
/** GS ! n : taille (largeur sur bits 4-6, hauteur sur bits 0-2). */
|
|
15
|
+
export declare function sizeByte(widthMultiplier?: number, heightMultiplier?: number): number;
|
|
16
|
+
/** Construit les commandes d'ouverture de style pour un item texte. */
|
|
17
|
+
export declare function openStyle(style?: TextStyle, opts?: EscPosTextOptions): number[];
|
|
18
|
+
/** Réinitialise les styles transitoires après un item (ESC @ = reset complet). */
|
|
19
|
+
export declare function resetStyle(): number[];
|
|
20
|
+
export declare function encodeQrCode(item: QrCodeItem): number[];
|
|
21
|
+
export declare function encodeBarcode(item: BarcodeItem): number[];
|
|
22
|
+
/** Sépare le flux en (octets, métadonnées images à rendre séparément). */
|
|
23
|
+
export interface EncodedJob {
|
|
24
|
+
bytes: Uint8Array;
|
|
25
|
+
/** Index des items `image` rencontrés (rendus par le pipeline natif). */
|
|
26
|
+
imageItemIndexes: number[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Encode un tableau d'items. Les items `image` produisent un marqueur
|
|
30
|
+
* (interruption) géré par le natif : l'encodeur retourne donc des "segments".
|
|
31
|
+
* Pour la référence/tests, on encode tout SAUF image (signalé dans imageItemIndexes).
|
|
32
|
+
*/
|
|
33
|
+
export declare function encodeEscPosItems(items: PrintItem[], opts?: EscPosTextOptions): EncodedJob;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { CODE_PAGE_TO_ESC_T, } from './text';
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* ENCODEUR ESC/POS TEXTE — référence pure (testée), mirrorée en Kotlin/Swift.
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*
|
|
7
|
+
* Transforme un tableau de `PrintItem` en flux d'octets ESC/POS. Utilisé :
|
|
8
|
+
* - par les tests unitaires (vérification octet par octet),
|
|
9
|
+
* - comme spécification pour les implémentations natives,
|
|
10
|
+
* - directement par les adapters ESC/POS/rawTcp (via le miroir natif).
|
|
11
|
+
*
|
|
12
|
+
* Les items `image` ne sont PAS encodés ici (ils passent par le pipeline image
|
|
13
|
+
* natif). L'encodeur émet un marqueur d'erreur si on lui en donne un.
|
|
14
|
+
*/
|
|
15
|
+
const ESC = 0x1b;
|
|
16
|
+
const GS = 0x1d;
|
|
17
|
+
const LF = 0x0a;
|
|
18
|
+
/** Concatène des segments d'octets. */
|
|
19
|
+
function concat(parts) {
|
|
20
|
+
const total = parts.reduce((n, p) => n + p.length, 0);
|
|
21
|
+
const out = new Uint8Array(total);
|
|
22
|
+
let off = 0;
|
|
23
|
+
for (const p of parts) {
|
|
24
|
+
out.set(p, off);
|
|
25
|
+
off += p.length;
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Encode une chaîne vers une page de code mono-octet.
|
|
31
|
+
* Pour WPC1252/Latin-1, le code-point Unicode == octet pour 0x00..0xFF (couvre FR).
|
|
32
|
+
* Les caractères hors plage sont remplacés par '?'.
|
|
33
|
+
*/
|
|
34
|
+
export function encodeString(value) {
|
|
35
|
+
var _a;
|
|
36
|
+
const bytes = [];
|
|
37
|
+
for (const ch of value) {
|
|
38
|
+
const cp = (_a = ch.codePointAt(0)) !== null && _a !== void 0 ? _a : 0x3f;
|
|
39
|
+
bytes.push(cp <= 0xff ? cp : 0x3f);
|
|
40
|
+
}
|
|
41
|
+
return bytes;
|
|
42
|
+
}
|
|
43
|
+
/** GS ! n : taille (largeur sur bits 4-6, hauteur sur bits 0-2). */
|
|
44
|
+
export function sizeByte(widthMultiplier = 1, heightMultiplier = 1) {
|
|
45
|
+
const w = Math.min(8, Math.max(1, widthMultiplier)) - 1;
|
|
46
|
+
const h = Math.min(8, Math.max(1, heightMultiplier)) - 1;
|
|
47
|
+
return (w << 4) | h;
|
|
48
|
+
}
|
|
49
|
+
/** Construit les commandes d'ouverture de style pour un item texte. */
|
|
50
|
+
export function openStyle(style = {}, opts = {}) {
|
|
51
|
+
var _a, _b, _c;
|
|
52
|
+
const cmds = [];
|
|
53
|
+
// Page de code (accents)
|
|
54
|
+
const cp = (_a = style.codePageId) !== null && _a !== void 0 ? _a : CODE_PAGE_TO_ESC_T[(_c = (_b = style.codePage) !== null && _b !== void 0 ? _b : opts.defaultCodePage) !== null && _c !== void 0 ? _c : 'WPC1252'];
|
|
55
|
+
cmds.push([ESC, 0x74, cp & 0xff]); // ESC t n
|
|
56
|
+
// Alignement (ESC a n)
|
|
57
|
+
const align = style.align === 'center' ? 1 : style.align === 'right' ? 2 : 0;
|
|
58
|
+
cmds.push([ESC, 0x61, align]);
|
|
59
|
+
// Police (ESC M n)
|
|
60
|
+
cmds.push([ESC, 0x4d, style.font === 'B' ? 1 : 0]);
|
|
61
|
+
// Gras (ESC E n)
|
|
62
|
+
cmds.push([ESC, 0x45, style.bold ? 1 : 0]);
|
|
63
|
+
// Double frappe (ESC G n)
|
|
64
|
+
cmds.push([ESC, 0x47, style.doubleStrike ? 1 : 0]);
|
|
65
|
+
// Soulignement (ESC - n) : 0/1/2
|
|
66
|
+
const ul = style.underline === 'single' ? 1 : style.underline === 'double' ? 2 : 0;
|
|
67
|
+
cmds.push([ESC, 0x2d, ul]);
|
|
68
|
+
// Inversion vidéo (GS B n)
|
|
69
|
+
cmds.push([GS, 0x42, style.invert ? 1 : 0]);
|
|
70
|
+
// Upside-down (ESC { n)
|
|
71
|
+
cmds.push([ESC, 0x7b, style.upsideDown ? 1 : 0]);
|
|
72
|
+
// Rotation 90° (ESC V n)
|
|
73
|
+
cmds.push([ESC, 0x56, style.rotate90 ? 1 : 0]);
|
|
74
|
+
// Taille (GS ! n)
|
|
75
|
+
cmds.push([GS, 0x21, sizeByte(style.widthMultiplier, style.heightMultiplier)]);
|
|
76
|
+
// Espacement caractères (ESC SP n)
|
|
77
|
+
if (style.letterSpacing != null)
|
|
78
|
+
cmds.push([ESC, 0x20, style.letterSpacing & 0xff]);
|
|
79
|
+
// Interligne (ESC 3 n) ou défaut (ESC 2)
|
|
80
|
+
if (style.lineSpacing != null)
|
|
81
|
+
cmds.push([ESC, 0x33, style.lineSpacing & 0xff]);
|
|
82
|
+
else
|
|
83
|
+
cmds.push([ESC, 0x32]);
|
|
84
|
+
return cmds.flat();
|
|
85
|
+
}
|
|
86
|
+
/** Réinitialise les styles transitoires après un item (ESC @ = reset complet). */
|
|
87
|
+
export function resetStyle() {
|
|
88
|
+
return [ESC, 0x40];
|
|
89
|
+
}
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// QR code (GS ( k)
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
export function encodeQrCode(item) {
|
|
94
|
+
var _a, _b;
|
|
95
|
+
const out = [];
|
|
96
|
+
const align = item.align === 'left' ? 0 : item.align === 'right' ? 2 : 1;
|
|
97
|
+
out.push([ESC, 0x61, align]);
|
|
98
|
+
// Modèle 2 : GS ( k 04 00 31 41 50 00
|
|
99
|
+
out.push([GS, 0x28, 0x6b, 0x04, 0x00, 0x31, 0x41, 0x32, 0x00]);
|
|
100
|
+
// Taille module : GS ( k 03 00 31 43 n
|
|
101
|
+
const size = Math.min(16, Math.max(1, (_a = item.size) !== null && _a !== void 0 ? _a : 6));
|
|
102
|
+
out.push([GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x43, size]);
|
|
103
|
+
// Correction erreur : GS ( k 03 00 31 45 n (48=L,49=M,50=Q,51=H)
|
|
104
|
+
const ecMap = { L: 48, M: 49, Q: 50, H: 51 };
|
|
105
|
+
out.push([GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x45, ecMap[(_b = item.errorCorrection) !== null && _b !== void 0 ? _b : 'M']]);
|
|
106
|
+
// Stockage data : GS ( k pL pH 31 50 30 d...
|
|
107
|
+
const data = encodeString(item.value);
|
|
108
|
+
const len = data.length + 3;
|
|
109
|
+
out.push([GS, 0x28, 0x6b, len & 0xff, (len >> 8) & 0xff, 0x31, 0x50, 0x30, ...data]);
|
|
110
|
+
// Impression : GS ( k 03 00 31 51 30
|
|
111
|
+
out.push([GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x51, 0x30]);
|
|
112
|
+
return out.flat();
|
|
113
|
+
}
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Code-barres 1D (GS k, fonction B)
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
const BARCODE_M = {
|
|
118
|
+
UPC_A: 65,
|
|
119
|
+
UPC_E: 66,
|
|
120
|
+
EAN13: 67,
|
|
121
|
+
EAN8: 68,
|
|
122
|
+
CODE39: 69,
|
|
123
|
+
ITF: 70,
|
|
124
|
+
CODABAR: 71,
|
|
125
|
+
CODE93: 72,
|
|
126
|
+
CODE128: 73,
|
|
127
|
+
};
|
|
128
|
+
export function encodeBarcode(item) {
|
|
129
|
+
var _a, _b;
|
|
130
|
+
const out = [];
|
|
131
|
+
const align = item.align === 'left' ? 0 : item.align === 'right' ? 2 : 1;
|
|
132
|
+
out.push([ESC, 0x61, align]);
|
|
133
|
+
// HRI position (GS H n) : 0 none,1 above,2 below,3 both
|
|
134
|
+
const hri = item.hri === 'above' ? 1 : item.hri === 'both' ? 3 : item.hri === 'none' ? 0 : 2;
|
|
135
|
+
out.push([GS, 0x48, hri]);
|
|
136
|
+
// Hauteur (GS h n)
|
|
137
|
+
out.push([GS, 0x68, Math.min(255, Math.max(1, (_a = item.height) !== null && _a !== void 0 ? _a : 80))]);
|
|
138
|
+
// Largeur module (GS w n)
|
|
139
|
+
out.push([GS, 0x77, Math.min(6, Math.max(2, (_b = item.width) !== null && _b !== void 0 ? _b : 3))]);
|
|
140
|
+
const m = BARCODE_M[item.symbology];
|
|
141
|
+
let data = encodeString(item.value);
|
|
142
|
+
// CODE128 attend un préfixe de jeu de codes ({B par défaut) si absent.
|
|
143
|
+
if (item.symbology === 'CODE128' && !(data[0] === 0x7b)) {
|
|
144
|
+
data = [0x7b, 0x42, ...data]; // "{B"
|
|
145
|
+
}
|
|
146
|
+
// Fonction B : GS k m n d1..dn
|
|
147
|
+
out.push([GS, 0x6b, m, data.length, ...data]);
|
|
148
|
+
return out.flat();
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Encode un tableau d'items. Les items `image` produisent un marqueur
|
|
152
|
+
* (interruption) géré par le natif : l'encodeur retourne donc des "segments".
|
|
153
|
+
* Pour la référence/tests, on encode tout SAUF image (signalé dans imageItemIndexes).
|
|
154
|
+
*/
|
|
155
|
+
export function encodeEscPosItems(items, opts = {}) {
|
|
156
|
+
var _a;
|
|
157
|
+
const parts = [resetStyle()]; // ESC @ initial
|
|
158
|
+
const imageItemIndexes = [];
|
|
159
|
+
const columns = (_a = opts.columns) !== null && _a !== void 0 ? _a : 48;
|
|
160
|
+
items.forEach((item, index) => {
|
|
161
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
162
|
+
switch (item.type) {
|
|
163
|
+
case 'text': {
|
|
164
|
+
const style = (_a = item.style) !== null && _a !== void 0 ? _a : {};
|
|
165
|
+
parts.push(openStyle(style, opts));
|
|
166
|
+
parts.push(encodeString(item.value));
|
|
167
|
+
if (style.newline !== false)
|
|
168
|
+
parts.push([LF]);
|
|
169
|
+
parts.push(resetStyle());
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case 'feed':
|
|
173
|
+
parts.push([ESC, 0x64, Math.min(255, Math.max(0, (_b = item.lines) !== null && _b !== void 0 ? _b : 1))]);
|
|
174
|
+
break;
|
|
175
|
+
case 'divider': {
|
|
176
|
+
const ch = ((_c = item.char) !== null && _c !== void 0 ? _c : '-').charCodeAt(0);
|
|
177
|
+
const n = (_d = item.columns) !== null && _d !== void 0 ? _d : columns;
|
|
178
|
+
const align = ((_e = item.style) === null || _e === void 0 ? void 0 : _e.align) === 'center' ? 1 : ((_f = item.style) === null || _f === void 0 ? void 0 : _f.align) === 'right' ? 2 : 0;
|
|
179
|
+
parts.push([ESC, 0x61, align]);
|
|
180
|
+
if ((_g = item.style) === null || _g === void 0 ? void 0 : _g.bold)
|
|
181
|
+
parts.push([ESC, 0x45, 1]);
|
|
182
|
+
parts.push(new Array(n).fill(ch));
|
|
183
|
+
parts.push([LF]);
|
|
184
|
+
parts.push(resetStyle());
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
case 'qrcode':
|
|
188
|
+
parts.push(encodeQrCode(item));
|
|
189
|
+
break;
|
|
190
|
+
case 'barcode':
|
|
191
|
+
parts.push(encodeBarcode(item));
|
|
192
|
+
break;
|
|
193
|
+
case 'cashDrawer':
|
|
194
|
+
parts.push(item.pin === 5 ? [ESC, 0x70, 0x01, 0x19, 0xfa] : [ESC, 0x70, 0x00, 0x19, 0xfa]);
|
|
195
|
+
break;
|
|
196
|
+
case 'cut': {
|
|
197
|
+
if (item.feedBefore)
|
|
198
|
+
parts.push([ESC, 0x64, item.feedBefore & 0xff]);
|
|
199
|
+
parts.push(item.mode === 'full' ? [GS, 0x56, 0x00] : [GS, 0x56, 0x01]);
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
case 'raw': {
|
|
203
|
+
const bin = atobToBytes(item.bytesBase64);
|
|
204
|
+
parts.push(Array.from(bin));
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case 'image':
|
|
208
|
+
imageItemIndexes.push(index);
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
return { bytes: concat(parts), imageItemIndexes };
|
|
213
|
+
}
|
|
214
|
+
/** Décodage base64 -> octets, compatible Node et navigateur (sans dépendance). */
|
|
215
|
+
function atobToBytes(b64) {
|
|
216
|
+
const clean = b64.includes('base64,') ? b64.split('base64,')[1] : b64;
|
|
217
|
+
const decode = typeof atob === 'function'
|
|
218
|
+
? atob
|
|
219
|
+
: (s) => {
|
|
220
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
221
|
+
let str = s.replace(/=+$/, '');
|
|
222
|
+
let output = '';
|
|
223
|
+
for (let bc = 0, bs = 0, buffer, i = 0; (buffer = str.charAt(i++));) {
|
|
224
|
+
buffer = chars.indexOf(buffer);
|
|
225
|
+
if (~buffer) {
|
|
226
|
+
bs = bc % 4 ? bs * 64 + buffer : buffer;
|
|
227
|
+
if (bc++ % 4)
|
|
228
|
+
output += String.fromCharCode(255 & (bs >> ((-2 * bc) & 6)));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return output;
|
|
232
|
+
};
|
|
233
|
+
const bin = decode(clean);
|
|
234
|
+
const out = new Uint8Array(bin.length);
|
|
235
|
+
for (let i = 0; i < bin.length; i++)
|
|
236
|
+
out[i] = bin.charCodeAt(i);
|
|
237
|
+
return out;
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=escpos-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escpos-text.js","sourceRoot":"","sources":["../../../src/core/escpos-text.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CODE_PAGE_TO_ESC_T,\n type BarcodeItem,\n type BarcodeSymbology,\n type CodePage,\n type PrintItem,\n type QrCodeItem,\n type TextStyle,\n} from './text';\n\n/**\n * ============================================================================\n * ENCODEUR ESC/POS TEXTE — référence pure (testée), mirrorée en Kotlin/Swift.\n * ============================================================================\n *\n * Transforme un tableau de `PrintItem` en flux d'octets ESC/POS. Utilisé :\n * - par les tests unitaires (vérification octet par octet),\n * - comme spécification pour les implémentations natives,\n * - directement par les adapters ESC/POS/rawTcp (via le miroir natif).\n *\n * Les items `image` ne sont PAS encodés ici (ils passent par le pipeline image\n * natif). L'encodeur émet un marqueur d'erreur si on lui en donne un.\n */\n\nconst ESC = 0x1b;\nconst GS = 0x1d;\nconst LF = 0x0a;\n\nexport interface EscPosTextOptions {\n /** Page de code par défaut (français : 'WPC1252'). */\n defaultCodePage?: CodePage;\n /** Nb de colonnes en police A (déduit largeur). Défaut 48 (80mm) / 32 (58mm). */\n columns?: number;\n}\n\n/** Concatène des segments d'octets. */\nfunction concat(parts: number[][]): Uint8Array {\n const total = parts.reduce((n, p) => n + p.length, 0);\n const out = new Uint8Array(total);\n let off = 0;\n for (const p of parts) {\n out.set(p, off);\n off += p.length;\n }\n return out;\n}\n\n/**\n * Encode une chaîne vers une page de code mono-octet.\n * Pour WPC1252/Latin-1, le code-point Unicode == octet pour 0x00..0xFF (couvre FR).\n * Les caractères hors plage sont remplacés par '?'.\n */\nexport function encodeString(value: string): number[] {\n const bytes: number[] = [];\n for (const ch of value) {\n const cp = ch.codePointAt(0) ?? 0x3f;\n bytes.push(cp <= 0xff ? cp : 0x3f);\n }\n return bytes;\n}\n\n/** GS ! n : taille (largeur sur bits 4-6, hauteur sur bits 0-2). */\nexport function sizeByte(widthMultiplier = 1, heightMultiplier = 1): number {\n const w = Math.min(8, Math.max(1, widthMultiplier)) - 1;\n const h = Math.min(8, Math.max(1, heightMultiplier)) - 1;\n return (w << 4) | h;\n}\n\n/** Construit les commandes d'ouverture de style pour un item texte. */\nexport function openStyle(style: TextStyle = {}, opts: EscPosTextOptions = {}): number[] {\n const cmds: number[][] = [];\n\n // Page de code (accents)\n const cp = style.codePageId ?? CODE_PAGE_TO_ESC_T[style.codePage ?? opts.defaultCodePage ?? 'WPC1252'];\n cmds.push([ESC, 0x74, cp & 0xff]); // ESC t n\n\n // Alignement (ESC a n)\n const align = style.align === 'center' ? 1 : style.align === 'right' ? 2 : 0;\n cmds.push([ESC, 0x61, align]);\n\n // Police (ESC M n)\n cmds.push([ESC, 0x4d, style.font === 'B' ? 1 : 0]);\n\n // Gras (ESC E n)\n cmds.push([ESC, 0x45, style.bold ? 1 : 0]);\n\n // Double frappe (ESC G n)\n cmds.push([ESC, 0x47, style.doubleStrike ? 1 : 0]);\n\n // Soulignement (ESC - n) : 0/1/2\n const ul = style.underline === 'single' ? 1 : style.underline === 'double' ? 2 : 0;\n cmds.push([ESC, 0x2d, ul]);\n\n // Inversion vidéo (GS B n)\n cmds.push([GS, 0x42, style.invert ? 1 : 0]);\n\n // Upside-down (ESC { n)\n cmds.push([ESC, 0x7b, style.upsideDown ? 1 : 0]);\n\n // Rotation 90° (ESC V n)\n cmds.push([ESC, 0x56, style.rotate90 ? 1 : 0]);\n\n // Taille (GS ! n)\n cmds.push([GS, 0x21, sizeByte(style.widthMultiplier, style.heightMultiplier)]);\n\n // Espacement caractères (ESC SP n)\n if (style.letterSpacing != null) cmds.push([ESC, 0x20, style.letterSpacing & 0xff]);\n\n // Interligne (ESC 3 n) ou défaut (ESC 2)\n if (style.lineSpacing != null) cmds.push([ESC, 0x33, style.lineSpacing & 0xff]);\n else cmds.push([ESC, 0x32]);\n\n return cmds.flat();\n}\n\n/** Réinitialise les styles transitoires après un item (ESC @ = reset complet). */\nexport function resetStyle(): number[] {\n return [ESC, 0x40];\n}\n\n// ---------------------------------------------------------------------------\n// QR code (GS ( k)\n// ---------------------------------------------------------------------------\n\nexport function encodeQrCode(item: QrCodeItem): number[] {\n const out: number[][] = [];\n const align = item.align === 'left' ? 0 : item.align === 'right' ? 2 : 1;\n out.push([ESC, 0x61, align]);\n\n // Modèle 2 : GS ( k 04 00 31 41 50 00\n out.push([GS, 0x28, 0x6b, 0x04, 0x00, 0x31, 0x41, 0x32, 0x00]);\n // Taille module : GS ( k 03 00 31 43 n\n const size = Math.min(16, Math.max(1, item.size ?? 6));\n out.push([GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x43, size]);\n // Correction erreur : GS ( k 03 00 31 45 n (48=L,49=M,50=Q,51=H)\n const ecMap: Record<string, number> = { L: 48, M: 49, Q: 50, H: 51 };\n out.push([GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x45, ecMap[item.errorCorrection ?? 'M']]);\n // Stockage data : GS ( k pL pH 31 50 30 d...\n const data = encodeString(item.value);\n const len = data.length + 3;\n out.push([GS, 0x28, 0x6b, len & 0xff, (len >> 8) & 0xff, 0x31, 0x50, 0x30, ...data]);\n // Impression : GS ( k 03 00 31 51 30\n out.push([GS, 0x28, 0x6b, 0x03, 0x00, 0x31, 0x51, 0x30]);\n return out.flat();\n}\n\n// ---------------------------------------------------------------------------\n// Code-barres 1D (GS k, fonction B)\n// ---------------------------------------------------------------------------\n\nconst BARCODE_M: Record<BarcodeSymbology, number> = {\n UPC_A: 65,\n UPC_E: 66,\n EAN13: 67,\n EAN8: 68,\n CODE39: 69,\n ITF: 70,\n CODABAR: 71,\n CODE93: 72,\n CODE128: 73,\n};\n\nexport function encodeBarcode(item: BarcodeItem): number[] {\n const out: number[][] = [];\n const align = item.align === 'left' ? 0 : item.align === 'right' ? 2 : 1;\n out.push([ESC, 0x61, align]);\n\n // HRI position (GS H n) : 0 none,1 above,2 below,3 both\n const hri = item.hri === 'above' ? 1 : item.hri === 'both' ? 3 : item.hri === 'none' ? 0 : 2;\n out.push([GS, 0x48, hri]);\n // Hauteur (GS h n)\n out.push([GS, 0x68, Math.min(255, Math.max(1, item.height ?? 80))]);\n // Largeur module (GS w n)\n out.push([GS, 0x77, Math.min(6, Math.max(2, item.width ?? 3))]);\n\n const m = BARCODE_M[item.symbology];\n let data = encodeString(item.value);\n // CODE128 attend un préfixe de jeu de codes ({B par défaut) si absent.\n if (item.symbology === 'CODE128' && !(data[0] === 0x7b)) {\n data = [0x7b, 0x42, ...data]; // \"{B\"\n }\n // Fonction B : GS k m n d1..dn\n out.push([GS, 0x6b, m, data.length, ...data]);\n return out.flat();\n}\n\n// ---------------------------------------------------------------------------\n// Encodeur principal\n// ---------------------------------------------------------------------------\n\n/** Sépare le flux en (octets, métadonnées images à rendre séparément). */\nexport interface EncodedJob {\n bytes: Uint8Array;\n /** Index des items `image` rencontrés (rendus par le pipeline natif). */\n imageItemIndexes: number[];\n}\n\n/**\n * Encode un tableau d'items. Les items `image` produisent un marqueur\n * (interruption) géré par le natif : l'encodeur retourne donc des \"segments\".\n * Pour la référence/tests, on encode tout SAUF image (signalé dans imageItemIndexes).\n */\nexport function encodeEscPosItems(items: PrintItem[], opts: EscPosTextOptions = {}): EncodedJob {\n const parts: number[][] = [resetStyle()]; // ESC @ initial\n const imageItemIndexes: number[] = [];\n const columns = opts.columns ?? 48;\n\n items.forEach((item, index) => {\n switch (item.type) {\n case 'text': {\n const style = item.style ?? {};\n parts.push(openStyle(style, opts));\n parts.push(encodeString(item.value));\n if (style.newline !== false) parts.push([LF]);\n parts.push(resetStyle());\n break;\n }\n case 'feed':\n parts.push([ESC, 0x64, Math.min(255, Math.max(0, item.lines ?? 1))]);\n break;\n case 'divider': {\n const ch = (item.char ?? '-').charCodeAt(0);\n const n = item.columns ?? columns;\n const align = item.style?.align === 'center' ? 1 : item.style?.align === 'right' ? 2 : 0;\n parts.push([ESC, 0x61, align]);\n if (item.style?.bold) parts.push([ESC, 0x45, 1]);\n parts.push(new Array(n).fill(ch));\n parts.push([LF]);\n parts.push(resetStyle());\n break;\n }\n case 'qrcode':\n parts.push(encodeQrCode(item));\n break;\n case 'barcode':\n parts.push(encodeBarcode(item));\n break;\n case 'cashDrawer':\n parts.push(item.pin === 5 ? [ESC, 0x70, 0x01, 0x19, 0xfa] : [ESC, 0x70, 0x00, 0x19, 0xfa]);\n break;\n case 'cut': {\n if (item.feedBefore) parts.push([ESC, 0x64, item.feedBefore & 0xff]);\n parts.push(item.mode === 'full' ? [GS, 0x56, 0x00] : [GS, 0x56, 0x01]);\n break;\n }\n case 'raw': {\n const bin = atobToBytes(item.bytesBase64);\n parts.push(Array.from(bin));\n break;\n }\n case 'image':\n imageItemIndexes.push(index);\n break;\n }\n });\n\n return { bytes: concat(parts), imageItemIndexes };\n}\n\n/** Décodage base64 -> octets, compatible Node et navigateur (sans dépendance). */\nfunction atobToBytes(b64: string): Uint8Array {\n const clean = b64.includes('base64,') ? b64.split('base64,')[1] : b64;\n const decode =\n typeof atob === 'function'\n ? atob\n : (s: string): string => {\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n let str = s.replace(/=+$/, '');\n let output = '';\n for (let bc = 0, bs = 0, buffer, i = 0; (buffer = str.charAt(i++)); ) {\n buffer = chars.indexOf(buffer);\n if (~buffer) {\n bs = bc % 4 ? bs * 64 + buffer : buffer;\n if (bc++ % 4) output += String.fromCharCode(255 & (bs >> ((-2 * bc) & 6)));\n }\n }\n return output;\n };\n const bin = decode(clean);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n"]}
|