srivatsavdamaraju commited on
Commit
56a2fa4
·
verified ·
1 Parent(s): 21c3fd0

Upload 5 files

Browse files
Files changed (5) hide show
  1. Dockerfile +45 -0
  2. index.js +62 -0
  3. package.json +17 -0
  4. render.yaml +7 -0
  5. usage.py +551 -0
Dockerfile ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use official Node.js 18 slim image
2
+ FROM node:18-slim
3
+
4
+ # Install Chromium and required dependencies
5
+ RUN apt-get update && apt-get install -y \
6
+ chromium \
7
+ fonts-liberation \
8
+ libappindicator3-1 \
9
+ libasound2 \
10
+ libatk-bridge2.0-0 \
11
+ libatk1.0-0 \
12
+ libcups2 \
13
+ libdbus-1-3 \
14
+ libdrm2 \
15
+ libgbm1 \
16
+ libgtk-3-0 \
17
+ libnspr4 \
18
+ libnss3 \
19
+ libx11-xcb1 \
20
+ libxcomposite1 \
21
+ libxdamage1 \
22
+ libxrandr2 \
23
+ libxss1 \
24
+ libxtst6 \
25
+ xdg-utils \
26
+ --no-install-recommends \
27
+ && rm -rf /var/lib/apt/lists/*
28
+
29
+ # Set working directory
30
+ WORKDIR /app
31
+
32
+ # Copy package files
33
+ COPY package*.json ./
34
+
35
+ # Install dependencies
36
+ RUN npm install
37
+
38
+ # Copy source code
39
+ COPY . .
40
+
41
+ # Expose port
42
+ EXPOSE 5000
43
+
44
+ # Start the server
45
+ CMD ["npm", "start"]
index.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require("express");
2
+ const cors = require("cors");
3
+ const puppeteer = require("puppeteer");
4
+
5
+ const app = express();
6
+ const PORT = process.env.PORT || 5000;
7
+
8
+ app.use(cors());
9
+ app.use(express.json({ limit: "5mb" }));
10
+
11
+ app.get("/", (_req, res) => {
12
+ res.json({ status: "ok", message: "HTML to PDF API" });
13
+ });
14
+
15
+ app.post("/api/html-to-pdf", async (req, res) => {
16
+ let browser;
17
+ try {
18
+ const { html_content: htmlContent, pdf_options: pdfOptions } = req.body || {};
19
+
20
+ if (!htmlContent || !htmlContent.trim()) {
21
+ return res.status(400).json({ error: "html_content is required" });
22
+ }
23
+
24
+ browser = await puppeteer.launch({
25
+ executablePath: "/usr/bin/chromium",
26
+ args: [
27
+ "--no-sandbox",
28
+ "--disable-setuid-sandbox",
29
+ "--disable-dev-shm-usage",
30
+ "--disable-gpu"
31
+ ],
32
+ headless: true
33
+ });
34
+
35
+ const page = await browser.newPage();
36
+
37
+ await page.setContent(
38
+ `<!DOCTYPE html><html><head><meta charset="utf-8" /></head><body>${htmlContent}</body></html>`,
39
+ { waitUntil: "networkidle0" }
40
+ );
41
+
42
+ const pdfBuffer = await page.pdf({
43
+ format: "A4",
44
+ printBackground: true,
45
+ margin: { top: "10mm", right: "10mm", bottom: "10mm", left: "10mm" },
46
+ ...(pdfOptions || {})
47
+ });
48
+
49
+ await browser.close();
50
+ browser = null;
51
+
52
+ res.json({ pdf_base64: pdfBuffer.toString("base64") });
53
+ } catch (err) {
54
+ console.error(err);
55
+ if (browser) {
56
+ await browser.close();
57
+ }
58
+ res.status(500).json({ error: "Failed to generate PDF", details: err.message });
59
+ }
60
+ });
61
+
62
+ app.listen(PORT, () => console.log(`✅ Server running on port ${PORT}`));
package.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "html-to-pdf-service",
3
+ "version": "1.0.0",
4
+ "description": "HTML to PDF API using puppeteer",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "start": "node index.js"
8
+ },
9
+ "dependencies": {
10
+ "express": "^4.19.2",
11
+ "cors": "^2.8.5",
12
+ "puppeteer": "^23.7.0"
13
+ },
14
+ "engines": {
15
+ "node": ">=18"
16
+ }
17
+ }
render.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ services:
2
+ - type: web
3
+ name: html-to-pdf-service
4
+ env: node
5
+ plan: free
6
+ buildCommand: npm install
7
+ startCommand: npm start
usage.py ADDED
@@ -0,0 +1,551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import requests
2
+ # import json
3
+
4
+ # # Step 1: Load large HTML content from external file
5
+ # with open("template.html", "r", encoding="utf-8") as f:
6
+ # html_content = f.read()
7
+
8
+ # # Step 2: Prepare payload
9
+ # payload = json.dumps({
10
+ # "html_content": html_content
11
+ # })
12
+
13
+ # headers = {
14
+ # 'Content-Type': 'application/json'
15
+ # }
16
+
17
+ # # Step 3: Make the API request
18
+ # url = "http://localhost:5000/api/html-to-pdf"
19
+ # response = requests.post(url, headers=headers, data=payload)
20
+
21
+ # # Step 4: Save the PDF if successful
22
+ # if response.status_code == 200:
23
+ # with open("output.pdf", "wb") as f:
24
+ # f.write(response.content)
25
+ # print("✅ PDF saved as output.pdf")
26
+ # else:
27
+ # print(f"❌ Error: {response.status_code}\n{response.text}")
28
+
29
+ #this is option 1
30
+
31
+
32
+
33
+ # this is option 2
34
+ import requests
35
+ import json
36
+
37
+ # Inline HTML (okay for small content only)
38
+ html_content = """
39
+ <!DOCTYPE html>
40
+ <html lang="en">
41
+ <head>
42
+ <meta charset="UTF-8">
43
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
44
+ <title>Business Performance Report</title>
45
+ <style>
46
+ /* Base styles */
47
+ * {
48
+ margin: 0;
49
+ padding: 0;
50
+ box-sizing: border-box;
51
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
52
+ }
53
+
54
+ body {
55
+ background-color: #f5f7fa;
56
+ color: #333;
57
+ line-height: 1.6;
58
+ padding: 20px;
59
+ }
60
+
61
+ .container {
62
+ max-width: 1200px;
63
+ margin: 0 auto;
64
+ background: white;
65
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
66
+ border-radius: 8px;
67
+ overflow: hidden;
68
+ }
69
+
70
+ /* Header styles */
71
+ header {
72
+ background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
73
+ color: white;
74
+ padding: 40px;
75
+ text-align: center;
76
+ }
77
+
78
+ header h1 {
79
+ font-size: 2.5rem;
80
+ margin-bottom: 10px;
81
+ }
82
+
83
+ header p {
84
+ font-size: 1.2rem;
85
+ opacity: 0.9;
86
+ }
87
+
88
+ /* Main content */
89
+ main {
90
+ padding: 40px;
91
+ }
92
+
93
+ .section {
94
+ margin-bottom: 50px;
95
+ padding-bottom: 30px;
96
+ border-bottom: 1px solid #eee;
97
+ }
98
+
99
+ .section:last-child {
100
+ border-bottom: none;
101
+ }
102
+
103
+ .section-title {
104
+ color: #1a2a6c;
105
+ margin-bottom: 25px;
106
+ padding-bottom: 10px;
107
+ border-bottom: 2px solid #fdbb2d;
108
+ }
109
+
110
+ .chart-container {
111
+ background: #f9f9f9;
112
+ border-radius: 8px;
113
+ padding: 25px;
114
+ margin: 25px 0;
115
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
116
+ }
117
+
118
+ .chart-title {
119
+ color: #b21f1f;
120
+ margin-bottom: 15px;
121
+ font-size: 1.3rem;
122
+ }
123
+
124
+ .chart-description {
125
+ margin-bottom: 20px;
126
+ color: #555;
127
+ }
128
+
129
+ /* Bar Chart Styles */
130
+ .bar-chart {
131
+ display: flex;
132
+ align-items: flex-end;
133
+ height: 300px;
134
+ padding: 20px 0;
135
+ border-bottom: 1px solid #ddd;
136
+ position: relative;
137
+ }
138
+
139
+ .bar-container {
140
+ flex: 1;
141
+ display: flex;
142
+ flex-direction: column;
143
+ align-items: center;
144
+ height: 100%;
145
+ margin: 0 10px;
146
+ }
147
+
148
+ .bar {
149
+ width: 60%;
150
+ background: linear-gradient(to top, #1a2a6c, #3f51b5);
151
+ border-radius: 4px 4px 0 0;
152
+ transition: height 0.5s ease;
153
+ position: relative;
154
+ }
155
+
156
+ .bar-label {
157
+ margin-top: 10px;
158
+ font-weight: bold;
159
+ color: #555;
160
+ }
161
+
162
+ .bar-value {
163
+ position: absolute;
164
+ top: -25px;
165
+ left: 0;
166
+ right: 0;
167
+ text-align: center;
168
+ font-weight: bold;
169
+ color: #1a2a6c;
170
+ }
171
+
172
+ .y-axis {
173
+ position: absolute;
174
+ left: 0;
175
+ top: 0;
176
+ bottom: 0;
177
+ width: 40px;
178
+ border-right: 1px solid #ddd;
179
+ display: flex;
180
+ flex-direction: column;
181
+ justify-content: space-between;
182
+ padding: 20px 5px;
183
+ font-size: 0.8rem;
184
+ color: #777;
185
+ }
186
+
187
+ /* Line Chart Styles */
188
+ .line-chart {
189
+ height: 300px;
190
+ position: relative;
191
+ margin: 40px 0 20px 40px;
192
+ border-left: 1px solid #ddd;
193
+ border-bottom: 1px solid #ddd;
194
+ }
195
+
196
+ .line {
197
+ position: absolute;
198
+ height: 2px;
199
+ background: #b21f1f;
200
+ transform-origin: 0 0;
201
+ box-shadow: 0 0 2px rgba(178, 31, 31, 0.5);
202
+ }
203
+
204
+ .line-point {
205
+ position: absolute;
206
+ width: 12px;
207
+ height: 12px;
208
+ border-radius: 50%;
209
+ background: #b21f1f;
210
+ transform: translate(-50%, -50%);
211
+ border: 2px solid white;
212
+ box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
213
+ }
214
+
215
+ .line-point-value {
216
+ position: absolute;
217
+ top: -25px;
218
+ left: 50%;
219
+ transform: translateX(-50%);
220
+ font-size: 0.8rem;
221
+ font-weight: bold;
222
+ color: #b21f1f;
223
+ }
224
+
225
+ .line-label {
226
+ position: absolute;
227
+ bottom: -25px;
228
+ left: 50%;
229
+ transform: translateX(-50%);
230
+ font-size: 0.9rem;
231
+ color: #555;
232
+ }
233
+
234
+ /* Pie Chart Styles */
235
+ .pie-container {
236
+ display: flex;
237
+ flex-wrap: wrap;
238
+ align-items: center;
239
+ justify-content: center;
240
+ }
241
+
242
+ .pie-chart {
243
+ width: 250px;
244
+ height: 250px;
245
+ border-radius: 50%;
246
+ margin: 20px;
247
+ position: relative;
248
+ overflow: hidden;
249
+ background: conic-gradient(
250
+ #1a2a6c 0% 35%,
251
+ #b21f1f 35% 65%,
252
+ #fdbb2d 65% 85%,
253
+ #4caf50 85% 100%
254
+ );
255
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
256
+ }
257
+
258
+ .pie-center {
259
+ position: absolute;
260
+ width: 40%;
261
+ height: 40%;
262
+ background: white;
263
+ border-radius: 50%;
264
+ top: 30%;
265
+ left: 30%;
266
+ box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.1);
267
+ }
268
+
269
+ .pie-legend {
270
+ flex: 1;
271
+ min-width: 200px;
272
+ margin: 20px;
273
+ }
274
+
275
+ .legend-item {
276
+ display: flex;
277
+ align-items: center;
278
+ margin-bottom: 15px;
279
+ }
280
+
281
+ .legend-color {
282
+ width: 20px;
283
+ height: 20px;
284
+ border-radius: 4px;
285
+ margin-right: 10px;
286
+ }
287
+
288
+ /* Key Metrics */
289
+ .metrics {
290
+ display: grid;
291
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
292
+ gap: 20px;
293
+ margin-top: 30px;
294
+ }
295
+
296
+ .metric-card {
297
+ background: white;
298
+ border-radius: 8px;
299
+ padding: 20px;
300
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
301
+ border-top: 4px solid #1a2a6c;
302
+ text-align: center;
303
+ }
304
+
305
+ .metric-value {
306
+ font-size: 2.5rem;
307
+ font-weight: bold;
308
+ color: #1a2a6c;
309
+ margin: 10px 0;
310
+ }
311
+
312
+ .metric-title {
313
+ color: #555;
314
+ font-size: 1rem;
315
+ }
316
+
317
+ /* Footer */
318
+ footer {
319
+ background: #1a2a6c;
320
+ color: white;
321
+ padding: 30px 40px;
322
+ text-align: center;
323
+ }
324
+
325
+ footer p {
326
+ margin: 5px 0;
327
+ opacity: 0.8;
328
+ }
329
+
330
+ /* Responsive adjustments */
331
+ @media (max-width: 768px) {
332
+ .bar-chart {
333
+ height: 250px;
334
+ }
335
+
336
+ .pie-container {
337
+ flex-direction: column;
338
+ }
339
+
340
+ .pie-legend {
341
+ width: 100%;
342
+ }
343
+ }
344
+ </style>
345
+ </head>
346
+ <body>
347
+ <div class="container">
348
+ <header>
349
+ <h1>Quarterly Performance Report</h1>
350
+ <p>Q3 2023 Business Metrics & Analytics</p>
351
+ </header>
352
+
353
+ <main>
354
+ <section class="section">
355
+ <h2 class="section-title">Executive Summary</h2>
356
+ <p>This report provides a comprehensive overview of business performance for Q3 2023. Our company has demonstrated strong growth across key metrics, with particular success in customer acquisition and revenue generation.</p>
357
+
358
+ <div class="metrics">
359
+ <div class="metric-card">
360
+ <div class="metric-value">+15.3%</div>
361
+ <div class="metric-title">Revenue Growth</div>
362
+ </div>
363
+ <div class="metric-card">
364
+ <div class="metric-value">+22.7%</div>
365
+ <div class="metric-title">New Customers</div>
366
+ </div>
367
+ <div class="metric-card">
368
+ <div class="metric-value">94.2%</div>
369
+ <div class="metric-title">Customer Satisfaction</div>
370
+ </div>
371
+ <div class="metric-card">
372
+ <div class="metric-value">+8.5%</div>
373
+ <div class="metric-title">Operational Efficiency</div>
374
+ </div>
375
+ </div>
376
+ </section>
377
+
378
+ <section class="section">
379
+ <h2 class="section-title">Revenue Analysis</h2>
380
+
381
+ <div class="chart-container">
382
+ <h3 class="chart-title">Monthly Revenue (in millions)</h3>
383
+ <p class="chart-description">Revenue growth over the past six months shows consistent upward trajectory.</p>
384
+
385
+ <div class="bar-chart">
386
+ <div class="y-axis">
387
+ <span>$5.0M</span>
388
+ <span>$4.0M</span>
389
+ <span>$3.0M</span>
390
+ <span>$2.0M</span>
391
+ <span>$1.0M</span>
392
+ <span>$0</span>
393
+ </div>
394
+
395
+ <div class="bar-container">
396
+ <div class="bar" style="height: 60%;">
397
+ <div class="bar-value">$3.0M</div>
398
+ </div>
399
+ <div class="bar-label">Apr</div>
400
+ </div>
401
+
402
+ <div class="bar-container">
403
+ <div class="bar" style="height: 65%;">
404
+ <div class="bar-value">$3.3M</div>
405
+ </div>
406
+ <div class="bar-label">May</div>
407
+ </div>
408
+
409
+ <div class="bar-container">
410
+ <div class="bar" style="height: 70%;">
411
+ <div class="bar-value">$3.5M</div>
412
+ </div>
413
+ <div class="bar-label">Jun</div>
414
+ </div>
415
+
416
+ <div class="bar-container">
417
+ <div class="bar" style="height: 80%;">
418
+ <div class="bar-value">$4.0M</div>
419
+ </div>
420
+ <div class="bar-label">Jul</div>
421
+ </div>
422
+
423
+ <div class="bar-container">
424
+ <div class="bar" style="height: 90%;">
425
+ <div class="bar-value">$4.5M</div>
426
+ </div>
427
+ <div class="bar-label">Aug</div>
428
+ </div>
429
+
430
+ <div class="bar-container">
431
+ <div class="bar" style="height: 100%;">
432
+ <div class="bar-value">$5.0M</div>
433
+ </div>
434
+ <div class="bar-label">Sep</div>
435
+ </div>
436
+ </div>
437
+ </div>
438
+
439
+ <div class="chart-container">
440
+ <h3 class="chart-title">Revenue Growth Trend</h3>
441
+ <p class="chart-description">Quarter-over-quarter growth rate demonstrates accelerating momentum.</p>
442
+
443
+ <div class="line-chart">
444
+ <!-- Line segments -->
445
+ <div class="line" style="width: 20%; bottom: 20%; left: 10%; transform: rotate(15deg);"></div>
446
+ <div class="line" style="width: 20%; bottom: 35%; left: 30%; transform: rotate(30deg);"></div>
447
+ <div class="line" style="width: 20%; bottom: 55%; left: 50%; transform: rotate(45deg);"></div>
448
+ <div class="line" style="width: 20%; bottom: 80%; left: 70%; transform: rotate(60deg);"></div>
449
+
450
+ <!-- Data points -->
451
+ <div class="line-point" style="bottom: 20%; left: 10%;">
452
+ <div class="line-point-value">+5%</div>
453
+ </div>
454
+ <div class="line-point" style="bottom: 35%; left: 30%;">
455
+ <div class="line-point-value">+8%</div>
456
+ </div>
457
+ <div class="line-point" style="bottom: 55%; left: 50%;">
458
+ <div class="line-point-value">+12%</div>
459
+ </div>
460
+ <div class="line-point" style="bottom: 80%; left: 70%;">
461
+ <div class="line-point-value">+15%</div>
462
+ </div>
463
+
464
+ <!-- Labels -->
465
+ <div class="line-label" style="left: 10%;">Q4 2022</div>
466
+ <div class="line-label" style="left: 30%;">Q1 2023</div>
467
+ <div class="line-label" style="left: 50%;">Q2 2023</div>
468
+ <div class="line-label" style="left: 70%;">Q3 2023</div>
469
+ </div>
470
+ </div>
471
+ </section>
472
+
473
+ <section class="section">
474
+ <h2 class="section-title">Market Share Analysis</h2>
475
+
476
+ <div class="chart-container">
477
+ <h3 class="chart-title">Product Category Distribution</h3>
478
+ <p class="chart-description">Breakdown of revenue by product category for Q3 2023.</p>
479
+
480
+ <div class="pie-container">
481
+ <div class="pie-chart">
482
+ <div class="pie-center"></div>
483
+ </div>
484
+
485
+ <div class="pie-legend">
486
+ <div class="legend-item">
487
+ <div class="legend-color" style="background-color: #1a2a6c;"></div>
488
+ <div>Software Solutions (35%)</div>
489
+ </div>
490
+ <div class="legend-item">
491
+ <div class="legend-color" style="background-color: #b21f1f;"></div>
492
+ <div>Consulting Services (30%)</div>
493
+ </div>
494
+ <div class="legend-item">
495
+ <div class="legend-color" style="background-color: #fdbb2d;"></div>
496
+ <div>Hardware Products (20%)</div>
497
+ </div>
498
+ <div class="legend-item">
499
+ <div class="legend-color" style="background-color: #4caf50;"></div>
500
+ <div>Support & Maintenance (15%)</div>
501
+ </div>
502
+ </div>
503
+ </div>
504
+ </div>
505
+ </section>
506
+ </main>
507
+
508
+ <footer>
509
+ <p>Global Solutions Inc. • Confidential Business Report</p>
510
+ <p>Data as of September 30, 2023</p>
511
+ </footer>
512
+ </div>
513
+ </body>
514
+ </html>
515
+ """
516
+
517
+ # Prepare payload
518
+ payload = json.dumps({
519
+ "html_content": html_content
520
+ })
521
+
522
+ headers = {
523
+ 'Content-Type': 'application/json'
524
+ }
525
+
526
+ # Send request
527
+ url = "http://localhost:5000/api/html-to-pdf"
528
+ response = requests.post(url, headers=headers, data=payload)
529
+
530
+ # Save result
531
+
532
+
533
+
534
+ import json
535
+
536
+ # Step 1: Full JSON string
537
+ json_data = response.text
538
+ # Step 2: Parse JSON to extract the list string
539
+ data = json.loads(json_data)
540
+ byte_string = data["pdf_base64"]
541
+
542
+ # Step 3: Convert to byte list
543
+ byte_list = [int(b.strip()) for b in byte_string.split(",")]
544
+
545
+ # Step 4: Convert to bytes and save as PDF
546
+ pdf_data = bytes(byte_list)
547
+
548
+ with open("outputs.pdf", "wb") as f:
549
+ f.write(pdf_data)
550
+
551
+ print("✅ PDF saved as outputs.pdf")