โ† All tools
๐Ÿ”ค

PDF OCR Online โ€” Convert Scanned PDF to Searchable Text

Recognize text in scanned PDF files instantly. Fast and accurate local client-side OCR tool. Free, no registration required.

+Upload PDF or photoor drag & drop files here๐Ÿš€ Launch OCR for Free

How It Works

01

Upload a scanned PDF

Open a scanned PDF or document photo directly in your browser.

02

Select the OCR tool

Click ๐Ÿ–Œ on a page card, then pick the โœ๏ธ OCR tool in the editor.

03

Draw the text area

Draw a rectangle around the text or table, then click "Recognize".

04

Edit and save

Recognized text appears as an editable, selectable layer on the page.

Why pdfredX

๐Ÿ”’

Full Privacy

Files never leave your browser. No upload to third-party servers.

โšก

Client-Side Processing

All computations happen locally โ€” fast, secure and free.

๐Ÿ†“

No Registration

Just open your browser and start. No accounts needed.

๐Ÿ“ฑ

Works on All Devices

Desktop, tablet, and mobile โ€” no installation required.

Related Articles

Area OCR for PDF: Recognize and Replace Text on a Scan, Right in the Browser
You have a scanned contract, invoice, or certificate, and you need to change a single line. There's no reason to retype the whole document. Select the area with the text you need, wait for recognition, and edit the result directly on top of the page. The file never leaves your browser.

You have a scanned contract, invoice, or certificate, and you need to change a single line. There's no reason to retype the whole document. Select the area with the text you need, wait for recognition, and edit the result directly on top of the page. The file never leaves your browser.


Step 01. Open the File

Open pdfredX and drag a PDF or a scanned photo into the upload zone. JPEG, PNG, HEIC, WebP, and multi-page PDFs are all accepted. If the document is already open, click the card of the page you need.

The browser converts the page into a Canvas image through PDF.js. This happens locally โ€” no upload to any server.


Step 02. Pick the OCR Tool and Select an Area

In the page editor, choose the OCR tool โ€” the cursor turns into a crosshair. Press and drag a rectangle around the text you want: a single line, a paragraph, or a whole block. The tighter the box, the fewer stray characters end up in the result.

When you release the mouse, a Recognize button appears. Click it.


Step 03. Check and Edit the Text

Tesseract.js processes the selected region directly in the browser. Progress shows in the status bar. After a few seconds, two things appear on the page:

  • A black rectangle โ€” covers the original text (the mask)
  • An editable text layer โ€” the recognized lines on top of the mask

Click the text and edit it right on the page. Size, position, and style are adjusted with the editor's standard controls.


Step 04. Save the Page to PDF

Click Save โ€” the page is exported through the Canvas API at the original full resolution (the scale multiplier is computed as fabNativeW / fabCanvas.getWidth()). The downscale to 1500px only ever touches the copy sent to OCR โ€” it has no effect on the exported file. Then click Create PDF โ€” every page of the document is packed into a single file.


How It Works Under the Hood

Tesseract.js 5.x loads lazily โ€” only on first use of the tool. That's around 2.5 MB. Before recognition, only the cropped fragment is downscaled to 1500px on its longer side โ€” a copy of the area you selected, the one that goes into Tesseract.js. Otherwise the engine hits the RAM ceiling on mobile devices. This shrinking applies strictly to the copy that goes into recognition: the Fabric.js canvas, the background page image, and the final PDF export all stay at the original high resolution, so the finished file loses no quality. By default a combined rus+eng language model is used โ€” it handles mixed-script documents better than a monolingual mode. The language switches automatically based on the site's interface language.

For each recognized line, two Fabric.js objects are created: a fabric.Rect with an opaque fill in the background color (the mask) and a fabric.IText with the recognized text on top. Both objects land in the Undo stack โ€” they can be reverted with Ctrl+Z.


Known Limitations

Recognition quality depends on scan quality. If the page was shot at an angle, with glare, or at too low a resolution, the result will be worse than from a well-calibrated flatbed scanner. Tesseract.js is not a latest-generation neural model but a classic OCR engine. For critical documents, verify the result by hand.

Handwriting is not recognized. The tool works with printed type only. Cursive, dashes, and tables with thin rules often produce errors.

Dark-background pages. If the document background isn't white, the auto-picked mask color may not match. In that case, set the color manually through the editor's color panel.


If you need to permanently hide data rather than rewrite it, the Redact PDF Text tool removes the underlying pixels on save.

Try Area OCR โ€” pdfredx.com, no sign-up, your file stays with you.

Medical Records, Contracts, Passports in PDF: What Happens to Your Text the Moment You Click Upload
Scans with personal data get uploaded to online services every day. Most people do it without a second thought. If you need to change one line in a scanned document, let's be honest about what happens to the file on each of the two paths.

Scans with personal data get uploaded to online services every day. Most people do it without a second thought. If you need to change one line in a scanned document, let's be honest about what happens to the file on each of the two paths.


What Happens to the File on Standard Server-Side Services

When you click the upload button on a typical PDF editor, the file goes to a server. What happens next depends entirely on a given company's policy โ€” the one most people never read.

A typical chain:

  • The file is saved to temporary storage on a contractor's servers (AWS, GCP, Azure).
  • It's processed on a remote machine โ€” OCR, conversion, annotation.
  • It's kept anywhere from a few hours to several days "for reliability" or "to improve the service."
  • At best it's deleted on schedule. At worst it's logged along with metadata.

Specific retention periods are usually written in fine print in the privacy policy. If you're handling a medical record, a contract with a deal amount, passport data, or a court document, that text physically lands on a machine you don't control.


How Processing Works in pdfredX: the File Never Leaves the Browser

In pdfredX the whole process โ€” from opening to saving โ€” happens inside a single browser tab.

Loading: the file is read through the standard FileReader API. The browser creates an ArrayBuffer in memory. There's no network request โ€” not a single byte goes beyond the device.

Render: PDF.js 3.11.174 parses the document and draws pages onto <canvas> elements right in the browser. It's the same engine Firefox uses for its built-in PDF viewer.

OCR: Tesseract.js 5.x is a full text-recognition engine compiled to WebAssembly. It runs in the browser the same way it runs in a terminal. The language model (~2.5 MB) loads once and is cached by the browser.

Result: editable text blocks exist only inside the Canvas of the current tab. Close the tab and they vanish along with the document.


Step-by-Step: Working Safely With Sensitive Documents

Step 01. Open the File Locally

Drag the scan into the upload zone at pdfredx.com. There's no server upload progress bar: the file isn't going to anyone else's machine โ€” it opens locally. Initialization speed depends on file size and device power โ€” a light scan opens instantly, while a heavy 100+ MB PDF on an old phone may take a couple of seconds: FileReader reads the file into memory first, then PDF.js parses and renders the pages right in the browser.

Step 02. Select the Area With Text

Pick the OCR tool and draw a rectangle around the fragment you need: a line, a paragraph, a form field. Click Recognize.

Step 03. Edit and Save

Fix the recognized text right on the page, then click Save โ†’ Create PDF. The file downloads straight from the browser through a standard <a download>.


What We Cannot Guarantee โ€” An Honest Disclaimer

We don't print "100% privacy" on the homepage, because that's impossible to guarantee independently of the user's environment.

Browser extensions with broad permissions (especially ones with "read data on all sites" access) can in theory intercept tab content. Before working with confidential documents, it's reasonable to review your installed extensions.

Keyloggers and malware on the user's machine are outside our control. If the device is compromised, that affects any local tool, not just a browser-based one.

The saved file stays on your disk. If the device isn't protected by encryption, confidential files can be read by third parties with physical access to it.

We provide isolation at the browser-tab level. The rest is on the user's side.


Process sensitive PDFs without sending them to a server โ€” pdfredx.com, no registration.

Tesseract.js 5.x in the Browser: How Area OCR With Text Replacement Works on Top of Fabric.js
The task sounded straightforward: the user draws a rectangle on a scanned PDF, clicks a button, and an editable text layer appears over the original โ€” exactly where the unreadable or wrong text used to be. No server, no file uploads. Here's how it's built.

The task sounded straightforward: the user draws a rectangle on a scanned PDF, clicks a button, and an editable text layer appears over the original โ€” exactly where the unreadable or wrong text used to be. No server, no file uploads. Here's how it's built.


The Architectural Challenge: OCR in the Browser With No Backend

The first design question: which is heavier โ€” shipping images to a server for OCR, or dragging the engine into the browser?

Server-side OCR solves the compute problem but creates another: a file with sensitive data flies off to someone else's machine, you get round-trip latency, and you need infrastructure. For a tool whose whole pitch is privacy, that doesn't fit.

Tesseract.js 5.x is a WebAssembly build of the classic Tesseract OCR engine. It runs directly in the browser through Web Workers, doesn't block the UI thread, and needs no server at all. Language packs (~2.5 MB each) download once and are cached. It is not a latest-generation neural network โ€” Tesseract works on classic segmentation algorithms, LSTM, and trained language models. It does well on clean scans and poorly on handwriting or heavily compressed JPEGs.


Lazy Loading: Don't Ship What Isn't Needed

Tesseract.js weighs several megabytes. Loading it when the site opens is unjustified โ€” most users will never open OCR. So the load is deferred:

async function fabStartOCR(areaRect = null) {
  if (!window.Tesseract) {
    await new Promise((res, rej) => {
      const s  = document.createElement('script');
      s.src    = 'https://unpkg.com/tesseract.js@5/dist/tesseract.min.js';
      s.onload = res;
      // On a network failure, remove the broken tag from the DOM. Otherwise
      // window.Tesseract stays undefined and the script duplicates on retry.
      s.onerror = (e) => { s.remove(); rej(e); };
      document.head.appendChild(s);
    });
  }
  // ...
}

The first fabStartOCR() call pulls in the script and the browser caches it; the second runs instantly.

The subtle part is error handling. If s.onerror fires (the user has network trouble), the promise rejects โ€” but the <script> tag itself stays in the <head>. On the next call, window.Tesseract is still undefined, so the if (!window.Tesseract) guard is true again, and a second broken tag gets appended, then a third. That's why onerror removes the element (s.remove()) before rejecting: every retry starts from a clean <head>.


Downscale to 1500px: Not Marketing, but Necessity

Scanned documents are often 300 DPI or higher. An A4 sheet at 300 DPI is 2480ร—3508 pixels. Feeding an image that size to Tesseract.js in the browser takes 300 to 600 MB of RAM just to decode it and build the engine's internal buffers.

On desktops that's still tolerable. On mobile devices with 2โ€“4 GB of shared memory it's a guaranteed crash or a frozen tab.

So before handing it to Tesseract, only the cropped fragment โ€” the copy of the selected region, not the whole page โ€” is downscaled:

const MAX_SIDE = 1500;
const scale    = Math.min(1, MAX_SIDE / Math.max(imgW, imgH));
const scaledW  = Math.floor(imgW * scale);
const scaledH  = Math.floor(imgH * scale);

1500px is an empirically chosen threshold at which Tesseract runs stably on an iPhone 12 and Android devices with 3 GB of RAM. The recognition quality degrades only marginally for most scans: Tesseract normalizes the image internally before processing anyway. This shrink touches only the crop that goes into OCR โ€” the Fabric.js canvas, the background image, and the exported PDF all keep their original resolution.


Language Models and the Combined Mode

pdfredX users often work with documents that mix Russian and English: contracts with Latin abbreviations, medical reports with Latin terms, financial statements.

Monolingual mode breaks at the transitions between scripts. So a combined model is the default:

const lang = currentLang === 'de' ? 'deu+eng' : 'rus+eng';
const { data } = await Tesseract.recognize(croppedCanvas, lang, { ... });

currentLang is the site's interface language, chosen by the user via the RU/EN/DE buttons. German users get deu+eng, everyone else rus+eng. It's not perfect (an English-speaking user with a Russian document gets rus+eng only if they switch), but it covers 90% of real cases.


The Recognition Area: Coordinates in Two Reference Frames

This is the most delicate part of the implementation. We have two scale spaces:

1. The Fabric.js canvas โ€” the working size, scaled to fit the screen 2. The native image โ€” the full resolution of the original page

The user draws a rectangle in Fabric.js space. OCR needs the coordinates in the original's space:

// Correctness condition: areaRect (a fabric.Rect) is created with originX:'left'
// and originY:'top', and areaRect.x/y are the coordinates of its top-left corner
// in the canvas's global Viewport space (not relative to a parent/group).
const scNX = fabNativeW / fabBaseW;  // X factor
const scNY = fabNativeH / fabBaseH;  // Y factor
ocrOffX = Math.floor(areaRect.x * scNX);
ocrOffY = Math.floor(areaRect.y * scNY);
ocrW    = Math.min(Math.ceil(areaRect.w * scNX), fabNativeW - ocrOffX);
ocrH    = Math.min(Math.ceil(areaRect.h * scNY), fabNativeH - ocrOffY);

fabNativeW/H is the original size; fabBaseW/H is the canvas size. The crop is taken from the original image, not the scaled one โ€” so recognition quality doesn't depend on browser-window size.

One important condition: the formula holds only if areaRect is created with originX: 'left' and originY: 'top' (the object's reference point is its top-left corner), and areaRect.x/y are read in the canvas's global Viewport space. Move the origin to the center (originX: 'center') or compute coordinates relative to a group/parent, and ocrOffX/ocrOffY shift by half the width and height โ€” the crop lands off the selected area.


The Masking Layer + Editable Text: Two Fabric Objects per Line

Tesseract returns a data.lines structure โ€” an array of lines with bbox coordinates and recognized text. For each line, a pair of objects is created:

data.lines.forEach(line => {
  // Scale the bbox back into Canvas space
  const x = (line.bbox.x0 / scNX) + areaRect.x;
  const y = (line.bbox.y0 / scNY) + areaRect.y;
  const w = (line.bbox.x1 - line.bbox.x0) / scNX;
  const h = (line.bbox.y1 - line.bbox.y0) / scNY;

  // Mask โ€” covers the original text
  const rect = new fabric.Rect({
    left: x, top: y, width: w, height: h,
    fill: '#ffffff', selectable: true, evented: true,
  });

  // Editable text on top
  const itext = new fabric.IText(line.text.trim(), {
    left: x, top: y,
    fontSize: Math.round(h * 0.8),
    fill: '#000000',
    fontFamily: 'Arial, sans-serif',
    selectable: true, editable: true,
  });

  fabCanvas.add(rect);
  fabCanvas.add(itext);
});
fabCanvas.renderAll();  // force a render: commit every object BEFORE the snapshot
fabPushUndo();          // the whole block โ€” one state in the undo stack

Order matters here. fabPushUndo() takes an instant snapshot of the canvas state (via toJSON). Call it right after synchronously adding objects in the loop, and Fabric.js may not have drawn the new rect and itext yet โ€” it defers the repaint to the next frame. So before the snapshot we force fabCanvas.renderAll(): it runs the render synchronously, and the undo stack captures the actual state with all the added lines.

White is the default mask color. On documents with a colored background, the user can change the color through the color panel and run OCR again on the same region.


The Limits We Live With

bbox positioning accuracy. Tesseract doesn't always pin down line boundaries precisely, especially in documents with non-standard line spacing. Sometimes the bbox is slightly off and you have to nudge the text block by hand in Select mode.

Tables. In PAGE_SEG_MODE_AUTO, Tesseract often mis-segments lines inside tables. Column-by-column selection and manual correction work better there.

Handwriting. Not recognized. Full stop.

Performance on long documents. OCR runs only on the selected area, not the whole document. That's a deliberate decision โ€” processing a full page in the browser isn't realistic without significant delays.


Try Area OCR in the browser โ€” pdfredx.com, no sign-up, no file uploads to any server.

๐Ÿš€ Launch OCR for Free