Paperman is an electronic filing cabinet: it scans paper into stacks of pages and lets you view, arrange, annotate, print, search and send them. It reads and writes PDF, JPEG and its own variant of PaperPort's .max file format, and runs on Linux, Windows and macOS.
On Ubuntu, install it from the PPA (ppa:sjg1/ppa). Windows installers
come with releases from the next one on, and until then from the
artifacts of the latest CI run
on master. See the
installation guide
for the rest, including macOS.
Full documentation is available at https://paperman.readthedocs.io
| Desktop view | Select scanner | Dated files | Open file | Preview | Preview2 |
|---|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
| Scanning | Fast folder finding | Scanning dialog | Side-by-side preview & OCR |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
- simple GUI based around stacks and pages
- view previews and browse through pages
- move pages in and out of stacks
- navigate through directories
- move stacks between directories
- double click to view full size page image (also on right pane)
- print stacks and pages, including page annotations
- email files as PDF via Gmail (Ctrl+Shift+E, paste with Ctrl+V)
- copy files as PDF to the clipboard (Ctrl+C, paste anywhere)
- full undo/redo
- fast scanning from sheet-fed scanners, through SANE on Linux and macOS and TWAIN on Windows, with presets; pages cut to the sheet and straightened, colourless pages stored as grey or mono, blank pages left out, sheets fed sideways turned upright, and a misfeed or jam waiting to be cleared without losing pages (see the scanning guide)
- scanning from the command line (
--scan) - OCR with full-text search
- a search server whose repositories the desktop can show and change over the network
new in 0.4:
- basic scanning
- partial creation of .max files (but monochrome images are uncompressed) (also it doesn't create greyscale/colour previews)
new in 0.5:
- better scanning, should now work properly with SANE and most scanners
- stacking/unstacking, moving files between directories, etc.
- colour previews are created (greyscale still not sorry)
- PDF, JPEG and TIFF conversion supported. Results may vary
- View quality menu to trade speed for quality
new in 0.7:
- better multithreaded scanning
- undo/redo
- better PDF support
- preview panes
- OCR engine
- zooming in and out in preview windows (hold down control and use mouse wheel)
- rewritten for QT4
- many many other changes and improvements
new in 0.7.1:
- a few bug fixes
new in 0.8
- first github release
- new icon
- cleaned up README
new in 1.0
- fully ported to QT4
- should build on very modern distributions that don't have QT3
new in 1.0.1
- builds with QT5, including on Ubuntu 20.04 Focal
new in 1.1
- presets for scanning, use Ctrl-1 to Ctrl-6 to quickly select
- fast folder finding in the scanning dialogue
- type the partial match in the 'Folder' field (press F6 to get there)
- it expects directories to be called yyyy and mmMMM, e.g. 2024/03mar/...
- press to scan, or click on the match to just create the dir
- filtering out of directories which don't match the current year / month
- keyboard shortcuts for A4 (Alt-A) and US letter/legal toggle (Alt-L)
- Fix most warnings
new in 1.2
- Import files from outside directories
- Avoid keeping all touched-files open
- Bring back command-line operation
- Presets (Ctrl-1 to Ctrl-6) for basic scanning operation
- Faster folder-selection within the scanning window
- Set the group for files
new in 1.2.1
- Expand tests to include common stacking/unstacking operations
- Fix various bugs in recent new features, particularly files not being opened before use
new in 1.3
- Fix problems with dir cache being out of date
- Add more tests for Dirmodel
- Drop the 'recently used' folder
- Set permissions on created files and directories
new in 1.3.1
- support Ubuntu oracular
- update build scripts to work on 24.04 host
- more tweaks to the findFolders() feature
new since 1.3.3 (not yet released)
- runs on Windows (10 and 11, x64 and Arm, with an installer) and macOS
- a TWAIN back end for scanners on Windows
- scanning: auto size and straightening by the scanner, auto colour, blank-page detection in colour, sideways feeding, a Long size for sheets longer than Legal, recovery from misfeeds and jams, Stop keeping the sheets already fed, and much faster transfer from Fujitsu scanners
- scanning from the command line, with
--logand--sane-debugfor finding out what went wrong - unfolding scanned booklets into their pages
- working with repositories on a paperman server: browsing, opening, changing and scanning into them, with changes shown as they happen
- a first run which offers somewhere to keep papers and lays the window out to suit the screen
- a fake Fujitsu scanner for testing without paper (Linux)
Convert a file to .jpg, the JPEG image file. This produces a separate file for each page of the document, with a _p addition. So a two-page document called fred,max will produce fred_p1.jpg and fred_p2.jpg
Convert a file to .max, Paperman's native format. The new file has the same name but with a .max extension.
Convert a file to PDF, The new file has the same name but with a .pdf extension.
Does an MD5 checksum of every page in every .max file in a directory. The results are written to a file called 'checksums.md5' in the current directory. For example:
maxview -s /paper
The file format is a line for each page, containing space-separated fields:
Length of filename in characters
Filename
Page number
Image size in bytes
md5 checksum, grouped by 32-bit words
For example:
27 Property taxes pd 2020.max 0 1093872 a3eb8cd8 b0cffff0 b845833c e370368e
27 Property taxes pd 2020.max 1 1093872 40f34c34 ae5f9549 158f97ab d6c1e6a8
27 Property taxes pd 2020.max 2 1093872 78572475 16422b9a c900d2a7 61ce49d6
27 Property taxes pd 2020.max 3 1093872 96562c4e cebfc3b3 5c863b22 d80abde6
Scans into a repository without showing the GUI, using the saved scanner
settings. The stack goes into the top-level directory of the first
configured repository unless --repo and --dir say otherwise, and the
scanner is the last one used unless --device names another. --pages
stops after that many sides; otherwise scanning continues until the
feeder is empty.
--set NAME=VALUE sets a scanner option by its SANE name before the scan
(for example mode=Color, resolution=200, source="ADF Duplex" or
page-height=355.6, with fixed-point values in their units) and can be
repeated. --auto-colour stores pages that turn out to have no colour
as greyscale, or as mono when they have no shading either, as the
Auto colour box in the scan window does. Progress is printed on stdout
and the exit code is 0 if at least one page was scanned. For example,
to scan two pages in colour into the 'inbox' directory of the
repository in ~/paper:
paperman --scan --repo ~/paper --dir inbox --pages 2 --set mode=Color
For a benchmark or a test scan that should not end up in a real
repository, point --repo at a throwaway directory: it is used for that
run only and is not added to the configured repositories.
mkdir -p /tmp/bench && paperman --scan --repo /tmp/bench --pages 50
Before every scan paperman turns on the fast-transfer settings a backend
offers: buffermode=On, so a scanner with memory scans ahead instead of
stopping after each sheet, and in colour compression=JPEG, so a side
arrives as under 1 MB rather than 25 MB. Both matter over USB (the
patched fujitsu backend has them; the fi-8950 runs at a third of its
speed without). A None chosen for compression in the scan dialog is
respected, and --set still overrides either.
With PAPERMAN_SCAN_STATS=1 in the environment, paperman prints a line
of statistics on stderr at the end of every scan, in the GUI as well as
with --scan: sides scanned, time per side, CPU used by the display and
scanning threads, and how far the display fell behind the scanner. This
is the first thing to look at if scanning seems slow, since it says
which side is the bottleneck.
To measure the scanner itself, tools/psip-bench.py talks the fi-8950's
network protocol directly, with neither the SANE backend nor paperman in
the way: it scans a number of sides (default 50) as fast as the scanner
will hand them over, prints the timing of every side with -v, and
reports the rate, how long the scanner made it wait for each side, and
how far the feeder ran ahead. Options vary resolution, pre-pick, divided
delivery and end-of-page detection. It always stops the feeder and
closes the session, so it can be interrupted with Ctrl-C.
tools/sane-bench.py does the same through SANE with scanimage, so it
works over USB (fujitsu backend) as well as the network, timing each side
as it completes; for a fujitsu device it adds buffermode=On and, in
colour, compression=JPEG, which the fi-8950 needs to run at speed over
USB.
With PAPERMAN_SNAP=<dir> set (or --snap <dir>), paperman saves a
snapshot of its window as it appears on screen once a second, as
snap-NNNN-<seconds>s.jpg in that directory; --clean-snaps (or
PAPERMAN_SNAP_CLEAN=1) removes the snapshots already there first. This shows what the display
actually showed and when, to set against a log, which is useful when the
display seems to lag behind what paperman reports. It grabs from
paperman's own event loop, so it misses the moments paperman is busy, and
it captures screen pixels, so it needs X11 (on Wayland the snapshots come
out empty).
Recursively process all .max files in a directory, performing OCR (Optical Character Recognition) on each page and saving the extracted text. The OCR text is saved both within the .max file as an annotation and in a searchable SQLite FTS5 index.
Features:
- Processes all .max files recursively in the specified directory
- Runs Tesseract OCR on each page to extract text
- Saves OCR text as an annotation in the .max file
- Builds a full-text search index (.paperindex) for fast searching
- If a file already has OCR text, it indexes the existing text without re-OCRing
- Runs in console mode (no GUI required)
Example:
paperman --ocr /paper/2024
This will:
- Scan all .max files in /paper/2024 and subdirectories
- Run OCR on each page (or use existing OCR text if available)
- Save the extracted text in each .max file
- Create a searchable index at /paper/2024/.paperindex
Search the OCR text index for files containing the specified query. Returns a list of matching files with page numbers and text snippets showing the context.
Features:
- Full-text search using SQLite FTS5 with porter stemming
- Returns ranked results with highlighted search terms
- Shows file path, filename, page number, and text snippet for each match
- Supports complex queries and boolean operators
- Runs in console mode (no GUI required)
Example:
paperman --search "invoice" /paper/2024
This will search the index at /paper/2024/.paperindex and display results like:
Found 3 results:
[1] expenses-jan.max (page 2)
Path: /paper/2024/01jan/expenses-jan.max
...payment details for <b>invoice</b> #12345...
[2] receipts.max (page 1)
Path: /paper/2024/02feb/receipts.max
...<b>invoice</b> dated February 15th...
If no directory is specified, searches the current directory.
Note: You must run --ocr on a directory first to create the search index.
Offers a fake Fujitsu fi-8170 beside the real scanners, for trying
something out without a scanner or paper to hand. It is listed as
FUJITSU fi-8170 (fake) and scans pictures put in DIR/hopper:
page.png is the front of a sheet and page.back.png its back, fed in
order of name. touch DIR/press-scan presses its Scan button. Setting
PAPERMAN_FAKE_SCANNER=DIR does the same. It is built on Linux only;
see doc/testing.rst for what it can do.
paperman --fake-scanner /tmp/fs
There is an installer for x64 PCs (paperman-setup-VERSION.exe) and one
for Windows on Arm (paperman-setup-VERSION-arm64.exe), with each release
from the next one on, and built by every CI run meanwhile; it needs
Windows 10 version 1809 or later, and no administrator. To scan, install
the scanner's 64-bit TWAIN driver: for the Ricoh fi-series that is
PaperStream IP (TWAIN x64), a separate download from the 32-bit one.
Sign out and back in after installing it, since PaperStream only sets the
scanner up for TWAIN when the user logs in.
To build it, use the MSYS2 MinGW64 toolchain, which is also what the CI job uses. In a MINGW64 shell:
pacman -S make mingw-w64-x86_64-gcc mingw-w64-x86_64-pkgconf \
mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-scxml \
mingw-w64-x86_64-poppler-qt6 mingw-w64-x86_64-podofo \
mingw-w64-x86_64-libtiff mingw-w64-x86_64-libjpeg-turbo \
mingw-w64-x86_64-tesseract-ocr mingw-w64-x86_64-tesseract-data-eng
qmake6 paperman.pro -o Makefile.win
make -f Makefile.win
make -f Makefile.win installer builds the installer too, with Inno
Setup installed.
Scanners are driven through TWAIN rather than SANE: every TWAIN data source installed on the machine appears in the device list. The TWAIN back end lives in win32/twainsane.cpp and presents the same options as SANE's fujitsu backend.
When bringing up a new scanner, run paperman from a shell with
PAPERMAN_TWAIN_DEBUG=1 set: it logs every TWAIN operation and the
capabilities the driver reports to stderr. If colours come out with red and
blue swapped, set PAPERMAN_TWAIN_RGB=1. PAPERMAN_TWAIN_DUMP=<file> writes
the first scanned image as a PBM/PGM/PPM, exactly as the back end hands it to
paperman, which separates driver problems from filing problems. Only the TWAIN 2 data source
manager (TWAINDSM.dll, installed with any current driver) is used; set
PAPERMAN_TWAIN_LEGACY=1 to fall back to the twain_32.dll shipped with
Windows.
Paperman builds on macOS with Qt 6 and Homebrew. Homebrew's Poppler has no Qt bindings, so Poppler is built separately:
brew install pkgconf podofo libtiff jpeg-turbo sane-backends cmake ninja \
openjpeg little-cms2 fontconfig
# Qt 6 from qt.io, or with aqtinstall:
# aqt install-qt mac desktop 6.11.3 clang_64 -m qtscxml -O ~/Qt
curl -LO https://poppler.freedesktop.org/poppler-26.09.0.tar.xz
tar xf poppler-26.09.0.tar.xz
cmake -S poppler-26.09.0 -B pbuild -G Ninja -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$HOME/poppler \
-DCMAKE_PREFIX_PATH="$HOME/Qt/6.11.3/macos;$(brew --prefix)" \
-DENABLE_QT5=OFF -DENABLE_GLIB=OFF -DENABLE_NSS3=OFF \
-DENABLE_GPGME=OFF -DENABLE_LIBCURL=OFF -DENABLE_BOOST=OFF \
-DENABLE_HARFBUZZ=OFF
ninja -C pbuild install
export PKG_CONFIG_PATH=$HOME/poppler/lib/pkgconfig:$(brew --prefix)/lib/pkgconfig
~/Qt/6.11.3/macos/bin/qmake6 paperman.pro -o Makefile
make -f Makefile
That makes paperman.app; a build with CONFIG+=test makes a plain
paperman instead, for the tests. Use make -f Makefile, since the
GNUmakefile in the repository builds the other programs too. Scanners
are reached through SANE, as on Linux, from Homebrew's sane-backends;
a scanner too recent for that release, such as the fi-8950, needs a
newer SANE built from source.
On an Intel Mac, Homebrew has no ready-built packages for the latest
macOS, so brew install needs --build-from-source.
Free code signing provided by SignPath.io, certificate by SignPath Foundation.
Only the Windows program and installer which this project builds are signed, by its release workflow, once an approver approves each release. Committers, reviewers and approvers: Simon Glass (@sjg20).
This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it.
See the full policy for how releases are signed.
Paperman includes a comprehensive test suite to verify functionality. Tests are built using the Qt Test framework.
To build paperman with test support enabled:
qmake "CONFIG+=test" paperman.pro
make
Run all tests:
env QT_QPA_PLATFORM=offscreen ./paperman -t
This will run all test suites including:
- utils - Utility function tests
- ops - File operation tests (duplicate, stack, unstack, rename, etc.)
- searchserver - HTTP search server tests
- ocrsearch - OCR indexing and search functionality tests
The OCR search test suite (test/test_ocrsearch.cpp) includes:
- testOcrIndexing() - Tests creating a search index and adding OCR text
- testOcrSearch() - Tests searching indexed text and verifying results
- testReindexing() - Tests re-indexing existing OCR text
- testSearchNoResults() - Tests handling of queries with no matches
All tests use temporary directories and clean up after themselves.
********* Start testing of TestOcrSearch *********
PASS : TestOcrSearch::initTestCase()
PASS : TestOcrSearch::testOcrIndexing()
PASS : TestOcrSearch::testOcrSearch()
PASS : TestOcrSearch::testReindexing()
PASS : TestOcrSearch::testSearchNoResults()
PASS : TestOcrSearch::cleanupTestCase()
Totals: 6 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of TestOcrSearch *********
Here's what I'd like it to support:
- more operations on images
- support for more image types (at the moment only JPEG is supported)
If you have other ideas then feel free to let me know on the mailing list.
Simon Glass sjg@chromium.org Aug 2020









