label-printer 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -271,6 +271,19 @@ function dotToPoint(dots, dpi) {
271
271
  const inch = dots / dpi;
272
272
  return Math.round(inch * pointsPerInch);
273
273
  }
274
+ function inToDot(inches, dpi, round = false) {
275
+ const res = inches * dpi;
276
+ if (round) return Math.round(res);
277
+ else return res;
278
+ }
279
+ function mmToDot(mm, dpi, round = false) {
280
+ const res = mm / 25.4 * dpi;
281
+ if (round) return Math.round(res);
282
+ else return res;
283
+ }
284
+ function unitToDot(value, dpi, unitSystem, round = false) {
285
+ return unitSystem === "imperial" ? inToDot(value, dpi, round) : mmToDot(value, dpi, round);
286
+ }
274
287
 
275
288
  // src/helpers/ImageDataParser.ts
276
289
  function parsePNG(buffer2) {
@@ -521,7 +534,6 @@ var ImageProcessor = class {
521
534
  */
522
535
  static getImageDataNode(image2, _target) {
523
536
  return __async(this, null, function* () {
524
- console.log("Processing image in Node.js environment");
525
537
  if (image2 instanceof Blob) {
526
538
  throw new Error("Blob input not supported in Node.js environment. Use file path or data URL instead.");
527
539
  }
@@ -690,7 +702,6 @@ var ImageProcessor = class {
690
702
  */
691
703
  static parse(buffer2, extension) {
692
704
  const normalizedExtension = extension.startsWith(".") ? extension.slice(1) : extension;
693
- console.log(`Parsing image with extension: ${normalizedExtension}`);
694
705
  if (normalizedExtension === "png") {
695
706
  return parsePNG(buffer2);
696
707
  } else if (normalizedExtension === "jpeg" || normalizedExtension === "jpg") {
@@ -1104,6 +1115,54 @@ ${widthBits} ${height}
1104
1115
  fs.writeFileSync(filePath, bytes);
1105
1116
  });
1106
1117
  }
1118
+ /**
1119
+ * Rotate a BW bitmap by 90, 180 or 270 degrees clockwise.
1120
+ * The bitmap uses 0=black, 1=white with MSB-first packing.
1121
+ */
1122
+ static rotateBWBitmap(bitmap2, rotation) {
1123
+ const srcWidthBytes = bitmap2.width;
1124
+ const srcWidthBits = srcWidthBytes * 8;
1125
+ const srcHeight = bitmap2.height;
1126
+ const src = bitmap2.bytes;
1127
+ const getBit = (col, row) => {
1128
+ const byteIndex = row * srcWidthBytes + (col >> 3);
1129
+ const bitIndex = 7 - (col & 7);
1130
+ return src[byteIndex] >> bitIndex & 1;
1131
+ };
1132
+ let dstWidthBits;
1133
+ let dstHeight;
1134
+ let getNewPixel;
1135
+ if (rotation === 90) {
1136
+ dstWidthBits = srcHeight;
1137
+ dstHeight = srcWidthBits;
1138
+ getNewPixel = (col, row) => getBit(srcWidthBits - 1 - row, col);
1139
+ } else if (rotation === 270) {
1140
+ dstWidthBits = srcHeight;
1141
+ dstHeight = srcWidthBits;
1142
+ getNewPixel = (col, row) => getBit(row, srcHeight - 1 - col);
1143
+ } else {
1144
+ dstWidthBits = srcWidthBits;
1145
+ dstHeight = srcHeight;
1146
+ getNewPixel = (col, row) => getBit(srcWidthBits - 1 - col, srcHeight - 1 - row);
1147
+ }
1148
+ const dstPad = dstWidthBits % 8 === 0 ? 0 : 8 - dstWidthBits % 8;
1149
+ const dstWidthBitsPadded = dstWidthBits + dstPad;
1150
+ const dstWidthBytes = dstWidthBitsPadded / 8;
1151
+ const dst = new Uint8Array(dstWidthBytes * dstHeight).fill(255);
1152
+ for (let row = 0; row < dstHeight; row++) {
1153
+ for (let col = 0; col < dstWidthBits; col++) {
1154
+ const bit = getNewPixel(col, row);
1155
+ const byteIndex = row * dstWidthBytes + (col >> 3);
1156
+ const mask = 1 << 7 - (col & 7);
1157
+ if (bit === 1) {
1158
+ dst[byteIndex] |= mask;
1159
+ } else {
1160
+ dst[byteIndex] &= ~mask & 255;
1161
+ }
1162
+ }
1163
+ }
1164
+ return { width: dstWidthBytes, height: dstHeight, bytes: dst };
1165
+ }
1107
1166
  static dilateOnce(bitmap2) {
1108
1167
  const widthBytes = bitmap2.width;
1109
1168
  const widthBits = widthBytes * 8;
@@ -1205,20 +1264,6 @@ var TSPLBitmapCommand = class _TSPLBitmapCommand extends TSPLVisualCommand {
1205
1264
  }
1206
1265
  };
1207
1266
 
1208
- // src/commands/tspl/types.ts
1209
- var alignmentToNumber = (alignment) => {
1210
- switch (alignment) {
1211
- case void 0:
1212
- return 0;
1213
- case "left":
1214
- return 1;
1215
- case "center":
1216
- return 2;
1217
- case "right":
1218
- return 3;
1219
- }
1220
- };
1221
-
1222
1267
  // src/commands/tspl/commands/basic/TSPLTextCommand.ts
1223
1268
  var TSPLTextCommand = class extends TSPLVisualCommand {
1224
1269
  constructor(content, x, y, font, rotation, xMultiplication, yMultiplication, alignment) {
@@ -1509,9 +1554,9 @@ var TSPLCommandGenerator = class _TSPLCommandGenerator {
1509
1554
  print(sets, copiesPerSet) {
1510
1555
  return new TSPLPrintCommand(sets, copiesPerSet);
1511
1556
  }
1512
- text(content, x, y, font, size) {
1557
+ text(content, x, y, font, size, rotation) {
1513
1558
  const fontName = font == "default" ? "0" : font;
1514
- return new TSPLTextCommand(content, x, y, fontName, 0, size, size, "left");
1559
+ return new TSPLTextCommand(content, x, y, fontName, rotation != null ? rotation : 0, size, size, "left");
1515
1560
  }
1516
1561
  upload(name, data) {
1517
1562
  return new TSPLDownload(name, data);
@@ -1538,10 +1583,10 @@ var TSPLCommandGenerator = class _TSPLCommandGenerator {
1538
1583
  image(image2, x, y, mode) {
1539
1584
  return new TSPLBitmapCommand(image2, x, y, mode);
1540
1585
  }
1541
- qrCode(content, width, x, y) {
1586
+ qrCode(content, width, x, y, rotation) {
1542
1587
  const cellCount = this.cellCount(content);
1543
1588
  const cellWidth = Math.round(width / cellCount);
1544
- return new TSPLQRCommand(`A${content}`, x, y, cellWidth, "H", "M");
1589
+ return new TSPLQRCommand(`A${content}`, x, y, cellWidth, "H", "M", rotation != null ? rotation : 0);
1545
1590
  }
1546
1591
  barCode(content, x, y, type, height, rotation, humanReadable, alignment, barWidth = 2) {
1547
1592
  const { narrow, wide } = _TSPLCommandGenerator.narrowWideFor(type, barWidth);
@@ -1591,6 +1636,20 @@ var TSPLCommandGenerator = class _TSPLCommandGenerator {
1591
1636
  };
1592
1637
  var TSPLCommandGenerator_default = new TSPLCommandGenerator();
1593
1638
 
1639
+ // src/commands/tspl/types.ts
1640
+ var alignmentToNumber = (alignment) => {
1641
+ switch (alignment) {
1642
+ case void 0:
1643
+ return 0;
1644
+ case "left":
1645
+ return 1;
1646
+ case "center":
1647
+ return 2;
1648
+ case "right":
1649
+ return 3;
1650
+ }
1651
+ };
1652
+
1594
1653
  // src/printers/index.ts
1595
1654
  var printers_exports = {};
1596
1655
  __export(printers_exports, {
@@ -2418,7 +2477,7 @@ var Printable = class {
2418
2477
  /**
2419
2478
  * Generates printable command for the given printer. Can be used to obtain a command for fields supported by the package then customizing it before printing
2420
2479
  * @param printer Printer to generate the command. Important because the command is printer language specific
2421
- * @returns A promise for a command. Most commands are syncronouse but some may require to access async resources
2480
+ * @returns A promise for a command. Most commands are synchronous but some may require to access async resources
2422
2481
  */
2423
2482
  commandForPrinter(printer, config) {
2424
2483
  return __async(this, null, function* () {
@@ -2437,8 +2496,105 @@ var Printable = class {
2437
2496
  }
2438
2497
  };
2439
2498
 
2499
+ // src/labels/types.ts
2500
+ var rotationForOrientation = (orientation) => {
2501
+ switch (orientation) {
2502
+ case "normal":
2503
+ return 0;
2504
+ case "left":
2505
+ return 90;
2506
+ case "upside-down":
2507
+ return 180;
2508
+ case "right":
2509
+ return 270;
2510
+ }
2511
+ };
2512
+
2440
2513
  // src/labels/Label.ts
2441
2514
  var fontkit = __toESM(require("fontkit"));
2515
+
2516
+ // src/labels/fields/superClasses/LabelField.ts
2517
+ var LabelField = class extends Printable {
2518
+ };
2519
+
2520
+ // src/labels/fields/superClasses/RotatableLabelField.ts
2521
+ var RotatableLabelField = class extends LabelField {
2522
+ constructor() {
2523
+ super(...arguments);
2524
+ this.rotation = 0;
2525
+ }
2526
+ setRotation(rotation) {
2527
+ this.rotation = rotation;
2528
+ }
2529
+ getRotation() {
2530
+ return this.rotation;
2531
+ }
2532
+ };
2533
+
2534
+ // src/labels/RotatableContainer.ts
2535
+ var RotatableContainer = class extends RotatableLabelField {
2536
+ /**
2537
+ * @param size Size in dots
2538
+ */
2539
+ constructor(size) {
2540
+ super();
2541
+ this.fields = [];
2542
+ this.size = size;
2543
+ }
2544
+ /**
2545
+ * Place fields in the container
2546
+ * @param fields
2547
+ */
2548
+ add(...fields) {
2549
+ this.fields.push(...fields);
2550
+ }
2551
+ commandForLanguage(language, config) {
2552
+ return __async(this, null, function* () {
2553
+ const commandList = yield Promise.all(this.fields.map((field) => this.rotationAdjustedCommand(field, language, config)));
2554
+ return this.commandGeneratorFor(language).commandGroup(commandList);
2555
+ });
2556
+ }
2557
+ rotationAdjustedCommand(field, language, config) {
2558
+ return __async(this, null, function* () {
2559
+ let originalRotation = void 0;
2560
+ let originalPosition = void 0;
2561
+ if (field.hasOwnProperty("rotation")) {
2562
+ const rotatableField = field;
2563
+ originalRotation = rotatableField.getRotation();
2564
+ rotatableField.setRotation((originalRotation + this.rotation) % 360);
2565
+ }
2566
+ if (typeof field.getPosition === "function" && typeof field.setPosition === "function") {
2567
+ const positionedField = field;
2568
+ originalPosition = positionedField.getPosition();
2569
+ positionedField.setPosition(this.adjustPosition(originalPosition));
2570
+ }
2571
+ const command = yield field.commandForLanguage(language, config);
2572
+ if (originalRotation !== void 0) {
2573
+ const rotatableField = field;
2574
+ rotatableField.setRotation(originalRotation);
2575
+ }
2576
+ if (originalPosition !== void 0) {
2577
+ const positionedField = field;
2578
+ positionedField.setPosition(originalPosition);
2579
+ }
2580
+ return command;
2581
+ });
2582
+ }
2583
+ adjustPosition(position) {
2584
+ switch (this.rotation) {
2585
+ case 90:
2586
+ return { x: this.size.height - position.y, y: position.x };
2587
+ case 180:
2588
+ return { x: this.size.width - position.x, y: this.size.height - position.y };
2589
+ case 270:
2590
+ return { x: position.y, y: this.size.width - position.x };
2591
+ default:
2592
+ return position;
2593
+ }
2594
+ }
2595
+ };
2596
+
2597
+ // src/labels/Label.ts
2442
2598
  var DEFAULT_FONT_WEIGHT = 400;
2443
2599
  var DEFAULT_FONT_STYLE = "normal";
2444
2600
  var FONT_PREFIX = "f";
@@ -2447,10 +2603,7 @@ var Label = class extends Printable {
2447
2603
  super();
2448
2604
  this.fonts = {};
2449
2605
  this.density = 8;
2450
- /**
2451
- * List of fields on the label
2452
- */
2453
- this.fields = [];
2606
+ this.orientation = "normal";
2454
2607
  this.fontCounter = 0;
2455
2608
  this._textWidthCorrectionFactor = 0.935;
2456
2609
  this.width = width;
@@ -2458,6 +2611,9 @@ var Label = class extends Printable {
2458
2611
  this.unitSystem = dimensionUnit;
2459
2612
  this.dpi = dpi;
2460
2613
  this.density = density;
2614
+ const widthInDots = unitToDot(width, dpi, dimensionUnit);
2615
+ const heightInDots = unitToDot(height, dpi, dimensionUnit);
2616
+ this.container = new RotatableContainer({ width: widthInDots, height: heightInDots });
2461
2617
  }
2462
2618
  /**
2463
2619
  * Configuration used when generating commands
@@ -2488,11 +2644,17 @@ var Label = class extends Printable {
2488
2644
  setTextWidthCorrectionFactor(factor) {
2489
2645
  this._textWidthCorrectionFactor = factor;
2490
2646
  }
2647
+ /**
2648
+ * Change the orientation the label is printed in. This will rotate all fields in the container
2649
+ * @param orientation
2650
+ */
2651
+ setOrientation(orientation) {
2652
+ this.orientation = orientation;
2653
+ this.container.setRotation(rotationForOrientation(orientation));
2654
+ }
2491
2655
  commandForLanguage(language, config) {
2492
2656
  return __async(this, null, function* () {
2493
- const configuration = config != null ? config : this.printConfig;
2494
- const commandList = yield Promise.all(this.fields.map((field) => field.commandForLanguage(language, configuration)));
2495
- return this.commandGeneratorFor(language).commandGroup(commandList);
2657
+ return yield this.container.commandForLanguage(language, config);
2496
2658
  });
2497
2659
  }
2498
2660
  /**
@@ -2500,7 +2662,7 @@ var Label = class extends Printable {
2500
2662
  * @param fields
2501
2663
  */
2502
2664
  add(...fields) {
2503
- this.fields.push(...fields);
2665
+ this.container.add(...fields);
2504
2666
  }
2505
2667
  /**
2506
2668
  * Register a font to be used. Use the name provided in components to use the font.
@@ -2566,11 +2728,17 @@ var Label = class extends Printable {
2566
2728
  */
2567
2729
  fullCommand(language, gap, direction, mirror = false, gapOffset = 0, generator) {
2568
2730
  return __async(this, null, function* () {
2731
+ let finalDimations;
2732
+ if (this.orientation == "normal" || this.orientation == "upside-down") {
2733
+ finalDimations = { width: this.width, height: this.height };
2734
+ } else {
2735
+ finalDimations = { width: this.height, height: this.width };
2736
+ }
2569
2737
  const commands = [
2570
2738
  this.fontUploadCommands(generator),
2571
2739
  generator.setUp(
2572
- this.width,
2573
- this.height,
2740
+ finalDimations.width,
2741
+ finalDimations.height,
2574
2742
  gap,
2575
2743
  gapOffset,
2576
2744
  direction,
@@ -2601,10 +2769,10 @@ var Label = class extends Printable {
2601
2769
  const family = this.fonts[font.name];
2602
2770
  if (!family) return null;
2603
2771
  const style = (_a = font.style) != null ? _a : DEFAULT_FONT_STYLE;
2604
- const weigth = (_b = font.weight) != null ? _b : DEFAULT_FONT_WEIGHT;
2772
+ const weight = (_b = font.weight) != null ? _b : DEFAULT_FONT_WEIGHT;
2605
2773
  const fontKeys = Object.keys(family.fonts);
2606
2774
  const exactMatch = fontKeys.find(
2607
- (key) => family.fonts[key].style == style && family.fonts[key].weight == weigth
2775
+ (key) => family.fonts[key].style == style && family.fonts[key].weight == weight
2608
2776
  );
2609
2777
  if (exactMatch) {
2610
2778
  return family.fonts[exactMatch];
@@ -2614,7 +2782,7 @@ var Label = class extends Printable {
2614
2782
  let weigthDiff = 99999999;
2615
2783
  let selectedKey = "";
2616
2784
  sameStyleKeys.forEach((key) => {
2617
- const diff = Math.abs(weigth - family.fonts[key].weight);
2785
+ const diff = Math.abs(weight - family.fonts[key].weight);
2618
2786
  if (diff < weigthDiff) {
2619
2787
  weigthDiff = diff;
2620
2788
  selectedKey = key;
@@ -2642,10 +2810,6 @@ var Label = class extends Printable {
2642
2810
  }
2643
2811
  };
2644
2812
 
2645
- // src/labels/fields/LabelField.ts
2646
- var LabelField = class extends Printable {
2647
- };
2648
-
2649
2813
  // src/labels/fields/Line.ts
2650
2814
  var Line = class extends LabelField {
2651
2815
  /**
@@ -2674,7 +2838,7 @@ var UNDERLINE_TAG = "u";
2674
2838
  var STRIKE_TAG = ["s", "del", "strike"];
2675
2839
  var PARAGRAPH_TAG = "p";
2676
2840
  var BREAK_TAG = "br";
2677
- var Text = class extends LabelField {
2841
+ var Text = class extends RotatableLabelField {
2678
2842
  constructor(content, x, y, formatted = true) {
2679
2843
  super();
2680
2844
  this.font = { name: "default", size: 10 };
@@ -2704,6 +2868,13 @@ var Text = class extends LabelField {
2704
2868
  }
2705
2869
  return false;
2706
2870
  }
2871
+ setPosition(position) {
2872
+ this.x = position.x;
2873
+ this.y = position.y;
2874
+ }
2875
+ getPosition() {
2876
+ return { x: this.x, y: this.y };
2877
+ }
2707
2878
  /**
2708
2879
  * Sets the field to single line
2709
2880
  * @param width Max width of the text. Leave it undefined to allow the field to grow
@@ -2724,13 +2895,81 @@ var Text = class extends LabelField {
2724
2895
  this.height = height;
2725
2896
  }
2726
2897
  /**
2727
- * Set a font to use as a base. If no formatting is set on the text with a html tag, this will be used
2898
+ * Set a font to use as a base. If no formatting is set on the text with a html tag, this will be used
2728
2899
  * Note: The font name either has to be a built in font on your printer or a font
2729
2900
  * that is registered on the label using 'registerFont'.
2730
2901
  */
2731
2902
  setFont(font) {
2732
2903
  this.font = font;
2733
2904
  }
2905
+ // --- Rotation-aware position helpers ---
2906
+ /** Advance cursor by `amount` along the character direction */
2907
+ advanceChar(x, y, amount) {
2908
+ switch (this.rotation) {
2909
+ case 0:
2910
+ return { x: x + amount, y };
2911
+ case 90:
2912
+ return { x, y: y + amount };
2913
+ // 90° CW: chars go downward
2914
+ case 180:
2915
+ return { x: x - amount, y };
2916
+ case 270:
2917
+ return { x, y: y - amount };
2918
+ }
2919
+ }
2920
+ /** Advance to the next line (reset char axis, advance in line direction) */
2921
+ advanceLine(x, y, lineHeight) {
2922
+ switch (this.rotation) {
2923
+ case 0:
2924
+ return { x: this.x, y: y + lineHeight };
2925
+ case 90:
2926
+ return { x: x - lineHeight, y: this.y };
2927
+ // 90° CW: lines stack leftward (-x)
2928
+ case 180:
2929
+ return { x: this.x, y: y - lineHeight };
2930
+ case 270:
2931
+ return { x: x + lineHeight, y: this.y };
2932
+ }
2933
+ }
2934
+ /** How far into the current line the cursor is (for width constraint calc) */
2935
+ charOffset(x, y) {
2936
+ switch (this.rotation) {
2937
+ case 0:
2938
+ return x - this.x;
2939
+ case 90:
2940
+ return y - this.y;
2941
+ // 90° CW: char axis is +y
2942
+ case 180:
2943
+ return this.x - x;
2944
+ case 270:
2945
+ return this.y - y;
2946
+ }
2947
+ }
2948
+ /** How many lines deep we are (for height constraint calc) */
2949
+ lineOffset(x, y) {
2950
+ switch (this.rotation) {
2951
+ case 0:
2952
+ return y - this.y;
2953
+ case 90:
2954
+ return this.x - x;
2955
+ // 90° CW: line axis is -x
2956
+ case 180:
2957
+ return this.y - y;
2958
+ case 270:
2959
+ return x - this.x;
2960
+ }
2961
+ }
2962
+ /** Whether the cursor is at the start of its line (char axis at origin) */
2963
+ atLineStart(x, y) {
2964
+ switch (this.rotation) {
2965
+ case 0:
2966
+ case 180:
2967
+ return x === this.x;
2968
+ case 90:
2969
+ case 270:
2970
+ return y === this.y;
2971
+ }
2972
+ }
2734
2973
  commandForLanguage(language, config) {
2735
2974
  return __async(this, null, function* () {
2736
2975
  this.context = {
@@ -2777,9 +3016,10 @@ var Text = class extends LabelField {
2777
3016
  const elementNode = rootNode;
2778
3017
  const tag = elementNode.rawTagName;
2779
3018
  if (tag == BREAK_TAG) {
3019
+ const linePos = this.advanceLine(initialX, initialY, font.size + this.lineSpacing);
2780
3020
  return {
2781
- x: this.x,
2782
- y: initialY + font.size + this.lineSpacing,
3021
+ x: linePos.x,
3022
+ y: linePos.y,
2783
3023
  command: this.context.generator.commandGroup([])
2784
3024
  };
2785
3025
  }
@@ -2798,9 +3038,10 @@ var Text = class extends LabelField {
2798
3038
  baseFont.style = "italic";
2799
3039
  }
2800
3040
  if (tag == PARAGRAPH_TAG) {
2801
- if (initialX != this.x) {
2802
- currentX = this.x;
2803
- currentY = initialY + baseFont.size + this.lineSpacing;
3041
+ if (!this.atLineStart(initialX, initialY)) {
3042
+ const linePos = this.advanceLine(initialX, initialY, baseFont.size + this.lineSpacing);
3043
+ currentX = linePos.x;
3044
+ currentY = linePos.y;
2804
3045
  }
2805
3046
  }
2806
3047
  elementNode.childNodes.forEach((node) => {
@@ -2811,8 +3052,9 @@ var Text = class extends LabelField {
2811
3052
  });
2812
3053
  if (tag == PARAGRAPH_TAG) {
2813
3054
  if (!this.endsWithBreak(elementNode)) {
2814
- currentX = this.x;
2815
- currentY += baseFont.size + this.lineSpacing;
3055
+ const linePos = this.advanceLine(currentX, currentY, baseFont.size + this.lineSpacing);
3056
+ currentX = linePos.x;
3057
+ currentY = linePos.y;
2816
3058
  }
2817
3059
  }
2818
3060
  return {
@@ -2832,21 +3074,23 @@ var Text = class extends LabelField {
2832
3074
  const textWidhtFunction = this.textWidthFunction;
2833
3075
  let fullWidth = textWidhtFunction(content, font);
2834
3076
  if (this.width) {
2835
- const initialPadding = initialX - this.x;
3077
+ const initialPadding = this.charOffset(initialX, initialY);
2836
3078
  let rowWidth = this.width - initialPadding;
2837
3079
  if (rowWidth <= 0) {
2838
3080
  rowWidth = this.width;
2839
- initialX = this.x;
2840
- initialY += font.size + this.lineSpacing;
3081
+ const linePos = this.advanceLine(initialX, initialY, font.size + this.lineSpacing);
3082
+ initialX = linePos.x;
3083
+ initialY = linePos.y;
2841
3084
  }
2842
- if (initialX == this.x) {
3085
+ if (this.atLineStart(initialX, initialY)) {
2843
3086
  content = content.trimStart();
2844
3087
  fullWidth = textWidhtFunction(content, font);
2845
3088
  }
2846
3089
  if (fullWidth <= rowWidth) {
3090
+ const end = this.advanceChar(initialX, initialY, fullWidth);
2847
3091
  return {
2848
- x: initialX + fullWidth,
2849
- y: initialY,
3092
+ x: end.x,
3093
+ y: end.y,
2850
3094
  command: this.textCommand(content, initialX, initialY, font, features)
2851
3095
  };
2852
3096
  } else {
@@ -2860,8 +3104,9 @@ var Text = class extends LabelField {
2860
3104
  let finalY = y;
2861
3105
  do {
2862
3106
  if (remainingWidth < rowWidth) {
2863
- finalX = x + remainingWidth;
2864
- finalY = y;
3107
+ const end = this.advanceChar(x, y, remainingWidth);
3108
+ finalX = end.x;
3109
+ finalY = end.y;
2865
3110
  commands.push(this.textCommand(remainingContent, x, y, font, features));
2866
3111
  remainingContent = "";
2867
3112
  } else {
@@ -2879,8 +3124,9 @@ var Text = class extends LabelField {
2879
3124
  let originalRowEndIndex = rowEndIndex;
2880
3125
  rowWidth = this.width;
2881
3126
  if (rowEndIndex < 0) {
2882
- x = this.x;
2883
- y += font.size + this.lineSpacing;
3127
+ const linePos2 = this.advanceLine(x, y, font.size + this.lineSpacing);
3128
+ x = linePos2.x;
3129
+ y = linePos2.y;
2884
3130
  continue;
2885
3131
  }
2886
3132
  while (!(!isWhitespace(remainingContent.charAt(rowEndIndex)) && (rowEndIndex == remainingContent.length - 1 || isWhitespace(remainingContent.charAt(rowEndIndex + 1))) || isBreakAfterChar(remainingContent.charAt(rowEndIndex))) && rowEndIndex > 0) {
@@ -2958,12 +3204,14 @@ var Text = class extends LabelField {
2958
3204
  const thisRow = remainingContent.substring(0, rowEndIndex + 1);
2959
3205
  commands.push(this.textCommand(thisRow, x, y, font, features));
2960
3206
  if (nextRowStartIndex == remainingContent.length) {
2961
- finalX = x + remainingWidth;
2962
- finalY = y;
3207
+ const end = this.advanceChar(x, y, remainingWidth);
3208
+ finalX = end.x;
3209
+ finalY = end.y;
2963
3210
  }
2964
- x = this.x;
2965
- y += font.size + this.lineSpacing;
2966
- currentHeight = y - this.y;
3211
+ const linePos = this.advanceLine(x, y, font.size + this.lineSpacing);
3212
+ x = linePos.x;
3213
+ y = linePos.y;
3214
+ currentHeight = this.lineOffset(x, y);
2967
3215
  remainingContent = remainingContent.substring(nextRowStartIndex);
2968
3216
  remainingWidth = textWidhtFunction(remainingContent, font);
2969
3217
  }
@@ -2980,9 +3228,10 @@ var Text = class extends LabelField {
2980
3228
  };
2981
3229
  }
2982
3230
  } else {
3231
+ const end = this.advanceChar(initialX, initialY, fullWidth);
2983
3232
  return {
2984
- x: initialX + fullWidth,
2985
- y: initialY,
3233
+ x: end.x,
3234
+ y: end.y,
2986
3235
  command: this.textCommand(content, initialX, initialY, font, features)
2987
3236
  };
2988
3237
  }
@@ -2995,7 +3244,7 @@ var Text = class extends LabelField {
2995
3244
  const finalX = Math.round(x);
2996
3245
  const finalY = Math.round(y);
2997
3246
  let commands = [];
2998
- const textCommand = this.context.generator.text(text, finalX, finalY, finalFont, finalFontSize);
3247
+ const textCommand = this.context.generator.text(text, finalX, finalY, finalFont, finalFontSize, this.rotation);
2999
3248
  if (features.length == 0) {
3000
3249
  return textCommand;
3001
3250
  } else {
@@ -3012,13 +3261,28 @@ var Text = class extends LabelField {
3012
3261
  return this.context.generator.commandGroup(commands);
3013
3262
  }
3014
3263
  textLineCommand(width, x, y, lineHeight, linePercentage, fontSize) {
3015
- const sy = Math.round(y + fontSize * linePercentage - lineHeight / 2);
3016
- const sx = Math.round(x);
3017
- return this.context.generator.line(
3018
- { x: sx, y: sy },
3019
- { x: sx + width, y: sy },
3020
- lineHeight
3021
- );
3264
+ const offset = Math.round(fontSize * linePercentage - lineHeight / 2);
3265
+ let start;
3266
+ let end;
3267
+ switch (this.rotation) {
3268
+ case 0:
3269
+ start = { x: Math.round(x), y: Math.round(y) + offset };
3270
+ end = { x: Math.round(x) + Math.round(width), y: start.y };
3271
+ break;
3272
+ case 90:
3273
+ start = { x: Math.round(x) - offset, y: Math.round(y) };
3274
+ end = { x: start.x, y: Math.round(y) + Math.round(width) };
3275
+ break;
3276
+ case 180:
3277
+ start = { x: Math.round(x), y: Math.round(y) - offset };
3278
+ end = { x: Math.round(x) - Math.round(width), y: start.y };
3279
+ break;
3280
+ case 270:
3281
+ start = { x: Math.round(x) + offset, y: Math.round(y) };
3282
+ end = { x: start.x, y: Math.round(y) - Math.round(width) };
3283
+ break;
3284
+ }
3285
+ return this.context.generator.line(start, end, lineHeight);
3022
3286
  }
3023
3287
  getFontName(font) {
3024
3288
  var _a;
@@ -3032,22 +3296,25 @@ var Text = class extends LabelField {
3032
3296
  get textWidthFunction() {
3033
3297
  var _a, _b, _c;
3034
3298
  if (this.font.name == "default") {
3035
- return this.defaultTextWidth;
3299
+ return (text, font) => this.defaultTextWidth(text, font);
3036
3300
  } else {
3037
- return (_c = (_b = (_a = this.context) == null ? void 0 : _a.config) == null ? void 0 : _b.textWidth) != null ? _c : this.defaultTextWidth;
3301
+ return (_c = (_b = (_a = this.context) == null ? void 0 : _a.config) == null ? void 0 : _b.textWidth) != null ? _c : (text, font) => this.defaultTextWidth(text, font);
3038
3302
  }
3039
3303
  }
3040
3304
  /**
3041
- * This function is used to calculate the font size if no
3042
- * print config is provided. This will asume that the font has square characters
3305
+ * Fallback width estimate when no font metrics are available.
3306
+ * Uses the TSPL x-multiplication value (dotToPoint of the dot size) as
3307
+ * the per-character width, which matches the rendered width of font "0".
3043
3308
  */
3044
3309
  defaultTextWidth(text, font) {
3045
- return text.length * font.size;
3310
+ var _a, _b, _c;
3311
+ const dpi = (_c = (_b = (_a = this.context) == null ? void 0 : _a.config) == null ? void 0 : _b.dpi) != null ? _c : 203;
3312
+ return text.length * dotToPoint(font.size, dpi);
3046
3313
  }
3047
3314
  };
3048
3315
 
3049
3316
  // src/labels/fields/BarCode.ts
3050
- var BarCode = class extends LabelField {
3317
+ var BarCode = class extends RotatableLabelField {
3051
3318
  /**
3052
3319
  * @param content Content to encode
3053
3320
  * @param x X coordinate in dots
@@ -3065,16 +3332,19 @@ var BarCode = class extends LabelField {
3065
3332
  this.type = type;
3066
3333
  this.height = height;
3067
3334
  this.barWidth = barWidth;
3068
- this.rotation = 0;
3069
3335
  this.humanReadable = "none";
3070
3336
  this.alignment = "left";
3071
3337
  }
3072
- setRotation(rotation) {
3073
- this.rotation = rotation;
3074
- }
3075
3338
  setHumanReadable(humanReadable) {
3076
3339
  this.humanReadable = humanReadable;
3077
3340
  }
3341
+ setPosition(position) {
3342
+ this.x = position.x;
3343
+ this.y = position.y;
3344
+ }
3345
+ getPosition() {
3346
+ return { x: this.x, y: this.y };
3347
+ }
3078
3348
  commandForLanguage(language, _config) {
3079
3349
  return __async(this, null, function* () {
3080
3350
  return yield this.commandGeneratorFor(language).barCode(this.content, this.x, this.y, this.type, this.height, this.rotation, this.humanReadable, this.alignment, this.barWidth);
@@ -3083,16 +3353,43 @@ var BarCode = class extends LabelField {
3083
3353
  };
3084
3354
 
3085
3355
  // src/labels/fields/Image.ts
3086
- var Image2 = class _Image extends LabelField {
3356
+ var Image2 = class _Image extends RotatableLabelField {
3087
3357
  constructor(x, y, image2) {
3088
3358
  super();
3089
3359
  this.x = x;
3090
3360
  this.y = y;
3091
3361
  this.image = image2;
3092
3362
  }
3363
+ setPosition(position) {
3364
+ this.x = position.x;
3365
+ this.y = position.y;
3366
+ }
3367
+ getPosition() {
3368
+ return { x: this.x, y: this.y };
3369
+ }
3093
3370
  commandForLanguage(language, _config) {
3094
3371
  return __async(this, null, function* () {
3095
- return yield this.commandGeneratorFor(language).image(this.image, this.x, this.y);
3372
+ if (this.rotation === 0) {
3373
+ return yield this.commandGeneratorFor(language).image(this.image, this.x, this.y);
3374
+ }
3375
+ const srcW = this.image.width * 8;
3376
+ const srcH = this.image.height;
3377
+ const bitmap2 = ImageUtils.rotateBWBitmap(this.image, 360 - this.rotation);
3378
+ let dx = this.x;
3379
+ let dy = this.y;
3380
+ switch (this.rotation) {
3381
+ case 90:
3382
+ dx = this.x - srcH;
3383
+ break;
3384
+ case 180:
3385
+ dx = this.x - srcW;
3386
+ dy = this.y - srcH;
3387
+ break;
3388
+ case 270:
3389
+ dy = this.y - srcW;
3390
+ break;
3391
+ }
3392
+ return yield this.commandGeneratorFor(language).image(bitmap2, dx, dy);
3096
3393
  });
3097
3394
  }
3098
3395
  /**
@@ -3113,7 +3410,7 @@ var Image2 = class _Image extends LabelField {
3113
3410
  };
3114
3411
 
3115
3412
  // src/labels/fields/QRCode.ts
3116
- var QRCode = class extends LabelField {
3413
+ var QRCode = class extends RotatableLabelField {
3117
3414
  constructor(content, x, y, width) {
3118
3415
  super();
3119
3416
  this.content = content;
@@ -3121,9 +3418,16 @@ var QRCode = class extends LabelField {
3121
3418
  this.y = y;
3122
3419
  this.width = width;
3123
3420
  }
3421
+ setPosition(position) {
3422
+ this.x = position.x;
3423
+ this.y = position.y;
3424
+ }
3425
+ getPosition() {
3426
+ return { x: this.x, y: this.y };
3427
+ }
3124
3428
  commandForLanguage(language, config) {
3125
3429
  return __async(this, null, function* () {
3126
- return yield this.commandGeneratorFor(language).qrCode(this.content, this.width, this.x, this.y);
3430
+ return yield this.commandGeneratorFor(language).qrCode(this.content, this.width, this.x, this.y, this.rotation);
3127
3431
  });
3128
3432
  }
3129
3433
  };