PDF.js Text Search with Highlighted Matches — Free JavaScript Snippet

PDF.js Text Search with Highlighted Matches · Tools · Plain HTML, CSS & JS · Live preview

What's included

Features

Per-page text extraction
getTextContent once per document.
Results with context
Snippets with the match marked.
Keyboard navigation
Enter and arrow keys cycle matches.
Highlight overlay
Boxes positioned with Util.transform.
Current match emphasis
Orange box scrolled into view.
Case sensitivity toggle
Instant re-search.
Safe rendering
PDF text is escaped before display.
High-DPI canvas
Crisp pages on dense screens.

About this UI Snippet

Searching Inside a PDF With PDF.js — Text Extraction and Highlight Geometry

Screenshot of the PDF.js Text Search with Highlighted Matches snippet rendered live

A PDF isn't a text document; it's a set of drawing instructions that happen to place glyphs. Searching one means asking PDF.js to reconstruct the text, then mapping matches back onto the drawing. This snippet does both and explains each step.

getTextContent returns runs, not paragraphs

For each page, page.getTextContent() returns items: runs of characters PDF.js found together, each with a str, a width and a transform matrix that says where the run starts on the page. A line of body text is often a single item; headings, bold words or kerned text can be split into several. Extraction happens once per document; searching is then a plain string scan over every item.

Results with context

Each match stores its page, item and character offset. The results list shows up to 34 characters either side, with the match wrapped in <mark>, escaped to avoid injecting HTML from the PDF.

From PDF coordinates to pixels

PDF space starts at the bottom-left with y going up; the canvas starts at the top-left. pdfjsLib.Util.transform(viewport.transform, item.transform) combines the page's viewport (scale, flip, rotation) with the item's own transform, giving the run's baseline position in canvas pixels. The font height comes from the length of the matrix's vertical vector, Math.hypot(tx[2], tx[3]).

Where exactly is the word?

The run's total width is known, but not each character's. Counting characters — placing the match at offset ÷ length of the run — drifts visibly on long lines, because an "m" is far wider than an "i". Instead, the snippet measures the text before the match and the match itself with canvas measureText in the run's font family (from getTextContent's styles), then scales those widths to the run's real width. PDF.js's own viewer goes further with a text layer of positioned spans.

Limitations

A match split across two items (for example a hyphenated line break) won't be found. Scanned PDFs have no text at all without OCR.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet into an AI assistant like Claude and ask it to explain how Util.transform maps PDF coordinates to canvas pixels, including the y-axis flip. Ask it to support matches that span text items, whole-word and regex search, exact character boxes using PDF.js's text layer, or searching all pages in a Web Worker for very large documents.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a PDF text search tool with PDF.js 3 (classic build from a CDN) in plain HTML, CSS and JavaScript, using jsPDF only to generate a sample document.

Requirements:
- Generate a three-page sample PDF about HTTP caching with jsPDF and open it on load; also allow opening a local PDF.
- Extract text content from every page once, keeping each non-empty text item.
- A search box (minimum two characters, debounced) with a match-case option that finds every occurrence within each text item and lists results with page number and escaped context, the match wrapped in a mark element, plus a summary of matches and pages.
- Selecting a result (click, Enter, or up/down arrows in the search box) renders that page on a high-DPI canvas and draws highlight boxes for all matches on it, with the current match emphasised and scrolled into view.
- Position boxes by combining the viewport transform with each text item's transform, measuring the prefix and match with measureText in the run's font family and scaling to the run width to find its horizontal position, and the transform's vertical vector length for font height.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<div class="ps">
  <aside class="ps-side">
    <form id="psForm" class="ps-search" role="search">
      <input id="psQuery" type="search" value="cache" placeholder="Search the PDF…" aria-label="Search text" autocomplete="off">
      <label class="ps-case"><input type="checkbox" id="psCase"> Match case</label>
    </form>
    <label class="ps-open">Open another PDF<input type="file" id="psFile" accept="application/pdf"></label>
    <div class="ps-summary" id="psSummary" aria-live="polite"></div>
    <ol class="ps-results" id="psResults"></ol>
  </aside>
  <main class="ps-main">
    <div class="ps-pagebar"><span id="psPageLabel"></span><span class="ps-hint">↑/↓ or Enter to step through matches</span></div>
    <div class="ps-stage"><div class="ps-page" id="psPage"><canvas id="psCanvas"></canvas><div class="ps-hl" id="psHl"></div></div></div>
  </main>
</div>

Step by step

How to Use

  1. 1
    Search the sampleA three-page article on HTTP caching opens with "cache" searched.
  2. 2
    Step throughPress Enter or ↑/↓ in the search box; the page and highlight follow.
  3. 3
    Click a resultJump to that page and match.
  4. 4
    Match caseToggle case-sensitive search.
  5. 5
    Open your own PDFRead locally; works for PDFs with a text layer.

Real-world uses

Common Use Cases

Document portals
Find clauses in contracts or policies.
Research tools
Search papers without leaving the app.
Compliance review
Locate required terms quickly.
Knowledge bases
Search manuals delivered as PDF.
Learning PDF.js
Text content and coordinate transforms explained.
Related: PDF.js Viewer with Thumbnails
Related: Table Search Highlight
Highlighting matches in HTML: Live Search with Highlighted Matches.

Got questions?

Frequently Asked Questions

With PDF.js, load the document, then for each page call page.getTextContent(). It returns items with a str property containing the text of each run; join them for plain text.

Combine the viewport transform with the text item's transform using pdfjsLib.Util.transform to get the run's baseline position in pixels, estimate the match's offset within the run, and draw an absolutely positioned box over the canvas.

A phrase may be split across separate text items, for example across a line break or a font change. Searching across item boundaries requires joining items and mapping offsets back, which this snippet keeps out for clarity.

No. Scanned PDFs are images with no text layer. You would need OCR, for example Tesseract.js, to create text first.

PDF.js gives the width of each run but not of each character. The snippet measures the prefix and match with canvas measureText in the run's font family and scales to the run width, which is close for embedded or substituted fonts. For exact boxes, render PDF.js's text layer and measure the spans.