Making Tesseract Portable in MacOS with Runtime Linking

Photo by Alex Bachor on Unsplash

I like the plug and play experience. Recently, I developed a desktop app for people to learn Japanese, but it required MacOS users to first install brew and tesseract. It may not seem much effort to developers, but these extra steps could detract the user experience and make it less accessible.

On Windows, it is easy to compile the entire Tesseract into one exe file and all I did was copy a compiled Tesseract.exe from another Github repo (Probably not the safest idea).

On Mac, I have tried rebuilding all the separate dependencies and Tesseract but to no avail. I was using Python so I couldn’t simply import Tesseract dependencies to Python like you would in Swift or Objective C, and the tesserocr interface required interacting with Cython and compiling paths at build time…which would probably not work with relative paths.

otool & install_name_tool

$ otool -L /usr/local/Cellar/tesseract/4.1.1/bin/tesseract
/usr/local/Cellar/tesseract/4.1.1/bin/tesseract:
/usr/local/Cellar/tesseract/4.1.1/lib/libtesseract.4.dylib (compatibility version 5.0.0, current version 5.1.0)
/usr/local/opt/leptonica/lib/liblept.5.dylib (compatibility version 6.0.0, current version 6.3.0)
/usr/lib/libcurl.4.dylib (compatibility version 7.0.0, current version 9.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 800.7.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.0.0)

I was not even sure which dylib needed to be changed. That was why I first copied the Tesseract folder and its dependency folders from the installed Cellar folder to a new folder on my desktop.

tesseract
leptonica
giflib
jpeg
libpng
libtiff
openjpeg
webp

Then I ran the cloned tesseract, read the error log, and updated the dylib images that were missing.

$ cd tesseract_standalone/tesseract/4.1.1/bin/
$ sudo install_name_tool -change /usr/local/opt/leptonica/lib/liblept.5.dylib ../../../leptonica/1.80.0/lib/liblept.5.dylib tesseract

Executable Path

With a bit of searching, I found out that the system couldn’t find this library with just the usual relative path notation like ../ but required the use of @executable_path.

sudo install_name_tool -change /usr/local/Cellar/tesseract/4.1.1/lib/libtesseract.4.dylib @executable_path/../lib/libtesseract.4.dylib tesseract

With this extra dylib updated, I can now also run the tesseract executable by clicking on it or through pytesseract.

Here is the final script!

#!/bin/sh

if ! command -v tesseract &> /dev/null
then
brew install tesseract
fi

if [ "$EUID" -ne 0 ]
then echo "Please run as root"
exit
fi

mkdir tesseract_standalone
cp -r /usr/local/Cellar/tesseract tesseract_standalone/
cp -r /usr/local/Cellar/leptonica tesseract_standalone/
cp -r /usr/local/Cellar/libpng tesseract_standalone/
cp -r /usr/local/Cellar/libtiff tesseract_standalone/
cp -r /usr/local/Cellar/webp tesseract_standalone/
cp -r /usr/local/Cellar/openjpeg tesseract_standalone/
cp -r /usr/local/Cellar/jpeg tesseract_standalone/
cp -r /usr/local/Cellar/giflib tesseract_standalone/

cd tesseract_standalone/tesseract/4.1.1/bin/
install_name_tool -change /usr/local/opt/leptonica/lib/liblept.5.dylib ../../../leptonica/1.80.0/lib/liblept.5.dylib tesseract
install_name_tool -change /usr/local/Cellar/tesseract/4.1.1/lib/libtesseract.4.dylib @executable_path/../lib/libtesseract.4.dylib tesseract
install_name_tool -change /usr/local/opt/leptonica/lib/liblept.5.dylib ../../../leptonica/1.80.0/lib/liblept.5.dylib ../../../tesseract/4.1.1/lib/libtesseract.4.dylib
install_name_tool -change /usr/local/opt/libpng/lib/libpng16.16.dylib ../../../libpng/1.6.37/lib/libpng16.16.dylib ../../../leptonica/1.80.0/lib/liblept.5.dylib
install_name_tool -change /usr/local/opt/jpeg/lib/libjpeg.9.dylib ../../../jpeg/9d/lib/libjpeg.9.dylib ../../../leptonica/1.80.0/lib/liblept.5.dylib
install_name_tool -change /usr/local/opt/giflib/lib/libgif.7.dylib ../../../giflib/5.2.1/lib/libgif.7.dylib ../../../leptonica/1.80.0/lib/liblept.5.dylib
install_name_tool -change /usr/local/opt/libtiff/lib/libtiff.5.dylib ../../../libtiff/4.2.0/lib/libtiff.5.dylib ../../../leptonica/1.80.0/lib/liblept.5.dylib
install_name_tool -change /usr/local/opt/webp/lib/libwebp.7.dylib ../../../webp/1.2.0/lib/libwebp.7.dylib ../../../leptonica/1.80.0/lib/liblept.5.dylib
install_name_tool -change /usr/local/opt/openjpeg/lib/libopenjp2.7.dylib ../../../openjpeg/2.4.0/lib/libopenjp2.2.4.0.dylib ../../../leptonica/1.80.0/lib/liblept.5.dylib
install_name_tool -change /usr/local/opt/jpeg/lib/libjpeg.9.dylib ../../../jpeg/9d/lib/libjpeg.9.dylib ../../../libtiff/4.2.0/lib/libtiff.5.dylib

Language Enthusiast. Programmer.