react-native-earl-thermal-printer 1.0.1 → 1.3.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.
Files changed (33) hide show
  1. package/README.md +114 -22
  2. package/android/.project +0 -6
  3. package/android/src/main/java/com/pinmi/react/printer/RNBLEPrinterModule.java +4 -4
  4. package/android/src/main/java/com/pinmi/react/printer/RNNetPrinterModule.java +4 -4
  5. package/android/src/main/java/com/pinmi/react/printer/RNPrinterModule.java +2 -2
  6. package/android/src/main/java/com/pinmi/react/printer/RNUSBPrinterModule.java +4 -4
  7. package/android/src/main/java/com/pinmi/react/printer/adapter/BLEPrinterAdapter.java +17 -15
  8. package/android/src/main/java/com/pinmi/react/printer/adapter/NetPrinterAdapter.java +17 -15
  9. package/android/src/main/java/com/pinmi/react/printer/adapter/PrinterAdapter.java +2 -2
  10. package/android/src/main/java/com/pinmi/react/printer/adapter/USBPrinterAdapter.java +17 -15
  11. package/dist/NativeBLEPrinter.d.ts +2 -2
  12. package/dist/NativeBLEPrinter.d.ts.map +1 -1
  13. package/dist/NativeNetPrinter.d.ts +2 -2
  14. package/dist/NativeNetPrinter.d.ts.map +1 -1
  15. package/dist/NativeUSBPrinter.d.ts +2 -2
  16. package/dist/NativeUSBPrinter.d.ts.map +1 -1
  17. package/dist/index.d.ts +6 -6
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +6 -6
  20. package/dist/index.js.map +1 -1
  21. package/dist/utils/EPToolkit.d.ts +4 -1
  22. package/dist/utils/EPToolkit.d.ts.map +1 -1
  23. package/dist/utils/EPToolkit.js +212 -20
  24. package/dist/utils/EPToolkit.js.map +1 -1
  25. package/ios/RNBLEPrinter.mm +4 -1
  26. package/ios/RNNetPrinter.mm +4 -1
  27. package/ios/RNUSBPrinter.mm +2 -0
  28. package/package.json +1 -1
  29. package/src/NativeBLEPrinter.ts +2 -2
  30. package/src/NativeNetPrinter.ts +2 -2
  31. package/src/NativeUSBPrinter.ts +2 -2
  32. package/src/index.ts +12 -12
  33. package/src/utils/EPToolkit.ts +270 -20
@@ -4,34 +4,150 @@ import * as iconv from "iconv-lite";
4
4
 
5
5
  import BufferHelper from "./buffer-helper";
6
6
 
7
- const init_printer_bytes = Buffer.from([27, 64]);
8
- const l_start_bytes = Buffer.from([27, 97, 0]);
7
+ // ── Printer initialization ──────────────────────────────────────────────────
8
+
9
+ const init_printer_bytes = Buffer.from([27, 64]); // ESC @
10
+
11
+ // ── Alignment ───────────────────────────────────────────────────────────────
12
+
13
+ const l_start_bytes = Buffer.from([27, 97, 0]); // ESC a 0 — Left
9
14
  const l_end_bytes = Buffer.from([]);
10
- const c_start_bytes = Buffer.from([27, 97, 1]);
11
- const c_end_bytes = Buffer.from([]); // [ 27, 97, 0 ];
12
- const r_start_bytes = Buffer.from([27, 97, 2]);
15
+ const c_start_bytes = Buffer.from([27, 97, 1]); // ESC a 1 — Center
16
+ const c_end_bytes = Buffer.from([]);
17
+ const r_start_bytes = Buffer.from([27, 97, 2]); // ESC a 2 — Right
13
18
  const r_end_bytes = Buffer.from([]);
14
19
 
15
- const default_space_bytes = Buffer.from([27, 50]);
20
+ // ── Spacing ─────────────────────────────────────────────────────────────────
21
+
22
+ const default_space_bytes = Buffer.from([27, 50]); // ESC 2 — Default line spacing
23
+
24
+ // ── Reset (sent after every newline) ────────────────────────────────────────
16
25
 
17
- const reset_bytes = Buffer.from([27, 97, 0, 29, 33, 0, 27, 50]);
18
- const m_start_bytes = Buffer.from([27, 33, 16, 28, 33, 8]);
26
+ const reset_bytes = Buffer.from([
27
+ 27,
28
+ 97,
29
+ 0, // ESC a 0 — Left align
30
+ 29,
31
+ 33,
32
+ 0, // GS ! 0 — Normal character size
33
+ 27,
34
+ 33,
35
+ 0, // ESC ! 0 — Normal print mode
36
+ 28,
37
+ 33,
38
+ 0, // FS ! 0 — Normal CJK mode
39
+ 27,
40
+ 45,
41
+ 0, // ESC - 0 — Underline off
42
+ 27,
43
+ 69,
44
+ 0, // ESC E 0 — Bold/emphasis off
45
+ 29,
46
+ 66,
47
+ 0, // GS B 0 — Reverse off
48
+ 27,
49
+ 123,
50
+ 0, // ESC { 0 — Upside-down off
51
+ 27,
52
+ 50, // ESC 2 — Default line spacing
53
+ ]);
54
+
55
+ // ── Legacy formatting (ESC ! / FS !) ────────────────────────────────────────
56
+ // These use the master print-mode byte; kept for backward compatibility.
57
+
58
+ const m_start_bytes = Buffer.from([27, 33, 16, 28, 33, 8]); // Double-height
19
59
  const m_end_bytes = Buffer.from([27, 33, 0, 28, 33, 0]);
20
- const b_start_bytes = Buffer.from([27, 33, 48, 28, 33, 12]);
60
+ const b_start_bytes = Buffer.from([27, 33, 48, 28, 33, 12]); // Double-height + double-width
21
61
  const b_end_bytes = Buffer.from([27, 33, 0, 28, 33, 0]);
62
+ const d_start_bytes = Buffer.from([27, 33, 32, 28, 33, 4]); // Double-width
63
+ const d_end_bytes = Buffer.from([27, 33, 0, 28, 33, 0]);
64
+ const db_start_bytes = Buffer.from([27, 33, 40, 28, 33, 12]); // Double-width + bold emphasis
65
+ const db_end_bytes = Buffer.from([27, 33, 0, 28, 33, 0]);
66
+
67
+ // ── Legacy alignment + size combos ──────────────────────────────────────────
68
+
22
69
  const cm_start_bytes = Buffer.from([27, 97, 1, 27, 33, 16, 28, 33, 8]);
23
70
  const cm_end_bytes = Buffer.from([27, 33, 0, 28, 33, 0]);
24
71
  const cb_start_bytes = Buffer.from([27, 97, 1, 27, 33, 48, 28, 33, 12]);
25
72
  const cb_end_bytes = Buffer.from([27, 33, 0, 28, 33, 0]);
26
73
  const cd_start_bytes = Buffer.from([27, 97, 1, 27, 33, 32, 28, 33, 4]);
27
74
  const cd_end_bytes = Buffer.from([27, 33, 0, 28, 33, 0]);
28
- const d_start_bytes = Buffer.from([27, 33, 32, 28, 33, 4]);
29
- const d_end_bytes = Buffer.from([27, 33, 0, 28, 33, 0]);
30
75
 
31
- const cut_bytes = Buffer.from([27, 105]);
76
+ // ── Underline ───────────────────────────────────────────────────────────────
77
+
78
+ const u_start_bytes = Buffer.from([27, 45, 1]); // ESC - 1 — 1-dot underline
79
+ const u_end_bytes = Buffer.from([27, 45, 0]); // ESC - 0 — underline off
80
+ const u2_start_bytes = Buffer.from([27, 45, 2]); // ESC - 2 — 2-dot (thick) underline
81
+ const u2_end_bytes = Buffer.from([27, 45, 0]);
82
+
83
+ // ── Bold / Emphasis (no size change) ────────────────────────────────────────
84
+
85
+ const bold_start_bytes = Buffer.from([27, 69, 1]); // ESC E 1
86
+ const bold_end_bytes = Buffer.from([27, 69, 0]); // ESC E 0
87
+
88
+ // ── Reverse (white-on-black) ────────────────────────────────────────────────
89
+
90
+ const rev_start_bytes = Buffer.from([29, 66, 1]); // GS B 1
91
+ const rev_end_bytes = Buffer.from([29, 66, 0]); // GS B 0
92
+
93
+ // ── Upside-down ─────────────────────────────────────────────────────────────
94
+
95
+ const updown_start_bytes = Buffer.from([27, 123, 1]); // ESC { 1
96
+ const updown_end_bytes = Buffer.from([27, 123, 0]); // ESC { 0
97
+
98
+ // ── Font selection ──────────────────────────────────────────────────────────
99
+
100
+ const font_a_bytes = Buffer.from([27, 77, 0]); // ESC M 0 — Font A (default, 12×24)
101
+ const font_b_bytes = Buffer.from([27, 77, 1]); // ESC M 1 — Font B (smaller, 9×17)
102
+
103
+ // ── Character size helpers (GS ! n) ─────────────────────────────────────────
104
+ // Width multiplier = bits 0-3 (0–7 → 1×–8×)
105
+ // Height multiplier = bits 4-7 (0–7 → 1×–8×)
106
+
107
+ function sizeBytes(w: number, h: number): Buffer {
108
+ const cw = Math.min(Math.max(w, 1), 8) - 1;
109
+ const ch = Math.min(Math.max(h, 1), 8) - 1;
110
+ return Buffer.from([29, 33, cw | (ch << 4)]); // GS ! n
111
+ }
112
+
113
+ const size_reset_bytes = Buffer.from([29, 33, 0]); // GS ! 0 — 1×1
114
+
115
+ // Pre-built size presets
116
+ const w2_start_bytes = sizeBytes(2, 1); // Width ×2
117
+ const w3_start_bytes = sizeBytes(3, 1); // Width ×3
118
+ const h2_start_bytes = sizeBytes(1, 2); // Height ×2
119
+ const h3_start_bytes = sizeBytes(1, 3); // Height ×3
120
+ const x2_start_bytes = sizeBytes(2, 2); // Width ×2, Height ×2
121
+ const x3_start_bytes = sizeBytes(3, 3); // Width ×3, Height ×3
122
+ const x4_start_bytes = sizeBytes(4, 4); // Width ×4, Height ×4
123
+
124
+ // ── Line spacing ────────────────────────────────────────────────────────────
125
+
126
+ function lineSpacingBytes(n: number): Buffer {
127
+ return Buffer.from([27, 51, Math.min(Math.max(n, 0), 255)]); // ESC 3 n
128
+ }
129
+
130
+ const default_line_spacing_bytes = Buffer.from([27, 50]); // ESC 2
131
+
132
+ // ── Character spacing ───────────────────────────────────────────────────────
133
+
134
+ function charSpacingBytes(n: number): Buffer {
135
+ return Buffer.from([27, 32, Math.min(Math.max(n, 0), 255)]); // ESC SP n
136
+ }
137
+
138
+ // ── Cut / Beep / Tail ───────────────────────────────────────────────────────
139
+
140
+ const cut_bytes = Buffer.from([27, 105]); // ESC i — full cut
141
+ const partial_cut_bytes = Buffer.from([27, 109]); // ESC m — partial cut
32
142
  const beep_bytes = Buffer.from([27, 66, 3, 2]);
33
143
  const line_bytes = Buffer.from([10, 10, 10, 10, 10]);
34
144
 
145
+ // ── Cash drawer ─────────────────────────────────────────────────────────────
146
+
147
+ const drawer_bytes = Buffer.from([27, 112, 0, 60, 120]); // ESC p 0 60 120
148
+
149
+ // ── Encoding ────────────────────────────────────────────────────────────────
150
+
35
151
  const encoding_mappings_bytes: { [key: string]: Buffer } = {
36
152
  // single byte encodings
37
153
  CP437: Buffer.from([27, 116, 0]),
@@ -48,27 +164,130 @@ const options_controller = {
48
164
  encoding: encoding_mappings_bytes,
49
165
  };
50
166
 
167
+ // ── Static tag → bytes map ──────────────────────────────────────────────────
168
+ // Tags are matched longest-first at parse time to avoid prefix collisions.
169
+
51
170
  const controller: { [key: string]: Buffer } = {
171
+ // Legacy formatting
52
172
  "<M>": m_start_bytes,
53
173
  "</M>": m_end_bytes,
54
174
  "<B>": b_start_bytes,
55
175
  "</B>": b_end_bytes,
56
176
  "<D>": d_start_bytes,
57
177
  "</D>": d_end_bytes,
178
+ "<DB>": db_start_bytes,
179
+ "</DB>": db_end_bytes,
180
+
181
+ // Alignment
58
182
  "<C>": c_start_bytes,
59
183
  "</C>": c_end_bytes,
184
+ "<L>": l_start_bytes,
185
+ "</L>": l_end_bytes,
186
+ "<R>": r_start_bytes,
187
+ "</R>": r_end_bytes,
188
+
189
+ // Legacy alignment + size combos
60
190
  "<CM>": cm_start_bytes,
61
191
  "</CM>": cm_end_bytes,
62
192
  "<CD>": cd_start_bytes,
63
193
  "</CD>": cd_end_bytes,
64
194
  "<CB>": cb_start_bytes,
65
195
  "</CB>": cb_end_bytes,
66
- "<L>": l_start_bytes,
67
- "</L>": l_end_bytes,
68
- "<R>": r_start_bytes,
69
- "</R>": r_end_bytes,
196
+
197
+ // Underline
198
+ "<U>": u_start_bytes,
199
+ "</U>": u_end_bytes,
200
+ "<U2>": u2_start_bytes,
201
+ "</U2>": u2_end_bytes,
202
+
203
+ // Bold emphasis only (no size change)
204
+ "<BOLD>": bold_start_bytes,
205
+ "</BOLD>": bold_end_bytes,
206
+
207
+ // Reverse (white on black)
208
+ "<REV>": rev_start_bytes,
209
+ "</REV>": rev_end_bytes,
210
+
211
+ // Upside-down
212
+ "<UPDOWN>": updown_start_bytes,
213
+ "</UPDOWN>": updown_end_bytes,
214
+
215
+ // Font selection
216
+ "<FONT_A>": font_a_bytes,
217
+ "<FONT_B>": font_b_bytes,
218
+
219
+ // Size presets (GS !)
220
+ "<W2>": w2_start_bytes,
221
+ "</W2>": size_reset_bytes,
222
+ "<W3>": w3_start_bytes,
223
+ "</W3>": size_reset_bytes,
224
+ "<H2>": h2_start_bytes,
225
+ "</H2>": size_reset_bytes,
226
+ "<H3>": h3_start_bytes,
227
+ "</H3>": size_reset_bytes,
228
+ "<X2>": x2_start_bytes,
229
+ "</X2>": size_reset_bytes,
230
+ "<X3>": x3_start_bytes,
231
+ "</X3>": size_reset_bytes,
232
+ "<X4>": x4_start_bytes,
233
+ "</X4>": size_reset_bytes,
234
+
235
+ // Close tags for parameterized tags
236
+ "</FS>": size_reset_bytes,
237
+ "</LINESPC>": default_line_spacing_bytes,
238
+ "</CHARSPC>": Buffer.from([27, 32, 0]),
239
+
240
+ // Inline action tags (no closing tag)
241
+ "<PARTCUT>": partial_cut_bytes,
242
+ "<DRAWER>": drawer_bytes,
243
+ "<TAB>": Buffer.from([9]),
244
+ "<RESET>": reset_bytes,
70
245
  };
71
246
 
247
+ // Sort tag keys longest-first so that e.g. "</BOLD>" is tried before "</B>"
248
+ const sorted_tags = Object.keys(controller).sort((a, b) => b.length - a.length);
249
+
250
+ // ── Parameterized tag regexes ───────────────────────────────────────────────
251
+
252
+ const parameterized_tags: Array<{
253
+ regex: RegExp;
254
+ handler: (match: RegExpMatchArray) => Buffer;
255
+ }> = [
256
+ {
257
+ // <FS:W,H> — custom font size (width 1–8, height 1–8)
258
+ regex: /^<FS:(\d+),(\d+)>/,
259
+ handler: (m) => sizeBytes(parseInt(m[1], 10), parseInt(m[2], 10)),
260
+ },
261
+ {
262
+ // <LINESPC:N> — line spacing in dots (0–255)
263
+ regex: /^<LINESPC:(\d+)>/,
264
+ handler: (m) => lineSpacingBytes(parseInt(m[1], 10)),
265
+ },
266
+ {
267
+ // <CHARSPC:N> — character spacing in dots (0–255)
268
+ regex: /^<CHARSPC:(\d+)>/,
269
+ handler: (m) => charSpacingBytes(parseInt(m[1], 10)),
270
+ },
271
+ {
272
+ // <FEED:N> — feed N lines (1–255)
273
+ regex: /^<FEED:(\d+)>/,
274
+ handler: (m) => {
275
+ const n = Math.min(Math.max(parseInt(m[1], 10), 1), 255);
276
+ return Buffer.from([27, 100, n]); // ESC d n
277
+ },
278
+ },
279
+ {
280
+ // <RAW:HH,HH,...> — send raw hex bytes
281
+ regex: /^<RAW:([0-9A-Fa-f,]+)>/,
282
+ handler: (m) => {
283
+ const hexParts = m[1].split(",").filter(Boolean);
284
+ return Buffer.from(hexParts.map((h) => parseInt(h, 16)));
285
+ },
286
+ },
287
+ ];
288
+
289
+ // ── Options ─────────────────────────────────────────────────────────────────
290
+
72
291
  type IOptions = {
73
292
  beep: boolean;
74
293
  cut: boolean;
@@ -83,6 +302,8 @@ const default_options: IOptions = {
83
302
  encoding: "UTF8",
84
303
  };
85
304
 
305
+ // ── Main text → buffer converter ────────────────────────────────────────────
306
+
86
307
  export function exchange_text(text: string, options: IOptions): Buffer {
87
308
  const m_options = options || default_options;
88
309
 
@@ -103,17 +324,42 @@ export function exchange_text(text: string, options: IOptions): Buffer {
103
324
  for (let i = 0; i < text.length; i++) {
104
325
  let ch = text[i];
105
326
  switch (ch) {
106
- case "<":
327
+ case "<": {
107
328
  bytes.concat(iconv.encode(temp, m_options.encoding));
108
329
  temp = "";
109
- // add bytes for changing font and justifying text
110
- for (const tag in controller) {
111
- if (text.substring(i, i + tag.length) === tag) {
330
+
331
+ const remaining = text.substring(i);
332
+
333
+ // 1. Try static tags (longest-first to avoid prefix collisions)
334
+ let matched = false;
335
+ for (const tag of sorted_tags) {
336
+ if (remaining.startsWith(tag)) {
112
337
  bytes.concat(controller[tag]);
113
338
  i += tag.length - 1;
339
+ matched = true;
340
+ break;
341
+ }
342
+ }
343
+
344
+ // 2. Try parameterized tags
345
+ if (!matched) {
346
+ for (const pt of parameterized_tags) {
347
+ const m = remaining.match(pt.regex);
348
+ if (m) {
349
+ bytes.concat(pt.handler(m));
350
+ i += m[0].length - 1;
351
+ matched = true;
352
+ break;
353
+ }
114
354
  }
115
355
  }
356
+
357
+ // 3. Not a known tag — keep the literal '<'
358
+ if (!matched) {
359
+ temp = "<";
360
+ }
116
361
  break;
362
+ }
117
363
  case "\n":
118
364
  temp = `${temp}${ch}`;
119
365
  bytes.concat(iconv.encode(temp, m_options.encoding));
@@ -148,6 +394,10 @@ export function exchange_text(text: string, options: IOptions): Buffer {
148
394
  return bytes.toBuffer();
149
395
  }
150
396
 
397
+ // ── Public helpers (re-exported for advanced usage) ─────────────────────────
398
+
399
+ export { sizeBytes, lineSpacingBytes, charSpacingBytes };
400
+
151
401
  // export async function exchange_image(
152
402
  // imagePath: string,
153
403
  // threshold: number