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.mjs CHANGED
@@ -496,7 +496,6 @@ var ImageProcessor = class {
496
496
  */
497
497
  static getImageDataNode(image2, _target) {
498
498
  return __async(this, null, function* () {
499
- console.log("Processing image in Node.js environment");
500
499
  if (image2 instanceof Blob) {
501
500
  throw new Error("Blob input not supported in Node.js environment. Use file path or data URL instead.");
502
501
  }
@@ -665,7 +664,6 @@ var ImageProcessor = class {
665
664
  */
666
665
  static parse(buffer2, extension) {
667
666
  const normalizedExtension = extension.startsWith(".") ? extension.slice(1) : extension;
668
- console.log(`Parsing image with extension: ${normalizedExtension}`);
669
667
  if (normalizedExtension === "png") {
670
668
  return parsePNG(buffer2);
671
669
  } else if (normalizedExtension === "jpeg" || normalizedExtension === "jpg") {
@@ -1079,6 +1077,54 @@ ${widthBits} ${height}
1079
1077
  fs.writeFileSync(filePath, bytes);
1080
1078
  });
1081
1079
  }
1080
+ /**
1081
+ * Rotate a BW bitmap by 90, 180 or 270 degrees clockwise.
1082
+ * The bitmap uses 0=black, 1=white with MSB-first packing.
1083
+ */
1084
+ static rotateBWBitmap(bitmap2, rotation) {
1085
+ const srcWidthBytes = bitmap2.width;
1086
+ const srcWidthBits = srcWidthBytes * 8;
1087
+ const srcHeight = bitmap2.height;
1088
+ const src = bitmap2.bytes;
1089
+ const getBit = (col, row) => {
1090
+ const byteIndex = row * srcWidthBytes + (col >> 3);
1091
+ const bitIndex = 7 - (col & 7);
1092
+ return src[byteIndex] >> bitIndex & 1;
1093
+ };
1094
+ let dstWidthBits;
1095
+ let dstHeight;
1096
+ let getNewPixel;
1097
+ if (rotation === 90) {
1098
+ dstWidthBits = srcHeight;
1099
+ dstHeight = srcWidthBits;
1100
+ getNewPixel = (col, row) => getBit(srcWidthBits - 1 - row, col);
1101
+ } else if (rotation === 270) {
1102
+ dstWidthBits = srcHeight;
1103
+ dstHeight = srcWidthBits;
1104
+ getNewPixel = (col, row) => getBit(row, srcHeight - 1 - col);
1105
+ } else {
1106
+ dstWidthBits = srcWidthBits;
1107
+ dstHeight = srcHeight;
1108
+ getNewPixel = (col, row) => getBit(srcWidthBits - 1 - col, srcHeight - 1 - row);
1109
+ }
1110
+ const dstPad = dstWidthBits % 8 === 0 ? 0 : 8 - dstWidthBits % 8;
1111
+ const dstWidthBitsPadded = dstWidthBits + dstPad;
1112
+ const dstWidthBytes = dstWidthBitsPadded / 8;
1113
+ const dst = new Uint8Array(dstWidthBytes * dstHeight).fill(255);
1114
+ for (let row = 0; row < dstHeight; row++) {
1115
+ for (let col = 0; col < dstWidthBits; col++) {
1116
+ const bit = getNewPixel(col, row);
1117
+ const byteIndex = row * dstWidthBytes + (col >> 3);
1118
+ const mask = 1 << 7 - (col & 7);
1119
+ if (bit === 1) {
1120
+ dst[byteIndex] |= mask;
1121
+ } else {
1122
+ dst[byteIndex] &= ~mask & 255;
1123
+ }
1124
+ }
1125
+ }
1126
+ return { width: dstWidthBytes, height: dstHeight, bytes: dst };
1127
+ }
1082
1128
  static dilateOnce(bitmap2) {
1083
1129
  const widthBytes = bitmap2.width;
1084
1130
  const widthBits = widthBytes * 8;
@@ -1484,9 +1530,9 @@ var TSPLCommandGenerator = class _TSPLCommandGenerator {
1484
1530
  print(sets, copiesPerSet) {
1485
1531
  return new TSPLPrintCommand(sets, copiesPerSet);
1486
1532
  }
1487
- text(content, x, y, font, size) {
1533
+ text(content, x, y, font, size, rotation) {
1488
1534
  const fontName = font == "default" ? "0" : font;
1489
- return new TSPLTextCommand(content, x, y, fontName, 0, size, size, "left");
1535
+ return new TSPLTextCommand(content, x, y, fontName, rotation != null ? rotation : 0, size, size, "left");
1490
1536
  }
1491
1537
  upload(name, data) {
1492
1538
  return new TSPLDownload(name, data);
@@ -1513,10 +1559,10 @@ var TSPLCommandGenerator = class _TSPLCommandGenerator {
1513
1559
  image(image2, x, y, mode) {
1514
1560
  return new TSPLBitmapCommand(image2, x, y, mode);
1515
1561
  }
1516
- qrCode(content, width, x, y) {
1562
+ qrCode(content, width, x, y, rotation) {
1517
1563
  const cellCount = this.cellCount(content);
1518
1564
  const cellWidth = Math.round(width / cellCount);
1519
- return new TSPLQRCommand(`A${content}`, x, y, cellWidth, "H", "M");
1565
+ return new TSPLQRCommand(`A${content}`, x, y, cellWidth, "H", "M", rotation != null ? rotation : 0);
1520
1566
  }
1521
1567
  barCode(content, x, y, type, height, rotation, humanReadable, alignment, barWidth = 2) {
1522
1568
  const { narrow, wide } = _TSPLCommandGenerator.narrowWideFor(type, barWidth);
@@ -2654,6 +2700,7 @@ var Text = class extends LabelField {
2654
2700
  super();
2655
2701
  this.font = { name: "default", size: 10 };
2656
2702
  this.type = "singleline";
2703
+ this.rotation = 0;
2657
2704
  this.context = void 0;
2658
2705
  this.lineSpacing = 1;
2659
2706
  this.content = content.replace("\n", "").replace('"', '\\"');
@@ -2699,13 +2746,88 @@ var Text = class extends LabelField {
2699
2746
  this.height = height;
2700
2747
  }
2701
2748
  /**
2702
- * Set a font to use as a base. If no formatting is set on the text with a html tag, this will be used
2749
+ * Set a font to use as a base. If no formatting is set on the text with a html tag, this will be used
2703
2750
  * Note: The font name either has to be a built in font on your printer or a font
2704
2751
  * that is registered on the label using 'registerFont'.
2705
2752
  */
2706
2753
  setFont(font) {
2707
2754
  this.font = font;
2708
2755
  }
2756
+ /**
2757
+ * Set the rotation of the text field. All text commands in this field will be rotated.
2758
+ * For multiline text, lines advance perpendicular to the character direction.
2759
+ */
2760
+ setRotation(rotation) {
2761
+ this.rotation = rotation;
2762
+ }
2763
+ // --- Rotation-aware position helpers ---
2764
+ /** Advance cursor by `amount` along the character direction */
2765
+ advanceChar(x, y, amount) {
2766
+ switch (this.rotation) {
2767
+ case 0:
2768
+ return { x: x + amount, y };
2769
+ case 90:
2770
+ return { x, y: y + amount };
2771
+ // 90° CW: chars go downward
2772
+ case 180:
2773
+ return { x: x - amount, y };
2774
+ case 270:
2775
+ return { x, y: y - amount };
2776
+ }
2777
+ }
2778
+ /** Advance to the next line (reset char axis, advance in line direction) */
2779
+ advanceLine(x, y, lineHeight) {
2780
+ switch (this.rotation) {
2781
+ case 0:
2782
+ return { x: this.x, y: y + lineHeight };
2783
+ case 90:
2784
+ return { x: x - lineHeight, y: this.y };
2785
+ // 90° CW: lines stack leftward (-x)
2786
+ case 180:
2787
+ return { x: this.x, y: y - lineHeight };
2788
+ case 270:
2789
+ return { x: x + lineHeight, y: this.y };
2790
+ }
2791
+ }
2792
+ /** How far into the current line the cursor is (for width constraint calc) */
2793
+ charOffset(x, y) {
2794
+ switch (this.rotation) {
2795
+ case 0:
2796
+ return x - this.x;
2797
+ case 90:
2798
+ return y - this.y;
2799
+ // 90° CW: char axis is +y
2800
+ case 180:
2801
+ return this.x - x;
2802
+ case 270:
2803
+ return this.y - y;
2804
+ }
2805
+ }
2806
+ /** How many lines deep we are (for height constraint calc) */
2807
+ lineOffset(x, y) {
2808
+ switch (this.rotation) {
2809
+ case 0:
2810
+ return y - this.y;
2811
+ case 90:
2812
+ return this.x - x;
2813
+ // 90° CW: line axis is -x
2814
+ case 180:
2815
+ return this.y - y;
2816
+ case 270:
2817
+ return x - this.x;
2818
+ }
2819
+ }
2820
+ /** Whether the cursor is at the start of its line (char axis at origin) */
2821
+ atLineStart(x, y) {
2822
+ switch (this.rotation) {
2823
+ case 0:
2824
+ case 180:
2825
+ return x === this.x;
2826
+ case 90:
2827
+ case 270:
2828
+ return y === this.y;
2829
+ }
2830
+ }
2709
2831
  commandForLanguage(language, config) {
2710
2832
  return __async(this, null, function* () {
2711
2833
  this.context = {
@@ -2752,9 +2874,10 @@ var Text = class extends LabelField {
2752
2874
  const elementNode = rootNode;
2753
2875
  const tag = elementNode.rawTagName;
2754
2876
  if (tag == BREAK_TAG) {
2877
+ const linePos = this.advanceLine(initialX, initialY, font.size + this.lineSpacing);
2755
2878
  return {
2756
- x: this.x,
2757
- y: initialY + font.size + this.lineSpacing,
2879
+ x: linePos.x,
2880
+ y: linePos.y,
2758
2881
  command: this.context.generator.commandGroup([])
2759
2882
  };
2760
2883
  }
@@ -2773,9 +2896,10 @@ var Text = class extends LabelField {
2773
2896
  baseFont.style = "italic";
2774
2897
  }
2775
2898
  if (tag == PARAGRAPH_TAG) {
2776
- if (initialX != this.x) {
2777
- currentX = this.x;
2778
- currentY = initialY + baseFont.size + this.lineSpacing;
2899
+ if (!this.atLineStart(initialX, initialY)) {
2900
+ const linePos = this.advanceLine(initialX, initialY, baseFont.size + this.lineSpacing);
2901
+ currentX = linePos.x;
2902
+ currentY = linePos.y;
2779
2903
  }
2780
2904
  }
2781
2905
  elementNode.childNodes.forEach((node) => {
@@ -2786,8 +2910,9 @@ var Text = class extends LabelField {
2786
2910
  });
2787
2911
  if (tag == PARAGRAPH_TAG) {
2788
2912
  if (!this.endsWithBreak(elementNode)) {
2789
- currentX = this.x;
2790
- currentY += baseFont.size + this.lineSpacing;
2913
+ const linePos = this.advanceLine(currentX, currentY, baseFont.size + this.lineSpacing);
2914
+ currentX = linePos.x;
2915
+ currentY = linePos.y;
2791
2916
  }
2792
2917
  }
2793
2918
  return {
@@ -2807,21 +2932,23 @@ var Text = class extends LabelField {
2807
2932
  const textWidhtFunction = this.textWidthFunction;
2808
2933
  let fullWidth = textWidhtFunction(content, font);
2809
2934
  if (this.width) {
2810
- const initialPadding = initialX - this.x;
2935
+ const initialPadding = this.charOffset(initialX, initialY);
2811
2936
  let rowWidth = this.width - initialPadding;
2812
2937
  if (rowWidth <= 0) {
2813
2938
  rowWidth = this.width;
2814
- initialX = this.x;
2815
- initialY += font.size + this.lineSpacing;
2939
+ const linePos = this.advanceLine(initialX, initialY, font.size + this.lineSpacing);
2940
+ initialX = linePos.x;
2941
+ initialY = linePos.y;
2816
2942
  }
2817
- if (initialX == this.x) {
2943
+ if (this.atLineStart(initialX, initialY)) {
2818
2944
  content = content.trimStart();
2819
2945
  fullWidth = textWidhtFunction(content, font);
2820
2946
  }
2821
2947
  if (fullWidth <= rowWidth) {
2948
+ const end = this.advanceChar(initialX, initialY, fullWidth);
2822
2949
  return {
2823
- x: initialX + fullWidth,
2824
- y: initialY,
2950
+ x: end.x,
2951
+ y: end.y,
2825
2952
  command: this.textCommand(content, initialX, initialY, font, features)
2826
2953
  };
2827
2954
  } else {
@@ -2835,8 +2962,9 @@ var Text = class extends LabelField {
2835
2962
  let finalY = y;
2836
2963
  do {
2837
2964
  if (remainingWidth < rowWidth) {
2838
- finalX = x + remainingWidth;
2839
- finalY = y;
2965
+ const end = this.advanceChar(x, y, remainingWidth);
2966
+ finalX = end.x;
2967
+ finalY = end.y;
2840
2968
  commands.push(this.textCommand(remainingContent, x, y, font, features));
2841
2969
  remainingContent = "";
2842
2970
  } else {
@@ -2854,8 +2982,9 @@ var Text = class extends LabelField {
2854
2982
  let originalRowEndIndex = rowEndIndex;
2855
2983
  rowWidth = this.width;
2856
2984
  if (rowEndIndex < 0) {
2857
- x = this.x;
2858
- y += font.size + this.lineSpacing;
2985
+ const linePos2 = this.advanceLine(x, y, font.size + this.lineSpacing);
2986
+ x = linePos2.x;
2987
+ y = linePos2.y;
2859
2988
  continue;
2860
2989
  }
2861
2990
  while (!(!isWhitespace(remainingContent.charAt(rowEndIndex)) && (rowEndIndex == remainingContent.length - 1 || isWhitespace(remainingContent.charAt(rowEndIndex + 1))) || isBreakAfterChar(remainingContent.charAt(rowEndIndex))) && rowEndIndex > 0) {
@@ -2933,12 +3062,14 @@ var Text = class extends LabelField {
2933
3062
  const thisRow = remainingContent.substring(0, rowEndIndex + 1);
2934
3063
  commands.push(this.textCommand(thisRow, x, y, font, features));
2935
3064
  if (nextRowStartIndex == remainingContent.length) {
2936
- finalX = x + remainingWidth;
2937
- finalY = y;
3065
+ const end = this.advanceChar(x, y, remainingWidth);
3066
+ finalX = end.x;
3067
+ finalY = end.y;
2938
3068
  }
2939
- x = this.x;
2940
- y += font.size + this.lineSpacing;
2941
- currentHeight = y - this.y;
3069
+ const linePos = this.advanceLine(x, y, font.size + this.lineSpacing);
3070
+ x = linePos.x;
3071
+ y = linePos.y;
3072
+ currentHeight = this.lineOffset(x, y);
2942
3073
  remainingContent = remainingContent.substring(nextRowStartIndex);
2943
3074
  remainingWidth = textWidhtFunction(remainingContent, font);
2944
3075
  }
@@ -2955,9 +3086,10 @@ var Text = class extends LabelField {
2955
3086
  };
2956
3087
  }
2957
3088
  } else {
3089
+ const end = this.advanceChar(initialX, initialY, fullWidth);
2958
3090
  return {
2959
- x: initialX + fullWidth,
2960
- y: initialY,
3091
+ x: end.x,
3092
+ y: end.y,
2961
3093
  command: this.textCommand(content, initialX, initialY, font, features)
2962
3094
  };
2963
3095
  }
@@ -2970,7 +3102,7 @@ var Text = class extends LabelField {
2970
3102
  const finalX = Math.round(x);
2971
3103
  const finalY = Math.round(y);
2972
3104
  let commands = [];
2973
- const textCommand = this.context.generator.text(text, finalX, finalY, finalFont, finalFontSize);
3105
+ const textCommand = this.context.generator.text(text, finalX, finalY, finalFont, finalFontSize, this.rotation);
2974
3106
  if (features.length == 0) {
2975
3107
  return textCommand;
2976
3108
  } else {
@@ -2987,13 +3119,28 @@ var Text = class extends LabelField {
2987
3119
  return this.context.generator.commandGroup(commands);
2988
3120
  }
2989
3121
  textLineCommand(width, x, y, lineHeight, linePercentage, fontSize) {
2990
- const sy = Math.round(y + fontSize * linePercentage - lineHeight / 2);
2991
- const sx = Math.round(x);
2992
- return this.context.generator.line(
2993
- { x: sx, y: sy },
2994
- { x: sx + width, y: sy },
2995
- lineHeight
2996
- );
3122
+ const offset = Math.round(fontSize * linePercentage - lineHeight / 2);
3123
+ let start;
3124
+ let end;
3125
+ switch (this.rotation) {
3126
+ case 0:
3127
+ start = { x: Math.round(x), y: Math.round(y) + offset };
3128
+ end = { x: Math.round(x) + Math.round(width), y: start.y };
3129
+ break;
3130
+ case 90:
3131
+ start = { x: Math.round(x) - offset, y: Math.round(y) };
3132
+ end = { x: start.x, y: Math.round(y) + Math.round(width) };
3133
+ break;
3134
+ case 180:
3135
+ start = { x: Math.round(x), y: Math.round(y) - offset };
3136
+ end = { x: Math.round(x) - Math.round(width), y: start.y };
3137
+ break;
3138
+ case 270:
3139
+ start = { x: Math.round(x) + offset, y: Math.round(y) };
3140
+ end = { x: start.x, y: Math.round(y) - Math.round(width) };
3141
+ break;
3142
+ }
3143
+ return this.context.generator.line(start, end, lineHeight);
2997
3144
  }
2998
3145
  getFontName(font) {
2999
3146
  var _a;
@@ -3007,17 +3154,20 @@ var Text = class extends LabelField {
3007
3154
  get textWidthFunction() {
3008
3155
  var _a, _b, _c;
3009
3156
  if (this.font.name == "default") {
3010
- return this.defaultTextWidth;
3157
+ return (text, font) => this.defaultTextWidth(text, font);
3011
3158
  } else {
3012
- return (_c = (_b = (_a = this.context) == null ? void 0 : _a.config) == null ? void 0 : _b.textWidth) != null ? _c : this.defaultTextWidth;
3159
+ return (_c = (_b = (_a = this.context) == null ? void 0 : _a.config) == null ? void 0 : _b.textWidth) != null ? _c : (text, font) => this.defaultTextWidth(text, font);
3013
3160
  }
3014
3161
  }
3015
3162
  /**
3016
- * This function is used to calculate the font size if no
3017
- * print config is provided. This will asume that the font has square characters
3163
+ * Fallback width estimate when no font metrics are available.
3164
+ * Uses the TSPL x-multiplication value (dotToPoint of the dot size) as
3165
+ * the per-character width, which matches the rendered width of font "0".
3018
3166
  */
3019
3167
  defaultTextWidth(text, font) {
3020
- return text.length * font.size;
3168
+ var _a, _b, _c;
3169
+ const dpi = (_c = (_b = (_a = this.context) == null ? void 0 : _a.config) == null ? void 0 : _b.dpi) != null ? _c : 203;
3170
+ return text.length * dotToPoint(font.size, dpi);
3021
3171
  }
3022
3172
  };
3023
3173
 
@@ -3061,13 +3211,37 @@ var BarCode = class extends LabelField {
3061
3211
  var Image2 = class _Image extends LabelField {
3062
3212
  constructor(x, y, image2) {
3063
3213
  super();
3214
+ this.rotation = 0;
3064
3215
  this.x = x;
3065
3216
  this.y = y;
3066
3217
  this.image = image2;
3067
3218
  }
3219
+ setRotation(rotation) {
3220
+ this.rotation = rotation;
3221
+ }
3068
3222
  commandForLanguage(language, _config) {
3069
3223
  return __async(this, null, function* () {
3070
- return yield this.commandGeneratorFor(language).image(this.image, this.x, this.y);
3224
+ if (this.rotation === 0) {
3225
+ return yield this.commandGeneratorFor(language).image(this.image, this.x, this.y);
3226
+ }
3227
+ const srcW = this.image.width * 8;
3228
+ const srcH = this.image.height;
3229
+ const bitmap2 = ImageUtils.rotateBWBitmap(this.image, 360 - this.rotation);
3230
+ let dx = this.x;
3231
+ let dy = this.y;
3232
+ switch (this.rotation) {
3233
+ case 90:
3234
+ dx = this.x - srcH;
3235
+ break;
3236
+ case 180:
3237
+ dx = this.x - srcW;
3238
+ dy = this.y - srcH;
3239
+ break;
3240
+ case 270:
3241
+ dy = this.y - srcW;
3242
+ break;
3243
+ }
3244
+ return yield this.commandGeneratorFor(language).image(bitmap2, dx, dy);
3071
3245
  });
3072
3246
  }
3073
3247
  /**
@@ -3091,14 +3265,18 @@ var Image2 = class _Image extends LabelField {
3091
3265
  var QRCode = class extends LabelField {
3092
3266
  constructor(content, x, y, width) {
3093
3267
  super();
3268
+ this.rotation = 0;
3094
3269
  this.content = content;
3095
3270
  this.x = x;
3096
3271
  this.y = y;
3097
3272
  this.width = width;
3098
3273
  }
3274
+ setRotation(rotation) {
3275
+ this.rotation = rotation;
3276
+ }
3099
3277
  commandForLanguage(language, config) {
3100
3278
  return __async(this, null, function* () {
3101
- return yield this.commandGeneratorFor(language).qrCode(this.content, this.width, this.x, this.y);
3279
+ return yield this.commandGeneratorFor(language).qrCode(this.content, this.width, this.x, this.y, this.rotation);
3102
3280
  });
3103
3281
  }
3104
3282
  };