Named pages
Declare @page cover, @page wide, and select them with the page property. Each named page carries its own size, orientation, margins and margin boxes, and the assignment survives a document assembled from several rendering passes.
Everything up to stage two is a browser doing what browsers do. What happens after is the reason this exists.
Any stylesheet you would give a browser, including the Paged Media rules browsers ignore.
One continuous column, in a browser context created for this render alone.
Named pages, margin boxes, running heads and cross-references are resolved and filled.
A structure tree with reading order, roles and language, so the file is navigable.
Every rule that could not be honoured, with its selector, its page and the reason.
CSS Paged Media and GCPM specify the constructs a paginated document needs. These are the ones implemented here, with the declaration each corresponds to.
Declare @page cover, @page wide, and select them with the page property. Each named page carries its own size, orientation, margins and margin boxes, and the assignment survives a document assembled from several rendering passes.
string-set copies text out of the flow into a named string; a margin box prints it with content: string(). The value tracks the last matching element on or before the page, so a running head follows the chapter rather than repeating a constant.
target-counter(attr(href), page) reads the page counter at the link's destination and prints it. Resolution happens after pagination has settled, so a reference points at the page the target actually landed on rather than an estimate.
leader('.') fills the space between a label and its page number with a repeating glyph, measured after the line breaks. A table of contents therefore aligns at any measure without a fixed-width column or a table.
float: footnote moves an element to the footnote area of the page holding its reference, generates the numbered call, and re-flows the body to make room. If the note cannot fit, it moves with its reference rather than separating from it.
orphans and widows set the minimum lines a fragment may leave behind or carry over; break-before, break-after and break-inside constrain where a break may fall at all. Constraints are reported when they cannot be satisfied instead of being silently relaxed.
A render either satisfies a declaration or it does not, and the response says which. The `unhonoured` array names the rule, the selector, the page and the reason, so a layout regression is a diff in your test suite rather than something a reader discovers.
curl -X POST https://api.pdfspectrum.com/v1/render \
-H "X-Api-Key: $PDFSPECTRUM_KEY" \
-H "Content-Type: application/json" \
-d '{
"html": "<h1>Quarterly Report</h1>...",
"css": "@page { size: A4; margin: 22mm } \
@page cover { margin: 0 } \
h1 { page: cover; string-set: title content() } \
@top-center { content: string(title) }",
"tagged": true,
"conformance": "pdf/ua-2"
}'{
"id": "rnd_8f21c4",
"pages": 34,
"bytes": 1284219,
"conformance": { "profile": "PDF/UA-2", "passed": true },
"unhonoured": [
{
"rule": "float: top",
"selector": "figure.wide",
"reason": "no page break available within 3 lines of the anchor",
"page": 12
}
]
}Every plan renders with the full engine. Conformance, tagging and diagnostics are not an upgrade — a document that has to be accessible has to be accessible on the free tier too.
100 credits / month
1,000 credits / month
3,000 credits / month
10,000 credits / month
30,000 credits / month
One hundred renders a month on the free tier, with the complete engine behind them.