fix
This commit is contained in:
+231
-47
@@ -37,67 +37,203 @@ class Source(BaseModel):
|
||||
|
||||
|
||||
class Margin(BaseModel):
|
||||
top: str = "20mm"
|
||||
right: str = "15mm"
|
||||
bottom: str = "20mm"
|
||||
left: str = "15mm"
|
||||
"""Okraje stranky. Kazda hodnota je CSS delka, tedy mm, cm, in, pt nebo px.
|
||||
|
||||
Okraje plati pro cely dokument. Pokud si zdrojove HTML nastavi vlastni
|
||||
pravidlo @page, ma prednost jeho hodnota.
|
||||
"""
|
||||
|
||||
top: str = Field(default="20mm", description="Horni okraj, CSS delka.")
|
||||
right: str = Field(default="15mm", description="Pravy okraj, CSS delka.")
|
||||
bottom: str = Field(default="20mm", description="Dolni okraj, CSS delka.")
|
||||
left: str = Field(default="15mm", description="Levy okraj, CSS delka.")
|
||||
|
||||
|
||||
class PageSettings(BaseModel):
|
||||
format: str = Field(default="A4", description="Nazev formatu (A4, A5, Letter) nebo rozmer 210mm 297mm.")
|
||||
orientation: Literal["portrait", "landscape"] = "portrait"
|
||||
margin: Margin = Field(default_factory=Margin)
|
||||
"""Rozmer stranky vysledneho PDF."""
|
||||
|
||||
format: str = Field(
|
||||
default="A4",
|
||||
description=(
|
||||
"Nazev formatu (A4, A5, A3, Letter, Legal) nebo explicitni rozmer "
|
||||
"ve tvaru sirka vyska, napriklad 210mm 297mm."
|
||||
),
|
||||
)
|
||||
orientation: Literal["portrait", "landscape"] = Field(
|
||||
default="portrait",
|
||||
description=(
|
||||
"Uplatni se jen u nazvu formatu. U explicitniho rozmeru urcuje "
|
||||
"orientaci poradi hodnot."
|
||||
),
|
||||
)
|
||||
margin: Margin = Field(default_factory=Margin, description="Okraje stranky.")
|
||||
|
||||
|
||||
class PageNumbers(BaseModel):
|
||||
enabled: bool = False
|
||||
format: str = Field(default="{page} / {pages}", description="Zastupne symboly {page} a {pages}.")
|
||||
position: PagePosition = "bottom-center"
|
||||
"""Cislovani stranek. Ve vychozim stavu je vypnute.
|
||||
|
||||
Cisla se kresli do okraje stranky, takze na ne musi byt v `page.margin`
|
||||
misto. Pri nulovem okraji se cislo nema kam vejit.
|
||||
"""
|
||||
|
||||
enabled: bool = Field(default=False, description="Zapne cislovani stranek.")
|
||||
format: str = Field(
|
||||
default="{page} / {pages}",
|
||||
description=(
|
||||
"Sablona popisku. {page} je cislo aktualni stranky, {pages} celkovy "
|
||||
"pocet stranek. Priklady: '{page}', 'Strana {page} z {pages}'."
|
||||
),
|
||||
)
|
||||
position: PagePosition = Field(
|
||||
default="bottom-center",
|
||||
description="Ktery okrajovy box stranky cislo dostane.",
|
||||
)
|
||||
mode: Literal["auto", "css", "overlay"] = Field(
|
||||
default="auto",
|
||||
description=(
|
||||
"auto zvoli css u necleneneho dokumentu a overlay u clenene nebo u Chromia. "
|
||||
"css pouziva CSS countery, overlay dopisuje cisla do hotoveho PDF."
|
||||
"css pouzije CSS countery pri renderu. Nejcistsi vysledek, ale vyzaduje "
|
||||
"engine weasyprint a chunking.enabled false, protoze v kazde casti se "
|
||||
"citac stranek restartuje. Jinak vraci 400 unsupported_combination. "
|
||||
"overlay dopise cisla do hotoveho PDF jako pruhlednou vrstvu, funguje "
|
||||
"vzdy. auto zvoli css u necleneneho dokumentu renderovaneho "
|
||||
"WeasyPrintem, jinak overlay."
|
||||
),
|
||||
)
|
||||
start_at: int = Field(
|
||||
default=1,
|
||||
description=(
|
||||
"Cislo prvni stranky. Uplatni se jen v rezimu overlay, v rezimu css "
|
||||
"se vychazi z citace dokumentu."
|
||||
),
|
||||
)
|
||||
start_at: int = 1
|
||||
|
||||
|
||||
class TocSettings(BaseModel):
|
||||
enabled: bool = False
|
||||
depth: int = Field(default=3, ge=1, le=6)
|
||||
title: str = "Obsah"
|
||||
"""Automaticky generovany obsah se skutecnymi cisly stranek.
|
||||
|
||||
Vyzaduje engine weasyprint, protoze jen ten umi rict, na ktere strance
|
||||
nadpis skoncil. Jina kombinace konci chybou 400 unsupported_combination.
|
||||
|
||||
Obsah se vklada na zacatek dokumentu a dokument se kvuli nemu renderuje
|
||||
dvakrat. Prvni pruchod zjisti cisla stranek, druhy je doplni.
|
||||
"""
|
||||
|
||||
enabled: bool = Field(default=False, description="Zapne generovani obsahu.")
|
||||
depth: int = Field(
|
||||
default=3, ge=1, le=6, description="Do jake urovne nadpisu obsah saha, h1 az h6."
|
||||
)
|
||||
title: str = Field(default="Obsah", description="Nadpis nad tabulkou obsahu.")
|
||||
|
||||
|
||||
class AssetSettings(BaseModel):
|
||||
allow_remote: bool = True
|
||||
timeout_seconds: int = Field(default_factory=lambda: get_settings().asset_timeout_seconds, ge=1)
|
||||
"""Stahovani obrazku, stylu a fontu, na ktere se dokument odkazuje."""
|
||||
|
||||
allow_remote: bool = Field(
|
||||
default=True,
|
||||
description=(
|
||||
"false zakaze stahovani externich assetu. Dokument se vyrenderuje bez "
|
||||
"nich a vsechny se objevi v missing_assets."
|
||||
),
|
||||
)
|
||||
timeout_seconds: int = Field(
|
||||
default_factory=lambda: get_settings().asset_timeout_seconds,
|
||||
ge=1,
|
||||
description=(
|
||||
"Timeout stazeni jednoho assetu. Nedostupny asset render nezastavi, "
|
||||
"jen se objevi v missing_assets."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class ChunkSettings(BaseModel):
|
||||
enabled: bool = True
|
||||
pages_per_chunk: int = Field(default=50, ge=1)
|
||||
"""Deleni velkeho dokumentu na casti renderovane samostatne.
|
||||
|
||||
Bez deleni drzi render cely strom stranek v pameti, coz je u tisicistrankoveho
|
||||
dokumentu ten limitujici faktor. Casti se po renderu opet slouci do jednoho
|
||||
PDF, vysledek je jeden souvisly soubor.
|
||||
|
||||
Rez vznika vzdy jen mezi primymi potomky hlavniho kontejneru, takze tabulka
|
||||
ani odstavec se nikdy nerozdeli. Delici body jsou section, article, h1,
|
||||
elementy s atributem data-chunk a elementy se stylem obsahujicim
|
||||
page-break-before nebo break-before. Dokument, ktery zadny takovy nema, se
|
||||
vyrenderuje vcelku i pri zapnutem deleni.
|
||||
|
||||
Deleni vylucuje rezim cislovani css a u cleneneho dokumentu prestanou byt
|
||||
klikatelne odkazy v obsahu. Cisla stranek zustavaji spravna.
|
||||
"""
|
||||
|
||||
enabled: bool = Field(default=True, description="Zapne deleni dokumentu na casti.")
|
||||
pages_per_chunk: int = Field(
|
||||
default=50,
|
||||
ge=1,
|
||||
description=(
|
||||
"Cilova velikost jedne casti ve strankach. Skutecny pocet je znamy az "
|
||||
"po renderu, deleni proto vychazi z odhadu podle mnozstvi textu, "
|
||||
"obrazku a radku tabulek."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class WaitFor(BaseModel):
|
||||
"""Chromium only. Ignored by the WeasyPrint engine."""
|
||||
"""Cekani na dokresleni stranky. Pouziva jen Chromium, WeasyPrint to ignoruje."""
|
||||
|
||||
state: Literal["load", "domcontentloaded", "networkidle"] = "load"
|
||||
selector: str | None = None
|
||||
timeout_seconds: int = Field(default=30, ge=1)
|
||||
state: Literal["load", "domcontentloaded", "networkidle"] = Field(
|
||||
default="load",
|
||||
description=(
|
||||
"Kdy se stranka povazuje za nactenou. networkidle ceka, az utichne "
|
||||
"sitovy provoz, coz je nejspolehlivejsi u stranek dokreslovanych skripty."
|
||||
),
|
||||
)
|
||||
selector: str | None = Field(
|
||||
default=None,
|
||||
description="Volitelny CSS selektor, na jehoz vyskyt se navic pocka.",
|
||||
)
|
||||
timeout_seconds: int = Field(
|
||||
default=30, ge=1, description="Limit cekani. Pri prekroceni vraci 504 render_timeout."
|
||||
)
|
||||
|
||||
|
||||
class ConvertRequest(BaseModel):
|
||||
"""Telo pozadavku. Stejne pro POST /convert i POST /jobs.
|
||||
|
||||
Povinne je pouze `source`, vsechno ostatni ma pouzitelnou vychozi hodnotu.
|
||||
Nastaveni tykajici se strankovani je rozdelene do ctyr bloku: `page` resi
|
||||
rozmer a okraje, `page_numbers` cislovani, `chunking` deleni velkeho
|
||||
dokumentu na renderovane casti a `toc` obsah s cisly stranek.
|
||||
"""
|
||||
|
||||
source: Source
|
||||
engine: EngineName = Field(default_factory=lambda: get_settings().default_engine) # type: ignore[arg-type]
|
||||
page: PageSettings = Field(default_factory=PageSettings)
|
||||
page_numbers: PageNumbers = Field(default_factory=PageNumbers)
|
||||
toc: TocSettings = Field(default_factory=TocSettings)
|
||||
engine: EngineName = Field(
|
||||
default_factory=lambda: get_settings().default_engine, # type: ignore[arg-type]
|
||||
description=(
|
||||
"weasyprint ma spravne strankovani a nizkou pametovou narocnost, ale "
|
||||
"nespousti JavaScript. chromium zvladne i dokumenty dokreslovane "
|
||||
"skripty, nema ale pouzitelne CSS countery ani pozice nadpisu. "
|
||||
"auto zvoli chromium, pokud dokument obsahuje aktivni skripty, jinak "
|
||||
"weasyprint, a pri selhani WeasyPrintu render zopakuje pres Chromium."
|
||||
),
|
||||
)
|
||||
page: PageSettings = Field(
|
||||
default_factory=PageSettings, description="Rozmer stranky a okraje."
|
||||
)
|
||||
page_numbers: PageNumbers = Field(
|
||||
default_factory=PageNumbers, description="Cislovani stranek, vychozi je vypnute."
|
||||
)
|
||||
toc: TocSettings = Field(
|
||||
default_factory=TocSettings, description="Obsah se skutecnymi cisly stranek."
|
||||
)
|
||||
outline: bool = Field(default=True, description="Generovat zalozky PDF z nadpisu h1 az h6.")
|
||||
pdf_profile: Literal["pdf/a-1b", "pdf/a-2b", "pdf/a-3b", "pdf/a-4b", "pdf/ua-1"] | None = None
|
||||
assets: AssetSettings = Field(default_factory=AssetSettings)
|
||||
chunking: ChunkSettings = Field(default_factory=ChunkSettings)
|
||||
wait_for: WaitFor = Field(default_factory=WaitFor)
|
||||
assets: AssetSettings = Field(
|
||||
default_factory=AssetSettings, description="Stahovani externich assetu."
|
||||
)
|
||||
chunking: ChunkSettings = Field(
|
||||
default_factory=ChunkSettings,
|
||||
description="Deleni velkeho dokumentu na casti renderovane samostatne.",
|
||||
)
|
||||
wait_for: WaitFor = Field(
|
||||
default_factory=WaitFor, description="Cekani na dokresleni stranky, jen pro Chromium."
|
||||
)
|
||||
filename: str | None = Field(default=None, description="Nazev souboru ve Content-Disposition.")
|
||||
callback_url: str | None = Field(
|
||||
default=None,
|
||||
@@ -116,6 +252,28 @@ class ConvertRequest(BaseModel):
|
||||
"toc": {"enabled": True, "depth": 3, "title": "Obsah"},
|
||||
"chunking": {"enabled": True, "pages_per_chunk": 50},
|
||||
},
|
||||
{
|
||||
"source": {"url": "https://example.com/smlouva.html"},
|
||||
"engine": "weasyprint",
|
||||
"page": {
|
||||
"format": "A4",
|
||||
"orientation": "landscape",
|
||||
"margin": {
|
||||
"top": "25mm",
|
||||
"right": "20mm",
|
||||
"bottom": "25mm",
|
||||
"left": "20mm",
|
||||
},
|
||||
},
|
||||
"page_numbers": {
|
||||
"enabled": True,
|
||||
"format": "Strana {page} z {pages}",
|
||||
"position": "bottom-right",
|
||||
"mode": "css",
|
||||
"start_at": 1,
|
||||
},
|
||||
"chunking": {"enabled": False},
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -127,10 +285,18 @@ class MissingAsset(BaseModel):
|
||||
|
||||
|
||||
class JobProgress(BaseModel):
|
||||
pages_rendered: int = 0
|
||||
chunks_done: int = 0
|
||||
chunks_total: int = 0
|
||||
pass_number: int = 0
|
||||
"""Postup renderu. Chunky jsou casti, na ktere byl dokument rozdelen."""
|
||||
|
||||
pages_rendered: int = Field(default=0, description="Pocet dosud vyrenderovanych stranek.")
|
||||
chunks_done: int = Field(default=0, description="Pocet hotovych casti.")
|
||||
chunks_total: int = Field(default=0, description="Celkovy pocet casti v tomto pruchodu.")
|
||||
pass_number: int = Field(
|
||||
default=0,
|
||||
description=(
|
||||
"Cislo pruchodu. Druhy pruchod nastava jen u dokumentu s generovanym "
|
||||
"obsahem, kde se zastupna cisla stranek nahrazuji skutecnymi."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class ErrorInfo(BaseModel):
|
||||
@@ -140,19 +306,37 @@ class ErrorInfo(BaseModel):
|
||||
|
||||
|
||||
class JobState(BaseModel):
|
||||
"""Stav jobu vcetne postupu renderu a doby dostupnosti vysledku."""
|
||||
|
||||
job_id: str
|
||||
status: JobStatus
|
||||
created_at: datetime
|
||||
started_at: datetime | None = None
|
||||
finished_at: datetime | None = None
|
||||
expires_at: datetime | None = None
|
||||
progress: JobProgress = Field(default_factory=JobProgress)
|
||||
engine_used: str | None = None
|
||||
page_count: int | None = None
|
||||
missing_assets: list[MissingAsset] = Field(default_factory=list)
|
||||
warnings: list[str] = Field(default_factory=list)
|
||||
error: ErrorInfo | None = None
|
||||
result_url: str | None = None
|
||||
status: JobStatus = Field(
|
||||
description="queued, running, done, failed, cancelled nebo expired."
|
||||
)
|
||||
created_at: datetime = Field(description="Cas zarazeni do fronty.")
|
||||
started_at: datetime | None = Field(default=None, description="Cas, kdy job zacal bezet.")
|
||||
finished_at: datetime | None = Field(default=None, description="Cas dokonceni nebo selhani.")
|
||||
expires_at: datetime | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"Cas, do ktereho je vysledek ke stazeni. Potom se soubor i zaznam jobu "
|
||||
"smazou a dalsi dotaz vraci 404. Odvozuje se z JOB_RESULT_TTL_SECONDS."
|
||||
),
|
||||
)
|
||||
progress: JobProgress = Field(default_factory=JobProgress, description="Postup renderu.")
|
||||
engine_used: str | None = Field(default=None, description="Engine, ktery dokument vyrenderoval.")
|
||||
page_count: int | None = Field(default=None, description="Pocet stranek vysledku.")
|
||||
missing_assets: list[MissingAsset] = Field(
|
||||
default_factory=list,
|
||||
description="Assety, ktere se nepodarilo nacist. V PDF na jejich miste neco chybi.",
|
||||
)
|
||||
warnings: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Upozorneni, ktera konverzi nezastavila.",
|
||||
)
|
||||
error: ErrorInfo | None = Field(default=None, description="Vyplnene jen u stavu failed.")
|
||||
result_url: str | None = Field(
|
||||
default=None, description="Adresa ke stazeni, vyplnena jen u stavu done."
|
||||
)
|
||||
|
||||
|
||||
class JobAccepted(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user