Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

macOCR

Turn any text, QR code or barcode on your screen into text on your clipboard.

Run ocr, drag a box around anything on screen, and whatever was in that box is now on your clipboard as text. It works on things you cannot select: a screenshot someone sent you, a video still, a PDF that will not copy, an error dialog, a conference badge, a parcel label.

How it works

Everything happens on your Mac using Apple's Vision framework. No API keys, no uploads, no account, and it works offline.

brew install schappim/ocr/ocr
ocr

Contents


Install

Homebrew (recommended)

brew install schappim/ocr/ocr

Homebrew is the easiest path because ocr --update can then upgrade you in place later.

Download a binary

Grab a build from the latest release.

Apple Silicon:

curl -L -o macOCR-arm64.tar.gz \
  https://github.com/schappim/macOCR/releases/latest/download/macOCR-arm64.tar.gz
tar xzf macOCR-arm64.tar.gz
sudo mv ocr /usr/local/bin/ocr

Intel:

curl -L -o macOCR-x86_64.tar.gz \
  https://github.com/schappim/macOCR/releases/latest/download/macOCR-x86_64.tar.gz
tar xzf macOCR-x86_64.tar.gz
sudo mv ocr /usr/local/bin/ocr

Released binaries are ad-hoc signed rather than notarised, so macOS may refuse to run a downloaded copy. Clear the quarantine flag once and it will start:

xattr -d com.apple.quarantine /usr/local/bin/ocr

You can also build it yourself.

First run

macOCR takes a screenshot, so the first run asks for Screen Recording permission. Grant it, then run ocr again.

Enabling access to screen

If you are calling ocr from another app such as Alfred, Raycast or Shortcuts, the permission prompt belongs to that app, not to Terminal. Approve it there too.


Quick start

ocr

Your cursor turns into a crosshair. Drag a box. macOCR then:

  1. prints what it read to stdout, and
  2. copies the same text to your clipboard, ready to paste.

Press Esc to cancel the selection. Cancelling copies nothing and leaves your clipboard alone.

By default macOCR reads both the text in the region and the payload of any QR code or barcode it finds, in the order they appear on screen. You do not have to know a code is there or ask for it.

Five more things worth knowing on day one:

ocr -l ja-JP                    # read Japanese instead of English
ocr -i ~/Desktop/receipt.png    # read an image file, no screen capture
ocr --rect 0,0,800,600          # capture a fixed region, no dragging
ocr -b                          # read only the QR codes and barcodes
ocr --json                      # get structured output for scripts

Command reference

Option Short What it does
--help -h Show usage, plus where macOCR lives and how to update it
--version -v Print the version and the architecture of this binary
--update Check GitHub for a newer release and upgrade via Homebrew
Reading
--language <code> -l Recognition language, for example de-DE. Needs macOS 11+
--list-languages List the language codes this Mac can actually recognise
--barcodes -b Read only QR codes and barcodes, ignoring text
--no-barcodes Read only text, ignoring QR codes and barcodes
--symbologies <list> Only look for these code types, for example QR,EAN13
--list-symbologies List every barcode symbology this Mac can read
Input and output
--rect <x,y,w,h> -R Capture a fixed region instead of selecting one by hand
--input <file> -i Read an image file instead of capturing the screen
--save-image <path> -s Also keep the screenshot that was captured
--json Print structured JSON to stdout instead of plain text

Whatever stdout looks like, the clipboard always gets the plain text. That is the point of macOCR.


Reading text

Text comes out in the order Vision found it, one line per line of text:

$ ocr
Invoice #10428
Due 30 September
Total $1,240.00

That output is also on your clipboard, so you can go straight to V.

If macOCR reads nothing, it prints nothing and leaves your clipboard untouched, so a misjudged selection costs you a second attempt rather than whatever you had copied earlier.


Languages

macOCR recognises English out of the box. Pass -l to read something else:

ocr -l ja-JP            # Japanese
ocr -l zh-Hans          # Simplified Chinese
ocr --language de-DE    # German

To see what your Mac supports:

ocr --list-languages

The list depends on your macOS version, which is why the flag asks the system rather than reciting a fixed list. macOS 13 (Ventura) and later recognise around thirty languages, including Japanese, Korean, Russian, Ukrainian, Thai, Vietnamese, Arabic and Turkish. macOS 11 and 12 recognise eight: English, French, Italian, German, Spanish, Portuguese, and Simplified and Traditional Chinese.

Choosing a language needs macOS 11 (Big Sur) or later. On macOS 10.15 the -l flag is not available at all and macOCR reads English.

English stays in the list as a fallback when you pick another language, so a mostly-Japanese screenshot with an English URL in it still reads both.


QR codes and barcodes

macOCR reads codes as well as text by default. Both come out of a single pass over the image, so reading codes costs nothing over the text-only recognition macOCR has always done. You just stop missing the QR code that was sitting in the region.

Codes are placed among the text by where they sit on screen. Point macOCR at a poster with a heading, a QR code in the middle and a footer, and it reads top to bottom:

LITTLE BIRD ELECTRONICS
Scan to shop
https://littlebirdelectronics.com.au
Free shipping over $99
Sydney, Australia

The URL in the third line was the QR code.

Narrowing it down

ocr -b                  # only the codes, ignoring any text
ocr --no-barcodes       # only the text, the way macOCR behaved before 1.3.0

--symbologies restricts which kinds of code count, which helps when a label carries both a product barcode and a QR code and you only want one of them:

ocr --symbologies QR
ocr -b --symbologies EAN13,UPCE,Code128

Names are matched loosely, so QR, qr, GS1-DataBar and VNBarcodeSymbologyGS1DataBar all land on the same symbology. A name macOCR does not recognise is an error rather than a silent fall back to scanning for everything.

What your Mac can read

ocr --list-symbologies

Every supported macOS reads Aztec, Code39 (and its checksum and full-ASCII variants), Code93, Code93i, Code128, DataMatrix, EAN8, EAN13, I2of5, ITF14, PDF417, QR and UPCE.

macOS 12 (Monterey) adds Codabar, MicroQR, MicroPDF417 and the GS1 DataBar family (plain, Expanded and Limited). macOS 14 (Sonoma) adds MSI Plessey.

Codes that are not text

Some codes carry raw bytes rather than text. There is nothing sensible to put on the clipboard for those, so macOCR notes it on stderr instead of dropping the code silently. On macOS 14 and later the raw bytes are available as payloadBase64 in --json output.


JSON output

A bare payload does not tell you which code it came from, or where on screen it was. --json gives you the details:

ocr --json
[
  {
    "boundingBox" : {
      "height" : 0.0619,
      "width" : 0.5535,
      "x" : 0.2232,
      "y" : 0.8790
    },
    "confidence" : 1,
    "text" : "LITTLE BIRD ELECTRONICS",
    "type" : "text"
  },
  {
    "boundingBox" : {
      "height" : 0.4193,
      "width" : 0.3714,
      "x" : 0.3142,
      "y" : 0.2822
    },
    "confidence" : 1,
    "payload" : "https://littlebirdelectronics.com.au",
    "symbology" : "QR",
    "type" : "barcode"
  }
]

Reading the fields:

Field Notes
type "text" or "barcode"
text The recognised line. Text entries only
payload The decoded contents. Barcode entries only
payloadBase64 Raw bytes for a non-text code. macOS 14+, barcode entries only
symbology QR, EAN13, Code128 and so on. Barcode entries only
confidence 0 to 1, rounded to four places
boundingBox Position within the captured image

Bounding boxes use Vision's normalised coordinates: 0 to 1 across the captured image, with the origin at the bottom left rather than the top left. Treat that as a guide rather than a guarantee. From macOS 14, Vision can report a code that runs off the edge of the image, and those boxes go slightly negative or past 1.

Entries come out in reading order, the same order the plain text output uses.


Scripting with macOCR

Capture without touching the mouse

--rect takes a region in screen coordinates and skips the interactive selection, which is what you want from a cron job, a hotkey or a test:

ocr --rect 100,200,500,300     # x, y, width, height

All four numbers are required and must be integers, in screen points. The values are passed straight to /usr/sbin/screencapture -R. If the captured region is not where you expected, run it once with --save-image and look at the result to calibrate.

Read a file instead of the screen

ocr -i ./screenshot.png
ocr -i ~/Documents/scan.jpg

Anything Core Image can decode works: PNG, JPEG, TIFF, HEIC and so on. ~ is expanded for you.

Keep the screenshot

ocr --save-image ~/Desktop/capture.png

Normally the screenshot is written to a private temporary file, decoded, and deleted immediately, so macOCR does not leave copies of your screen lying around. --save-image keeps a copy where you ask for it. It has nothing to save when combined with --input, and says so.

Exit status

Command Found something Found nothing Error
ocr 0 0 1
ocr --no-barcodes 0 0 1
ocr -b 0 1 1

ocr -b is the only mode that treats an empty result as failure, because it is the one mode that exists solely to find codes. That lets a script tell "nothing there" from a successful read.

Errors also exit 1 and explain themselves on stderr: an unreadable file, an unknown symbology, a cancelled capture, two contradictory flags. So check that you actually got a payload rather than trusting the status on its own:

if payload=$(ocr -b) && [ -n "$payload" ]; then
  open "$payload"
else
  echo "No QR code in that region"
fi

With --json, ocr -b still prints [] before exiting 1, so a pipe into jq never sees a syntax error.


Recipes

Open the URL in a QR code on screen

ocr -b --symbologies QR | head -1 | xargs open

Pull just the barcode payloads out of a mixed capture

ocr --json | jq -r '.[] | select(.type == "barcode") | .payload'

Drop low-confidence lines

ocr --json | jq -r '.[] | select(.confidence > 0.8) | .text // .payload'

Watch a fixed part of the screen and log changes

while :; do
  ocr --rect 0,0,600,80 --no-barcodes
  sleep 5
done | uniq

Batch a folder of images into text files

for f in ~/Scans/*.png; do
  ocr -i "$f" --no-barcodes > "${f%.png}.txt"
done

Stock-take: scan a shelf label straight into a lookup

sku=$(ocr -b --symbologies EAN13,Code128) && open "https://example.com/sku/$sku"

Launchers, Shortcuts and hotkeys

macOCR is a plain command line binary, so anything that can run a command can run it: Alfred, Raycast, LaunchBar, Hammerspoon, Quicksilver, Keyboard Maestro, or macOS Shortcuts.

Ready-made integrations:

Building a Shortcut yourself (macOS 12+)

  1. Open Shortcuts and create a new Shortcut.
  2. Add a Run Shell Script action.
  3. Set the script to the full path of the binary:
    • /opt/homebrew/bin/ocr for Homebrew on Apple Silicon
    • /usr/local/bin/ocr for Homebrew on Intel, or a manual install
  4. Open Shortcut Details and tick Pin in Menu Bar.

Use the full path. Shortcuts does not load your shell profile, so a bare ocr will not be found.

Shortcut settings

Shortcut in the menu bar


Updating

Check what you have:

ocr --version     # version, architecture, and this repository's URL

Update:

ocr --update

--update asks GitHub for the latest release. If macOCR was installed with Homebrew it hands over to brew upgrade schappim/ocr/ocr. If you installed manually it prints the exact commands to replace the binary where it actually lives, and installs nothing you did not ask for.

If macOS has told you macOCR is an Intel app, --update notices it is running under Rosetta and points you at the native Apple Silicon build, even when the version itself is current.

Version notes

  • 1.3.0 added barcode and QR scanning, --json, --symbologies and --list-symbologies. It also stopped macOCR clearing your clipboard when it read nothing, and made --list-languages report the languages the recogniser actually supports rather than a fixed shorter list. Scripts that want the old text-only behaviour should add --no-barcodes.
  • 1.2.0 added --help, --version and --update. If ocr --help prints nothing useful, you are on an older build and should reinstall using the commands in Install.

All releases are listed on the releases page.


Requirements

  • macOS 10.15 (Catalina) or later
  • Screen Recording permission, for capturing the screen. Not needed for --input

Feature availability by version:

Feature Needs
Text recognition, QR and barcode scanning macOS 10.15
Choosing a language with -l macOS 11 (Big Sur)
Codabar, MicroQR, MicroPDF417, GS1 DataBar macOS 12 (Monterey)
Around thirty recognition languages macOS 13 (Ventura)
MSI Plessey, and payloadBase64 for non-text codes macOS 14 (Sonoma)

Universal builds are published for Apple Silicon and Intel.


Building from source

macOCR uses CocoaPods for two dependencies: ScreenCapture, which shells out to /usr/sbin/screencapture, and ArgumentParserKit.

git clone https://github.com/schappim/macOCR.git
cd macOCR
pod install
xcodebuild -workspace ocr.xcworkspace -scheme ocr -configuration Release \
  -derivedDataPath build \
  CODE_SIGN_IDENTITY="-" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
sudo cp build/Build/Products/Release/ocr /usr/local/bin/ocr

Open ocr.xcworkspace rather than ocr.xcodeproj if you are working in Xcode.

Releases are cut by GitHub Actions, which builds x86_64 and arm64 on every push and publishes both when a v* tag is pushed. The release job refuses to publish a tag that disagrees with ocrVersion in ocr/main.swift, so bump the constant and the tag together.


Troubleshooting

Nothing happens, or nothing is recognised

Check Screen Recording permission under System Settings → Privacy & Security → Screen Recording. Grant it to the app that launches ocr, which may be Alfred, Raycast or Shortcuts rather than Terminal. Quit and reopen that app afterwards.

"ocr" cannot be opened because Apple cannot check it for malicious software

Released binaries are ad-hoc signed rather than notarised. Clear the quarantine flag:

xattr -d com.apple.quarantine /usr/local/bin/ocr

macOS says macOCR is an Intel application

You have the x86_64 build on an Apple Silicon Mac. Run ocr --update, which detects Rosetta and points you at the arm64 build.

command not found: ocr from Shortcuts, Alfred or a launcher

Use the full path (/opt/homebrew/bin/ocr or /usr/local/bin/ocr). Those tools do not read your shell profile.

-l is rejected as an unknown option

Choosing a language needs macOS 11 or later. Below that, macOCR does not register the flag at all.

A language works with -l but is missing from --list-languages

That was a bug in builds before 1.3.0: the list was pinned to an older recogniser revision and under-reported what the Mac could do. Update to 1.3.0 or later.

Recognition quality is poor

Vision reads rendered text far better than small or blurry text. Zoom in before capturing, or capture at a larger size. Selecting a tighter region around just the text also helps.


Who made this?

macOCR was made by Marcus Schappi. I build software, and sometimes hardware, for real-world businesses:

Contributions are welcome. Open an issue or a pull request.

Thoughts on Sherlocking?

Apple, please sherlock this software!


License

MIT. Copyright 2021 Marcus Schappi.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

About

Get any text on your screen into your clipboard.

Resources

Stars

2.4k stars

Watchers

24 watching

Forks

Releases

Packages

Used by

Contributors

Languages