@tbsoft-gmbh/signature-sdk 1.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.
@@ -0,0 +1,531 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/png-decode.ts
34
+ var png_decode_exports = {};
35
+ __export(png_decode_exports, {
36
+ decodePng: () => decodePng
37
+ });
38
+ function decodePng(buf) {
39
+ if (buf[0] !== 137 || buf[1] !== 80 || buf[2] !== 78 || buf[3] !== 71) {
40
+ return null;
41
+ }
42
+ let offset = 8;
43
+ let width = 0, height = 0, bitDepth = 0, colorType = 0;
44
+ const idatChunks = [];
45
+ while (offset < buf.length) {
46
+ const chunkLen = buf.readUInt32BE(offset);
47
+ const chunkType = buf.toString("ascii", offset + 4, offset + 8);
48
+ if (chunkType === "IHDR") {
49
+ width = buf.readUInt32BE(offset + 8);
50
+ height = buf.readUInt32BE(offset + 12);
51
+ bitDepth = buf[offset + 16];
52
+ colorType = buf[offset + 17];
53
+ } else if (chunkType === "IDAT") {
54
+ idatChunks.push(buf.slice(offset + 8, offset + 8 + chunkLen));
55
+ } else if (chunkType === "IEND") {
56
+ break;
57
+ }
58
+ offset += 12 + chunkLen;
59
+ }
60
+ if (width === 0 || height === 0 || idatChunks.length === 0) return null;
61
+ let channels;
62
+ switch (colorType) {
63
+ case 0:
64
+ channels = 1;
65
+ break;
66
+ // Grayscale
67
+ case 2:
68
+ channels = 3;
69
+ break;
70
+ // RGB
71
+ case 4:
72
+ channels = 2;
73
+ break;
74
+ // Grayscale + Alpha
75
+ case 6:
76
+ channels = 4;
77
+ break;
78
+ // RGBA
79
+ default:
80
+ return null;
81
+ }
82
+ const compressed = Buffer.concat(idatChunks);
83
+ let decompressed;
84
+ try {
85
+ decompressed = import_zlib.default.inflateSync(compressed);
86
+ } catch {
87
+ return null;
88
+ }
89
+ const bytesPerPixel = channels * (bitDepth / 8);
90
+ const stride = width * bytesPerPixel;
91
+ const pixels = Buffer.alloc(height * stride);
92
+ let srcOffset = 0;
93
+ for (let y = 0; y < height; y++) {
94
+ const filterType = decompressed[srcOffset++];
95
+ const rowStart = y * stride;
96
+ const prevRowStart = (y - 1) * stride;
97
+ for (let x = 0; x < stride; x++) {
98
+ const raw = decompressed[srcOffset++];
99
+ let value;
100
+ switch (filterType) {
101
+ case 0:
102
+ value = raw;
103
+ break;
104
+ case 1:
105
+ value = raw + (x >= bytesPerPixel ? pixels[rowStart + x - bytesPerPixel] : 0);
106
+ break;
107
+ case 2:
108
+ value = raw + (y > 0 ? pixels[prevRowStart + x] : 0);
109
+ break;
110
+ case 3:
111
+ const left = x >= bytesPerPixel ? pixels[rowStart + x - bytesPerPixel] : 0;
112
+ const up = y > 0 ? pixels[prevRowStart + x] : 0;
113
+ value = raw + Math.floor((left + up) / 2);
114
+ break;
115
+ case 4:
116
+ const a = x >= bytesPerPixel ? pixels[rowStart + x - bytesPerPixel] : 0;
117
+ const b = y > 0 ? pixels[prevRowStart + x] : 0;
118
+ const c = x >= bytesPerPixel && y > 0 ? pixels[prevRowStart + x - bytesPerPixel] : 0;
119
+ value = raw + paethPredictor(a, b, c);
120
+ break;
121
+ default:
122
+ value = raw;
123
+ }
124
+ pixels[rowStart + x] = value & 255;
125
+ }
126
+ }
127
+ return { width, height, channels, data: pixels };
128
+ }
129
+ function paethPredictor(a, b, c) {
130
+ const p = a + b - c;
131
+ const pa = Math.abs(p - a);
132
+ const pb = Math.abs(p - b);
133
+ const pc = Math.abs(p - c);
134
+ if (pa <= pb && pa <= pc) return a;
135
+ if (pb <= pc) return b;
136
+ return c;
137
+ }
138
+ var import_zlib;
139
+ var init_png_decode = __esm({
140
+ "src/png-decode.ts"() {
141
+ "use strict";
142
+ import_zlib = __toESM(require("zlib"));
143
+ }
144
+ });
145
+
146
+ // src/hid-display.ts
147
+ var hid_display_exports = {};
148
+ __export(hid_display_exports, {
149
+ DisplayRenderer: () => DisplayRenderer,
150
+ fontCharWidth: () => fontCharWidth,
151
+ fontLineHeight: () => fontLineHeight
152
+ });
153
+ module.exports = __toCommonJS(hid_display_exports);
154
+ var import_fs = __toESM(require("fs"));
155
+
156
+ // src/font-8x13.ts
157
+ var FONT_8X13 = {
158
+ // ─── Space ───────────────────────────────────────────────────────────────
159
+ " ": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
160
+ // ─── Digits ──────────────────────────────────────────────────────────────
161
+ "0": [0, 0, 60, 102, 110, 118, 102, 102, 102, 60, 0, 0, 0],
162
+ "1": [0, 0, 24, 56, 24, 24, 24, 24, 24, 126, 0, 0, 0],
163
+ "2": [0, 0, 60, 102, 6, 12, 24, 48, 96, 126, 0, 0, 0],
164
+ "3": [0, 0, 60, 102, 6, 28, 6, 6, 102, 60, 0, 0, 0],
165
+ "4": [0, 0, 12, 28, 44, 76, 126, 12, 12, 12, 0, 0, 0],
166
+ "5": [0, 0, 126, 96, 96, 124, 6, 6, 102, 60, 0, 0, 0],
167
+ "6": [0, 0, 28, 48, 96, 124, 102, 102, 102, 60, 0, 0, 0],
168
+ "7": [0, 0, 126, 6, 12, 24, 24, 48, 48, 48, 0, 0, 0],
169
+ "8": [0, 0, 60, 102, 102, 60, 102, 102, 102, 60, 0, 0, 0],
170
+ "9": [0, 0, 60, 102, 102, 102, 62, 6, 12, 56, 0, 0, 0],
171
+ // ─── Uppercase A–Z ───────────────────────────────────────────────────────
172
+ "A": [0, 0, 24, 60, 102, 102, 126, 102, 102, 102, 0, 0, 0],
173
+ "B": [0, 0, 124, 102, 102, 124, 102, 102, 102, 124, 0, 0, 0],
174
+ "C": [0, 0, 60, 102, 96, 96, 96, 96, 102, 60, 0, 0, 0],
175
+ "D": [0, 0, 120, 108, 102, 102, 102, 102, 108, 120, 0, 0, 0],
176
+ "E": [0, 0, 126, 96, 96, 124, 96, 96, 96, 126, 0, 0, 0],
177
+ "F": [0, 0, 126, 96, 96, 124, 96, 96, 96, 96, 0, 0, 0],
178
+ "G": [0, 0, 60, 102, 96, 96, 110, 102, 102, 62, 0, 0, 0],
179
+ "H": [0, 0, 102, 102, 102, 126, 102, 102, 102, 102, 0, 0, 0],
180
+ "I": [0, 0, 60, 24, 24, 24, 24, 24, 24, 60, 0, 0, 0],
181
+ "J": [0, 0, 30, 6, 6, 6, 6, 6, 102, 60, 0, 0, 0],
182
+ "K": [0, 0, 102, 108, 120, 112, 120, 108, 102, 102, 0, 0, 0],
183
+ "L": [0, 0, 96, 96, 96, 96, 96, 96, 96, 126, 0, 0, 0],
184
+ "M": [0, 0, 66, 102, 126, 126, 90, 66, 66, 66, 0, 0, 0],
185
+ "N": [0, 0, 98, 114, 122, 110, 102, 102, 102, 102, 0, 0, 0],
186
+ "O": [0, 0, 60, 102, 102, 102, 102, 102, 102, 60, 0, 0, 0],
187
+ "P": [0, 0, 124, 102, 102, 102, 124, 96, 96, 96, 0, 0, 0],
188
+ "Q": [0, 0, 60, 102, 102, 102, 102, 118, 108, 54, 0, 0, 0],
189
+ "R": [0, 0, 124, 102, 102, 124, 120, 108, 102, 102, 0, 0, 0],
190
+ "S": [0, 0, 60, 102, 96, 56, 12, 6, 102, 60, 0, 0, 0],
191
+ "T": [0, 0, 126, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0],
192
+ "U": [0, 0, 102, 102, 102, 102, 102, 102, 102, 60, 0, 0, 0],
193
+ "V": [0, 0, 102, 102, 102, 102, 102, 60, 24, 24, 0, 0, 0],
194
+ "W": [0, 0, 66, 66, 66, 90, 90, 126, 102, 66, 0, 0, 0],
195
+ "X": [0, 0, 102, 102, 60, 24, 60, 102, 102, 102, 0, 0, 0],
196
+ "Y": [0, 0, 102, 102, 102, 60, 24, 24, 24, 24, 0, 0, 0],
197
+ "Z": [0, 0, 126, 6, 12, 24, 48, 96, 96, 126, 0, 0, 0],
198
+ // ─── Lowercase a–z ───────────────────────────────────────────────────────
199
+ // Baseline row = index 9 (row 10). Descenders use indices 10-11.
200
+ "a": [0, 0, 0, 0, 60, 6, 62, 102, 102, 62, 0, 0, 0],
201
+ "b": [0, 0, 96, 96, 124, 102, 102, 102, 102, 124, 0, 0, 0],
202
+ "c": [0, 0, 0, 0, 60, 102, 96, 96, 102, 60, 0, 0, 0],
203
+ "d": [0, 0, 6, 6, 62, 102, 102, 102, 102, 62, 0, 0, 0],
204
+ "e": [0, 0, 0, 0, 60, 102, 126, 96, 102, 60, 0, 0, 0],
205
+ "f": [0, 0, 14, 24, 24, 126, 24, 24, 24, 24, 0, 0, 0],
206
+ "g": [0, 0, 0, 0, 62, 102, 102, 102, 62, 6, 6, 60, 0],
207
+ "h": [0, 0, 96, 96, 124, 102, 102, 102, 102, 102, 0, 0, 0],
208
+ "i": [0, 0, 24, 0, 56, 24, 24, 24, 24, 60, 0, 0, 0],
209
+ "j": [0, 0, 6, 0, 14, 6, 6, 6, 6, 6, 70, 60, 0],
210
+ "k": [0, 0, 96, 96, 102, 108, 120, 124, 108, 102, 0, 0, 0],
211
+ "l": [0, 0, 56, 24, 24, 24, 24, 24, 24, 14, 0, 0, 0],
212
+ "m": [0, 0, 0, 0, 108, 126, 126, 106, 98, 98, 0, 0, 0],
213
+ "n": [0, 0, 0, 0, 124, 102, 102, 102, 102, 102, 0, 0, 0],
214
+ "o": [0, 0, 0, 0, 60, 102, 102, 102, 102, 60, 0, 0, 0],
215
+ "p": [0, 0, 0, 0, 124, 102, 102, 102, 124, 96, 96, 96, 0],
216
+ "q": [0, 0, 0, 0, 62, 102, 102, 102, 62, 6, 6, 6, 0],
217
+ "r": [0, 0, 0, 0, 108, 118, 96, 96, 96, 96, 0, 0, 0],
218
+ "s": [0, 0, 0, 0, 60, 102, 56, 12, 102, 60, 0, 0, 0],
219
+ "t": [0, 0, 24, 24, 126, 24, 24, 24, 24, 14, 0, 0, 0],
220
+ "u": [0, 0, 0, 0, 102, 102, 102, 102, 102, 62, 0, 0, 0],
221
+ "v": [0, 0, 0, 0, 102, 102, 102, 60, 60, 24, 0, 0, 0],
222
+ "w": [0, 0, 0, 0, 66, 66, 90, 90, 126, 36, 0, 0, 0],
223
+ "x": [0, 0, 0, 0, 102, 102, 60, 60, 102, 102, 0, 0, 0],
224
+ "y": [0, 0, 0, 0, 102, 102, 102, 62, 6, 6, 70, 60, 0],
225
+ "z": [0, 0, 0, 0, 126, 12, 24, 48, 96, 126, 0, 0, 0],
226
+ // ─── Punctuation ─────────────────────────────────────────────────────────
227
+ ".": [0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 0, 0, 0],
228
+ ",": [0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 8, 16, 0],
229
+ ":": [0, 0, 0, 0, 24, 24, 0, 0, 24, 24, 0, 0, 0],
230
+ ";": [0, 0, 0, 0, 24, 24, 0, 0, 24, 24, 8, 16, 0],
231
+ "!": [0, 0, 24, 24, 24, 24, 24, 24, 0, 24, 0, 0, 0],
232
+ "?": [0, 0, 60, 102, 6, 12, 24, 24, 0, 24, 0, 0, 0],
233
+ "-": [0, 0, 0, 0, 0, 0, 126, 0, 0, 0, 0, 0, 0],
234
+ "+": [0, 0, 0, 0, 24, 24, 126, 24, 24, 0, 0, 0, 0],
235
+ "=": [0, 0, 0, 0, 0, 126, 0, 126, 0, 0, 0, 0, 0],
236
+ "(": [0, 0, 12, 24, 48, 48, 48, 48, 24, 12, 0, 0, 0],
237
+ ")": [0, 0, 48, 24, 12, 12, 12, 12, 24, 48, 0, 0, 0],
238
+ "/": [0, 0, 2, 6, 12, 24, 48, 96, 64, 0, 0, 0, 0],
239
+ "\\": [0, 0, 64, 96, 48, 24, 12, 6, 2, 0, 0, 0, 0],
240
+ "'": [0, 24, 24, 16, 32, 0, 0, 0, 0, 0, 0, 0, 0],
241
+ '"': [0, 102, 102, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0],
242
+ "_": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 0, 0],
243
+ "@": [0, 0, 60, 102, 110, 106, 110, 96, 98, 60, 0, 0, 0],
244
+ "#": [0, 0, 36, 36, 126, 36, 36, 126, 36, 36, 0, 0, 0],
245
+ "&": [0, 0, 56, 108, 108, 56, 118, 206, 204, 118, 0, 0, 0],
246
+ "%": [0, 0, 98, 102, 12, 24, 48, 102, 70, 0, 0, 0, 0],
247
+ "*": [0, 0, 0, 102, 60, 255, 60, 102, 0, 0, 0, 0, 0],
248
+ "<": [0, 0, 0, 6, 28, 112, 112, 28, 6, 0, 0, 0, 0],
249
+ ">": [0, 0, 0, 96, 56, 14, 14, 56, 96, 0, 0, 0, 0],
250
+ "[": [0, 0, 60, 48, 48, 48, 48, 48, 48, 60, 0, 0, 0],
251
+ "]": [0, 0, 60, 12, 12, 12, 12, 12, 12, 60, 0, 0, 0],
252
+ "{": [0, 0, 14, 24, 24, 48, 24, 24, 24, 14, 0, 0, 0],
253
+ "}": [0, 0, 112, 24, 24, 12, 24, 24, 24, 112, 0, 0, 0],
254
+ "|": [0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0],
255
+ "~": [0, 0, 0, 0, 0, 114, 156, 0, 0, 0, 0, 0, 0],
256
+ "^": [0, 24, 60, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0],
257
+ "`": [0, 24, 12, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0],
258
+ // ─── German characters ────────────────────────────────────────────────────
259
+ // Umlauts: dots sit at rows 0-1, letter body follows below
260
+ "\xE4": [0, 36, 0, 0, 60, 6, 62, 102, 102, 62, 0, 0, 0],
261
+ "\xF6": [0, 36, 0, 0, 60, 102, 102, 102, 102, 60, 0, 0, 0],
262
+ "\xFC": [0, 36, 0, 0, 102, 102, 102, 102, 102, 62, 0, 0, 0],
263
+ "\xC4": [36, 0, 24, 60, 102, 102, 126, 102, 102, 102, 0, 0, 0],
264
+ "\xD6": [36, 0, 60, 102, 102, 102, 102, 102, 102, 60, 0, 0, 0],
265
+ "\xDC": [36, 0, 102, 102, 102, 102, 102, 102, 102, 60, 0, 0, 0],
266
+ // ß: sharp-s / eszett
267
+ "\xDF": [0, 0, 60, 102, 102, 124, 102, 102, 102, 124, 96, 96, 0],
268
+ // ─── Currency / special symbols ──────────────────────────────────────────
269
+ // € (Euro): slightly narrower than full width, classic double-bar C shape
270
+ "\u20AC": [0, 0, 30, 48, 124, 48, 124, 48, 48, 30, 0, 0, 0]
271
+ };
272
+
273
+ // src/hid-display.ts
274
+ function fontScale(fontSize) {
275
+ return Math.max(1, Math.min(3, Math.round(fontSize)));
276
+ }
277
+ function fontLineHeight(fontSize) {
278
+ return 13 * fontScale(fontSize) + 2;
279
+ }
280
+ function fontCharWidth(fontSize) {
281
+ return 9 * fontScale(fontSize);
282
+ }
283
+ var DisplayRenderer = class {
284
+ // 1bpp, row-aligned to bytes
285
+ constructor(width, height) {
286
+ this.width = width;
287
+ this.height = height;
288
+ const rowBytes = Math.ceil(width / 8);
289
+ this.framebuffer = Buffer.alloc(rowBytes * height, 255);
290
+ }
291
+ get buffer() {
292
+ return this.framebuffer;
293
+ }
294
+ clear() {
295
+ this.framebuffer.fill(255);
296
+ }
297
+ /**
298
+ * Set a pixel (0=black, 1=white) in the framebuffer.
299
+ */
300
+ setPixel(x, y, black) {
301
+ if (x < 0 || x >= this.width || y < 0 || y >= this.height) return;
302
+ const rowBytes = Math.ceil(this.width / 8);
303
+ const byteIndex = y * rowBytes + Math.floor(x / 8);
304
+ const bitIndex = 7 - x % 8;
305
+ if (black) {
306
+ this.framebuffer[byteIndex] &= ~(1 << bitIndex);
307
+ } else {
308
+ this.framebuffer[byteIndex] |= 1 << bitIndex;
309
+ }
310
+ }
311
+ /**
312
+ * Draw a filled rectangle.
313
+ */
314
+ fillRect(x, y, w, h, black) {
315
+ for (let dy = 0; dy < h; dy++) {
316
+ for (let dx = 0; dx < w; dx++) {
317
+ this.setPixel(x + dx, y + dy, black);
318
+ }
319
+ }
320
+ }
321
+ /**
322
+ * Draw a horizontal line.
323
+ */
324
+ hLine(x, y, w) {
325
+ for (let dx = 0; dx < w; dx++) {
326
+ this.setPixel(x + dx, y, true);
327
+ }
328
+ }
329
+ /**
330
+ * Draw text using the 8x13 bitmap font.
331
+ * fontSize: 1=normal(8x13), 2=large(16x26), 3=headline(24x39)
332
+ * bold: draws text twice with 1px right shift for thicker strokes
333
+ */
334
+ drawText(x, y, text, fontSize = 1, bold = false) {
335
+ const w = this._renderText(x, y, text, fontSize, true);
336
+ if (bold) this._renderText(x + 1, y, text, fontSize, true);
337
+ return w;
338
+ }
339
+ /** Draw inverted text (white on black) for button labels. */
340
+ drawTextInverted(x, y, text, fontSize = 1, bold = false) {
341
+ const w = this._renderText(x, y, text, fontSize, false);
342
+ if (bold) this._renderText(x + 1, y, text, fontSize, false);
343
+ return w;
344
+ }
345
+ _renderText(x, y, text, fontSize, black) {
346
+ const cleanText = text.replace(/[\u200B-\u200F\u2028-\u202F\u2060-\u206F\uFEFF]/g, "");
347
+ const scale = fontScale(fontSize);
348
+ const charW = 9 * scale;
349
+ let curX = x;
350
+ for (const char of cleanText) {
351
+ const glyph = FONT_8X13[char] || FONT_8X13["?"];
352
+ if (!glyph) continue;
353
+ for (let row = 0; row < 13; row++) {
354
+ for (let col = 0; col < 8; col++) {
355
+ if (glyph[row] & 1 << 7 - col) {
356
+ for (let sy = 0; sy < scale; sy++) {
357
+ for (let sx = 0; sx < scale; sx++) {
358
+ this.setPixel(curX + col * scale + sx, y + row * scale + sy, black);
359
+ }
360
+ }
361
+ }
362
+ }
363
+ }
364
+ curX += charW;
365
+ }
366
+ return curX - x;
367
+ }
368
+ /**
369
+ * Draw text fitted within a rectangle. Wraps words and scales to fit.
370
+ */
371
+ drawTextInRect(left, top, width, height, text, fontSize = 1) {
372
+ const charW = fontCharWidth(fontSize);
373
+ const charH = fontLineHeight(fontSize);
374
+ const maxCols = Math.floor(width / charW);
375
+ const maxRows = Math.floor(height / charH);
376
+ const words = text.split(" ");
377
+ const lines = [];
378
+ let currentLine = "";
379
+ for (const word of words) {
380
+ if (currentLine.length + word.length + 1 <= maxCols) {
381
+ currentLine += (currentLine ? " " : "") + word;
382
+ } else {
383
+ if (currentLine) lines.push(currentLine);
384
+ currentLine = word;
385
+ }
386
+ }
387
+ if (currentLine) lines.push(currentLine);
388
+ for (let i = 0; i < Math.min(lines.length, maxRows); i++) {
389
+ this.drawText(left, top + i * charH, lines[i], fontSize);
390
+ }
391
+ }
392
+ /**
393
+ * Load a BMP file and draw it at the specified position.
394
+ * Supports 1-bit, 8-bit, and 24-bit BMPs.
395
+ */
396
+ drawBmpFile(x, y, filePath) {
397
+ try {
398
+ const data = import_fs.default.readFileSync(filePath);
399
+ return this.drawBmpData(x, y, data);
400
+ } catch {
401
+ return false;
402
+ }
403
+ }
404
+ /**
405
+ * Load a PNG file and draw it at the specified position.
406
+ * Uses a minimal PNG decoder (no external dependencies).
407
+ */
408
+ drawPngFile(x, y, filePath) {
409
+ try {
410
+ const data = import_fs.default.readFileSync(filePath);
411
+ return this.drawImageData(x, y, data, filePath);
412
+ } catch {
413
+ return false;
414
+ }
415
+ }
416
+ /**
417
+ * Draw image data. Detects format from header.
418
+ */
419
+ drawImageData(x, y, data, hint = "") {
420
+ if (data[0] === 66 && data[1] === 77) {
421
+ return this.drawBmpData(x, y, data);
422
+ }
423
+ if (data[0] === 137 && data[1] === 80 && data[2] === 78 && data[3] === 71) {
424
+ return this.drawPngData(x, y, data);
425
+ }
426
+ return false;
427
+ }
428
+ drawBmpData(x, y, data) {
429
+ if (data.length < 54) return false;
430
+ const dataOffset = data.readUInt32LE(10);
431
+ const bmpWidth = data.readInt32LE(18);
432
+ const bmpHeight = data.readInt32LE(22);
433
+ const bitsPerPixel = data.readUInt16LE(28);
434
+ const isBottomUp = bmpHeight > 0;
435
+ const absHeight = Math.abs(bmpHeight);
436
+ for (let row = 0; row < absHeight; row++) {
437
+ const srcRow = isBottomUp ? absHeight - 1 - row : row;
438
+ const dstY = y + row;
439
+ if (bitsPerPixel === 1) {
440
+ const rowBytes = Math.ceil(bmpWidth / 8);
441
+ const paddedRowBytes = Math.ceil(rowBytes / 4) * 4;
442
+ const rowOffset = dataOffset + srcRow * paddedRowBytes;
443
+ for (let col = 0; col < bmpWidth; col++) {
444
+ const byteIdx = rowOffset + Math.floor(col / 8);
445
+ const bitIdx = 7 - col % 8;
446
+ const pixel = data[byteIdx] >> bitIdx & 1;
447
+ this.setPixel(x + col, dstY, pixel === 0);
448
+ }
449
+ } else if (bitsPerPixel === 24) {
450
+ const paddedRowBytes = Math.ceil(bmpWidth * 3 / 4) * 4;
451
+ const rowOffset = dataOffset + srcRow * paddedRowBytes;
452
+ for (let col = 0; col < bmpWidth; col++) {
453
+ const idx = rowOffset + col * 3;
454
+ const b = data[idx];
455
+ const g = data[idx + 1];
456
+ const r = data[idx + 2];
457
+ const brightness = (r * 299 + g * 587 + b * 114) / 1e3;
458
+ this.setPixel(x + col, dstY, brightness < 128);
459
+ }
460
+ } else if (bitsPerPixel === 8) {
461
+ const paddedRowBytes = Math.ceil(bmpWidth / 4) * 4;
462
+ const rowOffset = dataOffset + srcRow * paddedRowBytes;
463
+ for (let col = 0; col < bmpWidth; col++) {
464
+ const pixel = data[rowOffset + col];
465
+ this.setPixel(x + col, dstY, pixel < 128);
466
+ }
467
+ }
468
+ }
469
+ return true;
470
+ }
471
+ drawPngData(x, y, data) {
472
+ try {
473
+ const { decodePng: decodePng2 } = (init_png_decode(), __toCommonJS(png_decode_exports));
474
+ const pixels = decodePng2(data);
475
+ if (!pixels) return false;
476
+ for (let row = 0; row < pixels.height; row++) {
477
+ for (let col = 0; col < pixels.width; col++) {
478
+ const idx = (row * pixels.width + col) * pixels.channels;
479
+ let brightness;
480
+ if (pixels.channels >= 3) {
481
+ brightness = (pixels.data[idx] * 299 + pixels.data[idx + 1] * 587 + pixels.data[idx + 2] * 114) / 1e3;
482
+ } else {
483
+ brightness = pixels.data[idx];
484
+ }
485
+ if (pixels.channels === 4 || pixels.channels === 2) {
486
+ const alpha = pixels.data[idx + pixels.channels - 1];
487
+ if (alpha < 128) continue;
488
+ }
489
+ this.setPixel(x + col, y + row, brightness < 128);
490
+ }
491
+ }
492
+ return true;
493
+ } catch {
494
+ return false;
495
+ }
496
+ }
497
+ /**
498
+ * Send the entire framebuffer to the pad.
499
+ */
500
+ async sendToDevice(protocol) {
501
+ return protocol.drawImage(0, 0, this.width, this.height, this.framebuffer);
502
+ }
503
+ /**
504
+ * Send a region of the framebuffer to the pad.
505
+ */
506
+ async sendRegionToDevice(protocol, x, y, w, h) {
507
+ const rowBytes = Math.ceil(this.width / 8);
508
+ const regionRowBytes = Math.ceil(w / 8);
509
+ const regionBuf = Buffer.alloc(regionRowBytes * h, 255);
510
+ for (let row = 0; row < h; row++) {
511
+ for (let col = 0; col < w; col++) {
512
+ const srcByteIdx = (y + row) * rowBytes + Math.floor((x + col) / 8);
513
+ const srcBitIdx = 7 - (x + col) % 8;
514
+ const pixel = this.framebuffer[srcByteIdx] >> srcBitIdx & 1;
515
+ const dstByteIdx = row * regionRowBytes + Math.floor(col / 8);
516
+ const dstBitIdx = 7 - col % 8;
517
+ if (pixel === 0) {
518
+ regionBuf[dstByteIdx] &= ~(1 << dstBitIdx);
519
+ }
520
+ }
521
+ }
522
+ return protocol.drawImage(x, y, w, h, regionBuf);
523
+ }
524
+ };
525
+ // Annotate the CommonJS export names for ESM import in node:
526
+ 0 && (module.exports = {
527
+ DisplayRenderer,
528
+ fontCharWidth,
529
+ fontLineHeight
530
+ });
531
+ //# sourceMappingURL=hid-display.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/png-decode.ts","../src/hid-display.ts","../src/font-8x13.ts"],"sourcesContent":["/**\r\n * Minimal PNG decoder using only Node.js built-in zlib.\r\n * Supports 8-bit RGB, RGBA, Grayscale, and Grayscale+Alpha PNGs.\r\n * No external dependencies.\r\n */\r\n\r\nimport zlib from \"zlib\";\r\n\r\nexport interface DecodedPng {\r\n width: number;\r\n height: number;\r\n channels: number; // 1=gray, 2=gray+alpha, 3=RGB, 4=RGBA\r\n data: Buffer; // Raw pixel data, row-major, channels interleaved\r\n}\r\n\r\nexport function decodePng(buf: Buffer): DecodedPng | null {\r\n // Verify PNG signature\r\n if (buf[0] !== 0x89 || buf[1] !== 0x50 || buf[2] !== 0x4E || buf[3] !== 0x47) {\r\n return null;\r\n }\r\n\r\n let offset = 8; // Skip signature\r\n let width = 0, height = 0, bitDepth = 0, colorType = 0;\r\n const idatChunks: Buffer[] = [];\r\n\r\n // Parse chunks\r\n while (offset < buf.length) {\r\n const chunkLen = buf.readUInt32BE(offset);\r\n const chunkType = buf.toString(\"ascii\", offset + 4, offset + 8);\r\n\r\n if (chunkType === \"IHDR\") {\r\n width = buf.readUInt32BE(offset + 8);\r\n height = buf.readUInt32BE(offset + 12);\r\n bitDepth = buf[offset + 16];\r\n colorType = buf[offset + 17];\r\n } else if (chunkType === \"IDAT\") {\r\n idatChunks.push(buf.slice(offset + 8, offset + 8 + chunkLen));\r\n } else if (chunkType === \"IEND\") {\r\n break;\r\n }\r\n\r\n offset += 12 + chunkLen; // 4 len + 4 type + data + 4 crc\r\n }\r\n\r\n if (width === 0 || height === 0 || idatChunks.length === 0) return null;\r\n\r\n // Determine channels from color type\r\n let channels: number;\r\n switch (colorType) {\r\n case 0: channels = 1; break; // Grayscale\r\n case 2: channels = 3; break; // RGB\r\n case 4: channels = 2; break; // Grayscale + Alpha\r\n case 6: channels = 4; break; // RGBA\r\n default: return null; // Indexed/other not supported\r\n }\r\n\r\n // Decompress IDAT data\r\n const compressed = Buffer.concat(idatChunks);\r\n let decompressed: Buffer;\r\n try {\r\n decompressed = zlib.inflateSync(compressed);\r\n } catch {\r\n return null;\r\n }\r\n\r\n // Apply PNG filtering (each row has a filter byte prefix)\r\n const bytesPerPixel = channels * (bitDepth / 8);\r\n const stride = width * bytesPerPixel;\r\n const pixels = Buffer.alloc(height * stride);\r\n\r\n let srcOffset = 0;\r\n for (let y = 0; y < height; y++) {\r\n const filterType = decompressed[srcOffset++];\r\n const rowStart = y * stride;\r\n const prevRowStart = (y - 1) * stride;\r\n\r\n for (let x = 0; x < stride; x++) {\r\n const raw = decompressed[srcOffset++];\r\n let value: number;\r\n\r\n switch (filterType) {\r\n case 0: // None\r\n value = raw;\r\n break;\r\n case 1: // Sub\r\n value = raw + (x >= bytesPerPixel ? pixels[rowStart + x - bytesPerPixel] : 0);\r\n break;\r\n case 2: // Up\r\n value = raw + (y > 0 ? pixels[prevRowStart + x] : 0);\r\n break;\r\n case 3: // Average\r\n const left = x >= bytesPerPixel ? pixels[rowStart + x - bytesPerPixel] : 0;\r\n const up = y > 0 ? pixels[prevRowStart + x] : 0;\r\n value = raw + Math.floor((left + up) / 2);\r\n break;\r\n case 4: // Paeth\r\n const a = x >= bytesPerPixel ? pixels[rowStart + x - bytesPerPixel] : 0;\r\n const b = y > 0 ? pixels[prevRowStart + x] : 0;\r\n const c = (x >= bytesPerPixel && y > 0) ? pixels[prevRowStart + x - bytesPerPixel] : 0;\r\n value = raw + paethPredictor(a, b, c);\r\n break;\r\n default:\r\n value = raw;\r\n }\r\n\r\n pixels[rowStart + x] = value & 0xFF;\r\n }\r\n }\r\n\r\n return { width, height, channels, data: pixels };\r\n}\r\n\r\nfunction paethPredictor(a: number, b: number, c: number): number {\r\n const p = a + b - c;\r\n const pa = Math.abs(p - a);\r\n const pb = Math.abs(p - b);\r\n const pc = Math.abs(p - c);\r\n if (pa <= pb && pa <= pc) return a;\r\n if (pb <= pc) return b;\r\n return c;\r\n}\r\n","/**\r\n * Display rendering for Signotec pads via HID.\r\n * Renders text and images into monochrome bitmaps, then sends them to the pad.\r\n *\r\n * The Signotec pad does NOT accept text commands directly.\r\n * Text is rendered client-side into bitmaps, then transferred as image data.\r\n */\r\n\r\nimport fs from \"fs\";\r\nimport path from \"path\";\r\nimport type { HidProtocol } from \"./hid-protocol\";\r\nimport { FONT_8X13 } from \"./font-8x13\";\r\n\r\n/**\r\n * Font sizing:\r\n * fontSize 1 = 8x13 at 1x (compact, ~35 chars/line, matches DLL \"Arial 30\")\r\n * fontSize 2 = 8x13 at 2x (large, ~17 chars/line)\r\n * fontSize 3 = 8x13 at 3x (headline, ~11 chars/line)\r\n *\r\n * The 8x13 font closely matches the DLL's Pango-rendered \"Arial 30\"\r\n * which produces ~15px characters (30 * 10/20.5 ≈ 14.6px).\r\n */\r\nfunction fontScale(fontSize: number): number {\r\n return Math.max(1, Math.min(3, Math.round(fontSize)));\r\n}\r\n\r\n/** Returns the line height (char height + gap) for a fontSize. */\r\nexport function fontLineHeight(fontSize: number): number {\r\n return 13 * fontScale(fontSize) + 2;\r\n}\r\n\r\n/** Returns the character width (including 1px gap) for a fontSize. */\r\nexport function fontCharWidth(fontSize: number): number {\r\n return 9 * fontScale(fontSize);\r\n}\r\n\r\n/**\r\n * Render text into a 1bpp monochrome bitmap suitable for the pad display.\r\n * Uses a built-in bitmap font (no canvas dependency).\r\n */\r\nexport class DisplayRenderer {\r\n private width: number;\r\n private height: number;\r\n private framebuffer: Buffer; // 1bpp, row-aligned to bytes\r\n\r\n constructor(width: number, height: number) {\r\n this.width = width;\r\n this.height = height;\r\n const rowBytes = Math.ceil(width / 8);\r\n this.framebuffer = Buffer.alloc(rowBytes * height, 0xFF); // White background (1=white in monochrome)\r\n }\r\n\r\n get buffer(): Buffer {\r\n return this.framebuffer;\r\n }\r\n\r\n clear(): void {\r\n this.framebuffer.fill(0xFF); // All white\r\n }\r\n\r\n /**\r\n * Set a pixel (0=black, 1=white) in the framebuffer.\r\n */\r\n setPixel(x: number, y: number, black: boolean): void {\r\n if (x < 0 || x >= this.width || y < 0 || y >= this.height) return;\r\n const rowBytes = Math.ceil(this.width / 8);\r\n const byteIndex = y * rowBytes + Math.floor(x / 8);\r\n const bitIndex = 7 - (x % 8);\r\n if (black) {\r\n this.framebuffer[byteIndex] &= ~(1 << bitIndex); // Clear bit = black\r\n } else {\r\n this.framebuffer[byteIndex] |= (1 << bitIndex); // Set bit = white\r\n }\r\n }\r\n\r\n /**\r\n * Draw a filled rectangle.\r\n */\r\n fillRect(x: number, y: number, w: number, h: number, black: boolean): void {\r\n for (let dy = 0; dy < h; dy++) {\r\n for (let dx = 0; dx < w; dx++) {\r\n this.setPixel(x + dx, y + dy, black);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Draw a horizontal line.\r\n */\r\n hLine(x: number, y: number, w: number): void {\r\n for (let dx = 0; dx < w; dx++) {\r\n this.setPixel(x + dx, y, true);\r\n }\r\n }\r\n\r\n /**\r\n * Draw text using the 8x13 bitmap font.\r\n * fontSize: 1=normal(8x13), 2=large(16x26), 3=headline(24x39)\r\n * bold: draws text twice with 1px right shift for thicker strokes\r\n */\r\n drawText(x: number, y: number, text: string, fontSize = 1, bold = false): number {\r\n const w = this._renderText(x, y, text, fontSize, true);\r\n if (bold) this._renderText(x + 1, y, text, fontSize, true);\r\n return w;\r\n }\r\n\r\n /** Draw inverted text (white on black) for button labels. */\r\n drawTextInverted(x: number, y: number, text: string, fontSize = 1, bold = false): number {\r\n const w = this._renderText(x, y, text, fontSize, false);\r\n if (bold) this._renderText(x + 1, y, text, fontSize, false);\r\n return w;\r\n }\r\n\r\n private _renderText(x: number, y: number, text: string, fontSize: number, black: boolean): number {\r\n const cleanText = text.replace(/[\\u200B-\\u200F\\u2028-\\u202F\\u2060-\\u206F\\uFEFF]/g, \"\");\r\n const scale = fontScale(fontSize);\r\n const charW = 9 * scale;\r\n let curX = x;\r\n for (const char of cleanText) {\r\n const glyph = FONT_8X13[char] || FONT_8X13[\"?\"];\r\n if (!glyph) continue;\r\n for (let row = 0; row < 13; row++) {\r\n for (let col = 0; col < 8; col++) {\r\n if (glyph[row] & (1 << (7 - col))) {\r\n for (let sy = 0; sy < scale; sy++) {\r\n for (let sx = 0; sx < scale; sx++) {\r\n this.setPixel(curX + col * scale + sx, y + row * scale + sy, black);\r\n }\r\n }\r\n }\r\n }\r\n }\r\n curX += charW;\r\n }\r\n return curX - x;\r\n }\r\n\r\n /**\r\n * Draw text fitted within a rectangle. Wraps words and scales to fit.\r\n */\r\n drawTextInRect(left: number, top: number, width: number, height: number, text: string, fontSize = 1): void {\r\n const charW = fontCharWidth(fontSize);\r\n const charH = fontLineHeight(fontSize);\r\n const maxCols = Math.floor(width / charW);\r\n const maxRows = Math.floor(height / charH);\r\n\r\n // Simple word wrap\r\n const words = text.split(\" \");\r\n const lines: string[] = [];\r\n let currentLine = \"\";\r\n\r\n for (const word of words) {\r\n if (currentLine.length + word.length + 1 <= maxCols) {\r\n currentLine += (currentLine ? \" \" : \"\") + word;\r\n } else {\r\n if (currentLine) lines.push(currentLine);\r\n currentLine = word;\r\n }\r\n }\r\n if (currentLine) lines.push(currentLine);\r\n\r\n // Draw each line\r\n for (let i = 0; i < Math.min(lines.length, maxRows); i++) {\r\n this.drawText(left, top + i * charH, lines[i], fontSize);\r\n }\r\n }\r\n\r\n /**\r\n * Load a BMP file and draw it at the specified position.\r\n * Supports 1-bit, 8-bit, and 24-bit BMPs.\r\n */\r\n drawBmpFile(x: number, y: number, filePath: string): boolean {\r\n try {\r\n const data = fs.readFileSync(filePath);\r\n return this.drawBmpData(x, y, data);\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Load a PNG file and draw it at the specified position.\r\n * Uses a minimal PNG decoder (no external dependencies).\r\n */\r\n drawPngFile(x: number, y: number, filePath: string): boolean {\r\n try {\r\n const data = fs.readFileSync(filePath);\r\n return this.drawImageData(x, y, data, filePath);\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Draw image data. Detects format from header.\r\n */\r\n drawImageData(x: number, y: number, data: Buffer, hint = \"\"): boolean {\r\n // Check BMP magic\r\n if (data[0] === 0x42 && data[1] === 0x4D) {\r\n return this.drawBmpData(x, y, data);\r\n }\r\n // Check PNG magic\r\n if (data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4E && data[3] === 0x47) {\r\n return this.drawPngData(x, y, data);\r\n }\r\n return false;\r\n }\r\n\r\n private drawBmpData(x: number, y: number, data: Buffer): boolean {\r\n if (data.length < 54) return false;\r\n\r\n const dataOffset = data.readUInt32LE(10);\r\n const bmpWidth = data.readInt32LE(18);\r\n const bmpHeight = data.readInt32LE(22);\r\n const bitsPerPixel = data.readUInt16LE(28);\r\n const isBottomUp = bmpHeight > 0;\r\n const absHeight = Math.abs(bmpHeight);\r\n\r\n for (let row = 0; row < absHeight; row++) {\r\n const srcRow = isBottomUp ? (absHeight - 1 - row) : row;\r\n const dstY = y + row;\r\n\r\n if (bitsPerPixel === 1) {\r\n const rowBytes = Math.ceil(bmpWidth / 8);\r\n const paddedRowBytes = Math.ceil(rowBytes / 4) * 4;\r\n const rowOffset = dataOffset + srcRow * paddedRowBytes;\r\n\r\n for (let col = 0; col < bmpWidth; col++) {\r\n const byteIdx = rowOffset + Math.floor(col / 8);\r\n const bitIdx = 7 - (col % 8);\r\n const pixel = (data[byteIdx] >> bitIdx) & 1;\r\n this.setPixel(x + col, dstY, pixel === 0); // BMP: 0=black in 1bpp\r\n }\r\n } else if (bitsPerPixel === 24) {\r\n const paddedRowBytes = Math.ceil((bmpWidth * 3) / 4) * 4;\r\n const rowOffset = dataOffset + srcRow * paddedRowBytes;\r\n\r\n for (let col = 0; col < bmpWidth; col++) {\r\n const idx = rowOffset + col * 3;\r\n const b = data[idx];\r\n const g = data[idx + 1];\r\n const r = data[idx + 2];\r\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\r\n this.setPixel(x + col, dstY, brightness < 128);\r\n }\r\n } else if (bitsPerPixel === 8) {\r\n const paddedRowBytes = Math.ceil(bmpWidth / 4) * 4;\r\n const rowOffset = dataOffset + srcRow * paddedRowBytes;\r\n\r\n for (let col = 0; col < bmpWidth; col++) {\r\n const pixel = data[rowOffset + col];\r\n this.setPixel(x + col, dstY, pixel < 128);\r\n }\r\n }\r\n }\r\n return true;\r\n }\r\n\r\n private drawPngData(x: number, y: number, data: Buffer): boolean {\r\n // Minimal PNG decoder for simple cases\r\n // For the Signotec pad, images are small monochrome PNGs\r\n try {\r\n const { decodePng } = require(\"./png-decode\");\r\n const pixels = decodePng(data);\r\n if (!pixels) return false;\r\n\r\n for (let row = 0; row < pixels.height; row++) {\r\n for (let col = 0; col < pixels.width; col++) {\r\n const idx = (row * pixels.width + col) * pixels.channels;\r\n let brightness: number;\r\n if (pixels.channels >= 3) {\r\n brightness = (pixels.data[idx] * 299 + pixels.data[idx + 1] * 587 + pixels.data[idx + 2] * 114) / 1000;\r\n } else {\r\n brightness = pixels.data[idx];\r\n }\r\n // Handle alpha channel\r\n if (pixels.channels === 4 || pixels.channels === 2) {\r\n const alpha = pixels.data[idx + pixels.channels - 1];\r\n if (alpha < 128) continue; // Transparent pixel, leave as white\r\n }\r\n this.setPixel(x + col, y + row, brightness < 128);\r\n }\r\n }\r\n return true;\r\n } catch {\r\n // PNG decode not available - fall back to drawing nothing\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Send the entire framebuffer to the pad.\r\n */\r\n async sendToDevice(protocol: HidProtocol): Promise<boolean> {\r\n return protocol.drawImage(0, 0, this.width, this.height, this.framebuffer);\r\n }\r\n\r\n /**\r\n * Send a region of the framebuffer to the pad.\r\n */\r\n async sendRegionToDevice(protocol: HidProtocol, x: number, y: number, w: number, h: number): Promise<boolean> {\r\n const rowBytes = Math.ceil(this.width / 8);\r\n const regionRowBytes = Math.ceil(w / 8);\r\n const regionBuf = Buffer.alloc(regionRowBytes * h, 0xFF);\r\n\r\n for (let row = 0; row < h; row++) {\r\n for (let col = 0; col < w; col++) {\r\n const srcByteIdx = (y + row) * rowBytes + Math.floor((x + col) / 8);\r\n const srcBitIdx = 7 - ((x + col) % 8);\r\n const pixel = (this.framebuffer[srcByteIdx] >> srcBitIdx) & 1;\r\n\r\n const dstByteIdx = row * regionRowBytes + Math.floor(col / 8);\r\n const dstBitIdx = 7 - (col % 8);\r\n if (pixel === 0) {\r\n regionBuf[dstByteIdx] &= ~(1 << dstBitIdx);\r\n }\r\n }\r\n }\r\n\r\n return protocol.drawImage(x, y, w, h, regionBuf);\r\n }\r\n}\r\n\r\n// ============================================================\r\n// Built-in 5x7 bitmap font\r\n// Each character is 5 pixels wide, 7 pixels tall\r\n// Encoded as 7 bytes, each byte is a row, MSB first\r\n// ============================================================\r\nconst FONT_5X7: Record<string, number[]> = {\r\n \" \": [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],\r\n \"!\": [0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x04],\r\n '\"': [0x0A, 0x0A, 0x0A, 0x00, 0x00, 0x00, 0x00],\r\n \"#\": [0x0A, 0x0A, 0x1F, 0x0A, 0x1F, 0x0A, 0x0A],\r\n \"$\": [0x04, 0x0F, 0x14, 0x0E, 0x05, 0x1E, 0x04],\r\n \"%\": [0x18, 0x19, 0x02, 0x04, 0x08, 0x13, 0x03],\r\n \"&\": [0x0C, 0x12, 0x14, 0x08, 0x15, 0x12, 0x0D],\r\n \"'\": [0x06, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00],\r\n \"(\": [0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02],\r\n \")\": [0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08],\r\n \"*\": [0x00, 0x04, 0x15, 0x0E, 0x15, 0x04, 0x00],\r\n \"+\": [0x00, 0x04, 0x04, 0x1F, 0x04, 0x04, 0x00],\r\n \",\": [0x00, 0x00, 0x00, 0x00, 0x06, 0x04, 0x08],\r\n \"-\": [0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00],\r\n \".\": [0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06],\r\n \"/\": [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00],\r\n \"0\": [0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E],\r\n \"1\": [0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E],\r\n \"2\": [0x0E, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1F],\r\n \"3\": [0x1F, 0x02, 0x04, 0x02, 0x01, 0x11, 0x0E],\r\n \"4\": [0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02],\r\n \"5\": [0x1F, 0x10, 0x1E, 0x01, 0x01, 0x11, 0x0E],\r\n \"6\": [0x06, 0x08, 0x10, 0x1E, 0x11, 0x11, 0x0E],\r\n \"7\": [0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08],\r\n \"8\": [0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E],\r\n \"9\": [0x0E, 0x11, 0x11, 0x0F, 0x01, 0x02, 0x0C],\r\n \":\": [0x00, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00],\r\n \";\": [0x00, 0x06, 0x06, 0x00, 0x06, 0x04, 0x08],\r\n \"A\": [0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11],\r\n \"B\": [0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E],\r\n \"C\": [0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E],\r\n \"D\": [0x1C, 0x12, 0x11, 0x11, 0x11, 0x12, 0x1C],\r\n \"E\": [0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F],\r\n \"F\": [0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10],\r\n \"G\": [0x0E, 0x11, 0x10, 0x17, 0x11, 0x11, 0x0F],\r\n \"H\": [0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11],\r\n \"I\": [0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E],\r\n \"J\": [0x07, 0x02, 0x02, 0x02, 0x02, 0x12, 0x0C],\r\n \"K\": [0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11],\r\n \"L\": [0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F],\r\n \"M\": [0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11],\r\n \"N\": [0x11, 0x11, 0x19, 0x15, 0x13, 0x11, 0x11],\r\n \"O\": [0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E],\r\n \"P\": [0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10],\r\n \"Q\": [0x0E, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0D],\r\n \"R\": [0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11],\r\n \"S\": [0x0F, 0x10, 0x10, 0x0E, 0x01, 0x01, 0x1E],\r\n \"T\": [0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04],\r\n \"U\": [0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E],\r\n \"V\": [0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04],\r\n \"W\": [0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A],\r\n \"X\": [0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11],\r\n \"Y\": [0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04],\r\n \"Z\": [0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F],\r\n \"a\": [0x00, 0x00, 0x0E, 0x01, 0x0F, 0x11, 0x0F],\r\n \"b\": [0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1E],\r\n \"c\": [0x00, 0x00, 0x0E, 0x10, 0x10, 0x11, 0x0E],\r\n \"d\": [0x01, 0x01, 0x0D, 0x13, 0x11, 0x11, 0x0F],\r\n \"e\": [0x00, 0x00, 0x0E, 0x11, 0x1F, 0x10, 0x0E],\r\n \"f\": [0x06, 0x09, 0x08, 0x1C, 0x08, 0x08, 0x08],\r\n \"g\": [0x00, 0x0F, 0x11, 0x11, 0x0F, 0x01, 0x0E],\r\n \"h\": [0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x11],\r\n \"i\": [0x04, 0x00, 0x0C, 0x04, 0x04, 0x04, 0x0E],\r\n \"j\": [0x02, 0x00, 0x06, 0x02, 0x02, 0x12, 0x0C],\r\n \"k\": [0x10, 0x10, 0x12, 0x14, 0x18, 0x14, 0x12],\r\n \"l\": [0x0C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E],\r\n \"m\": [0x00, 0x00, 0x1A, 0x15, 0x15, 0x11, 0x11],\r\n \"n\": [0x00, 0x00, 0x16, 0x19, 0x11, 0x11, 0x11],\r\n \"o\": [0x00, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E],\r\n \"p\": [0x00, 0x00, 0x1E, 0x11, 0x1E, 0x10, 0x10],\r\n \"q\": [0x00, 0x00, 0x0D, 0x13, 0x0F, 0x01, 0x01],\r\n \"r\": [0x00, 0x00, 0x16, 0x19, 0x10, 0x10, 0x10],\r\n \"s\": [0x00, 0x00, 0x0E, 0x10, 0x0E, 0x01, 0x1E],\r\n \"t\": [0x08, 0x08, 0x1C, 0x08, 0x08, 0x09, 0x06],\r\n \"u\": [0x00, 0x00, 0x11, 0x11, 0x11, 0x13, 0x0D],\r\n \"v\": [0x00, 0x00, 0x11, 0x11, 0x11, 0x0A, 0x04],\r\n \"w\": [0x00, 0x00, 0x11, 0x11, 0x15, 0x15, 0x0A],\r\n \"x\": [0x00, 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11],\r\n \"y\": [0x00, 0x00, 0x11, 0x11, 0x0F, 0x01, 0x0E],\r\n \"z\": [0x00, 0x00, 0x1F, 0x02, 0x04, 0x08, 0x1F],\r\n \"?\": [0x0E, 0x11, 0x01, 0x02, 0x04, 0x00, 0x04],\r\n // German characters\r\n \"\\u00C4\": [0x0A, 0x00, 0x0E, 0x11, 0x1F, 0x11, 0x11], // Ä\r\n \"\\u00D6\": [0x0A, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E], // Ö\r\n \"\\u00DC\": [0x0A, 0x00, 0x11, 0x11, 0x11, 0x11, 0x0E], // Ü\r\n \"\\u00E4\": [0x0A, 0x00, 0x0E, 0x01, 0x0F, 0x11, 0x0F], // ä\r\n \"\\u00F6\": [0x0A, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E], // ö\r\n \"\\u00FC\": [0x0A, 0x00, 0x11, 0x11, 0x11, 0x13, 0x0D], // ü\r\n \"\\u00DF\": [0x0E, 0x11, 0x12, 0x14, 0x12, 0x11, 0x16], // ß\r\n};\r\n","// 8x13 Bitmap Font\r\n// Based on the classic X11/IBM VGA 8x13 bitmap font style.\r\n// Each character is 8 pixels wide and 13 pixels tall.\r\n// Each glyph is an array of 13 bytes; each byte represents one row (MSB = leftmost pixel).\r\n// Baseline is at row 10 (index 9). Rows 11-12 (indices 10-12) are used for descenders.\r\n\r\nexport const FONT_8X13: Record<string, number[]> = {\r\n // ─── Space ───────────────────────────────────────────────────────────────\r\n \" \": [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],\r\n\r\n // ─── Digits ──────────────────────────────────────────────────────────────\r\n \"0\": [0x00, 0x00, 0x3C, 0x66, 0x6E, 0x76, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"1\": [0x00, 0x00, 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, 0x00, 0x00],\r\n \"2\": [0x00, 0x00, 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, 0x00, 0x00],\r\n \"3\": [0x00, 0x00, 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"4\": [0x00, 0x00, 0x0C, 0x1C, 0x2C, 0x4C, 0x7E, 0x0C, 0x0C, 0x0C, 0x00, 0x00, 0x00],\r\n \"5\": [0x00, 0x00, 0x7E, 0x60, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"6\": [0x00, 0x00, 0x1C, 0x30, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"7\": [0x00, 0x00, 0x7E, 0x06, 0x0C, 0x18, 0x18, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00],\r\n \"8\": [0x00, 0x00, 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"9\": [0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00, 0x00, 0x00],\r\n\r\n // ─── Uppercase A–Z ───────────────────────────────────────────────────────\r\n \"A\": [0x00, 0x00, 0x18, 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00],\r\n \"B\": [0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00],\r\n \"C\": [0x00, 0x00, 0x3C, 0x66, 0x60, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"D\": [0x00, 0x00, 0x78, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00, 0x00, 0x00],\r\n \"E\": [0x00, 0x00, 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x7E, 0x00, 0x00, 0x00],\r\n \"F\": [0x00, 0x00, 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00],\r\n \"G\": [0x00, 0x00, 0x3C, 0x66, 0x60, 0x60, 0x6E, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00],\r\n \"H\": [0x00, 0x00, 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00],\r\n \"I\": [0x00, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00],\r\n \"J\": [0x00, 0x00, 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"K\": [0x00, 0x00, 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x66, 0x00, 0x00, 0x00],\r\n \"L\": [0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, 0x00, 0x00],\r\n \"M\": [0x00, 0x00, 0x42, 0x66, 0x7E, 0x7E, 0x5A, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00],\r\n \"N\": [0x00, 0x00, 0x62, 0x72, 0x7A, 0x6E, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00],\r\n \"O\": [0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"P\": [0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00],\r\n \"Q\": [0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x76, 0x6C, 0x36, 0x00, 0x00, 0x00],\r\n \"R\": [0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x78, 0x6C, 0x66, 0x66, 0x00, 0x00, 0x00],\r\n \"S\": [0x00, 0x00, 0x3C, 0x66, 0x60, 0x38, 0x0C, 0x06, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"T\": [0x00, 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00],\r\n \"U\": [0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"V\": [0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x00, 0x00, 0x00],\r\n \"W\": [0x00, 0x00, 0x42, 0x42, 0x42, 0x5A, 0x5A, 0x7E, 0x66, 0x42, 0x00, 0x00, 0x00],\r\n \"X\": [0x00, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00],\r\n \"Y\": [0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00],\r\n \"Z\": [0x00, 0x00, 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x60, 0x7E, 0x00, 0x00, 0x00],\r\n\r\n // ─── Lowercase a–z ───────────────────────────────────────────────────────\r\n // Baseline row = index 9 (row 10). Descenders use indices 10-11.\r\n \"a\": [0x00, 0x00, 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00],\r\n \"b\": [0x00, 0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, 0x00, 0x00],\r\n \"c\": [0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x60, 0x60, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"d\": [0x00, 0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00],\r\n \"e\": [0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"f\": [0x00, 0x00, 0x0E, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00],\r\n \"g\": [0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x3C, 0x00],\r\n \"h\": [0x00, 0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00],\r\n \"i\": [0x00, 0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00],\r\n \"j\": [0x00, 0x00, 0x06, 0x00, 0x0E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x46, 0x3C, 0x00],\r\n \"k\": [0x00, 0x00, 0x60, 0x60, 0x66, 0x6C, 0x78, 0x7C, 0x6C, 0x66, 0x00, 0x00, 0x00],\r\n \"l\": [0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00],\r\n \"m\": [0x00, 0x00, 0x00, 0x00, 0x6C, 0x7E, 0x7E, 0x6A, 0x62, 0x62, 0x00, 0x00, 0x00],\r\n \"n\": [0x00, 0x00, 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00],\r\n \"o\": [0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"p\": [0x00, 0x00, 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00],\r\n \"q\": [0x00, 0x00, 0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x00],\r\n \"r\": [0x00, 0x00, 0x00, 0x00, 0x6C, 0x76, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00],\r\n \"s\": [0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x38, 0x0C, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"t\": [0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00],\r\n \"u\": [0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00],\r\n \"v\": [0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x3C, 0x18, 0x00, 0x00, 0x00],\r\n \"w\": [0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x5A, 0x5A, 0x7E, 0x24, 0x00, 0x00, 0x00],\r\n \"x\": [0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x3C, 0x3C, 0x66, 0x66, 0x00, 0x00, 0x00],\r\n \"y\": [0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x46, 0x3C, 0x00],\r\n \"z\": [0x00, 0x00, 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, 0x00, 0x00],\r\n\r\n // ─── Punctuation ─────────────────────────────────────────────────────────\r\n \".\": [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00],\r\n \",\": [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x08, 0x10, 0x00],\r\n \":\": [0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00],\r\n \";\": [0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x08, 0x10, 0x00],\r\n \"!\": [0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00],\r\n \"?\": [0x00, 0x00, 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00],\r\n \"-\": [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],\r\n \"+\": [0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00],\r\n \"=\": [0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00],\r\n \"(\": [0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00],\r\n \")\": [0x00, 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00],\r\n \"/\": [0x00, 0x00, 0x02, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00],\r\n \"\\\\\": [0x00, 0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00],\r\n \"'\": [0x00, 0x18, 0x18, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],\r\n \"\\\"\": [0x00, 0x66, 0x66, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],\r\n \"_\": [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00],\r\n \"@\": [0x00, 0x00, 0x3C, 0x66, 0x6E, 0x6A, 0x6E, 0x60, 0x62, 0x3C, 0x00, 0x00, 0x00],\r\n \"#\": [0x00, 0x00, 0x24, 0x24, 0x7E, 0x24, 0x24, 0x7E, 0x24, 0x24, 0x00, 0x00, 0x00],\r\n \"&\": [0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x76, 0xCE, 0xCC, 0x76, 0x00, 0x00, 0x00],\r\n \"%\": [0x00, 0x00, 0x62, 0x66, 0x0C, 0x18, 0x30, 0x66, 0x46, 0x00, 0x00, 0x00, 0x00],\r\n \"*\": [0x00, 0x00, 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00],\r\n \"<\": [0x00, 0x00, 0x00, 0x06, 0x1C, 0x70, 0x70, 0x1C, 0x06, 0x00, 0x00, 0x00, 0x00],\r\n \">\": [0x00, 0x00, 0x00, 0x60, 0x38, 0x0E, 0x0E, 0x38, 0x60, 0x00, 0x00, 0x00, 0x00],\r\n \"[\": [0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00],\r\n \"]\": [0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00, 0x00],\r\n \"{\": [0x00, 0x00, 0x0E, 0x18, 0x18, 0x30, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00],\r\n \"}\": [0x00, 0x00, 0x70, 0x18, 0x18, 0x0C, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00],\r\n \"|\": [0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00],\r\n \"~\": [0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],\r\n \"^\": [0x00, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],\r\n \"`\": [0x00, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],\r\n\r\n // ─── German characters ────────────────────────────────────────────────────\r\n // Umlauts: dots sit at rows 0-1, letter body follows below\r\n \"ä\": [0x00, 0x24, 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00],\r\n \"ö\": [0x00, 0x24, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"ü\": [0x00, 0x24, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00, 0x00],\r\n \"Ä\": [0x24, 0x00, 0x18, 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00],\r\n \"Ö\": [0x24, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n \"Ü\": [0x24, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00],\r\n // ß: sharp-s / eszett\r\n \"ß\": [0x00, 0x00, 0x3C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00],\r\n\r\n // ─── Currency / special symbols ──────────────────────────────────────────\r\n // € (Euro): slightly narrower than full width, classic double-bar C shape\r\n \"€\": [0x00, 0x00, 0x1E, 0x30, 0x7C, 0x30, 0x7C, 0x30, 0x30, 0x1E, 0x00, 0x00, 0x00],\r\n};\r\n"],"mappings":"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AM,CAAC,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,CAAI;AAAA;AAAA,EAGnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,GAAM,IAAM,IAAM,IAAM,IAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,GAAM,IAAM,GAAM,GAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,KAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,IAAM,IAAM,KAAM,GAAM,GAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,KAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,KAAM,IAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,KAAM,KAAM,IAAM,GAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA;AAAA,EAGnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,IAAM,IAAM,IAAM,IAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,IAAM,IAAM,KAAM,IAAM,IAAM,IAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,IAAM,IAAM,KAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,IAAM,IAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,GAAM,GAAM,GAAM,GAAM,GAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,KAAM,KAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,KAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,IAAM,IAAM,IAAM,GAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,KAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,KAAM,IAAM,IAAM,IAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,KAAM,KAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,KAAM,GAAM,GAAM,CAAI;AAAA;AAAA;AAAA,EAInF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,GAAM,IAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,KAAM,IAAM,IAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,KAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,KAAM,KAAM,IAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,KAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,IAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,GAAM,GAAM,GAAM,GAAM,GAAM,IAAM,IAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,KAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,KAAM,IAAM,IAAM,IAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,KAAM,KAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,KAAM,IAAM,IAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,KAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,KAAM,KAAM,KAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,KAAM,KAAM,IAAM,IAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,IAAM,IAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,KAAM,IAAM,IAAM,IAAM,IAAM,KAAM,GAAM,GAAM,CAAI;AAAA;AAAA,EAGnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,IAAM,IAAM,GAAM,IAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,IAAM,GAAM,GAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,IAAM,GAAM,GAAM,IAAM,IAAM,GAAM,IAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,GAAM,IAAM,IAAM,IAAM,GAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,KAAM,GAAM,GAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,IAAM,KAAM,IAAM,IAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,GAAM,KAAM,GAAM,KAAM,GAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,MAAO,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACpF,KAAM,CAAC,GAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,KAAM,KAAM,IAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,KAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,KAAM,KAAM,KAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,KAAM,IAAM,IAAM,KAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,KAAM,IAAM,KAAM,KAAM,KAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,KAAM,IAAM,IAAM,IAAM,KAAM,IAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,KAAM,IAAM,KAAM,IAAM,KAAM,GAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,IAAM,KAAM,KAAM,IAAM,GAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,KAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,KAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,GAAM,GAAM,GAAM,GAAM,KAAM,KAAM,GAAM,GAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,IAAM,IAAM,KAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,CAAI;AAAA,EACnF,KAAM,CAAC,GAAM,IAAM,IAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,CAAI;AAAA;AAAA;AAAA,EAInF,QAAM,CAAC,GAAM,IAAM,GAAM,GAAM,IAAM,GAAM,IAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,QAAM,CAAC,GAAM,IAAM,GAAM,GAAM,IAAM,KAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,QAAM,CAAC,GAAM,IAAM,GAAM,GAAM,KAAM,KAAM,KAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAAA,EACnF,QAAM,CAAC,IAAM,GAAM,IAAM,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","names":["zlib","fs","decodePng"]}