Skip to content

Commit ce65134

Browse files
feat: enhance API to support direct HTML content rendering alongside URL rendering
### Changes - Updated README.md to reflect new feature allowing HTML content to be passed directly for rendering. - Modified PuppeteerWrapper to handle both URL and HTML content, ensuring either can be provided for rendering. - Adjusted Swagger API documentation to indicate that either 'url' or 'html' must be supplied in requests. - Enhanced validation logic to enforce the requirement of at least one of 'url' or 'html' in the request configuration. ### Problem Solved This update allows users to generate PDFs and images directly from HTML content, improving flexibility for various use cases.
1 parent 6bb0580 commit ce65134

4 files changed

Lines changed: 130 additions & 7 deletions

File tree

README.md

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ It provides a basic yet performant wrapper along with additional features to enh
1717

1818
## Features
1919

20-
✅ Generate PNG images from any URL<br>
21-
✅ Generate PDFs from any URL<br>
20+
✅ Generate PNG images from any URL or HTML content<br>
21+
✅ Generate PDFs from any URL or HTML content<br>
2222
✅ Generate Videos from any URL with smooth animation<br>
23+
**Direct HTML rendering** - Pass HTML content directly without needing a URL<br>
2324
✅ Support for custom headers (like Authorization)<br>
2425
✅ Support to render Lazy animations<br>
2526
✅ Additional support for blocking: Cookies, Ads, Trackers, Banner<br>
@@ -33,16 +34,116 @@ For usage in commercial services, please refer to the `license.txt` file in this
3334

3435
Note: License is not enforced, but we are a small team, and any support to further develop this product would be greatly appreciated! 🙏
3536

36-
## How to create your API call
37+
## API Usage
38+
39+
### Render from URL
3740

3841
Use the Playground at [html2pdfapi](https://html2pdfapi.com/playground) (a free account is required), to create the API request in your favorite language.
3942
You can omit the `apiKey` parameter.
4043

44+
**Example - Generate PDF from URL:**
45+
```bash
46+
curl -X POST http://localhost:3000/render \
47+
-H "Content-Type: application/json" \
48+
-d '{
49+
"url": "https://example.com",
50+
"type": "pdf"
51+
}' \
52+
--output output.pdf
53+
```
54+
55+
### Render from HTML Content
56+
57+
You can now pass HTML content directly to the API without needing a URL! This is perfect for:
58+
- Generating PDFs from dynamically created HTML
59+
- Creating images from HTML templates
60+
- Converting HTML email templates to images
61+
- Any scenario where you have HTML as a string
62+
63+
**Example - Generate PDF from HTML:**
64+
```bash
65+
curl -X POST http://localhost:3000/render \
66+
-H "Content-Type: application/json" \
67+
-d '{
68+
"html": "<!DOCTYPE html><html><head><title>My Document</title><style>body { font-family: Arial; padding: 40px; } h1 { color: #333; }</style></head><body><h1>Hello World!</h1><p>This PDF was generated from HTML content.</p></body></html>",
69+
"type": "pdf"
70+
}' \
71+
--output output.pdf
72+
```
73+
74+
**Example - Generate PNG from HTML:**
75+
```bash
76+
curl -X POST http://localhost:3000/render \
77+
-H "Content-Type: application/json" \
78+
-d '{
79+
"html": "<!DOCTYPE html><html><head><style>body { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; display: flex; justify-content: center; align-items: center; height: 100vh; font-family: sans-serif; } .card { background: rgba(255,255,255,0.1); padding: 40px; border-radius: 20px; }</style></head><body><div class=\"card\"><h1>Beautiful Card</h1><p>Rendered from HTML!</p></div></body></html>",
80+
"type": "image",
81+
"image": { "type": "png" }
82+
}' \
83+
--output output.png
84+
```
85+
86+
**Example - With Custom Options:**
87+
```bash
88+
curl -X POST http://localhost:3000/render \
89+
-H "Content-Type: application/json" \
90+
-d '{
91+
"html": "<html><body><h1>Custom Settings</h1></body></html>",
92+
"type": "pdf",
93+
"device": {
94+
"width": 800,
95+
"height": 600
96+
},
97+
"pdf": {
98+
"format": "Letter",
99+
"printBackground": true,
100+
"margin": {
101+
"top": "20px",
102+
"right": "20px",
103+
"bottom": "20px",
104+
"left": "20px"
105+
}
106+
}
107+
}' \
108+
--output custom.pdf
109+
```
110+
111+
**Important Notes:**
112+
- Either `url` OR `html` must be provided (not both)
113+
- All standard options (device settings, PDF options, image options, etc.) work with both URL and HTML modes
114+
- When using HTML content, network idle wait conditions (`networkidle0`, `networkidle2`) are automatically adjusted to prevent timeouts
115+
41116
The Saas solution of our service provides out-of-the-box async support so that you don't have to implement your own.
42117

43118
There are many libraries you can use to achieve it, it depends on the language you are using, this is a very lightweight and versatile solution if you are looking
44119
for a simple, yet performant solution.
45120

121+
## API Reference
122+
123+
### Endpoints
124+
125+
| Endpoint | Method | Description |
126+
|----------|--------|-------------|
127+
| `/render` | POST | Render URL or HTML to PDF/Image/Video |
128+
| `/render?config=...` | GET | Render URL using GET with JSON config parameter |
129+
| `/health` | GET | Health check with browser pool stats |
130+
| `/docs` | GET | Swagger API documentation |
131+
132+
### Request Parameters
133+
134+
| Parameter | Type | Required | Description |
135+
|-----------|------|----------|-------------|
136+
| `url` | string | Either url or html | URL to render |
137+
| `html` | string | Either url or html | HTML content to render |
138+
| `type` | string | Yes | Output type: `image`, `pdf`, `video`, `html` |
139+
| `device` | object | No | Device settings (width, height, userAgent, etc.) |
140+
| `render` | object | No | Render options (waitTime, fullPage, scroll, etc.) |
141+
| `image` | object | No | Image settings (type, quality, compression, etc.) |
142+
| `pdf` | object | No | PDF settings (format, margins, orientation, etc.) |
143+
| `video` | object | No | Video settings (fps, duration, codec, etc.) |
144+
145+
For complete API documentation, visit `/docs` endpoint after starting the server.
146+
46147
## Getting Started with Development
47148

48149
To get started, run the following commands:

app/core/wrapper.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export class PuppeteerWrapper {
5151

5252
// generation config
5353
this.url = config?.url ?? "";
54+
this.html = config?.html ?? "";
5455
this.type = config?.type ?? "image";
5556

5657
// custom headers
@@ -288,7 +289,20 @@ export class PuppeteerWrapper {
288289
throw new Error("Page not initialized. Call initialize() first.");
289290
}
290291

291-
await this.page.goto(this.url, { waitUntil: this.render.waitUntil });
292+
// Load either URL or HTML content
293+
if (this.html) {
294+
// When using setContent with HTML, we can't use networkidle0/networkidle2
295+
// as there are no network requests, so we use 'load' or 'domcontentloaded'
296+
const waitUntil = this.render.waitUntil === 'networkidle0' || this.render.waitUntil === 'networkidle2'
297+
? 'load'
298+
: this.render.waitUntil;
299+
await this.page.setContent(this.html, { waitUntil });
300+
} else if (this.url) {
301+
await this.page.goto(this.url, { waitUntil: this.render.waitUntil });
302+
} else {
303+
throw new Error("Either URL or HTML content must be provided");
304+
}
305+
292306
if (this.render.waitTime && this.render.waitTime >= 0) {
293307
await new Promise((resolve) => setTimeout(resolve, this.render.waitTime));
294308
}
@@ -304,7 +318,7 @@ export class PuppeteerWrapper {
304318
let content;
305319
let contentType;
306320
let filename;
307-
let host = new URL(this.url).hostname;
321+
let host = this.url ? new URL(this.url).hostname : 'html-content';
308322

309323
// set config with all the properties of this class
310324
const config = {

app/docs/swagger.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,16 @@
196196
"schemas": {
197197
"RenderRequest": {
198198
"type": "object",
199-
"required": ["url", "type"],
199+
"required": ["type"],
200200
"properties": {
201201
"url": {
202202
"type": "string",
203203
"format": "uri",
204-
"description": "URL of the web page to render"
204+
"description": "URL of the web page to render. Either 'url' or 'html' must be provided."
205+
},
206+
"html": {
207+
"type": "string",
208+
"description": "HTML content to render directly. Either 'url' or 'html' must be provided."
205209
},
206210
"type": {
207211
"type": "string",

app/utils/validator.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { z } from "zod";
66
export function validateConfig(config) {
77
const configSchema = z.object({
88
url: z.string().url().optional(),
9+
html: z.string().optional(),
910
type: z.enum(["image", "pdf", "video", "html"]),
1011
headers: z.record(z.string()).optional(),
1112
render: z
@@ -138,6 +139,9 @@ export function validateConfig(config) {
138139
timeout: z.number().positive().default(30000),
139140
})
140141
.optional(),
142+
}).refine((data) => data.url || data.html, {
143+
message: "Either 'url' or 'html' must be provided",
144+
path: ["url", "html"],
141145
});
142146

143147
try {

0 commit comments

Comments
 (0)