Metadata-Version: 2.4
Name: scantpaper
Version: 4.0.3
Summary: GUI to produce PDFs or DjVus from scanned documents
Author-email: Jeffrey Ratcliffe <jffry@posteo.net>
Maintainer-email: Jeffrey Ratcliffe <jffry@posteo.net>
License: GPL-3.0-only
Project-URL: Homepage, https://github.com/carygravel/scantpaper
Project-URL: Repository, https://github.com/carygravel/scantpaper.git
Project-URL: Bug Tracker, https://github.com/carygravel/scantpaper/issues
Keywords: scan,pdf,djvu
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: X11 Applications :: GTK
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Programming Language :: Python
Classifier: Topic :: Multimedia :: Graphics :: Capture :: Scanners
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: img2pdf
Requires-Dist: ocrmypdf>=16.10.0
Requires-Dist: pikepdf
Requires-Dist: pycairo
Requires-Dist: PyGObject
Requires-Dist: python-iso639
Requires-Dist: python-sane
Requires-Dist: tesserocr
Requires-Dist: tomli>=1.1.0; python_version < "3.11"
Requires-Dist: typing_extensions>=4.4.0
Provides-Extra: test
Requires-Dist: polib; extra == "test"
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: pytest-mock; extra == "test"
Requires-Dist: pytest-timeout; extra == "test"
Requires-Dist: pytest-xvfb; extra == "test"
Requires-Dist: tomli; extra == "test"

# Scantpaper

A GUI to produce PDFs or DjVus from scanned documents.

Scantpaper is a Linux application (it needs GTK4, SANE, and a Python 3
interpreter, all of which are available on other Unix-like systems like MacOS or BSD as well).
It is the Python rewrite (v3) of the popular
[gscan2pdf](https://gscan2pdf.sourceforge.net/).

[![Test package](https://github.com/carygravel/scantpaper/actions/workflows/test.yml/badge.svg)](https://github.com/carygravel/scantpaper/actions/workflows/test.yml)
[![Build packages](https://github.com/carygravel/scantpaper/actions/workflows/deb.yml/badge.svg)](https://github.com/carygravel/scantpaper/actions/workflows/deb.yml)
[![Release](https://img.shields.io/github/v/release/carygravel/scantpaper)](https://github.com/carygravel/scantpaper/releases)
[![License](https://img.shields.io/badge/License-GPL--3.0--only-blue.svg)](https://www.gnu.org/licenses/)

- **Scan** single- or double-sided, with automatic interleaving
- **OCR** with tesseract for searchable PDF/A, without the need for temporary files
- **Edit** with crop, rotate, threshold, unsharp mask, and unpaper clean-up
- **Save** as PDF, DjVu, TIFF, PS, TXT, hOCR, or image files
- **Recover** crashed sessions and restore them on the next start

<p align="center">
    <img width="1254" height="656" alt="Screenshot of the scantpaper main window with thumbnails on the left and a page preview on the right" src="screenshot.png" />
    <em>Screenshot: Main page</em>
</p>

---

## Table of Contents

- [Quick Start](#quick-start)
- [Description](#description)
- [Command-line Options](#command-line-options)
- [Diagnostics](#diagnostics)
- [Configuration](#configuration)
- [Dependencies](#dependencies)
- [Download, Installation & Removal](#download-installation--removal)
- [Support](#support)
- [Reporting Bugs](#reporting-bugs)
- [Translations](#translations)
- [FAQs](#faqs)
- [Known Limitations](#known-limitations)
- [See Also](#see-also)
- [History](#history)
- [Author](#author)
- [Thanks To](#thanks-to)
- [Contributing](#contributing)
- [Donate](#donate)
- [License](#license)

---

## Quick Start

Install scantpaper and its dependencies (see [Download, Installation & Removal](#download-installation--removal)), then:

1. Start the application with `scantpaper` (or `python3 -m scantpaper.app` from a source checkout, with `PYTHONPATH=src` set).
   Add `--debug|info|warn|error|fatal` to enable logging at the required level.
2. Scan one or several pages with **File → Scan**. Activating **Scan** while
   scan options are still being applied starts the scan when the apply
   finishes, so a second click is not needed.
3. Select the pages and create a PDF with **File → Save**.
4. To make the saved PDF searchable, enable **OCR** in the scan window or run **Tools → OCR** before saving.

---

## Description

scantpaper provides a GUI for scanning, editing, and saving documents as PDF, DjVu, TIFF, PS, TXT, hOCR, SDB (scantpaper session), or image files (PNG, JPEG, PNM, GIF), and can prepend or append to an existing PDF. It supports batch scanning, metadata, OCR, and various editing tools.

### How it works

Scans are acquired with SANE and held in a session database while you edit
them. When saving, PDFs are produced with `img2pdf` and OCR'd with `ocrmypdf`
(which produces PDF/A out of the box); DjVu export uses `djvulibre-bin`, TIFF
export uses `libtiff`, and images are written with ImageMagick.

```
┌─────────┐   ┌─────────────────┐   ┌──────────────┐   ┌──────────────────┐
│  SANE   │   │ SQLite session  │   │ edit tools / │   │ img2pdf /        │──▶ PDF (PDF/A)
│ scanner │──▶│ (pages in temp  │──▶│ OCR          │──▶│ ocrmypdf         │
│         │   │  directory)     │   │ (tesseract)  │   ├──────────────────┤──▶ DjVu
└─────────┘   └─────────────────┘   └──────────────┘   │ djvulibre-bin    │
                                                       ├──────────────────┤──▶ TIFF
                                                       │ libtiff          │
                                                       ├──────────────────┤──▶ PNG, JPEG, PNM, GIF
                                                       │ imagemagick      │
                                                       └──────────────────┘
```

### Page Numbering

Page numbers are always consecutive (1, 2, 3, …). Deleting a page renumbers the
remainder automatically, and editing a page's number in the document table moves
that page to the corresponding position.

### Scan Flow

- **Single-sided:** Each scan is appended at the end.
- **Double-sided:** Scan all facing pages first (front 1, front 2, …, front n);
  they are appended in order. When you flip the stack the ADF feeds them in
  reverse (back of page n, then back of page n-1, …, back of page 1). Each
  reverse page is inserted immediately after its matching front page, producing
  a fully interleaved result: front 1, back 1, front 2, back 2, …, front n,
  back n.

  ```
  scan pass 1 (fronts):     front 1, front 2, ..., front n
  flip stack
  scan pass 2 (backs):      back n, back n-1, ..., back 1
  interleaved result:       front 1, back 1, front 2, back 2, ..., front n, back n
  ```
- **Extended mode (insert before page N):** Each new scan is inserted before the
  selected page, advancing the insertion point for the next scan.

#### ADF / duplex scanning

Automatic document feeder (ADF) and duplex scans now import every side of the
document. Some Brother scanners (e.g. the DS-740D) prefetch the reverse side of a
sheet as soon as the front side finishes reading; cancelling the scan session
between pages discarded that buffered side, so only the first page of a duplex
job was imported. Scantpaper no longer cancels the session between pages when
scanning from a feeder, which preserves the prefetched side. The session is
still cancelled at the end of the batch, when the requested page count is
reached, on error, or when you cancel the scan.

For multi-page flatbed batches the behaviour follows gscan2pdf semantics: the
**Force new scan job between pages** preference (enabled via
**Edit → Preferences**, only available when **Allow batch scanning from
flatbed** is enabled) controls whether the session is cancelled between flatbed
pages; the session is always cancelled at the end of the batch. Switching to a
flatbed source starts **# Pages** at one page. To scan an unbounded flatbed
batch, choose **All** explicitly; the dialog then warns that scanning continues
until you cancel. Cancelling stops the batch after the page already in
progress. If the scanner does not respond to cancel, scantpaper reports the
failure and rebuilds the device session for the next scan; a device still held
by the operating system is reported as busy rather than leaving the
application unable to scan.

When scanning a book destructively (cutting off the binding and aligning every
untorn outer edge to the same rim), enable **Alternate rotation every 2nd
page** in the **Postprocessing** tab. Odd pages then use the configured
rotation angle, even pages use that angle plus 180 degrees, so the flipped
back of each sheet comes out upright too. The toggle is only visible for
flatbed scanning (including one-page-at-a-time manual scans) when batch scanning
from the flatbed is allowed; rotation still runs before OCR. In manual
(single-page) mode the alternating parity persists across consecutive scans until
you explicitly turn the toggle off.

#### Hiding unwanted devices

SANE backends sometimes list a webcam or another non-scanner as a device. Select
it in the **Device** list and click **Blacklist this device** to hide it; the
pattern is stored in the **Device blacklist** field in **Edit → Preferences**.
That field also accepts a regular expression, matched case-insensitively
against the label shown in the list and the device's backend name, so patterns
such as `Camera` or `v4l:.*` work. If a pattern hides every device, scantpaper
reports that no devices were found rather than offering an empty list.

### Main Features

- **Scan:** Options for device, page count, source document, side to scan, and
  device-dependent options (page size, mode, resolution, batch-scan, etc.).
  Optionally rotate (including alternating by page parity for book flatbed
  scans), clean up with unpaper, or run a user-defined tool on each page on
  scan, and OCR each page. When applying a scan-options profile, the
  scanner driver may revert an option (some drivers couple or alias options,
  e.g. page size and scan area); such an option is then set at most twice and
  dropped for the rest of that apply, so the scan still proceeds instead of
  ending in a reload-recursion error.
  If the scan options request 1-bit output - a 1-bit depth, or a bilevel
  scan mode such as Lineart - the delivered pages are checked
  against that request. Bilevel transfers always arrive as 8-bit grayscale
  (the SANE front-end expands them, even when the driver sends 1-bit data,
  for example when a hardware compression option is active), so scantpaper
  converts each such page to black-and-white at scan time,
  using the colour-aware Threshold semantics and value, shows a notice in the
  progress bar, and logs a warning - so "Automatic" PDF compression encodes
  the pages as Group 4 or JBIG2 rather than JPEG.
  Scan-area and page-size fields accept fractional values typed with your
  locale's decimal separator (e.g. `115,2` mm in a German locale), show them
  without trailing zeros (`174` mm, not `174,00`), and step by whole units with
  the arrows while keeping the typed fraction. Only separators your locale
  actually uses are accepted when typing numbers; a period in a comma locale is
  rejected rather than silently misinterpreted. The same fractional entry
  applies to the page-properties resolution fields and to the
  blank/dark-page and unpaper white/black thresholds, so e.g. `300` dpi and
  `0,9` are shown exactly as typed.
- **Save:** Save selected/all pages in multiple formats. Supports metadata. The
  Title, Author, Subject, and Keywords fields offer autocompletion, suggesting
  values from imported documents and values you have entered before. When saving
  as PDF, the progress bar tracks each page as it is written and reports the PDF
  conversion step. Imported images are stored in a compact format (JPEG for
  scanned pages, the original bytes for imported JPEG/PNG files, lossless PNG
  for bilevel, transparent, or posterised grayscale pages). When saving as PDF,
  the default **Automatic** compression picks an encoding per page: lossless
  CCITT Group 4 for bilevel pages, lossless Flate for images with transparency
  or posterised (few-level) grayscale, and JPEG for other continuous-tone
  colour or grayscale pages at the configured **Quality**.
  The **Compression** menu also offers an explicit codec for every page:
  **G4**, **Flate**, **JPEG** and **JPEG 2000**,
  plus **JBIG2** when the optional `jbig2` encoder is installed. JPEG 2000
  usually produces smaller files than JPEG for scanned pages but is slower to
  encode and less widely supported by viewers; JBIG2 typically halves the size
  of a bilevel page compared with Group 4. scantpaper always uses JBIG2's
  lossless mode (see
  [JBIG2 and the Xerox character-substitution
  bug](#jbig2-and-the-xerox-character-substitution-bug)), so the saved bitmap
  is unchanged; the exact output size depends on the installed encoder
  version, and like G4 it thresholds grayscale and colour pages to bilevel
  first. The **Bilevel compression** preference in
  **Edit → Preferences** (Group 4 by default, JBIG2 when available) tells
  **Automatic** how to encode bilevel pages, so a user who never changes the
  preference keeps Group 4 output. The **Quality** spin-button steers both
  JPEG and JPEG 2000, and lower values shrink those streams. Unmodified
  imported JPEG pages are embedded byte-for-byte, while pages that
  scantpaper generated by scanning or editing are re-encoded at the configured
  quality, so lowering **Quality** shrinks the PDF. A lossless encoding
  (Automatic's Group 4 or Flate handling, or the explicit **Flate** option) is
  preserved through the PDF/A conversion, so the page image is not silently
  transcoded to JPEG, on every supported OCRmyPDF version. Saving to a file
  that already exists
  asks for confirmation first: declining leaves the existing file untouched
  and keeps the save dialog open, and a save that fails reports the error
  instead of silently doing nothing. When saving as PDF, pages are processed
  one at a time and page images are decoded on demand, so saving a document
  with many high-resolution pages no longer keeps every decoded page in
  memory and no longer exhausts the memory of the process.
- **Import hOCR:** Apply a saved hOCR file to the selected pages or all pages.
  Pages are matched in order, the page box must match the target page's pixel
  size, and only the text layer is replaced; images and annotations are left
  untouched. This is useful for restoring corrected OCR after rebuilding a
  session from the original images.
- **Thumbnails:** Page thumbnails are generated with a decimate-then-LANCZOS
  downscale for a sharp preview in the thumbnail panel without slowing down
  bulk imports, and bilevel or palette pages (common in scanned/OCR PDFs) are
  anti-aliased so their previews stay legible.
- **Lists:** Typing with a list widget focused (the thumbnail panel, or a
  picker such as the **File → Open crashed session** dialog) no longer opens
  GTK's interactive-search overlay, which showed nothing because no list
  configures a search column. It also removes the `gtk_css_node_insert_after`
  Gtk-CRITICAL that GTK 4.24 printed in its place on the first such keystroke.
- **Email as PDF:** Attach pages as PDF to a blank email (requires xdg-email).
- **Print:** Print selected/all pages.
- **Dialogs:** Every dialog insets its content and its action buttons from the
  window edge, spaces its rows apart and spaces the controls within a row
  apart, so no control touches the window border and a label no longer sits
  flush against the field beside it. Action buttons are also separated from
  each other; a message dialog keeps its joined button bar. In the scan, Clean
  up and Preferences dialogs, rows keep the height their content needs when
  the window is taller than the page: the surplus appears as empty space below
  the last row instead of stretching a frame, checkbox or label-and-control
  row. A message dialog keeps its "Don't show this message again" control in
  a fixed row below the scrolling list, gives the spare height to the list,
  and shows each message as selectable text. A message without a page number
  leaves its Page cell empty instead of showing "None". Escape and the window
  close control close it with a response, so the caller returns and the dialog
  does not trip the GTK4 IBus module's focus-out crash.

### JBIG2 and the Xerox character-substitution bug

JBIG2 is a format for bilevel images (ITU-T T.88 / ISO/IEC 14492), not a
single compression mode. It defines lossless and lossy coding. In the lossy
mode an encoder can replace similar-looking shapes with one shared symbol
and discard the difference; if that matching is done badly, a 6 can decode
as an 8. That is what happened in 2013 on some Xerox WorkCentre and
ColorQube devices, whose firmware used lossy pattern matching and
substitution. The fault was in the encoder, not in JBIG2 as a format; David
Kriesel, who reported the bug, concluded that "the error cause is not JBIG2
itself".

scantpaper always uses the lossless path. It converts only bilevel pages,
one image at a time, with no shared symbol dictionary and no lossy
substitution, and it never enables OCRmyPDF's lossy JBIG2 option. The JBIG2
step therefore reproduces the bilevel bitmap exactly. OCRmyPDF 17 removed
its lossy JBIG2 support for the same reason and now encodes each image
independently and losslessly.

"Lossless" describes the compression step, not the whole scan. If you
choose JBIG2 for a grayscale or colour page, scantpaper thresholds it to
bilevel first, exactly as it does for Group 4, and that thresholding is
where detail is lost. No compression setting can repair a scan that was
already wrong.

PDF/A does not require lossless compression. ISO 19005 permits lossy
compression, and PDF/A allows JBIG2 in both its lossless and lossy forms, so
a PDF/A stamp is not evidence that a scan used the lossless mode.
scantpaper's PDF/A output is lossless because scantpaper asks for the
lossless encoder path, not because PDF/A forces it. Some archival
guidelines are stricter still: Germany's BSI TR-03138 for replacement
scanning forbids pattern matching and substitution, and also soft pattern
matching; together those rule out JBIG2's symbol-coding modes, including the
lossy pattern-matching-and-substitution method behind the Xerox bug. That
is a scanning policy, not a PDF/A rule, so check the recipient's
requirements if you scan for one.

Further reading:

- [JBIG2](https://en.wikipedia.org/wiki/JBIG2) — Wikipedia, especially
  "Character substitution errors in scanned documents".
- [Xerox § Character substitution bug](https://en.wikipedia.org/wiki/Xerox#Character_substitution_bug)
  — Wikipedia.
- [Xerox scanners/photocopiers randomly alter numbers in scanned documents](https://www.dkriesel.com/en/blog/2013/0802_xerox-workcentres_are_switching_written_numbers_when_scanning)
  — David Kriesel's original report.
- [ITU-T Recommendation T.88](https://www.itu.int/rec/T-REC-T.88-200002-I/en)
  — the JBIG2 standard.
- [ISO 19005-2:2011 (PDF/A-2)](https://www.iso.org/standard/50655.html)
  — its introduction notes that a conforming file may use lossy
  compression.
- [PDF Association, PDF/A FAQ](https://pdfa.org/pdfa-faq/)
  — confirms that compression, including lossy compression, is permitted.
- [OCRmyPDF: Installing the JBIG2 encoder](https://ocrmypdf.readthedocs.io/en/stable/jbig2.html)
  — states that OCRmyPDF uses lossless JBIG2.

### Edit Menu

- **Undo, Redo:** Undo or redo the last action. An operation applied to
  several pages at once (for example rotating or cropping a selection) is
  undone or redone as a single step, reverting every affected page
  together. Text and annotation corrections are undone and redone the same
  way; undoing an edit also restores the page's previous saved state.
  Clearing the document with **New File** is itself an undo step: undo right
  after it restores the cleared pages, and undoing a later edit never
  resurrects pages that were cleared before it.
- **Cut, Copy, Paste:** Cut, copy, or paste selected pages.
- **Delete:** Remove selected pages.
- **Select:** Select all, odd, even, inverted, blank, dark, or modified pages, or pages without (up-to-date) OCR.
- **Properties:** Edit image metadata.
- **Preferences:** Configure default behaviors and frontends.

### View Menu

- Pan, Select, Select & Pan tools, for both the image and the text-layer or
  annotation canvas:
    - Pan: Use the left mouse button to drag the image or canvas to pan the
      view. The middle mouse button does nothing.
    - Select: Use the left mouse button to select a rectangular box. The
      middle mouse button does nothing.
    - Select & Pan: Use the left mouse button to select a rectangular box, and
      the middle mouse button to drag the image or canvas to pan the view.
    - In all of the above, the mouse wheel zooms in or out.
    - When no page is displayed in the image view, pressing or dragging does
      nothing.
- Clear image selection: Remove the selection rectangle from the image and
  layer panes. The same action is also on the toolbar next to the selection
  tools and available from the keyboard with Escape.
- Zoom: 100%, fit to window, in, and out.
- Rendering: pages are shown at high quality whenever they are static, so the
  full-page view is easy to assess; a faster rendering is used only while you
  are panning or zooming, for smooth interaction.
- Rotate: 90° clockwise, 180°, and 90° anticlockwise.
- Layer editing: choose **Edit text layer** or **Edit annotations** to work on
  that layer. In the tabbed view the selected tab decides which layer is shown,
  and its control bar appears below the image only while that tab is open (on
  the Image tab neither layer is selected and no control bar is shown). In a
  split view the chosen layer is displayed beside the image, so the annotation
  layer can be corrected next to the scan as well as the text layer. The layer
  you were last editing is remembered when you restart. The control bar has a
  fixed height: it appears or disappears with its layer immediately, and cannot
  be resized by dragging. Click a slice to focus
  it for correction; with the Select or Select & Pan tool active, press or drag
  anywhere else in the pane to start a new selection rectangle, while the Pan
  tool pans instead. Focusing a slice zooms so its line is drawn at a
  consistent, comfortable reading size rather than a fixed fraction of the
  pane, so short and long words from the same line look alike and a long word
  still fits with a margin around it. The zoom button beside the navigation
  controls opens a **Focus zoom** slider (50-200%, default 100%) that adjusts
  that size while you work. **Reset** returns it to 100%, and the chosen level
  is remembered across restarts and applies to both the text and annotation
  layers.

### Keyboard Shortcuts

| Action       | Shortcut       |
|--------------|----------------|
| New          | Ctrl+N         |
| Open         | Ctrl+O         |
| Scan         | Ctrl+G         |
| Save         | Ctrl+S         |
| Email as PDF | Ctrl+E         |
| Print        | Ctrl+P         |
| Quit         | Ctrl+Q         |
| Undo         | Ctrl+Z         |
| Redo         | Ctrl+Shift+Z   |
| Cut          | Ctrl+X         |
| Copy         | Ctrl+C         |
| Paste        | Ctrl+V         |
| Delete       | Del            |
| **Select**   |                |
| All          | Ctrl+A         |
| Odd          | Ctrl+1         |
| Even         | Ctrl+2         |
| Invert       | Ctrl+I         |
| Blank        | Ctrl+B         |
| Dark         | Ctrl+D         |
| Modified     | Ctrl+M         |
| **View**     |                |
| Clear image selection | Esc    |
| Zoom in      | +              |
| Zoom out     | −              |
| Rotate 90° clockwise   | Ctrl+Shift+R |
| Rotate 180°            | Ctrl+Shift+F |
| Rotate 90° anticlockwise | Ctrl+Shift+C |
| Help         | Ctrl+H         |

### Tools

- **Threshold:** Binarize images. The value is an ink-strength cutoff: pixels that differ from the paper colour by more than the given percentage are rendered black, so coloured text and annotations (stamps, highlighters) on white paper are kept. The default is 20; values saved by older versions are migrated automatically.
- **Brightness / Contrast:** Adjust brightness and contrast.
- **Negate:** Invert colours.
- **Unsharp mask:** Sharpen images.
- **Crop:** Crop selected pages. The selection rectangle stays visible when
  you move between pages, so you can preview where the crop will land on each
  page before applying it.
- **Clean up:** Use unpaper to clean up scans.
- **Split:** Split pages vertically or horizontally.
- **OCR:** Use tesseract to create a text layer for the selected pages. The
  text layer is embedded into saved PDFs, making them searchable, and can be
  viewed and edited in the text layer window. Deleting a slice in that window
  removes the corresponding text from the saved PDF's text layer, and
  correcting one replaces the text that is saved and drawn; emptying a slice
  deletes it. Slices added manually are preserved in the saved text layer too,
  even when they sit inside an existing OCR paragraph. Accepting a correction
  moves on to the next slice in the current sort order, so a confidence-sorted
  pass walks from the least to the most confident text without re-selecting
  each one. To add a slice, first draw a
  rectangle - on the image or
  directly in the text layer pane - then type the text and press the +
   control; the Add control stays disabled, with an explanatory tooltip, until
   a rectangle has been drawn or selected. Drawing that first rectangle works
   on a page that has not been OCRed yet, so the text layer can be started
   from scratch. A focused slice can be rotated 90°
   clockwise or anticlockwise to author vertical text (e.g. rotated index
   columns) whose glyphs tesseract read wrongly; slices added while a slice is
   rotated inherit that rotation. A slice added without an inherited rotation
   is oriented automatically: a tall, narrow selection becomes vertical text
   and a wide, short one stays upright, so a drawn box for a vertical column
   renders legibly instead of as shrunken horizontal text. Rotated slices are
   embedded at their angle in saved PDFs. Upright and rotated slices can be
   mixed on one page: each word is saved at its own size and angle, so a search
   highlight covers the word it found rather than a larger block around it.
   Re-importing a PDF cannot recover
   that rotation, because the imported text layer carries positions but not
   text direction. Page pixels are fed to tesseract in memory and the stored
   image is reused, so OCR is faster than in previous versions.
- **User-defined:** Run user-defined commands.

#### User-defined Tool Variables

- `%i` - input filename
- `%o` - output filename
- `%r` - resolution

By default a user-defined tool is run once per selected page, with `%i`
(and `%o`/`%r` where used) substituted for that page. Selecting "Process
selected pages at once" in the user-defined tools dialog instead passes every
selected page to a single invocation: `%i` expands to all the working files,
space-separated, so a tool that edits files in place (such as
`mogrify -negate %i`) can process the whole selection in one go. Only the
pages whose file actually changed are replaced, and the whole batch is a
single undo step, so one undo restores every page. `%o` and `%r` are not
 available in batch mode; a tool that uses them must be run one page at a time.

### OCR Languages and Tesseract Options

Tesseract can recognise several languages at once. In the scan window and the
**Tools → OCR** dialog, pick one **Language to recognise** (the primary
language) and tick any **Additional languages** in the popover. The resulting
specification is stored in the `ocr language` setting as a `+`-joined list,
for example `deu+eng`. The primary language is listed first, so put the main
language of the page first; tesseract prioritises the first language, and
adding more languages makes recognition slower and can reduce accuracy, so
select only the languages you need.

Next to the language, two selectors control how tesseract runs:

- **Page segmentation mode** tells tesseract how to lay out the page, from
  **Automatic** (the default, suitable for most pages) through **Single
  column**, **Single block**, **Single line**, **Single word**, **Single
  character**, **Sparse text**, and **Raw line**.
- **OCR engine mode** chooses the engine: **Default** (the engine's own
  choice), **LSTM only**, **Legacy**, or **Combined**. **Legacy** needs
  language data that includes the legacy engine; if it is missing, OCR does
  not start and the error names the engine mode.

Both are stored and restored with the other OCR options. Do not use
`tessedit_pageseg_mode` or `tessedit_ocr_engine_mode` in the advanced field
instead: those variables are set by these controls, so the field rejects them.

The **Advanced Tesseract engine parameters** expander accepts one
`name=value` engine parameter per line, for example:

```
user_words_file=/home/me/words.txt
tessedit_char_whitelist=0123456789
```

These are the values of tesseract's `-c` option, passed straight to the engine,
and cover options that have no dedicated control, such as a **user word list**
(`user_words_file`) or a **user pattern list** (`user_patterns_file`). A
leading `-c` is optional, so `-c user_words_file=/path` works too.

Press **Tab** to leave the field for the next control, so OK (or Scan) stays
reachable without the mouse. **Ctrl+Tab** and **Ctrl+Shift+Tab** move focus
forward and backward too. A literal tab character is not needed.

Do not paste tesseract command-line options such as `--user-words`: the field
expects engine parameters, and the error names the parameter to use instead:

| Command-line option | Engine parameter     |
|---------------------|----------------------|
| `--user-words`      | `user_words_file`    |
| `--user-patterns`   | `user_patterns_file` |
| `--dpi`             | `user_defined_dpi`   |

Lines beginning with `#` are comments; blank lines are ignored; a value may
itself contain `=`. Paths are not expanded, so use absolute paths. A
`user_words_file` or `user_patterns_file` that does not exist or cannot be read
is reported before OCR starts, and the file paths that will be applied are
written to the log. Invalid or unknown options are reported before any page is
modified.

The **Available parameters** button below the field opens a browser of the
engine parameters that the installed Tesseract reports, with each parameter's
name, default value, and description. Nothing is bundled with the application,
so the list follows the installed engine. Type in the filter to narrow the list
by name or description; matching is case-insensitive, and a filter that matches
nothing says so. Select a parameter and press **Enter** or **Insert** to write
`name=` at the cursor in the advanced field and return to the field with the
cursor after the `=`, ready for the value. If there is already text on the
current line, the new parameter starts on its own line.
**tessedit_pageseg_mode** and **tessedit_ocr_engine_mode** stay visible but are
annotated with the control that sets them and cannot be inserted. If the list
cannot be read, the browser says so and OCR still runs with the parameters
already in the field.

The `tessdata_best` language models give more accurate OCR but are larger and
notably slower, and they only support the LSTM engine. Point the **Tessdata
directory** control (see below) at their directory to use them; the
`TESSDATA_PREFIX` environment variable and replacing the system models also
work.

#### Tessdata directory

The **Tessdata directory** control at the top of the OCR options chooses the
directory that holds the tesseract language data (the `.traineddata` files).
Leave it empty for the automatic directory, shown in the entry as
**Default (system tessdata)**; **Browse** picks a folder and **Default**
clears the choice.

The directory is resolved in this order:

1. the chosen **Tessdata directory**;
2. the `TESSDATA_PREFIX` environment variable, if it is set;
3. the system tessdata directory.

A chosen directory must directly contain at least one `.traineddata` file.
This also applies to `TESSDATA_PREFIX`: point it at the directory that
contains the files, not at a parent such as `/usr/share`. A broad value makes
tesseract search the whole tree and hang, so it is rejected with an error
instead of being used. Nothing falls back to another directory: a wrong path
is reported before OCR starts.

Changing the directory rebuilds the language list from it. If the current
language is not present in the new directory it stays selected and is marked
*(not installed)*, and OCR refuses to start until a language from that
directory is chosen.

The selected directory is stored with the other OCR options and restored on
the next start. Tesseract 3's `m_data_sub_dir` parameter no longer exists in
Tesseract 4 and later, so it is rejected like any other unknown engine
parameter.


---

## Command-line Options

scantpaper supports the following options:

- `--device <device> [...]`
    Specifies the device(s) to use, instead of getting the list from the SANE API. Useful for remote scanners. May be repeated, or given multiple space-separated devices.

- `--help`  
    Displays help and exits.

- `--log=<log-file>`  
    Specifies a file to store logging messages. On exit, the log is compressed to `<log-file>.xz`.

- `--debug`, `--info`, `--warn`, `--error`, `--fatal`  
    Defines the log level. Defaults to `--debug` if a log file is specified, otherwise `--warn`.

- `--import=<PDF|DjVu|images>`  
    Imports the specified file(s). For multi-page documents, a window is displayed to select required pages.

- `--import-all=<PDF|DjVu|images>`  
    Imports all pages of the specified file(s).

- `--locale=<directory>`
    Sets the directory containing translated messages. See [Translations](#translations).

- `--version`  
    Displays the program version and exits.

### Example output

```sh
$ scantpaper --version
scantpaper X.Y.Z
```

(Replace `X.Y.Z` with your installed version.)

```sh
$ scantpaper --help
usage: scantpaper [-h] [--device DEVICE [DEVICE ...]]
                  [--import IMPORT_FILES [IMPORT_FILES ...]]
                  [--import-all IMPORT_ALL [IMPORT_ALL ...]] [--locale LOCALE]
                  [--log LOG] [--version] [--debug] [--info] [--warn]
                  [--error] [--fatal]

A GUI to produce PDFs or DjVus from scanned documents

options:
  -h, --help            show this help message and exit
  --device DEVICE [DEVICE ...]
  --import IMPORT_FILES [IMPORT_FILES ...]
  --import-all IMPORT_ALL [IMPORT_ALL ...]
  --locale LOCALE
  --log LOG
  --version             show program's version number and exit
  --debug
  --info
  --warn
  --error
  --fatal

Please see /usr/share/doc/C/scantpaper/documentation.html for more detail
```

### Examples

```sh
# Import every page of a PDF, letting you edit before saving
scantpaper --import-all ~/scans/document.pdf

# Import a PDF, choosing the pages to import in a dialog
scantpaper --import ~/scans/document.pdf

# Use a remote scanner
scantpaper --device "net:scanner.example.com:6566"
```

Scanning is handled with SANE. PDF conversion uses `img2pdf` and `ocrmypdf`. TIFF export uses `libtiff`.

Saved PDFs are marked as created by scantpaper: the document info `Creator`
field and the XMP creator tool begin with `scantpaper v<version>`, followed
by the OCR toolchain used, e.g.
`scantpaper v3.0.16 / OCRmyPDF 16.13.0 / Tesseract OCR-hOCR 5.5.0`. The
`Producer` field continues to name the library that wrote the file.

---

## Diagnostics

To diagnose errors, start scantpaper from the command line with logging enabled:

```sh
PYTHONPATH=src python3 -m scantpaper.app --debug
```

---

## Configuration

scantpaper creates a config file at `~/.config/scantpaperrc`. The directory can be changed by setting `$XDG_CONFIG_HOME`. Preferences are usually set via **Edit → Preferences**.

If the config file cannot be read as JSON (for example after a partial write
or a hand edit), scantpaper rescues as many recognised settings as it can and
resets the rest to their defaults. The unreadable original is kept as a backup
at `~/.config/scantpaperrc.old`, and the start-up message tells you which
settings were kept. Wrongly typed values (e.g. a string where a number is
expected) are corrected when possible; each correction is written to the log
rather than shown in the start-up message. Only values that cannot be corrected
are reported. The original file is never silently overwritten: saving on exit
waits until the message has been acknowledged.

Scan options saved in the configuration file by gscan2pdf 2.x (whose
`default-scan-options` block has no `frontend` list) are recognised and applied
to the scan dialog on startup, and are kept when the file is written back.

### Sessions

An unsaved session's data (pages, edits, OCR, annotations) is stored in an
SQLite database in a temporary directory named `scantpaper-????????`, created
under `$TMPDIR` (or `/tmp`) by default. The database is a sibling of that
directory: `/tmp/scantpaper-????????.sdb`, not a file inside it. You can change
the temporary location in **Edit → Preferences**.

On a normal exit the temporary directory, the database and its sidecars are
removed. If scantpaper crashes, they survive. The next start opens a
**Crashed sessions** picker, and **File → Open crashed session** opens the same
picker later. A snapshot that another instance is using is not listed.

The picker offers **Restore**, **Discard** and **Cancel**:

- **Restore** copies the snapshot into a fresh temporary session and opens that
  copy. The snapshot and its directory are removed only after the restored
  pages are available, so a failed restore leaves the snapshot intact. The
  restored work is an ordinary unsaved temporary document: it is removed on a
  normal exit unless you save it.
- **Discard** asks for confirmation and then removes the selected snapshots,
  their sidecars and their session directories.
- **Cancel** leaves everything unchanged.

A snapshot whose latest action removed every page is still listed, because its
undo history may contain recoverable pages. Restoring it opens an empty
document; discarding it removes it.

To remove a leftover snapshot manually, quit scantpaper first and delete both
the database and its directory:

```sh
rm -rf /tmp/scantpaper-???????? /tmp/scantpaper-????????.sdb*
```

A session saved as an `.sdb` file (**File → Save As**) can be reopened with
**File → Open**. The opened file itself becomes the working session: edits are
written to it as you work, so they persist even if you quit without saving the
session again, and quitting does not warn about unsaved pages. Saving over an
existing `.sdb` file asks for confirmation before the old file is replaced; the
replacement is atomic, so a failed save leaves the previous file intact.
Because the opened file is the source of truth, an in-place session is not
offered for restoration as a crashed session after a crash.

To reuse corrected OCR when rebuilding a session from the original images,
save the old session's pages as hOCR, import the images into a new session in
the same page order, and choose **File → Import hOCR…** for the selected pages
or all pages. The import matches hOCR pages to pages by position, checks each
page box against the target page's pixel size, and replaces only the text
layer as one undo step. Pages without OCR are exported as empty page boxes so
they keep their position; importing an empty page box clears that page's text
layer. Annotations are not part of hOCR and are not transferred. The TIFF or
other image files must be imported in the old session's page order, because a
wrong-order page of the same size cannot be detected from its page box.

A session with no recorded actions opens as an empty document, and a session
whose latest action removed every page reopens empty. The first page scanned
into a restored session is recorded as its first undo step.

---

## Dependencies

Package names below are the Debian package names. Equivalent packages for other
distributions are given in the [wheel file](#from-a-wheel-file) installation
instructions.

### Required

- gir1.2-gdkpixbuf-2.0
- gir1.2-gtk-4.0
- imagemagick
- img2pdf
- libtiff-tools
- ocrmypdf
- poppler-utils
- python3-gi
- python3-gi-cairo
- python3-iso639
- python3-pil
- python3-sane
- python3-tesserocr

### Optional

- djvulibre-bin
- jbig2 (for JBIG2 PDF compression)
- qpdf (for PDF encryption)
- unpaper
- xdg-utils

### Development

- python3-pytest-mock
- python3-pytest-cov
- python3-pytest-timeout
- python3-pytest-xvfb

---

## Download, Installation & Removal

### Requirements

- Python 3.10 or later.
- GTK 4.6 or later and PyGObject 3.42 or later.
- The [dependencies](#dependencies) listed below. They are installed
  automatically when installing from a wheel or with `uv`, but must be
  installed manually when running from a tarball or the repository.

Ubuntu 22.04 (jammy), with GTK 4.6 and PyGObject 3.42, is supported until
2027-06. Ubuntu 24.04 (noble) and newer toolchains are also supported.

### Packaged installs

#### Debian-based

- Debian `sid` should automatically have the latest version.

    ```sh
    sudo apt update
    sudo apt install scantpaper
    ```

- Ubuntu users can use the PPA:

    ```sh
    sudo apt-add-repository ppa:jeffreyratcliffe/ppa
    sudo apt update
    sudo apt install scantpaper
    ```

In either case to remove scantpaper afterwards:

```sh
sudo apt remove scantpaper
```

#### From Fedora RPM packages

Download the RPM files from
[Github](https://github.com/carygravel/scantpaper/releases/) and install them
all in a single transaction:

```sh
sudo dnf install *.rpm
```

The release provides scantpaper together with locally rebuilt versions of
`python3-sane`, `python3-tesserocr`, `python3-cysignals` and `python3-iso639`
(packaged as RPMs because the Fedora versions do not satisfy scantpaper's
dependencies, and with a `python3-sane` patch for Epson `epsonscan2` scanners).
scantpaper requires these packages by their `python3dist(...)` names, so they
must be installed in the same transaction: installing scantpaper first fails
with `nothing provides python3.14dist(...)`.

To remove scantpaper afterwards:

```sh
sudo dnf remove scantpaper
```

Weak dependencies such as `qpdf` are installed by default, but can be skipped
with `--setopt=install_weak_deps=0`, in which case the optional features they
provide (e.g. PDF encryption) are disabled.

#### From openSUSE RPM packages

Download the Leap 16.0 RPM files - the ones with the `.lp160.1` release
suffix - from
[Github](https://github.com/carygravel/scantpaper/releases/) into a directory
of their own and install them all in a single transaction:

```sh
sudo zypper install --allow-unsigned-rpm *.rpm
```

The release provides scantpaper together with locally rebuilt, Leap 16.0
versions of the Python dependencies that the distro does not package
(`python313-sane`, `python313-tesserocr`, `python313-cysignals`,
`python313-python-iso639`, `python313-pdfminer.six`, `python313-img2pdf`,
`python313-ocrmypdf`, `python313-setuptools-scm` and `python313-hatch-vcs`,
with a `python313-sane` patch for Epson `epsonscan2` scanners).

Installing the set replaces any same-named packages you already have from
repo-oss or from a third-party repository such as
`devel:languages:python`. That is intended: the rebuilt packages carry fixes
the repository builds do not, the `python313-sane` `snap()` patch being the
important one. Three of them are versioned so that a later system update keeps
your build instead of reverting it to the distribution's.

To remove scantpaper afterwards:

```sh
sudo zypper remove scantpaper
```

The RPMs are unsigned (the workflow builds them from source), hence the
`--allow-unsigned-rpm`. That is the only flag needed. PostScript export needs
`tiff2ps`, which is not packaged on Leap 16.0, so the PostScript save type is
not available there.

##### Tumbleweed

Download the Tumbleweed RPMs - the files without the `.lp160.1` release
suffix - into a directory of their own and install them in one transaction:

```sh
sudo zypper install --allow-unsigned-rpm *.rpm
```

Only the patched `python-sane` is rebuilt; every other Python dependency
comes from the Tumbleweed repositories. The patched package replaces
Tumbleweed's unpatched `2.9.2` build, so Epson `epsonscan2` scanners get the
`snap()` fix. A later Tumbleweed `python-sane` release will sort above it
until the fix is upstreamed. PostScript export has the same limitation as
Leap: `tiff2ps` is not packaged on Tumbleweed (`tiff` 4.7.2), so the
PostScript save type is not available.

#### From a wheel file

Download `.whl` from [Github](https://github.com/carygravel/scantpaper/releases/).
```sh
# Install the C-libraries that pip cannot handle:
# For Debian/Ubuntu
sudo apt update
sudo apt install libgirepository-2.0-dev libcairo2-dev libsane-dev pkg-config python3-dev gir1.2-glib-2.0
# For Fedora
sudo dnf install gobject-introspection-devel cairo-devel sane-backends-devel pkgconf-pkg-config python3-devel
# For openSUSE
sudo zypper install gobject-introspection-devel cairo-devel sane-backends-devel pkgconf-pkg-config python3-devel
# For Arch
sudo pacman -S gobject-introspection cairo sane pkgconf python
# For Homebrew
brew install pygobject3 gobject-introspection cairo sane-backends pkg-config
# Possibly upgrade pip
python3 -m pip install --upgrade pip
# Install from the wheel file, automatically including python dependencies
pip install scantpaper-x.x.x-py3-none-any.whl
```

If you haven't already, you will then probably have to add `~/.local/bin` to
your path in order to find the new executable, after which you can start it with:

```sh
scantpaper
```

To then remove it:

```sh
pip uninstall scantpaper
```

#### With `uv`

To install the runtime dependencies with `uv`:

```sh
uv sync
```

or with the additional development dependencies:

```sh
uv sync --extra test
```

After which you can start it with:

```sh
uv scantpaper
```

### From source

#### From a tarball

Download `.tar.gz` from [Github](https://github.com/carygravel/scantpaper/releases/).
After installing the [dependencies](#dependencies) listed above:

```sh
tar xvfz scantpaper-x.x.x.tar.gz
cd scantpaper-x.x.x
PYTHONPATH=src python3 -m scantpaper.app
```

#### From the repository

Browse the code at [Github](https://github.com/carygravel/scantpaper).
After installing the [dependencies](#dependencies) listed above:

```sh
git clone https://github.com/carygravel/scantpaper.git
cd scantpaper
PYTHONPATH=src python3 -m scantpaper.app
```

In either of the above two cases, just delete the source directory to remove it.

---

## Support

- **Mailing lists:**
    - [gscan2pdf-announce](https://lists.sourceforge.net/lists/listinfo/gscan2pdf-announce) (announcements)
    - [gscan2pdf-help](https://lists.sourceforge.net/lists/listinfo/gscan2pdf-help) (general support)

---

## Reporting Bugs

- Please read the [FAQs](#faqs) first.
- Report bugs preferably against the
[Debian package](https://packages.debian.org/sid/scantpaper) or
[Debian Bugs](https://www.debian.org/Bugs/).
- Alternatively, use the
[Github issue tracker](https://github.com/carygravel/scantpaper/issues).
- Include the log file created by `scantpaper --log=log` with your report.
  On exit the log is compressed to `log.xz`, so submit that file.

---

## Translations

scantpaper is partly translated into several languages. We'd like to
organise the translations on
[Launchpad Rosetta](https://translations.launchpad.net/scantpaper) — but if
that's too much effort for you, attaching a `.po` file to an
[issue](https://github.com/carygravel/scantpaper/issues) or a merge request
is just as welcome.

Missing strings are seeded as `#, fuzzy` (needs review) entries and fall
back to English until a translator confirms them, so only confirmed
translations reach users. The catalogs live in the `po/scantpaper/`
template directory, one `po/scantpaper/<language>.po` per language next to
the generated `po/scantpaper/scantpaper.pot` — the layout Rosetta expects,
so the directory can be tarballed and uploaded as-is.

- Scanner option translations come from sane-backends. Contribute via the
[sane-devel mailing list](mailto:sane-devel@lists.alioth.debian.org) or
[SANE project](http://www.sane-project.org/cvs.html).

To test updated `.po` files:

```sh
python3 dev/compile_mo.py --src po/scantpaper --out src/scantpaper/locale --domain scantpaper
PYTHONPATH=src python3 -m scantpaper.app --log=log --locale=locale
```

Set locale variables as needed (e.g., for Russian):

```sh
LC_ALL=ru_RU.utf8 LC_MESSAGES=ru_RU.utf8 LC_CTYPE=ru_RU.utf8 LANG=ru_RU.utf8 LANGUAGE=ru_RU.utf8 PYTHONPATH=src python3 -m scantpaper.app --log=log --locale=locale
```

### Checking for upstream translations

```sh
python3 dev/check_launchpad.py
```

reports whether anything has moved on
[Launchpad](https://translations.launchpad.net/scantpaper) since the last sync,
by comparing the current state of the translation template and of every
language against the committed baseline `po/launchpad-state.json`. Two triggers
are watched: the template (the message set / `.pot`), read from the Launchpad
JSON API with a conditional request, and each language's last-changed time,
which exists only on the public series translation page. A language is reported
when its last-changed time is newer than the baseline, when a new locale
appears, or when the template changes.

It also reports the reverse direction: whether the local template generated
from source is stale relative to the last pot uploaded to Launchpad. The local
message set is compared against the `uploaded_pot` fingerprint recorded in the
baseline; a difference (new, removed or changed strings) is reported as
`Local pot is stale:` naming the added and removed strings, and the exit code
is unaffected unless `--fail-on-change` is given.

The check is advisory by default: it exits successfully whether or not it found
changes, and it never downloads a catalog (Launchpad requires a login to export
one). `--fail-on-change` makes a detected change non-zero for CI. The one
always-non-zero outcome is a state the check could not read, so a broken check
is loud rather than mistaken for "nothing new". The committed baseline is
advanced deliberately, after the catalogs have been synced:

```sh
python3 dev/check_launchpad.py --update
```

After a new template has been uploaded to Launchpad, record its message set so
the next run no longer reports the pot as stale:

```sh
python3 dev/check_launchpad.py --record-pot
```

The [translations-check workflow](.github/workflows/translations-check.yml)
runs the same check daily and on demand (`workflow_dispatch`), and files one
labelled issue when it detects upstream changes and a separate one when new
strings are ready to upload.

### Validating catalogs and source strings

```sh
python3 dev/check_po.py
```

This needs `xgettext`, `msgcat` and `msgfmt` on `PATH`. `intltool` is not
needed: the translatable strings in the `.ui` files are extracted with the
GtkBuilder ITS rules vendored in `dev/`, so the template does not depend on the
gettext version installed. CI runs the same script via `test_po_files.py`.
`dev/check_po.py` compares those vendored rules against the copies gettext
installed and reports any drift as an advisory.

Errors fail the script; advisories are printed but do not. A release is
therefore never blocked by an advisory, but they are worth reading.

The hard checks catch defects that break a catalog or a translation: a bad
format specifier, an unbalanced or renamed placeholder, a plural entry with
the wrong number of forms, a `Plural-Forms` header that disagrees with CLDR,
an obsolete-entry count over the catalog's ceiling, and counted strings
written as `%(plural)s` rather than `ngettext`.

The advisories fall into two groups. Source-side ones report strings that are
hard to translate, so they are best fixed by rewording: a concatenation
fragment that fixes English word order into a translatable string, a msgid
differing from another only by case, a doubled space, a msgid over 200
characters, and a `μs` written with GREEK SMALL LETTER MU. Catalog-side ones
report mistranslations that only a native speaker can judge: a `msgstr` copied
verbatim from a different msgid (the signature of a bad fuzzy match), and two
sibling controls such as `Select Odd` and `Select Even` sharing one
translation.

`dev/check_po.py` has a `--pot` option that points it at an existing template
instead of generating one, which the test suite uses to exercise the
source-side checks.

---

## FAQs

### Why isn't option xyz available in the scan window?

It may not be supported by SANE or your scanner. If you see it in `scanimage --help` but not in scantpaper, send the output to the maintainer.

### How do I scan a multipage document with a flatbed scanner?

Enable "Allow batch scanning from flatbed" in Preferences. Some scanners require additional settings. Switching to the flatbed starts **# Pages** at one page; choose a finite count, or choose **All** to scan until you cancel.

### How do I scan a book?

Cut off the binding and scan every sheet one at a time, always aligning the
untorn outer edge to the same rim. Scan in landscape to save time, set the
rotation for the front pages in the **Postprocessing** tab, then tick
**Alternate rotation every 2nd page** so the backs (which are rotated 180
degrees relative to the fronts) can be scanned too without manual rotation. For
one-page-at-a-time flatbed scanning, the toggle remains active across consecutive
scans so every second scan is flipped automatically; turn it off to reset
parity.

### Why is option xyz ghosted out?

The required package may not be installed (e.g., xdg-email, unpaper, imagemagick).

### Why can I not scan from the flatbed of my HP scanner?

Set "# Pages" to "1" and "Batch scan" to "No".

### Why is the list of changes not displayed when updating in Ubuntu?

Only changelogs from official Ubuntu builds are shown.

### Why can't scantpaper find my scanner?

If the scanner is remote and not found automatically, specify the device:

```sh
scantpaper --device <device>
```

### How can I search for text in the OCR layer?

Use `pdftotext` or `djvutxt` to extract text. Many viewers support searching the embedded text layer.

### How can I change the colour of the selection box or OCR output?

The selection rectangle is styled with the `.rubberband` CSS class.
scantpaper ships a default rule for it that follows the active GTK theme,
so the selection is visible out of the box. To use a colour of your own,
create or edit `~/.config/gtk-4.0/gtk.css` (a user stylesheet overrides the
application default):

```css
.rubberband {
    border: 1px solid #2a76c6;
    background-color: rgba(42, 118, 198, 0.2);
}

#scantpaper-ocr-output {
    color: black;
}
```

### What's in a name?

"scant" (https://en.wiktionary.org/wiki/scant) in this sense means "short (of)", as I am trying to digitalise my paperwork, and I liked the play on "scan".

---

## Known Limitations

### PDFs larger than 2 GiB

Saving more than approximately 250 uncompressed scanned pages (at 300 dpi, 8-bit grayscale) produces a PDF exceeding 2 GiB.  At that size, three tools in the save pipeline overflow 32-bit file offsets and produce truncated or corrupt output:

| Component | Tested version | Overflow point | Symptom |
|---|---|---|---|
| **img2pdf** (pikepdf engine, linearization) | 0.6.2 | 32-bit xref offsets in linearized output | Truncated PDF; first pages readable, later pages missing |
| **Ghostscript** (PDF/A conversion via ocrmypdf) | 10.07.1 | 32-bit file access in gs interpreter | Ghostscript error or corrupt output |
| **pikepdf** / **qpdf** (xref-stream linearization, metadata save) | pikepdf 10.5.0, qpdf 12.4.0 | 32-bit offsets in xref streams | "unable to find /Root dictionary"; PDF unopenable |

Scantpaper now estimates the output size before conversion and refuses to save when it would exceed 2 GiB, showing an error message suggesting fewer pages.

**When updating dependencies**, re-test by saving ~250 high-resolution uncompressed pages (e.g., 7000×5000 px grayscale TIFFs) and verifying the output PDF opens correctly in a PDF viewer.  Note that Ghostscript's 64-bit integer support (needed for >2 GiB files) is build-dependent — see the [Ghostscript documentation on word size](https://ghostscript.readthedocs.io/en/latest/Use.html#word-size-32-or-64-bits).

---

## See Also

- [gscan2pdf](https://gscan2pdf.sourceforge.net/) (the Perl predecessor of scantpaper)
- [XSane](http://xsane.org/)
- [Scan Tailor](http://scantailor.org/)

---

## History

I started writing `gscan2pdf` as a Perl & Gtk2 project in 2006.
Version 2 switched to Gtk3, but kept the basic software architecture.
This stored the pages as temporary files with hashed names, which had a couple
of major disadvantages:

- Difficult to support PDF/A directly
- It was impossible to create documents with more than a few hundred pages, as
it ran out of open file handles.
- In the event of a crash, it was tedious to recreate the document from the image files.
- AFAIK, Perl's support for Gtk4 never extended beyond that provided by introspection.

Therefore I decided in 2022 to completely rewrite `gscan2pdf` in Python and
renamed it for v3 `scantpaper`. The rewrite:

- Supports PDF/A by using `ocrmypdf` to write PDFs
- Stores all session data in a single Sqlite database
- Should be simple to migrate to Gtk4

See also the [changelog](changelog.md) for detailed release notes.

---

## Author

Jeffrey Ratcliffe (jffry at posteo dot net)

---

## Thanks To

- All contributors (patches, translations, bugs, feedback)
- The SANE project
- The authors of `img2pdf` and `ocrmypdf`, without which this would have been much harder.

---

## Contributing

See [contributing](CONTRIBUTING.md).

---

## Donate

[![Donate via PayPal](https://www.paypalobjects.com/en_US/DE/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=GYQGXYD5UZS6S)

---

## License

Copyright © 2006–2026 Jeffrey Ratcliffe <jffry@posteo.net>

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License v3 as published by the Free Software
Foundation.

This program is distributed in the hope that it will be useful, but **WITHOUT
ANY WARRANTY**; without even the implied warranty of **MERCHANTABILITY** or
**FITNESS FOR A PARTICULAR PURPOSE**. See the
[GNU General Public License](https://www.gnu.org/licenses/) for more details.
