Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Umi-OCR Administrator & Developer Guide

Generated June 2026 from hiroi-sora/Umi-OCR at 83173ef

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide is the deep reference for integrators, administrators, and developers working with Umi-OCR: how the application is built, how its processes talk to each other, the complete HTTP API and command-line surface, configuration files, security model, and the internal architecture that makes it work. End-user task instructions live in the companion User Guide.

Umi-OCR is a portable, fully offline OCR desktop application built on Python + Qt/QML. It bundles an OCR engine (as a swappable plugin), recognizes images / screenshots / scanned PDFs / QR codes, and exposes its functionality over a local HTTP service that doubles as the cross-process command-line bridge.


1. Executive Summary

Attribute Value
Project Umi-OCR
Author hiroi-sora
License MIT
Version (this snapshot) 2.1.5 (main branch)
Source commit 83173ef (2025-11-20)
Primary platform Windows 7 x64+ (Linux maintained separately)
UI stack PySide2 (Qt for Python) + QML
HTTP framework Bottle (vendored) on a WSGI reference server
Default service port 1224 (auto-increments if occupied)
OCR engine External plugin (PaddleOCR-json / RapidOCR)
Network posture Offline; loopback-only by default

What ships in this repository. This repo contains the application logic: the Python backend (UmiOCR-data/py_src/), the QML frontend (UmiOCR-data/qt_res/), the API documentation (docs/), and the i18n tooling (dev-tools/). It does not contain the Python runtime, Qt libraries, or the OCR engine — those are delivered by the separate Windows and Linux runtime repositories, and the engine comes from the plugins repository.

Note

Because the runtime and engine live in other repositories, you will not find dependency manifests (requirements.txt, pyproject.toml) here. Third-party library versions are recorded in CHANGE_LOG.md per release (for example, the v2.1.5 Windows build pins PyMuPDF 1.24.11, Pillow 10.4.0, pynput 1.8.0, psutil, fontTools 4.56.0, zxing-cpp 2.3.0).


2. Architecture

2.1. High-Level Diagram

                         ┌──────────────────────────────────────────────┐
   Umi-OCR.exe  ───────► │  main.py  (Windows/PyStand env bootstrap)     │
   (launcher)            │   • os.MessageBox last-resort error dialog    │
                         │   • set cwd = UmiOCR-data, add site-packages  │
                         │   • add PySide2 plugin path                   │
                         └───────────────────────┬──────────────────────┘
                                                 │  from py_src.run import main
                                                 ▼
                         ┌──────────────────────────────────────────────┐
                         │  py_src/run.py : main()                       │
                         │   • umi_about.init()   (version/host info)    │
                         │   • pre_configs.readConfigs()  (.pre_settings)│
                         │   • cmd_client.initCmd()  ── single-instance ─┼──► if already running:
                         │   • runQml()                                  │     POST argv → /argv, exit
                         └───────────────────────┬──────────────────────┘
                                                 ▼
   ┌─────────────────────────────────────────────────────────────────────────────────┐
   │  QGuiApplication + QQmlApplicationEngine  (qt_res/qml/Main.qml)                    │
   │                                                                                   │
   │   QML UI  ◄──── qmlRegisterType ────►  Python "Connector" QObjects                │
   │   (tabs, widgets)                       TagPageConnector, MissionConnector,        │
   │                                         PubSubConnector, KeyMouseConnector,        │
   │                                         UtilsConnector, GlobalConfigsConnector,    │
   │                                         ThemeConnector, ImageConnector,            │
   │                                         PluginsConnector, DocPreviewConnector      │
   │                                                                                   │
   │   ┌──────────────┐   ┌──────────────┐   ┌─────────────────┐   ┌────────────────┐  │
   │   │ Mission       │   │ tbpu          │   │ ocr/output      │   │ plugins_ctrl    │  │
   │   │ (task queues) │   │ (layout parse)│   │ (txt/pdf/csv…)  │   │ (load engines)  │  │
   │   └──────┬───────┘   └──────────────┘   └─────────────────┘   └───────┬────────┘  │
   │          │                                                            │           │
   │          ▼                                                            ▼           │
   │   MissionOCR / MissionDOC / MissionQRCode              OCR engine plugin (subproc)│
   │                                                        e.g. PaddleOCR-json        │
   └───────────────────────────────┬───────────────────────────────────────────────────┘
                                    ▼
              ┌───────────────────────────────────────────────────────┐
              │  Bottle HTTP service  (QThreadPool worker thread)       │
              │  127.0.0.1:1224                                         │
              │   /argv  /api/ocr  /api/qrcode  /api/doc/*              │
              └───────────────────────────────────────────────────────┘

2.2. Startup Sequence

  1. Umi-OCR.exe (PyStand on Windows) sets the PYSTAND environment variable and runs UmiOCR-data/main.py.
  2. main.py initializes the runtime environment: installs a ctypes-based os.MessageBox as the last-resort error reporter, redirects stdout/stderr to the console if present, changes the working directory to UmiOCR-data, adds . and site-packages to the import path, and registers the PySide2 plugin path. It then calls py_src/run.py:main().
  3. run.py:main() adds py_src/imports to the path, calls umi_about.init(app_path) to populate version/host metadata, strips a problematic QMLSCENE_DEVICE env var if present, reads pre-QML configuration, and runs the single-instance check (initCmd()).
  4. If no other instance owns the lock, runQml() starts Qt: enables shared OpenGL contexts and high-DPI scaling, initializes the renderer, creates the QGuiApplication, registers every Python connector type with qmlRegisterType, initializes i18n, and loads qt_res/qml/Main.qml.

2.3. The QML ↔ Python Bridge

The UI is QML; the logic is Python. They are connected by connector classes — QObject subclasses registered with qmlRegisterType() in run.py. QML instantiates them and calls their slots; Python emits signals back. The registered connectors are:

Connector Source Responsibility
TagPageConnector tag_pages/tag_pages_connector.py Tab/page lifecycle, holds page py/qml objects
MissionConnector mission/mission_connector.py Submits and controls OCR task queues
DocPreviewConnector mission/doc_preview_connector.py Document preview rendering
PubSubConnector event_bus/pubsub_connector.py Publish/subscribe event bus
KeyMouseConnector event_bus/key_mouse/key_mouse_connector.py Global keyboard/mouse hooks
PluginsConnector plugins_controller/plugins_connector.py OCR engine plugin loading
UtilsConnector utils/utils_connector.py Clipboard, file finding, shell open
GlobalConfigsConnector utils/global_configs_connector.py Shortcuts, language, OpenGL, log level, web service control
ThemeConnector utils/theme_connector.py Theme load/save
ImageConnector / PixmapProvider image_controller/ Image processing, QML image provider
I18n utils/i18n_configs.py Runtime translation

2.4. The Mission (Task Queue) Framework

All recognition work flows through a generic task-queue base class, Mission (mission/mission.py), subclassed by MissionOCR, MissionDOC, and MissionQRCode. Key behaviors:

  • Queues of queues. Each submission is a mission list (a queue of individual tasks) identified by a UUID. Multiple mission lists can be active at once.
  • Scheduling modes. _schedulingMode is "1111" (round-robin — take the first task from each queue in turn) or "1234" (sequential — finish one queue before starting the next). Round-robin is the default.
  • Async vs. sync. addMissionList(msnInfo, msnList) returns immediately with a mission ID and invokes callbacks (onStart, onReady, onGet, onEnd). addMissionWait(argd, msnList) blocks on a threading.Condition until the queue finishes and returns the result list — this is what the synchronous HTTP /api/ocr route uses.
  • Lifecycle control. pauseMissionList, resumeMissionList, stopMissionList, stopAllMissions. State per queue is waiting → running → stop.
  • Worker thread. A single async worker (_taskRun) drains all queues under a QMutex; subclasses override msnPreTask (parameter prep) and msnTask (actual recognition).

2.5. OCR Engine Plugin System

The OCR engine is not built in — it is a plugin loaded at runtime by plugins_controller/plugins_controller.py:

  • Plugins live under ./UmiOCR-data/plugins/. The controller adds that directory to the import path and imports every subfolder that contains an __init__.py.
  • Each plugin must define a PluginInfo dict with at least group (must be one of PLUGINS_GROUPS, currently ["ocr"]) and api_class, plus optional global_options / local_options that describe its configurable parameters.
  • After dynamic plugins load, ocr/api.initOcrPlugins() wires them into the API manager.

This is why parameters like ocr.language, ocr.cls, and ocr.limit_side_len are engine-specific (they belong to the PaddleOCR plugin). Integrators should never hard-code an engine's parameters — query the live option set via the get_options endpoints (see §4) and build requests from that.

2.6. Layout Post-Processing (tbpu)

tbpu ("text block processing unit", ocr/tbpu/) re-orders and merges the engine's raw text blocks into readable paragraphs. The parser registry (ocr/tbpu/__init__.py) maps a string key to a parser class:

Key Class Behavior
none ParserNone No processing
multi_para MultiPara Multi-column, natural paragraphs (default)
multi_line MultiLine Multi-column, always wrap
multi_none MultiNone Multi-column, no wrap
single_para SinglePara Single-column, natural paragraphs
single_line SingleLine Single-column, always wrap
single_none SingleNone Single-column, no wrap
single_code SingleCode Single-column, preserve indentation

IgnoreArea (also in tbpu/) drops text blocks that fall entirely within user-defined rectangles. Helper algorithms live in tbpu/parser_tools/ (gap_tree.py, line_preprocessing.py, paragraph_parse.py).

2.7. Output Writers

Document/batch results are serialized by writer classes in ocr/output/, selected by key via the Output registry:

Key Writer Output
txt output_txt.py Text with page/file info
txtPlain output_txt_plain.py Plain recognized text
(per-file) output_txt_individual.py One text file per input
csv output_csv.py CSV table
jsonl output_jsonl.py One JSON object per line
md output_md.py Markdown
pdfLayered output_pdf_layered.py Double-layer searchable PDF
pdfOneLayer output_pdf_one_layer.py Single-layer plain-text PDF

3. Repository Structure

Umi-OCR/
├── README.md / README_en.md / README_ja.md   Project overview (multi-language)
├── CHANGE_LOG.md                              Version history + dependency pins
├── LICENSE                                    MIT
├── docs/
│   ├── README_CLI.md                          Command-line manual (authoritative)
│   └── http/                                  HTTP API manual (authoritative)
│       ├── README.md  api_ocr.md  api_doc.md  api_qrcode.md  argv.md
│       └── api_doc_demo.py / .html            Working API client examples
├── dev-tools/i18n/                            Qt Linguist translation toolchain
└── UmiOCR-data/                               The application (working directory)
    ├── main.py                                Windows/PyStand bootstrap (entry point)
    ├── about.json                             Name, version, URLs, translators
    └── py_src/
        ├── run.py                             App startup, connector registration
        ├── imports/                           umi_about, umi_log, plugin_i18n, call_func
        ├── utils/                             configs, i18n, theme, threads, file finder
        ├── mission/                           task-queue framework (OCR/DOC/QRCode)
        ├── ocr/
        │   ├── api/                           OCR plugin API manager
        │   ├── tbpu/                          layout post-processing parsers
        │   └── output/                        result writers (txt/csv/pdf/…)
        ├── server/                            HTTP service + CLI bridge (bottle)
        ├── tag_pages/                         per-tab Python logic
        ├── event_bus/                         pub/sub + keyboard/mouse hooks
        ├── image_controller/                  screenshot + image handling
        ├── plugins_controller/                dynamic plugin loader
        └── platform/                          win32 / linux platform shims
    └── qt_res/qml/                            QML UI (Main.qml, TabPages, Widgets, …)

Tip

The two docs/ manuals are the project's own authoritative API references. They are written in Chinese; this guide translates and consolidates them. When the source code and the manual disagree, trust the source — but in this snapshot they are consistent.


4. HTTP API Reference

All endpoints are served by the Bottle app in py_src/server/web_server.py, listening on 127.0.0.1:1224 by default. Responses are JSON strings (non-ASCII characters are escaped to \uXXXX for transport).

Important

The HTTP service must be enabled (it is on by default). The service-related settings appear only when Advanced mode is enabled in the Global Settings tab. To allow LAN clients, switch the host from “local only” to “any available address” — see the Security section before doing so.

4.1. Service Conventions

  • CORS: the service sets Access-Control-Allow-Origin: * and allows GET, POST, PUT, OPTIONS. OPTIONS preflights are translated to the requested method.
  • Request size limit: BaseRequest.MEMFILE_MAX = 104857600 (100 MB per request).
  • Status codes: code == 100 means success across all endpoints. code == 101 means "no text found" (OCR/QR) or "no file uploaded" (doc upload). Other codes indicate errors and carry a human-readable data string.
  • Concurrency: the backend has limited concurrency support — avoid parallel calls. Under sustained heavy load, occasional Error: connect ECONNREFUSED responses can occur; simply retry.

4.2. Base Routes

Method Path Purpose
GET / , /umiocr Returns the application full-name string (liveness check)
POST /argv Cross-process command-line bridge (localhost only)

4.3. Image OCR

GET /api/ocr/get_options

Returns the parameter definitions for the currently loaded OCR engine (titles, defaults, types, option lists). Use this to discover valid parameters dynamically rather than hard-coding them.

curl http://127.0.0.1:1224/api/ocr/get_options

Each parameter is described by title, toolTip, default, and type (enum, boolean, text, number, var). For PaddleOCR the set includes ocr.language, ocr.cls, ocr.limit_side_len, tbpu.parser, tbpu.ignoreArea, and data.format.

POST /api/ocr

Recognizes a single Base64-encoded image.

Request body (JSON):

Field Required Description
base64 yes Image as a Base64 string, without any data:image/png;base64, prefix
options no Parameter dict (see get_options); missing keys take their defaults
import json, requests

payload = {
    "base64": "iVBORw0KGgoAAAAN……",
    "options": {
        "ocr.language": "models/config_chinese.txt",
        "tbpu.parser": "multi_para",
        "data.format": "text",
    },
}
resp = requests.post(
    "http://127.0.0.1:1224/api/ocr",
    data=json.dumps(payload),
    headers={"Content-Type": "application/json"},
)
print(json.loads(resp.text))

Response (JSON):

Field Type Description
code int 100 success, 101 no text, otherwise failure
data list | string Result (see below); error string on failure
time double Recognition time in seconds
timestamp double Start timestamp (seconds)

When data.format is dict (default), data is a list of blocks, each with text, score (0–1), box (four clockwise corner [x,y] points starting top-left), and end (the line terminator — empty, space, or \n — derived from layout parsing, so concatenating text + end reconstructs paragraphs). When data.format is text, data is the joined string.

Warning

Some HTTP libraries auto-convert escaped newlines (\\n) in the response body into real newlines, which then breaks JSON parsing. If you hit this, fetch the raw response string, replace every real newline with \\n, then parse.

Request-validation error codes (/api/ocr): 800 body not JSON, 801 empty body, 802 missing base64, 803 options not a dict, 804 options parsing failed.

4.4. Document OCR (PDF) — Five-Step Workflow

Document recognition (v2.1.4+) is asynchronous: upload → poll → build output → download → clean up. Routes live in server/doc_server.py.

1. POST /api/doc/upload      → returns task id
2. POST /api/doc/result      → poll until is_done && state=="success"
3. POST /api/doc/download     → build target file(s), return download URL
4. GET  <download URL>        → fetch the file
5. GET  /api/doc/clear/<id>  → release server resources

GET /api/doc/get_options

Like the OCR options query, plus document-specific keys:

Key Default Type Meaning
tbpu.ignoreRangeStart 1 int First page the ignore-regions apply to
tbpu.ignoreRangeEnd -1 int Last page (negatives count from the end)
pageRangeStart 1 int First page to OCR
pageRangeEnd -1 int Last page to OCR (negatives count from the end)
pageList [] var Explicit page list, e.g. [1,2,5]; takes priority over the range
password "" text Password for encrypted documents
doc.extractionMode "mixed" enum mixed, fullPage, imageOnly, textOnly

POST /api/doc/upload

Multipart form upload. Fields: file (required) and json (optional options string). Returns {"code": 100, "data": "<task id>"} on success.

Note

On some Linux systems, a non-ASCII file_name causes code == 101 (file not received). The official client handles this by retrying with an ASCII-only temp_name for the upload part. Allowed extensions are restricted to the document suffixes in DocSuf.

POST /api/doc/result

Poll task status. Body: id (required), is_data (include recognized text, default false), is_unread (only return new entries, default true), format (dict or text). When code == 100 the response also carries processed_count, pages_count, is_done, state (waiting/running/success/failure), and message (only on failure).

POST /api/doc/download

Build the output. Body: id (required), file_types (array — one type returns a single file, several return a zip), ignore_blank (skip blank pages, default true). Valid file_types: pdfLayered, pdfOneLayer, txt, txtPlain, jsonl, csv. Returns a download data URL and name.

Caution

The download URL contains an opaque id that is not the task id. You cannot construct the download URL from the task id — always use the URL returned by /api/doc/download.

GET /api/doc/clear/<id>

Deletes all of the task's temporary files (including the uploaded original) and aborts it if still running. Tasks auto-clear 24 hours after completion (or after 24 hours of running); leftovers are also purged on next startup. Always clear tasks promptly to free resources.

Document error codes: 201–207 cover doc info extraction, encryption/password, mission submission, output-writer creation, write, save, and zip-packaging failures respectively.

4.5. QR Code / Barcode

POST /api/qrcode serves both reading and generating, distinguished by the body.

Read — body base64 (required) + optional preprocessing.* options (median_filter_size, sharpness_factor, contrast_factor, grayscale, threshold). Response mirrors OCR: a data list where each item has text, format (e.g. QRCode, Code128, …), box, orientation (0 = upright), and score (always 1, for OCR-format compatibility).

Generate — body text (required) + optional options: format (default QRCode), w, h (default 0 = auto-minimum), quiet_zone (default -1 = auto), ec_level (error correction: -1 auto, 1=7%, 0=15%, 3=25%, 2=30%; effective only for Aztec/PDF417/QRCode). Returns the generated image as a JPEG Base64 string in data.

4.6. The /argv Command Bridge

POST /argv accepts a JSON list of command-line arguments and executes them against the running instance, returning the result text. This is how the CLI reaches an already-running Umi-OCR.

// Equivalent to: Umi-OCR.exe --screenshot
fetch("http://127.0.0.1:1224/argv", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(["--screenshot"]),
}).then(r => r.text()).then(console.log);

Important

/argv is restricted to 127.0.0.1. The route checks REMOTE_ADDR and returns 401 for any non-loopback caller, because it can perform sensitive actions (read local images, close the app). This restriction holds even when the rest of the service is exposed to the LAN.


5. Command-Line Interface (Internals)

5.1. How the CLI Works

The command line is not a separate program — it is Umi-OCR.exe invoked with arguments. The flow (server/cmd_client.py):

  1. initCmd() reads sys.argv[1:] and checks for --force.
  2. _isMultiOpen() compares the recorded last_pid + last_ptime (process create time) in .pre_settings against live processes (via psutil) to detect whether an instance is already running.
  3. If already running: _sendCmd(argv) POSTs the argument list to http://127.0.0.1:<port>/argv and exits — the running instance does the work.
  4. If not running, with arguments: _newSend() launches a new process with --force (to avoid recursion), waits up to ~30 s for the service to come up, then forwards the arguments.
  5. If not running, no arguments (or --force): the process becomes the primary instance, records its pid/ptime, and starts the GUI.

The parser itself (server/cmd_server.py, an argparse.ArgumentParser(prog="Umi-OCR")) and the _Actuator execute commands inside the GUI process.

5.2. Flag Reference

Flag Type Action
--show / --hide / --quit store_true Control the main window
--screenshot store_true Screenshot OCR (accepts screen= / rect= positional params)
--clipboard store_true OCR the clipboard image
--path store_true OCR image path(s)/folder(s) from positional paras
--qrcode_read store_true Read code(s) from positional path(s)
--qrcode_create store_true Generate a code from positional "text" "out.jpg" [w] [h]
--reload store_true Publish <<settingsReload>> to reload .settings (v2.1.5+)
--all_pages store_true List page templates and open pages
--add_page / --del_page int Create/close a tab by index
--all_modules store_true List callable py/qml modules
--call_py / --call_qml str Target module (prefix match allowed)
--func str Function to call on the target module
--thread store_true Run the call on the worker thread to capture a return value (may be unstable)
--clip store_true Copy result to clipboard
--output / --> str Write result to file (overwrite)
--output_append / -->> str Write result to file (append)
paras nargs=* Positional parameters for the above

5.3. Advanced Dynamic Invocation

--all_modules, --call_py, --call_qml, --func, and --thread let you call arbitrary functions on any tab module. Module names accept a prefix (e.g. Scr matches ScreenshotOCR_1). Positional paras are auto-coerced to int → float → JSON (list/dict) → string. By default calls run on the main thread asynchronously (no return value); --thread runs them synchronously on the worker thread to capture a return (with a stability caveat). Example — add documents to the Batch Document tab and start it:

umi-ocr --call_qml BatchDOC --func addDocs '[ "C:/Users/My/Desktop/111.epub", "C:/Users/My/Desktop/222.pdf"]'
umi-ocr --call_qml BatchDOC --func docStart

Warning

Synchronous (--thread) calls run outside the QML main thread and can destabilize or crash the UI. Use them only when you must capture a return value, and prefer the dedicated HTTP API for production automation.


6. Configuration

Umi-OCR keeps configuration in two files, both inside the working directory ./UmiOCR-data/.

6.1. .pre_settings (pre-QML, JSON)

Loaded by utils/pre_configs.py before the QML engine starts, so it holds only the few keys needed early:

Key Default Purpose
i18n "" Interface language
opengl "" Renderer / OpenGL type
server_port 1224 HTTP service port
last_pid -1 PID of the last run (single-instance detection)
last_ptime -1 Process create time of the last run

Written atomically with json.dump (UTF-8, indented). Permission/IO errors are captured and surfaced via getErrorStr() rather than crashing.

6.2. .settings (main, INI)

The main configuration, managed by the QML engine and written whenever you change a setting in the UI (language, theme, fonts, OCR engine, service host, log level, etc.). You may edit it by hand and reload it without restarting via umi-ocr --reload, which publishes the <<settingsReload>> event.

Note

The HTTP server_port lives in .pre_settings, not .settings, because the service may need to start before the QML config is available. If port 1224 is occupied, the server auto-increments and persists the new port back to .pre_settings.

6.3. Logging

Logging (imports/umi_log.py) writes entries at or above a configurable level (default ERROR) to Umi-OCR/UmiOCR-data/logs. The level is adjustable in the Global Settings tab. Launching Umi-OCR from a terminal shows real-time logs. Qt's own messages are redirected into this logger via qInstallMessageHandler.


7. Security

Umi-OCR is designed to run on a trusted local machine. Understand these properties before exposing it.

7.1. Network Exposure

  • Default: loopback only. The service binds to 127.0.0.1, reachable only from the local machine.
  • /argv is hard-locked to localhost. It checks REMOTE_ADDR == "127.0.0.1" and returns 401 otherwise, regardless of the host setting — because it can read local files and shut down the app.
  • LAN exposure is opt-in. Switching the host to “any available address” in Global Settings makes the OCR/QR/doc endpoints reachable from the network.

Caution

When you expose the service to the LAN: there is no authentication on the API endpoints, CORS is wide open (Access-Control-Allow-Origin: *), and document upload accepts files up to 100 MB. Anyone who can reach the port can submit OCR/document jobs that consume CPU, memory, and disk on your machine. Only enable LAN access on a trusted network, and prefer a firewall rule or reverse proxy with authentication in front of it. The /argv control bridge remains localhost-only, so remote callers cannot drive the CLI.

7.2. File-Path Safety

The document routes guard against path traversal: both upload and download verify with os.path.commonpath that the resolved path stays inside UPLOAD_DIR (./temp_doc), returning an "Unauthorized path" error otherwise. Uploaded filenames are sanitized (illegal characters replaced, length capped at 255).

7.3. Data Handling

  • Fully offline. No telemetry, no cloud calls. Recognition happens in the local engine subprocess.
  • Temporary files. Uploaded documents and generated outputs live under ./temp_doc/<uuid>/ and are deleted on task clear, after a 24-hour retention window, or at next startup (the upload directory is wiped on init).
  • Encrypted PDFs. Passwords are passed through to the document engine and are not persisted beyond the task.

7.4. Process & Engine Isolation

The OCR engine runs as a separate subprocess loaded as a plugin. If it crashes, the API returns code: 902 ("failed to pass command to recognizer; subprocess likely crashed") rather than taking down the UI. Restarting Umi-OCR re-spawns the engine.


8. Operations

8.1. Starting and Stopping

  • Start: run Umi-OCR.exe (or the Scoop shim, or the Linux launcher). No service manager is involved — it is a desktop process.
  • Stop: close the window, press Esc to hide then quit, or umi-ocr --quit.

Warning

If an HTTP client holds an open connection when you close Umi-OCR, the UI thread exits but the network worker thread may linger (the app won't fully close). Ensure clients disconnect, or force-end the process via Task Manager. The custom WSGI server tracks active connections and force-closes them on shutdown, but a client that keeps reconnecting can still hold it open.

8.2. Health Check

curl http://127.0.0.1:1224/
# → the application full-name string if the service is alive

8.3. Choosing / Swapping the OCR Engine

Install engine plugins into ./UmiOCR-data/plugins/ (from the plugins repo) and select one under Global Settings. The two common engines:

  • PaddleOCR-json — slightly faster; rich language model libraries.
  • RapidOCR — broader hardware/OS compatibility.

8.4. Performance Tuning

  • Raise/lower the engine's ocr.limit_side_len (e.g. 960 → 4320) to trade speed for large-image accuracy.
  • Avoid concurrent API calls; the backend serializes work through the mission queue and has limited concurrency.
  • For overnight batch jobs, enable the post-task shutdown/sleep option in Batch OCR.
  • On display glitches, switch the Renderer (OpenGL → ANGLE/software) or disable hardware acceleration.

8.5. Deployment Checklist

  • [ ] Extract the release; confirm Umi-OCR.exe launches and a tab opens.
  • [ ] Verify an OCR engine plugin is present and selected (run a Screenshot OCR).
  • [ ] Confirm the HTTP service is enabled and curl http://127.0.0.1:1224/ responds.
  • [ ] If automating, decide loopback-only vs. LAN, and firewall accordingly.
  • [ ] Set the log level appropriately and confirm the logs directory is writable.
  • [ ] For document OCR, verify ./temp_doc is writable and has enough free space.

9. Troubleshooting (Operator View)

Error: code: 902 — recognizer subprocess crash

Cause: The OCR engine subprocess died or stopped responding (issue #987; can surface as [Errno 22] Invalid argument). Resolution: Restart Umi-OCR to re-spawn the engine. Switch engines if it recurs. Reduce image size / batch concurrency on low-memory hosts.

Error: 无法创建输出器 — unable to create output writer (doc code 203)

Cause: The selected output writer could not be instantiated (issue #640) — typically a non-writable output path or a file locked by another process. Resolution: Point output to a writable directory; close any program holding the target file.

Error: width=0,height=0 on screenshot (Linux)

Cause: An empty capture region (issue #1015). Resolution: Drag a non-empty rectangle; pass a valid rect=x,y,w,h.

Document /result reports the wrong completion state

Cause: Reported as an open issue (#1051) where the doc /result endpoint can return a completion state prematurely. Resolution: Rely on is_done && state == "success" together before calling /api/doc/download, and poll until both hold.

Error: connect ECONNREFUSED under load

Cause: Backend concurrency limits during sustained, high-volume calls. Resolution: Retry the request; serialize calls instead of issuing them in parallel.

App won't fully exit

Cause: A lingering HTTP client connection keeps the network thread alive. Resolution: Disconnect clients; force-end via Task Manager if needed.


10. Internals & Code Quality

10.1. Conventions

  • Connectors bridge QML and Python; each is a QObject with QML-invokable slots.
  • Mission subclasses override msnPreTask / msnTask; everything funnels through the shared queue runner with QMutex locking.
  • Registries map string keys to classes (tbpu.Parser, ocr.output.Output, plugin PluginInfo), making parsers/writers/engines pluggable.
  • Event bus (event_bus/) uses publish/subscribe for cross-component signals such as <<ScreenshotOcrEnd>> and <<settingsReload>>.
  • Platform shims (platform/win32, platform/linux) isolate OS-specific calls (key translation, new-process launch, native APIs).

10.2. Notable Design Choices

  • HTTP-as-IPC. Using a local HTTP service for both the public API and the cross-process CLI keeps a single code path for "do this OCR action," whether the caller is the command line, a script, or the UI.
  • Vendored Bottle. server/bottle.py is bundled so the app has no external web-framework dependency at runtime.
  • Custom WSGI server. _WSGIRefServer tracks active connections so it can force-close them on shutdown and find a free port if 1224 is taken.

10.3. Known Issues & Improvement Opportunities (from the issue tracker)

Theme Issues Note
PaddleOCR v5 / new engines #1045, #1016, #989 Demand for newer PaddleOCR (incl. GPU PP-OCRv5, #1053) — handled via the plugin system
Table recognition #87, #445, #1058 Recognizing tables/structured data is a recurring request
Math formula recognition #254 Experimental
macOS build #1057 Not currently provided (Windows + Linux only)
Linux autostart #1050 Auto-start on Linux requested
Engine subprocess robustness #987 code: 902 crashes — candidate for supervised restart

Tip

Most "support more engines / GPU / new models" requests are addressable without changing this repository — they are engine-plugin work in the plugins repository. The plugin contract (PluginInfo with group/api_class/global_options/local_options) is the extension point.

10.4. Contributing

  • Code: Build instructions live in the Windows and Linux runtime repositories — clone a runtime, then drop this application code into it.
  • Translations: Use Weblate; the dev-tools/i18n/ toolchain (Qt Linguist, lupdate/lrelease wrappers) compiles .ts → .qm files under UmiOCR-data/i18n/.
  • Engine plugins: Implement the PluginInfo contract in the plugins repository.

11. Quick Reference

11.1. Endpoints

Method Path Auth Purpose
GET / , /umiocr none Liveness / app name
POST /argv localhost-only Run CLI args against the instance
GET /api/ocr/get_options none OCR parameter schema
POST /api/ocr none Image OCR (Base64)
GET /api/doc/get_options none Document OCR parameter schema
POST /api/doc/upload none Upload document, start task
POST /api/doc/result none Poll task status / results
POST /api/doc/download none Build output, get download URL
GET /api/doc/download/<id>/<name> none Download built file
GET /api/doc/clear/<id> none Clear task + temp files
POST /api/qrcode none Read or generate codes

11.2. Status Codes

Code Meaning
100 Success
101 No text (OCR/QR) / no file uploaded (doc)
201–207 Document task errors (info, password, submit, writer, write, save, zip)
800–804 OCR request validation errors
902 Recognizer subprocess communication failure

11.3. Key Files & Paths

Path Purpose
UmiOCR-data/main.py Entry point (env bootstrap)
UmiOCR-data/py_src/run.py App startup
UmiOCR-data/.pre_settings Pre-QML config (port, language, pid)
UmiOCR-data/.settings Main config (INI, GUI-managed)
UmiOCR-data/plugins/ OCR engine plugins
UmiOCR-data/logs/ Log files
UmiOCR-data/temp_doc/ Document-OCR temp files

11.4. Defaults

Setting Default
Service host 127.0.0.1 (loopback)
Service port 1224 (auto-increment if busy)
Request size limit 100 MB
Layout parser multi_para
Document extraction mode mixed
Document download type pdfLayered
Temp file retention 24 hours
Log level ERROR