@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 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
- const { posPrinter } = require('@devraghu/electron-printer');
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
- posPrinter
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 Usage
66
+ ## TypeScript
67
+
68
+ Full TypeScript support with exported types:
67
69
 
68
70
  ```typescript
69
- import { posPrinter, type PosPrintData, type PosPrintOptions, type PrintResult } from '@devraghu/electron-printer';
71
+ import { printer, type PrintData, type PrintOptions, type PrintResult } from '@devraghu/electron-printer';
70
72
 
71
- const options: PosPrintOptions = {
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: PosPrintData[] = [
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
- posPrinter
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 { posPrinter } from '@devraghu/electron-printer';
237
+ import { printer } from '@devraghu/electron-printer';
236
238
 
237
- const printers = posPrinter.getPrinters();
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 { posPrinter } from '@devraghu/electron-printer';
249
+ import { printer } from '@devraghu/electron-printer';
248
250
 
249
251
  // Open cash drawer on specific printer
250
- posPrinter
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
- │ │ ├── pos-printer.ts # posPrinter exports
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
- │ │ └── index.html # Print template
273
+ │ │ ├── index.html # Print template
274
+ │ │ └── index.css # Print styles
272
275
  │ └── preload/
273
- │ └── preload.ts # Preload script with API types
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
- import { DrawerOptions, OpenCashDrawerResult, PrinterErrorCodes, PrinterInfo, PrinterStatus, PrinterType, getAvailablePrinters, openCashDrawer } from '@devraghu/cashdrawer';
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{fileURLToPath as e}from"node:url";var r=e(import.meta.url.replace(/\/(?:[^\/]*)$/,""));import{BrowserWindow as t,ipcMain as o}from"electron";import{join as n}from"node:path";import{PrinterErrorCodes as a,PrinterStatus as i,PrinterType as s,getAvailablePrinters as p,openCashDrawer as c}from"@devraghu/cashdrawer";var l={};function d(e,r,t){return new Promise((n,a)=>{o.once(`${e}-reply`,(e,r)=>{r.status?n(r):a(r.error)}),r.send(e,t)})}function h(e){let r=1200,t=219;if("string"==typeof e)switch(e){case"44mm":t=166;break;case"57mm":t=215;break;case"58mm":t=219;break;case"76mm":t=287;break;case"78mm":t=295;break;case"80mm":t=302}else"object"==typeof e&&(t=e.width,r=e.height);return{width:t,height:r}}function m(e){return Math.ceil(264.5833*e)}if(l.d=(e,r)=>{for(var t in r)l.o(r,t)&&!l.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},l.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),"renderer"===process.type)throw new Error("electron-pos-printer: this module must be used in the main process only");const w={print:(e,o)=>new Promise((a,i)=>{o.preview||o.printerName||o.silent||i(new Error("A printer name is required, if you don't want to specify a printer name, set silent to true")),"object"!=typeof o.pageSize||o.pageSize.height&&o.pageSize.width||i(new Error("height and width properties are required for options.pageSize"));let s=!1,p=null;const c=o.timeOutPerLine?o.timeOutPerLine*e.length+200:400*e.length+200;o.preview||o.silent||setTimeout(()=>{if(!s){const e=p||new Error("[TimedOutError] Make sure your printer is connected");i(e),s=!0}},c);let l=new t({...h(o.pageSize),show:!!o.preview,webPreferences:{nodeIntegration:!1,contextIsolation:!0,sandbox:!1,preload:n(r,"preload/preload.cjs")}});l.on("closed",()=>{l=null}),l.loadFile(o.pathTemplate||n(r,"renderer/index.html")),l.webContents.on("did-finish-load",async()=>{if(l)return await d("body-init",l.webContents,o),(async(e,r)=>{for(const[t,o]of r.entries()){if("image"===o.type&&!o.path&&!o.url)throw e.close(),new Error("An Image url/path is required for type image");if(o.style&&"object"!=typeof o.style)throw e.close(),new Error('`options.styles` at "'+o.style+'" should be an object. Example: {style: {fontSize: 12}}');const r=await d("render-line",e.webContents,{line:o,lineIndex:t});if(!r.status)throw e.close(),new Error(`Failed to render line ${t} (type: ${o.type}): ${r.error||"Unknown error"}`)}return{message:"page-rendered"}})(l,e).then(async()=>{if(!l)return void i(new Error("Window was closed during rendering"));let{width:r,height:t}=function(e){let r=1e4,t=58e3;if("string"==typeof e)switch(e){case"44mm":t=Math.ceil(44e3);break;case"57mm":t=Math.ceil(57e3);break;case"58mm":t=Math.ceil(58e3);break;case"76mm":t=Math.ceil(76e3);break;case"78mm":t=Math.ceil(78e3);break;case"80mm":t=Math.ceil(8e4)}else"object"==typeof e&&(t=m(e.width),r=m(e.height));return{width:t,height:r}}(o.pageSize);if("string"==typeof o.pageSize){t=m(await l.webContents.executeJavaScript("document.body.clientHeight"))}o.preview?a({complete:!0,data:e,options:o}):l.webContents.print({silent:!!o.silent,printBackground:!!o.printBackground,deviceName:o.printerName,copies:o?.copies||1,pageSize:{width:r,height:t},...o.header&&{header:o.header},...o.footer&&{footer:o.footer},...o.color&&{color:o.color},...o.printBackground&&{printBackground:o.printBackground},...o.margins&&{margins:o.margins},...o.landscape&&{landscape:o.landscape},...o.scaleFactor&&{scaleFactor:o.scaleFactor},...o.pagesPerSheet&&{pagesPerSheet:o.pagesPerSheet},...o.collate&&{collate:o.collate},...o.pageRanges&&{pageRanges:o.pageRanges},...o.duplexMode&&{duplexMode:o.duplexMode},...o.dpi&&{dpi:o.dpi}},(e,r)=>{r&&(p=new Error(r),i(p)),s||(a({complete:e,options:o}),s=!0),l?.close()})}).catch(e=>i(e));i(new Error("Window was closed before loading completed"))})}),openCashDrawer:c,getPrinters:p};export{a as PrinterErrorCodes,i as PrinterStatus,s as PrinterType,w as posPrinter};
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
+ };
@@ -1 +1 @@
1
- (()=>{"use strict";const e=require("electron"),r=require("node:fs"),n=require("node:path"),s={onBodyInit:r=>{e.ipcRenderer.on("body-init",(e,n)=>{r(n)})},onRenderLine:r=>{e.ipcRenderer.on("render-line",(e,n)=>{r(n)})},sendBodyInitReply:r=>{e.ipcRenderer.send("body-init-reply",r)},sendRenderLineReply:r=>{e.ipcRenderer.send("render-line-reply",r)},readFileAsBase64:e=>{try{const s=n.resolve(e),i=process.cwd();if(!s.startsWith(i+n.sep)&&s!==i)return{success:!1,error:"Access denied: Path outside allowed directory"};if(!r.existsSync(s))return{success:!1,error:"File not found"};return{success:!0,data:r.readFileSync(s).toString("base64")}}catch(e){return{success:!1,error:e.message}}},getFileExtension:e=>n.extname(e).slice(1)};e.contextBridge.exposeInMainWorld("electronAPI",s)})();
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}