label-printer 0.3.1 → 0.4.3
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/README.md +7 -1
- package/dist/index.js +552 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +555 -34
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -2
package/dist/index.mjs
CHANGED
|
@@ -17,6 +17,12 @@ var __spreadValues = (a, b) => {
|
|
|
17
17
|
return a;
|
|
18
18
|
};
|
|
19
19
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
21
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
22
|
+
}) : x)(function(x) {
|
|
23
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
24
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
25
|
+
});
|
|
20
26
|
var __export = (target, all) => {
|
|
21
27
|
for (var name in all)
|
|
22
28
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -181,9 +187,6 @@ var TSPLVisualCommand = class extends TSPLCommand {
|
|
|
181
187
|
}
|
|
182
188
|
};
|
|
183
189
|
|
|
184
|
-
// src/helpers/ImageUtils.ts
|
|
185
|
-
import pixels from "image-pixels";
|
|
186
|
-
|
|
187
190
|
// src/helpers/UnitUtils.ts
|
|
188
191
|
var pointsPerInch = 72;
|
|
189
192
|
function getSizePreserveAspect(width, height, desiredWidth, desiredHeight) {
|
|
@@ -220,6 +223,538 @@ function pointsToDots(points, dpi) {
|
|
|
220
223
|
return dots;
|
|
221
224
|
}
|
|
222
225
|
|
|
226
|
+
// src/helpers/ImageDataParser.ts
|
|
227
|
+
function parsePNG(buffer) {
|
|
228
|
+
const pngSignature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
229
|
+
if (!buffer.subarray(0, 8).equals(pngSignature)) {
|
|
230
|
+
throw new Error("Invalid PNG file");
|
|
231
|
+
}
|
|
232
|
+
let width = 0, height = 0, bitDepth = 0, colorType = 0;
|
|
233
|
+
let compressionMethod = 0, filterMethod = 0, interlaceMethod = 0;
|
|
234
|
+
let palette = null;
|
|
235
|
+
let transparency = null;
|
|
236
|
+
let idatChunks = [];
|
|
237
|
+
let offset = 8;
|
|
238
|
+
while (offset < buffer.length) {
|
|
239
|
+
const chunkLength = buffer.readUInt32BE(offset);
|
|
240
|
+
const chunkType = buffer.subarray(offset + 4, offset + 8).toString("ascii");
|
|
241
|
+
const chunkData = buffer.subarray(offset + 8, offset + 8 + chunkLength);
|
|
242
|
+
if (chunkType === "IHDR") {
|
|
243
|
+
width = chunkData.readUInt32BE(0);
|
|
244
|
+
height = chunkData.readUInt32BE(4);
|
|
245
|
+
bitDepth = chunkData.readUInt8(8);
|
|
246
|
+
colorType = chunkData.readUInt8(9);
|
|
247
|
+
compressionMethod = chunkData.readUInt8(10);
|
|
248
|
+
filterMethod = chunkData.readUInt8(11);
|
|
249
|
+
interlaceMethod = chunkData.readUInt8(12);
|
|
250
|
+
} else if (chunkType === "PLTE") {
|
|
251
|
+
palette = chunkData;
|
|
252
|
+
} else if (chunkType === "tRNS") {
|
|
253
|
+
transparency = chunkData;
|
|
254
|
+
} else if (chunkType === "IDAT") {
|
|
255
|
+
idatChunks.push(chunkData);
|
|
256
|
+
} else if (chunkType === "IEND") {
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
offset += 8 + chunkLength + 4;
|
|
260
|
+
}
|
|
261
|
+
if (compressionMethod !== 0) throw new Error("Unsupported PNG compression method");
|
|
262
|
+
if (filterMethod !== 0) throw new Error("Unsupported PNG filter method");
|
|
263
|
+
if (interlaceMethod !== 0) throw new Error("Interlaced PNGs not supported");
|
|
264
|
+
if (idatChunks.length === 0) throw new Error("No image data found in PNG");
|
|
265
|
+
const compressedData = Buffer.concat(idatChunks);
|
|
266
|
+
let decompressedData;
|
|
267
|
+
try {
|
|
268
|
+
const zlib = __require("zlib");
|
|
269
|
+
decompressedData = zlib.inflateSync(compressedData);
|
|
270
|
+
} catch (error) {
|
|
271
|
+
throw new Error("Failed to decompress PNG data: " + error);
|
|
272
|
+
}
|
|
273
|
+
if (bitDepth !== 8) throw new Error("Only 8-bit PNGs are supported in this parser");
|
|
274
|
+
let bytesPerPixel;
|
|
275
|
+
let channels;
|
|
276
|
+
let outputChannels = 4;
|
|
277
|
+
switch (colorType) {
|
|
278
|
+
case 0:
|
|
279
|
+
channels = 1;
|
|
280
|
+
bytesPerPixel = 1;
|
|
281
|
+
break;
|
|
282
|
+
// Grayscale
|
|
283
|
+
case 2:
|
|
284
|
+
channels = 3;
|
|
285
|
+
bytesPerPixel = 3;
|
|
286
|
+
break;
|
|
287
|
+
// RGB
|
|
288
|
+
case 3:
|
|
289
|
+
channels = 1;
|
|
290
|
+
bytesPerPixel = 1;
|
|
291
|
+
break;
|
|
292
|
+
// Palette
|
|
293
|
+
case 4:
|
|
294
|
+
channels = 2;
|
|
295
|
+
bytesPerPixel = 2;
|
|
296
|
+
break;
|
|
297
|
+
// Grayscale+Alpha
|
|
298
|
+
case 6:
|
|
299
|
+
channels = 4;
|
|
300
|
+
bytesPerPixel = 4;
|
|
301
|
+
break;
|
|
302
|
+
// RGBA
|
|
303
|
+
default:
|
|
304
|
+
throw new Error(`Unsupported PNG color type: ${colorType}`);
|
|
305
|
+
}
|
|
306
|
+
const scanlineLength = width * bytesPerPixel;
|
|
307
|
+
const data = new Uint8Array(width * height * outputChannels);
|
|
308
|
+
for (let y = 0; y < height; y++) {
|
|
309
|
+
const scanlineStart = y * (scanlineLength + 1);
|
|
310
|
+
const filterType = decompressedData[scanlineStart];
|
|
311
|
+
const scanline = decompressedData.subarray(scanlineStart + 1, scanlineStart + 1 + scanlineLength);
|
|
312
|
+
const prevScanline = y > 0 ? decompressedData.subarray((y - 1) * (scanlineLength + 1) + 1, (y - 1) * (scanlineLength + 1) + 1 + scanlineLength) : Buffer.alloc(scanlineLength);
|
|
313
|
+
const unfilteredScanline = applyPNGFilter(filterType, scanline, prevScanline, bytesPerPixel);
|
|
314
|
+
for (let x = 0; x < width; x++) {
|
|
315
|
+
const outIdx = (y * width + x) * outputChannels;
|
|
316
|
+
if (colorType === 0) {
|
|
317
|
+
const gray = unfilteredScanline[x];
|
|
318
|
+
data[outIdx] = gray;
|
|
319
|
+
data[outIdx + 1] = gray;
|
|
320
|
+
data[outIdx + 2] = gray;
|
|
321
|
+
data[outIdx + 3] = 255;
|
|
322
|
+
} else if (colorType === 2) {
|
|
323
|
+
data[outIdx] = unfilteredScanline[x * 3];
|
|
324
|
+
data[outIdx + 1] = unfilteredScanline[x * 3 + 1];
|
|
325
|
+
data[outIdx + 2] = unfilteredScanline[x * 3 + 2];
|
|
326
|
+
data[outIdx + 3] = 255;
|
|
327
|
+
} else if (colorType === 3) {
|
|
328
|
+
if (!palette) throw new Error("Palette PNG missing PLTE chunk");
|
|
329
|
+
const idx = unfilteredScanline[x];
|
|
330
|
+
data[outIdx] = palette[idx * 3];
|
|
331
|
+
data[outIdx + 1] = palette[idx * 3 + 1];
|
|
332
|
+
data[outIdx + 2] = palette[idx * 3 + 2];
|
|
333
|
+
data[outIdx + 3] = transparency && idx < transparency.length ? transparency[idx] : 255;
|
|
334
|
+
} else if (colorType === 4) {
|
|
335
|
+
const gray = unfilteredScanline[x * 2];
|
|
336
|
+
data[outIdx] = gray;
|
|
337
|
+
data[outIdx + 1] = gray;
|
|
338
|
+
data[outIdx + 2] = gray;
|
|
339
|
+
data[outIdx + 3] = unfilteredScanline[x * 2 + 1];
|
|
340
|
+
} else if (colorType === 6) {
|
|
341
|
+
data[outIdx] = unfilteredScanline[x * 4];
|
|
342
|
+
data[outIdx + 1] = unfilteredScanline[x * 4 + 1];
|
|
343
|
+
data[outIdx + 2] = unfilteredScanline[x * 4 + 2];
|
|
344
|
+
data[outIdx + 3] = unfilteredScanline[x * 4 + 3];
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
data,
|
|
350
|
+
width,
|
|
351
|
+
height,
|
|
352
|
+
bitsPerPixel: outputChannels
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
function applyPNGFilter(filterType, scanline, prevScanline, bytesPerPixel) {
|
|
356
|
+
const result = Buffer.alloc(scanline.length);
|
|
357
|
+
for (let i = 0; i < scanline.length; i++) {
|
|
358
|
+
let filtered = scanline[i];
|
|
359
|
+
let a = i >= bytesPerPixel ? result[i - bytesPerPixel] : 0;
|
|
360
|
+
let b = prevScanline[i] || 0;
|
|
361
|
+
let c = i >= bytesPerPixel && prevScanline[i - bytesPerPixel] ? prevScanline[i - bytesPerPixel] : 0;
|
|
362
|
+
switch (filterType) {
|
|
363
|
+
case 0:
|
|
364
|
+
result[i] = filtered;
|
|
365
|
+
break;
|
|
366
|
+
case 1:
|
|
367
|
+
result[i] = filtered + a & 255;
|
|
368
|
+
break;
|
|
369
|
+
case 2:
|
|
370
|
+
result[i] = filtered + b & 255;
|
|
371
|
+
break;
|
|
372
|
+
case 3:
|
|
373
|
+
result[i] = filtered + Math.floor((a + b) / 2) & 255;
|
|
374
|
+
break;
|
|
375
|
+
case 4:
|
|
376
|
+
const p = a + b - c;
|
|
377
|
+
const pa = Math.abs(p - a);
|
|
378
|
+
const pb = Math.abs(p - b);
|
|
379
|
+
const pc = Math.abs(p - c);
|
|
380
|
+
let paeth;
|
|
381
|
+
if (pa <= pb && pa <= pc) {
|
|
382
|
+
paeth = a;
|
|
383
|
+
} else if (pb <= pc) {
|
|
384
|
+
paeth = b;
|
|
385
|
+
} else {
|
|
386
|
+
paeth = c;
|
|
387
|
+
}
|
|
388
|
+
result[i] = filtered + paeth & 255;
|
|
389
|
+
break;
|
|
390
|
+
default:
|
|
391
|
+
result[i] = filtered;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return result;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// src/helpers/ImageProcessor.ts
|
|
398
|
+
var ImageProcessor = class {
|
|
399
|
+
/**
|
|
400
|
+
* Get pixel information about an image
|
|
401
|
+
* @param image Image to process (local file path, remote URL, data URL, or Blob)
|
|
402
|
+
* @returns Promise with image data including width, height, pixel data, and bits per pixel
|
|
403
|
+
*/
|
|
404
|
+
static getImageData(image) {
|
|
405
|
+
return __async(this, null, function* () {
|
|
406
|
+
if (typeof window !== "undefined") {
|
|
407
|
+
return this.getImageDataBrowser(image);
|
|
408
|
+
} else {
|
|
409
|
+
return this.getImageDataNode(image);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
/******** BROWSER ********/
|
|
414
|
+
/**
|
|
415
|
+
* Get pixel information about an image in browser environment
|
|
416
|
+
* @param image Image to process
|
|
417
|
+
* @returns Promise with image data
|
|
418
|
+
*/
|
|
419
|
+
static getImageDataBrowser(image) {
|
|
420
|
+
return __async(this, null, function* () {
|
|
421
|
+
console.log("Processing image in browser environment");
|
|
422
|
+
return new Promise((resolve, reject) => {
|
|
423
|
+
const img = new Image();
|
|
424
|
+
img.crossOrigin = "anonymous";
|
|
425
|
+
img.onload = () => {
|
|
426
|
+
try {
|
|
427
|
+
const canvas = document.createElement("canvas");
|
|
428
|
+
const ctx = canvas.getContext("2d");
|
|
429
|
+
if (!ctx) {
|
|
430
|
+
reject(new Error("Could not get canvas context"));
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
canvas.width = img.width;
|
|
434
|
+
canvas.height = img.height;
|
|
435
|
+
ctx.drawImage(img, 0, 0);
|
|
436
|
+
const imageData = ctx.getImageData(0, 0, img.width, img.height);
|
|
437
|
+
resolve({
|
|
438
|
+
data: new Uint8Array(imageData.data),
|
|
439
|
+
width: img.width,
|
|
440
|
+
height: img.height,
|
|
441
|
+
bitsPerPixel: 4
|
|
442
|
+
// RGBA
|
|
443
|
+
});
|
|
444
|
+
} catch (error) {
|
|
445
|
+
reject(error);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
img.onerror = () => reject(new Error("Failed to load image"));
|
|
449
|
+
if (typeof image === "string") {
|
|
450
|
+
img.src = image;
|
|
451
|
+
} else {
|
|
452
|
+
const url = URL.createObjectURL(image);
|
|
453
|
+
img.onload = () => {
|
|
454
|
+
URL.revokeObjectURL(url);
|
|
455
|
+
resolve({
|
|
456
|
+
data: new Uint8Array(0),
|
|
457
|
+
// Will be set by the actual onload
|
|
458
|
+
width: 0,
|
|
459
|
+
height: 0,
|
|
460
|
+
bitsPerPixel: 4
|
|
461
|
+
});
|
|
462
|
+
};
|
|
463
|
+
img.src = url;
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
/******** NODEJS ********/
|
|
469
|
+
/**
|
|
470
|
+
* Get pixel information about an image in Node.js environment
|
|
471
|
+
* @param image Image to process
|
|
472
|
+
* @returns Promise with image data
|
|
473
|
+
*/
|
|
474
|
+
static getImageDataNode(image) {
|
|
475
|
+
return __async(this, null, function* () {
|
|
476
|
+
console.log("Processing image in Node.js environment");
|
|
477
|
+
if (image instanceof Blob) {
|
|
478
|
+
throw new Error("Blob input not supported in Node.js environment. Use file path or data URL instead.");
|
|
479
|
+
}
|
|
480
|
+
if (image.startsWith("data:")) {
|
|
481
|
+
return this.getImageFromData(image);
|
|
482
|
+
} else if (image.startsWith("http://") || image.startsWith("https://")) {
|
|
483
|
+
return this.getImageFromUrl(image);
|
|
484
|
+
} else {
|
|
485
|
+
return this.getImageFromFile(image);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Parse a data URL to extract image data
|
|
491
|
+
* @param dataURL Data URL string
|
|
492
|
+
* @returns Promise with image data
|
|
493
|
+
*/
|
|
494
|
+
static getImageFromData(dataURL) {
|
|
495
|
+
return __async(this, null, function* () {
|
|
496
|
+
var _a;
|
|
497
|
+
const [header, data] = dataURL.split(",");
|
|
498
|
+
const mimeType = (_a = header.match(/data:([^;]+)/)) == null ? void 0 : _a[1];
|
|
499
|
+
if (!(mimeType == null ? void 0 : mimeType.startsWith("image/"))) {
|
|
500
|
+
throw new Error("Invalid image data URL");
|
|
501
|
+
}
|
|
502
|
+
const buffer = Buffer.from(data, "base64");
|
|
503
|
+
const extension = mimeType.split("/")[1].toLowerCase();
|
|
504
|
+
return this.parse(buffer, extension);
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Image data from file
|
|
509
|
+
* @param image
|
|
510
|
+
* @returns
|
|
511
|
+
*/
|
|
512
|
+
static getImageFromFile(image) {
|
|
513
|
+
return __async(this, null, function* () {
|
|
514
|
+
const fs = yield import("fs");
|
|
515
|
+
const path = yield import("path");
|
|
516
|
+
if (!fs.existsSync(image)) {
|
|
517
|
+
throw new Error(`Image file not found: ${image}`);
|
|
518
|
+
}
|
|
519
|
+
const buffer = fs.readFileSync(image);
|
|
520
|
+
const ext = path.extname(image).toLowerCase();
|
|
521
|
+
return this.parse(buffer, ext);
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Fetch and process a remote image in Node.js environment
|
|
526
|
+
* @param url Remote image URL
|
|
527
|
+
* @returns Promise with image data
|
|
528
|
+
*/
|
|
529
|
+
static getImageFromUrl(url) {
|
|
530
|
+
return __async(this, null, function* () {
|
|
531
|
+
let fetch;
|
|
532
|
+
try {
|
|
533
|
+
fetch = globalThis.fetch;
|
|
534
|
+
} catch (e) {
|
|
535
|
+
return this.fetchWithHttps(url);
|
|
536
|
+
}
|
|
537
|
+
if (!fetch) {
|
|
538
|
+
return this.fetchWithHttps(url);
|
|
539
|
+
}
|
|
540
|
+
const response = yield fetch(url);
|
|
541
|
+
if (!response.ok) {
|
|
542
|
+
throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`);
|
|
543
|
+
}
|
|
544
|
+
const arrayBuffer = yield response.arrayBuffer();
|
|
545
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
546
|
+
const contentType = response.headers.get("content-type");
|
|
547
|
+
const imageType = this.getImageType(contentType || "", url);
|
|
548
|
+
return this.parse(buffer, imageType);
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Fetch remote image using Node.js https module (fallback)
|
|
553
|
+
* @param url Remote image URL
|
|
554
|
+
* @returns Promise with image data
|
|
555
|
+
*/
|
|
556
|
+
static fetchWithHttps(url) {
|
|
557
|
+
return __async(this, null, function* () {
|
|
558
|
+
const https = yield import("https");
|
|
559
|
+
const http = yield import("http");
|
|
560
|
+
return new Promise((resolve, reject) => {
|
|
561
|
+
const client = url.startsWith("https:") ? https : http;
|
|
562
|
+
const request = client.get(url, (response) => {
|
|
563
|
+
if (response.statusCode !== 200) {
|
|
564
|
+
reject(new Error(`Failed to fetch image: ${response.statusCode} ${response.statusMessage}`));
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
const chunks = [];
|
|
568
|
+
response.on("data", (chunk) => {
|
|
569
|
+
chunks.push(chunk);
|
|
570
|
+
});
|
|
571
|
+
response.on("end", () => {
|
|
572
|
+
try {
|
|
573
|
+
const buffer = Buffer.concat(chunks);
|
|
574
|
+
const contentType = response.headers["content-type"] || "";
|
|
575
|
+
const imageType = this.getImageType(contentType || "", url);
|
|
576
|
+
const data = this.parse(buffer, imageType);
|
|
577
|
+
resolve(data);
|
|
578
|
+
} catch (error) {
|
|
579
|
+
reject(error);
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
response.on("error", (error) => {
|
|
583
|
+
reject(error);
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
request.on("error", (error) => {
|
|
587
|
+
reject(new Error(`Failed to fetch remote image: ${error.message}`));
|
|
588
|
+
});
|
|
589
|
+
request.setTimeout(3e4, () => {
|
|
590
|
+
request.destroy();
|
|
591
|
+
reject(new Error("Request timeout: Failed to fetch remote image within 30 seconds"));
|
|
592
|
+
});
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Decide content type
|
|
598
|
+
*/
|
|
599
|
+
static getImageType(contentType, url) {
|
|
600
|
+
if (contentType) {
|
|
601
|
+
if (contentType.includes("png")) {
|
|
602
|
+
return "png";
|
|
603
|
+
} else if (contentType.includes("jpeg") || contentType.includes("jpg")) {
|
|
604
|
+
return "jpeg";
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
const urlLower = url.toLowerCase();
|
|
608
|
+
if (urlLower.includes(".png")) {
|
|
609
|
+
return "png";
|
|
610
|
+
} else if (urlLower.includes(".jpg") || urlLower.includes(".jpeg")) {
|
|
611
|
+
return "jpeg";
|
|
612
|
+
}
|
|
613
|
+
return "";
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
*
|
|
617
|
+
* @param buffer
|
|
618
|
+
* @param extension
|
|
619
|
+
* @returns
|
|
620
|
+
*/
|
|
621
|
+
static parse(buffer, extension) {
|
|
622
|
+
console.log(`Parsing image with extension: ${extension}`);
|
|
623
|
+
if (extension === "png") {
|
|
624
|
+
return parsePNG(buffer);
|
|
625
|
+
} else if (extension === "jpeg" || extension === "jpg") {
|
|
626
|
+
return this.parseJPEG(buffer);
|
|
627
|
+
} else {
|
|
628
|
+
throw new Error(`Unsupported image format: ${extension}. Supported formats: PNG, JPEG`);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* JPEG parser that creates a meaningful placeholder image
|
|
633
|
+
* Note: Full JPEG decoding requires complex DCT and Huffman decoding.
|
|
634
|
+
* This creates a gradient pattern based on the image dimensions.
|
|
635
|
+
* @param buffer JPEG file buffer
|
|
636
|
+
* @returns Image data
|
|
637
|
+
*/
|
|
638
|
+
static parseJPEG(buffer) {
|
|
639
|
+
if (buffer[0] !== 255 || buffer[1] !== 216) {
|
|
640
|
+
throw new Error("Invalid JPEG file");
|
|
641
|
+
}
|
|
642
|
+
let offset = 2;
|
|
643
|
+
let width = 0;
|
|
644
|
+
let height = 0;
|
|
645
|
+
while (offset < buffer.length - 1) {
|
|
646
|
+
if (buffer[offset] === 255) {
|
|
647
|
+
const marker = buffer[offset + 1];
|
|
648
|
+
if (marker >= 192 && marker <= 194) {
|
|
649
|
+
height = buffer.readUInt16BE(offset + 5);
|
|
650
|
+
width = buffer.readUInt16BE(offset + 7);
|
|
651
|
+
break;
|
|
652
|
+
}
|
|
653
|
+
if (offset + 2 < buffer.length) {
|
|
654
|
+
const segmentLength = buffer.readUInt16BE(offset + 2);
|
|
655
|
+
offset += 2 + segmentLength;
|
|
656
|
+
} else {
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
} else {
|
|
660
|
+
offset++;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
if (width === 0 || height === 0) {
|
|
664
|
+
throw new Error("Could not determine JPEG dimensions");
|
|
665
|
+
}
|
|
666
|
+
const pixelCount = width * height;
|
|
667
|
+
const data = new Uint8Array(pixelCount * 4);
|
|
668
|
+
let avgR = 0, avgG = 0, avgB = 0;
|
|
669
|
+
const sampleSize = Math.min(1e3, buffer.length);
|
|
670
|
+
for (let i = 0; i < sampleSize; i += 3) {
|
|
671
|
+
avgR += buffer[i] || 0;
|
|
672
|
+
avgG += buffer[i + 1] || 0;
|
|
673
|
+
avgB += buffer[i + 2] || 0;
|
|
674
|
+
}
|
|
675
|
+
avgR = Math.floor(avgR / (sampleSize / 3));
|
|
676
|
+
avgG = Math.floor(avgG / (sampleSize / 3));
|
|
677
|
+
avgB = Math.floor(avgB / (sampleSize / 3));
|
|
678
|
+
for (let y = 0; y < height; y++) {
|
|
679
|
+
for (let x = 0; x < width; x++) {
|
|
680
|
+
const i = (y * width + x) * 4;
|
|
681
|
+
const xRatio = x / width;
|
|
682
|
+
const yRatio = y / height;
|
|
683
|
+
const r = Math.floor(avgR * (0.5 + 0.5 * xRatio));
|
|
684
|
+
const g = Math.floor(avgG * (0.5 + 0.5 * yRatio));
|
|
685
|
+
const b = Math.floor(avgB * (0.5 + 0.5 * (xRatio + yRatio) / 2));
|
|
686
|
+
data[i] = Math.min(255, Math.max(0, r));
|
|
687
|
+
data[i + 1] = Math.min(255, Math.max(0, g));
|
|
688
|
+
data[i + 2] = Math.min(255, Math.max(0, b));
|
|
689
|
+
data[i + 3] = 255;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return {
|
|
693
|
+
data,
|
|
694
|
+
width,
|
|
695
|
+
height,
|
|
696
|
+
bitsPerPixel: 4
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Convert image data to grayscale
|
|
701
|
+
* @param imageData Original image data
|
|
702
|
+
* @returns Grayscale image data
|
|
703
|
+
*/
|
|
704
|
+
static toGrayscale(imageData) {
|
|
705
|
+
const { data, width, height } = imageData;
|
|
706
|
+
const grayscaleData = new Uint8Array(data.length);
|
|
707
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
708
|
+
const r = data[i];
|
|
709
|
+
const g = data[i + 1];
|
|
710
|
+
const b = data[i + 2];
|
|
711
|
+
const a = data[i + 3];
|
|
712
|
+
const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
|
|
713
|
+
grayscaleData[i] = gray;
|
|
714
|
+
grayscaleData[i + 1] = gray;
|
|
715
|
+
grayscaleData[i + 2] = gray;
|
|
716
|
+
grayscaleData[i + 3] = a;
|
|
717
|
+
}
|
|
718
|
+
return {
|
|
719
|
+
data: grayscaleData,
|
|
720
|
+
width,
|
|
721
|
+
height,
|
|
722
|
+
bitsPerPixel: imageData.bitsPerPixel
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Resize image data (simple nearest neighbor algorithm)
|
|
727
|
+
* @param imageData Original image data
|
|
728
|
+
* @param newWidth Target width
|
|
729
|
+
* @param newHeight Target height
|
|
730
|
+
* @returns Resized image data
|
|
731
|
+
*/
|
|
732
|
+
static resize(imageData, newWidth, newHeight) {
|
|
733
|
+
const { data, width, height, bitsPerPixel } = imageData;
|
|
734
|
+
const resizedData = new Uint8Array(newWidth * newHeight * bitsPerPixel);
|
|
735
|
+
const xRatio = width / newWidth;
|
|
736
|
+
const yRatio = height / newHeight;
|
|
737
|
+
for (let y = 0; y < newHeight; y++) {
|
|
738
|
+
for (let x = 0; x < newWidth; x++) {
|
|
739
|
+
const srcX = Math.floor(x * xRatio);
|
|
740
|
+
const srcY = Math.floor(y * yRatio);
|
|
741
|
+
const srcIndex = (srcY * width + srcX) * bitsPerPixel;
|
|
742
|
+
const destIndex = (y * newWidth + x) * bitsPerPixel;
|
|
743
|
+
for (let c = 0; c < bitsPerPixel; c++) {
|
|
744
|
+
resizedData[destIndex + c] = data[srcIndex + c];
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return {
|
|
749
|
+
data: resizedData,
|
|
750
|
+
width: newWidth,
|
|
751
|
+
height: newHeight,
|
|
752
|
+
bitsPerPixel
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
var ImageProcessor_default = ImageProcessor;
|
|
757
|
+
|
|
223
758
|
// src/helpers/ImageUtils.ts
|
|
224
759
|
var BLACK_PIXEL = 0;
|
|
225
760
|
var WHITE_PIXEL = 1;
|
|
@@ -231,14 +766,7 @@ var ImageUtils = class {
|
|
|
231
766
|
*/
|
|
232
767
|
static getPixels(image) {
|
|
233
768
|
return __async(this, null, function* () {
|
|
234
|
-
|
|
235
|
-
const bitsPerPixel = data.length / height / width;
|
|
236
|
-
return {
|
|
237
|
-
data,
|
|
238
|
-
width,
|
|
239
|
-
height,
|
|
240
|
-
bitsPerPixel
|
|
241
|
-
};
|
|
769
|
+
return yield ImageProcessor_default.getImageData(image);
|
|
242
770
|
});
|
|
243
771
|
}
|
|
244
772
|
/**
|
|
@@ -765,8 +1293,7 @@ var Printer = class {
|
|
|
765
1293
|
*/
|
|
766
1294
|
writeCommand(command) {
|
|
767
1295
|
return __async(this, null, function* () {
|
|
768
|
-
if (!this.usbDevice.opened)
|
|
769
|
-
yield this.usbDevice.openAndConfigure();
|
|
1296
|
+
if (!this.usbDevice.opened) yield this.usbDevice.openAndConfigure();
|
|
770
1297
|
yield command.write(this.usbDevice);
|
|
771
1298
|
});
|
|
772
1299
|
}
|
|
@@ -805,9 +1332,8 @@ var isWhitespace = (text) => text.trim() === "";
|
|
|
805
1332
|
var unsupportedUsbError = "usb-unsupported";
|
|
806
1333
|
var stringHelper = new StringUtils();
|
|
807
1334
|
var usbAgent;
|
|
808
|
-
var getUSB = () => __async(
|
|
809
|
-
if (usbAgent)
|
|
810
|
-
return usbAgent;
|
|
1335
|
+
var getUSB = () => __async(null, null, function* () {
|
|
1336
|
+
if (usbAgent) return usbAgent;
|
|
811
1337
|
if (typeof window !== "undefined") {
|
|
812
1338
|
if (navigator.usb) {
|
|
813
1339
|
usbAgent = navigator.usb;
|
|
@@ -820,12 +1346,12 @@ var getUSB = () => __async(void 0, null, function* () {
|
|
|
820
1346
|
}
|
|
821
1347
|
return usbAgent;
|
|
822
1348
|
});
|
|
823
|
-
var getDevices = () => __async(
|
|
1349
|
+
var getDevices = () => __async(null, null, function* () {
|
|
824
1350
|
const agent = yield getUSB();
|
|
825
1351
|
const devices = yield agent.getDevices();
|
|
826
1352
|
return devices.map((device) => new UsbDevice(device));
|
|
827
1353
|
});
|
|
828
|
-
var requestDevice = () => __async(
|
|
1354
|
+
var requestDevice = () => __async(null, null, function* () {
|
|
829
1355
|
const agent = yield getUSB();
|
|
830
1356
|
const device = yield agent.requestDevice({ filters: [] });
|
|
831
1357
|
if (device) {
|
|
@@ -924,8 +1450,7 @@ var UsbDevice = class {
|
|
|
924
1450
|
readString(length) {
|
|
925
1451
|
return __async(this, null, function* () {
|
|
926
1452
|
const bytes = yield this.readData(length);
|
|
927
|
-
if (bytes)
|
|
928
|
-
return stringHelper.toString(bytes);
|
|
1453
|
+
if (bytes) return stringHelper.toString(bytes);
|
|
929
1454
|
return void 0;
|
|
930
1455
|
});
|
|
931
1456
|
}
|
|
@@ -944,8 +1469,7 @@ var TSPLPrinter = class extends Printer {
|
|
|
944
1469
|
}
|
|
945
1470
|
static try(device) {
|
|
946
1471
|
return __async(this, null, function* () {
|
|
947
|
-
if (!device.opened)
|
|
948
|
-
yield device.openAndConfigure();
|
|
1472
|
+
if (!device.opened) yield device.openAndConfigure();
|
|
949
1473
|
const testCommand = new TSPLRawCommand("~!I");
|
|
950
1474
|
yield testCommand.write(device);
|
|
951
1475
|
const response = yield device.readString(64);
|
|
@@ -1004,7 +1528,7 @@ var PrinterService = class _PrinterService {
|
|
|
1004
1528
|
var labels_exports = {};
|
|
1005
1529
|
__export(labels_exports, {
|
|
1006
1530
|
BarCode: () => BarCode,
|
|
1007
|
-
Image: () =>
|
|
1531
|
+
Image: () => Image2,
|
|
1008
1532
|
Label: () => Label,
|
|
1009
1533
|
Line: () => Line,
|
|
1010
1534
|
Text: () => Text,
|
|
@@ -1098,8 +1622,10 @@ var Label = class extends Printable {
|
|
|
1098
1622
|
};
|
|
1099
1623
|
}
|
|
1100
1624
|
const fontBuffer = Buffer.from(font.data);
|
|
1625
|
+
const builtFont = fontkit.create(fontBuffer);
|
|
1626
|
+
const finalFont = builtFont.fonts ? builtFont.fonts[0] : builtFont;
|
|
1101
1627
|
this.fonts[font.name].fonts[key] = __spreadProps(__spreadValues({}, font), {
|
|
1102
|
-
font:
|
|
1628
|
+
font: finalFont,
|
|
1103
1629
|
alias: `${FONT_PREFIX}${this.fontCounter}.${this.fontExtension}`
|
|
1104
1630
|
});
|
|
1105
1631
|
this.fontCounter += 1;
|
|
@@ -1138,7 +1664,6 @@ var Label = class extends Printable {
|
|
|
1138
1664
|
const commands = yield this.fullCommand(language, 0, direction, mirror, 0, generator);
|
|
1139
1665
|
commands.push(generator.display());
|
|
1140
1666
|
const group = generator.commandGroup(commands);
|
|
1141
|
-
group.print(console.log);
|
|
1142
1667
|
return group;
|
|
1143
1668
|
});
|
|
1144
1669
|
}
|
|
@@ -1301,8 +1826,7 @@ var Text = class extends LabelField {
|
|
|
1301
1826
|
* @returns
|
|
1302
1827
|
*/
|
|
1303
1828
|
generateFormattedText() {
|
|
1304
|
-
if (!this.context)
|
|
1305
|
-
throw "context-not-set";
|
|
1829
|
+
if (!this.context) throw "context-not-set";
|
|
1306
1830
|
const rootNode = parse(this.content);
|
|
1307
1831
|
const { command } = this.generateFormattedRecursive(this.x, this.y, rootNode, this.font, []);
|
|
1308
1832
|
return command;
|
|
@@ -1358,8 +1882,7 @@ var Text = class extends LabelField {
|
|
|
1358
1882
|
* @returns
|
|
1359
1883
|
*/
|
|
1360
1884
|
generatePlainTextCore(content, initialX, initialY, font, features = []) {
|
|
1361
|
-
if (!this.context)
|
|
1362
|
-
throw "context-not-set";
|
|
1885
|
+
if (!this.context) throw "context-not-set";
|
|
1363
1886
|
const textWidhtFunction = this.textWithFunction;
|
|
1364
1887
|
let fullWidth = textWidhtFunction(content, font);
|
|
1365
1888
|
if (this.width) {
|
|
@@ -1451,8 +1974,7 @@ var Text = class extends LabelField {
|
|
|
1451
1974
|
}
|
|
1452
1975
|
textCommand(text, x, y, font, features) {
|
|
1453
1976
|
var _a, _b;
|
|
1454
|
-
if (!this.context)
|
|
1455
|
-
throw "no-context";
|
|
1977
|
+
if (!this.context) throw "no-context";
|
|
1456
1978
|
const finalFontSize = dotToPoint(font.size, (_b = (_a = this.context.config) == null ? void 0 : _a.dpi) != null ? _b : 203);
|
|
1457
1979
|
const finalFont = this.getFontName(font);
|
|
1458
1980
|
const finalX = Math.round(x);
|
|
@@ -1485,8 +2007,7 @@ var Text = class extends LabelField {
|
|
|
1485
2007
|
}
|
|
1486
2008
|
getFontName(font) {
|
|
1487
2009
|
var _a;
|
|
1488
|
-
if (!this.context)
|
|
1489
|
-
throw "no-context";
|
|
2010
|
+
if (!this.context) throw "no-context";
|
|
1490
2011
|
if (font.name == "default") {
|
|
1491
2012
|
return "default";
|
|
1492
2013
|
} else {
|
|
@@ -1537,7 +2058,7 @@ var BarCode = class extends LabelField {
|
|
|
1537
2058
|
};
|
|
1538
2059
|
|
|
1539
2060
|
// src/labels/fields/Image.ts
|
|
1540
|
-
var
|
|
2061
|
+
var Image2 = class _Image extends LabelField {
|
|
1541
2062
|
constructor(x, y, image) {
|
|
1542
2063
|
super();
|
|
1543
2064
|
this.x = x;
|