Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsUse the API your PDF renderer provides for a stylesheet string: tc-lib-pdf accepts raw CSS with setGlobalCSS() or addGlobalCSS(); mPDF parses a separate string with WriteHTML(..., HEADER_CSS); dompdf can receive the CSS inside a <style> element concatenated into the HTML passed to loadHtml(). Do not include <style> tags in the tc-lib-pdf or mPDF stylesheet argument. The renderer’s supported CSS subset, not PHP itself, determines how the final PDF looks.
Choose the API that matches your renderer
| Renderer | How to pass a CSS string | Important behavior |
|---|---|---|
| tc-lib-pdf | setGlobalCSS($css) or addGlobalCSS($css) |
setGlobalCSS() replaces the global stylesheet; addGlobalCSS() appends to it. Pass CSS without surrounding <style> tags. |
| mPDF | WriteHTML($stylesheet, MpdfHTMLParserMode::HEADER_CSS), then body markup with HTML_BODY |
Keep the stylesheet and document markup in separate parser calls when the CSS is stored separately. |
| dompdf | Concatenate a <style> block containing the string into the HTML sent to loadHtml() |
Inline style blocks and style attributes are supported, but browser-only CSS may not be. |
Method names and constants can differ between installed releases. Check the version-specific documentation for the library in your application before deploying.
tc-lib-pdf: set or append global CSS
tc-lib-pdf exposes two explicit methods for CSS held in a PHP string. Use setGlobalCSS() when the string should become the complete global stylesheet. Use addGlobalCSS() when existing global rules must remain and your string should be appended.
Replace the global stylesheet
<?php
$css = 'body { font-family: helvetica; font-size: 10pt; }
h1 { color: #174a7e; margin-bottom: 12pt; }
.total { font-weight: bold; }';
$pdf->setGlobalCSS($css);
$pdf->addHTMLCell(
'<h1>Invoice</h1><p>Example content</p><p class="total">Total: $125.00</p>',
15,
20,
180
);
The argument is CSS text only. Do not write <style>body { ... }</style> around it. Global rules are a reusable baseline; style blocks in the document and individual style attributes can provide more specific, document-level rules according to the cascade described by the library.
#1 Best Overall
Append without discarding existing rules
<?php
$baseCss = 'body { font-family: helvetica; font-size: 10pt; }';
$invoiceCss = '.invoice-number { letter-spacing: 0.5pt; }';
$pdf->setGlobalCSS($baseCss);
$pdf->addGlobalCSS($invoiceCss);
$pdf->addHTMLCell('<p class="invoice-number">INV-1007</p>', 15, 20, 180);
Use this pattern when a common stylesheet is initialized once and each document adds a small set of rules. If you need to discard all earlier global rules, call setGlobalCSS() with the complete replacement string instead.
mPDF: parse the stylesheet before the body
mPDF distinguishes a stylesheet input from a complete HTML document. Send the CSS string in HEADER_CSS mode, then send the document markup in HTML_BODY mode.
<?php
$stylesheet = 'body { font-family: sans-serif; color: #222; }
h1 { font-size: 20pt; color: #174a7e; }
.notice { border: 1px solid #999; padding: 8pt; }';
$html = '<h1>Example report</h1>
<p class="notice">Generated from a PHP string.</p>';
$mpdf->WriteHTML(
$stylesheet,
MpdfHTMLParserMode::HEADER_CSS
);
$mpdf->WriteHTML(
$html,
MpdfHTMLParserMode::HTML_BODY
);
$mpdf->Output();
Do not put a complete <html> document or a <style> wrapper in the value passed as HEADER_CSS. The default WriteHTML mode is intended for a complete page, including head, body and style definitions; the explicit modes make the separation unambiguous.
When one complete string is more convenient
If your application already builds a complete HTML page, you can put the CSS in a <style> element in that page and use the renderer’s complete-document mode. Keep the separate-mode example above when CSS is maintained independently or reused across several documents.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Rank #2
dompdf: put the string in a style block
For dompdf, construct a complete HTML string and insert the CSS between <style> and </style> before calling loadHtml().
<?php
$css = 'body { font-family: sans-serif; font-size: 11pt; }
.summary { background: #f3f5f7; padding: 12px; }
.summary strong { color: #174a7e; }';
$bodyHtml = '<h1>Summary</h1><div class="summary"><strong>Ready</strong></div>';
$html = '<!doctype html><html><head><meta charset="utf-8"><style>'
. $css
. '</style></head><body>'
. $bodyHtml
. '</body></html>';
$dompdf->loadHtml($html);
$dompdf->render();
$dompdf->stream('summary.pdf');
Ensure the generated string is valid HTML. Dompdf describes its engine as mostly CSS 2.1 compliant with selected CSS3 properties, so a rule that works in Chrome may be ignored or laid out differently in the PDF.
Build the CSS and markup safely
Keep CSS and content separate
Store a reusable stylesheet in a variable, file, or template and keep document data in a different variable. This makes it easier to replace a theme, test a minimal rule set, and identify whether a problem is caused by CSS or markup.
Escape data before interpolation
A CSS string does not make the HTML safe. Escape untrusted text before inserting it into elements or attributes, and validate values that become CSS tokens such as colors, dimensions, class names or URLs. A renderer is not a browser security boundary.
Sanitize externally supplied HTML and CSS
Do not pass arbitrary user HTML or CSS directly to a PDF engine. The mPDF documentation specifically warns that outside input must be vetted and sanitized more strictly than ordinary browser input. Apply an allowlist for elements, attributes, selectors and properties appropriate to your application, and reject dangerous URLs or unexpected at-rules.
Start with renderer-supported CSS
Prefer simple selectors, normal flow, explicit dimensions and print-oriented colors until the basic document renders correctly. Then add advanced layout rules one at a time. Browser features such as JavaScript-driven layout, unsupported flex or grid behavior, web fonts, filters and complex positioning may not be implemented by your selected engine.
How to diagnose a PDF that looks wrong
- No styles appear: For tc-lib-pdf, confirm that the value contains raw CSS and that you called
setGlobalCSS()oraddGlobalCSS()before adding the HTML. For mPDF, confirm that the stylesheet call usesHEADER_CSS, not the body mode. - mPDF reports parser or constant errors: Check the installed mPDF version and its namespace/constants. API names can change; use the version’s documented parser-mode symbols.
- dompdf shows literal style text: Inspect the assembled HTML and verify that the CSS is inside a real
<style>element in the<head>, not appended after the closing HTML tag. - Only some properties work: Compare each rule with the renderer’s supported CSS documentation. Reduce the case to one element and one property, then replace browser-specific rules with supported print CSS.
- Rules unexpectedly override one another: Check source order and specificity. In tc-lib-pdf, global CSS is a lower-priority author source than style blocks in the document, while inline style attributes are also part of the cascade.
- Broken layout or missing content: Validate the HTML string, close every element, include a character set, and test with a minimal document. Malformed markup can cause a renderer to recover differently from a browser.
- Fonts or images do not appear: Verify that the renderer can access the resource in its server environment and that the URL or path is permitted. Do not assume browser-relative paths or remote resources will resolve identically.
- Output differs between machines: Pin the library version and compare font availability, locale, timezone and resource permissions. Render a fixed fixture in continuous integration so layout changes are visible after upgrades.
Performance and reliability considerations
Reuse immutable stylesheets
For repeated documents, build the baseline CSS once and reuse it rather than concatenating large strings for every element. With tc-lib-pdf, initialize global rules once and append only document-specific rules. With mPDF, send the shared stylesheet before each document body according to the lifecycle of the PDF object.
Keep documents bounded
Large HTML trees, high-resolution images and complicated tables increase memory and render time. Paginate long reports, resize images before embedding them, and avoid duplicating the same CSS block in every fragment.
Rank #4
Test the actual PDF
A successful method call only proves that the renderer accepted the input. Open the generated PDF and check page breaks, overflow, fonts, colors, links and images. Keep representative fixtures for invoices, long tables and multilingual text because different CSS features fail in different ways.
Plan for version changes
Method availability, parser constants and CSS support vary by release. Record the renderer version with your deployment, review its changelog before upgrading, and rerun visual or text-based PDF checks after changes.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your actual requirement is a clean screenshot or PDF of a live webpage rather than rendering your own PHP HTML, ScreenshotNeo provides a website screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP tools—take_screenshot, get_page_info and capture_pdf—work with Claude, Cursor and other MCP clients.
This is a different workflow from passing a CSS string to tc-lib-pdf, mPDF or dompdf: ScreenshotNeo captures a URL, while the PHP libraries render HTML that your application supplies.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →One-call PHP example
See the ScreenshotNeo documentation for request details and available options.
<?php
import requests;
$r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90);
open("shot.webp", "wb").write($r.content);
The snippet above is the supplied Python form. Equivalent cURL and Node.js calls are useful when your PHP service delegates capture to another process:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo includes full-page capture with lazy images loaded, element selection by CSS selector, dark mode, device presets and custom viewports, retina scale, PDF paper sizes and margins, custom CSS and JavaScript, selector hiding, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Parameters used by other screenshot APIs also work, which can simplify migration.
The Free plan includes 1,000 screenshots per month without a card. Paid plans start at $5 for 3,000 shots; higher plans are Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000) and Business ($249 for 1,000,000). Yearly billing gives two months free, and every feature is available on every plan. Sign up free for 1,000 screenshots a month with no card.
Free tools Windows power users keep installed
One-click scans. No signup required.
Frequently Asked Questions
Can I put the same CSS string in a file and load it for every PDF?
Yes. Read or cache the text once, then pass it through the renderer’s documented stylesheet API. Keep the string free of renderer-incompatible wrappers and verify the result after library upgrades.
Why does CSS that works in a browser fail in the PDF?
HTML-to-PDF engines implement their own, usually smaller CSS subset and pagination rules. Check the selected engine’s support notes and replace unsupported browser features with simpler print CSS.
Is rendering untrusted HTML safer than displaying it in a browser?
No. Treat HTML and CSS supplied by users as hostile input, sanitize them with an allowlist, and restrict external resources before handing them to the PDF renderer.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

