@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/render.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["render.ts"],"names":[],"mappings":"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"}
package/render.ts ADDED
@@ -0,0 +1,201 @@
1
+ /**
2
+ * HTML to bitmap rendering module
3
+ * Standalone utility for converting HTML to PNG images
4
+ */
5
+
6
+ import puppeteer, { Browser } from "puppeteer";
7
+ import * as path from "path";
8
+ import * as fs from "fs";
9
+ import { Jimp } from "jimp";
10
+ import QRCode from "qrcode";
11
+
12
+ export interface RenderOptions {
13
+ width: number;
14
+ height: number;
15
+ deviceScaleFactor?: number; // Default 3 for high quality (288 DPI effective)
16
+ keepScale?: boolean; // If true, don't scale down - keep full resolution output
17
+ }
18
+
19
+ let browserInstance: Browser | null = null;
20
+
21
+ const DEFAULT_SCALE = 3; // Render at 3x for quality, then scale down
22
+
23
+ /**
24
+ * Generate QR code data URLs for all qr attributes found in page
25
+ * Returns map of qr data -> data URL
26
+ */
27
+ async function generateQrDataUrls(qrValues: string[]): Promise<Map<string, string>> {
28
+ const map = new Map<string, string>();
29
+ for (const data of qrValues) {
30
+ if (!map.has(data)) {
31
+ const dataUrl = await QRCode.toDataURL(data, {
32
+ type: "image/png",
33
+ margin: 0,
34
+ errorCorrectionLevel: "M",
35
+ color: { dark: "#000000", light: "#ffffff" },
36
+ });
37
+ map.set(data, dataUrl);
38
+ }
39
+ }
40
+ return map;
41
+ }
42
+
43
+ /**
44
+ * Process <img qr="..."> elements in page via DOM manipulation
45
+ */
46
+ async function processQrElements(page: puppeteer.Page): Promise<void> {
47
+ // Get all qr attribute values from the page
48
+ const qrValues = await page.evaluate(() => {
49
+ const imgs = document.querySelectorAll("img[qr]");
50
+ return Array.from(imgs).map(img => img.getAttribute("qr") || "");
51
+ });
52
+
53
+ if (qrValues.length === 0) return;
54
+
55
+ // Generate QR codes
56
+ const qrMap = await generateQrDataUrls(qrValues);
57
+
58
+ // Convert map to object for passing to page context
59
+ const qrUrls: Record<string, string> = {};
60
+ qrMap.forEach((url, data) => { qrUrls[data] = url; });
61
+
62
+ // Replace qr attributes with src in the DOM
63
+ await page.evaluate((urls: Record<string, string>) => {
64
+ const imgs = document.querySelectorAll("img[qr]");
65
+ imgs.forEach(img => {
66
+ const qrData = img.getAttribute("qr");
67
+ if (qrData && urls[qrData]) {
68
+ img.setAttribute("src", urls[qrData]);
69
+ img.removeAttribute("qr");
70
+ }
71
+ });
72
+ }, qrUrls);
73
+ }
74
+
75
+ /**
76
+ * Scale image buffer down to target dimensions
77
+ */
78
+ async function scaleDown(buffer: Buffer, targetWidth: number, targetHeight: number): Promise<Buffer> {
79
+ const image = await Jimp.read(buffer);
80
+ image.resize({ w: targetWidth, h: targetHeight });
81
+ return image.getBuffer("image/png");
82
+ }
83
+
84
+ /**
85
+ * Get or create a shared browser instance for better performance
86
+ */
87
+ async function getBrowser(): Promise<Browser> {
88
+ if (!browserInstance || !browserInstance.connected) {
89
+ browserInstance = await puppeteer.launch({ headless: true });
90
+ }
91
+ return browserInstance;
92
+ }
93
+
94
+ /**
95
+ * Close the shared browser instance
96
+ */
97
+ export async function closeBrowser(): Promise<void> {
98
+ if (browserInstance) {
99
+ await browserInstance.close();
100
+ browserInstance = null;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Render HTML file to PNG buffer
106
+ * Processes <img qr="..."> tags to inline base64 QR codes
107
+ */
108
+ export async function renderHtmlFile(htmlPath: string, options: RenderOptions): Promise<Buffer> {
109
+ const absolutePath = path.resolve(htmlPath);
110
+ if (!fs.existsSync(absolutePath)) {
111
+ throw new Error(`HTML file not found: ${absolutePath}`);
112
+ }
113
+ return renderHtmlUrl(`file://${absolutePath}`, options);
114
+ }
115
+
116
+ /**
117
+ * Render HTML string to PNG buffer
118
+ * Processes <img qr="..."> tags to inline base64 QR codes via DOM
119
+ * @param html - HTML content string
120
+ * @param options - Render dimensions
121
+ * @param basePath - Optional base path for resolving relative resources
122
+ */
123
+ export async function renderHtmlString(html: string, options: RenderOptions, basePath?: string): Promise<Buffer> {
124
+ const browser = await getBrowser();
125
+ const page = await browser.newPage();
126
+
127
+ try {
128
+ await page.setViewport({
129
+ width: options.width,
130
+ height: options.height,
131
+ deviceScaleFactor: options.deviceScaleFactor ?? 3,
132
+ });
133
+
134
+ if (basePath) {
135
+ // Set base URL for relative resource resolution
136
+ const baseUrl = `file://${path.resolve(basePath)}/`;
137
+ await page.goto(baseUrl, { waitUntil: "domcontentloaded" });
138
+ await page.setContent(html, { waitUntil: "networkidle0" });
139
+ } else {
140
+ await page.setContent(html, { waitUntil: "networkidle0" });
141
+ }
142
+
143
+ // Process <img qr="..."> elements
144
+ await processQrElements(page);
145
+
146
+ const pngBuffer = await page.screenshot({ type: "png" });
147
+ const scaleFactor = options.deviceScaleFactor ?? DEFAULT_SCALE;
148
+ if (scaleFactor > 1 && !options.keepScale) {
149
+ return scaleDown(Buffer.from(pngBuffer), options.width, options.height);
150
+ }
151
+ return Buffer.from(pngBuffer);
152
+ } finally {
153
+ await page.close();
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Render HTML from URL to PNG buffer
159
+ * Processes <img qr="..."> tags to inline base64 QR codes via DOM
160
+ */
161
+ export async function renderHtmlUrl(url: string, options: RenderOptions): Promise<Buffer> {
162
+ const browser = await getBrowser();
163
+ const page = await browser.newPage();
164
+
165
+ try {
166
+ await page.setViewport({
167
+ width: options.width,
168
+ height: options.height,
169
+ deviceScaleFactor: options.deviceScaleFactor ?? 3,
170
+ });
171
+
172
+ await page.goto(url, { waitUntil: "networkidle0" });
173
+
174
+ // Process <img qr="..."> elements
175
+ await processQrElements(page);
176
+
177
+ const pngBuffer = await page.screenshot({ type: "png" });
178
+ const scaleFactor = options.deviceScaleFactor ?? DEFAULT_SCALE;
179
+ if (scaleFactor > 1 && !options.keepScale) {
180
+ return scaleDown(Buffer.from(pngBuffer), options.width, options.height);
181
+ }
182
+ return Buffer.from(pngBuffer);
183
+ } finally {
184
+ await page.close();
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Render HTML file and save to PNG file
190
+ */
191
+ export async function renderHtmlToFile(
192
+ htmlPath: string,
193
+ outputPath: string,
194
+ options: RenderOptions
195
+ ): Promise<void> {
196
+ const buffer = await renderHtmlFile(htmlPath, options);
197
+ fs.writeFileSync(outputPath, buffer);
198
+ }
199
+
200
+ // Legacy export for compatibility
201
+ export const renderHtml = renderHtmlFile;
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "outDir": ".",
11
+ "rootDir": ".",
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true
15
+ },
16
+ "include": ["*.ts"],
17
+ "exclude": ["node_modules"]
18
+ }