ABDALLALSWAITI commited on
Commit
06295a6
·
verified ·
1 Parent(s): 3e80a46

Upload puppeteer_pdf.js

Browse files
Files changed (1) hide show
  1. puppeteer_pdf.js +204 -67
puppeteer_pdf.js CHANGED
@@ -1,80 +1,217 @@
1
  #!/usr/bin/env node
2
- const puppeteer = require('puppetee‌​r');
 
3
  const path = require('path');
4
  const fs = require('fs');
5
 
6
- if (process.argv.length < 4) {
7
- console.error('Usage: node puppeteer_pdf.js <html_file_path> <aspect_ratio>');
8
- process.exit(1);
 
 
9
  }
10
 
11
- const htmlFilePath = process.argv[2];
12
- const aspectRatio = process.argv[3];
13
 
 
14
  if (!fs.existsSync(htmlFilePath)) {
15
- console.error(`Error: HTML file not found at ${htmlFilePath}`);
16
- process.exit(1);
17
  }
18
 
19
- function mmToPx(mm) { return Math.round((mm / 25.4) * 96); }
20
-
21
- (async () => {
22
- let browser;
23
- try {
24
- const opts = {
25
- headless: 'new',
26
- args: ['--no-sandbox','--disable-setuid-sandbox','--disable-dev-shm-usage','--disable-gpu','--no-zygote']
27
- };
28
- for (const p of ['/usr/bin/chromium','/usr/bin/chromium-browser','/usr/bin/google-chrome','/usr/bin/google-chrome-stable']) {
29
- if (fs.existsSync(p)) { opts.executablePath = p; break; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  }
 
31
 
32
- browser = await puppeteer.launch(opts);
33
- const page = await browser.newPage();
34
-
35
- let wMM, hMM;
36
- if (aspectRatio === '16:9') { wMM = 297; hMM = 210; }
37
- else if (aspectRatio === '1:1') { wMM = 210; hMM = 210; }
38
- else { wMM = 210; hMM = 297; } // default 9:16
39
-
40
- await page.setViewport({ width: mmToPx(wMM), height: mmToPx(hMM), deviceScaleFactor: 1 });
41
-
42
- const fileUrl = 'file://' + path.resolve(htmlFilePath);
43
- await page.goto(fileUrl, { waitUntil: ['domcontentloaded','networkidle0'], timeout: 60000 });
44
-
45
- // انتظر الصور والخطوط
46
- await page.evaluate(async () => {
47
- const imgs = Array.from(document.querySelectorAll('img'));
48
- await Promise.all([
49
- (document.fonts && document.fonts.ready) ? document.fonts.ready : Promise.resolve(),
50
- ...imgs.map(img => img.complete ? Promise.resolve() : new Promise(res => {
51
- img.addEventListener('load', res);
52
- img.addEventListener('error', res);
53
- setTimeout(res, 5000);
54
- }))
55
- ]);
56
- });
57
-
58
- await page.emulateMediaType('screen'); // غيّر لـ 'print' لو عندك CSS للطباعة
59
- await new Promise(r => setTimeout(r, 300));
60
-
61
- const pdfPath = htmlFilePath.replace(/\.html?$/i, '.pdf');
62
- await page.pdf({
63
- path: pdfPath,
64
- printBackground: true,
65
- preferCSSPageSize: true,
66
- margin: { top: '0mm', right: '0mm', bottom: '0mm', left: '0mm' },
67
- width: `${wMM}mm`,
68
- height: `${hMM}mm`,
69
- scale: 1
70
- });
71
-
72
- if (!fs.existsSync(pdfPath)) throw new Error('PDF not created');
73
- process.exit(0);
74
- } catch (e) {
75
- console.error('PDF conversion failed:', e?.message || e);
76
  process.exit(1);
77
- } finally {
78
- if (browser) await browser.close();
79
- }
80
- })();
 
1
  #!/usr/bin/env node
2
+
3
+ const puppeteer = require('puppeteer');
4
  const path = require('path');
5
  const fs = require('fs');
6
 
7
+ // Get command line arguments
8
+ const args = process.argv.slice(2);
9
+ if (args.length < 2) {
10
+ console.error('Usage: node puppeteer_pdf.js <html_file_path> <aspect_ratio>');
11
+ process.exit(1);
12
  }
13
 
14
+ const htmlFilePath = args[0];
15
+ const aspectRatio = args[1];
16
 
17
+ // Check if HTML file exists
18
  if (!fs.existsSync(htmlFilePath)) {
19
+ console.error(`Error: HTML file not found at ${htmlFilePath}`);
20
+ process.exit(1);
21
  }
22
 
23
+ // Main conversion function
24
+ async function convertToPDF() {
25
+ let browser;
26
+ try {
27
+ console.log('Starting PDF conversion...');
28
+ console.log(`Aspect Ratio: ${aspectRatio}`);
29
+
30
+ // Launch browser with system Chromium
31
+ const browserOptions = {
32
+ headless: 'new',
33
+ args: [
34
+ '--no-sandbox',
35
+ '--disable-setuid-sandbox',
36
+ '--disable-dev-shm-usage',
37
+ '--disable-web-security',
38
+ '--disable-features=IsolateOrigins',
39
+ '--disable-site-isolation-trials',
40
+ '--disable-accelerated-2d-canvas',
41
+ '--disable-gpu',
42
+ '--single-process',
43
+ '--no-zygote',
44
+ '--disable-software-rasterizer',
45
+ '--disable-dev-tools',
46
+ '--no-first-run',
47
+ '--no-default-browser-check',
48
+ '--disable-breakpad',
49
+ '--disable-crash-reporter',
50
+ '--crash-dumps-dir=/tmp',
51
+ '--disable-component-update'
52
+ ]
53
+ };
54
+
55
+ // Try to use system Chromium if available
56
+ const chromiumPaths = [
57
+ '/usr/bin/chromium',
58
+ '/usr/bin/chromium-browser',
59
+ '/usr/bin/google-chrome',
60
+ '/usr/bin/google-chrome-stable'
61
+ ];
62
+
63
+ for (const chromiumPath of chromiumPaths) {
64
+ if (fs.existsSync(chromiumPath)) {
65
+ browserOptions.executablePath = chromiumPath;
66
+ console.log(`Using Chromium at: ${chromiumPath}`);
67
+ break;
68
+ }
69
+ }
70
+
71
+ browser = await puppeteer.launch(browserOptions);
72
+ const page = await browser.newPage();
73
+
74
+ // Set viewport for better rendering based on aspect ratio
75
+ let viewportWidth = 1920;
76
+ let viewportHeight = 1080;
77
+
78
+ if (aspectRatio === '9:16') {
79
+ viewportWidth = 1080;
80
+ viewportHeight = 1920;
81
+ } else if (aspectRatio === '1:1') {
82
+ viewportWidth = 1080;
83
+ viewportHeight = 1080;
84
+ }
85
+
86
+ await page.setViewport({
87
+ width: viewportWidth,
88
+ height: viewportHeight,
89
+ deviceScaleFactor: 2
90
+ });
91
+
92
+ console.log(`Viewport set to: ${viewportWidth}x${viewportHeight}`);
93
+
94
+ // Load the HTML file
95
+ const absoluteHtmlPath = path.resolve(htmlFilePath);
96
+ const htmlContent = fs.readFileSync(absoluteHtmlPath, 'utf8');
97
+
98
+ // Set base URL for relative paths
99
+ const baseUrl = 'file://' + path.dirname(absoluteHtmlPath) + '/';
100
+
101
+ console.log('Loading HTML content...');
102
+ await page.setContent(htmlContent, {
103
+ waitUntil: ['networkidle0', 'domcontentloaded'],
104
+ timeout: 30000
105
+ });
106
+
107
+ // Inject base tag for relative paths
108
+ await page.evaluate((baseUrl) => {
109
+ const base = document.createElement('base');
110
+ base.href = baseUrl;
111
+ document.head.appendChild(base);
112
+ }, baseUrl);
113
+
114
+ // Wait for all images and fonts to load
115
+ console.log('Waiting for images and fonts...');
116
+ await page.evaluate(async () => {
117
+ const selectors = Array.from(document.querySelectorAll("img"));
118
+ await Promise.all([
119
+ document.fonts.ready,
120
+ ...selectors.map((img) => {
121
+ if (img.complete) return Promise.resolve();
122
+ return new Promise((resolve) => {
123
+ img.addEventListener("load", resolve);
124
+ img.addEventListener("error", resolve);
125
+ setTimeout(resolve, 5000);
126
+ });
127
+ }),
128
+ ]);
129
+ });
130
+
131
+ // Count pages for logging
132
+ const pageCount = await page.evaluate(() => {
133
+ const pages = document.querySelectorAll('.page, .slide, section.page, article.page');
134
+ return pages.length || 'unknown';
135
+ });
136
+ console.log(`Detected ${pageCount} page element(s) in HTML`);
137
+
138
+ // Emulate screen media (not print) to preserve CSS
139
+ await page.emulateMediaType('screen');
140
+
141
+ // Additional wait for rendering and animations
142
+ console.log('Waiting for final render...');
143
+ await new Promise(resolve => setTimeout(resolve, 2000));
144
+
145
+ // Generate PDF path
146
+ const pdfPath = htmlFilePath.replace('.html', '.pdf');
147
+
148
+ // Configure PDF options based on aspect ratio
149
+ let pdfOptions = {
150
+ path: pdfPath,
151
+ printBackground: true,
152
+ preferCSSPageSize: true, // CRITICAL: Respect CSS @page rules
153
+ margin: {
154
+ top: '0mm',
155
+ right: '0mm',
156
+ bottom: '0mm',
157
+ left: '0mm'
158
+ },
159
+ displayHeaderFooter: false,
160
+ scale: 1.0 // No scaling to preserve exact dimensions
161
+ };
162
+
163
+ // Set dimensions based on aspect ratio
164
+ if (aspectRatio === '16:9') {
165
+ pdfOptions.format = 'A4';
166
+ pdfOptions.landscape = true;
167
+ pdfOptions.width = '297mm';
168
+ pdfOptions.height = '210mm';
169
+ console.log('PDF format: A4 Landscape (297mm x 210mm)');
170
+ } else if (aspectRatio === '1:1') {
171
+ pdfOptions.width = '210mm';
172
+ pdfOptions.height = '210mm';
173
+ pdfOptions.landscape = false;
174
+ console.log('PDF format: Square (210mm x 210mm)');
175
+ } else if (aspectRatio === '9:16') {
176
+ pdfOptions.format = 'A4';
177
+ pdfOptions.landscape = false;
178
+ pdfOptions.width = '210mm';
179
+ pdfOptions.height = '297mm';
180
+ console.log('PDF format: A4 Portrait (210mm x 297mm)');
181
+ } else {
182
+ pdfOptions.format = 'A4';
183
+ pdfOptions.landscape = true;
184
+ console.log('PDF format: A4 Landscape (default)');
185
+ }
186
+
187
+ // Generate the PDF
188
+ console.log('Generating PDF...');
189
+ await page.pdf(pdfOptions);
190
+
191
+ // Verify PDF was created
192
+ if (fs.existsSync(pdfPath)) {
193
+ const stats = fs.statSync(pdfPath);
194
+ console.log(`Successfully converted ${htmlFilePath} to ${pdfPath}`);
195
+ console.log(`PDF size: ${(stats.size / 1024).toFixed(2)} KB`);
196
+ process.exit(0);
197
+ } else {
198
+ console.error('PDF file was not created');
199
+ process.exit(1);
200
+ }
201
+
202
+ } catch (error) {
203
+ console.error(`PDF conversion failed: ${error.message}`);
204
+ console.error(error.stack);
205
+ process.exit(1);
206
+ } finally {
207
+ if (browser) {
208
+ await browser.close();
209
+ }
210
  }
211
+ }
212
 
213
+ // Run conversion
214
+ convertToPDF().catch(error => {
215
+ console.error('Unhandled error:', error);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  process.exit(1);
217
+ });