-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate-pdf.mjs
More file actions
99 lines (79 loc) · 2.69 KB
/
generate-pdf.mjs
File metadata and controls
99 lines (79 loc) · 2.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/usr/bin/env node
/**
* generate-pdf.mjs — Convert HTML to PDF using Playwright Chromium
*
* Usage:
* node generate-pdf.mjs <input.html> <output.pdf> [--format=letter|a4]
*
* Fonts are loaded from ./fonts/ relative to the input HTML file.
*/
import { chromium } from 'playwright';
import { readFile, stat } from 'node:fs/promises';
import { resolve, dirname } from 'node:path';
const args = process.argv.slice(2);
if (args.length < 2) {
console.error('Usage: node generate-pdf.mjs <input.html> <output.pdf> [--format=letter|a4]');
process.exit(1);
}
const inputPath = resolve(args[0]);
const outputPath = resolve(args[1]);
const formatFlag = args.find(a => a.startsWith('--format='));
const format = formatFlag ? formatFlag.split('=')[1] : 'letter';
const inputDir = dirname(inputPath);
async function main() {
// Read HTML
let html = await readFile(inputPath, 'utf-8');
// Rewrite font paths to absolute file:// URIs
// This ensures Chromium can load self-hosted fonts from any working directory
html = html.replace(
/url\(['"]?\.\/fonts\//g,
`url('file://${inputDir}/fonts/`
);
// Also handle fonts relative to project root
const projectRoot = resolve(import.meta.dirname || dirname(import.meta.url.replace('file://', '')));
html = html.replace(
/url\(['"]?fonts\//g,
`url('file://${projectRoot}/fonts/`
);
// Launch browser
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
// Set content and wait for fonts
await page.setContent(html, { waitUntil: 'networkidle' });
await page.evaluate(() => document.fonts.ready);
// Page dimensions
const dimensions = {
letter: { width: '8.5in', height: '11in' },
a4: { width: '210mm', height: '297mm' },
};
const dim = dimensions[format] || dimensions.letter;
// Generate PDF
const pdfBuffer = await page.pdf({
width: dim.width,
height: dim.height,
printBackground: true,
preferCSSPageSize: false,
margin: {
top: '0.6in',
right: '0.6in',
bottom: '0.6in',
left: '0.6in',
},
});
await browser.close();
// Write PDF
const { writeFile } = await import('node:fs/promises');
await writeFile(outputPath, pdfBuffer);
// Count pages (regex on PDF structure)
const pdfText = pdfBuffer.toString('latin1');
const pageCount = (pdfText.match(/\/Type\s*\/Page[^s]/g) || []).length;
// File size
const stats = await stat(outputPath);
const sizeKB = (stats.size / 1024).toFixed(1);
console.log(`PDF generated: ${outputPath}`);
console.log(`Pages: ${pageCount} | Size: ${sizeKB} KB | Format: ${format}`);
}
main().catch(err => {
console.error('PDF generation failed:', err.message);
process.exit(1);
});