9.6 KiB
API
Vsechny cesty jsou uvedene relativne. Verejne se volaji s prefixem
/apps/html-to-pdf, ktery Caddy pred predanim do containeru odstranuje.
Odpovedi jsou JSON, vyjimkou je stazeni hotoveho PDF.
POST /convert
Synchronni prevod. Vraci primo application/pdf.
Urceno pro mensi dokumenty. Pokud konverze presahne SYNC_TIMEOUT_SECONDS
(vychozi 60 s), job se zrusi a sluzba vrati HTTP 413 s odkazem na asynchronni
endpoint. Zruseni se loguje, nikdy nezmizi potichu.
Hlavicky odpovedi:
| Hlavicka | Vyznam |
|---|---|
X-Page-Count |
pocet stranek vysledku |
X-Engine-Used |
engine, ktery dokument vyrenderoval |
X-Missing-Assets |
pocet assetu, ktere se nepodarilo nacist |
X-Warnings |
pocet varovani |
Pokud je X-Missing-Assets nenulovy, v PDF neco chybi. Detaily jsou dostupne
jen u asynchronni cesty, kde se vraci cely seznam.
Vysledek se nikam neuklada. Vznikne v docasnem adresari, odesle se v odpovedi
a hned po jejim odeslani se i s pracovnim adresarem smaze. Neexistuje adresa,
ze ktere by sel stahnout znovu. Kdo potrebuje vysledek pozdeji, pouzije
POST /jobs.
POST /jobs
Zaradi konverzi do fronty. Vraci HTTP 202.
{
"job_id": "1f0e...",
"status": "queued",
"created_at": "2026-08-27T10:00:00Z",
"result_url": "/apps/html-to-pdf/jobs/1f0e.../result"
}
GET /jobs/{job_id}
Stav jobu.
{
"job_id": "1f0e...",
"status": "running",
"created_at": "2026-08-27T10:00:00Z",
"started_at": "2026-08-27T10:00:01Z",
"finished_at": null,
"expires_at": null,
"progress": {
"pages_rendered": 420,
"chunks_done": 9,
"chunks_total": 21,
"pass_number": 1
},
"engine_used": null,
"page_count": null,
"missing_assets": [],
"warnings": [],
"error": null,
"result_url": null
}
Stavy: queued, running, done, failed, cancelled, expired.
Pole pass_number rozlisuje prvni a druhy pruchod. Druhy pruchod nastava jen
tehdy, kdyz se generuje obsah se skutecnymi cisly stranek, takze u takoveho
dokumentu ukazatel postupu probehne dvakrat.
GET /jobs/{job_id}/result
Stahne hotove PDF. Soubor se streamuje, nenacita se cely do pameti. Stahovat lze opakovane, dokud vysledek nevyprsi.
- 404 job neexistuje nebo uz expiroval
- 409 job jeste nedobehl nebo skoncil chybou
DELETE /jobs/{job_id}
Zrusi bezici job nebo smaze hotovy vysledek hned, bez cekani na expiraci. Soubor z uloziste zmizi okamzite, zaznam jobu zustava jeste po dobu TTL, aby bylo videt, co se s nim stalo.
Uchovavani souboru
Nic se neuklada trvale. Vsechno zije jen v docasnem adresari STORAGE_DIR
a v pameti procesu.
| Co | Kde skonci | Jak dlouho |
|---|---|---|
| zdrojove HTML | jen v pameti behem konverze | do konce konverze |
vysledek POST /convert |
docasny adresar jobu | do odeslani odpovedi, pak se maze |
vysledek POST /jobs |
docasny adresar jobu | JOB_RESULT_TTL_SECONDS od dokonceni, vychozi 1 hodina |
| meziprodukty renderu | docasny adresar jobu | do dokonceni jobu, pak se mazou |
| vysledek neuspesneho jobu | nikde | pracovni adresar se maze hned |
Presny cas expirace je v poli expires_at u GET /jobs/{job_id}. Po nem se
smaze soubor i zaznam jobu a dalsi dotaz vraci 404 job_not_found.
Uloziste neni trvale. Restart sluzby znamena ztratu rozpracovanych jobu
i hotovych vysledku, ktere jeste nikdo nestahl. Rozpracovane joby se oznaci
jako failed s kodem service_restarted. Pri startu se navic smazou adresare
jobu, ktere po restartu zustaly bez zaznamu.
GET /health
Stav sluzby, verze, dostupnost enginu a stav fronty. U Chromia se dostupnost overuje skutecnym nastartovanim prohlizece.
status je ok, pokud je k dispozici alespon jeden engine, jinak degraded.
GET /version
Nazev aplikace, verze a aktualni root_path.
GET /docs
Swagger UI. OpenAPI dokument obsahuje servers s prefixem /apps/html-to-pdf,
takze tlacitko Try it out vola spravnou verejnou cestu.
Telo requestu
Stejne pro /convert i /jobs. Povinne je pouze source, vsechno ostatni ma
pouzitelnou vychozi hodnotu.
{
"source": {
"url": "https://example.com/dokument.html",
"html": null,
"base_url": null
},
"engine": "auto",
"page": {
"format": "A4",
"orientation": "portrait",
"margin": { "top": "20mm", "right": "15mm", "bottom": "20mm", "left": "15mm" }
},
"page_numbers": {
"enabled": false,
"format": "{page} / {pages}",
"position": "bottom-center",
"mode": "auto",
"start_at": 1
},
"toc": { "enabled": false, "depth": 3, "title": "Obsah" },
"outline": true,
"pdf_profile": null,
"assets": { "allow_remote": true, "timeout_seconds": 10 },
"chunking": { "enabled": true, "pages_per_chunk": 50 },
"wait_for": { "state": "load", "selector": null, "timeout_seconds": 30 },
"filename": null,
"callback_url": null
}
source
Vyplnene musi byt prave jedno z poli url a html, jinak sluzba vraci 422.
base_url slouzi k rozpadu relativnich cest a pouziva se hlavne spolu s html.
Pri pouziti url se base_url odvodi z finalni adresy po presmerovanich.
engine
weasyprintspravne strankovani, nizka pametova narocnost, nespousti JavaScriptchromiumzvladne i dokumenty dokreslovane JavaScriptemautozvoli chromium, pokud dokument obsahuje aktivni skripty, jinak weasyprint. Pokud render pres weasyprint selze, sluzba to zaloguje a zopakuje ho pres chromium, coz se objevi vewarnings.
page.format
Nazev formatu (A4, A5, Letter) nebo explicitni rozmer (210mm 297mm).
U nazvu se uplatni i orientation, u explicitniho rozmeru je orientace dana
poradim hodnot.
page_numbers.mode
csscislovani resi CSS countery pri renderu. Nejhezci vysledek, ale funguje jen kdyz se cely dokument renderuje najednou, protoze v kazde casti se citac stranek restartuje. Vyzadujeengine: weasyprintachunking.enabled: false, jinak sluzba vraci 400 s kodemunsupported_combination.overlaycisla se dopisi do hotoveho PDF jako pruhledna vrstva. Jedina moznost u clenenych dokumentu a u Chromia.autozvolicssu necleneneho dokumentu renderovaneho WeasyPrintem, jinakoverlay.
Cislovaci vrstvu kresli WeasyPrint i tehdy, kdyz dokument vyrenderovalo Chromium. Bez nainstalovaneho WeasyPrintu proto cislovani stranek nefunguje.
toc
Generuje obsah s odkazy a skutecnymi cisly stranek. Vyzaduje
engine: weasyprint, protoze Chromium neumi rict, na ktere strance nadpis
skoncil. Pri jine kombinaci sluzba vraci 400.
Nadpisy bez atributu id ho dostanou automaticky.
Pokud je dokument rozdelen na casti, odkazy v obsahu nejsou klikatelne, protoze
cil lezi v jine casti. Cisla stranek jsou spravna. Sluzba na to upozorni ve
warnings.
outline
Zalozky PDF generovane z nadpisu h1 az h6. Vypnuti se resi CSS pravidlem
bookmark-level: none, takze funguje i uvnitr jednotlivych casti.
pdf_profile
Predava se WeasyPrintu jako pdf_variant. Povolene hodnoty: pdf/a-1b,
pdf/a-2b, pdf/a-3b, pdf/a-4b, pdf/ua-1.
assets
allow_remote: false zakaze stahovani externich assetu. Nedostupne assety
render nezastavi, ale objevi se v missing_assets a v logu.
chunking
pages_per_chunk je cilova velikost casti. Skutecny pocet stranek je znamy az
po renderu, deleni proto vychazi z odhadu podle mnozstvi textu, obrazku a radku
tabulek. Rez vznika vzdy jen mezi primymi potomky kontejneru, takze tabulka ani
odstavec se nikdy nerozdeli.
Delici body jsou elementy section, article, h1, elementy s atributem
data-chunk a elementy se stylem obsahujicim page-break-before nebo
break-before. Pokud dokument zadny takovy nema, renderuje se vcelku a sluzba
to zaloguje.
obrazky v tabulkach
WeasyPrint neumi vyresit procentualni sirku obrazku uvnitr bunky tabulky.
Sirku bunky v tu chvili jeste nezna, obrazek vyjde nulove siroky a z PDF zmizi
bez jakekoliv chyby, takze se neobjevi ani v missing_assets.
Sluzba proto u enginu weasyprint u obrazku uvnitr td a th prepise
width: <n>% na width: auto a procentualni atribut width odstrani. Pokud
obrazek nema zadne max-width, doplni se max-width: 100%, aby z bunky
nevystoupil. Obrazek se tim vykresli ve sve vlastni velikosti omezene bunkou,
coz je to, co dokument zamyslel.
Chromia se to netyka, ten takove obrazky rozvrhne spravne, a uprava se u nej neprovadi.
wait_for
Pouziva jen Chromium. state je load, domcontentloaded nebo networkidle,
selector navic ceka na konkretni element.
callback_url
Po dokonceni nebo selhani jobu na nej sluzba posle POST se stejnym telem, jake
vraci GET /jobs/{job_id}. Neuspech se loguje a nekolikrat zopakuje, job se
kvuli nemu neoznaci jako failed.
Chyby
{
"error_code": "blocked_target",
"message": "Cilova adresa smeruje do privatniho nebo vyhrazeneho rozsahu a je zablokovana.",
"detail": { "url": "http://127.0.0.1/a.html", "host": "127.0.0.1" }
}
| error_code | HTTP | Kdy nastane |
|---|---|---|
invalid_request |
400 | chybny vstup |
blocked_target |
400 | adresa smeruje do zakazaneho rozsahu nebo ma nepovolene schema |
unsupported_combination |
400 | nepodporovana kombinace parametru, napriklad obsah s Chromiem |
limit_exceeded |
400 | prekrocen nakonfigurovany limit |
source_unavailable |
502 | zdrojovy dokument se nepodarilo stahnout |
render_timeout |
504 | render presahl casovy limit |
sync_too_long |
413 | synchronni konverze presahla limit, pouzijte POST /jobs |
engine_unavailable |
503 | pozadovany engine neni k dispozici |
queue_full |
503 | fronta je plna |
job_not_found |
404 | job neexistuje nebo expiroval |
result_not_ready |
409 | job jeste nedobehl |
service_restarted |
v tele jobu | sluzba se restartovala drive, nez job dobehl |
internal_error |
500 | neocekavana chyba |