diff --git a/package-lock.json b/package-lock.json index 9dbb71f..92540c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15714,22 +15714,19 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", "integrity": "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==", - "dev": true, - "requires": {} + "dev": true }, "@webpack-cli/info": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz", "integrity": "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==", - "dev": true, - "requires": {} + "dev": true }, "@webpack-cli/serve": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz", "integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==", - "dev": true, - "requires": {} + "dev": true }, "@xtuc/ieee754": { "version": "1.2.0", @@ -15777,8 +15774,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} + "dev": true }, "acorn-walk": { "version": "7.2.0", @@ -15850,8 +15846,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "ansi-escapes": { "version": "4.3.2", @@ -18916,8 +18911,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "requires": {} + "dev": true }, "jest-regex-util": { "version": "29.2.0", @@ -21716,8 +21710,7 @@ "version": "7.5.7", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "dev": true, - "requires": {} + "dev": true }, "xml-name-validator": { "version": "3.0.0", diff --git a/src/get-default-printer/get-default-printer.spec.ts b/src/get-default-printer/get-default-printer.spec.ts index f10c6a7..e356c65 100644 --- a/src/get-default-printer/get-default-printer.spec.ts +++ b/src/get-default-printer/get-default-printer.spec.ts @@ -1,6 +1,7 @@ import { mocked } from "jest-mock"; -import getDefaultPrinter, { Printer } from "./get-default-printer"; +import getDefaultPrinter from "./get-default-printer"; import execAsync from "../utils/exec-file-async"; +import { Printer } from ".."; jest.mock("../utils/throw-if-unsupported-os"); jest.mock("../utils/exec-file-async"); @@ -26,11 +27,12 @@ it("gets the default printer", async () => { stderr: "", }); - const result: Printer = await getDefaultPrinter(); + const result: Printer | null = await getDefaultPrinter(); expect(result).toStrictEqual({ deviceId: "Microsoft Print to PDF", name: "Microsoft Print to PDF", + paperSizes: [], }); }); @@ -62,3 +64,29 @@ it("throws when execAsync fails", () => { mockedExecAsync.mockRejectedValue("error"); return expect(getDefaultPrinter()).rejects.toBe("error"); }); + +it("gets the default printer with custom and repeated properties", async () => { + const stdout = ` + + Name : Microsoft Print to PDF + Caption : Microsoft Print to PDF + DeviceID : Microsoft Print to PDF + PaperSizesSupported : {1, 1, 1, 1...} + PortName : USB001 + PrinterPaperNames : {A4, 144mm x 100mm} + + `; + + mockedExecAsync.mockResolvedValue({ + stdout, + stderr: "", + }); + + const result: Printer | null = await getDefaultPrinter(); + + expect(result).toStrictEqual({ + deviceId: "Microsoft Print to PDF", + name: "Microsoft Print to PDF", + paperSizes: ["A4", "144mm x 100mm"], + }); +}); diff --git a/src/get-default-printer/get-default-printer.ts b/src/get-default-printer/get-default-printer.ts index b4e1496..c138383 100644 --- a/src/get-default-printer/get-default-printer.ts +++ b/src/get-default-printer/get-default-printer.ts @@ -1,11 +1,7 @@ import execFileAsync from "../utils/exec-file-async"; import throwIfUnsupportedOperatingSystem from "../utils/throw-if-unsupported-os"; import isValidPrinter from "../utils/windows-printer-valid"; - -export interface Printer { - deviceId: string; - name: string; -} +import { Printer } from ".."; async function getDefaultPrinter(): Promise { try { @@ -13,7 +9,7 @@ async function getDefaultPrinter(): Promise { const { stdout } = await execFileAsync("Powershell.exe", [ "-Command", - "Get-CimInstance Win32_Printer -Property DeviceID,Name -Filter Default=true", + `Get-CimInstance Win32_Printer -Property DeviceID,Name,PrinterPaperNames -Filter Default=true`, ]); const printer = stdout.trim(); diff --git a/src/get-printers/get-printers.spec.ts b/src/get-printers/get-printers.spec.ts index e02d398..cd88c06 100644 --- a/src/get-printers/get-printers.spec.ts +++ b/src/get-printers/get-printers.spec.ts @@ -1,5 +1,5 @@ import { mocked } from "jest-mock"; -import { Printer } from "../get-default-printer/get-default-printer"; +import { Printer } from ".."; import execAsync from "../utils/exec-file-async"; import getPrinters from "./get-printers"; @@ -70,18 +70,21 @@ it("returns list of available printers", async () => { const result: Printer[] = await getPrinters(); expect(result).toStrictEqual([ - { deviceId: "OneNote", name: "OneNote" }, + { deviceId: "OneNote", name: "OneNote", paperSizes: [] }, { deviceId: "Microsoft-XPS-Document-Writer", name: "Microsoft XPS Document Writer", + paperSizes: [], }, { deviceId: "Microsoft_Print_to_PDF", name: "Microsoft Print to PDF", + paperSizes: [], }, { deviceId: "Fax", name: "Fax", + paperSizes: [], }, ]); }); @@ -106,3 +109,32 @@ it("fails with an error", () => { mockedExecAsync.mockRejectedValue("error"); return expect(getPrinters()).rejects.toBe("error"); }); + +it("returns list of available printers with custom properties", async () => { + const stdout = ` + + Status : Unknown + Name : Canon Printer + Caption : Canon Printer + DeviceID : Canon-Printer + PaperSizesSupported : {1, 1, 1, 1...} + PortName : USB001 + PrinterPaperNames : {A4, 144mm x 100mm, 2 x 4, 4 x 4...} + + `; + + mockedExecAsync.mockResolvedValue({ + stdout, + stderr: "", + }); + + const result: Printer[] = await getPrinters(); + + expect(result).toStrictEqual([ + { + deviceId: "Canon-Printer", + name: "Canon Printer", + paperSizes: ["A4", "144mm x 100mm", "2 x 4", "4 x 4"], + }, + ]); +}); diff --git a/src/get-printers/get-printers.ts b/src/get-printers/get-printers.ts index 0c083f3..d063fe5 100644 --- a/src/get-printers/get-printers.ts +++ b/src/get-printers/get-printers.ts @@ -1,7 +1,7 @@ import execFileAsync from "../utils/exec-file-async"; import isValidPrinter from "../utils/windows-printer-valid"; import throwIfUnsupportedOperatingSystem from "../utils/throw-if-unsupported-os"; -import { Printer } from "../get-default-printer/get-default-printer"; +import { Printer } from ".."; async function getPrinters(): Promise { function stdoutHandler(stdout: string) { @@ -26,7 +26,7 @@ async function getPrinters(): Promise { throwIfUnsupportedOperatingSystem(); const { stdout } = await execFileAsync("Powershell.exe", [ "-Command", - "Get-CimInstance Win32_Printer -Property DeviceID,Name", + `Get-CimInstance Win32_Printer -Property DeviceID,Name,PrinterPaperNames`, ]); return stdoutHandler(stdout); } catch (error) { diff --git a/src/index.ts b/src/index.ts index 60fb236..ef8fbd0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,4 +2,9 @@ export { default as print } from "./print/print"; export { default as getPrinters } from "./get-printers/get-printers"; export { default as getDefaultPrinter } from "./get-default-printer/get-default-printer"; export { PrintOptions } from "./print/print"; -export { Printer } from "./get-default-printer/get-default-printer"; + +export type Printer = { + deviceId: string; + name: string; + paperSizes: string[]; +}; diff --git a/src/utils/windows-printer-valid.ts b/src/utils/windows-printer-valid.ts index 5d2c2fd..4d3da18 100644 --- a/src/utils/windows-printer-valid.ts +++ b/src/utils/windows-printer-valid.ts @@ -1,4 +1,11 @@ -import { Printer } from "../get-default-printer/get-default-printer"; +import { Printer } from "../index"; + +// map windows-printer key to final printerData key +const properties: { [key: string]: keyof Printer } = { + DeviceID: "deviceId", + Name: "name", + PrinterPaperNames: "paperSizes", +}; export default function isValidPrinter(printer: string): { isValid: boolean; @@ -7,22 +14,35 @@ export default function isValidPrinter(printer: string): { const printerData: Printer = { deviceId: "", name: "", + paperSizes: [], }; - const isValid = printer.split(/\r?\n/).some((line) => { - const [label, value] = line.split(":").map((el) => el.trim()); + printer.split(/\r?\n/).forEach((line) => { + let [label, value] = line.split(":").map((el) => el.trim()); - const lowerLabel = label.toLowerCase(); + // handle array dots + if (value.match(/^{(.*)(\.{3})}$/)) { + value = value.replace("...}", "}"); + } - // @ts-ignore - if (lowerLabel === "deviceid") printerData.deviceId = value; + // handle array returns + const matches = value.match(/^{(.*)}$/); - // @ts-ignore - if (lowerLabel === "name") printerData.name = value; + if (matches && matches[1]) { + // @ts-ignore + value = matches[1].split(", "); + } + + const key = properties[label]; - return !!(printerData.deviceId && printerData.name); + if (key === undefined) return; + + // @ts-ignore + printerData[key] = value; }); + const isValid = !!(printerData.deviceId && printerData.name); + return { isValid, printerData, diff --git a/webpack.production.config.js b/webpack.production.config.js index 522deb9..c549cab 100644 --- a/webpack.production.config.js +++ b/webpack.production.config.js @@ -24,7 +24,7 @@ module.exports = { }, plugins: [ new CleanWebpackPlugin(), - new CopyPlugin({patterns: [{ from: "./src/SumatraPDF-3.4.6-32.exe" }]}), + new CopyPlugin({ patterns: [{ from: "./src/SumatraPDF-3.4.6-32.exe" }] }), ], target: "node", node: {