The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →The reliable image-first workflow is: render a DOM element with html2canvas, await its canvas, convert the canvas to PNG data, place that image in a jsPDF document with explicit dimensions, and save the PDF. For a mostly automatic HTML-to-PDF conversion, use jsPDF’s html() method instead. The image route gives you the rendered pixels and precise image placement; the integrated route handles more of the PDF layout for you.
Choose the right jsPDF workflow
There are two practical implementations. Both render the page visually, so the result depends on browser rendering and html2canvas CSS support rather than on a full browser print engine.
| Need | Recommended method | What you control | Main limitation |
|---|---|---|---|
| Turn an existing element into a PDF quickly | pdf.html(element, options) |
Page size, margins, dimensions, html2canvas options and PDF output | Rendering still follows html2canvas’s supported CSS subset |
| Obtain image data or position a raster capture yourself | html2canvas() followed by addImage() |
Image format, coordinates, scaling and page composition | You must handle tall content and pagination |
Use html() when your goal is simply an HTML element in a PDF. Use the explicit canvas path when you also need a PNG, want to crop or resize the result, or need to compose several images on custom pages. An image added with addImage() is raster content: text in that portion is not ordinary selectable PDF text.
Install the dependencies
In a browser application, install jsPDF and html2canvas:
#1 Best Overall
npm install jspdf html2canvas
Import both modules in your JavaScript entry point. If you pass an HTML string to jsPDF’s HTML module instead of a DOM element, the module also relies on DOMPurify. Prefer a real, sanitized DOM element whenever possible, especially when any markup or data originated outside your application.
Image-first: render a DOM element, then add it to a PDF
This complete example captures the element with id invoice, converts the canvas to PNG data, scales it to the PDF’s usable width, and saves a one-page file.
import { jsPDF } from "jspdf";
import html2canvas from "html2canvas";
async function saveElementAsPdf(element) {
if (!(element instanceof HTMLElement)) {
throw new TypeError("saveElementAsPdf expects a DOM element");
}
const canvas = await html2canvas(element, {
backgroundColor: "#ffffff",
scale: Math.min(window.devicePixelRatio || 1, 2),
useCORS: true
});
const imageData = canvas.toDataURL("image/png");
const pdf = new jsPDF({
orientation: "portrait",
unit: "mm",
format: "a4"
});
const pageWidth = pdf.internal.pageSize.getWidth();
const pageHeight = pdf.internal.pageSize.getHeight();
const margin = 10;
const usableWidth = pageWidth - margin * 2;
const imageHeight = (canvas.height * usableWidth) / canvas.width;
pdf.addImage(
imageData,
"PNG",
margin,
margin,
usableWidth,
Math.min(imageHeight, pageHeight - margin * 2),
undefined,
"FAST"
);
pdf.save("capture.pdf");
}
document.querySelector("#save-pdf").addEventListener("click", async () => {
const button = document.querySelector("#save-pdf");
button.disabled = true;
try {
await saveElementAsPdf(document.querySelector("#invoice"));
} catch (error) {
console.error("PDF capture failed", error);
alert("The PDF could not be created. Check the console for details.");
} finally {
button.disabled = false;
}
});
The Math.min() in this deliberately minimal version prevents the image from being drawn below the page. It also means a capture taller than one page is clipped. Do not use this one-page logic for a report, invoice or article that can grow beyond the page; choose one of the pagination approaches below.
Keep the capture stable before rendering
- Wait for fonts and important images before calling
html2canvas. - Give the target element a fixed or predictable width so line wrapping does not change between screens.
- Set a solid
backgroundColorwhen transparent page backgrounds would make text hard to read. - Use
scalefor sharper output, but watch memory use for large elements. - Use
useCORS: trueonly when remote image servers send suitable CORS headers; it cannot bypass browser security rules.
await document.fonts.ready;
await Promise.all(
[...document.querySelectorAll("#invoice img")].map((img) =>
img.complete
? Promise.resolve()
: new Promise(resolve => {
img.addEventListener("load", resolve, { once: true });
img.addEventListener("error", resolve, { once: true });
})
)
);
Paginate a tall canvas
A canvas is one bitmap. To make a multi-page image PDF, slice that bitmap into page-height sections and add each section at the same displayed width. The following function uses an offscreen canvas for each slice and adds pages until the source is exhausted.
import { jsPDF } from "jspdf";
function canvasToMultiPagePdf(canvas, filename = "capture.pdf") {
const pdf = new jsPDF({ unit: "mm", format: "a4", orientation: "portrait" });
const margin = 10;
const pageWidth = pdf.internal.pageSize.getWidth();
const pageHeight = pdf.internal.pageSize.getHeight();
const outputWidth = pageWidth - margin * 2;
const outputHeight = pageHeight - margin * 2;
const pixelsPerPage = Math.floor(canvas.width * outputHeight / outputWidth);
let sourceY = 0;
let page = 0;
while (sourceY < canvas.height) {
const sliceHeight = Math.min(pixelsPerPage, canvas.height - sourceY);
const slice = document.createElement("canvas");
slice.width = canvas.width;
slice.height = sliceHeight;
const context = slice.getContext("2d");
context.drawImage(
canvas,
0, sourceY, canvas.width, sliceHeight,
0, 0, canvas.width, sliceHeight
);
if (page > 0) pdf.addPage();
const displayedHeight = sliceHeight * outputWidth / canvas.width;
pdf.addImage(
slice.toDataURL("image/png"),
"PNG",
margin,
margin,
outputWidth,
displayedHeight,
undefined,
"FAST"
);
sourceY += sliceHeight;
page += 1;
}
pdf.save(filename);
}
Bitmap slicing can split a heading, table row or paragraph at a page boundary. If readable page breaks matter, divide the source DOM into page-sized sections before rendering, or use jsPDF’s HTML pipeline with page-break options and inspect the output. Scaling an entire long page onto one sheet preserves all pixels but usually makes the text impractical to read.
Rank #2
Use jsPDF’s integrated html() method
For a normal element-to-PDF operation, this shorter route lets jsPDF coordinate the HTML rendering pipeline:
import { jsPDF } from "jspdf";
async function saveWithHtmlModule() {
const element = document.querySelector("#invoice");
const pdf = new jsPDF({ unit: "mm", format: "a4", orientation: "portrait" });
await pdf.html(element, {
margin: [10, 10, 10, 10],
autoPaging: "text",
html2canvas: {
scale: 2,
useCORS: true,
backgroundColor: "#ffffff"
},
callback: (document) => document.save("invoice.pdf")
});
}
saveWithHtmlModule().catch(console.error);
Option names and behavior can vary with the jsPDF version installed, so check the API documentation for that version. The HTML module can render through a prepared container and html2canvas, or use jsPDF’s PDF context. Margins, page dimensions, and html2canvas settings all affect the result. Test the exact CSS, browser and package versions used in production.
CSS, images and browser constraints
html2canvas resolves asynchronously and returns a canvas, but it does not reproduce every CSS property. Its documentation notes that each property must be implemented individually, so unsupported properties can be missing or visually different. Modern evergreen browsers such as Chrome/Chromium, Firefox and Safari are the practical target, yet browser-specific rendering differences remain.
Free tools Windows power users keep installed
One-click scans. No signup required.
- Cross-origin images: images without permissive CORS headers can be omitted or make the canvas unusable. Proxy them through your own origin or configure the asset server.
- Web fonts: wait for
document.fonts.ready; otherwise fallback fonts alter line breaks and height. - Animations and videos: pause animations and use a poster frame for video if a deterministic capture is required.
- Fixed positioning: test sticky and fixed elements; their visual position may differ from a browser screenshot or print layout.
- Large canvases: high
scalevalues multiply width, height and memory. Lower the scale or capture sections when mobile devices run out of memory. - Transparency: PNG can preserve transparent pixels, but a PDF page often needs a deliberate background color for consistent printing.
Security and dependency maintenance
A jsPDF security advisory published March 17, 2026 reports HTML injection in certain output() overloads when attacker-controlled options reach those methods. Versions through 4.2.0 are listed as affected; version 4.2.1 is identified as the fix. Upgrade to a patched release and do not pass untrusted filenames, viewer URLs or other options into affected overloads. This advisory concerns particular output() options; it is separate from html2canvas’s CSS-support limitations and is not a claim that ordinary html() rendering is itself vulnerable.
Sanitize untrusted HTML before inserting it into the DOM. Avoid treating a PDF filename, URL or viewer option supplied by a user as trusted configuration. Lock and regularly review your jsPDF and html2canvas versions, then test PDF generation after upgrades.
Performance, reliability and output choices
PNG versus JPEG
PNG keeps text, lines and flat colors crisp and supports transparency, but files are larger. JPEG can be smaller for photographic content, but compression artifacts appear around text and it does not preserve transparency. Choose the format passed to toDataURL() and the matching type passed to addImage().
Reduce work before capture
- Capture only the required subtree instead of the entire document.
- Resize oversized source images before they enter the DOM.
- Use a moderate scale and measure memory on your lowest-powered supported device.
- Move repeated captures to a queued job rather than starting many high-resolution canvases at once.
- For deterministic server-side captures, use a browser-based screenshot service instead of depending on a user’s viewport, fonts and network.
Validate the resulting file
Open the PDF in more than one viewer, check every page boundary, confirm remote images and fonts loaded, and verify that the saved dimensions match your intended paper size. Compare a representative long document, not only a short card that fits on one page.
Troubleshooting
The PDF is blank
Make sure the element exists and is visible when capture starts. Await the html2canvas promise, wait for fonts and images, and inspect browser console errors. A zero-width or zero-height element produces no useful image.
Images are missing or the canvas is tainted
Check the image response’s CORS headers. Host the assets on the same origin, configure the asset server for the requesting origin, or proxy the files. useCORS: true helps only when the server permits the request.
Text or CSS looks different
Identify unsupported CSS, wait for web fonts, and use stable dimensions. Replace effects that do not render reliably with simpler backgrounds, borders and layouts. Inspect the output in the browser you support rather than assuming pixel-perfect parity.
Rank #4
Only the top of a long document appears
The one-page image example intentionally limits its height. Use canvas slicing, render separate page sections, or configure the HTML module’s paging behavior. Do not merely increase the PDF image height beyond the page.
The browser becomes unresponsive
Lower the capture scale, reduce the element’s pixel dimensions, split the document, and avoid several simultaneous high-resolution captures. Large canvases consume memory proportional to their pixel count.
The download never starts
Call the save operation after the asynchronous render completes and from a user-initiated action where browser download policies require it. Catch and log rejected promises so a rendering failure is not mistaken for a download problem.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If you need a clean screenshot or PDF from a URL rather than a DOM element already in your application, ScreenshotNeo provides a website screenshot API and MCP server. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; 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.
One request returns PNG, JPEG, WebP or a PDF. The API supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper size/margins/orientation/page ranges, HTML/CSS to image, custom JavaScript and CSS, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Common parameter names used by other screenshot APIs also work.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
For AI workflows, its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.
Best Value
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 options and response handling. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is included on every plan. Create a free ScreenshotNeo account.
Equivalent calls from Python and Node.js
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('fs').writeFileSync('shot.webp', data);
These API examples capture the supplied URL directly. If your objective is a PDF assembled from an in-browser, user-specific DOM state, keep the jsPDF workflow; if it is a repeatable URL capture, the API avoids browser setup and exposes a verdict for each response.
Frequently Asked Questions
Does the image-first PDF preserve selectable text?
No. Once the canvas is encoded and inserted with addImage(), that region is a bitmap. Use the HTML module or a text-oriented PDF generation approach when selectable text is required.
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 minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Can html2canvas capture a page from another domain?
It can capture a DOM element in your page, but cross-origin assets require suitable CORS response headers or a same-origin proxy. It is not a bypass for browser security policy.
Why does my multi-page output split rows awkwardly?
Canvas slicing cuts pixels at fixed boundaries. Render deliberate page sections or use the HTML module’s paging behavior when semantic page breaks are more important than a single continuous screenshot.
The Bottom Line
Use pdf.html() for the shortest HTML-to-PDF path; use html2canvas() plus addImage() when you need the actual image and custom placement. In both cases, plan for unsupported CSS, cross-origin assets, asynchronous fonts and explicit pagination for tall content.
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.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitches




