Development

This guide covers building paperman from source and working on the codebase.

Prerequisites

The quickest way to set up a build environment is:

make setup

This installs all Debian packages, the Flutter SDK, Java JDK and the Android SDK. For a lighter install that only covers the Qt5/C++ desktop app and server:

scripts/setup.sh --qt

The full list of what the script installs is documented in scripts/setup.sh. The manual steps are shown below for reference.

Qt5/C++ dependencies (desktop app + server):

sudo apt-get install -y \
  build-essential qt5-qmake qtbase5-dev qtbase5-dev-tools \
  libqt5sql5-sqlite libpoppler-qt5-dev libpodofo-dev \
  libtiff-dev libjpeg-dev libsane-dev zlib1g-dev \
  imagemagick tesseract-ocr tesseract-ocr-eng

Qt6/C++ dependencies (alternative to Qt5):

sudo apt-get install -y \
  build-essential qmake6 qt6-base-dev qt6-base-dev-tools \
  libqt6sql6-sqlite libpoppler-qt6-dev libpodofo-dev \
  qt6-scxml-dev libqt6statemachine6 \
  libtiff-dev libjpeg-dev libsane-dev zlib1g-dev \
  imagemagick tesseract-ocr tesseract-ocr-eng

Python packages for demo-asset generation and documentation:

sudo apt-get install -y \
  python3-reportlab python3-pil python3-numpy poppler-utils \
  python3-sphinx python3-sphinx-rtd-theme

Flutter app (Android + Linux desktop) additionally needs the Flutter SDK, Java 21 and several Linux desktop libraries. See scripts/setup.sh for the full details.

Building

Generate the qmake Makefile and build with Qt5:

qmake "CONFIG+=test" paperman.pro
make

Or with Qt6:

qmake6 "CONFIG+=test" paperman.pro -o Makefile.qt6
make -f Makefile.qt6

The CONFIG+=test flag compiles in the built-in test suites. Without it the -t option is not available.

The top-level GNUmakefile wraps the qmake-generated Makefile and adds extra targets. GNU make picks it up automatically, so a plain make in the project root builds everything including the server, Flutter app and Sphinx documentation. Run make help to list all targets.

Project Layout

*.cpp / *.h           Desktop app sources (Qt Widgets)
qi/                   Scanner interface widgets
test/                 Test sources and generated test data
scripts/              Helper scripts (test-file generation, etc.)
doc/                  Sphinx documentation
app/                  Flutter mobile app
paperman.pro          Qt project file (desktop app)
paperman-server.pro   Qt project file (search server)
GNUmakefile           Top-level build wrapper

Key Source Files

desktopmodel.cpp

Central model that manages the stack/page tree shown in the GUI.

file.cpp, filemax.cpp, filepdf.cpp, filejpeg.cpp

File-format backends. File is the abstract base; the subclasses handle .max, .pdf and .jpg respectively.

dirmodel.cpp

Directory tree model for the left-hand panel.

desktopundo.cpp, dmop.cpp, dmuserop.cpp

Undo/redo framework and desktop-model operations.

searchserver.cpp

Embedded HTTP server for the REST API.

searchindex.cpp

SQLite FTS5 index used by the search server.

Testing

See Testing for full details on running and writing tests.

Quick reference:

# Run all suites
QT_QPA_PLATFORM=offscreen ./paperman -t

# Run a single suite
QT_QPA_PLATFORM=offscreen ./paperman -t TestOps

# List available suites
./paperman -t list

# Generate test files (also done automatically by 'make test')
make test-setup

Test suites live in test/ and are registered in test/suite.cpp. Each suite is a QObject subclass using the Qt Test framework (QCOMPARE, QVERIFY, etc.). Test data files are generated at build time by scripts/make_test_files.py and are not tracked in git.

Build Targets

The most useful targets during development:

make setup             # Install all build dependencies
make paperman          # Build the desktop app only
make test              # Build and run all tests (includes test-setup)
make test-setup        # Generate test files without running tests
make app-test          # Run Flutter widget tests
make docs              # Build the Sphinx documentation
make clean             # Remove all build artefacts

Continuous Integration

A GitHub Actions workflow (.github/workflows/ci.yml) runs on every push to master and on pull requests. It has four parallel jobs:

qt — Desktop app, server and C++ tests (Qt5)

Installs Qt5/C++ dependencies, builds paperman and paperman-server, generates test data, then runs the Qt unit tests, page-fetch integration test and parallel conversion test.

qt6 — Desktop app, server and C++ tests (Qt6)

Same as qt but builds against Qt6 instead of Qt5.

flutter — Flutter app and widget tests

Sets up Java 21 and Flutter 3.41.1, generates demo assets, runs flutter analyze and flutter test, then builds a release APK and uploads it as a build artefact.

docs — Sphinx documentation

Installs Python 3.12, the Sphinx dependencies from doc/requirements.txt and builds the HTML documentation.

Coding Style

  • C++ sources use Qt naming conventions (camelCase for methods, CamelCase for classes)

  • Indentation is 3 spaces in most files

  • Commit messages use present/imperative tense and British spelling

Main Classes

The display is laid out as follows:

Mainwindow (QMainWindow)
+-- Mainwidget (QStackedWidget)
    |
    +-- Page 0: Desktopwidget (QSplitter, horizontal)
    |   |
    |   +-- LEFT: Dirview (QTreeView)
    |   |         Model: Dirmodel
    |   |
    |   +-- MIDDLE: QWidget "group"
    |   |   +-- Toolbar
    |   |   +-- Desktopview (QListView, IconMode)
    |   |       Delegate: Desktopdelegate
    |   |       Model: Desktopmodel via Desktopproxy
    |   |
    |   +-- RIGHT: Pagewidget
    |       +-- Pagetools (toolbar, from pagetools.ui)
    |       +-- QStackedWidget
    |           +-- _splitter (QSplitter, horizontal)
    |           |   |
    |           |   +-- LEFT: Pageview (QListView, IconMode)
    |           |   |         Delegate: Pagedelegate
    |           |   |         Model: Pagemodel
    |           |   |
    |           |   +-- RIGHT: _ocr_split (QSplitter, vertical)
    |           |       +-- TOP: MyScrollArea (big page preview)
    |           |       +-- BOTTOM: OCR area (QTextEdit + Ocrbar)
    |           |
    |           +-- _textframe (annotation/info view)
    |
    +-- Page 1: Pagewidget (single-stack view, used for full-screen)