boxpdf-html
Advanced tools
+24
-2
@@ -17,3 +17,18 @@ #!/usr/bin/env node | ||
| import { PDFDocument } from "pdf-lib"; | ||
| import { renderFlow } from "boxpdf"; | ||
| import { renderFlow, savePdf } from "boxpdf"; | ||
| // src/password-env.ts | ||
| function passwordFromEnvironment(name, environment = process.env) { | ||
| if (name === void 0) return void 0; | ||
| const password = environment[name]; | ||
| if (password === void 0) { | ||
| throw new Error(`environment variable "${name}" named by --password-env is not set`); | ||
| } | ||
| if (password.length === 0) { | ||
| throw new Error(`environment variable "${name}" named by --password-env is empty`); | ||
| } | ||
| return password; | ||
| } | ||
| // src/cli.ts | ||
| var help = `boxpdf-html | ||
@@ -40,2 +55,3 @@ | ||
| --profile Print render phase timings. | ||
| --password-env <name> Encrypt with the password stored in this environment variable. | ||
| -h, --help Show this help. | ||
@@ -46,2 +62,3 @@ | ||
| boxpdf-html invoice.html invoice.pdf --css dist/tailwind.css | ||
| BOXPDF_PASSWORD='open me' boxpdf-html invoice.html invoice.pdf --password-env BOXPDF_PASSWORD | ||
| boxpdf-html invoice.html invoice.pdf --font ./Inter.ttf --bold-font ./Inter-Bold.ttf | ||
@@ -67,2 +84,3 @@ boxpdf-html invoice.html invoice.pdf \\ | ||
| } | ||
| const password = passwordFromEnvironment(options.passwordEnv); | ||
| const inputPath = options.input === "-" ? void 0 : resolve(options.input); | ||
@@ -91,3 +109,4 @@ const baseUrl = options.baseUrl ? resolve(options.baseUrl) : inputPath ? dirname(inputPath) : process.cwd(); | ||
| await renderFlow(pdf, result.nodes, { margin: options.margin, debug: options.debug }); | ||
| writeFileSync(resolve(options.output), await pdf.save()); | ||
| const bytes = password === void 0 ? await pdf.save() : await savePdf(pdf, { encryption: { password } }); | ||
| writeFileSync(resolve(options.output), bytes); | ||
| } | ||
@@ -155,2 +174,5 @@ function parseArgs(args) { | ||
| break; | ||
| case "--password-env": | ||
| options.passwordEnv = next(); | ||
| break; | ||
| default: | ||
@@ -157,0 +179,0 @@ fail(`unknown option "${arg}"`); |
+1
-1
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { PDFDocument } from \"pdf-lib\";\nimport { renderFlow } from \"boxpdf\";\nimport { fontFamily, htmlToBoxpdf } from \"./index.js\";\nimport { injectCss, loadFaces, loadImages, resolveAssetUrl } from \"./render-file.js\";\n\ninterface CliOptions {\n input?: string;\n output?: string;\n css: string[];\n baseUrl?: string;\n font?: string;\n boldFont?: string;\n italicFont?: string;\n boldItalicFont?: string;\n families: string[];\n width?: number;\n margin: number;\n debug: boolean;\n unsupportedCss: boolean;\n profile: boolean;\n}\n\nconst help = `boxpdf-html\n\nUsage:\n boxpdf-html <input.html> <output.pdf> [options]\n boxpdf-html - <output.pdf> [options]\n boxpdf-html mcp # start the MCP server (stdio)\n\nOptions:\n --css <file> Inject an extra stylesheet before rendering. Repeatable.\n --base-url <dir-or-url> Base path for relative images and background URLs.\n --font <file> Default normal TTF/OTF font.\n --bold-font <file> Default bold TTF/OTF font.\n --italic-font <file> Default italic TTF/OTF font.\n --bold-italic-font <file> Default bold italic TTF/OTF font.\n --font-family <mapping> Map a CSS family to loaded font files. Repeatable.\n Example: Inter=normal:Inter.ttf,bold:Inter-Bold.ttf\n --width <pt> CSS containing block width in PDF points.\n --margin <pt> Page margin for renderFlow. Default: 40.\n --debug Draw boxpdf debug overlays.\n --unsupported-css Print aggregated unsupported CSS diagnostics.\n --profile Print render phase timings.\n -h, --help Show this help.\n\nExamples:\n boxpdf-html invoice.html invoice.pdf\n boxpdf-html invoice.html invoice.pdf --css dist/tailwind.css\n boxpdf-html invoice.html invoice.pdf --font ./Inter.ttf --bold-font ./Inter-Bold.ttf\n boxpdf-html invoice.html invoice.pdf \\\\\n --font-family 'Inter=normal:Inter.ttf,bold:Inter-Bold.ttf,italic:Inter-Italic.ttf'\n`;\n\nmain().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`boxpdf-html: ${message}`);\n process.exit(1);\n});\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n if (argv[0] === \"mcp\") {\n const { startMcpServer } = await import(\"./mcp.js\");\n startMcpServer();\n return;\n }\n\n const options = parseArgs(argv);\n if (!options.input || !options.output) {\n printHelpAndExit(options.input || options.output ? 1 : 0);\n }\n\n const inputPath = options.input === \"-\" ? undefined : resolve(options.input);\n const baseUrl = options.baseUrl ? resolve(options.baseUrl) : inputPath ? dirname(inputPath) : process.cwd();\n const html = injectCss(readInput(options.input), options.css.map((path) => readFileSync(resolve(path), \"utf8\")));\n const pdf = await PDFDocument.create();\n const faces = await loadFaces(pdf, options, baseUrl);\n const images = await loadImages(pdf, html, baseUrl, {\n allowRemote: true,\n onWarn: (message) => console.warn(`boxpdf-html: ${message}`)\n });\n\n const result = htmlToBoxpdf(html, {\n font: faces.normal,\n boldFont: faces.bold,\n italicFont: faces.italic,\n resolveFont: fontFamily(faces.families),\n resolveImage: ({ url }) => images.get(resolveAssetUrl(url, baseUrl)),\n baseUrl,\n width: options.width ?? Math.max(0, 612 - options.margin * 2),\n diagnostics: options.unsupportedCss ? { unsupportedCss: true, sampleLimit: 5 } : undefined,\n profile: options.profile ? (event) => console.error(`[profile] ${event.phase} ${event.elapsedMs.toFixed(1)}ms`) : undefined\n });\n\n for (const warning of result.warnings) console.warn(`boxpdf-html: ${warning}`);\n if (options.unsupportedCss) printUnsupportedCss(result.diagnostics?.unsupportedCss ?? []);\n\n await renderFlow(pdf, result.nodes, { margin: options.margin, debug: options.debug });\n writeFileSync(resolve(options.output), await pdf.save());\n}\n\nfunction parseArgs(args: string[]): CliOptions {\n const options: CliOptions = {\n css: [],\n families: [],\n margin: 40,\n debug: false,\n unsupportedCss: false,\n profile: false\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i]!;\n if (arg === \"-h\" || arg === \"--help\" || arg === \"help\") printHelpAndExit(0);\n if (!arg.startsWith(\"-\") || arg === \"-\") {\n if (!options.input) options.input = arg;\n else if (!options.output) options.output = arg;\n else fail(`unexpected argument \"${arg}\"`);\n continue;\n }\n\n const next = (): string => {\n const value = args[i + 1];\n if (!value) fail(`${arg} requires a value`);\n i += 1;\n return value;\n };\n\n switch (arg) {\n case \"--css\":\n options.css.push(next());\n break;\n case \"--base-url\":\n options.baseUrl = next();\n break;\n case \"--font\":\n options.font = next();\n break;\n case \"--bold-font\":\n options.boldFont = next();\n break;\n case \"--italic-font\":\n options.italicFont = next();\n break;\n case \"--bold-italic-font\":\n options.boldItalicFont = next();\n break;\n case \"--font-family\":\n options.families.push(next());\n break;\n case \"--width\":\n options.width = parseNumber(next(), arg);\n break;\n case \"--margin\":\n options.margin = parseNumber(next(), arg);\n break;\n case \"--debug\":\n options.debug = true;\n break;\n case \"--unsupported-css\":\n options.unsupportedCss = true;\n break;\n case \"--profile\":\n options.profile = true;\n break;\n default:\n fail(`unknown option \"${arg}\"`);\n }\n }\n\n return options;\n}\n\nfunction readInput(input: string): string {\n if (input === \"-\") return readFileSync(0, \"utf8\");\n return readFileSync(resolve(input), \"utf8\");\n}\n\nfunction printUnsupportedCss(items: Array<{ property: string; value: string; count: number; samples?: string[] }>): void {\n if (items.length === 0) return;\n console.error(\"Unsupported CSS:\");\n for (const item of items) {\n console.error(`- ${item.property}: ${item.value} (${item.count})`);\n for (const sample of item.samples ?? []) console.error(` ${sample}`);\n }\n}\n\nfunction parseNumber(value: string, option: string): number {\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed < 0) fail(`${option} must be a non-negative number`);\n return parsed;\n}\n\nfunction printHelpAndExit(code: number): never {\n console.log(help);\n process.exit(code);\n}\n\nfunction fail(message: string): never {\n console.error(`boxpdf-html: ${message}`);\n process.exit(1);\n}\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../src/cli.ts","../src/password-env.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { PDFDocument } from \"pdf-lib\";\nimport { renderFlow, savePdf } from \"boxpdf\";\nimport { fontFamily, htmlToBoxpdf } from \"./index.js\";\nimport { passwordFromEnvironment } from \"./password-env.js\";\nimport { injectCss, loadFaces, loadImages, resolveAssetUrl } from \"./render-file.js\";\n\ninterface CliOptions {\n input?: string;\n output?: string;\n css: string[];\n baseUrl?: string;\n font?: string;\n boldFont?: string;\n italicFont?: string;\n boldItalicFont?: string;\n families: string[];\n width?: number;\n margin: number;\n debug: boolean;\n unsupportedCss: boolean;\n profile: boolean;\n passwordEnv?: string;\n}\n\nconst help = `boxpdf-html\n\nUsage:\n boxpdf-html <input.html> <output.pdf> [options]\n boxpdf-html - <output.pdf> [options]\n boxpdf-html mcp # start the MCP server (stdio)\n\nOptions:\n --css <file> Inject an extra stylesheet before rendering. Repeatable.\n --base-url <dir-or-url> Base path for relative images and background URLs.\n --font <file> Default normal TTF/OTF font.\n --bold-font <file> Default bold TTF/OTF font.\n --italic-font <file> Default italic TTF/OTF font.\n --bold-italic-font <file> Default bold italic TTF/OTF font.\n --font-family <mapping> Map a CSS family to loaded font files. Repeatable.\n Example: Inter=normal:Inter.ttf,bold:Inter-Bold.ttf\n --width <pt> CSS containing block width in PDF points.\n --margin <pt> Page margin for renderFlow. Default: 40.\n --debug Draw boxpdf debug overlays.\n --unsupported-css Print aggregated unsupported CSS diagnostics.\n --profile Print render phase timings.\n --password-env <name> Encrypt with the password stored in this environment variable.\n -h, --help Show this help.\n\nExamples:\n boxpdf-html invoice.html invoice.pdf\n boxpdf-html invoice.html invoice.pdf --css dist/tailwind.css\n BOXPDF_PASSWORD='open me' boxpdf-html invoice.html invoice.pdf --password-env BOXPDF_PASSWORD\n boxpdf-html invoice.html invoice.pdf --font ./Inter.ttf --bold-font ./Inter-Bold.ttf\n boxpdf-html invoice.html invoice.pdf \\\\\n --font-family 'Inter=normal:Inter.ttf,bold:Inter-Bold.ttf,italic:Inter-Italic.ttf'\n`;\n\nmain().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`boxpdf-html: ${message}`);\n process.exit(1);\n});\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n if (argv[0] === \"mcp\") {\n const { startMcpServer } = await import(\"./mcp.js\");\n startMcpServer();\n return;\n }\n\n const options = parseArgs(argv);\n if (!options.input || !options.output) {\n printHelpAndExit(options.input || options.output ? 1 : 0);\n }\n const password = passwordFromEnvironment(options.passwordEnv);\n\n const inputPath = options.input === \"-\" ? undefined : resolve(options.input);\n const baseUrl = options.baseUrl ? resolve(options.baseUrl) : inputPath ? dirname(inputPath) : process.cwd();\n const html = injectCss(readInput(options.input), options.css.map((path) => readFileSync(resolve(path), \"utf8\")));\n const pdf = await PDFDocument.create();\n const faces = await loadFaces(pdf, options, baseUrl);\n const images = await loadImages(pdf, html, baseUrl, {\n allowRemote: true,\n onWarn: (message) => console.warn(`boxpdf-html: ${message}`)\n });\n\n const result = htmlToBoxpdf(html, {\n font: faces.normal,\n boldFont: faces.bold,\n italicFont: faces.italic,\n resolveFont: fontFamily(faces.families),\n resolveImage: ({ url }) => images.get(resolveAssetUrl(url, baseUrl)),\n baseUrl,\n width: options.width ?? Math.max(0, 612 - options.margin * 2),\n diagnostics: options.unsupportedCss ? { unsupportedCss: true, sampleLimit: 5 } : undefined,\n profile: options.profile ? (event) => console.error(`[profile] ${event.phase} ${event.elapsedMs.toFixed(1)}ms`) : undefined\n });\n\n for (const warning of result.warnings) console.warn(`boxpdf-html: ${warning}`);\n if (options.unsupportedCss) printUnsupportedCss(result.diagnostics?.unsupportedCss ?? []);\n\n await renderFlow(pdf, result.nodes, { margin: options.margin, debug: options.debug });\n const bytes = password === undefined\n ? await pdf.save()\n : await savePdf(pdf, { encryption: { password } });\n writeFileSync(resolve(options.output), bytes);\n}\n\nfunction parseArgs(args: string[]): CliOptions {\n const options: CliOptions = {\n css: [],\n families: [],\n margin: 40,\n debug: false,\n unsupportedCss: false,\n profile: false\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i]!;\n if (arg === \"-h\" || arg === \"--help\" || arg === \"help\") printHelpAndExit(0);\n if (!arg.startsWith(\"-\") || arg === \"-\") {\n if (!options.input) options.input = arg;\n else if (!options.output) options.output = arg;\n else fail(`unexpected argument \"${arg}\"`);\n continue;\n }\n\n const next = (): string => {\n const value = args[i + 1];\n if (!value) fail(`${arg} requires a value`);\n i += 1;\n return value;\n };\n\n switch (arg) {\n case \"--css\":\n options.css.push(next());\n break;\n case \"--base-url\":\n options.baseUrl = next();\n break;\n case \"--font\":\n options.font = next();\n break;\n case \"--bold-font\":\n options.boldFont = next();\n break;\n case \"--italic-font\":\n options.italicFont = next();\n break;\n case \"--bold-italic-font\":\n options.boldItalicFont = next();\n break;\n case \"--font-family\":\n options.families.push(next());\n break;\n case \"--width\":\n options.width = parseNumber(next(), arg);\n break;\n case \"--margin\":\n options.margin = parseNumber(next(), arg);\n break;\n case \"--debug\":\n options.debug = true;\n break;\n case \"--unsupported-css\":\n options.unsupportedCss = true;\n break;\n case \"--profile\":\n options.profile = true;\n break;\n case \"--password-env\":\n options.passwordEnv = next();\n break;\n default:\n fail(`unknown option \"${arg}\"`);\n }\n }\n\n return options;\n}\n\nfunction readInput(input: string): string {\n if (input === \"-\") return readFileSync(0, \"utf8\");\n return readFileSync(resolve(input), \"utf8\");\n}\n\nfunction printUnsupportedCss(items: Array<{ property: string; value: string; count: number; samples?: string[] }>): void {\n if (items.length === 0) return;\n console.error(\"Unsupported CSS:\");\n for (const item of items) {\n console.error(`- ${item.property}: ${item.value} (${item.count})`);\n for (const sample of item.samples ?? []) console.error(` ${sample}`);\n }\n}\n\nfunction parseNumber(value: string, option: string): number {\n const parsed = Number(value);\n if (!Number.isFinite(parsed) || parsed < 0) fail(`${option} must be a non-negative number`);\n return parsed;\n}\n\nfunction printHelpAndExit(code: number): never {\n console.log(help);\n process.exit(code);\n}\n\nfunction fail(message: string): never {\n console.error(`boxpdf-html: ${message}`);\n process.exit(1);\n}\n","export function passwordFromEnvironment(\n name: string | undefined,\n environment: NodeJS.ProcessEnv = process.env\n): string | undefined {\n if (name === undefined) return undefined;\n const password = environment[name];\n if (password === undefined) {\n throw new Error(`environment variable \"${name}\" named by --password-env is not set`);\n }\n if (password.length === 0) {\n throw new Error(`environment variable \"${name}\" named by --password-env is empty`);\n }\n return password;\n}\n"],"mappings":";;;;;;;;;;;;;AAEA,SAAS,cAAc,qBAAqB;AAC5C,SAAS,SAAS,eAAe;AACjC,SAAS,mBAAmB;AAC5B,SAAS,YAAY,eAAe;;;ACL7B,SAAS,wBACd,MACA,cAAiC,QAAQ,KACrB;AACpB,MAAI,SAAS,OAAW,QAAO;AAC/B,QAAM,WAAW,YAAY,IAAI;AACjC,MAAI,aAAa,QAAW;AAC1B,UAAM,IAAI,MAAM,yBAAyB,IAAI,sCAAsC;AAAA,EACrF;AACA,MAAI,SAAS,WAAW,GAAG;AACzB,UAAM,IAAI,MAAM,yBAAyB,IAAI,oCAAoC;AAAA,EACnF;AACA,SAAO;AACT;;;ADeA,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiCb,KAAK,EAAE,MAAM,CAAC,UAAmB;AAC/B,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAQ,MAAM,gBAAgB,OAAO,EAAE;AACvC,UAAQ,KAAK,CAAC;AAChB,CAAC;AAED,eAAe,OAAsB;AACnC,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,MAAI,KAAK,CAAC,MAAM,OAAO;AACrB,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,mBAAU;AAClD,mBAAe;AACf;AAAA,EACF;AAEA,QAAM,UAAU,UAAU,IAAI;AAC9B,MAAI,CAAC,QAAQ,SAAS,CAAC,QAAQ,QAAQ;AACrC,qBAAiB,QAAQ,SAAS,QAAQ,SAAS,IAAI,CAAC;AAAA,EAC1D;AACA,QAAM,WAAW,wBAAwB,QAAQ,WAAW;AAE5D,QAAM,YAAY,QAAQ,UAAU,MAAM,SAAY,QAAQ,QAAQ,KAAK;AAC3E,QAAM,UAAU,QAAQ,UAAU,QAAQ,QAAQ,OAAO,IAAI,YAAY,QAAQ,SAAS,IAAI,QAAQ,IAAI;AAC1G,QAAM,OAAO,UAAU,UAAU,QAAQ,KAAK,GAAG,QAAQ,IAAI,IAAI,CAAC,SAAS,aAAa,QAAQ,IAAI,GAAG,MAAM,CAAC,CAAC;AAC/G,QAAM,MAAM,MAAM,YAAY,OAAO;AACrC,QAAM,QAAQ,MAAM,UAAU,KAAK,SAAS,OAAO;AACnD,QAAM,SAAS,MAAM,WAAW,KAAK,MAAM,SAAS;AAAA,IAClD,aAAa;AAAA,IACb,QAAQ,CAAC,YAAY,QAAQ,KAAK,gBAAgB,OAAO,EAAE;AAAA,EAC7D,CAAC;AAED,QAAM,SAAS,aAAa,MAAM;AAAA,IAChC,MAAM,MAAM;AAAA,IACZ,UAAU,MAAM;AAAA,IAChB,YAAY,MAAM;AAAA,IAClB,aAAa,WAAW,MAAM,QAAQ;AAAA,IACtC,cAAc,CAAC,EAAE,IAAI,MAAM,OAAO,IAAI,gBAAgB,KAAK,OAAO,CAAC;AAAA,IACnE;AAAA,IACA,OAAO,QAAQ,SAAS,KAAK,IAAI,GAAG,MAAM,QAAQ,SAAS,CAAC;AAAA,IAC5D,aAAa,QAAQ,iBAAiB,EAAE,gBAAgB,MAAM,aAAa,EAAE,IAAI;AAAA,IACjF,SAAS,QAAQ,UAAU,CAAC,UAAU,QAAQ,MAAM,aAAa,MAAM,KAAK,IAAI,MAAM,UAAU,QAAQ,CAAC,CAAC,IAAI,IAAI;AAAA,EACpH,CAAC;AAED,aAAW,WAAW,OAAO,SAAU,SAAQ,KAAK,gBAAgB,OAAO,EAAE;AAC7E,MAAI,QAAQ,eAAgB,qBAAoB,OAAO,aAAa,kBAAkB,CAAC,CAAC;AAExF,QAAM,WAAW,KAAK,OAAO,OAAO,EAAE,QAAQ,QAAQ,QAAQ,OAAO,QAAQ,MAAM,CAAC;AACpF,QAAM,QAAQ,aAAa,SACvB,MAAM,IAAI,KAAK,IACf,MAAM,QAAQ,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;AACnD,gBAAc,QAAQ,QAAQ,MAAM,GAAG,KAAK;AAC9C;AAEA,SAAS,UAAU,MAA4B;AAC7C,QAAM,UAAsB;AAAA,IAC1B,KAAK,CAAC;AAAA,IACN,UAAU,CAAC;AAAA,IACX,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,gBAAgB;AAAA,IAChB,SAAS;AAAA,EACX;AAEA,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,QAAQ,QAAQ,QAAQ,YAAY,QAAQ,OAAQ,kBAAiB,CAAC;AAC1E,QAAI,CAAC,IAAI,WAAW,GAAG,KAAK,QAAQ,KAAK;AACvC,UAAI,CAAC,QAAQ,MAAO,SAAQ,QAAQ;AAAA,eAC3B,CAAC,QAAQ,OAAQ,SAAQ,SAAS;AAAA,UACtC,MAAK,wBAAwB,GAAG,GAAG;AACxC;AAAA,IACF;AAEA,UAAM,OAAO,MAAc;AACzB,YAAM,QAAQ,KAAK,IAAI,CAAC;AACxB,UAAI,CAAC,MAAO,MAAK,GAAG,GAAG,mBAAmB;AAC1C,WAAK;AACL,aAAO;AAAA,IACT;AAEA,YAAQ,KAAK;AAAA,MACX,KAAK;AACH,gBAAQ,IAAI,KAAK,KAAK,CAAC;AACvB;AAAA,MACF,KAAK;AACH,gBAAQ,UAAU,KAAK;AACvB;AAAA,MACF,KAAK;AACH,gBAAQ,OAAO,KAAK;AACpB;AAAA,MACF,KAAK;AACH,gBAAQ,WAAW,KAAK;AACxB;AAAA,MACF,KAAK;AACH,gBAAQ,aAAa,KAAK;AAC1B;AAAA,MACF,KAAK;AACH,gBAAQ,iBAAiB,KAAK;AAC9B;AAAA,MACF,KAAK;AACH,gBAAQ,SAAS,KAAK,KAAK,CAAC;AAC5B;AAAA,MACF,KAAK;AACH,gBAAQ,QAAQ,YAAY,KAAK,GAAG,GAAG;AACvC;AAAA,MACF,KAAK;AACH,gBAAQ,SAAS,YAAY,KAAK,GAAG,GAAG;AACxC;AAAA,MACF,KAAK;AACH,gBAAQ,QAAQ;AAChB;AAAA,MACF,KAAK;AACH,gBAAQ,iBAAiB;AACzB;AAAA,MACF,KAAK;AACH,gBAAQ,UAAU;AAClB;AAAA,MACF,KAAK;AACH,gBAAQ,cAAc,KAAK;AAC3B;AAAA,MACF;AACE,aAAK,mBAAmB,GAAG,GAAG;AAAA,IAClC;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,UAAU,OAAuB;AACxC,MAAI,UAAU,IAAK,QAAO,aAAa,GAAG,MAAM;AAChD,SAAO,aAAa,QAAQ,KAAK,GAAG,MAAM;AAC5C;AAEA,SAAS,oBAAoB,OAA4F;AACvH,MAAI,MAAM,WAAW,EAAG;AACxB,UAAQ,MAAM,kBAAkB;AAChC,aAAW,QAAQ,OAAO;AACxB,YAAQ,MAAM,KAAK,KAAK,QAAQ,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;AACjE,eAAW,UAAU,KAAK,WAAW,CAAC,EAAG,SAAQ,MAAM,KAAK,MAAM,EAAE;AAAA,EACtE;AACF;AAEA,SAAS,YAAY,OAAe,QAAwB;AAC1D,QAAM,SAAS,OAAO,KAAK;AAC3B,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,EAAG,MAAK,GAAG,MAAM,gCAAgC;AAC1F,SAAO;AACT;AAEA,SAAS,iBAAiB,MAAqB;AAC7C,UAAQ,IAAI,IAAI;AAChB,UAAQ,KAAK,IAAI;AACnB;AAEA,SAAS,KAAK,SAAwB;AACpC,UAAQ,MAAM,gBAAgB,OAAO,EAAE;AACvC,UAAQ,KAAK,CAAC;AAChB;","names":[]} |
+3
-3
| { | ||
| "name": "boxpdf-html", | ||
| "version": "1.4.1", | ||
| "version": "1.5.0", | ||
| "mcpName": "io.github.earonesty/boxpdf-html", | ||
@@ -46,3 +46,3 @@ "description": "Readable HTML-to-PDF translator built on boxpdf.", | ||
| "dependencies": { | ||
| "boxpdf": "^1.10.0", | ||
| "boxpdf": "^1.11.0", | ||
| "css-tree": "^3.1.0", | ||
@@ -62,3 +62,3 @@ "parse5": "^8.0.0", | ||
| "engines": { | ||
| "node": ">=18" | ||
| "node": ">=20" | ||
| }, | ||
@@ -65,0 +65,0 @@ "publishConfig": { |
+12
-0
@@ -24,2 +24,13 @@ # boxpdf-html | ||
| With PDF 2.0 AES-256 password encryption: | ||
| ```sh | ||
| BOXPDF_PASSWORD='open me' \ | ||
| npx boxpdf-html invoice.html invoice.pdf --password-env BOXPDF_PASSWORD | ||
| ``` | ||
| `--password-env` accepts an environment-variable name. The variable must be | ||
| set to a non-empty password. The password value is never accepted as a command | ||
| argument, which keeps it out of the command line and process listing. | ||
| With custom fonts and local images: | ||
@@ -41,2 +52,3 @@ | ||
| boxpdf-html input.html output.pdf --base-url ./public | ||
| boxpdf-html input.html output.pdf --password-env BOXPDF_PASSWORD | ||
| boxpdf-html input.html output.pdf --debug | ||
@@ -43,0 +55,0 @@ boxpdf-html input.html output.pdf --unsupported-css |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
1262294
0.42%11017
0.36%307
4.07%13
8.33%Updated