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.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use the PDF.js viewport as the single source of truth. Load the page, create a viewport with the scale and rotation you will actually render, and set your HTML wrapper to viewport.width and viewport.height. Choose the PDF boundary first: MediaBox for the physical page, CropBox for the visible area, or TrimBox for the intended finished size.

Those values are rendered CSS-pixel dimensions at the selected scale; they are not automatically the same as raw PDF coordinates or a canvas’s high-density backing-store size.

Start by deciding which PDF page boundary you need

A PDF can expose several rectangles. Selecting the wrong one is the most common reason an HTML overlay appears offset, clipped or consistently too large.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Box Use it when What it represents
MediaBox You need the physical medium or the full page extent. The required rectangle defining the physical page.
CropBox You are matching what a viewer displays or what should be visible. The visible region; by default it is tied to the MediaBox.
TrimBox You are laying out content for a print-production workflow. The intended finished dimensions after trimming.

PDF viewers and libraries may use a visible box rather than the physical box for display. If a file has different box origins or sizes, inspect the document’s page geometry and make an explicit choice instead of assuming all rectangles start at (0,0).

Get the rendered dimensions from the PDF.js viewport

PDF.js describes each page viewport in pixels at 72 DPI by default. The viewport also includes your requested scale and rotation, so its dimensions change when either setting changes. The PDF.js rendering example shows this model and the coordinate conversion used by the renderer: PDF.js rendering examples.

  1. Load the document and await getPage(pageNumber).
  2. Choose the scale and rotation used for the visible page.
  3. Call page.getViewport({ scale, rotation }).
  4. Set the page wrapper and HTML overlay CSS width and height to viewport.width and viewport.height.
  5. Render the canvas with that same viewport.

Do not calculate an HTML size from the PDF’s raw point coordinates while rendering with a different scale. The viewport has already applied the scale and rotation that determine the displayed geometry.

A complete PDF.js example with an HTML overlay

The following example renders page 1 and places an absolutely positioned HTML layer over it. It assumes PDF.js is available as an ES module and that your page contains #pdf-canvas and #html-layer.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import * as pdfjsLib from "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.4.168/pdf.min.mjs";
pdfjsLib.GlobalWorkerOptions.workerSrc =
  "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.4.168/pdf.worker.min.mjs";

const pdf = await pdfjsLib.getDocument("/files/invoice.pdf").promise;
const page = await pdf.getPage(1);

const scale = 1.5;
const rotation = 0;
const viewport = page.getViewport({ scale, rotation });

const canvas = document.querySelector("#pdf-canvas");
const context = canvas.getContext("2d");
const wrapper = document.querySelector("#page-wrapper");
const htmlLayer = document.querySelector("#html-layer");

// CSS/layout dimensions: these are the dimensions your HTML uses.
wrapper.style.width = `${viewport.width}px`;
wrapper.style.height = `${viewport.height}px`;
htmlLayer.style.width = `${viewport.width}px`;
htmlLayer.style.height = `${viewport.height}px`;

// Optional HiDPI backing store. Keep this separate from CSS dimensions.
const devicePixelRatio = window.devicePixelRatio || 1;
canvas.width = Math.ceil(viewport.width * devicePixelRatio);
canvas.height = Math.ceil(viewport.height * devicePixelRatio);
canvas.style.width = `${viewport.width}px`;
canvas.style.height = `${viewport.height}px`;

const renderViewport = page.getViewport({ scale, rotation });
await page.render({
  canvasContext: context,
  viewport: renderViewport,
  transform: devicePixelRatio !== 1
    ? [devicePixelRatio, 0, 0, devicePixelRatio, 0, 0]
    : undefined
}).promise;

The wrapper's CSS size and the canvas's width/height attributes serve different purposes. CSS controls layout; the backing store controls raster density. Multiplying only the backing store by devicePixelRatio keeps the overlay aligned while improving sharpness.

Map PDF coordinates through the viewport transform

Dimensions alone align the page rectangle, not individual points. PDF coordinates use a bottom-left origin, while canvas coordinates use a top-left origin. PDF.js's viewport transform handles the origin conversion, scaling and rotation. Use it whenever you position an HTML element from a PDF-space rectangle.

const pdfRect = { x1: 72, y1: 540, x2: 288, y2: 612 };
const [x1, y1, x2, y2] = viewport.convertToViewportRectangle([
  pdfRect.x1, pdfRect.y1, pdfRect.x2, pdfRect.y2
]);

const left = Math.min(x1, x2);
const top = Math.min(y1, y2);
const width = Math.abs(x2 - x1);
const height = Math.abs(y2 - y1);

const field = document.createElement("div");
field.className = "overlay-field";
Object.assign(field.style, {
  position: "absolute",
  left: `${left}px`,
  top: `${top}px`,
  width: `${width}px`,
  height: `${height}px`
});
document.querySelector("#html-layer").append(field);

Using convertToViewportRectangle (or the equivalent transform API in your installed PDF.js version) avoids hand-written y-axis flips that fail when the page is rotated. Check the API documentation for the exact method names in your project version.

Rotation, scale and page-specific sizing

Rotation

Pass the same rotation to both the viewport used for layout and the viewport used for rendering. A 90- or 270-degree rotation normally swaps the resulting width and height. Recompute the overlay dimensions whenever the user rotates the page.

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

Scale

A scale of 1 uses PDF.js's 72-DPI viewport basis. A scale of 2 produces twice the CSS width and height, not merely a sharper image. If your application zooms, update the wrapper and every overlay from the newly created viewport.

Different pages

Every page has its own viewport. Do not reuse page 1's dimensions for a document whose pages have mixed sizes or orientations. Create a wrapper, canvas and HTML layer per page, using that page's viewport values.

CSS pixels versus canvas pixels

Keep these two measurements distinct:

  • Layout size: viewport.width and viewport.height, assigned to CSS width and height for the wrapper and overlays.
  • Backing-store size: layout dimensions multiplied by devicePixelRatio, assigned to the canvas's width and height attributes.

If you set the CSS wrapper to the multiplied backing-store dimensions, the page and HTML targets become too large. If you omit the backing-store multiplier on a high-density display, alignment remains correct but the bitmap may look soft.

Inspect page size without writing code

In the PDF.js viewer, open the document properties. The viewer can show page size, width, height, units and orientation, with common labels such as A3, A4, Letter and Legal. Treat that display as a diagnostic: your application still needs the viewport dimensions produced with its actual scale and rotation.

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

When the viewer's reported size disagrees with your overlay, check which box the viewer is presenting and whether the file has a nonzero page-box origin. A visible CropBox can differ from the physical MediaBox, and a production-oriented TrimBox may be smaller than both.

Troubleshooting mis-sized or misaligned targets

The overlay is the right shape but shifted vertically

Cause: a PDF-space y coordinate was used directly as a canvas coordinate. Fix: convert the rectangle through the viewport transform and use the returned top-left coordinates.

The page is consistently too large or too small

Cause: the overlay uses raw PDF points, a different scale, or backing-store pixels. Fix: log viewport.width and viewport.height, set those values as CSS dimensions, and ensure the same scale is used for rendering and layout.

Only some documents clip at the edge

Cause: the document's CropBox, MediaBox or another page rectangle differs, or has a nonzero origin. Fix: inspect the page boxes and choose the rectangle matching your purpose. Do not assume all files are standard Letter or A4.

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.

Rotation breaks the fields

Cause: coordinates were manually flipped or the overlay retained dimensions from the unrotated viewport. Fix: recreate the viewport with the requested rotation and map every position through its transform.

The canvas looks blurry after alignment is fixed

Cause: the canvas backing store is only CSS-sized on a high-DPI display. Fix: multiply the backing-store attributes by devicePixelRatio, keep the CSS width and height at the unmultiplied viewport values, and render with the matching transform.

The code works in one PDF.js release but not another

Cause: method signatures and module packaging can change between releases. Fix: pin the PDF.js version you deploy and verify getDocument, getPage, getViewport and coordinate-conversion methods against that version's API documentation.

Performance and reliability considerations

  • Render only the pages visible in the viewport when a long document does not require all overlays at once.
  • Cache each page's viewport for the current scale and rotation; invalidate it after zoom or rotation.
  • Use one coordinate system throughout a layout pass. Mixing dimensions from different scales creates fractional rounding errors and visible drift.
  • Round canvas backing dimensions up with Math.ceil, but retain the floating-point viewport values for CSS and coordinate calculations.
  • When exporting or printing, decide whether the physical, visible or trim boundary is authoritative before generating HTML or PDF output.
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 captures a URL through a website screenshot API, so you can avoid managing a browser, PDF.js worker and viewport code for ordinary web-page captures. Its endpoint can return PNG, JPEG, WebP or PDF. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks or 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.

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

See the ScreenshotNeo API documentation for the full parameter list. A minimal cURL request is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

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)

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}`);
const buffer = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', buffer);

For dynamic pages, ScreenshotNeo also supports full-page capture with lazy images loaded, CSS-selector element capture, custom CSS and JavaScript, waits, blocking rules, cookies, headers, user agents, timezone and geolocation, PDF paper and margin settings, caching with a chosen TTL, signed links, asynchronous jobs, signed webhooks, bulk capture of up to 100 URLs per call, and a usage API. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create a free ScreenshotNeo account to try it.

FAQ

Is a PDF point always one CSS pixel?

No. PDF.js uses a 72-DPI viewport basis, then applies your chosen scale and rotation. Use the resulting viewport dimensions rather than assuming a universal point-to-CSS-pixel conversion.

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

Which box should I use for a browser preview?

Usually the visible CropBox, but verify the renderer's behavior for the files you receive. Use MediaBox when the physical page boundary is the requirement, and TrimBox for finished print dimensions.

Why do width and height match but text still misses the target?

Matching page dimensions does not transform individual coordinates. Convert each PDF-space rectangle through the PDF.js viewport transform, which handles origin, scale and rotation.

Should device-pixel ratio change my HTML overlay size?

No. It changes the canvas backing-store dimensions for sharpness. Keep HTML and canvas CSS dimensions equal to the viewport's unmultiplied width and height.

Frequently Asked Questions

Can I determine page size from a filename such as “A4” without opening the PDF?

No. A filename is not authoritative; inspect the page boxes or the PDF.js document properties, then use the viewport created for your render settings.

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

Do all pages in one PDF have identical dimensions?

Not necessarily. PDF pages can vary in size and orientation, so create and apply a viewport for each page you render.

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.