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

Render the HTML in a real browser on an explicitly sized A4 canvas, wait for fonts, images, and JavaScript to finish, then export that canvas as a JPEG. For portrait A4, use 210mm × 297mm (8.27in × 11.7in); for landscape, use 297mm × 210mm. Because JPEG is a pixel format, also choose a density: about 794 × 1123 pixels at 96 ppi, 1240 × 1754 at 150 ppi, or 2480 × 3508 at 300 ppi.

The reliable workflow below uses Playwright or Puppeteer, keeps content inside the A4 page, waits for rendering to settle, and verifies the resulting file dimensions before you print or distribute it.

A4 size: physical dimensions and JPG pixels

A4 defines a physical sheet, not a single universal image resolution. Its portrait dimensions are 210mm × 297mm (8.27in × 11.7in). Landscape reverses those measurements. A JPG needs a pixel width and height, so multiply the inch dimensions by your chosen pixels-per-inch (ppi) assumption.

Target density Portrait pixels Landscape pixels Typical use
96 ppi 794 × 1123 1123 × 794 Browser previews and ordinary screen sharing
150 ppi 1240 × 1754 1754 × 1240 General documents and moderate-quality printing
300 ppi 2480 × 3508 3508 × 2480 Sharp print output and small text

These values are arithmetic from the documented A4 dimensions and the stated ppi assumptions. They are not separate browser standards. If a print shop specifies a different resolution, use that specification instead.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Prepare the HTML for one A4 page

Use a physical-size page element

Put all content inside a page wrapper whose CSS width and height are measured in millimetres. box-sizing: border-box makes borders and padding part of the declared size rather than adding to it.

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    @page { size: A4 portrait; margin: 0; }
    * { box-sizing: border-box; }
    html, body { margin: 0; padding: 0; }
    body { background: #e8e8e8; }
    .page {
      width: 210mm;
      height: 297mm;
      padding: 16mm;
      background: #fff;
      overflow: hidden;
      font-family: Arial, sans-serif;
      color: #111;
    }
    h1 { margin: 0 0 8mm; font-size: 26pt; }
    p { margin: 0 0 5mm; font-size: 11pt; line-height: 1.45; }
  </style>
</head>
<body>
  <main class="page">
    <h1>Your A4 document</h1>
    <p>Replace this content with the HTML you need to export.</p>
  </main>
</body>
</html>

Control margins, bleed, and overflow

Reset the browser’s default body margin. Decide whether the JPG includes a bleed (artwork extending beyond the trim edge) or a safe margin. Keep text, logos, and other critical details inside the safe area. A fixed-height page with overflow: hidden prevents accidental spill, but it can also clip content; measure or paginate deliberately instead of hiding a mistake.

Use print CSS only when it helps

@page is the CSS print-layout rule for page dimensions and print properties. It is useful when you first create an A4 PDF or when your styles have print-specific rules. For a direct screenshot, the explicit .page element and a deterministic viewport are the important controls.

Convert HTML to an A4 JPG with Playwright

Playwright’s screenshot API supports jpeg. Its scale option is significant: "css" emits one image pixel per CSS pixel, while device scaling can emit more pixels on a high-DPI device. Set it deliberately and inspect the output rather than assuming the browser’s screen density.

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

Complete Node.js example

Install Playwright with npm install playwright, save your document as document.html, then run this script:

const { chromium } = require('playwright');
const path = require('path');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage({
    viewport: { width: 794, height: 1123 },
    deviceScaleFactor: 1
  });

  await page.goto('file://' + path.resolve('document.html'), {
    waitUntil: 'networkidle'
  });
  await page.evaluate(async () => {
    await document.fonts.ready;
    await Promise.all(Array.from(document.images).map(img => {
      if (img.complete) return Promise.resolve();
      return new Promise(resolve => {
        img.addEventListener('load', resolve, { once: true });
        img.addEventListener('error', resolve, { once: true });
      });
    }));
  });
  await page.addStyleTag({
    content: '* { animation: none !important; transition: none !important; }'
  });

  await page.locator('.page').screenshot({
    path: 'a4.jpg',
    type: 'jpeg',
    quality: 92,
    scale: 'css'
  });
  await browser.close();
})();

With a 794 × 1123 CSS viewport and scale: 'css', the expected portrait output is approximately 794 × 1123 pixels. For a 300-ppi target, use a 2480 × 3508 viewport (or an equivalent device scale) and keep scale choices consistent.

Capture a full page when its height is not fixed

page.screenshot({ fullPage: true }) captures the document’s complete scrollable height. That is appropriate for a long web page, not for a single A4 sheet. For multiple A4 pages, create one fixed-size wrapper per page and capture each wrapper, or generate an A4 PDF first and rasterize each PDF page. Do not silently squeeze a multi-page document into one image.

Convert HTML to an A4 JPG with Puppeteer

Puppeteer provides setContent() for injecting HTML, viewport controls, and page.screenshot(). This example keeps the same readiness checks and captures the A4 element.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const puppeteer = require('puppeteer');
const fs = require('fs');

(async () => {
  const html = fs.readFileSync('document.html', 'utf8');
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.setViewport({ width: 794, height: 1123, deviceScaleFactor: 1 });
  await page.setContent(html, { waitUntil: 'networkidle0' });
  await page.evaluate(async () => {
    await document.fonts.ready;
    await Promise.all(Array.from(document.images).map(img => {
      if (img.complete) return Promise.resolve();
      return new Promise(resolve => {
        img.onload = img.onerror = resolve;
      });
    }));
  });
  await page.addStyleTag({
    content: '* { animation: none !important; transition: none !important; }'
  });
  const element = await page.$('.page');
  await element.screenshot({ path: 'a4.jpg', type: 'jpeg', quality: 92 });
  await browser.close();
})();

Make the render deterministic

Wait for fonts and images

networkidle only describes network activity; a web font may still be applying and an image may have a completed request but not a usable layout. Waiting for document.fonts.ready and each image’s completion avoids shifted text and blank image boxes.

Wait for client-side data

If JavaScript fills the page after load, wait for a meaningful selector rather than a guessed delay. In Playwright, for example, wait for page.locator('.report-ready').waitFor() after your application adds that marker. A delay can be a fallback for animations or third-party widgets, but it is less reliable than a state-based signal.

Rank #3
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

Disable motion and control external resources

Animations can produce different pixels on every run. Disable transitions and animations during capture. If external fonts, images, or API data are unavailable in your build environment, inline them or provide deterministic test fixtures. Check cross-origin permissions when canvas-based content is involved.

Check the JPG before sending it to print

  • Verify the file’s actual width and height with an image viewer or an image-metadata tool.
  • Open the image at 100% and inspect small text, hairlines, logos, and image sharpness.
  • Confirm that no content is clipped at the bottom or right edge.
  • Check that the printer or document system interprets the intended physical size; pixel dimensions alone do not guarantee a 1:1 print size.
  • For professional printing, ask whether 300 ppi, an embedded color profile, bleed, or crop marks are required.

Common failures and fixes

The output is the wrong pixel size

Cause: the viewport, device scale factor, or screenshot scale was not explicit. Fix: set both viewport dimensions and scale, then verify the file. Remember that CSS pixels and device pixels are different units.

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

Text or images are clipped

Cause: content exceeds the fixed 210mm × 297mm box, or padding was added outside the declared size. Fix: keep box-sizing: border-box, reduce content or font size, add a second page, and inspect overflow rather than hiding it.

Fonts change between runs

Cause: capture started before web fonts finished loading, or the font was unavailable. Fix: await document.fonts.ready, self-host or preload required fonts, and confirm the computed font family.

Images are blank

Cause: lazy loading, an image error, blocked authentication, or a capture taken before decoding. Fix: scroll or otherwise trigger lazy images, wait for completion, check the browser console and response status, and provide valid credentials or local assets where required.

The page contains a cookie banner, popup, or chat bubble

Cause: those elements are part of the live page at capture time. Fix: dismiss them in setup code, hide known selectors, or use a renderer that can remove common consent and overlay components before the shot.

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.

JavaScript content is missing

Cause: the script failed, an API request timed out, or capture happened before the app’s ready state. Fix: wait for a page-specific ready selector, inspect console and network errors, and increase the navigation timeout only after fixing the underlying failure.

A long document is squeezed into one page

Cause: a full document was forced into one fixed-height element. Fix: paginate into separate A4 wrappers or create an A4 PDF and rasterize each page independently.

Performance, reliability, and cost choices

Higher pixel targets require more browser memory and produce larger JPEGs. Capture only the page element when possible instead of the entire browser viewport. Reuse a browser process for batches, but create an isolated page for each URL and close pages promptly. Set navigation and resource timeouts, retry transient network failures with a limit, and log the URL, viewport, scale, and output dimensions so a bad image can be reproduced.

JPEG quality is a trade-off: increase it for text and thin lines, then compare file size. JPEG is lossy; use PNG instead when the artwork contains flat UI colors, transparency, or text that shows compression artifacts. If the final destination is a PDF, generating an A4 PDF first may preserve pagination and vector text better than rasterizing HTML directly.

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

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request renders a URL as a clean PNG, JPEG, WebP, or PDF. It accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; 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.

For a JPEG endpoint call, 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 same API supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets or any viewport, retina scale, custom CSS and JavaScript, clicks before capture, hidden selectors, waits for a selector, delay, or network idle, blocked ads/trackers/requests/resource types, custom headers, cookies, user agents and Authorization, timezone and geolocation, transparent backgrounds, resizing, selectable cache TTL, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work, easing migration.

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

The API’s free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan, and yearly billing provides two months free. Sign up for the free ScreenshotNeo plan to try it without a card.

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

Frequently Asked Questions

Should I use 96, 150, or 300 ppi for an A4 JPG?

Use 96 ppi for screen-focused previews, 150 ppi for ordinary documents, and 300 ppi when small text or print sharpness matters. Follow your printer’s specification when one is provided.

Can I convert an A4 HTML document to one JPG if it has several pages?

No single fixed-height canvas can represent several sheets without either clipping or shrinking. Create one A4 image per page, or make an A4 PDF and rasterize each page.

Why does an A4 JPG print at the wrong physical size?

A JPG stores pixels; printing software may apply its own scaling. Set the output pixel dimensions and choose 100% or actual-size printing, then confirm the printer’s margins and paper settings.

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.

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