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 errorsSome links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
If a check mark appears in your browser but disappears from a PDF built by GitHub Actions, the usual cause is a rendering difference rather than bad HTML. First identify the converter and version, then test a literal ✓ and an inline SVG. For Puppeteer, choose the intended media type, wait for fonts, and enable print backgrounds. For wkhtmltopdf, load the exact fonts and provide SVG assets for native checkboxes. Reproduce the build inside the runner, upload the PDF as an artifact, and inspect both its pixels and extracted text.
Why a tick can vanish only in the Actions PDF
Your workstation and the GitHub-hosted runner may use different browser engines, font files, CSS media modes, and JavaScript timing. A glyph can therefore exist in the HTML DOM while the PDF renderer has no font containing that character, hides the element under print CSS, or clips a background image. Native form controls are especially engine-dependent.
Puppeteer’s page.pdf() generates the document with the print CSS media type by default. A design written only for screen media will not necessarily survive. Puppeteer also exposes waitForFonts; its default is true, but setting it explicitly makes the dependency visible in your build. Background-based ticks require printBackground: true.
wkhtmltopdf has a different rendering stack and exposes explicit --checkbox-checked-svg and --checkbox-svg options. Its current stable series is 0.12.6, released June 11, 2020, so an old binary can behave differently from a modern Chromium installation.
#1 Best Overall
Run this diagnostic before changing the workflow
- Record the converter. Print the Node, Puppeteer or Playwright, Chromium, and wkhtmltopdf versions in the Actions log. A fix for Puppeteer will not repair a wkhtmltopdf job.
- Replace the mark temporarily. Change the checkbox or icon-font glyph to literal text:
✓. If that fails, the problem is likely media CSS, clipping, or a missing font rather than an icon library. - Try an inline SVG. Use a small path or polyline inside the document. If the SVG appears while the glyph does not, install the required font or stop depending on the icon font.
- Inspect print rules. Look for
display:none, white text, zero-sized elements,overflow:hidden, or a screen-only background declaration inside@media print. - Reproduce in the runner. Generate the PDF in the same Action image, upload it with
actions/upload-artifact, and inspect the actual file. Do not rely on a local preview.
Make the mark deterministic in HTML and CSS
Use a text fallback with a font family that you install in CI, and keep an inline SVG fallback for layouts where exact shape matters. Do not depend on a browser-native checkbox for a report that must look identical across engines.
<span class="tick" role="img" aria-label="Complete">✓</span>
<svg class="tick-svg" viewBox="0 0 20 20" aria-label="Complete" role="img">
<path d="M3 10.5 8 15 17 5" fill="none" stroke="currentColor"
stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
.tick {
display: inline-block;
font-family: "DejaVu Sans", "Noto Sans", sans-serif;
font-size: 16px;
line-height: 1;
color: #111;
}
.tick-svg {
display: none;
width: 16px;
height: 16px;
color: #111;
}
@media print {
.tick {
display: inline-block;
font-family: "DejaVu Sans", "Noto Sans", sans-serif;
font-size: 16px;
color: #111;
}
.tick-svg { display: inline-block; }
}
During diagnosis, show only one representation at a time so you can tell whether the failure is a glyph, CSS, or clipping issue. If you use a colored background or a background-image for the tick, the Puppeteer section below is mandatory.
Fixing Puppeteer or Chromium PDF jobs
Choose the correct media type
Leave Puppeteer in its default print mode when you have authored an @media print stylesheet. If the intended design is the screen layout, call page.emulateMediaType('screen') before creating the PDF. Do not switch media types merely to hide a missing print rule; make the choice deliberate.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Wait for fonts and backgrounds
Wait for page loading and for document.fonts.ready. Set waitForFonts: true and printBackground: true in page.pdf(). A network-idle wait is useful for pages that load styles or images asynchronously, but give navigation and selectors finite timeouts so a stalled request cannot consume the whole job.
Complete Node.js example
const path = require('node:path');
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
try {
const page = await browser.newPage();
await page.goto(`file://${path.resolve('report.html')}`, {
waitUntil: 'networkidle0',
timeout: 60000
});
// Use 'print' for @media print rules. Use 'screen' only when that is
// intentionally the design you want in the PDF.
await page.emulateMediaType('print');
await page.evaluate(() => document.fonts.ready);
await page.pdf({
path: 'report.pdf',
format: 'A4',
printBackground: true,
preferCSSPageSize: true,
waitForFonts: true,
margin: { top: '16mm', right: '16mm', bottom: '16mm', left: '16mm' }
});
} finally {
await browser.close();
}
})();
If the report is generated from a URL rather than a local file, wait for the application’s ready selector after navigation. For a dynamically inserted tick, use page.waitForSelector('.tick', {visible: true}) or an equivalent application-ready signal before calling page.pdf(). Keep the same Chromium revision in local and CI builds by pinning your package lockfile and installing dependencies with npm ci.
Example GitHub Actions job
name: build-pdf
on: [push, pull_request]
jobs:
pdf:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: sudo apt-get update
- run: sudo apt-get install -y fontconfig fonts-dejavu fonts-noto-core
- run: fc-cache -f -v
- run: npm ci
- run: node build-pdf.js
- uses: actions/upload-artifact@v4
with:
name: pdf-output
path: report.pdf
Install every font used by your CSS, not just the two examples above. If you bundle font files in the repository, load them with @font-face and wait for document.fonts.ready. Keep the font files under version control or retrieve them from a pinned, authenticated source; an Action that silently receives a different font can change glyph metrics and page breaks.
Fixing wkhtmltopdf jobs
Use print media intentionally
Add --print-media-type when your report’s correct rules are inside @media print. If the check mark is inserted by JavaScript, allow that code to run with --javascript-delay and verify the delay is long enough for your page rather than guessing on a developer laptop.
Provide explicit checkbox artwork
Native checkbox appearance is not portable in wkhtmltopdf. Supply deterministic SVG files and pass both options:
wkhtmltopdf
--print-media-type
--javascript-delay 1000
--checkbox-checked-svg checked.svg
--checkbox-svg unchecked.svg
report.html report.pdf
The checked SVG is used for checked controls; the other SVG supplies the unchecked appearance. Test the assets at the final CSS size and with the same color scheme as the document. If your report does not need an interactive control, replacing the input with inline SVG or a styled text mark is usually simpler.
Install the exact font files in the runner and refresh Fontconfig before invoking wkhtmltopdf. On images that keep fonts in a nonstandard directory, set FONTCONFIG_PATH to that image’s configuration directory and verify it in the log. Do not assume that a font installed on your workstation exists on the runner.
Puppeteer and wkhtmltopdf: which behavior matters?
| Concern | Puppeteer/Chromium | wkhtmltopdf |
|---|---|---|
| Media mode | page.pdf() uses print CSS; call emulateMediaType('screen') for an intentional screen layout. |
Use --print-media-type when print CSS is the desired layout. |
| Fonts | Wait for document.fonts.ready; waitForFonts is available and defaults to true. |
Install files, refresh Fontconfig, and check FONTCONFIG_PATH when using a custom location. |
| Native checkboxes | Prefer HTML/CSS or inline SVG for a stable result. | Use --checkbox-checked-svg and --checkbox-svg for explicit assets. |
| Background marks | Set printBackground: true. |
Ensure the stylesheet and command-line media setting do not suppress the background. |
| Dynamic JavaScript | Wait for a selector, a known application-ready state, or a bounded network-idle period. | Use a verified --javascript-delay and test that scripts execute in the selected binary. |
| Maintenance | Pin the Puppeteer package and its Chromium revision in the lockfile. | Record the wkhtmltopdf binary; the stable 0.12.6 series dates from 2020. |
How to prove the fix in CI
- Build the PDF in Actions and upload it as an artifact on every diagnostic run, including failed jobs when possible.
- Open the PDF visually at 100% and at the target print scale. Check for a missing glyph, a white-on-white mark, a clipped baseline, and a missing background separately.
- Extract text from the PDF with the text tool used by your project. A visible SVG path may not appear as the character
✓in extracted text; that is different from a missing visual mark. - Log the converter version, installed font list, selected media mode, and the URL or file path being rendered. These four details make a future regression reproducible.
Common failures and targeted fixes
- “It works locally, but not on ubuntu-latest.” The runner has a different browser revision or lacks the font. Pin dependencies, install the font, run
fc-cache -f -v, and compare versions in the log. - Literal text works, icon-font tick fails. The icon font is absent, blocked, or lacks that code point. Bundle it with
@font-face, wait for fonts, or use a system font with known coverage. - SVG works, text is invisible. Check the computed print color, font family, font size, line height, and element display. Also check whether an ancestor clips overflow.
- Only the colored box disappears. In Puppeteer, set
printBackground: true. In either engine, make sure print CSS does not replace the background withnone. - A native checkbox is empty or has a platform style. Replace it with inline SVG, or pass wkhtmltopdf’s explicit checkbox SVG options. Native controls are not a cross-engine contract.
- The tick appears intermittently. The page is being captured before a webfont or JavaScript component is ready. Wait for the font promise and a visible selector; use a bounded delay only as a fallback.
- The mark is cut off at the baseline. Increase line height, remove restrictive height or overflow rules, and inspect the element at the PDF’s actual CSS size.
- The PDF uses the wrong layout. Confirm whether the converter selected print or screen media, then move the required declarations into an explicit print stylesheet or deliberately emulate screen media.
- The PDF looks correct but automated text checks fail. A path or background image has no text character to extract. Keep an accessible text fallback if downstream systems require searchable or machine-readable status.
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. One GET request can return a PNG, JPEG, WebP, or PDF, while the service first accepts cookie and consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Each cleanup step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are identified in the response and are not billed; response headers include X-Page-Verdict and X-Billed.
For a one-call capture, see the ScreenshotNeo documentation:
curl -G "https://api.screenshotneo.com/v1/shot"
-d access_key=YOUR_API_KEY
--data-urlencode url=https://stripe.com
-o shot.webp
The service also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Every feature is included on every plan; 1,000 screenshots per month are free with no card, and paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account to try it without adding a card.
Rank #4
FAQ
Can I solve this by changing PDF viewers?
A viewer can expose a display bug, but it cannot restore a glyph that was never embedded or an element that the converter hid. Compare the PDF in a second viewer only after checking the runner-generated file and its extracted text.
Should I always switch Puppeteer to screen media?
No. Use print media when the document has print rules; switch to screen only when the screen stylesheet is intentionally the design for the PDF. Mixing the two without a defined choice makes regressions likely.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Is an inline SVG searchable as a check mark?
Usually not as the Unicode character. SVG paths preserve the visible shape, while searchable status requires a text node or an accessible, separately maintained text representation.
Frequently Asked Questions
Can I solve this by changing PDF viewers?
A viewer can expose a display bug, but it cannot restore a glyph that was never embedded or an element that the converter hid. Compare the PDF in a second viewer only after checking the runner-generated file and its extracted text.
Best Value
Should I always switch Puppeteer to screen media?
No. Use print media when the document has print rules; switch to screen only when the screen stylesheet is intentionally the design for the PDF. Mixing the two without a defined choice makes regressions likely.
Is an inline SVG searchable as a check mark?
Usually not as the Unicode character. SVG paths preserve the visible shape, while searchable status requires a text node or an accessible, separately maintained text representation.
The Bottom Line
Make the check mark independent of platform controls: install and wait for fonts, author explicit print behavior, enable backgrounds, and use inline SVG or wkhtmltopdf’s checkbox SVG options. Validate the artifact produced by the actual GitHub Actions runner, not only the local browser preview.
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.

