Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Use the PDF.js viewport—not raw PDF coordinates—to size an HTML page wrapper or overlay. Load the target page, create a viewport with the exact scale and rotation you will render, and set the matching HTML dimensions from viewport.width and viewport.height. Then map individual PDF coordinates through viewport.transform, because PDF coordinates begin at the bottom-left while canvas coordinates begin at the top-left.
Before measuring anything, decide which PDF boundary represents your target: the physical MediaBox, the visible CropBox, or the finished-page TrimBox. A mismatch here is the most common reason an apparently correct overlay is shifted or sized incorrectly.
What “PDF page size” means in a browser
A PDF page has several rectangles, and they serve different purposes. Choosing the wrong one can make HTML appear clipped, padded, or offset even when your arithmetic is correct.
| Box | Purpose | Use it when targeting HTML |
|---|---|---|
MediaBox |
The required physical-medium boundary for the page. | Use it when the HTML must match the complete physical sheet, including areas outside the visible crop. |
CropBox |
The region intended for display or printing; it is tied to the MediaBox by default. |
Use it when your browser overlay should match what a viewer displays. |
TrimBox |
The intended finished dimensions after trimming. | Use it for print-layout workflows where HTML represents the final trimmed piece. |
The PDF specification defines these boxes independently, so a document can have different physical, visible, and finished boundaries. Inspect the document properties in a PDF viewer when you need the named size, width, height, units, or orientation; common labels include A3, A4, Letter, and Legal.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
#1 Best Overall
The reliable measurement: the PDF.js viewport
PDF.js describes each page viewport as a pixel-sized rectangle at 72 DPI by default. The viewport also includes the scale and rotation you request. Therefore, the dimensions you should copy into CSS are the dimensions of the viewport created for the render, not the dimensions of the raw PDF coordinate rectangle. The PDF.js rendering example demonstrates this viewport-based approach: PDF.js rendering example.
Scale changes the target dimensions
If a page is 612 by 792 PDF points and you render at scale 1, the viewport is 612 by 792 CSS pixels (before any rotation). At scale 1.5, it becomes 918 by 1,188 CSS pixels. Those values are the correct CSS dimensions for an HTML layer rendered beside or over that page.
Rotation can swap width and height
A 90-degree rotation normally changes a portrait viewport into a landscape viewport. Always read the dimensions after passing the rotation to getViewport; do not swap values yourself, because the viewport also contains the transform needed for coordinate conversion.
CSS dimensions are not the canvas backing size
For sharp output on a high-density display, PDF.js examples multiply the canvas backing dimensions by devicePixelRatio. Keep that multiplier separate from layout dimensions:
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Rank #2
- The wrapper and HTML overlay use
viewport.widthandviewport.heightin CSS pixels. - The canvas bitmap can use those values multiplied by the device-pixel ratio.
- Do not enlarge the HTML overlay just because the canvas backing store is larger.
Complete PDF.js example: size an HTML target to the rendered page
The following browser code loads page 1, renders it at a chosen scale and rotation, and creates an absolutely positioned HTML layer with exactly the same CSS size. Replace the worker URL and document URL with the versions used by your project. PDF.js method signatures can vary by installed version, so check the API for that version before deploying.
<div id="page" class="page">
<canvas id="pdf-canvas"></canvas>
<div id="html-overlay">HTML aligned to the PDF page</div>
</div>
<style>
.page { position: relative; }
#pdf-canvas, #html-overlay { position: absolute; inset: 0; }
#html-overlay { pointer-events: none; }
</style>
<script type="module">
import * as pdfjsLib from '/pdfjs/pdf.mjs';
pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdfjs/pdf.worker.mjs';
const loadingTask = pdfjsLib.getDocument('/documents/example.pdf');
const pdf = await loadingTask.promise;
const page = await pdf.getPage(1);
const scale = 1.5;
const rotation = 0;
const viewport = page.getViewport({ scale, rotation });
const wrapper = document.querySelector('#page');
const canvas = document.querySelector('#pdf-canvas');
const overlay = document.querySelector('#html-overlay');
const context = canvas.getContext('2d');
// These are CSS layout dimensions.
wrapper.style.width = `${viewport.width}px`;
wrapper.style.height = `${viewport.height}px`;
overlay.style.width = `${viewport.width}px`;
overlay.style.height = `${viewport.height}px`;
// This is only the high-density canvas backing store.
const outputScale = window.devicePixelRatio || 1;
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = `${viewport.width}px`;
canvas.style.height = `${viewport.height}px`;
const renderContext = {
canvasContext: context,
viewport,
transform: outputScale !== 1
? [outputScale, 0, 0, outputScale, 0, 0]
: null
};
await page.render(renderContext).promise;
</script>
The wrapper, canvas CSS size, and overlay all use one geometry source: the same viewport that is passed to page.render. That prevents fractional rounding or an accidental scale mismatch from accumulating between layers.
Mapping an HTML element to a PDF position
Matching page dimensions is only half of alignment. PDF coordinates use a bottom-left origin; canvas and normal browser layout use a top-left origin. The viewport transform accounts for that origin change, the requested scale, and rotation.
For a PDF-space point (x, y) and a viewport transform represented as [a, b, c, d, e, f], calculate the rendered point as:
Recommended Free Tools
Rank #3
- hole punched
- high quality card stock
- 4 pages
- made in USA
- keyboard shortcuts
const [a, b, c, d, e, f] = viewport.transform;
const canvasX = a * x + c * y + e;
const canvasY = b * x + d * y + f;
Use the transformed coordinates for an absolutely positioned HTML element. Do not simply subtract y from the page height: that shortcut fails when the page is rotated, when the page has a nonzero box origin, or when the transform includes translation.
Preserve one coordinate convention
- Store source measurements in PDF units if they came from a PDF extraction or design file.
- Apply the viewport transform once when converting them to rendered positions.
- Position HTML in the same CSS pixel coordinate system as the wrapper.
- Do not apply the device-pixel ratio to HTML coordinates.
Choosing the correct page boundary
- Define the output. Decide whether “matching” means the whole physical sheet, the viewer-visible page, or the final trimmed page.
- Inspect the document. In a viewer’s document properties, note the displayed page size, orientation, and units. If the document has unusual whitespace or cropping, inspect its page boxes in a PDF inspection tool.
- Use the renderer’s visible geometry. Create the PDF.js viewport using the page and the scale and rotation used for rendering. The resulting dimensions are the dimensions your browser layer must use.
- Check nonzero origins. A box rectangle can begin at an origin other than (0, 0). That affects position mapping even when the width and height look familiar.
- Validate a known point. Place a temporary marker at a corner or other known PDF coordinate and verify it after rotation and scaling.
Common failure modes and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Overlay has the right shape but is uniformly too large or small. | HTML uses raw PDF points while the render uses a scale other than 1. | Set both wrapper dimensions from the final viewport and use that same viewport for rendering. |
| Portrait content appears landscape, or vice versa. | Rotation was applied to the canvas but not to the overlay. | Pass the same rotation to getViewport and read its resulting width and height. |
| Every element is vertically displaced. | PDF bottom-left coordinates were treated as top-left CSS coordinates. | Map points through viewport.transform. |
| Only some documents are offset or clipped. | The document’s visible and physical boxes differ, or a box has a nonzero origin. | Determine whether the target is MediaBox, CropBox, or TrimBox, then inspect the page geometry. |
| Text alignment is correct but the canvas looks blurry. | The CSS size was increased instead of the canvas backing store. | Keep CSS dimensions at viewport size and multiply only canvas.width and canvas.height by devicePixelRatio. |
| Different pages in one file need different overlay sizes. | Each page has its own viewport. | Call getViewport separately for every page and size each page wrapper independently. |
| Dimensions change after a responsive resize. | The application changed scale, rotation, or device-pixel handling. | Recreate the viewport and update both the canvas and HTML layer together. |
Performance and reliability considerations
Render only the pages you need
Fetching a document and rendering every page is unnecessary when an overlay targets one page. Retrieve the required page, create its viewport, and defer other pages until they enter the viewing area.
Keep layout and raster work separate
Changing the CSS wrapper size should not require changing the PDF scale unless you intentionally want a different rendered resolution. For zoom controls, recalculate the viewport, resize the canvas backing store, and reposition the overlay as one operation.
Use deterministic settings for exports
For screenshots or generated images, fix scale, rotation, and page selection. Record those values with the output so a later process can reproduce the same geometry. If the source PDF changes its page boxes, previously stored overlay coordinates may no longer align.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteRank #4
Or skip the browser setup
If your real task is obtaining a clean image or PDF of an HTML target rather than placing live HTML over a PDF.js canvas, ScreenshotNeo provides a single request. It accepts a URL and can return PNG, JPEG, WebP, or PDF. Cookie and consent banners are accepted and removed before capture, along with more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers.
For the complete parameter list and current request behavior, see the ScreenshotNeo documentation.
cURL
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)
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 data = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', data));
ScreenshotNeo also supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, device presets or custom viewports, retina scale, PDF paper sizes and margins, custom CSS and JavaScript, clicks before capture, selector or network-idle waits, request blocking, headers and cookies, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, and a usage API. An 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 without a card.
Practical checklist
- Choose
MediaBox,CropBox, orTrimBoxbased on the output you are matching. - Create one PDF.js viewport with the exact scale and rotation used for rendering.
- Set HTML width and height from
viewport.widthandviewport.height. - Keep CSS dimensions separate from the high-density canvas backing dimensions.
- Map PDF positions through the viewport transform instead of manually flipping the y-axis.
- Recompute geometry per page and whenever scale or rotation changes.
Frequently Asked Questions
Does PDF.js’s 72 DPI default equal a printer’s physical DPI?
No. It is the viewport’s default pixel interpretation of PDF units. Printing software can apply its own physical scaling, so use the PDF box and print settings required by that workflow.
Can I align to TrimBox while displaying CropBox?
Yes, but treat them as separate coordinate regions. Size the displayed wrapper from the viewport you render, then apply the TrimBox’s offset and dimensions when positioning the finished-area overlay.
Why does a page-size label such as A4 not guarantee identical pixel dimensions?
A named paper size describes a physical format. PDF.js pixel dimensions still depend on the viewport scale and rotation, and the document’s page boxes can add or remove visible margins.
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.

