Free tools Windows power users keep installed
One-click scans. No signup required.
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Use one PDF instance, capture each element with await html2canvas(), add each resulting canvas to that document in order, and insert pages when needed. This keeps cards, charts, or sections independent while giving you precise control over page breaks and margins. If the elements already form one document layout, capturing their common container can be simpler.
Choose the capture strategy first
| Approach | Best when | Main trade-off |
|---|---|---|
| Capture a shared container | The browser layout should stay together exactly as one composition. | One very tall canvas can exceed browser or platform limits and become blank or partially rendered. |
| Capture each element separately | Sections need controlled order, independent scaling, or separate PDF pages. | You must calculate placement, margins, and page breaks yourself. |
| Use html2pdf.js | You want an integrated client-side flow based on html2canvas and jsPDF, with documented page-break controls. | Its layout behavior still depends on html2canvas rendering and the PDF options you choose. |
html2canvas reconstructs a page from DOM information; it is not a literal browser screenshot and only supports CSS properties it understands. The project describes it as taking “screenshots” of webpages or parts of them directly in the user’s browser. The capture is asynchronous, so every canvas must resolve before you finalize the PDF.
Prerequisites and document design
- Run this in a modern evergreen browser (Chrome/Chromium, Firefox, or Safari). html2canvas depends on browser APIs and is not a Node.js server renderer.
- Install
html2canvasandjspdfin your web application. The example below uses ES-module imports, so your bundler resolves both packages. - Give each target a stable selector such as
data-pdf-section. Avoid selecting transient overlays, sticky navigation, or elements that are still animating. - Decide whether every section starts on a new PDF page. A single canvas image can also be scaled to fit a page, but shrinking very tall content may make text hard to read.
Complete JavaScript example: one element per page
This example captures elements in DOM order and places each image on its own A4 page. It uses millimetres for PDF coordinates and preserves each canvas’s aspect ratio.
import html2canvas from 'html2canvas';
import { jsPDF } from 'jspdf';
const targets = [...document.querySelectorAll('[data-pdf-section]')];
if (!targets.length) throw new Error('No PDF sections found');
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 maxWidth = pageWidth - margin * 2;
const maxHeight = pageHeight - margin * 2;
for (let index = 0; index < targets.length; index += 1) {
const canvas = await html2canvas(targets[index], {
backgroundColor: '#ffffff',
scale: Math.min(window.devicePixelRatio || 1, 2),
useCORS: true,
logging: false
});
const image = canvas.toDataURL('image/jpeg', 0.92);
const ratio = Math.min(maxWidth / canvas.width, maxHeight / canvas.height);
const width = canvas.width * ratio;
const height = canvas.height * ratio;
const x = (pageWidth - width) / 2;
const y = margin;
if (index > 0) pdf.addPage();
pdf.addImage(image, 'JPEG', x, y, width, height);
}
pdf.save('combined-sections.pdf');
The first section uses the PDF’s initial page; later sections call addPage(). If your installed jsPDF version exposes different internals or signatures, check that version’s official API documentation before shipping. The important sequence is library-independent: select, await, convert, place, add a page, then save.
#1 Best Overall
Capture all canvases before writing the PDF
Sequential capture is easiest to reason about and limits simultaneous memory use. If capture time matters, you can start promises together, but large canvases may consume substantial memory. Do not call pdf.save() until every promise has resolved and every image has been added.
Put several sections on one page
Replace the unconditional page insertion with a vertical cursor. After calculating a section’s displayed height, place it at y; when y + height exceeds pageHeight - margin, call addPage() and reset y to the top margin. Keep the same width and aspect-ratio calculation shown above. If an individual element is taller than one page, split its source layout into smaller elements or accept that it must be scaled down; an image cannot flow naturally across PDF pages without additional slicing logic.
Capturing one combined container instead
Wrap the sections in a parent whose CSS layout already matches the desired document, then capture that parent:
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →const container = document.querySelector('#invoice');
const canvas = await html2canvas(container, {
windowWidth: container.scrollWidth,
windowHeight: container.scrollHeight,
useCORS: true,
backgroundColor: '#fff'
});
Setting the window dimensions to the element’s scroll dimensions can help when content is clipped, but browser canvas limits vary. Very large width, height, or total pixel area can produce a blank or partially rendered result. For long reports, separate captures are usually safer.
Images, fonts, and browser security
Cross-origin images
An image loaded from another origin can taint the canvas. useCORS: true only helps when that image server sends a suitable Access-Control-Allow-Origin header. It cannot override the browser’s same-origin policy. If you control neither server, use a same-origin proxy that fetches the asset and serves it with appropriate CORS headers. Test the proxy’s permissions and caching rules rather than disabling browser security.
Iframes
Cross-origin iframe documents cannot be rendered because the browser prevents access to their contents. Same-origin iframe content can be traversed recursively. If the frame is third-party, capture the frame’s own page separately (with its permission) or redesign the export so the needed data is in your document.
Unsupported or changing CSS
Because html2canvas reconstructs the DOM, unsupported CSS, web-font loading, animations, and hover states can differ from what you see on screen. Wait until fonts and data are ready, pause animations, and apply an export-only class that sets stable colors and visibility. Mark unwanted nodes with data-html2canvas-ignore, or use the configuration’s ignore predicate.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Quality, size, and performance controls
- Scale: A higher
scaleimproves detail but increases pixels, memory, and PDF size. Device-pixel ratio is a practical starting point; cap it for mobile devices or large sections. - Image format: PNG preserves sharp text and transparency but is larger. JPEG is smaller for photographic content and does not preserve transparency. Choose the format deliberately in
toDataURL()andaddImage(). - Lazy content: Scroll or otherwise trigger lazy-loaded images before capture, then wait for them to finish. html2canvas cannot capture pixels that have not loaded.
- Repeated exports: For long-lived applications, the configuration documents
clearImageCacheas a way to release image-cache state. Avoid enabling it while concurrent captures share that cache. - Text behavior: This workflow places raster images into the PDF. Do not promise searchable or selectable text unless your particular PDF implementation separately adds a text layer.
Failure diagnosis
The PDF is blank
- Check that the selector returns elements and that each element has non-zero dimensions.
- Look for a tainted canvas caused by a cross-origin image; fix response headers or proxy the asset.
- Reduce
scaleand split an oversized container into smaller captures. - Wait for fonts, images, and application data before calling html2canvas.
The right or bottom edge is cut off
Capture the element’s actual scroll dimensions, remove restrictive overflow during export, and try matching windowWidth and windowHeight to those dimensions. If the resulting canvas becomes enormous, capture sections independently instead.
A chart, shadow, or layout looks different
That property may not be supported by html2canvas or may depend on a browser state such as hover. Use an export stylesheet with simpler, explicit CSS and freeze animation before capture.
One section fails while others succeed
Wrap each capture in a try/catch, record the failing selector, and continue only if omitting that section is acceptable. A single inaccessible image or cross-origin iframe can affect one element without affecting the rest.
Or skip the browser setup
ScreenshotNeo accepts a URL and returns a PNG, JPEG, WebP, or PDF from one API request. For a server-side PDF or image capture, call the API instead of maintaining browser automation:
PC 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 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchcurl -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 documentation for PDF options, full-page capture, custom CSS and JavaScript, selectors, waits, cookies, headers, device presets, and other controls. Cookie banners, newsletter popups, and chat widgets are removed before the shot; bot checks, blank pages, failed loads, and timeouts are not billed, and response headers identify the page verdict and billing status. Its MCP server gives Claude, Cursor, and other MCP clients take_screenshot, get_page_info, and capture_pdf tools. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Can I preserve a transparent background?
Yes, if the canvas and the PDF image path both preserve alpha. Set an appropriate transparent backgroundColor and use a format and PDF configuration that support transparency; JPEG does not.
Rank #4
Can I export elements that are hidden with display:none?
No useful pixels exist for html2canvas to reconstruct from a non-rendered node. Render the element off-screen or temporarily apply an export class that makes it measurable, then restore the original styles.
Does this produce a real browser screenshot?
No. html2canvas recreates the DOM as a canvas and supports only the CSS it understands. For a browser-rendered page captured remotely, use a screenshot service such as ScreenshotNeo.
Frequently Asked Questions
Can I preserve a transparent background?
Yes, when the canvas and PDF image path preserve alpha; JPEG cannot store transparency.
Can I export elements hidden with display:none?
Not directly. Make the element measurable with an export-only style, capture it, then restore the original style.
Does html2canvas produce a literal browser screenshot?
No. It reconstructs the DOM into a canvas and supports only the CSS it implements.
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.
Recommended Free Tools

