@bobfrankston/brother-label 1.0.1
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 +193 -0
- package/api.d.ts +39 -0
- package/api.d.ts.map +1 -0
- package/api.js +398 -0
- package/api.js.map +1 -0
- package/api.ts +488 -0
- package/brother-print.d.ts +3 -0
- package/brother-print.d.ts.map +1 -0
- package/brother-print.js +629 -0
- package/brother-print.js.map +1 -0
- package/brother-print.ts +697 -0
- package/cli.d.ts +7 -0
- package/cli.d.ts.map +1 -0
- package/cli.js +165 -0
- package/cli.js.map +1 -0
- package/cli.ts +180 -0
- package/index.d.ts +6 -0
- package/index.d.ts.map +1 -0
- package/index.js +8 -0
- package/index.js.map +1 -0
- package/index.ts +21 -0
- package/package.json +56 -0
- package/render.d.ts +38 -0
- package/render.d.ts.map +1 -0
- package/render.js +170 -0
- package/render.js.map +1 -0
- package/render.ts +201 -0
- package/tsconfig.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# @bobfrankston/brother-label
|
|
2
|
+
|
|
3
|
+
Print labels to Brother P-touch label printers from Node.js. Supports text, HTML, images, and QR codes.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @bobfrankston/brother-label
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Command Line Usage
|
|
12
|
+
|
|
13
|
+
### Print text
|
|
14
|
+
```bash
|
|
15
|
+
brother-print print "Hello World"
|
|
16
|
+
brother-print print "Line 1\\nLine 2" # Multi-line
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Print QR code
|
|
20
|
+
```bash
|
|
21
|
+
brother-print qr "https://example.com"
|
|
22
|
+
brother-print qr "https://example.com" -l "My Site" # With text label
|
|
23
|
+
brother-print qr "any data" -o qr.png # Save to file
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Print HTML
|
|
27
|
+
```bash
|
|
28
|
+
brother-print html label.html
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
#### Inline QR codes in HTML
|
|
32
|
+
Use `<img qr="data">` to embed QR codes directly in HTML - they're converted to base64 at render time:
|
|
33
|
+
```html
|
|
34
|
+
<img qr="https://example.com" style="width:10mm; height:10mm">
|
|
35
|
+
<img qr="any data here" class="my-qr">
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Print image
|
|
39
|
+
```bash
|
|
40
|
+
brother-print image photo.png
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Preview (save without printing)
|
|
44
|
+
```bash
|
|
45
|
+
brother-print preview "Test Label" -o preview.png
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Configuration
|
|
49
|
+
```bash
|
|
50
|
+
brother-print config --show # Show current config
|
|
51
|
+
brother-print config -t 12mm # Set default tape size
|
|
52
|
+
brother-print config -p "Brother PT-P710BT" # Set default printer
|
|
53
|
+
brother-print list # List Brother printers
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Options
|
|
57
|
+
| Option | Description |
|
|
58
|
+
|--------|-------------|
|
|
59
|
+
| `-p, --printer <name>` | Printer name |
|
|
60
|
+
| `-t, --tape <size>` | Tape size: 6mm, 9mm, 12mm, 18mm, 24mm, or 'auto' |
|
|
61
|
+
| `-l, --label <text>` | Text label beside QR code (qr command only) |
|
|
62
|
+
| `-o, --output <file>` | Save to file instead of printing |
|
|
63
|
+
|
|
64
|
+
## API Usage
|
|
65
|
+
|
|
66
|
+
### Print text
|
|
67
|
+
```typescript
|
|
68
|
+
import { print } from "@bobfrankston/brother-label";
|
|
69
|
+
|
|
70
|
+
await print({ text: "Hello World" });
|
|
71
|
+
await print({ text: "Hello", tape: 12 }); // 12mm tape
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Print QR code
|
|
75
|
+
```typescript
|
|
76
|
+
import { print, render } from "@bobfrankston/brother-label";
|
|
77
|
+
|
|
78
|
+
// Print QR code
|
|
79
|
+
await print({ qr: "https://example.com" });
|
|
80
|
+
|
|
81
|
+
// QR code with text label
|
|
82
|
+
await print({ qr: "https://example.com", qrLabel: "My Site" });
|
|
83
|
+
|
|
84
|
+
// Render to buffer without printing
|
|
85
|
+
const buffer = await render({ qr: "https://example.com" });
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Print HTML
|
|
89
|
+
```typescript
|
|
90
|
+
// From file
|
|
91
|
+
await print({ htmlPath: "label.html" });
|
|
92
|
+
|
|
93
|
+
// Inline HTML
|
|
94
|
+
await print({
|
|
95
|
+
html: "<div style='font-size:24px'>Hello</div>",
|
|
96
|
+
basePath: __dirname // For resolving relative resources
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// With aspect ratio
|
|
100
|
+
await print({ htmlPath: "label.html", aspect: "4:1" });
|
|
101
|
+
|
|
102
|
+
// HTML with inline QR codes - <img qr="..."> auto-converted to base64
|
|
103
|
+
await print({
|
|
104
|
+
html: `<img qr="https://example.com" style="width:10mm">`,
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Print image
|
|
109
|
+
```typescript
|
|
110
|
+
// From file
|
|
111
|
+
await print({ imagePath: "photo.png" });
|
|
112
|
+
|
|
113
|
+
// From buffer
|
|
114
|
+
await print({ imageBuffer: fs.readFileSync("photo.png") });
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Configuration
|
|
118
|
+
```typescript
|
|
119
|
+
import { getConfig, setConfig, listPrinters } from "@bobfrankston/brother-label";
|
|
120
|
+
|
|
121
|
+
// Get current config
|
|
122
|
+
const config = getConfig();
|
|
123
|
+
console.log(config.defaultTape); // 12
|
|
124
|
+
console.log(config.defaultPrinter); // "Brother PT-P710BT"
|
|
125
|
+
|
|
126
|
+
// Set defaults
|
|
127
|
+
setConfig({ defaultTape: 24, defaultPrinter: "Brother PT-P710BT" });
|
|
128
|
+
|
|
129
|
+
// List printers
|
|
130
|
+
const printers = await listPrinters();
|
|
131
|
+
printers.forEach(p => console.log(p.name));
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Render without printing
|
|
135
|
+
```typescript
|
|
136
|
+
import { render } from "@bobfrankston/brother-label";
|
|
137
|
+
|
|
138
|
+
const buffer = await render({ text: "Hello" });
|
|
139
|
+
fs.writeFileSync("label.png", buffer);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## API Reference
|
|
143
|
+
|
|
144
|
+
### Types
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
type TapeSize = 6 | 9 | 12 | 18 | 24;
|
|
148
|
+
|
|
149
|
+
interface PrintOptions {
|
|
150
|
+
// Content (exactly one required)
|
|
151
|
+
text?: string; // Plain text
|
|
152
|
+
html?: string; // Inline HTML
|
|
153
|
+
htmlPath?: string; // Path to HTML file
|
|
154
|
+
textFile?: string; // Path to text file
|
|
155
|
+
imagePath?: string; // Path to image file
|
|
156
|
+
imageBuffer?: Buffer; // Image buffer
|
|
157
|
+
qr?: string; // QR code data
|
|
158
|
+
|
|
159
|
+
// Settings
|
|
160
|
+
tape?: TapeSize; // Tape size in mm
|
|
161
|
+
printer?: string; // Printer name
|
|
162
|
+
basePath?: string; // Base path for HTML resources
|
|
163
|
+
aspect?: string; // Aspect ratio for HTML (e.g., "4:1")
|
|
164
|
+
qrLabel?: string; // Text label beside QR code
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
interface PrintResult {
|
|
168
|
+
image: Buffer; // The rendered image
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Functions
|
|
173
|
+
|
|
174
|
+
| Function | Description |
|
|
175
|
+
|----------|-------------|
|
|
176
|
+
| `print(options)` | Render and print a label |
|
|
177
|
+
| `render(options)` | Render label to PNG buffer |
|
|
178
|
+
| `getConfig()` | Get current configuration |
|
|
179
|
+
| `setConfig(config)` | Set default tape/printer |
|
|
180
|
+
| `getConfigPath()` | Get config file path |
|
|
181
|
+
| `listPrinters()` | List Brother printers |
|
|
182
|
+
|
|
183
|
+
## Supported Printers
|
|
184
|
+
|
|
185
|
+
- Brother PT-P710BT (tested)
|
|
186
|
+
- Other Brother P-touch printers (should work)
|
|
187
|
+
- Brother QL series (untested)
|
|
188
|
+
|
|
189
|
+
## Requirements
|
|
190
|
+
|
|
191
|
+
- Windows (uses Windows printing APIs)
|
|
192
|
+
- Node.js 20+
|
|
193
|
+
- Puppeteer (for HTML rendering)
|
package/api.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brother Label Printer API
|
|
3
|
+
* Programmatic interface for printing labels on Brother P-touch printers
|
|
4
|
+
*/
|
|
5
|
+
export type TapeSize = 6 | 9 | 12 | 18 | 24;
|
|
6
|
+
export type Orientation = "landscape" | "portrait";
|
|
7
|
+
export interface PrinterConfig {
|
|
8
|
+
defaultTape?: TapeSize;
|
|
9
|
+
defaultPrinter?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PrinterInfo {
|
|
12
|
+
name: string;
|
|
13
|
+
}
|
|
14
|
+
export interface PrintOptions {
|
|
15
|
+
text?: string;
|
|
16
|
+
html?: string;
|
|
17
|
+
htmlPath?: string;
|
|
18
|
+
textFile?: string;
|
|
19
|
+
imagePath?: string;
|
|
20
|
+
imageBuffer?: Buffer;
|
|
21
|
+
qr?: string;
|
|
22
|
+
tape?: TapeSize;
|
|
23
|
+
printer?: string;
|
|
24
|
+
orientation?: Orientation;
|
|
25
|
+
length?: number;
|
|
26
|
+
basePath?: string;
|
|
27
|
+
aspect?: string;
|
|
28
|
+
qrLabel?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface PrintResult {
|
|
31
|
+
image: Buffer;
|
|
32
|
+
}
|
|
33
|
+
export declare function getConfig(): PrinterConfig;
|
|
34
|
+
export declare function setConfig(config: Partial<PrinterConfig>): void;
|
|
35
|
+
export declare function getConfigPath(): string;
|
|
36
|
+
export declare function listPrinters(): Promise<PrinterInfo[]>;
|
|
37
|
+
export declare function render(options: PrintOptions): Promise<Buffer>;
|
|
38
|
+
export declare function print(options: PrintOptions): Promise<PrintResult>;
|
|
39
|
+
//# sourceMappingURL=api.d.ts.map
|
package/api.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["api.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAC5C,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,UAAU,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC1B,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAEzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IAGZ,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IACxB,KAAK,EAAE,MAAM,CAAC;CACjB;AA0BD,wBAAgB,SAAS,IAAI,aAAa,CAczC;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAI9D;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAGD,wBAAsB,YAAY,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAuB3D;AA+VD,wBAAsB,MAAM,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAInE;AAED,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAMvE"}
|
package/api.js
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brother Label Printer API
|
|
3
|
+
* Programmatic interface for printing labels on Brother P-touch printers
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import * as os from "os";
|
|
8
|
+
import { renderHtmlFile, renderHtmlString, closeBrowser } from "./render.js";
|
|
9
|
+
import { Jimp, loadFont, measureText, measureTextHeight } from "jimp";
|
|
10
|
+
import { spawn } from "child_process";
|
|
11
|
+
import QRCode from "qrcode";
|
|
12
|
+
// Constants
|
|
13
|
+
const CONFIG_PATH = path.join(os.homedir(), ".brother-label.json");
|
|
14
|
+
const DEFAULT_PRINTER = "Brother PT-P710BT";
|
|
15
|
+
const PRINT_DPI = 300; // High resolution for quality output
|
|
16
|
+
// Tape sizes: height is printable area in pixels at 300 DPI
|
|
17
|
+
const TAPE_SIZES = {
|
|
18
|
+
6: { width: 1137, height: 56 }, // 3.79" x 0.19" @ 300 DPI
|
|
19
|
+
9: { width: 1137, height: 84 }, // 3.79" x 0.28" @ 300 DPI
|
|
20
|
+
12: { width: 1137, height: 113 }, // 3.79" x 0.38" @ 300 DPI
|
|
21
|
+
18: { width: 1137, height: 169 }, // 3.79" x 0.56" @ 300 DPI
|
|
22
|
+
24: { width: 1137, height: 213 }, // 3.79" x 0.71" @ 300 DPI
|
|
23
|
+
};
|
|
24
|
+
// Brother CustomMediaSize names and widths in microns
|
|
25
|
+
const MEDIA_OPTIONS = {
|
|
26
|
+
6: { name: "CustomMediaSize257", width: 5900 },
|
|
27
|
+
9: { name: "CustomMediaSize258", width: 9000 },
|
|
28
|
+
12: { name: "CustomMediaSize259", width: 11900 },
|
|
29
|
+
18: { name: "CustomMediaSize260", width: 18100 },
|
|
30
|
+
24: { name: "CustomMediaSize261", width: 24000 },
|
|
31
|
+
};
|
|
32
|
+
// Config functions
|
|
33
|
+
export function getConfig() {
|
|
34
|
+
try {
|
|
35
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
36
|
+
const raw = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
|
|
37
|
+
// Handle legacy format where tape was stored as "24mm" string
|
|
38
|
+
if (typeof raw.defaultTape === "string") {
|
|
39
|
+
raw.defaultTape = parseInt(raw.defaultTape.replace("mm", ""), 10);
|
|
40
|
+
}
|
|
41
|
+
return raw;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Ignore config errors
|
|
46
|
+
}
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
export function setConfig(config) {
|
|
50
|
+
const current = getConfig();
|
|
51
|
+
const merged = { ...current, ...config };
|
|
52
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(merged, null, 2));
|
|
53
|
+
}
|
|
54
|
+
export function getConfigPath() {
|
|
55
|
+
return CONFIG_PATH;
|
|
56
|
+
}
|
|
57
|
+
// Printer listing
|
|
58
|
+
export async function listPrinters() {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const ps = spawn("powershell", [
|
|
61
|
+
"-Command",
|
|
62
|
+
"Get-Printer | Where-Object { $_.Name -like '*Brother*' -or $_.Name -like '*PT*' -or $_.Name -like '*QL*' } | Select-Object -ExpandProperty Name"
|
|
63
|
+
], { stdio: "pipe" });
|
|
64
|
+
let stdout = "";
|
|
65
|
+
let stderr = "";
|
|
66
|
+
ps.stdout.on("data", (data) => { stdout += data.toString(); });
|
|
67
|
+
ps.stderr.on("data", (data) => { stderr += data.toString(); });
|
|
68
|
+
ps.on("close", (code) => {
|
|
69
|
+
if (code !== 0) {
|
|
70
|
+
reject(new Error(`Failed to list printers: ${stderr}`));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const printers = stdout.trim().split("\n")
|
|
74
|
+
.filter(p => p.trim())
|
|
75
|
+
.map(name => ({ name: name.trim() }));
|
|
76
|
+
resolve(printers);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// Calculate HTML viewport dimensions from tape height and optional aspect ratio
|
|
81
|
+
function getHtmlDimensions(tapeHeight, aspect) {
|
|
82
|
+
if (!aspect) {
|
|
83
|
+
// Default: square viewport
|
|
84
|
+
return { width: tapeHeight, height: tapeHeight };
|
|
85
|
+
}
|
|
86
|
+
// Parse aspect ratio "width:height" or "width/height" (e.g., "3.5:2" or "3.5/2")
|
|
87
|
+
const separator = aspect.includes("/") ? "/" : ":";
|
|
88
|
+
const parts = aspect.split(separator);
|
|
89
|
+
if (parts.length !== 2) {
|
|
90
|
+
throw new Error(`Invalid aspect ratio: ${aspect}. Use format "width:height" or "width/height"`);
|
|
91
|
+
}
|
|
92
|
+
const aspectWidth = parseFloat(parts[0]);
|
|
93
|
+
const aspectHeight = parseFloat(parts[1]);
|
|
94
|
+
if (isNaN(aspectWidth) || isNaN(aspectHeight) || aspectHeight === 0) {
|
|
95
|
+
throw new Error(`Invalid aspect ratio: ${aspect}. Use format "width:height" (e.g., "3.5:2")`);
|
|
96
|
+
}
|
|
97
|
+
// Scale so height fits tape, width is proportional
|
|
98
|
+
const height = tapeHeight;
|
|
99
|
+
const width = Math.round(tapeHeight * (aspectWidth / aspectHeight));
|
|
100
|
+
return { width, height };
|
|
101
|
+
}
|
|
102
|
+
// Validation
|
|
103
|
+
function validateOptions(options) {
|
|
104
|
+
const contentFields = [
|
|
105
|
+
options.text,
|
|
106
|
+
options.html,
|
|
107
|
+
options.htmlPath,
|
|
108
|
+
options.textFile,
|
|
109
|
+
options.imagePath,
|
|
110
|
+
options.imageBuffer,
|
|
111
|
+
options.qr,
|
|
112
|
+
].filter(f => f !== undefined);
|
|
113
|
+
if (contentFields.length === 0) {
|
|
114
|
+
throw new Error("No content provided. Specify one of: text, html, htmlPath, textFile, imagePath, imageBuffer, qr");
|
|
115
|
+
}
|
|
116
|
+
if (contentFields.length > 1) {
|
|
117
|
+
throw new Error("Multiple content options provided. Specify exactly one.");
|
|
118
|
+
}
|
|
119
|
+
if (options.tape !== undefined && !TAPE_SIZES[options.tape]) {
|
|
120
|
+
throw new Error(`Invalid tape size: ${options.tape}. Valid sizes: 6, 9, 12, 18, 24`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Resolve effective settings from options + config + defaults
|
|
124
|
+
function resolveSettings(options) {
|
|
125
|
+
const config = getConfig();
|
|
126
|
+
return {
|
|
127
|
+
tape: options.tape ?? config.defaultTape ?? 24,
|
|
128
|
+
printer: options.printer ?? config.defaultPrinter ?? DEFAULT_PRINTER,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
// Render text to image buffer
|
|
132
|
+
async function renderText(text, tape) {
|
|
133
|
+
const tapeSize = TAPE_SIZES[tape];
|
|
134
|
+
const targetHeight = tapeSize.height;
|
|
135
|
+
const lines = text.split("\\n").join("\n");
|
|
136
|
+
// Load largest font for quality
|
|
137
|
+
const fontDir = path.join(path.dirname(new URL(import.meta.url).pathname).replace(/^\/([A-Z]:)/, "$1"), "node_modules/@jimp/plugin-print/fonts/open-sans");
|
|
138
|
+
const fontPath = path.join(fontDir, "open-sans-128-black", "open-sans-128-black.fnt");
|
|
139
|
+
const font = await loadFont(fontPath);
|
|
140
|
+
// Measure text
|
|
141
|
+
const textWidth = measureText(font, lines);
|
|
142
|
+
const textHeight = measureTextHeight(font, lines, textWidth + 100);
|
|
143
|
+
// Create temp image for text
|
|
144
|
+
const padding = 10;
|
|
145
|
+
const imgWidth = textWidth + padding * 2;
|
|
146
|
+
const imgHeight = textHeight + padding * 2;
|
|
147
|
+
const tempImage = new Jimp({ width: imgWidth, height: imgHeight, color: 0xffffffff });
|
|
148
|
+
tempImage.print({ font, x: padding, y: padding, text: lines });
|
|
149
|
+
// Scale to fit tape height
|
|
150
|
+
const scale = targetHeight / imgHeight;
|
|
151
|
+
const finalWidth = Math.round(imgWidth * scale);
|
|
152
|
+
const finalHeight = Math.round(imgHeight * scale);
|
|
153
|
+
tempImage.resize({ w: finalWidth, h: finalHeight });
|
|
154
|
+
// Create final image with padding
|
|
155
|
+
const hPadding = Math.round(targetHeight * 0.2);
|
|
156
|
+
const outputWidth = finalWidth + hPadding * 2;
|
|
157
|
+
const image = new Jimp({ width: outputWidth, height: targetHeight, color: 0xffffffff });
|
|
158
|
+
const xOffset = hPadding;
|
|
159
|
+
const yOffset = Math.round((targetHeight - finalHeight) / 2);
|
|
160
|
+
image.composite(tempImage, xOffset, yOffset);
|
|
161
|
+
return image.getBuffer("image/png");
|
|
162
|
+
}
|
|
163
|
+
// Render QR code to image buffer
|
|
164
|
+
async function renderQr(data, tape, labelText) {
|
|
165
|
+
const tapeSize = TAPE_SIZES[tape];
|
|
166
|
+
const targetHeight = tapeSize.height;
|
|
167
|
+
const qrSize = Math.floor(targetHeight * 0.95);
|
|
168
|
+
// Margins in pixels at PRINT_DPI (3mm left, 4mm right)
|
|
169
|
+
const pxPerMm = PRINT_DPI / 25.4;
|
|
170
|
+
const leftMargin = Math.round(3 * pxPerMm);
|
|
171
|
+
const rightMargin = Math.round(4 * pxPerMm);
|
|
172
|
+
// Generate QR code at high resolution
|
|
173
|
+
const qrBuffer = await QRCode.toBuffer(data, {
|
|
174
|
+
type: "png",
|
|
175
|
+
width: qrSize,
|
|
176
|
+
margin: 0,
|
|
177
|
+
errorCorrectionLevel: "M",
|
|
178
|
+
color: { dark: "#000000", light: "#ffffff" },
|
|
179
|
+
});
|
|
180
|
+
const qrImage = await Jimp.read(qrBuffer);
|
|
181
|
+
if (!labelText) {
|
|
182
|
+
// QR only
|
|
183
|
+
const outputWidth = leftMargin + qrSize + rightMargin;
|
|
184
|
+
const image = new Jimp({ width: outputWidth, height: targetHeight, color: 0xffffffff });
|
|
185
|
+
const yOffset = Math.floor((targetHeight - qrSize) / 2);
|
|
186
|
+
image.composite(qrImage, leftMargin, yOffset);
|
|
187
|
+
return image.getBuffer("image/png");
|
|
188
|
+
}
|
|
189
|
+
// QR + text label
|
|
190
|
+
const fontDir = path.join(path.dirname(new URL(import.meta.url).pathname).replace(/^\/([A-Z]:)/, "$1"), "node_modules/@jimp/plugin-print/fonts/open-sans");
|
|
191
|
+
const fontPath = path.join(fontDir, "open-sans-64-black", "open-sans-64-black.fnt");
|
|
192
|
+
const font = await loadFont(fontPath);
|
|
193
|
+
const textWidth = measureText(font, labelText);
|
|
194
|
+
const textHeight = measureTextHeight(font, labelText, textWidth + 50);
|
|
195
|
+
// Scale text to fit beside QR
|
|
196
|
+
const maxTextHeight = targetHeight * 0.8;
|
|
197
|
+
const textScale = Math.min(1, maxTextHeight / textHeight);
|
|
198
|
+
const scaledTextWidth = Math.round(textWidth * textScale);
|
|
199
|
+
const scaledTextHeight = Math.round(textHeight * textScale);
|
|
200
|
+
// Create text image
|
|
201
|
+
const textImg = new Jimp({ width: textWidth + 20, height: textHeight + 20, color: 0xffffffff });
|
|
202
|
+
textImg.print({ font, x: 10, y: 10, text: labelText });
|
|
203
|
+
if (textScale < 1) {
|
|
204
|
+
textImg.resize({ w: scaledTextWidth, h: scaledTextHeight });
|
|
205
|
+
}
|
|
206
|
+
// Compose: QR on left, text on right
|
|
207
|
+
const gap = Math.floor(targetHeight * 0.15);
|
|
208
|
+
const outputWidth = leftMargin + qrSize + gap + scaledTextWidth + rightMargin;
|
|
209
|
+
const image = new Jimp({ width: outputWidth, height: targetHeight, color: 0xffffffff });
|
|
210
|
+
const qrY = Math.floor((targetHeight - qrSize) / 2);
|
|
211
|
+
image.composite(qrImage, leftMargin, qrY);
|
|
212
|
+
const textY = Math.floor((targetHeight - scaledTextHeight) / 2);
|
|
213
|
+
image.composite(textImg, leftMargin + qrSize + gap, textY);
|
|
214
|
+
return image.getBuffer("image/png");
|
|
215
|
+
}
|
|
216
|
+
// Render content to image buffer
|
|
217
|
+
async function renderContent(options, tape) {
|
|
218
|
+
const tapeSize = TAPE_SIZES[tape];
|
|
219
|
+
if (options.text !== undefined) {
|
|
220
|
+
return renderText(options.text, tape);
|
|
221
|
+
}
|
|
222
|
+
if (options.textFile !== undefined) {
|
|
223
|
+
const text = fs.readFileSync(options.textFile, "utf-8");
|
|
224
|
+
return renderText(text, tape);
|
|
225
|
+
}
|
|
226
|
+
if (options.html !== undefined) {
|
|
227
|
+
const { width, height } = getHtmlDimensions(tapeSize.height, options.aspect);
|
|
228
|
+
const buffer = await renderHtmlString(options.html, { width, height }, options.basePath);
|
|
229
|
+
await closeBrowser();
|
|
230
|
+
return buffer;
|
|
231
|
+
}
|
|
232
|
+
if (options.htmlPath !== undefined) {
|
|
233
|
+
const { width, height } = getHtmlDimensions(tapeSize.height, options.aspect);
|
|
234
|
+
const buffer = await renderHtmlFile(options.htmlPath, { width, height });
|
|
235
|
+
await closeBrowser();
|
|
236
|
+
return buffer;
|
|
237
|
+
}
|
|
238
|
+
if (options.imagePath !== undefined) {
|
|
239
|
+
return fs.readFileSync(options.imagePath);
|
|
240
|
+
}
|
|
241
|
+
if (options.imageBuffer !== undefined) {
|
|
242
|
+
return options.imageBuffer;
|
|
243
|
+
}
|
|
244
|
+
if (options.qr !== undefined) {
|
|
245
|
+
return renderQr(options.qr, tape, options.qrLabel);
|
|
246
|
+
}
|
|
247
|
+
throw new Error("No content to render");
|
|
248
|
+
}
|
|
249
|
+
// Print single image buffer to printer
|
|
250
|
+
async function printBuffer(imageBuffer, printer, tape) {
|
|
251
|
+
const media = MEDIA_OPTIONS[tape];
|
|
252
|
+
const tempPath = path.join(os.tmpdir(), `label-${Date.now()}.png`);
|
|
253
|
+
fs.writeFileSync(tempPath, imageBuffer);
|
|
254
|
+
try {
|
|
255
|
+
await new Promise((resolve, reject) => {
|
|
256
|
+
const psScript = `
|
|
257
|
+
Add-Type -AssemblyName System.Drawing
|
|
258
|
+
Add-Type -AssemblyName System.Printing
|
|
259
|
+
Add-Type -AssemblyName ReachFramework
|
|
260
|
+
Add-Type -AssemblyName PresentationCore
|
|
261
|
+
|
|
262
|
+
$img = [System.Drawing.Image]::FromFile('${tempPath.replace(/\\/g, "\\\\")}')
|
|
263
|
+
|
|
264
|
+
$DPI = ${PRINT_DPI}
|
|
265
|
+
$MICRONS_PER_INCH = 25400
|
|
266
|
+
|
|
267
|
+
$labelLengthMicrons = [int]($img.Width / $DPI * $MICRONS_PER_INCH) + 1000
|
|
268
|
+
$tapeWidthMicrons = ${media.width}
|
|
269
|
+
|
|
270
|
+
$imgWidthWpf = $img.Width / $DPI * 96
|
|
271
|
+
$imgHeightWpf = $img.Height / $DPI * 96
|
|
272
|
+
|
|
273
|
+
$ticketXml = @"
|
|
274
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
275
|
+
<psf:PrintTicket xmlns:psf="http://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
|
|
276
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1"
|
|
277
|
+
xmlns:psk="http://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords"
|
|
278
|
+
xmlns:ns0001="http://schemas.brother.info/mfc/printing/2006/11/printschemakeywords">
|
|
279
|
+
<psf:Feature name="psk:PageMediaSize">
|
|
280
|
+
<psf:Option name="ns0001:${media.name}">
|
|
281
|
+
<psf:ScoredProperty name="psk:MediaSizeWidth">
|
|
282
|
+
<psf:Value xsi:type="xsd:integer">${media.width}</psf:Value>
|
|
283
|
+
</psf:ScoredProperty>
|
|
284
|
+
<psf:ScoredProperty name="psk:MediaSizeHeight">
|
|
285
|
+
<psf:ParameterRef name="psk:PageMediaSizeMediaSizeHeight" />
|
|
286
|
+
</psf:ScoredProperty>
|
|
287
|
+
</psf:Option>
|
|
288
|
+
</psf:Feature>
|
|
289
|
+
<psf:ParameterInit name="psk:PageMediaSizeMediaSizeHeight">
|
|
290
|
+
<psf:Value xsi:type="xsd:integer">$labelLengthMicrons</psf:Value>
|
|
291
|
+
</psf:ParameterInit>
|
|
292
|
+
<psf:Feature name="psk:PageOrientation">
|
|
293
|
+
<psf:Option name="psk:Landscape" />
|
|
294
|
+
</psf:Feature>
|
|
295
|
+
<psf:Feature name="ns0001:PageRollFeedToEndOfSheet">
|
|
296
|
+
<psf:Option name="ns0001:FeedToImageEdge" />
|
|
297
|
+
</psf:Feature>
|
|
298
|
+
<psf:Feature name="psk:PageMediaType">
|
|
299
|
+
<psf:Option name="psk:Label" />
|
|
300
|
+
</psf:Feature>
|
|
301
|
+
<psf:Feature name="psk:JobRollCutAtEndOfJob">
|
|
302
|
+
<psf:Option name="psk:CutSheetAtStandardMediaSize" />
|
|
303
|
+
</psf:Feature>
|
|
304
|
+
<psf:Feature name="ns0001:JobRollCutSheet">
|
|
305
|
+
<psf:Option name="ns0001:CutSheet">
|
|
306
|
+
<psf:ScoredProperty name="ns0001:CutSheetCount">
|
|
307
|
+
<psf:ParameterRef name="ns0001:JobCutSheetCount" />
|
|
308
|
+
</psf:ScoredProperty>
|
|
309
|
+
</psf:Option>
|
|
310
|
+
</psf:Feature>
|
|
311
|
+
<psf:ParameterInit name="ns0001:JobCutSheetCount">
|
|
312
|
+
<psf:Value xsi:type="xsd:integer">1</psf:Value>
|
|
313
|
+
</psf:ParameterInit>
|
|
314
|
+
</psf:PrintTicket>
|
|
315
|
+
"@
|
|
316
|
+
|
|
317
|
+
$xmlBytes = [System.Text.Encoding]::UTF8.GetBytes($ticketXml)
|
|
318
|
+
$memStream = New-Object System.IO.MemoryStream(,$xmlBytes)
|
|
319
|
+
$ticket = New-Object System.Printing.PrintTicket($memStream)
|
|
320
|
+
|
|
321
|
+
$server = New-Object System.Printing.LocalPrintServer
|
|
322
|
+
$queue = $server.GetPrintQueue('${printer}')
|
|
323
|
+
|
|
324
|
+
$xpsPath = [System.IO.Path]::GetTempFileName() + ".xps"
|
|
325
|
+
|
|
326
|
+
$pageWidth = $labelLengthMicrons / $MICRONS_PER_INCH * 96
|
|
327
|
+
$pageHeight = $tapeWidthMicrons / $MICRONS_PER_INCH * 96
|
|
328
|
+
|
|
329
|
+
$package = [System.IO.Packaging.Package]::Open($xpsPath, [System.IO.FileMode]::Create)
|
|
330
|
+
$xpsDoc = New-Object System.Windows.Xps.Packaging.XpsDocument($package)
|
|
331
|
+
$writer = [System.Windows.Xps.Packaging.XpsDocument]::CreateXpsDocumentWriter($xpsDoc)
|
|
332
|
+
|
|
333
|
+
$visual = New-Object System.Windows.Media.DrawingVisual
|
|
334
|
+
$dc = $visual.RenderOpen()
|
|
335
|
+
|
|
336
|
+
$bitmapImg = New-Object System.Windows.Media.Imaging.BitmapImage
|
|
337
|
+
$bitmapImg.BeginInit()
|
|
338
|
+
$bitmapImg.UriSource = New-Object System.Uri('${tempPath.replace(/\\/g, "\\\\")}')
|
|
339
|
+
$bitmapImg.EndInit()
|
|
340
|
+
|
|
341
|
+
$yOffset = ($pageHeight - $imgHeightWpf) / 2
|
|
342
|
+
$rect = New-Object System.Windows.Rect(0, $yOffset, $imgWidthWpf, $imgHeightWpf)
|
|
343
|
+
$dc.DrawImage($bitmapImg, $rect)
|
|
344
|
+
$dc.Close()
|
|
345
|
+
|
|
346
|
+
$writer.Write($visual, $ticket)
|
|
347
|
+
|
|
348
|
+
$xpsDoc.Close()
|
|
349
|
+
$package.Close()
|
|
350
|
+
$img.Dispose()
|
|
351
|
+
|
|
352
|
+
$xpsDocForPrint = New-Object System.Windows.Xps.Packaging.XpsDocument($xpsPath, [System.IO.FileAccess]::Read)
|
|
353
|
+
$seq = $xpsDocForPrint.GetFixedDocumentSequence()
|
|
354
|
+
|
|
355
|
+
$xpsWriter = [System.Printing.PrintQueue]::CreateXpsDocumentWriter($queue)
|
|
356
|
+
$xpsWriter.Write($seq, $ticket)
|
|
357
|
+
|
|
358
|
+
$xpsDocForPrint.Close()
|
|
359
|
+
Remove-Item $xpsPath -ErrorAction SilentlyContinue
|
|
360
|
+
`;
|
|
361
|
+
const ps = spawn("powershell", ["-Command", psScript], { stdio: "pipe" });
|
|
362
|
+
let stdout = "";
|
|
363
|
+
let stderr = "";
|
|
364
|
+
ps.stdout.on("data", (data) => { stdout += data.toString(); });
|
|
365
|
+
ps.stderr.on("data", (data) => { stderr += data.toString(); });
|
|
366
|
+
ps.on("close", (code) => {
|
|
367
|
+
if (code === 0) {
|
|
368
|
+
resolve();
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
reject(new Error(`Print failed: ${stderr}`));
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
ps.on("error", (err) => {
|
|
375
|
+
reject(new Error(`Failed to print: ${err.message}`));
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
finally {
|
|
380
|
+
if (fs.existsSync(tempPath)) {
|
|
381
|
+
fs.unlinkSync(tempPath);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// Main API functions
|
|
386
|
+
export async function render(options) {
|
|
387
|
+
validateOptions(options);
|
|
388
|
+
const { tape } = resolveSettings(options);
|
|
389
|
+
return renderContent(options, tape);
|
|
390
|
+
}
|
|
391
|
+
export async function print(options) {
|
|
392
|
+
validateOptions(options);
|
|
393
|
+
const { tape, printer } = resolveSettings(options);
|
|
394
|
+
const image = await renderContent(options, tape);
|
|
395
|
+
await printBuffer(image, printer, tape);
|
|
396
|
+
return { image };
|
|
397
|
+
}
|
|
398
|
+
//# sourceMappingURL=api.js.map
|
package/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["api.ts"],"names":[],"mappings":"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"}
|