Use one dimension as the source of truth. In addImage(), set the target width (or height), calculate the other dimension from the image’s intrinsic dimensions, and pass both values to jsPDF. For HTML converted with html2canvas, define the CSS viewport with windowWidth, map it to a PDF width, and avoid a conflicting html2canvas.scale. Independent width and height values are the usual reason images, cards, and other elements look stretched.
Why jsPDF stretches an image or element
jsPDF places raster data in the rectangle you provide. If that rectangle has a different aspect ratio from the source, the renderer scales the horizontal and vertical axes by different amounts. A 65×60 destination box, for example, will distort an image whose source ratio is not 65:60. The jsPDF issue tracker documents this exact symptom and the ratio-preserving calculation: issue 3401.
There are two different workflows:
- Direct image placement: you already have PNG, JPEG or WebP data and call
addImage(). Preserve the source ratio mathematically. - HTML rendering:
doc.html()uses html2canvas to lay out a DOM element, rasterize it and scale the result into the PDF. Here, CSS pixels, canvas pixels and PDF units must be coordinated.
Decide first whether you want to preserve one complete image’s ratio or reflow an HTML layout to a page width. Those are different goals; forcing an HTML screenshot into a fixed-height box is not a ratio fix.
Preserve aspect ratio with addImage()
Calculate the missing dimension
Read the source dimensions with getImageProperties(). Choose a target width and derive the height:
#1 Best Overall
- 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.
const props = pdf.getImageProperties(imgData);
const targetWidth = 180; // PDF units; millimetres if unit: 'mm'
const targetHeight = (props.height * targetWidth) / props.width;
pdf.addImage(imgData, 'PNG', x, y, targetWidth, targetHeight);
The equivalent height-first formula is targetWidth = (props.width * targetHeight) / props.height. Never guess both dimensions unless you intentionally want cropping or distortion.
Complete browser example
import { jsPDF } from 'jspdf';
async function makePdf(imgData) {
const pdf = new jsPDF({ unit: 'mm', format: 'a4', orientation: 'portrait' });
const margin = 10;
const pageWidth = pdf.internal.pageSize.getWidth();
const usableWidth = pageWidth - margin * 2;
const props = pdf.getImageProperties(imgData);
const height = (props.height * usableWidth) / props.width;
pdf.addImage(imgData, 'PNG', margin, margin, usableWidth, height);
pdf.save('ratio-safe.pdf');
}
// imgData can be a data URL or a supported image source produced by your app.
If the calculated height extends below the page, keep the ratio and scale both dimensions down. For an image that must fit inside a rectangle, use a contain calculation:
const scale = Math.min(boxWidth / props.width, boxHeight / props.height);
const width = props.width * scale;
const height = props.height * scale;
const left = boxX + (boxWidth - width) / 2;
const top = boxY + (boxHeight - height) / 2;
pdf.addImage(imgData, 'PNG', left, top, width, height);
This letterboxes the image when the box has a different ratio. It does not crop. To crop, create a deliberately cropped source image first; do not solve cropping by supplying mismatched dimensions.
Fit HTML to A4 without distortion
Set the page width and CSS viewport together
The jsPDF HTML plug-in’s width option is the target width in jsPDF units. The rendered element is scaled to fit that width. windowWidth is the CSS-pixel viewport used while html2canvas lays out the container. Supplying both gives the renderer a known layout width and a known PDF width. The official options are documented at jsPDF’s HTML plug-in reference.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallconst pdf = new jsPDF({ unit: 'mm', format: 'a4', orientation: 'portrait' });
const pageWidth = pdf.internal.pageSize.getWidth();
const margin = 10;
const contentWidth = pageWidth - margin * 2;
const element = document.querySelector('#content');
pdf.html(element, {
x: margin,
y: margin,
width: contentWidth,
windowWidth: element.scrollWidth,
autoPaging: 'text',
callback: doc => doc.save('document.pdf')
});
Do not add an explicit html2canvas.scale when relying on width plus windowWidth. The jsPDF documentation notes that width has no effect when html2canvas.scale is specified or when windowWidth is omitted, so those settings can make an apparently correct width calculation ineffective.
Choose a sensible CSS layout width
Your HTML should have a stable content width (for example, a report column with a max-width) rather than a responsive layout that changes at the capture viewport. Measure the actual element after fonts and images have loaded:
Rank #2
- 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 element = document.querySelector('#content');
console.log({
cssWidth: element.scrollWidth,
cssHeight: element.scrollHeight,
rect: element.getBoundingClientRect()
});
Use the measured scrollWidth for windowWidth. If the page should reflow to a narrower paper column, change the CSS layout width intentionally and let text wrap; do not squeeze a wide screenshot into a narrow, fixed-height rectangle.
Prevent text and cards being cut at page breaks
For mostly text-based documents, autoPaging: 'text' asks jsPDF to avoid cutting text in half. The default true (also described as 'slice') can split shapes or text chunks at a page boundary. Neither mode can repair a canvas that was already clipped before jsPDF received it, so solve viewport and canvas-size problems first.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Control html2canvas’s pixel and viewport settings
html2canvas distinguishes the layout viewport from the raster canvas. Its configuration reference lists scale, width, height, windowWidth, windowHeight, x and y: official configuration documentation.
scalecontrols raster resolution and defaults towindow.devicePixelRatio. A larger value can improve detail but increases memory use and canvas dimensions.widthandheightset the canvas dimensions; they are not PDF millimetres.windowWidthandwindowHeightaffect CSS layout and media queries.xandycrop from the source element.
A reliable direct-capture pattern for a long element is:
const canvas = await html2canvas(element, {
windowWidth: element.scrollWidth,
windowHeight: element.scrollHeight
});
Then convert the resulting canvas to an image and apply the ratio-preserving addImage() calculation. Keep the canvas ratio intact even when splitting it across PDF pages.
Diagnose blank, blurry, oversized or partial PDFs
| Symptom | Likely cause | Fix |
|---|---|---|
| Image looks wide or tall | Independent destination width and height | Read intrinsic properties and calculate the second dimension. |
| HTML is unexpectedly narrow or wide | Missing windowWidth, or a conflicting html2canvas.scale |
Set width and windowWidth together; remove the scale override. |
| Bottom of a long page is missing | Capture viewport does not include the element’s scroll dimensions | Use windowWidth: element.scrollWidth and windowHeight: element.scrollHeight. |
| Blank or partially rendered PDF | Browser canvas dimension or area limit exceeded | Capture smaller sections or pages and combine them; check the browser and device limits. |
| Correct in browser, wrong in PDF | CSS property is not implemented by html2canvas | Simplify unsupported effects or isolate the content to supported CSS. |
| Images disappear | Cross-origin image taints the canvas | Use useCORS: true only when the image server sends an appropriate Access-Control-Allow-Origin header, or use a same-origin proxy. |
| Output is huge or blurry | Scale, CSS pixels and PDF units are being treated as the same unit | Choose the CSS layout width first, select a practical raster scale, then map the result to PDF units while preserving its ratio. |
Canvas limits are real
The html2canvas FAQ reports rough current evergreen-browser maximum dimensions of about 32,767 pixels for Chrome/Chromium, Firefox and desktop Safari, with lower limits on iOS Safari; maximum total area varies by browser and platform. These are guidance figures, not guarantees. When a canvas exceeds a limit, the browser can silently return blank or partially rendered output without throwing an error. See the html2canvas FAQ.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Rank #3
- 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.
For very long reports, render one logical page or section at a time. This reduces peak memory and avoids asking one canvas to contain the entire document. Validate the result at the browser sizes you support, because media queries and image loading can change the measured dimensions.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Cross-origin assets and unsupported CSS
html2canvas does not reproduce every CSS feature. Filters, complex effects or browser-only layout behavior can differ from the on-screen page even when your jsPDF coordinates are correct. Replace unsupported effects with simpler CSS in a print-specific container when fidelity matters.
For remote images, configure the origin to send the required CORS header before enabling useCORS. If you cannot change that server, proxy the asset through your own origin. A missing header cannot be fixed by changing image width or height.
A repeatable troubleshooting sequence
- Log
scrollWidth,scrollHeightand the element’s bounding rectangle after fonts and images finish loading. - Choose the goal: preserve a complete source ratio, or reflow HTML to a page width.
- For images, calculate one destination dimension from
getImageProperties(); use a contain calculation when fitting a box. - For
doc.html(), setwidthandwindowWidthtogether and remove an overridinghtml2canvas.scale. - For direct html2canvas, match
windowWidthandwindowHeightto the element’s scroll dimensions when content is clipped. - If output is blank, reduce the capture into sections and check canvas dimension and area limits.
- If assets are missing, verify CORS headers; if layout differs, check whether the CSS is supported.
- Validate several source aspect ratios, page orientations and viewport widths in every target browser.
Or skip the browser setup
ScreenshotNeo is a hosted website screenshot API and MCP server. It accepts a URL and returns PNG, JPEG, WebP or PDF without requiring you to manage a browser. 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, 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.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for all options. The same request in 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)
And in 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}`);
ScreenshotNeo also supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, retina scale, PDF paper size/margins/orientation/page ranges, custom CSS and JavaScript, clicks, selector or network-idle waits, request and resource blocking, custom headers/cookies/user agents and Authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, 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 work as well, which can simplify migration. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.
| Plan | Included shots | Price |
|---|---|---|
| Free | 1,000 per month | $0, no card |
| Starter | 3,000 | $5 |
| Growth | 15,000 | $15 |
| Pro | 60,000 | $39 |
| Scale | 250,000 | $99 |
| Business | 1,000,000 | $249 |
All features are included on every plan, and annual billing provides two months free. Create a free ScreenshotNeo account to get 1,000 screenshots each month with no card; paid plans start at $5 for 3,000 shots.
Quick Recap
Key takeaways for ratio-safe PDFs
- For
addImage(), derive height from width (or width from height) using the source dimensions. - For HTML, distinguish CSS layout width, canvas pixels and PDF units; set jsPDF
widthand html2canvaswindowWidthdeliberately. - Use scroll dimensions for long captures, split documents before browser canvas limits, and treat CORS and unsupported CSS as separate rendering problems.
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.

