@devraghu/electron-printer 1.1.0 → 2.1.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/README.md +17 -14
- package/dist/index.d.ts +1 -128
- package/dist/index.js +157 -1
- package/dist/preload/preload.cjs +1 -1
- package/dist/renderer/assets/index-Dr_XYfBm.css +1 -0
- package/dist/renderer/assets/index-Dxv7U4zV.js +9 -0
- package/dist/renderer/index.html +12 -1
- package/package.json +84 -92
- package/dist/renderer/renderer.js +0 -2
- package/dist/renderer/renderer.js.LICENSE.txt +0 -1
- package/dist/renderer/renderer.min.css +0 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ yarn add @devraghu/electron-printer
|
|
|
21
21
|
This library is designed for use in the **main process only**.
|
|
22
22
|
|
|
23
23
|
```js
|
|
24
|
-
|
|
24
|
+
import { printer } from '@devraghu/electron-printer';
|
|
25
25
|
|
|
26
26
|
const options = {
|
|
27
27
|
preview: false,
|
|
@@ -55,7 +55,7 @@ const data = [
|
|
|
55
55
|
},
|
|
56
56
|
];
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
printer
|
|
59
59
|
.print(data, options)
|
|
60
60
|
.then(() => console.log('Print successful'))
|
|
61
61
|
.catch((error) => console.error(error));
|
|
@@ -63,12 +63,14 @@ posPrinter
|
|
|
63
63
|
|
|
64
64
|
> **Note:** If you need to trigger printing from the renderer process, use IPC to communicate with the main process.
|
|
65
65
|
|
|
66
|
-
## TypeScript
|
|
66
|
+
## TypeScript
|
|
67
|
+
|
|
68
|
+
Full TypeScript support with exported types:
|
|
67
69
|
|
|
68
70
|
```typescript
|
|
69
|
-
import {
|
|
71
|
+
import { printer, type PrintData, type PrintOptions, type PrintResult } from '@devraghu/electron-printer';
|
|
70
72
|
|
|
71
|
-
const options:
|
|
73
|
+
const options: PrintOptions = {
|
|
72
74
|
preview: false,
|
|
73
75
|
margin: '0 0 0 0',
|
|
74
76
|
copies: 1,
|
|
@@ -77,7 +79,7 @@ const options: PosPrintOptions = {
|
|
|
77
79
|
pageSize: '80mm',
|
|
78
80
|
};
|
|
79
81
|
|
|
80
|
-
const data:
|
|
82
|
+
const data: PrintData[] = [
|
|
81
83
|
{
|
|
82
84
|
type: 'text',
|
|
83
85
|
value: 'Hello World',
|
|
@@ -85,7 +87,7 @@ const data: PosPrintData[] = [
|
|
|
85
87
|
},
|
|
86
88
|
];
|
|
87
89
|
|
|
88
|
-
|
|
90
|
+
printer
|
|
89
91
|
.print(data, options)
|
|
90
92
|
.then(() => console.log('Done'))
|
|
91
93
|
.catch((error) => console.error(error));
|
|
@@ -232,9 +234,9 @@ Tables can contain text and images in cells:
|
|
|
232
234
|
Retrieve a list of all available printers on the system:
|
|
233
235
|
|
|
234
236
|
```typescript
|
|
235
|
-
import {
|
|
237
|
+
import { printer } from '@devraghu/electron-printer';
|
|
236
238
|
|
|
237
|
-
const printers =
|
|
239
|
+
const printers = await printer.getPrinters();
|
|
238
240
|
console.log(printers);
|
|
239
241
|
// [{ name: 'XP-80C', isDefault: true, ... }, ...]
|
|
240
242
|
```
|
|
@@ -244,10 +246,10 @@ console.log(printers);
|
|
|
244
246
|
Open a cash drawer connected to a thermal printer:
|
|
245
247
|
|
|
246
248
|
```typescript
|
|
247
|
-
import {
|
|
249
|
+
import { printer } from '@devraghu/electron-printer';
|
|
248
250
|
|
|
249
251
|
// Open cash drawer on specific printer
|
|
250
|
-
|
|
252
|
+
printer
|
|
251
253
|
.openCashDrawer('XP-80C')
|
|
252
254
|
.then(() => console.log('Cash drawer opened'))
|
|
253
255
|
.catch((error) => console.error('Failed to open cash drawer:', error));
|
|
@@ -262,15 +264,16 @@ electron-printer/
|
|
|
262
264
|
├── src/
|
|
263
265
|
│ ├── main/
|
|
264
266
|
│ │ ├── index.ts # Main exports
|
|
265
|
-
│ │ ├──
|
|
267
|
+
│ │ ├── printer.ts # Printer API
|
|
266
268
|
│ │ ├── models.ts # TypeScript interfaces
|
|
267
269
|
│ │ └── utils.ts # Helper functions
|
|
268
270
|
│ ├── renderer/
|
|
269
271
|
│ │ ├── renderer.ts # Content rendering
|
|
270
272
|
│ │ ├── utils.ts # HTML generation
|
|
271
|
-
│ │
|
|
273
|
+
│ │ ├── index.html # Print template
|
|
274
|
+
│ │ └── index.css # Print styles
|
|
272
275
|
│ └── preload/
|
|
273
|
-
│ └── preload.ts # Preload script
|
|
276
|
+
│ └── preload.ts # Preload script
|
|
274
277
|
├── demo/ # Demo application
|
|
275
278
|
├── test/ # Playwright E2E tests
|
|
276
279
|
└── dist/ # Compiled output
|
package/dist/index.d.ts
CHANGED
|
@@ -1,128 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export declare const posPrinter: {
|
|
4
|
-
print: (data: PosPrintData[], options: PosPrintOptions) => Promise<PrintResult>;
|
|
5
|
-
openCashDrawer: typeof openCashDrawer;
|
|
6
|
-
getPrinters: typeof getAvailablePrinters;
|
|
7
|
-
};
|
|
8
|
-
export declare type PaperSize = "80mm" | "78mm" | "76mm" | "57mm" | "58mm" | "44mm";
|
|
9
|
-
/**
|
|
10
|
-
* @type PosPrintPosition
|
|
11
|
-
* @description Alignment for type barCode and qrCode
|
|
12
|
-
*
|
|
13
|
-
*/
|
|
14
|
-
export declare type PosPrintPosition = "left" | "center" | "right";
|
|
15
|
-
/**
|
|
16
|
-
* @type PosPrintType
|
|
17
|
-
* @name PosPrintType
|
|
18
|
-
* **/
|
|
19
|
-
export declare type PosPrintType = "text" | "barCode" | "qrCode" | "image" | "table";
|
|
20
|
-
/**
|
|
21
|
-
* @interface
|
|
22
|
-
* @name PosPrintData
|
|
23
|
-
* **/
|
|
24
|
-
export interface PosPrintData {
|
|
25
|
-
/**
|
|
26
|
-
* @property type
|
|
27
|
-
* @description type data to print: 'text' | 'barCode' | 'qrcode' | 'image' | 'table'
|
|
28
|
-
*/
|
|
29
|
-
type: PosPrintType;
|
|
30
|
-
value?: string;
|
|
31
|
-
style?: PrintDataStyle;
|
|
32
|
-
width?: string;
|
|
33
|
-
height?: string;
|
|
34
|
-
fontsize?: number;
|
|
35
|
-
displayValue?: boolean;
|
|
36
|
-
position?: PosPrintPosition;
|
|
37
|
-
path?: string;
|
|
38
|
-
url?: string;
|
|
39
|
-
tableHeader?: PosPrintTableField[] | string[];
|
|
40
|
-
tableBody?: PosPrintTableField[][] | string[][];
|
|
41
|
-
tableFooter?: PosPrintTableField[] | string[];
|
|
42
|
-
tableHeaderStyle?: PrintDataStyle;
|
|
43
|
-
tableBodyStyle?: PrintDataStyle;
|
|
44
|
-
tableFooterStyle?: PrintDataStyle;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* @description Print options
|
|
48
|
-
* {@link https://www.electronjs.org/docs/latest/api/web-contents#contentsprintoptions-callback}
|
|
49
|
-
* @field copies: number of copies to print
|
|
50
|
-
* @field preview: bool,false=print,true=pop preview window
|
|
51
|
-
* @field deviceName: string,default device name, check it at webContent.getPrinters()
|
|
52
|
-
* @field timeoutPerLine: int,timeout,actual time is :data.length * timeoutPerLine ms
|
|
53
|
-
* @field silent: To print silently
|
|
54
|
-
* @field pathTemplate: Path to HTML file for custom print options
|
|
55
|
-
*/
|
|
56
|
-
export interface PosPrintOptions {
|
|
57
|
-
header?: string;
|
|
58
|
-
width?: string | number;
|
|
59
|
-
footer?: string;
|
|
60
|
-
copies?: number;
|
|
61
|
-
preview?: boolean;
|
|
62
|
-
printerName?: string;
|
|
63
|
-
margin?: string;
|
|
64
|
-
timeOutPerLine?: number;
|
|
65
|
-
silent?: boolean;
|
|
66
|
-
color?: boolean;
|
|
67
|
-
printBackground?: boolean;
|
|
68
|
-
margins?: {
|
|
69
|
-
marginType?: "default" | "none" | "printableArea" | "custom";
|
|
70
|
-
top?: number;
|
|
71
|
-
bottom?: number;
|
|
72
|
-
right?: number;
|
|
73
|
-
left?: number;
|
|
74
|
-
};
|
|
75
|
-
landscape?: boolean;
|
|
76
|
-
scaleFactor?: number;
|
|
77
|
-
pagesPerSheet?: number;
|
|
78
|
-
collate?: boolean;
|
|
79
|
-
pageRanges?: {
|
|
80
|
-
from: number;
|
|
81
|
-
to: number;
|
|
82
|
-
}[];
|
|
83
|
-
duplexMode?: "simplex" | "shortEdge" | "longEdge";
|
|
84
|
-
pageSize?: PaperSize | SizeOptions;
|
|
85
|
-
dpi?: {
|
|
86
|
-
horizontal: number;
|
|
87
|
-
vertical: number;
|
|
88
|
-
};
|
|
89
|
-
pathTemplate?: string;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* @interface
|
|
93
|
-
* @name PosPrintTableField
|
|
94
|
-
* */
|
|
95
|
-
export interface PosPrintTableField {
|
|
96
|
-
type: "text" | "image";
|
|
97
|
-
value?: string;
|
|
98
|
-
path?: string;
|
|
99
|
-
style?: PrintDataStyle;
|
|
100
|
-
width?: string;
|
|
101
|
-
height?: string;
|
|
102
|
-
}
|
|
103
|
-
export interface PrintResult {
|
|
104
|
-
complete: boolean;
|
|
105
|
-
data?: PosPrintData[];
|
|
106
|
-
options?: PosPrintOptions;
|
|
107
|
-
}
|
|
108
|
-
export interface SizeOptions {
|
|
109
|
-
height: number;
|
|
110
|
-
width: number;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* CSS Style interface - a subset of CSSStyleDeclaration properties commonly used for printing
|
|
114
|
-
* Uses Record type for flexibility while maintaining type safety
|
|
115
|
-
*/
|
|
116
|
-
export type PrintDataStyle = {
|
|
117
|
-
[K in keyof CSSStyleDeclaration]?: CSSStyleDeclaration[K];
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
export {
|
|
121
|
-
DrawerOptions,
|
|
122
|
-
OpenCashDrawerResult,
|
|
123
|
-
PrinterErrorCodes,
|
|
124
|
-
PrinterInfo,
|
|
125
|
-
PrinterStatus,
|
|
126
|
-
PrinterType,
|
|
127
|
-
};
|
|
128
|
-
|
|
1
|
+
export { }
|
package/dist/index.js
CHANGED
|
@@ -1 +1,157 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ipcMain as w, BrowserWindow as p } from "electron";
|
|
2
|
+
import { join as d } from "node:path";
|
|
3
|
+
import { getAvailablePrinters as g, openCashDrawer as P } from "@devraghu/cashdrawer";
|
|
4
|
+
import { PrinterErrorCodes as D, PrinterStatus as R, PrinterType as k } from "@devraghu/cashdrawer";
|
|
5
|
+
const l = /* @__PURE__ */ new Set(["44mm", "57mm", "58mm", "76mm", "78mm", "80mm"]), y = {
|
|
6
|
+
"44mm": 166,
|
|
7
|
+
"57mm": 215,
|
|
8
|
+
"58mm": 219,
|
|
9
|
+
"76mm": 287,
|
|
10
|
+
"78mm": 295,
|
|
11
|
+
"80mm": 302
|
|
12
|
+
}, b = {
|
|
13
|
+
"44mm": 44e3,
|
|
14
|
+
"57mm": 57e3,
|
|
15
|
+
"58mm": 58e3,
|
|
16
|
+
"76mm": 76e3,
|
|
17
|
+
"78mm": 78e3,
|
|
18
|
+
"80mm": 8e4
|
|
19
|
+
};
|
|
20
|
+
function h(e, t, n) {
|
|
21
|
+
return new Promise((r, i) => {
|
|
22
|
+
w.once(`${e}-reply`, (o, a) => {
|
|
23
|
+
a.status ? r(a) : i(a.error);
|
|
24
|
+
}), t.send(e, n);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function E(e) {
|
|
28
|
+
if (typeof e == "string") {
|
|
29
|
+
if (!l.has(e)) {
|
|
30
|
+
const r = [...l].join(", ");
|
|
31
|
+
throw new Error(`Invalid pageSize "${e}". Valid sizes are: ${r}`);
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
width: y[e],
|
|
35
|
+
height: 1200
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (typeof e == "object") {
|
|
39
|
+
if (!e.width || !e.height)
|
|
40
|
+
throw new Error("height and width properties are required for options.pageSize");
|
|
41
|
+
return {
|
|
42
|
+
width: e.width,
|
|
43
|
+
height: e.height
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return { width: 219, height: 1200 };
|
|
47
|
+
}
|
|
48
|
+
function s(e) {
|
|
49
|
+
return Math.ceil(e * 264.5833);
|
|
50
|
+
}
|
|
51
|
+
async function v(e, t) {
|
|
52
|
+
if (typeof t == "string") {
|
|
53
|
+
const i = await e.executeJavaScript("document.body.clientHeight");
|
|
54
|
+
return {
|
|
55
|
+
width: b[t] ?? 58e3,
|
|
56
|
+
height: s(i)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return typeof t == "object" ? {
|
|
60
|
+
width: s(t.width),
|
|
61
|
+
height: s(t.height)
|
|
62
|
+
} : { width: 58e3, height: 1e4 };
|
|
63
|
+
}
|
|
64
|
+
function I(e, t, n) {
|
|
65
|
+
return new Promise((r, i) => {
|
|
66
|
+
const o = setTimeout(() => {
|
|
67
|
+
i(new Error("[TimedOutError] Make sure your printer is connected"));
|
|
68
|
+
}, t), a = () => clearTimeout(o);
|
|
69
|
+
n.addEventListener("abort", a, { once: !0 }), e.then((c) => {
|
|
70
|
+
a(), r(c);
|
|
71
|
+
}).catch((c) => {
|
|
72
|
+
a(), i(c);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function x(e, t) {
|
|
77
|
+
if (!t.preview && !t.printerName && !t.silent)
|
|
78
|
+
throw new Error("A printer name is required, if you don't want to specify a printer name, set silent to true");
|
|
79
|
+
if (typeof t.copies == "number" && t.copies <= 0)
|
|
80
|
+
throw new Error(`Invalid copies value: ${t.copies}. Must be a positive integer`);
|
|
81
|
+
for (const [n, r] of e.entries()) {
|
|
82
|
+
if (r.type === "image" && !r.path && !r.url)
|
|
83
|
+
throw new Error(`Print item ${n}: image requires a path or url`);
|
|
84
|
+
if (r.style && typeof r.style != "object")
|
|
85
|
+
throw new Error(`Print item ${n}: style must be an object. Example: {style: {fontSize: 12}}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (process.type === "renderer")
|
|
89
|
+
throw new Error("electron-printer: this module must be used in the main process only");
|
|
90
|
+
function M(e, t) {
|
|
91
|
+
return {
|
|
92
|
+
silent: !!e.silent,
|
|
93
|
+
printBackground: !!e.printBackground,
|
|
94
|
+
deviceName: e.printerName,
|
|
95
|
+
copies: e.copies ?? 1,
|
|
96
|
+
pageSize: t,
|
|
97
|
+
header: e.header,
|
|
98
|
+
footer: e.footer,
|
|
99
|
+
color: e.color,
|
|
100
|
+
margins: e.margins,
|
|
101
|
+
landscape: e.landscape,
|
|
102
|
+
scaleFactor: e.scaleFactor,
|
|
103
|
+
pagesPerSheet: e.pagesPerSheet,
|
|
104
|
+
collate: e.collate,
|
|
105
|
+
pageRanges: e.pageRanges,
|
|
106
|
+
duplexMode: e.duplexMode,
|
|
107
|
+
dpi: e.dpi
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
async function C(e, t) {
|
|
111
|
+
for (const [n, r] of t.entries()) {
|
|
112
|
+
const i = await h("render-line", e.webContents, { printItem: r, itemIndex: n });
|
|
113
|
+
if (!i.status)
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Failed to render item ${n} (type: ${r.type}): ${i.error || "Unknown error"}`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function H(e, t) {
|
|
120
|
+
return new Promise((n, r) => {
|
|
121
|
+
e.webContents.print(t, (i, o) => {
|
|
122
|
+
o ? r(new Error(o)) : n({ success: i }), e.close();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
async function T(e, t) {
|
|
127
|
+
x(e, t);
|
|
128
|
+
const n = E(t.pageSize), r = new p({
|
|
129
|
+
...n,
|
|
130
|
+
show: !!t.preview,
|
|
131
|
+
webPreferences: {
|
|
132
|
+
nodeIntegration: !1,
|
|
133
|
+
contextIsolation: !0,
|
|
134
|
+
sandbox: !1,
|
|
135
|
+
preload: d(import.meta.dirname, "preload/preload.cjs")
|
|
136
|
+
}
|
|
137
|
+
}), i = t.pathTemplate || d(import.meta.dirname, "renderer/index.html");
|
|
138
|
+
try {
|
|
139
|
+
if (await r.loadFile(i), await h("body-init", r.webContents, t), await C(r, e), t.preview)
|
|
140
|
+
return { complete: !0, data: e, options: t };
|
|
141
|
+
const o = await v(r.webContents, t.pageSize), a = M(t, o), c = H(r, a), m = new AbortController(), u = (t.timeOutPerLine ?? 400) * e.length + 200, { success: f } = t.silent ? await c : await I(c, u, m.signal);
|
|
142
|
+
return m.abort(), { complete: f, options: t };
|
|
143
|
+
} catch (o) {
|
|
144
|
+
throw r.close(), o;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const A = {
|
|
148
|
+
print: T,
|
|
149
|
+
openCashDrawer: P,
|
|
150
|
+
getPrinters: g
|
|
151
|
+
};
|
|
152
|
+
export {
|
|
153
|
+
D as PrinterErrorCodes,
|
|
154
|
+
R as PrinterStatus,
|
|
155
|
+
k as PrinterType,
|
|
156
|
+
A as printer
|
|
157
|
+
};
|
package/dist/preload/preload.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";const t=require("electron"),a=require("node:fs"),d=require("node:path");function i(e){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const n in e)if(n!=="default"){const s=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(r,n,s.get?s:{enumerable:!0,get:()=>e[n]})}}return r.default=e,Object.freeze(r)}const o=i(a),c=i(d),l={onBodyInit:e=>{t.ipcRenderer.once("body-init",(r,n)=>{e(n)})},onRenderLine:e=>{t.ipcRenderer.on("render-line",(r,n)=>{e(n)})},sendBodyInitReply:e=>{t.ipcRenderer.send("body-init-reply",e)},sendRenderLineReply:e=>{t.ipcRenderer.send("render-line-reply",e)},readFileAsBase64:e=>{try{const r=c.resolve(e),n=process.cwd();return!r.startsWith(n+c.sep)&&r!==n?{success:!1,error:"Access denied: Path outside allowed directory"}:o.existsSync(r)?{success:!0,data:o.readFileSync(r).toString("base64")}:{success:!1,error:"File not found"}}catch(r){return{success:!1,error:r.message}}},getFileExtension:e=>c.extname(e).slice(1)};t.contextBridge.exposeInMainWorld("electronAPI",l);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@page{margin:0;page-break-before:always}body{font-size:12px}.barcode-container:after{content:"";display:table;clear:both}.font{font-size:12px;margin:0 15px}table{width:100%;display:table;border-collapse:collapse;border-spacing:0;font-family:monospace,cursive}table tbody{border-top:1px solid #999}table th,table td{padding:7px 2px}table tr{border-bottom:1px dotted #999;padding:5px 0;text-align:center}table img{max-height:40px}
|