RapidHTML2PNG is a PHP API endpoint that converts one or more HTML blocks into a PNG file with a transparent background.
The project is implemented as a single script (convert.php) with built-in:
- input validation and HTML sanitization,
- CSS loading by URL with cache invalidation,
- content-hash based PNG caching,
- automatic renderer detection and fallback,
- optional forced renderer selection via request flag,
- API key check, single-instance execution lock, and runtime timeout.
Renderer priority:
wkhtmltoimage(best fidelity, full HTML/CSS rendering)gd(stable fallback, simplified text-based rendering)imagick(secondary fallback, simplified text-based rendering)
Important detail:
imagickandgdfallback modes do not render full browser layout. They extract text and apply only basic style hints.- Output PNG is saved with a transparent background.
- GD fallback uses a Unicode TrueType font (
DejaVuSanswhen available), so Cyrillic text is rendered in UTF-8 correctly. - Optional request flag
render_engineallows forced engine selection (wkhtmltoimage|gd|imagick|imagemagick); without it, auto-fallback is used.
The endpoint normalizes text to UTF-8 in all critical data paths:
- Request payload parsing (
application/json,multipart/form-data,x-www-form-urlencoded) html_blocksvalidation/sanitizationcss_urlvalidation input- CSS content loaded from remote URL and CSS cache files
- HTML text extraction used by fallback renderers
- Temporary HTML files for rendering include
<meta charset="UTF-8">
If non-UTF-8 text is detected, the API attempts conversion from common legacy encodings (Windows-1251, CP1251, KOI8-R, ISO-8859-1) before returning an error.
Minimum:
- PHP 7.4+
- Extensions:
curl,gd,mbstring - Environment variable:
RAPIDHTML2PNG_API_KEY - Writable directories:
assets/media/rapidhtml2pnglogs
Optional (for better rendering quality):
wkhtmltoimageimagickextension
RapidHTML2PNG/
├── convert.php
├── main.css
├── test_html_to_render.html
├── test_28_minimal.html
├── test_40.html
├── Dockerfile
├── docker-compose.yml
├── init.sh
├── start_server.bat
├── RUN_E2E_TEST.md
├── check_dimensions.php
├── debug_imagemagick.php
├── assets/
│ └── media/
│ └── rapidhtml2png/
└── logs/
docker compose up -dDefault dev key in docker-compose.yml:
rapidhtml2png-dev-key- change it before production use
API endpoint:
http://localhost:8080/convert.php
php -S localhost:8080Set API key before start:
export RAPIDHTML2PNG_API_KEY=your-secret-keyWindows helper:
start_server.batPOST /convert.php
Supported content types:
application/jsonmultipart/form-dataapplication/x-www-form-urlencoded
api_key(required): request API key (or send viaX-API-Keyheader)html_blocks(required): array of HTML stringscss_url(optional):http/httpsURL to CSS filerender_engine(optional): renderer overrideauto(default)wkhtmltoimagegdimagickorimagemagick(alias)
Compatibility aliases for the same option:
rendererengine
curl -X POST http://localhost:8080/convert.php \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_KEY" \
-d '{
"api_key": "YOUR_KEY",
"html_blocks": ["<div><p>Hello</p></div>"],
"css_url": "http://localhost/main.css",
"render_engine": "auto"
}'$html = Get-Content -Raw test_html_to_render.html
$body = @{
'api_key' = 'YOUR_KEY'
'html_blocks[]' = $html
'css_url' = 'http://localhost/main.css'
'render_engine' = 'gd'
}
Invoke-RestMethod -Method Post -Uri 'http://localhost:8080/convert.php' -Body $body- API key is mandatory. Missing/invalid key is rejected before conversion starts.
- Only one active conversion request is allowed at a time (
convert_runtime.lock). - Concurrent requests return:
Work already in progress, please wait. - Hard timeout per request:
300seconds. - In forced mode (
render_engineset), engine-specific cache files are used:<hash>_<engine>.png.
Success response:
{
"success": true,
"message": "HTML converted to PNG successfully",
"data": {
"content_hash": "<md5>",
"render_engine_requested": "auto|wkhtmltoimage|gd|imagemagick",
"library_detection": { ... },
"rendering": {
"engine": "wkhtmltoimage|imagemagick|gd",
"selection_mode": "auto|forced",
"requested_engine": "auto|wkhtmltoimage|gd|imagemagick",
"cached": false,
"output_file": ".../assets/media/rapidhtml2png/<md5>.png",
"file_size": 12345,
"width": 800,
"height": 200,
"mime_type": "image/png"
}
}
}Error response:
{
"success": false,
"error": "...",
"timestamp": "..."
}Two cache layers are used:
- CSS cache:
- Stored in
assets/media/rapidhtml2png/css_cache - Uses conditional requests (
ETag,Last-Modified) when available - Falls back to TTL logic if required
- PNG cache:
- Output file name is MD5 of
html_blocks + css_content - If the same hash already exists, the existing PNG is returned
- In forced mode (
render_engineset), engine-specific cache files are used:<hash>_<engine>.png
css_url is fetched by PHP inside the container.
When using Docker, this works reliably:
http://localhost/main.css
This usually fails from inside container context:
http://localhost:8080/main.css
405 Method Not Allowed: usePOST, notGET.Missing required parameter: html_blocks: send at least one HTML block.No rendering libraries available: install/enable required extensions and tools.Requested rendering engine is not available: removerender_engineor pick an available engine fromlibrary_detection.- CSS fetch errors: verify
css_urlis reachable from the PHP runtime environment. WriteBlob FailedorOutput file was not created: fix output permissions inside container:docker compose exec -T app sh -lc "chown -R www-data:www-data /var/www/html/assets/media/rapidhtml2png && chmod -R 775 /var/www/html/assets/media/rapidhtml2png"
Set your license in this repository.