Table of contents
tc-lib-pdf renders a subset of HTML and CSS directly, with no browser engine involved. Markup goes into a placed block on the page, and the engine resolves the cascade, the box model, floats, tables, and page breaks itself.
Table of contents
The Two Entry Points
public addHTMLCell(
string $html,
float $posx = 0,
float $posy = 0,
float $width = 0,
float $height = 0,
?TCellDef $cell = null,
array $styles = [],
): void
public getHTMLCell(/* the same parameters */): string
addHTMLCell() writes the result into the document. It accounts for automatic page and region breaks, appending content to each page stream the block reaches, so a block longer than the remaining space continues onto the following pages.
getHTMLCell() returns the PDF operators as a string and writes nothing. Append them yourself:
$pdf->page->addContent($pdf->getHTMLCell($html, 15, 15, 180));
Use getHTMLCell() when the content fits on the current page and you want control over where the operators land, for instance inside an XObject template or between other page content. Use addHTMLCell() for anything that may span a page break.
Both take positions and dimensions in the document unit selected on the constructor. The $cell argument overrides the padding and margin for this call alone, in points. $styles carries the cell border styles.
A minimal call:
$pdf->addHTMLCell(
html: '<h1>Invoice 2026-0184</h1><p>Due on 30 September 2026.</p>',
posx: 15,
posy: 20,
width: 180,
);
A font must be inserted and added to the page before any text renders. See /docs/fonts/.
Where the Styles Come From
Three author sources are resolved in the normal cascade order:
- The global stylesheet, registered once and injected into every rendering call as the lowest-priority author source.
<style>blocks inside the markup passed to the call.styleattributes on individual elements.
A document rule beats a global rule at equal specificity. To force a global rule to win, mark it !important.
$pdf->setGlobalCSS('
body { font-family: helvetica; font-size: 10pt; color: #222; }
.card { border: 0.5pt solid #999; padding: 4pt; }
');
$pdf->addGlobalCSS('.badge { background-color: #eef; font-weight: bold; }');
| Method | Effect |
|---|---|
setGlobalCSS(string $css) | Replaces the global stylesheet. The argument has no surrounding <style> tags. |
addGlobalCSS(string $css) | Appends to the current global stylesheet. |
resetGlobalCSS() | Clears it. |
The global CSS goes through the same parser as document CSS, so @import, @media, and @spot rules work there too.
The Supported CSS Subset
The engine covers the parts of CSS that a paged document needs. /examples/E073_css_supported_categories/ enumerates the categories in its own markup and renders them, which makes it the reference for what actually works in the release you have installed.
- Cascade: source order, specificity, shorthand expansion and normalization, and the
inheritkeyword including its shorthand forms. - Selectors: type, class, id, descendant, child, the adjacent and general sibling combinators (
+and~), attribute selectors, and a subset of pseudo-classes including:empty. - Pseudo-elements:
::beforeand::afterwith text content. - Box model: margin, padding, border, width and height, and
inline-block. - Flow: floats,
clear, and positioning, plusfieldsetandlegend. - Typography:
font-size-adjust,font-variant,quotes,overflow-wrap,word-break,white-space,text-indentwith theeach-linekeyword,letter-spacing,font-stretch, andline-height. - Paged media:
orphans,widows, page break control, and theprintmedia type. - Colour: every notation tc-lib-color parses, including spot colours addressed from CSS for prepress work.
CSS flexbox and grid are not implemented. Layout that depends on them needs a different tool; /comparison/ covers where the headless-browser renderers fit.
Defaults You Can Change
The engine applies its own defaults before the cascade runs. These set them:
| Method | What it controls |
|---|---|
setDefaultCSSMargin(float $top, $right, $bottom, $left) | Default element margin. |
setDefaultCSSPadding(float $top, $right, $bottom, $left) | Default element padding. |
setDefaultCSSBorderSpacing(float $vert, float $horiz) | Default table border spacing. |
setDefaultCellMargin(float $top, $right, $bottom, $left) | Default cell margin, stored in points. |
setDefaultCellPadding(float $top, $right, $bottom, $left) | Default cell padding, stored in points. |
setDefaultCellBorderPos(float $borderpos) | BORDERPOS_DEFAULT, BORDERPOS_EXTERNAL, or BORDERPOS_INTERNAL. |
setHtmlVSpace(array $tagvs) | Vertical space before and after each tag, as a fixed height or a number of line heights. |
setHTMLMonospaceFont(string $fontname) | The font used for <pre>, <code>, and <tt>. |
setHtmlImageImportResize(bool $enabled) | Import raster images at a reduced pixel size when they render smaller than their intrinsic size. |
setHTMLMonospaceFont() matters for tagged output. PDF/UA requires every font to be embedded, and the default Courier is a non-embedded standard-14 font, so a tagged document needs an embedded monospace font set here after inserting it with the font stack. See /docs/standards/.
Tables
Tables carry the full structure tags: caption, colgroup, col, thead, tbody, and tfoot. Cells span rows and columns, both border models are supported, and a thead repeats at the top of every page the table crosses.
<table>
<thead>
<tr><th>Item</th><th>Qty</th><th>Amount</th></tr>
</thead>
<tbody>
<tr><td>Consulting</td><td>12</td><td>1,440.00</td></tr>
</tbody>
</table>
Widths accept pixel units as well as the document unit; see /examples/E077_html_table_header_px_unit/ for a header sized in pixels and repeated across pages. Floats and table clearance interact as CSS specifies, which /examples/E043_html_tables/ exercises.
The column grid comes from every row: a row with more cells than the first adds columns inside the table width, and a cell spanning rows keeps its column in the following rows. A height attribute or CSS height on a cell sets the minimum height of its row.
A table with page-break-inside: avoid or nobr="true" is kept together: it moves to the next page when its estimated height does not fit in the remaining space. The estimate includes the captions, nested tables, and cells spanning several columns at the width of all the columns they cover. /examples/E087_html_table_grid/ draws each case against its expected result.
Lists
Ordered, unordered, and nested lists are supported with their CSS list-style properties. /examples/E032_html_lists/ renders the variations.
Forms
Form controls written as markup are mapped to AcroForm widgets, with labels, field flags, and select precedence carried across. The result is an interactive PDF form rather than a picture of one.
<form>
<label for="name">Name</label>
<input type="text" name="name" value="">
<select name="country"><option value="it">Italy</option></select>
</form>
See /examples/E042_html_form/ for the markup side and /examples/E040_annotation_form/ for the widget API underneath.
Flow Across Pages
addHTMLCell() breaks the block at the page boundary and continues on the next page, honouring orphans and widows. /examples/E018_html_page_span/ shows a block long enough to cross several pages.
Two things interact with the flow and are worth knowing:
- Page margins bound the block. A cell placed inside the header or footer band behaves differently from one in the content area; /examples/E078_htmlcell_page_margins/ covers the cases.
- No-write regions and column regions constrain where text may go, and HTML flow respects both. See /examples/E080_no_write_regions/ and /examples/E019_page_regions/.
Line height is resolved from CSS; /examples/E069_html_line_height/ shows the values and units side by side.
Images and Remote Assets in Markup
An <img> in the markup is loaded through the same file layer as everything else, which means the allowlists apply. A local path outside markupAllowedPaths and a remote host outside allowedHosts are both refused, and the document renders without the image. No exception is raised. See /docs/remote-resources/.
SVG referenced from markup goes through the same path. For direct SVG placement, use addSVG() or getSetSVG(); see /examples/E030_svg/.
CSS Inside SVG
An SVG carries a cascade of its own. A <style> sheet in the image competes with the presentation attributes and the style attribute of each element, in the normal order of precedence.
Selector support covers type, class, id and universal selectors, attribute selectors, the descendant, child, adjacent sibling and general sibling combinators, and the structural pseudo-classes that a single forward pass can decide: :root, :first-child, :nth-child(), and :not() over a simple compound. A selector that would have to look ahead of the element being painted, such as :last-child, :only-child, :nth-last-child() or :empty, matches nothing rather than matching wrongly. This is narrower than the HTML cascade above, which resolves the whole tree before painting.
Tagged and Accessible Output
In a PDF/UA mode the HTML renderer generates the structure tree from the markup: heading levels are mapped, text runs are tagged, alt text becomes /Alt on figures, an image with an empty alt becomes an Artifact, and form fields get /TU descriptions. A lang or xml:lang attribute that differs from the language in effect becomes /Lang on the element’s structure element, with a Span or Div added for an element that has no role of its own; an empty value is written as und. /examples/E015_pdfua/, /examples/E016_pdfua1/, and /examples/E017_pdfua2/ render the same document under each profile.
To build the tree by hand instead, use beginStructElem() and endStructElem(); see /examples/E063_manual_tag_tree/. suspendPdfUaTagging() and resumePdfUaTagging() bracket content that should not enter the tree, and the suspensions nest.
Hyphenation
loadTexHyphenPatterns() and setTexHyphenPatterns() load a TeX pattern dictionary that the HTML renderer then applies to wrapped text. Soft hyphens in the markup are honoured on their own. See /examples/E028_text_hyphenation/ and /examples/E064_custom_hyphenation_dictionary/.
enableZeroWidthBreakPoints(true) lets a line break inside an unbroken run that offers no space to break at, such as a long URL. /examples/E083_text_line_wrap/ renders the same string with the setting on and off.
Line Breaking
Chinese, Japanese, and Korean text has no spaces between words. The engine adds a line break opportunity around each ideographic character and Hangul syllable, following the UAX #14 line start and end rules: a line does not start with closing punctuation, a full stop, a prolonged sound mark, or a small kana, and does not end with an opening bracket. enableIdeographicBreaks(false) turns this off. The same rules apply to addTextCell() and getTextCell().
CSS and markup adjust the break opportunities:
| Markup or CSS | Effect |
|---|---|
word-break: keep-all | No line break between ideographic characters. |
white-space: nowrap | No line breaks. |
word-break: break-all | A line break between any two characters. |
word-break: break-word, overflow-wrap: break-word or anywhere | A line break inside a word that does not fit on a line. |
<wbr>, ​ (zero width space) | A break opportunity, without a hyphen. |
­ (soft hyphen) | A break opportunity, with a hyphen when the line breaks there. |
⁠ (word joiner),  (zero width no-break space) | No line break at that position. |
These controls take no width, and they are kept through the bidirectional reordering, so they also apply in mixed-direction text. /examples/E086_cjk_line_break/ renders each case in a frame, and /examples/E085_bidi_line_break/ covers line breaking in mixed-direction text.
text-indent indents the first line of a block; with the each-line keyword it also indents the first line after each forced line break. The value is inherited.
Malformed Markup
tidyHTML(string $html, string $defcss) runs the input through Tidy and returns corrected markup, raising an exception when Tidy cannot parse it. Markup arriving from a CMS or from user input is worth passing through it before rendering.
Related Guides
- /docs/fonts/ : the font setup every rendering call depends on.
- /docs/remote-resources/ : the allowlists that govern images, fonts, and SVG referenced from markup.
- /docs/standards/ : PDF/UA tagging, and the conformance modes that change what the renderer emits.
- /features/ : the full capability index.
- /examples/ : the ten HTML and CSS examples, and 81 others.