ScreenshotNeo

BlogHTML to image & PDF

How to Fix Lines Rendering Incorrectly in wkhtmltopdf PDFs

Diagnose broken borders, split table rows, and font artifacts in wkhtmltopdf PDFs with reproducible fixes and safer alternatives.

By the ScreenshotNeo team30 September 20268 min read

How to Fix Lines Rendering Incorrectly in wkhtmltopdf PDFs

When lines look wrong in a wkhtmltopdf PDF, first identify which kind of line is failing. A missing CSS border, a table row split at a page boundary, and stray box or null-like glyphs near text are different defects with different tests. Capture the affected page, element, font, and repeatability before changing CSS.

This guide gives a reproducible workflow, focused fixes, command options to vary deliberately, and a safe migration path when the renderer cannot reproduce your layout.

1. Classify the line-rendering defect

Symptom Likely area First test
Border or rule is missing, too thick, or the wrong color CSS cascade, table borders, overflow, print media Inspect computed border properties and render a one-element case
Horizontal rule or table row breaks across pages Pagination, row height, page-break rules, floats, overflow Use a short table that crosses exactly one page boundary
Boxes, empty squares, or null-like characters appear at line ends Font selection, missing font files, old text shaping code Render with a known fallback font, then with the production font

Do not apply a workaround from one class to another. A border problem is not evidence that page-break-inside is broken, and a font artifact is not fixed by changing table overflow.

Classify the visible defect before changing CSS or renderer settings.
Classify the visible defect before changing CSS or renderer settings.

2. Record a reproducible baseline

  1. Run wkhtmltopdf --version and save the complete output, including patched-Qt or build details.
  2. Record the operating system and version, installation source, installed fonts, and the exact command line.
  3. Save a minimal HTML/CSS/JS file and the resulting PDF. Keep the original report too, because reduction can remove the trigger.
  4. Write the expected appearance and actual appearance in concrete terms: page number, selector, font, border width, and whether the defect repeats.

The project support guidance asks for the version, OS/version, and a detailed HTML/CSS/JS test case. Follow that format when escalating an issue (official support instructions).

wkhtmltopdf --version
uname -a
fc-list | head
wkhtmltopdf input.html output.pdf

The official downloads page lists the 0.12.6 stable series, released June 11, 2020, and notes that Linux package dependencies vary by distribution. Verify the binary actually installed on the target host instead of assuming every package behaves the same (downloads and platform notes).

3. Reduce the HTML to one failing line

Make a copy of the source and remove scripts, images, and unrelated components until one element still fails. Keep the document doctype, the same font declarations, and the same command options. Use local files while debugging so network timing cannot change the result.

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    @page { margin: 18mm; }
    body { font-family: Arial, sans-serif; }
    .rule { border-top: 1px solid #222; height: 0; }
  </style>
</head>
<body>
  <div class="rule"></div>
</body>
</html>

Change one variable per render. A useful matrix is production CSS versus a minimal rule; production font versus Arial; normal media versus print media; and the installed binary versus a pinned build. Keep each PDF so you can identify the change that mattered.

4. Fix missing or malformed CSS borders

Check the cascade and box geometry

  • Inspect computed border-width, border-style, and border-color; a reset may have changed one to none or 0.
  • Check inherited styles and selector specificity. Add a temporary, highly specific rule to prove whether the declaration is reached.
  • Confirm the element has non-zero dimensions. A border on a collapsed element can be clipped or appear absent.
  • Check transforms, absolute positioning, negative margins, and parent clipping. A visible border can be outside the printed box.
  • Add print-specific rules and render with the same media mode used in production.
@media print {
  .invoice .rule {
    display: block;
    height: 0;
    border-top: 1px solid #000 !important;
    overflow: visible;
  }
}

Tables and responsive wrappers

For tables, test border-collapse: collapse versus separate, explicit cell borders, and fixed versus automatic layout. A 2022 issue report describes a Bootstrap responsive wrapper where changing overflow-x: auto to overflow-x: visible fixed that reporter’s clipped table case; it is a case-specific observation, not a universal rule (issue discussion).

.table-wrap { overflow-x: visible; }
table { width: 100%; border-collapse: collapse; table-layout: fixed; }
th, td { border: 1px solid #444; }

Test the wrapper change in isolation. If the page becomes too wide, set the page width or simplify the table rather than keeping an overflow rule that hides content.

5. Fix lines and rows split at page boundaries

Build a test table with enough rows to cross one page. Then vary row height, container overflow, floats, and page-break declarations. A row can split because the engine cannot fit it, because a parent establishes a clipping context, or because a break rule is ignored for that structure.

thead { display: table-header-group; }
tfoot { display: table-footer-group; }
tr, .keep-together { page-break-inside: avoid; }
.section { page-break-before: always; }

These declarations are useful experiments, not guarantees. Historical reports document lines breaking inside rows and page-break rules being ignored for some large rows in older builds (row-splitting report, page-break report). If one row is taller than a page, it cannot be kept intact; redesign it into smaller blocks.

Remove overflow: auto and floats from the smallest failing case. Replace flex or grid wrappers with simple block or table layout for print if the reduced case shows a layout interaction. Confirm that a rule is not being overridden by a later print stylesheet.

6. Fix boxes or null-like characters near text

Render the same sentence with a common fallback font, then with the production font. Verify that every font file is installed and readable by the account running wkhtmltopdf; a desktop preview does not prove the server has the font.

body { font-family: Arial, sans-serif; }
/* Then test the real family separately. */
body { font-family: "Your Installed Font", Arial, sans-serif; }

An older issue associated inserted 0x00/null-like characters with selected fonts in 0.12.1-era builds and reported that removing the font style removed the symptom. A commenter suggested text-rendering: geometricPrecision; treat that as an anecdotal workaround and reproduce it on your exact build before adopting it (font artifact report).

Also test font embedding, weight changes, and text at the exact line break where the artifact appears. If only one font triggers it, ship a tested fallback or evaluate another renderer rather than hiding characters with CSS.

7. Vary documented renderer options deliberately

The command-line manual documents controls that help isolate inputs (usage documentation):

Option Diagnostic use
--user-style-sheet file.css Apply a controlled stylesheet to every page
--print-media-type Use print media rules instead of screen rules
--viewport-size 1280x900 Hold viewport geometry constant
--zoom 1.0 Check whether scaling changes thin rules
--dpi 96 Make rasterization assumptions explicit
--page-size A4, --page-width, --page-height Control the printable box and page breaks
wkhtmltopdf \
  --print-media-type \
  --viewport-size 1280x900 \
  --zoom 1.0 \
  --dpi 96 \
  --page-size A4 \
  --user-style-sheet diagnostic.css \
  reduced.html reduced.pdf

Record every option with the output. These switches control renderer inputs; the manual does not promise that any one repairs arbitrary CSS faults.

8. Security and reliability during reproduction

Do not run untrusted HTML or JavaScript directly through wkhtmltopdf. The project warns that unsanitized input can lead to complete takeover of the server (official security warning). Sanitize user content, isolate the process, restrict outbound network access, set CPU and memory limits, and use a disposable working directory.

A controlled capture pipeline separates page cleanup, rendering, and PDF output.
A controlled capture pipeline separates page cleanup, rendering, and PDF output.

For repeatable builds, pin the binary and fonts in an image, log the command and renderer version, and compare PDFs in CI. Network-loaded fonts and images introduce timing variance; prefer local, versioned assets for regression tests.

9. Troubleshooting checklist

Error or symptom Cause to investigate Fix or next action
Border disappears only in production Different font, binary, media mode, or CSS bundle Compare version, computed CSS, fonts, and command line; run the reduced case on both hosts
Only table edges are clipped Responsive wrapper overflow Test overflow-x: visible; simplify wrapper and set an explicit page width
Rows split despite page-break-inside: avoid Oversized row, float or overflow ancestor, or engine limitation Shorten the row, remove clipping contexts, and test a block layout
Blank squares or odd characters Font unavailable or font shaping defect Install or embed the font, test a fallback, and compare builds
Changes appear to do nothing Wrong input file, cached asset, or overridden rule Use absolute paths, inline diagnostic CSS, and inspect the generated HTML
Command fails on Linux Distribution-specific package dependencies Verify the package source and dependencies listed for your platform

10. When to evaluate another renderer

If a minimal case still fails after font, CSS, pagination, and option tests, compare engines against your actual document. Use these axes: CSS and layout coverage, pagination and header/footer behavior, browser/runtime and packaging requirements, maintenance activity, isolation controls for untrusted input, license and operating cost, and migration effort. A historical commenter reported that Puppeteer solved their case, which is a lead for evaluation rather than proof of a universal fix (issue discussion).

Or skip the browser setup

For production screenshots or PDFs, ScreenshotNeo provides a website capture API and MCP server. It accepts consent banners before capture and removes 60+ known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Only clean shots are billed: bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and X-Page-Verdict and X-Billed headers explain the result.

One request returns PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for all options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

For PDF output and line-sensitive pages, configure paper size, margins, landscape mode, page ranges, full-page lazy-image loading, custom CSS or JavaScript, selector waits, delay or network-idle waits, request blocking, headers, cookies, user agent, timezone, geolocation, and caching TTL. You can capture one CSS-selected element, click before capture, hide selectors, resize images, use transparent backgrounds, create signed links, run async jobs with signed webhooks, capture up to 100 URLs per call, and query usage. The MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

There are 1,000 free shots each month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free, and every feature is on every plan. Create your free ScreenshotNeo account.

FAQ

Is every line defect a wkhtmltopdf bug?

No. The same visual symptom can come from CSS, clipping, pagination, missing fonts, or an engine limitation. Classification and reduction separate them.

Should I always set overflow-x: visible?

No. It fixed one reported responsive-table case. Test it against your own wrapper and page width.

Does text-rendering: geometricPrecision guarantee clean text?

No. It was an old, anecdotal workaround. Verify it with your font and binary.

What belongs in a bug report?

Version/build, OS/version, exact command, smallest HTML/CSS/JS reproducer, expected and actual output, and the PDF showing the defect.