An offline document archive that reads Arabic and English.
Photograph your paperwork, Waraqi pulls the text out, and you can search it later. It runs on your machine, with no account and no server.
Tools that do this already exist. They assume a server you are willing to run, and they treat Arabic as an afterthought when they support it at all. That puts them out of reach of the people who need them most: anyone holding a folder of contracts, IDs, prescriptions and invoices they cannot search.
There is also a reason not to reach for a cloud service. The documents in that folder are an ID card, a rental contract, a medical prescription. Those are exactly the things people should not be uploading to someone else's computer.
Most paperwork here is not in one language either. A single page carries an Arabic header, an English form field and a number in either script. Waraqi reads both on every page.
- Drag photos and PDFs onto the window, or pick them
- PDF pages with a real text layer are read directly, with no OCR at all
- Scanned pages, and photographs, go through OCR in Arabic and English together
- Full text search in either language, with Arabic normalization
- Tags, searchable, with counts, applied automatically from the page
- Edit the extracted text when OCR gets something wrong
- Export one document as a searchable PDF, or the whole archive as files and a CSV
- A four digit PIN on the window
- Arabic and English interface, right to left and left to right, dark and light
flowchart TD
A[Drop or pick a file] --> B[SHA-256]
B -->|already in the library| Z[Skipped as a duplicate]
B -->|new| C[Copy into the library folder]
C --> R{PDF or image?}
R -->|PDF page with text| T[Read the text layer directly]
R -->|PDF page, scanned| D
R -->|image| D[Upscale, greyscale, contrast, deskew]
D --> E[Tesseract, both languages]
E --> F[Drop lines with no content]
T --> G[Normalize Arabic]
F --> G
G --> H[(SQLite + FTS5)]
G --> I[Keyword rules] --> J[Type and tags]
J --> H
PDFs are checked page by page rather than file by file, because hybrid documents are common: a typed contract with a scanned signature page. Pages carrying real text cost nothing and are perfectly accurate. Only the scanned ones are recognised, and a document with more than thirty scanned pages stops there and says so, rather than running for an hour.
The copy in step three is not tidiness. The webview may only display files inside the app's own data folder, so importing has to move the file there before it can ever be shown.
There is no language setting. Asking someone to classify a document before importing it is work the app should do.
English mostly takes care of itself. Arabic does not, and that is where the work went. The same word is written several ways: diacritics are optional, alef carries hamza or not, taa marbuta and haa get swapped, numerals come in two sets. Whichever way a person types it is legitimate.
normalize('إيجار') === normalize('ايجار') // hamza
normalize('مُسْتَشْفَى') === normalize('مستشفي') // diacritics
normalize('١٥٠٠') === '1500' // numeralsEvery document is normalized before it is indexed and every query is normalized
the same way. SQLite's own tokenizer does none of this for Arabic, which is the
reason searching an Arabic archive normally fails. Those three cases are covered
by tests: npm test.
How well the OCR itself performs, measured rather than claimed, is in ACCURACY.md.
Tags and document types follow the document, not the interface. A page reading Passport is tagged Passport, one reading جواز سفر is tagged جواز سفر.
With no API key set, Waraqi makes no network requests at any point, including the first run. The language models, the Tesseract WebAssembly core and the Amiri font are all committed here, so a fresh clone builds and runs with nothing downloaded.
Your documents live in one folder alongside a single SQLite file:
Windows %APPDATA%\ngo.josa.waraqi\
macOS ~/Library/Application Support/ngo.josa.waraqi/
Linux ~/.local/share/ngo.josa.waraqi/
Delete that folder and the app is empty. The archive export writes your images plus a CSV of all the text, so you can leave and take everything with you.
Off unless you supply your own Gemini key in settings. It comes in two parts, kept separate because they carry different weight.
Fix text sends only the text already extracted. Re-read image sends the photograph, and asks every time, naming the destination and the size. Neither writes anything on its own: the result lands in the editor and you decide.
src/lib/ai.js is the only file in the project that touches the network.
Needs Node 20 or later and Rust.
npm install
npm run tauri dev # run it
npm run tauri build # build an installer
npm test # run the testsTauri 2 for the shell, vanilla JavaScript for the frontend, SQLite for storage. No framework.
src/lib/ arabic.js normalization, db.js schema and FTS triggers,
prep.js preprocessing, ocr.js Tesseract, importer.js the pipeline,
search.js, tags.js, types.js, lock.js, ai.js, export/
src/ui/ library grid, viewer, import handling, tag sidebar, settings, lock
public/ language models, Tesseract core, Amiri font
Search runs on SQLite FTS5 with an external content table, so the index is kept in sync by triggers rather than storing the text twice. Tesseract runs in a worker with its models read from disk.
MIT, see LICENSE. Third party components are listed in THIRD_PARTY.md.