# Scanner (OCR, QR, documents)

> Point the camera at text, codes, or paper and get the result in your field.

URL: https://wmkeyboard.pages.dev/tools/scanner/
Section: Tools

Three camera-driven tools live under one Scanners group. **Text scan (OCR)** reads printed words into your field. **QR and barcode scanner** decodes a code and inserts or opens its value. **Document scanner** captures a paper document as a cropped, cleaned-up image. All three are in every build. On F-Droid's, text scan reads with PaddleOCR alone, the QR scanner with zxing-cpp, and the document scanner is the built-in one. The QR scanner reads with Google ML Kit or zxing-cpp; text scan reads with ML Kit, PaddleOCR or Tesseract, depending on the language. The document scanner uses Google's scanner where Google Play services can run it and the keyboard's own everywhere else.

The three scanners are also modes of the [Camera tool](https://wmkeyboard.pages.dev/tools/camera/#scanner-modes). A rail down the right edge of the camera, the QR scanner and Text scan switches between Photo, QR code, Text and Document, so a single Camera button on the toolbar reaches every one of them. Each scanner's own tool opens the camera straight in its mode.

  

## Scanning text (OCR)

Open **Text scan (OCR)** from the toolbar or toolbox to replace the keyboard with a full-bleed camera view. It covers the toolbar row too, not only the key area. Point it at printed text and it recognizes words on-device. A torch toggle appears if your device has a flash.

### Which languages it reads

Three engines read the text on your device, and a fourth, which you set up yourself, reads it online:

- **ML Kit** reads Latin-script text: English, French, Spanish and the like. It needs no download. On a phone with Google Play services it also reads Chinese (simplified and traditional), Japanese, Korean and Devanagari (Hindi, Marathi, Nepali and the like), and those recognizers read Latin words mixed into the text as well. Google Play services downloads each of them the first time you scan in that script; until it arrives, the panel asks you to try again in a minute.
- **PaddleOCR** reads Chinese (simplified and traditional) and Japanese, including vertical columns, with the English words mixed into them. It uses the PP-OCRv5 mobile models from Baidu's PaddleOCR project, which read these scripts far better than Tesseract does. Both languages share one download of 11 MB. It has no setting of its own: **Automatic** uses it. On the F-Droid build, which has neither ML Kit nor Tesseract, it reads Latin-script text too (English, French, Spanish and the like), and its download there is 21 MB; see [On the F-Droid build](#on-the-f-droid-build).
- **Tesseract** reads more than 100 languages in most scripts, for example Bangla, Hindi, Arabic, Russian, Greek, Thai, Chinese, Japanese and Korean. Each language needs a one-time download of its data, from 0.4 MB to 12 MB.
- **Online model** sends the photo to an online reader and shows the text it sends back. It reads any script, mixed scripts and handwriting, with no language data to download. The reader is one you add: a model that can read images, through any service with an OpenAI-compatible API (OpenAI, OpenRouter, Gemini, Mistral, or Ollama or LM Studio on your own computer), or a [Rens](https://git.maid.zone/stuff/rens) server, a free front end to Google Lens. See [Reading text online](#reading-text-online).

With **Text recognition** on **Automatic** (the default), every script ML Kit reads goes to ML Kit, Chinese and Japanese go to PaddleOCR where ML Kit can't read them, and every other script reads with Tesseract. So on a phone with Google Play services, Chinese, Japanese, Korean and Devanagari text reads with ML Kit. On a phone without it, Chinese and Japanese read with PaddleOCR and Korean and Devanagari with Tesseract. If you downloaded Tesseract's Chinese or Japanese data before PaddleOCR came, the scanner keeps reading with it until PaddleOCR's download is on your device too. The scanner reads in the language you're typing in when you open it. If you've turned on more than one language, a chip at the bottom left of the viewfinder names the text's language, and tapping it steps to the next one without switching your keyboard. All your languages that read the same way share one stop on the chip: on Automatic, every Latin-script language is one stop.

On the F-Droid build, **Text recognition** offers only **Automatic** and **Online model**. Automatic reads Chinese, Japanese and every Latin-script language with PaddleOCR, so they all share one stop on the chip. A language in any other script, Bangla or Russian for example, shows a message in place of the viewfinder saying that nothing on the phone reads it, and reads only with Online model.

A language without Tesseract data of its own reads with the main one for its script: Maithili and other Devanagari languages read with Hindi's, and a Latin-script language with no data of its own reads with English's.

The first time you scan in a language that needs Tesseract or PaddleOCR data, the viewfinder is replaced by a **Download** button that says how big the data is. The download shows its progress in the panel and can be cancelled there. With [data saver](https://wmkeyboard.pages.dev/privacy/data-saver/) set to ask, the first press explains that it uses mobile data and a second press starts it. Set to block, the button says why it can't. Tesseract's data comes from the `tessdata_fast` 4.1.0 set of the Tesseract project on GitHub. PaddleOCR's is three files, a text detector, a line recognizer and its list of characters, converted to run on the same LiteRT engine as [offline Whisper voice typing](https://wmkeyboard.pages.dev/tools/whisper/) and published on GitHub by the PPOCRv5-Android project; the download is pinned to one version and each file is checked against its checksum. The F-Droid build fetches only the list of characters from there. Its detector and recognizer are PaddlePaddle's own models exported for MNN and published in WM Keyboard's data repository, checked the same way; on a set of test pages they read exactly the text the LiteRT ones do. Both stay in the app's private storage. You can also download or delete each language on the tool's settings page, and delete it under [Storage](https://wmkeyboard.pages.dev/privacy/data/). On a phone that cannot download, **Get the files** on that page links each language's `.traineddata` file, or PaddleOCR's three files, and **Import** installs ones fetched elsewhere ([Using it without internet](https://wmkeyboard.pages.dev/privacy/offline/)).

After a capture, the recognized words appear as tappable chips grouped by line:

  

- With **Start with everything selected** on (the default), every word starts selected and tapping a chip deselects it, so you can trim the capture down to what you need. Turn it off and chips start empty, so you tap to pick words one at a time instead.
- **Copy** and **Insert** act on whatever's currently selected.
- **Translate** opens the [Translate](https://wmkeyboard.pages.dev/tools/translate/) tool on the selected words, translated straight away into its language. Pick only the line you need first, and only that line is translated.
- **Scan again** discards the capture and returns to the viewfinder. A select-all/deselect-all toggle flips every chip at once.

PaddleOCR finds each line of text first and then reads the lines one by one, so a line that's tilted, or a column of vertical Japanese, comes out in reading order: rows top to bottom, then columns right to left. Each line becomes one chip, since Chinese and Japanese put no spaces between words.

Tesseract reads each photo twice, once with a threshold set from each small area of the photo and once with one threshold for the whole photo, and keeps whichever read found more text. The first copes with uneven light: a shadow across the page, the dark corners of a phone photo, or the torch's bright spot. The second copes with light text on a dark screen. "No text found" means both reads came back empty. If recognition itself fails, the panel says **Could not read** instead. If a downloaded language won't load, with Tesseract or PaddleOCR, the scanner deletes that language's data and offers the download again.

If your device has no camera, the panel shows a message instead of a blank viewfinder.

To read a photo that's already on your phone, such as a screenshot, tap the picture button in the top-right corner of the camera view. The keyboard's [file manager](https://wmkeyboard.pages.dev/tools/files/) opens, or Android's photo picker in the Play Store build, and the photo you choose is read in the language on the chip, then lands on the same chips as a capture. It is shown whole rather than cropped to the viewfinder.

To read what's on the screen itself, tap the screenshot button beside it. It's only there in the F-Droid and GitHub builds, with **Capture the screen** on under Privacy / Permissions / Shizuku or root and Shizuku or root ready. The keyboard captures everything above its own top edge, with no screen recording prompt, and reads it like a picture you picked. Back stays in the scanner.

A picture you copied can be read the same way, with no camera at all: press and hold it in the [clipboard panel](https://wmkeyboard.pages.dev/tools/clipboard/) and tap **Extract text**. The scanner opens on the picture and reads it at once, and its back button returns to the clipboard. **Scan again** shows the picture with the language chip and the shutter, so you can read it again in another language. The button is only there while Text scan is on in your tools.

## Scanning QR codes and barcodes

**QR and barcode scanner** is also full-bleed and decodes continuously as you point the camera, with no shutter tap needed. It reads QR codes plus the common barcode formats: Aztec, Codabar, Code 39/93/128, Data Matrix, EAN-8/13, ITF, PDF417, and UPC-A/E, labeled by format in the result.

  

The first code it decodes freezes into a result card with **Scan again**, **Copy**, **Share** and **Insert**. If the decoded value is a plain URL (`https://…`, `http://…`, or `www.…`), an **Open** button appears above that row. Anything else is text only, so a scanned value like a Wi-Fi network payload inserts as raw text rather than turning into a connect action. Pinch to zoom while scanning, and a small pill reads out the zoom ("1.2x" style) above roughly 1.05x. One particular link gets a little extra recognition on the result card, over on [Easter eggs](https://wmkeyboard.pages.dev/reference/easter-eggs/#scan-the-inevitable-qr-code).

To read a code that's already on your phone, such as a screenshot of a QR code, tap the picture button in the bottom-left corner of the camera view. The keyboard's [file manager](https://wmkeyboard.pages.dev/tools/files/) opens, or Android's photo picker in the Play Store build, and the photo you choose is read the same way, on-device, landing on the same result card. When the picture holds no code, a short message says so and the camera view stays up.

### Which reader decodes codes

Two readers can decode what the camera sees, and the result card is the same with either.

- **ML Kit** is Google's barcode reader. It is the default wherever it can run: the GitHub builds, which carry its model, and the Google Play copy on a phone with Google Play services, which supplies the model.
- **zxing-cpp** is an open-source reader (Apache-2.0). The F-Droid build reads with it, since ML Kit is a Google library, and so does the Google Play copy on a phone without Google Play services. Anywhere else, turn on **Read codes with zxing-cpp** in the options below to use it instead of ML Kit. Besides the formats above it reads Micro QR, rMQR, MicroPDF417, MaxiCode, GS1 DataBar, Telepen and the Code 39 variants, which the card labels "Barcode" when it has no name for them. It also tries light codes on a dark background, such as a QR code on a phone in dark mode.

zxing-cpp is a separate download of about 1 MB, fetched the first time the scanner opens with it: from Google Play on the Play build, and from GitHub elsewhere, which on F-Droid's build waits until you turn on [Download feature libraries](https://wmkeyboard.pages.dev/reference/settings/privacy/#downloads). Until it's on your phone, the scanner shows its size and a **Download** button in place of the camera. A UPC code reads as its own 12 or 8 digits with either reader.

## Options

All three are one bundle on [Extensions](https://wmkeyboard.pages.dev/reference/settings/extensions/), **Scanners**. Off there, the three tools and the Scanners group of the Tools list are gone until it is switched back on.

All three scanners have their own settings screen, and all three start with the same header rows before anything tool-specific:

- **Turn on**: Text scan is in the "recommended" and "show me everything" starter sets that setup lands, so it arrives on unless you picked the simplest answer. Neither QR and barcode scanner nor Document scanner is in any starter set, so setup leaves both switched off unless you pressed **Everything** on its tools page. Turn them on here.
- **Icon colour**: only shown while [colorful tool icons](https://wmkeyboard.pages.dev/reference/settings/tools/) is on globally, which it is by default. Text scan is green, QR and barcode teal, Document scanner a light blue.
- **Trigger words**: under a **Keyword shortcut** heading. It's `ocr` for Text scan, `scan` for QR and barcode, and `docscan` for Document scanner. Type one of them on its own and the suggestion strip offers to open that tool. Commas separate several words, and an empty field means the tool never offers itself.
- **Match capitals**: off by default on all three, so a trigger word matches however you capitalise it. Turn it on and only the exact spelling counts.
- **Reset to defaults**: joins the group once you've edited that tool's trigger words, and puts them back.

Tools / QR and barcode scanner / Options

- **Read codes with zxing-cpp**: off by default, and shown only where ML Kit's reader can run. Decodes with the open-source reader instead; see [which reader decodes codes](#which-reader-decodes-codes). The caption above the rows names the reader in use.
- **Insert automatically**: off by default. With it on, the scanned code's text types into your field the instant it's spotted, with no confirm tap.
- **Vibrate on detection**: on by default. A short buzz when a code is spotted.
- **Load link details**: off by default. When a scanned value is a web link, it fetches the page's title and description to show above the result. That needs a network connection. [Power Saving mode](https://wmkeyboard.pages.dev/typing/power-saving/) force-disables this row while its background-network toggle is on.

Recognition for both OCR and the QR/barcode scanner runs entirely on-device, so no photo is uploaded and both work offline once any Tesseract or PaddleOCR data they need is downloaded. The one exception is **Text recognition** set to **Online model**, which sends each photo you scan to the service you named. Link previews are the one exception, since fetching a page's title needs the network. See [network policy](https://wmkeyboard.pages.dev/privacy/network/) for what else does and doesn't touch the network.

Tools / Text scan (OCR) / Options

- **Start with everything selected**: on by default, described above.
- **Text recognition**: **Automatic** (the default), **ML Kit**, **Tesseract** or **Online model**. See [which languages it reads](#which-languages-it-reads). The online model ignores the text's language, so the viewfinder shows no language chip with it. With ML Kit the chip picks its recognizer, and a language in a script ML Kit has no recognizer for reads with the Latin one. Tesseract reads Latin script too, usually slower than ML Kit.

### Reading text online

With **Text recognition** on **Online model**, an **Online text reader** group appears under it. It is a [provider list](https://wmkeyboard.pages.dev/tools/providers/): add readers with **Add a provider**, drag them into order, and switch them off. The first reader that reads the photo is used, and the next one is asked only if it fails. With **How to use them** set to **Choose in the panel**, a chip at the bottom right of the viewfinder names the reader, and tapping it picks another. Two kinds of reader can be added, each more than once:

- **Your vision model**, a model that reads images through an OpenAI-compatible API:
  - **Server address**: such as `https://api.openai.com/v1`, `https://openrouter.ai/api/v1`, or `http://192.168.0.10:11434/v1` for Ollama on your computer. The full address ending in `/chat/completions` works too.
  - **Model**: required. The name of a model that reads images, as the service spells it, for example `gpt-4o-mini`.
  - **API key**: the service's key. Leave it blank for a server of your own that asks for none. It's kept with your other API keys, and leaves the phone in a backup only with **Include API keys** on.
- **Rens**, the address of a Rens server, your own or [a public one](https://git.maid.zone/stuff/rens#instances). Rens passes the photo to Google Lens and sends back the text Lens found. It strips the photo's metadata and gives it a random name first, and Google sees the server, not your phone. **Test** checks that the address is a Rens server without sending a photo.

A reader that was set up before the list existed is in it already, as **Your vision model**.

Each scan sends the photo, scaled down to 1600 pixels on its longer side. A vision model gets a request to write out exactly the text in it and nothing else; Rens gets the photo the way its own web page sends it. The text comes back as the same word chips the other engines give. A paid service may charge for each scan. With no reader on, the panel says so after the shutter, and an error from the reader, such as a wrong key or a model that can't read images, shows as a message rather than as "No text found". Every scan shows in the [network activity log](https://wmkeyboard.pages.dev/privacy/network-activity/) as **Scan text, online**.

Tools / Text scan (OCR) / Language data

One row for each download the languages you type in need, with its size, a **Download** button, its progress, or a delete button once it's on your device. Two languages that read with the same data share one row, for example Norwegian Bokmål and Nynorsk, or Chinese and Japanese, which on **Automatic** share PaddleOCR's row (named "with PaddleOCR") wherever ML Kit can't read them. On **Tesseract** the list shows Tesseract's own Chinese and Japanese data instead. The list is hidden while **Text recognition** is on ML Kit or Online model. On the F-Droid build it has one row, PaddleOCR's, for every language PaddleOCR reads.

## Scanning a document

**Document scanner** has two scanners behind it, and both work the same way from the keyboard's side: the scan runs full screen, outside the keyboard (an IME can't host that kind of flow itself), and the pages come back as JPEGs. They go into the field you scanned for, one after another. Some chat apps take only the first: WhatsApp opens its own send screen on it. Whatever pages the field didn't take then open together in that app's share screen, so WhatsApp sends them all as one album, and a toast says how many went that way.

- **Google's scanner** is the default on a phone with Google Play services. Edge detection, cropping and cleanup happen in Google's own UI, and gallery import is allowed alongside the camera.
- **The built-in scanner** is the keyboard's own, built on [OpenCV](https://opencv.org/). It is what the F-Droid build and phones without Google Play services use, and on any other phone you can pick it with **Use the built-in scanner**. If Google's scanner fails to start, the built-in one opens in its place.

  

The built-in scanner goes through three steps:

1. **Capture.** The camera outlines the page it sees as you aim. Press the shutter, or the photo button to pick a picture you already have. The light button turns on the flash as a torch.
2. **Corners.** The scanner places the four corners on the page it found. Drag any that missed; a magnifier shows the spot under your finger while you drag. When it finds no page, the corners start just inside the edges of the picture. The button beside the tick swaps between the corners it found and the whole picture. A red outline means the corners cross over each other, and the tick waits until they don't.
3. **Page.** The flattened page, with four looks: **Original**, **Colour** (the default: paper turned white and shadows lifted, ink and coloured areas kept), **Grey**, and **Black and white**. The turn button rotates it a quarter turn at a time. **Add page** keeps it and goes back to the camera for the next one; **Done** keeps it and sends every page.

Back steps back through those three. Leaving the camera with pages kept asks whether to send or discard them. The camera's permission is asked for on the scanner's own screen; without it, the photo button still works.

The built-in scanner needs its library, about 2.5 MB. Builds that don't carry it fetch it the first time you scan (or from the banner on the tool's settings page), the same way as the keyboard's other feature libraries: from Google Play on the Play build, and from GitHub elsewhere, which on F-Droid's build waits until you turn on [Download feature libraries](https://wmkeyboard.pages.dev/reference/settings/privacy/#downloads). Everything runs on your device.

Once you're back in your app, each captured page is inserted exactly like a Camera-tool photo, sent to the field one page at a time.

Tools / Document scanner / Options

- **Use the built-in scanner**: off by default, and shown only where Google's scanner can run. Scans with the keyboard's own scanner instead.
- **Save to the gallery**: off by default. Also keeps scanned pages in the **WM Keyboard** album under Pictures.

## Details & edge cases

- **Camera permission** works differently across the three tools. OCR and QR/barcode check for it themselves, and when it isn't granted they offer an **Allow the camera** button. That opens the same disclosure-then-system-prompt trampoline the [Camera tool](https://wmkeyboard.pages.dev/tools/camera/#granting-camera-access) uses, and returns you to the panel. The document scanner skips that prompt: Google's scanner manages its own camera permission, and the built-in one asks on its own screen.
- None of the three scanners work before you've unlocked your device once after a restart (no direct-boot support), unlike some other tools that do work at the lock screen.
- The trigger words in [Options](#options) are what make a standalone `ocr`, `scan` or `docscan` surface a chip into the matching tool. [Smart chips](https://wmkeyboard.pages.dev/smart/chips/) covers that mechanism in full.
- Open any of the three from Settings → **Tools** → **Scanners**, or by typing its keyword as described above.

## On the F-Droid build

Elsewhere text scan leans on Google: ML Kit's text-recognition library (and its Chinese, Japanese, Korean and Devanagari ones, whose models Google Play services fetches), and the LiteRT engine that offline voice typing also uses, for PaddleOCR. Beside them sits a Tesseract library built into the app, about 1.7 MB on a 64-bit phone. None of the three is in the F-Droid build. ML Kit and LiteRT are Google's, and Tesseract's library is one F-Droid can't build from source.

So the F-Droid build runs PaddleOCR on [MNN](https://github.com/alibaba/MNN) instead, an open-source engine from Alibaba that the build compiles from source for the [sticker generator](https://wmkeyboard.pages.dev/emoji/sticker-generator/). Both features share that one library, which arrives with the first download after you turn on [Download feature libraries](https://wmkeyboard.pages.dev/reference/settings/privacy/#downloads). It reads Chinese, Japanese and Latin script. Anything else needs the Online model reader. Google's document scanner depends on Google Play services' document scanner module, so F-Droid's document scanner is the built-in one, and its QR scanner reads with zxing-cpp; neither has any Google code in it. See [Which build you have](https://wmkeyboard.pages.dev/start/editions/) if you're deciding between builds.
