October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
Canvas

How to Take a Screenshot of a PDF Page with JavaScript (PDF.js)

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

Use Mozilla PDF.js to render the PDF page into an HTML canvas, then export that canvas as PNG or JPEG. The reliable sequence is: load the document, call pdf.getPage(pageNumber), create a viewport with page.getViewport({ scale }), size the canvas, await page.render(), and finally call canvas.toDataURL() or canvas.toBlob(). This captures the page content—not the browser’s built-in PDF plug-in controls—and works in a normal HTTP(S) application.

The complete browser workflow

PDF.js has a low-level display layer intended for custom rendering. It converts PDF coordinates (whose origin is at the bottom-left) into canvas coordinates (top-left) through the viewport and render transform. Render only the page or pages you need, wait for the render promise, and export after drawing has finished.

1. Load PDF.js and its worker

Use matching versions of the PDF.js main library and worker. The worker performs parsing away from the main UI thread. Pin both files to the same release in your package manager or deployment bundle. Run the app from HTTP or HTTPS; the normal worker setup is not enabled for file:// pages.

2. Render and download page one

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>PDF page screenshot</title>
  <style>#pdf-canvas { max-width: 100%; height: auto; }</style>
</head>
<body>
  <canvas id="pdf-canvas"></canvas>
  <button id="save" disabled>Save PNG</button>
  <script type="module">
    import * as pdfjsLib from '/pdfjs/pdf.mjs';
    pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdfjs/pdf.worker.mjs';

    const canvas = document.querySelector('#pdf-canvas');
    const context = canvas.getContext('2d', { alpha: false });
    const save = document.querySelector('#save');

    const loadingTask = pdfjsLib.getDocument('/files/example.pdf');
    const pdf = await loadingTask.promise;
    const pageNumber = 1;
    const page = await pdf.getPage(pageNumber);

    const scale = 2;
    const viewport = page.getViewport({ scale });
    const outputScale = window.devicePixelRatio || 1;

    canvas.width = Math.floor(viewport.width * outputScale);
    canvas.height = Math.floor(viewport.height * outputScale);
    canvas.style.width = `${Math.floor(viewport.width)}px`;
    canvas.style.height = `${Math.floor(viewport.height)}px`;

    const transform = outputScale !== 1
      ? [outputScale, 0, 0, outputScale, 0, 0]
      : null;

    const renderTask = page.render({
      canvasContext: context,
      viewport,
      transform
    });
    await renderTask.promise;

    save.disabled = false;
    save.addEventListener('click', () => {
      canvas.toBlob((blob) => {
        if (!blob) return;
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = `page-${pageNumber}.png`;
        link.click();
        URL.revokeObjectURL(link.href);
      }, 'image/png');
    });
  </script>
</body>
</html>

In a bundler, import PDF.js from your installed package instead of a local /pdfjs path, and set GlobalWorkerOptions.workerSrc to the matching worker URL. The important ordering is unchanged: do not export until renderTask.promise resolves.

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

Control resolution, dimensions and orientation

Viewport scale

scale multiplies the page’s logical dimensions. A larger value creates more pixels and a sharper image, but increases rendering time, memory use and file size. For a thumbnail, use a value near 1; for a document image intended for zooming or printing, use a larger value and test the resulting dimensions.

Device-pixel ratio

The example separates the CSS display size from the canvas backing-store size. On a high-density display, window.devicePixelRatio multiplies the backing store while the CSS size remains the viewport size. Without this step, a canvas can look soft even when its logical dimensions are correct. If you are generating a fixed-size server asset, choose an explicit output scale rather than inheriting a user’s screen density.

Page rotation and crop

PDF.js includes a page’s rotation in the viewport returned by getViewport. You can pass an explicit rotation when you need to override it. A canvas screenshot is the full rendered page; cropping to a region is a separate canvas operation performed after rendering.

Export PNG or JPEG

PNG: lossless default

Use canvas.toDataURL('image/png') when text and diagrams must remain lossless. A data URL is convenient for an <img> or small result, but it temporarily keeps the entire encoded image in memory.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const pngDataUrl = canvas.toDataURL('image/png');

JPEG: smaller photographic files

JPEG can be substantially smaller when a PDF page contains photographs and some quality loss is acceptable. It is not lossless and can create ringing around small text. Prefer toBlob for uploads or downloads:

canvas.toBlob((blob) => {
  if (!blob) throw new Error('Canvas export failed');
  // Upload blob or create an object URL for download.
}, 'image/jpeg', 0.92);

The quality argument is used for lossy formats such as JPEG; PNG ignores it. Always check for a null blob and revoke object URLs after use.

Remote PDFs, CORS and deployment

A PDF loaded from another origin is subject to the browser’s same-origin policy. PDF.js does not bypass that policy. The PDF host must send suitable CORS headers, or your application must fetch the file through a server-side endpoint on an origin your page is allowed to access. A typical failure is a successful URL in a new browser tab but a blocked request inside JavaScript; that is a CORS configuration problem, not a rendering-scale problem.

Keep the PDF.js library and worker on compatible, matching versions. Serve the page, worker and PDF over HTTP(S), configure correct MIME types, and avoid opening the HTML directly from the filesystem. If documents require authorization, fetch them through an authenticated endpoint and pass the resulting data to getDocument rather than exposing private tokens in a public PDF URL.

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

Rendering multiple pages safely

For a page picker, render one selected page into a reusable canvas. For a batch export, process pages sequentially and release intermediate canvases or blobs as soon as each result is stored. Rendering every page at a large scale at once can exhaust memory.

async function renderPageToBlob(pdf, pageNumber, scale = 1.5, type = 'image/png') {
  const page = await pdf.getPage(pageNumber);
  const viewport = page.getViewport({ scale });
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d', { alpha: false });
  const outputScale = window.devicePixelRatio || 1;
  canvas.width = Math.floor(viewport.width * outputScale);
  canvas.height = Math.floor(viewport.height * outputScale);
  const transform = outputScale !== 1
    ? [outputScale, 0, 0, outputScale, 0, 0]
    : null;
  await page.render({ canvasContext: context, viewport, transform }).promise;
  return await new Promise((resolve, reject) =>
    canvas.toBlob(blob => blob ? resolve(blob) : reject(new Error('Export failed')), type)
  );
}

const loadingTask = pdfjsLib.getDocument('/files/report.pdf');
const pdf = await loadingTask.promise;
for (let pageNumber = 1; pageNumber <= pdf.numPages; pageNumber++) {
  const blob = await renderPageToBlob(pdf, pageNumber);
  // Upload or download blob here, then let the loop continue.
}

Render only visible pages in a scrolling viewer. A letter page at 96 DPI is about 816×1056 pixels and uses approximately 3.5 MB for one canvas before high-DPI multiplication; dozens of large canvases can therefore consume hundreds of megabytes. The canvas backend is the stable default. PDF.js describes its SVG backend as experimental and not feature complete, so do not select it for a production screenshot path without validating every document type.

Common failures and fixes

“Setting up fake worker” or worker-load errors

The worker URL is missing, incorrect or from a different PDF.js version. Set GlobalWorkerOptions.workerSrc to the deployed matching worker file and verify it returns JavaScript rather than an HTML error page.

Nothing appears on the canvas

Check that you awaited both getDocument(...).promise and page.render(...).promise. Confirm the canvas has non-zero width and height; setting only CSS dimensions does not create drawing pixels. Inspect network requests for a missing PDF or worker.

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

“Failed to fetch” or CORS errors

Move the PDF to the same origin, configure CORS on the PDF host, or proxy the request through your backend. Browser JavaScript cannot override a server’s cross-origin policy.

Blurry output

Increase scale, size the backing store with devicePixelRatio, and avoid enlarging a low-resolution canvas with CSS. Remember that increasing both scale and device-pixel ratio multiplies pixel count.

Tab freezes or the browser runs out of memory

Lower the scale, render sequentially, reuse one canvas, and discard finished blobs or canvases. Do not render all pages concurrently unless you have a measured memory budget.

Fonts, annotations or unusual PDFs look different

Ensure PDF.js can load its auxiliary resources and that the document is not damaged or encrypted without a password. If your feature requires interactive annotations rather than a visual screenshot, add the appropriate PDF.js annotation-layer UI; the canvas alone is a bitmap of the page.

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

If you need a screenshot or PDF conversion in a backend, CI job or AI workflow, ScreenshotNeo can capture a URL with one request. It accepts cookie and consent banners before capture, removes more than 60 known consent platforms plus newsletter popups and chat widgets, and lets you turn each cleanup step off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed; each response reports the result in X-Page-Verdict and X-Billed headers. It also provides an MCP server for Claude, Cursor and other MCP clients, with take_screenshot, get_page_info and capture_pdf tools.

One-call cURL example

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());
require('node:fs').writeFileSync('shot.webp', data);

See the ScreenshotNeo documentation for the 63 capture options, including full-page lazy-image loading, CSS-selector element capture, dark mode, device presets, retina scale, PDF paper and margin controls, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting and the OpenAPI specification. Existing parameter names used by other screenshot APIs are accepted to ease migration.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; Growth is $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000 and Business $249 for 1,000,000. Yearly billing provides two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to start without a card.

Choosing the right implementation

Need Recommended approach Reason
One page inside an existing web app PDF.js canvas No upload service; immediate control over scale and export.
Many pages in a browser viewer PDF.js with sequential rendering Virtualize visible pages to control memory.
Server, CI or automated URL capture ScreenshotNeo One HTTP call, cleanup of consent UI and explicit billing verdicts.
Lossless diagrams and text PNG No JPEG compression artifacts.
Photographic pages where size matters JPEG Smaller output at a chosen quality, with some loss.

Frequently Asked Questions

Can I screenshot the browser’s native PDF viewer with JavaScript?

Use PDF.js instead. The native plug-in UI is outside your page’s DOM and is not a dependable target for custom capture.

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

Does PDF.js bypass authentication or CORS?

No. Your server must make the PDF available under normal browser security rules, or your backend must fetch it and serve an allowed response.

Should I use PNG or JPEG for text-heavy PDFs?

PNG is the safer default for crisp text and line art; choose JPEG only when smaller files outweigh compression artifacts.

How do I capture only one page from a multi-page PDF?

Call pdf.getPage(desiredPageNumber), render that page into the canvas, and export after its render promise resolves.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.