Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Render the R Markdown document to HTML, add print-color CSS, and let Puppeteer print that HTML with backgrounds enabled. Puppeteer uses the print media type by default, and Chromium can alter colors for print unless the stylesheet requests exact color adjustment. Add both -webkit-print-color-adjust: exact and print-color-adjust: exact, set printBackground: true, and use preferCSSPageSize: true when your CSS defines the page size.

If you want the PDF to follow the on-screen stylesheet instead, call page.emulateMediaType('screen') before page.pdf(). The workflow below covers the R Markdown configuration, a complete Puppeteer script, page sizing, dependencies, troubleshooting, and an API alternative.

The reliable workflow

  1. Write a print stylesheet that preserves exact foreground and background colors.
  2. Attach it to html_document in the R Markdown YAML.
  3. Render the .Rmd to HTML with rmarkdown::render().
  4. Open the generated HTML in Chromium through Puppeteer.
  5. Print with printBackground: true and the page-size option that matches your CSS.

This is an HTML-to-PDF workflow. It is different from R Markdown’s LaTeX-based pdf_document(), where browser print CSS and Puppeteer settings do not participate.

1. Add print-color CSS

Create a file named print-colors.css beside your R Markdown source:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
@media print {
  *, *::before, *::after {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

The vendor-prefixed declaration supports Chromium’s print implementation; the unprefixed declaration is the standards spelling. The @media print wrapper keeps the rule scoped to printed output. If a color is currently defined only inside @media screen, move the print-critical declaration into @media print or choose the screen-media option shown later.

Preserving colored panels and callouts

Exact color adjustment does not by itself force background graphics into the PDF. Puppeteer’s printBackground option must also be enabled. For example:

.callout-warning {
  color: #5c3b00;
  background: #fff3cd;
  border-left: 0.35rem solid #f0ad4e;
}

@media print {
  .callout-warning {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

2. Configure R Markdown to produce HTML

Use html_document and attach the stylesheet through YAML:

---
title: "Color-preserving report"
output:
  html_document:
    css: print-colors.css
    self_contained: true
---

css accepts a CSS or Sass file. self_contained: true embeds linked stylesheets, images, and scripts as data URIs so the generated HTML is easier to move to another machine. MathJax remains an external dependency even for a self-contained document, so an offline environment needs a separately available MathJax setup if the report contains equations.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Render it from R:

rmarkdown::render(
  "report.Rmd",
  output_format = "html_document",
  output_file = "report.html"
)

Passing output_format = 'html_document' makes the browser route explicit, even if the document has other output formats in its metadata. Open report.html in Chromium before automating it. If the colors are absent there, Puppeteer cannot restore them; fix the HTML or CSS first.

Inline CSS instead of a separate file

You can put the same declarations in an R Markdown CSS chunk when keeping the report in one source file is more convenient. The important part is that the generated HTML contains the @media print rule and both exact-color properties. A separate file is generally easier to inspect and reuse across reports.

3. Install and run Puppeteer

In a Node.js project, install Puppeteer:

npm install puppeteer

The following script renders a local HTML file to PDF. Save it as print-report.mjs:

import puppeteer from 'puppeteer';
import { pathToFileURL } from 'node:url';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  const htmlUrl = pathToFileURL(process.argv[2] || 'report.html').href;

  await page.goto(htmlUrl, { waitUntil: 'networkidle0' });
  await page.evaluate(() => document.fonts?.ready);

  // Print media is Puppeteer's default. Keep this line for clarity.
  await page.emulateMediaType('print');

  await page.pdf({
    path: process.argv[3] || 'report.pdf',
    printBackground: true,
    preferCSSPageSize: true
  });
} finally {
  await browser.close();
}

Run it after rendering:

node print-report.mjs report.html report.pdf

networkidle0 waits until network activity has settled, and waiting for document.fonts.ready reduces substitutions caused by late-loading web fonts. For a self-contained HTML file these waits are usually quick; for externally hosted assets they depend on network access.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use the screen stylesheet instead

If the intended PDF should match what a viewer sees on screen, replace the print-media call with:

await page.emulateMediaType('screen');
await page.pdf({
  path: 'report.pdf',
  printBackground: true,
  preferCSSPageSize: true
});

This makes screen media queries active while printing. It does not remove the value of printBackground: true; background graphics can still be omitted without that option.

4. Control paper size, margins, and pagination

When the document owns its page geometry, define it in CSS and let Puppeteer honor it:

@page {
  size: A4;
  margin: 16mm 14mm 18mm;
}

@media print {
  .page-break {
    break-after: page;
  }
}

With preferCSSPageSize: true, an @page size takes priority over Puppeteer’s format, width, or height settings. If you omit @page, you can instead select a Puppeteer format such as A4 in the PDF options, but do not expect a CSS page declaration to win unless preferCSSPageSize is enabled.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
Sale
bookdown (Chapman & Hall/CRC The R Series)
  • bookdown: Authoring Books and Technical Documents with R Markdown
  • ABIS BOOK
  • CRC Press

Keep page-break markers print-only if they should not appear in the browser preview:

.page-break {
  display: none;
}

@media print {
  .page-break {
    display: block;
    break-after: page;
  }
}

Modern Chromium honors break-after; older stylesheets may also contain page-break-after for compatibility. Check the generated PDF when a break falls inside a table, figure, or colored callout, because the browser may move the entire block.

Choosing the correct R Markdown output path

Path Renderer Browser print CSS Best use
html_document → Puppeteer HTML, then Chromium Yes CSS-defined colors, web fonts, responsive layouts, and browser-accurate styling
html_document → pagedown::chrome_print() HTML, then headless Chrome managed by R Yes An R-owned workflow that still needs Chromium printing
pdf_document LaTeX engine No LaTeX typography and PDF graphics controls rather than browser CSS

The pagedown package’s chrome_print() is an alternative when you want the R process to own the Chrome step. It remains an HTML-to-Chromium route; switching to pdf_document() changes the renderer and therefore the styling controls.

Diagnose missing colors in order

  1. Inspect the HTML. Open the generated file in Chromium and use DevTools to verify that the element has the expected computed foreground and background colors.
  2. Check media conditions. A rule under @media screen is inactive when Puppeteer prints with the default print media type. Move the rule to @media print or call emulateMediaType('screen').
  3. Verify both color-adjust declarations. Include -webkit-print-color-adjust: exact and print-color-adjust: exact on the elements or a broad print rule.
  4. Enable backgrounds. Set printBackground: true; otherwise colored panels, fills, and background images can disappear.
  5. Check asset paths. Relative CSS, image, font, and script URLs resolve from the HTML file’s location. Move the whole directory together, use correct absolute paths, or enable self_contained: true for dependencies that R Markdown can embed.
  6. Check external services. MathJax remains external in a self-contained document. A blocked CDN, proxy, or offline runner can leave equations or dependent styling incomplete.
  7. Check pagination separately. Define @page margins and size, then enable preferCSSPageSize: true. A layout change can make a color block appear missing when it was pushed to another page.
  8. Record versions. Save the Chromium revision and Puppeteer version when comparing output. Print defaults and implementation details can change between releases, so compare the generated HTML as well as the PDF.

Common failure modes and fixes

The PDF is black-and-white but the HTML is colored

This usually means print media rules are overriding the screen palette, or exact color adjustment is absent. Inspect computed styles under print media, add the two color-adjust declarations, and ensure backgrounds are enabled.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Colored backgrounds vanish while text color survives

Background graphics are controlled independently from text. Add printBackground: true to page.pdf(), then inspect whether the background is a CSS background or an image whose URL failed to resolve.

The PDF is blank or missing late content

Do not print immediately after goto when the report loads assets asynchronously. Use waitUntil: 'networkidle0', wait for fonts, and add an explicit selector wait when your report has a known completion marker.

Fonts or images differ between machines

Use self-contained HTML where practical, install the required fonts in the Chromium environment, and avoid relying on a user profile’s local files. For network resources, verify that the runner can reach every URL before printing.

Page size settings appear ignored

If CSS contains @page, set preferCSSPageSize: true. Otherwise remove the CSS size declaration and specify Puppeteer’s format, width, or height deliberately.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reproducibility, and cost considerations

  • Render once, print once. Generate the HTML with rmarkdown::render(), then reuse that artifact for retries instead of rerunning expensive R code.
  • Keep assets local when repeatability matters. Embedded CSS and images reduce network variability. External MathJax still needs a controlled, reachable source.
  • Reuse a browser for batches. Launching Chromium for every report adds startup overhead; keep one browser process and create a fresh page per document when processing multiple files.
  • Use deterministic inputs. Pin the Node, Puppeteer, and Chromium versions in CI, record the R and rmarkdown versions, and keep the same fonts installed on every runner.
  • Validate output. For important reports, inspect representative pages for colored callouts, syntax highlighting, charts, and page breaks rather than checking only that a PDF file exists.
  • Expect environment limits. Sandboxed CI containers may require the documented Chromium launch settings for that environment. Do not disable security flags globally; change them only when the runner requires it and isolate the job.

Or skip the browser setup

If your rendered report is available at a reachable URL, ScreenshotNeo can capture it through one API request instead of maintaining a local browser workflow. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

Use the API examples from the ScreenshotNeo documentation after publishing the HTML URL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/report.html -o shot.webp
import requests
r = requests.get('https://api.screenshotneo.com/v1/shot', params={'access_key': 'YOUR_API_KEY', 'url': 'https://example.com/report.html'}, timeout=90)
open('shot.webp', 'wb').write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/report.html' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo’s output options include PNG, JPEG, WebP, and PDF; consult the linked documentation for the format and print settings you need. Every feature is included on every plan. The Free plan provides 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots, and yearly billing gives two months free. Create a free ScreenshotNeo account to try it.

FAQ

Frequently Asked Questions

Will this preserve colors in a LaTeX PDF generated by pdf_document()?

No. That output uses a LaTeX engine, so Puppeteer print CSS and Chromium color settings do not control it. Use the HTML-plus-Chromium route when CSS fidelity is the requirement.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why does self_contained: true not make MathJax work offline?

R Markdown embeds most linked assets, but MathJax remains external. An offline or restricted runner must provide MathJax separately.

Can I let Puppeteer choose the page size instead of CSS?

Yes. Omit the CSS @page size and set Puppeteer’s format, width, or height; use preferCSSPageSize: true when CSS should take precedence.

Quick Recap

SaleBestseller No. 3
bookdown (Chapman & Hall/CRC The R Series)
bookdown (Chapman & Hall/CRC The R Series)
bookdown: Authoring Books and Technical Documents with R Markdown; ABIS BOOK; CRC Press
$22.90
SaleBestseller No. 4

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.