label-printer 0.10.0 → 0.11.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.d.mts CHANGED
@@ -97,11 +97,11 @@ type BWBitmap = BitmapLike;
97
97
  interface CommandGenerator<T extends Command> {
98
98
  commandGroup: (commands: T[]) => CommandGroup<T>;
99
99
  print: (sets: number, copiesPerSet: number) => T;
100
- text: (content: string, x: number, y: number, font: string | "default", size: number) => T;
100
+ text: (content: string, x: number, y: number, font: string | "default", size: number, rotation?: Rotation) => T;
101
101
  upload: (name: string, data: ArrayBuffer | Uint8Array) => T;
102
102
  line: (start: Point, end: Point, thickness: number) => T;
103
103
  image: (image: BitmapLike, x: number, y: number, mode?: GraphicMode) => T;
104
- qrCode: (content: string, width: number, x: number, y: number) => T;
104
+ qrCode: (content: string, width: number, x: number, y: number, rotation?: Rotation) => T;
105
105
  barCode: (content: string, x: number, y: number, type: BarcodeType, height: number, rotation: Rotation, humanReadable: BarcodeHumanReable, alignment: Alignment, barWidth?: number) => T;
106
106
  /**
107
107
  * Should instruct the printer to display the image of the label on its screen instead of printing it
@@ -413,13 +413,13 @@ declare class TSPLDensityCommand extends TSPLCommand {
413
413
  declare class TSPLCommandGenerator implements CommandGenerator<TSPLCommand> {
414
414
  commandGroup(commands: TSPLCommand[]): TSPLCommandGroup;
415
415
  print(sets: number, copiesPerSet: number): TSPLCommand;
416
- text(content: string, x: number, y: number, font: string | "default", size: number): TSPLCommand;
416
+ text(content: string, x: number, y: number, font: string | "default", size: number, rotation?: Rotation): TSPLCommand;
417
417
  upload(name: string, data: ArrayBuffer | Uint8Array): TSPLCommand;
418
418
  setUp(width: number, height: number, gap: number, offset: number, direction: LabelDirection, mirror: boolean | undefined, unitSystem: UnitSystem, density: number): TSPLCommand;
419
419
  display(): TSPLCommandGroup;
420
420
  line(start: Point, end: Point, thickness: number): TSPLCommand;
421
421
  image(image: BitmapLike, x: number, y: number, mode?: GraphicMode | undefined): TSPLCommand;
422
- qrCode(content: string, width: number, x: number, y: number): TSPLCommand;
422
+ qrCode(content: string, width: number, x: number, y: number, rotation?: Rotation): TSPLCommand;
423
423
  barCode(content: string, x: number, y: number, type: BarcodeType, height: number, rotation: Rotation, humanReadable: BarcodeHumanReable, alignment: Alignment, barWidth?: number): TSPLCommand;
424
424
  /**
425
425
  * Calculates the narrow and wide element widths for a barcode type based on the
@@ -670,6 +670,7 @@ declare class Text extends LabelField {
670
670
  private readonly formatted;
671
671
  private font;
672
672
  private type;
673
+ private rotation;
673
674
  private context;
674
675
  private readonly lineSpacing;
675
676
  /**
@@ -701,6 +702,21 @@ declare class Text extends LabelField {
701
702
  * that is registered on the label using 'registerFont'.
702
703
  */
703
704
  setFont(font: FontOption): void;
705
+ /**
706
+ * Set the rotation of the text field. All text commands in this field will be rotated.
707
+ * For multiline text, lines advance perpendicular to the character direction.
708
+ */
709
+ setRotation(rotation: Rotation): void;
710
+ /** Advance cursor by `amount` along the character direction */
711
+ private advanceChar;
712
+ /** Advance to the next line (reset char axis, advance in line direction) */
713
+ private advanceLine;
714
+ /** How far into the current line the cursor is (for width constraint calc) */
715
+ private charOffset;
716
+ /** How many lines deep we are (for height constraint calc) */
717
+ private lineOffset;
718
+ /** Whether the cursor is at the start of its line (char axis at origin) */
719
+ private atLineStart;
704
720
  commandForLanguage(language: PrinterLanguage, config?: PrintConfig): Promise<Command>;
705
721
  /**
706
722
  * Generate commands for formatted text
@@ -727,8 +743,9 @@ declare class Text extends LabelField {
727
743
  private getFontName;
728
744
  private get textWidthFunction();
729
745
  /**
730
- * This function is used to calculate the font size if no
731
- * print config is provided. This will asume that the font has square characters
746
+ * Fallback width estimate when no font metrics are available.
747
+ * Uses the TSPL x-multiplication value (dotToPoint of the dot size) as
748
+ * the per-character width, which matches the rendered width of font "0".
732
749
  */
733
750
  private defaultTextWidth;
734
751
  }
@@ -768,7 +785,9 @@ declare class Image extends LabelField {
768
785
  */
769
786
  private readonly y;
770
787
  private readonly image;
788
+ private rotation;
771
789
  constructor(x: number, y: number, image: BitmapLike);
790
+ setRotation(rotation: Rotation): void;
772
791
  commandForLanguage(language: PrinterLanguage, _config?: PrintConfig | undefined): Promise<Command>;
773
792
  /**
774
793
  * Create an image field for an image
@@ -787,7 +806,9 @@ declare class QRCode extends LabelField {
787
806
  private readonly x;
788
807
  private readonly y;
789
808
  private readonly width;
809
+ private rotation;
790
810
  constructor(content: string, x: number, y: number, width: number);
811
+ setRotation(rotation: Rotation): void;
791
812
  commandForLanguage(language: PrinterLanguage, config?: PrintConfig | undefined): Promise<Command>;
792
813
  }
793
814
 
package/dist/index.d.ts CHANGED
@@ -97,11 +97,11 @@ type BWBitmap = BitmapLike;
97
97
  interface CommandGenerator<T extends Command> {
98
98
  commandGroup: (commands: T[]) => CommandGroup<T>;
99
99
  print: (sets: number, copiesPerSet: number) => T;
100
- text: (content: string, x: number, y: number, font: string | "default", size: number) => T;
100
+ text: (content: string, x: number, y: number, font: string | "default", size: number, rotation?: Rotation) => T;
101
101
  upload: (name: string, data: ArrayBuffer | Uint8Array) => T;
102
102
  line: (start: Point, end: Point, thickness: number) => T;
103
103
  image: (image: BitmapLike, x: number, y: number, mode?: GraphicMode) => T;
104
- qrCode: (content: string, width: number, x: number, y: number) => T;
104
+ qrCode: (content: string, width: number, x: number, y: number, rotation?: Rotation) => T;
105
105
  barCode: (content: string, x: number, y: number, type: BarcodeType, height: number, rotation: Rotation, humanReadable: BarcodeHumanReable, alignment: Alignment, barWidth?: number) => T;
106
106
  /**
107
107
  * Should instruct the printer to display the image of the label on its screen instead of printing it
@@ -413,13 +413,13 @@ declare class TSPLDensityCommand extends TSPLCommand {
413
413
  declare class TSPLCommandGenerator implements CommandGenerator<TSPLCommand> {
414
414
  commandGroup(commands: TSPLCommand[]): TSPLCommandGroup;
415
415
  print(sets: number, copiesPerSet: number): TSPLCommand;
416
- text(content: string, x: number, y: number, font: string | "default", size: number): TSPLCommand;
416
+ text(content: string, x: number, y: number, font: string | "default", size: number, rotation?: Rotation): TSPLCommand;
417
417
  upload(name: string, data: ArrayBuffer | Uint8Array): TSPLCommand;
418
418
  setUp(width: number, height: number, gap: number, offset: number, direction: LabelDirection, mirror: boolean | undefined, unitSystem: UnitSystem, density: number): TSPLCommand;
419
419
  display(): TSPLCommandGroup;
420
420
  line(start: Point, end: Point, thickness: number): TSPLCommand;
421
421
  image(image: BitmapLike, x: number, y: number, mode?: GraphicMode | undefined): TSPLCommand;
422
- qrCode(content: string, width: number, x: number, y: number): TSPLCommand;
422
+ qrCode(content: string, width: number, x: number, y: number, rotation?: Rotation): TSPLCommand;
423
423
  barCode(content: string, x: number, y: number, type: BarcodeType, height: number, rotation: Rotation, humanReadable: BarcodeHumanReable, alignment: Alignment, barWidth?: number): TSPLCommand;
424
424
  /**
425
425
  * Calculates the narrow and wide element widths for a barcode type based on the
@@ -670,6 +670,7 @@ declare class Text extends LabelField {
670
670
  private readonly formatted;
671
671
  private font;
672
672
  private type;
673
+ private rotation;
673
674
  private context;
674
675
  private readonly lineSpacing;
675
676
  /**
@@ -701,6 +702,21 @@ declare class Text extends LabelField {
701
702
  * that is registered on the label using 'registerFont'.
702
703
  */
703
704
  setFont(font: FontOption): void;
705
+ /**
706
+ * Set the rotation of the text field. All text commands in this field will be rotated.
707
+ * For multiline text, lines advance perpendicular to the character direction.
708
+ */
709
+ setRotation(rotation: Rotation): void;
710
+ /** Advance cursor by `amount` along the character direction */
711
+ private advanceChar;
712
+ /** Advance to the next line (reset char axis, advance in line direction) */
713
+ private advanceLine;
714
+ /** How far into the current line the cursor is (for width constraint calc) */
715
+ private charOffset;
716
+ /** How many lines deep we are (for height constraint calc) */
717
+ private lineOffset;
718
+ /** Whether the cursor is at the start of its line (char axis at origin) */
719
+ private atLineStart;
704
720
  commandForLanguage(language: PrinterLanguage, config?: PrintConfig): Promise<Command>;
705
721
  /**
706
722
  * Generate commands for formatted text
@@ -727,8 +743,9 @@ declare class Text extends LabelField {
727
743
  private getFontName;
728
744
  private get textWidthFunction();
729
745
  /**
730
- * This function is used to calculate the font size if no
731
- * print config is provided. This will asume that the font has square characters
746
+ * Fallback width estimate when no font metrics are available.
747
+ * Uses the TSPL x-multiplication value (dotToPoint of the dot size) as
748
+ * the per-character width, which matches the rendered width of font "0".
732
749
  */
733
750
  private defaultTextWidth;
734
751
  }
@@ -768,7 +785,9 @@ declare class Image extends LabelField {
768
785
  */
769
786
  private readonly y;
770
787
  private readonly image;
788
+ private rotation;
771
789
  constructor(x: number, y: number, image: BitmapLike);
790
+ setRotation(rotation: Rotation): void;
772
791
  commandForLanguage(language: PrinterLanguage, _config?: PrintConfig | undefined): Promise<Command>;
773
792
  /**
774
793
  * Create an image field for an image
@@ -787,7 +806,9 @@ declare class QRCode extends LabelField {
787
806
  private readonly x;
788
807
  private readonly y;
789
808
  private readonly width;
809
+ private rotation;
790
810
  constructor(content: string, x: number, y: number, width: number);
811
+ setRotation(rotation: Rotation): void;
791
812
  commandForLanguage(language: PrinterLanguage, config?: PrintConfig | undefined): Promise<Command>;
792
813
  }
793
814
 
package/dist/index.js CHANGED
@@ -521,7 +521,6 @@ var ImageProcessor = class {
521
521
  */
522
522
  static getImageDataNode(image2, _target) {
523
523
  return __async(this, null, function* () {
524
- console.log("Processing image in Node.js environment");
525
524
  if (image2 instanceof Blob) {
526
525
  throw new Error("Blob input not supported in Node.js environment. Use file path or data URL instead.");
527
526
  }
@@ -690,7 +689,6 @@ var ImageProcessor = class {
690
689
  */
691
690
  static parse(buffer2, extension) {
692
691
  const normalizedExtension = extension.startsWith(".") ? extension.slice(1) : extension;
693
- console.log(`Parsing image with extension: ${normalizedExtension}`);
694
692
  if (normalizedExtension === "png") {
695
693
  return parsePNG(buffer2);
696
694
  } else if (normalizedExtension === "jpeg" || normalizedExtension === "jpg") {
@@ -1104,6 +1102,54 @@ ${widthBits} ${height}
1104
1102
  fs.writeFileSync(filePath, bytes);
1105
1103
  });
1106
1104
  }
1105
+ /**
1106
+ * Rotate a BW bitmap by 90, 180 or 270 degrees clockwise.
1107
+ * The bitmap uses 0=black, 1=white with MSB-first packing.
1108
+ */
1109
+ static rotateBWBitmap(bitmap2, rotation) {
1110
+ const srcWidthBytes = bitmap2.width;
1111
+ const srcWidthBits = srcWidthBytes * 8;
1112
+ const srcHeight = bitmap2.height;
1113
+ const src = bitmap2.bytes;
1114
+ const getBit = (col, row) => {
1115
+ const byteIndex = row * srcWidthBytes + (col >> 3);
1116
+ const bitIndex = 7 - (col & 7);
1117
+ return src[byteIndex] >> bitIndex & 1;
1118
+ };
1119
+ let dstWidthBits;
1120
+ let dstHeight;
1121
+ let getNewPixel;
1122
+ if (rotation === 90) {
1123
+ dstWidthBits = srcHeight;
1124
+ dstHeight = srcWidthBits;
1125
+ getNewPixel = (col, row) => getBit(srcWidthBits - 1 - row, col);
1126
+ } else if (rotation === 270) {
1127
+ dstWidthBits = srcHeight;
1128
+ dstHeight = srcWidthBits;
1129
+ getNewPixel = (col, row) => getBit(row, srcHeight - 1 - col);
1130
+ } else {
1131
+ dstWidthBits = srcWidthBits;
1132
+ dstHeight = srcHeight;
1133
+ getNewPixel = (col, row) => getBit(srcWidthBits - 1 - col, srcHeight - 1 - row);
1134
+ }
1135
+ const dstPad = dstWidthBits % 8 === 0 ? 0 : 8 - dstWidthBits % 8;
1136
+ const dstWidthBitsPadded = dstWidthBits + dstPad;
1137
+ const dstWidthBytes = dstWidthBitsPadded / 8;
1138
+ const dst = new Uint8Array(dstWidthBytes * dstHeight).fill(255);
1139
+ for (let row = 0; row < dstHeight; row++) {
1140
+ for (let col = 0; col < dstWidthBits; col++) {
1141
+ const bit = getNewPixel(col, row);
1142
+ const byteIndex = row * dstWidthBytes + (col >> 3);
1143
+ const mask = 1 << 7 - (col & 7);
1144
+ if (bit === 1) {
1145
+ dst[byteIndex] |= mask;
1146
+ } else {
1147
+ dst[byteIndex] &= ~mask & 255;
1148
+ }
1149
+ }
1150
+ }
1151
+ return { width: dstWidthBytes, height: dstHeight, bytes: dst };
1152
+ }
1107
1153
  static dilateOnce(bitmap2) {
1108
1154
  const widthBytes = bitmap2.width;
1109
1155
  const widthBits = widthBytes * 8;
@@ -1509,9 +1555,9 @@ var TSPLCommandGenerator = class _TSPLCommandGenerator {
1509
1555
  print(sets, copiesPerSet) {
1510
1556
  return new TSPLPrintCommand(sets, copiesPerSet);
1511
1557
  }
1512
- text(content, x, y, font, size) {
1558
+ text(content, x, y, font, size, rotation) {
1513
1559
  const fontName = font == "default" ? "0" : font;
1514
- return new TSPLTextCommand(content, x, y, fontName, 0, size, size, "left");
1560
+ return new TSPLTextCommand(content, x, y, fontName, rotation != null ? rotation : 0, size, size, "left");
1515
1561
  }
1516
1562
  upload(name, data) {
1517
1563
  return new TSPLDownload(name, data);
@@ -1538,10 +1584,10 @@ var TSPLCommandGenerator = class _TSPLCommandGenerator {
1538
1584
  image(image2, x, y, mode) {
1539
1585
  return new TSPLBitmapCommand(image2, x, y, mode);
1540
1586
  }
1541
- qrCode(content, width, x, y) {
1587
+ qrCode(content, width, x, y, rotation) {
1542
1588
  const cellCount = this.cellCount(content);
1543
1589
  const cellWidth = Math.round(width / cellCount);
1544
- return new TSPLQRCommand(`A${content}`, x, y, cellWidth, "H", "M");
1590
+ return new TSPLQRCommand(`A${content}`, x, y, cellWidth, "H", "M", rotation != null ? rotation : 0);
1545
1591
  }
1546
1592
  barCode(content, x, y, type, height, rotation, humanReadable, alignment, barWidth = 2) {
1547
1593
  const { narrow, wide } = _TSPLCommandGenerator.narrowWideFor(type, barWidth);
@@ -2679,6 +2725,7 @@ var Text = class extends LabelField {
2679
2725
  super();
2680
2726
  this.font = { name: "default", size: 10 };
2681
2727
  this.type = "singleline";
2728
+ this.rotation = 0;
2682
2729
  this.context = void 0;
2683
2730
  this.lineSpacing = 1;
2684
2731
  this.content = content.replace("\n", "").replace('"', '\\"');
@@ -2724,13 +2771,88 @@ var Text = class extends LabelField {
2724
2771
  this.height = height;
2725
2772
  }
2726
2773
  /**
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
2774
+ * 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
2775
  * Note: The font name either has to be a built in font on your printer or a font
2729
2776
  * that is registered on the label using 'registerFont'.
2730
2777
  */
2731
2778
  setFont(font) {
2732
2779
  this.font = font;
2733
2780
  }
2781
+ /**
2782
+ * Set the rotation of the text field. All text commands in this field will be rotated.
2783
+ * For multiline text, lines advance perpendicular to the character direction.
2784
+ */
2785
+ setRotation(rotation) {
2786
+ this.rotation = rotation;
2787
+ }
2788
+ // --- Rotation-aware position helpers ---
2789
+ /** Advance cursor by `amount` along the character direction */
2790
+ advanceChar(x, y, amount) {
2791
+ switch (this.rotation) {
2792
+ case 0:
2793
+ return { x: x + amount, y };
2794
+ case 90:
2795
+ return { x, y: y + amount };
2796
+ // 90° CW: chars go downward
2797
+ case 180:
2798
+ return { x: x - amount, y };
2799
+ case 270:
2800
+ return { x, y: y - amount };
2801
+ }
2802
+ }
2803
+ /** Advance to the next line (reset char axis, advance in line direction) */
2804
+ advanceLine(x, y, lineHeight) {
2805
+ switch (this.rotation) {
2806
+ case 0:
2807
+ return { x: this.x, y: y + lineHeight };
2808
+ case 90:
2809
+ return { x: x - lineHeight, y: this.y };
2810
+ // 90° CW: lines stack leftward (-x)
2811
+ case 180:
2812
+ return { x: this.x, y: y - lineHeight };
2813
+ case 270:
2814
+ return { x: x + lineHeight, y: this.y };
2815
+ }
2816
+ }
2817
+ /** How far into the current line the cursor is (for width constraint calc) */
2818
+ charOffset(x, y) {
2819
+ switch (this.rotation) {
2820
+ case 0:
2821
+ return x - this.x;
2822
+ case 90:
2823
+ return y - this.y;
2824
+ // 90° CW: char axis is +y
2825
+ case 180:
2826
+ return this.x - x;
2827
+ case 270:
2828
+ return this.y - y;
2829
+ }
2830
+ }
2831
+ /** How many lines deep we are (for height constraint calc) */
2832
+ lineOffset(x, y) {
2833
+ switch (this.rotation) {
2834
+ case 0:
2835
+ return y - this.y;
2836
+ case 90:
2837
+ return this.x - x;
2838
+ // 90° CW: line axis is -x
2839
+ case 180:
2840
+ return this.y - y;
2841
+ case 270:
2842
+ return x - this.x;
2843
+ }
2844
+ }
2845
+ /** Whether the cursor is at the start of its line (char axis at origin) */
2846
+ atLineStart(x, y) {
2847
+ switch (this.rotation) {
2848
+ case 0:
2849
+ case 180:
2850
+ return x === this.x;
2851
+ case 90:
2852
+ case 270:
2853
+ return y === this.y;
2854
+ }
2855
+ }
2734
2856
  commandForLanguage(language, config) {
2735
2857
  return __async(this, null, function* () {
2736
2858
  this.context = {
@@ -2777,9 +2899,10 @@ var Text = class extends LabelField {
2777
2899
  const elementNode = rootNode;
2778
2900
  const tag = elementNode.rawTagName;
2779
2901
  if (tag == BREAK_TAG) {
2902
+ const linePos = this.advanceLine(initialX, initialY, font.size + this.lineSpacing);
2780
2903
  return {
2781
- x: this.x,
2782
- y: initialY + font.size + this.lineSpacing,
2904
+ x: linePos.x,
2905
+ y: linePos.y,
2783
2906
  command: this.context.generator.commandGroup([])
2784
2907
  };
2785
2908
  }
@@ -2798,9 +2921,10 @@ var Text = class extends LabelField {
2798
2921
  baseFont.style = "italic";
2799
2922
  }
2800
2923
  if (tag == PARAGRAPH_TAG) {
2801
- if (initialX != this.x) {
2802
- currentX = this.x;
2803
- currentY = initialY + baseFont.size + this.lineSpacing;
2924
+ if (!this.atLineStart(initialX, initialY)) {
2925
+ const linePos = this.advanceLine(initialX, initialY, baseFont.size + this.lineSpacing);
2926
+ currentX = linePos.x;
2927
+ currentY = linePos.y;
2804
2928
  }
2805
2929
  }
2806
2930
  elementNode.childNodes.forEach((node) => {
@@ -2811,8 +2935,9 @@ var Text = class extends LabelField {
2811
2935
  });
2812
2936
  if (tag == PARAGRAPH_TAG) {
2813
2937
  if (!this.endsWithBreak(elementNode)) {
2814
- currentX = this.x;
2815
- currentY += baseFont.size + this.lineSpacing;
2938
+ const linePos = this.advanceLine(currentX, currentY, baseFont.size + this.lineSpacing);
2939
+ currentX = linePos.x;
2940
+ currentY = linePos.y;
2816
2941
  }
2817
2942
  }
2818
2943
  return {
@@ -2832,21 +2957,23 @@ var Text = class extends LabelField {
2832
2957
  const textWidhtFunction = this.textWidthFunction;
2833
2958
  let fullWidth = textWidhtFunction(content, font);
2834
2959
  if (this.width) {
2835
- const initialPadding = initialX - this.x;
2960
+ const initialPadding = this.charOffset(initialX, initialY);
2836
2961
  let rowWidth = this.width - initialPadding;
2837
2962
  if (rowWidth <= 0) {
2838
2963
  rowWidth = this.width;
2839
- initialX = this.x;
2840
- initialY += font.size + this.lineSpacing;
2964
+ const linePos = this.advanceLine(initialX, initialY, font.size + this.lineSpacing);
2965
+ initialX = linePos.x;
2966
+ initialY = linePos.y;
2841
2967
  }
2842
- if (initialX == this.x) {
2968
+ if (this.atLineStart(initialX, initialY)) {
2843
2969
  content = content.trimStart();
2844
2970
  fullWidth = textWidhtFunction(content, font);
2845
2971
  }
2846
2972
  if (fullWidth <= rowWidth) {
2973
+ const end = this.advanceChar(initialX, initialY, fullWidth);
2847
2974
  return {
2848
- x: initialX + fullWidth,
2849
- y: initialY,
2975
+ x: end.x,
2976
+ y: end.y,
2850
2977
  command: this.textCommand(content, initialX, initialY, font, features)
2851
2978
  };
2852
2979
  } else {
@@ -2860,8 +2987,9 @@ var Text = class extends LabelField {
2860
2987
  let finalY = y;
2861
2988
  do {
2862
2989
  if (remainingWidth < rowWidth) {
2863
- finalX = x + remainingWidth;
2864
- finalY = y;
2990
+ const end = this.advanceChar(x, y, remainingWidth);
2991
+ finalX = end.x;
2992
+ finalY = end.y;
2865
2993
  commands.push(this.textCommand(remainingContent, x, y, font, features));
2866
2994
  remainingContent = "";
2867
2995
  } else {
@@ -2879,8 +3007,9 @@ var Text = class extends LabelField {
2879
3007
  let originalRowEndIndex = rowEndIndex;
2880
3008
  rowWidth = this.width;
2881
3009
  if (rowEndIndex < 0) {
2882
- x = this.x;
2883
- y += font.size + this.lineSpacing;
3010
+ const linePos2 = this.advanceLine(x, y, font.size + this.lineSpacing);
3011
+ x = linePos2.x;
3012
+ y = linePos2.y;
2884
3013
  continue;
2885
3014
  }
2886
3015
  while (!(!isWhitespace(remainingContent.charAt(rowEndIndex)) && (rowEndIndex == remainingContent.length - 1 || isWhitespace(remainingContent.charAt(rowEndIndex + 1))) || isBreakAfterChar(remainingContent.charAt(rowEndIndex))) && rowEndIndex > 0) {
@@ -2958,12 +3087,14 @@ var Text = class extends LabelField {
2958
3087
  const thisRow = remainingContent.substring(0, rowEndIndex + 1);
2959
3088
  commands.push(this.textCommand(thisRow, x, y, font, features));
2960
3089
  if (nextRowStartIndex == remainingContent.length) {
2961
- finalX = x + remainingWidth;
2962
- finalY = y;
3090
+ const end = this.advanceChar(x, y, remainingWidth);
3091
+ finalX = end.x;
3092
+ finalY = end.y;
2963
3093
  }
2964
- x = this.x;
2965
- y += font.size + this.lineSpacing;
2966
- currentHeight = y - this.y;
3094
+ const linePos = this.advanceLine(x, y, font.size + this.lineSpacing);
3095
+ x = linePos.x;
3096
+ y = linePos.y;
3097
+ currentHeight = this.lineOffset(x, y);
2967
3098
  remainingContent = remainingContent.substring(nextRowStartIndex);
2968
3099
  remainingWidth = textWidhtFunction(remainingContent, font);
2969
3100
  }
@@ -2980,9 +3111,10 @@ var Text = class extends LabelField {
2980
3111
  };
2981
3112
  }
2982
3113
  } else {
3114
+ const end = this.advanceChar(initialX, initialY, fullWidth);
2983
3115
  return {
2984
- x: initialX + fullWidth,
2985
- y: initialY,
3116
+ x: end.x,
3117
+ y: end.y,
2986
3118
  command: this.textCommand(content, initialX, initialY, font, features)
2987
3119
  };
2988
3120
  }
@@ -2995,7 +3127,7 @@ var Text = class extends LabelField {
2995
3127
  const finalX = Math.round(x);
2996
3128
  const finalY = Math.round(y);
2997
3129
  let commands = [];
2998
- const textCommand = this.context.generator.text(text, finalX, finalY, finalFont, finalFontSize);
3130
+ const textCommand = this.context.generator.text(text, finalX, finalY, finalFont, finalFontSize, this.rotation);
2999
3131
  if (features.length == 0) {
3000
3132
  return textCommand;
3001
3133
  } else {
@@ -3012,13 +3144,28 @@ var Text = class extends LabelField {
3012
3144
  return this.context.generator.commandGroup(commands);
3013
3145
  }
3014
3146
  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
- );
3147
+ const offset = Math.round(fontSize * linePercentage - lineHeight / 2);
3148
+ let start;
3149
+ let end;
3150
+ switch (this.rotation) {
3151
+ case 0:
3152
+ start = { x: Math.round(x), y: Math.round(y) + offset };
3153
+ end = { x: Math.round(x) + Math.round(width), y: start.y };
3154
+ break;
3155
+ case 90:
3156
+ start = { x: Math.round(x) - offset, y: Math.round(y) };
3157
+ end = { x: start.x, y: Math.round(y) + Math.round(width) };
3158
+ break;
3159
+ case 180:
3160
+ start = { x: Math.round(x), y: Math.round(y) - offset };
3161
+ end = { x: Math.round(x) - Math.round(width), y: start.y };
3162
+ break;
3163
+ case 270:
3164
+ start = { x: Math.round(x) + offset, y: Math.round(y) };
3165
+ end = { x: start.x, y: Math.round(y) - Math.round(width) };
3166
+ break;
3167
+ }
3168
+ return this.context.generator.line(start, end, lineHeight);
3022
3169
  }
3023
3170
  getFontName(font) {
3024
3171
  var _a;
@@ -3032,17 +3179,20 @@ var Text = class extends LabelField {
3032
3179
  get textWidthFunction() {
3033
3180
  var _a, _b, _c;
3034
3181
  if (this.font.name == "default") {
3035
- return this.defaultTextWidth;
3182
+ return (text, font) => this.defaultTextWidth(text, font);
3036
3183
  } else {
3037
- return (_c = (_b = (_a = this.context) == null ? void 0 : _a.config) == null ? void 0 : _b.textWidth) != null ? _c : this.defaultTextWidth;
3184
+ 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
3185
  }
3039
3186
  }
3040
3187
  /**
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
3188
+ * Fallback width estimate when no font metrics are available.
3189
+ * Uses the TSPL x-multiplication value (dotToPoint of the dot size) as
3190
+ * the per-character width, which matches the rendered width of font "0".
3043
3191
  */
3044
3192
  defaultTextWidth(text, font) {
3045
- return text.length * font.size;
3193
+ var _a, _b, _c;
3194
+ const dpi = (_c = (_b = (_a = this.context) == null ? void 0 : _a.config) == null ? void 0 : _b.dpi) != null ? _c : 203;
3195
+ return text.length * dotToPoint(font.size, dpi);
3046
3196
  }
3047
3197
  };
3048
3198
 
@@ -3086,13 +3236,37 @@ var BarCode = class extends LabelField {
3086
3236
  var Image2 = class _Image extends LabelField {
3087
3237
  constructor(x, y, image2) {
3088
3238
  super();
3239
+ this.rotation = 0;
3089
3240
  this.x = x;
3090
3241
  this.y = y;
3091
3242
  this.image = image2;
3092
3243
  }
3244
+ setRotation(rotation) {
3245
+ this.rotation = rotation;
3246
+ }
3093
3247
  commandForLanguage(language, _config) {
3094
3248
  return __async(this, null, function* () {
3095
- return yield this.commandGeneratorFor(language).image(this.image, this.x, this.y);
3249
+ if (this.rotation === 0) {
3250
+ return yield this.commandGeneratorFor(language).image(this.image, this.x, this.y);
3251
+ }
3252
+ const srcW = this.image.width * 8;
3253
+ const srcH = this.image.height;
3254
+ const bitmap2 = ImageUtils.rotateBWBitmap(this.image, 360 - this.rotation);
3255
+ let dx = this.x;
3256
+ let dy = this.y;
3257
+ switch (this.rotation) {
3258
+ case 90:
3259
+ dx = this.x - srcH;
3260
+ break;
3261
+ case 180:
3262
+ dx = this.x - srcW;
3263
+ dy = this.y - srcH;
3264
+ break;
3265
+ case 270:
3266
+ dy = this.y - srcW;
3267
+ break;
3268
+ }
3269
+ return yield this.commandGeneratorFor(language).image(bitmap2, dx, dy);
3096
3270
  });
3097
3271
  }
3098
3272
  /**
@@ -3116,14 +3290,18 @@ var Image2 = class _Image extends LabelField {
3116
3290
  var QRCode = class extends LabelField {
3117
3291
  constructor(content, x, y, width) {
3118
3292
  super();
3293
+ this.rotation = 0;
3119
3294
  this.content = content;
3120
3295
  this.x = x;
3121
3296
  this.y = y;
3122
3297
  this.width = width;
3123
3298
  }
3299
+ setRotation(rotation) {
3300
+ this.rotation = rotation;
3301
+ }
3124
3302
  commandForLanguage(language, config) {
3125
3303
  return __async(this, null, function* () {
3126
- return yield this.commandGeneratorFor(language).qrCode(this.content, this.width, this.x, this.y);
3304
+ return yield this.commandGeneratorFor(language).qrCode(this.content, this.width, this.x, this.y, this.rotation);
3127
3305
  });
3128
3306
  }
3129
3307
  };