Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
If text in a jsPDF file cannot be selected or searched, first check whether your code turns the page into a canvas image and inserts that image into the PDF. An image-only page has no text to select. For data-driven documents, write text with jsPDF’s text APIs; for HTML layouts, use doc.html() and verify the generated PDF. If you need text in languages or characters outside the standard PDF fonts’ limited coverage, embed a TrueType font with the glyphs you need.
Why your jsPDF PDF behaves like a screenshot
A PDF can look like a document while containing only a picture of one. The key distinction is what the PDF contains: text operators that represent characters, or an image that represents the page visually. If your workflow renders HTML to a canvas, converts that canvas to a data URL, and passes it to addImage(), the page content is an image. You can zoom in and see the letters, but you cannot select, search, or extract them as text.
This is the documented rendering approach used by html2pdf.js: HTML is rendered as an image and placed in the PDF, so its documentation warns that the resulting text is not selectable or searchable and that files can be large. html2canvas reconstructs a visual representation from the DOM, rendering the properties it understands; it is a capture layer, not a semantic text writer.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →html2canvas(element).then(canvas => {
const pdf = new jsPDF();
pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 0, 0, 210, 297);
pdf.save('file.pdf');
});
The code can produce a visually correct page and still be the reason text is not selectable. The remedy is not a PDF-viewer setting: change the generation path so body text is written as text rather than flattened into an image.
#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.
Choose the generation path that matches the document
| Approach | Selectability and search | Visual fidelity | Fonts and Unicode | Pagination and complexity |
|---|---|---|---|---|
| Direct jsPDF text APIs | Text is written as text and can be selectable/searchable. | You control layout in code rather than capturing arbitrary CSS. | Use a registered TrueType font when standard fonts do not cover your characters. | Requires you to implement text placement and layout. |
jsPDF doc.html() |
Inspect the result; the HTML pipeline depends on html2canvas, so do not assume every HTML element becomes PDF text. | Useful for HTML layouts, subject to the rendering pipeline and supported properties. | Configure and verify the fonts used by the HTML rendering path. | autoPaging: 'text' tries to avoid cutting text in half at page breaks; verify the actual output. |
Canvas or screenshot inserted with addImage() |
Image content is not selectable or searchable as text. | Favors visual capture of the rendered page. | Characters appear as pixels rather than embedded text glyphs. | Often simpler for visual capture, but does not meet searchable-text requirements. |
Fix 1: write known content directly with jsPDF
When you already have the text as data—such as invoice fields, report values, or labels—use jsPDF’s text methods instead of taking a screenshot of an HTML element. This minimal browser example uses the jsPDF global exposed by a browser build:
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
doc.setFont('helvetica', 'normal');
doc.setFontSize(12);
doc.text('Selectable PDF text', 20, 30);
doc.save('selectable.pdf');
setFont() selects the font for subsequent text elements; setFontSize() controls their size, and text() writes the string at the coordinates you provide. jsPDF’s programmatic text and font APIs are the right starting point when the source is known data and you can define the layout yourself.
Build layout around text, not a canvas
For a multi-line or multi-page document, your application must decide where each block goes and when to start another page. Keep the content in strings or structured data, write it through the text API, and apply your own wrapping, spacing, and page-break logic. A single text() call at a fixed coordinate proves that the text path works; it does not automatically create a complete document layout.
Recommended Free Tools
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.
Fix 2: use jsPDF’s HTML module and verify the PDF
If the source is an HTML layout, try the official HTML module rather than manually converting a canvas to a data URL and adding it as a page image. The following example asks the module to use text-aware automatic paging:
const doc = new jsPDF({ unit: 'mm', format: 'a4' });
doc.html(document.querySelector('#content'), {
x: 15,
y: 15,
width: 180,
autoPaging: 'text',
callback: pdf => pdf.save('selectable.pdf')
});
Load a jsPDF build that includes the HTML module, and make sure #content exists when this code runs. The broader jsPDF documentation notes that doc.html() depends on html2canvas; when you supply an HTML string, it also depends on DOMPurify. That dependency matters: the result is still affected by the rendering pipeline and the CSS and content it supports. The autoPaging: 'text' option tries to avoid splitting text across page breaks, but it is not proof that every page has the text behavior you want.
Inspect the generated file, not just the preview
- Generate a small PDF containing a recognizable word.
- Open the saved PDF in a viewer and try to select the word, search for it, and copy it into a plain-text editor.
- If those checks fail, inspect the generation code. If the final page is still created with
addImage()from a canvas, it remains an image path. - If direct
doc.text()output works but HTML output does not, reduce the HTML to a small test and isolate unsupported CSS, canvas content, or cross-origin assets. - After changing the layout or paging settings, repeat the checks on the actual saved PDF, including pages near breaks.
Make Unicode text work with an embedded font
jsPDF’s standard PDF fonts cover a limited ASCII code page. If your content contains UTF-8 or non-Latin characters, register a TrueType font that includes the glyphs you need before writing the text. This browser example fetches a font file served by your application and registers it:
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.
const fontBinary = await fetch('/fonts/Inter-Regular.ttf')
.then(r => r.arrayBuffer())
.then(buf => String.fromCharCode(...new Uint8Array(buf)));
doc.addFileToVFS('Inter-Regular.ttf', fontBinary);
doc.addFont('Inter-Regular.ttf', 'Inter', 'normal');
doc.setFont('Inter', 'normal');
doc.text('Unicode: café, Ελληνικά, 䏿–‡', 20, 40);
The registration sequence is significant: add the font data to jsPDF’s virtual file system, register the face with addFont(), select it with setFont(), and then write the text. A font only supports the glyphs it contains, so a registered font that lacks a particular character will not solve that character’s rendering problem. Test the actual languages and symbols in your document.
Free tools Windows power users keep installed
One-click scans. No signup required.
Troubleshoot non-selectable text systematically
- The whole page acts like one picture. Search your source for
html2canvas,canvas,toDataURL(), andaddImage(). Together, these commonly reveal an image-based output path. Replace that path for text content. - You are unsure whether the PDF contains text at all. Generate a one-page test with
doc.text('test', 20, 20). If that word is selectable, direct jsPDF text output works and the issue lies in the other rendering path. - Direct text works but the HTML-generated page does not. Reduce the HTML and test again. Isolate unsupported CSS, canvas-based content, and cross-origin assets; html2canvas documents limitations involving unsupported properties and cross-origin resources.
- Some characters are missing or wrong. Register a TrueType font before writing Unicode text and confirm that it contains every required glyph. Do not infer full character coverage just because the font registered successfully.
- Text is cut at a page boundary. Try
autoPaging: 'text'for HTML output, then inspect the affected pages in the saved PDF. The option attempts to avoid splitting text; check the result rather than treating it as a guarantee. - The PDF looks right but is unexpectedly large. Check whether full pages are being embedded as images. Image-based pages trade text semantics for visual capture and can produce large files; do not assume a particular size saving without comparing your own output.
Keep images where they belong
Selectable text and faithful visual capture are different goals. Charts, photographs, and other content that is inherently visual can remain images. For body copy that readers need to search, copy, or extract, use a text-producing path. A mixed document can combine text written with jsPDF’s text APIs and images for visual elements; it does not need to flatten every page just because some elements are images.
Or skip the browser setup
ScreenshotNeo is a website screenshot API, not a way to turn a jsPDF canvas image into selectable text. Use it when your goal is a clean visual capture of a web page—not as a fix for an image-only PDF. One GET request can return a screenshot or PDF:
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 request options. ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; those steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses include X-Page-Verdict and X-Billed headers. It also provides an MCP server for AI agents, with take_screenshot, get_page_info, and capture_pdf tools. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots.
For a visual web capture, visit ScreenshotNeo or sign up free for 1,000 screenshots a month with no card.
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.

