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

The usual fix is to separate capture resolution from PDF placement size. Start html2canvas at scale: 1, measure the resulting bitmap, then pass jsPDF dimensions in the PDF unit (such as millimetres), not in canvas pixels. Preserve the bitmap’s aspect ratio when calling addImage. A Retina display commonly makes html2canvas default to window.devicePixelRatio (often 2), so the bitmap can be twice as wide and tall as the CSS element. That is a resolution difference, not permission to enlarge the image in the PDF.

This guide uses “canvas2html” as a possible wrapper or local name for html2canvas. There is no canonical package identified as canvas2html; check which library your project actually imports before copying an option name.

Understand the four sizes before changing code

Zoomed exports happen when values from different coordinate systems are treated as interchangeable. Log all four of these measurements:

Measurement What it means Typical unit What can change it
Element CSS size The layout box reported by the browser CSS pixels Styles, responsive layout, browser zoom and viewport width
Canvas bitmap size The actual raster pixels in the canvas Bitmap pixels Element size multiplied by html2canvas scale
window.devicePixelRatio Device backing-pixel ratio used by html2canvas by default Ratio Display density, operating-system scaling and browser zoom
jsPDF placement size The rectangle reserved for the image on the PDF page The PDF unit selected at construction addImage coordinates, doc.html width and page margins

For example, a 600 CSS-pixel element captured at scale 2 produces a canvas about 1,200 pixels wide. If you then call addImage(data, 'PNG', 10, 10, 600, ...) in a PDF created with unit: 'mm', the number 600 means 600 millimetres, not 600 pixels. That combination makes the image appear enormous.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
PDF Extra 2024| Complete PDF Reader and Editor | Create, Edit, Convert, Combine, Comment, Fill & Sign PDFs | Lifetime License | 1 Windows PC | 1 User [PC Online code]
  • EDIT text, images & designs in PDF documents. ORGANIZE PDFs. Convert PDFs to Word, Excel & ePub.
  • READ and Comment PDFs – Intuitive reading modes & document commenting and mark up.
  • CREATE, COMBINE, SCAN and COMPRESS PDFs
  • FILL forms & Digitally Sign PDFs. PROTECT and Encrypt PDFs
  • LIFETIME License for 1 Windows PC or Laptop. 5GB MobiDrive Cloud Storage Included.

Use a scale-1 baseline

  1. Capture the element with an explicit scale of one:
const canvas = await html2canvas(element, { scale: 1 });
  1. Compare canvas.width and canvas.height with the element’s CSS dimensions. They should be close for a normal, unclipped capture.
  2. Only increase scale deliberately when you need more raster detail. A scale of 2 quadruples the number of pixels and increases memory use; it does not require a larger PDF placement rectangle.

Do not “fix” a large PDF by dividing arbitrary numbers until it looks right. First identify whether the bitmap is already too large or whether jsPDF is receiving the wrong placement unit.

Complete html2canvas-to-jsPDF export

The following example captures one element, calculates a proportional height from the image itself, and places it inside 10-unit margins. The PDF uses millimetres, so every placement value is a millimetre value.

async function exportElement() {
  const element = document.querySelector('#invoice');
  if (!element) throw new Error('Missing #invoice element');

  const css = element.getBoundingClientRect();
  const canvas = await html2canvas(element, {
    scale: 1,
    useCORS: true,
    backgroundColor: '#ffffff'
  });

  console.log({
    cssWidth: css.width,
    cssHeight: css.height,
    bitmapWidth: canvas.width,
    bitmapHeight: canvas.height,
    devicePixelRatio: window.devicePixelRatio
  });

  const pdf = new jsPDF({ unit: 'mm', format: 'a4' });
  const data = canvas.toDataURL('image/png');
  const image = pdf.getImageProperties(data);
  const margin = 10;
  const pageWidth = pdf.internal.pageSize.getWidth();
  const usableWidth = pageWidth - (margin * 2);
  const usableHeight = image.height * usableWidth / image.width;

  pdf.addImage(data, 'PNG', margin, margin, usableWidth, usableHeight);
  pdf.save('export.pdf');
}

exportElement().catch(console.error);

getImageProperties returns the raster dimensions that jsPDF will use for the aspect-ratio calculation. The formula height = image.height * width / image.width prevents stretching. If the content is taller than one page, the proportional height is still correct; you must then paginate or render sections rather than force the entire image into a page.

Keep doc.html width separate from raster scale

jsPDF’s HTML API exposes two different controls. The nested html2canvas options determine how the browser rasterizes the content. The width option determines the target fit in PDF units. The documented behavior is that target width has no effect when html2canvas.scale is explicitly specified.

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.
const pdf = new jsPDF({ unit: 'mm', format: 'a4' });
await pdf.html(document.querySelector('#invoice'), {
  margin: [10, 10, 10, 10],
  width: 190,
  html2canvas: {
    scale: 1,
    useCORS: true
  },
  callback: (doc) => doc.save('html-export.pdf')
});

Choose one policy and document it in your code: either let doc.html fit the content by its PDF width, or explicitly control the html2canvas scale and place the resulting image yourself. Mixing a device-pixel scale with pixel-like PDF numbers is the common source of apparent zoom.

Account for browser zoom and display density

Browser zoom and operating-system display scaling can alter the relationship between CSS pixels and physical pixels. html2canvas’s default scale is window.devicePixelRatio, so the same page can produce different bitmap dimensions on different screens. A fixed scale: 1 makes the export’s raster dimensions deterministic relative to the CSS layout, although responsive CSS can still change when the viewport width changes.

If you intentionally want sharper output, choose a known scale and keep PDF placement based on the image’s aspect ratio:

Rank #2
MobiPDF Lifetime - Professional PDF Editor for Windows | Edit, Sign & Convert PDFs | Best Adobe Acrobat Pro Alternative | Lifetime License
  • Edit PDFs with Ease. Modify text, images, and layouts directly within your PDF documents.
  • Convert & Organize. Export PDFs to Word, Excel, or ePub, and organize files with ease.
  • Read & Annotate. Enjoy intuitive reading modes and powerful tools to comment, highlight, and mark up PDFs.
  • Create & Manage PDFs. Create new PDFs, combine multiple files, scan documents, and compress for easy sharing.
  • Fill & Sign Forms. Complete forms and digitally sign documents with secure e-signature tools.
const scale = 2; // intentional quality choice, not a PDF size
const canvas = await html2canvas(element, { scale });
const data = canvas.toDataURL('image/png');
const props = pdf.getImageProperties(data);
const width = 190;
const height = props.height * width / props.width;
pdf.addImage(data, 'PNG', 10, 10, width, height);

Do not use screen.width or a monitor’s physical resolution to calculate PDF dimensions. Use the element’s layout dimensions for capture and the PDF page’s units for placement.

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

Load remote images without tainting the canvas

Images from another origin can be skipped or make the canvas unreadable under browser security rules. useCORS: true works only when the image server sends the appropriate CORS response header. If it does not, serve the asset from the same origin or fetch it through a server-side proxy that you control.

const canvas = await html2canvas(element, {
  scale: 1,
  useCORS: true,
  imageTimeout: 15000
});

Make sure images are loaded before starting the capture. A CORS option cannot repair a server that does not grant cross-origin access, and converting the canvas to a data URL will fail if the bitmap has already been tainted.

Prevent clipping on long or responsive pages

A very tall element can exceed browser canvas limits even when the JavaScript is correct. If the bottom is missing, capture page-sized sections instead of one giant bitmap. For a responsive component, set capture dimensions to its scrollable size:

const width = element.scrollWidth;
const height = element.scrollHeight;
const canvas = await html2canvas(element, {
  scale: 1,
  windowWidth: Math.max(window.innerWidth, width),
  windowHeight: Math.max(window.innerHeight, height),
  width,
  height
});

For multi-page PDFs, render a header and each content slice separately, or use jsPDF’s HTML pagination features. Never compensate for clipping by enlarging the remaining image.

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

Audit drawImage source rectangles

Custom canvas code can enlarge or crop an image before jsPDF ever sees it. When calling drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight), verify that the source rectangle describes the intended pixels and that destination dimensions are in the canvas coordinate system. jsPDF’s context2d implementation applies separate clipping and scale factors, so a source rectangle that is too small or a destination rectangle that is too large produces a genuine zoom.

  • For a full-image copy, omit the source rectangle and provide only destination coordinates and size.
  • Keep source width and height proportional to destination width and height.
  • Log every rectangle when the exported image is cropped or magnified.

Diagnostic checklist

  1. Log getBoundingClientRect(), canvas dimensions and window.devicePixelRatio.
  2. Run once with scale: 1 and browser zoom at 100 percent.
  3. Confirm the PDF’s unit in new jsPDF({ unit: ... }).
  4. Inspect every addImage width and height; they are PDF units, not pixels.
  5. Calculate one dimension from the other using the image properties.
  6. Test with local, same-origin images before debugging CORS.
  7. Capture a short element before attempting a full document.
  8. Check custom drawImage source and destination rectangles.

Common symptoms and precise fixes

Symptom Likely cause Fix
Bitmap width and height are exactly about twice the CSS size Default scale follows a Retina devicePixelRatio of 2 Set scale: 1, or keep scale 2 intentionally and size the PDF from image properties
Bitmap dimensions look correct, but the PDF is huge Pixel values were passed as millimetres, points or inches Use the PDF page width in its declared unit and calculate the proportional height
Image is stretched Independent width and height values were supplied Derive height from props.height * width / props.width
Remote images disappear or toDataURL throws a security error Missing CORS header or cross-origin canvas taint Use useCORS: true with a correctly configured server, or use a same-origin proxy
Bottom of a long page is missing Canvas or browser dimension limit Match capture dimensions to scroll dimensions and split content into page-sized sections
Only a selected region is enlarged or cropped Incorrect sx, sy, sWidth or sHeight Log the source rectangle and make destination dimensions proportional
Output changes between monitors Implicit device-pixel scaling or responsive layout Set an explicit scale and a controlled viewport; avoid monitor pixels in PDF calculations

Quality, speed and memory trade-offs

Scale is a quality setting. Increasing it can make text and fine lines sharper, but pixel count and memory grow rapidly. Start at 1, compare the result at the final PDF viewing size, and increase only if detail is genuinely insufficient. Prefer JPEG for photographic content when file size matters, and PNG for diagrams, text and transparency. A transparent PDF background is not automatic: choose the html2canvas background behavior and the PDF image format deliberately.

Rank #3
Scrivar PDF Pro - Organize, Edit, Compress, Convert, Merge, eSign, OCR & 30+ tools | Lifetime License
  • EVERY PDF TOOL UNLOCKED - 30+ tools in one app: edit text and images, convert, merge, split, compress, sign, OCR, redact, watermark, batch process, and more. No feature gates, no upsells, nothing held back.
  • PAY ONCE, OWN FOREVER — A one-time purchase, not a subscription. Other apps runs $240/year — Scrivar is yours for life, with free updates included.
  • UNLIMITED eSIGN, BUILT IN — Send contracts and forms for signature and track every step. Recipients sign in their browser with no account or app needed. Replace DocuSign and save hundreds a year.
  • PC, MAC, AND WEB — Install on any Win 10/11 PC or macOS 11+ Mac (Intel or Apple Silicon), or work in your browser at scrivar.com. Same tools, same account, everywhere you work.
  • OCR + FULL OFFICE CONVERSION — Turn scanned documents into searchable, selectable text, and convert PDFs to and from Word, Excel, and PowerPoint with formatting kept intact.

Large pages should be segmented. Smaller captures reduce peak memory, make retries practical and let you identify the exact section that fails. Wait for fonts, images and application data to settle before capture; otherwise a correctly sized canvas can still contain incomplete content.

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 provides a website screenshot API and MCP server when you need a rendered page rather than a hand-tuned browser pipeline. One GET request returns PNG, JPEG, WebP or PDF. Before capture it accepts the cookie or consent banner like a visitor and removes 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 result with X-Page-Verdict and X-Billed headers. Its MCP tools—take_screenshot, get_page_info and capture_pdf—work with Claude, Cursor and other MCP clients.

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

Use the documented API parameters and options for full-page captures, lazy-image loading, CSS-selector element capture, dark mode, device presets or custom viewports, retina scale, PDF paper and margins, custom CSS or JavaScript, click and wait actions, request blocking, headers, cookies, user agents, Authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting and the OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify a migration.

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

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

Frequently Asked Questions

Does setting html2canvas scale to 1 reduce CSS dimensions?

No. It changes the raster pixel density only; the captured layout remains based on the element and viewport CSS dimensions.

Should I divide addImage dimensions by devicePixelRatio?

Not as a general rule. Choose PDF-unit dimensions from the page geometry and preserve the image aspect ratio; devicePixelRatio belongs to capture resolution.

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

Can useCORS bypass a server’s access policy?

No. The image server must send a suitable CORS header, or the asset must be same-origin or proxied.

Why does a full-page capture fail only on very long documents?

Browser canvas dimensions and memory are finite. Split the document into page-sized captures and place those sections on separate PDF pages.

Quick Recap

Bestseller No. 1
PDF Extra 2024| Complete PDF Reader and Editor | Create, Edit, Convert, Combine, Comment, Fill & Sign PDFs | Lifetime License | 1 Windows PC | 1 User [PC Online code]
PDF Extra 2024| Complete PDF Reader and Editor | Create, Edit, Convert, Combine, Comment, Fill & Sign PDFs | Lifetime License | 1 Windows PC | 1 User [PC Online code]
READ and Comment PDFs – Intuitive reading modes & document commenting and mark up.; CREATE, COMBINE, SCAN and COMPRESS PDFs
$99.99
Bestseller No. 2
MobiPDF Lifetime - Professional PDF Editor for Windows | Edit, Sign & Convert PDFs | Best Adobe Acrobat Pro Alternative | Lifetime License
MobiPDF Lifetime - Professional PDF Editor for Windows | Edit, Sign & Convert PDFs | Best Adobe Acrobat Pro Alternative | Lifetime License
Edit PDFs with Ease. Modify text, images, and layouts directly within your PDF documents.; Convert & Organize. Export PDFs to Word, Excel, or ePub, and organize files with ease.
$99.99

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.