Introduction
A folder of scanned receipts is data you can't actually use. The numbers are locked inside pixels — you can't search them, total them, or look anything up.
The goal here is to take a single scanned receipt image and turn it into a record you can query with SQL-like expressions. We'll OCR the image with aux4/image, archive it as a searchable PDF, pull the text back out with aux4/pdf, and store the structured result in aux4/repository.
Install the packages
aux4 aux4 pkger install aux4/image-text aux4/pdf aux4/repository
aux4/image-text adds OCR commands (text, pdf) under the image profile and relies on Tesseract, which it installs for you.
Step 1: read text straight off an image
The fastest path from pixels to text is aux4 image text. Point it at an image and it prints what it recognizes to stdout:
aux4 image text receipt.png
GROCERY MART
742 Market Street
Milk 3.99
Bread 2.49
Eggs 4.25
Total 10.73
The page-segmentation mode matters for odd layouts. A single line of text — a label, a SKU — reads better with --psm 7:
aux4 image text label.png --psm 7
SKU-48217
Use --lang to OCR a different language (--lang eng is the default; install extra Tesseract language packs for others).
Step 2: archive the scan as a searchable PDF
Raw text is great for a pipeline, but for archiving you usually want to keep the original image and be able to search it. aux4 image pdf does both — it lays an invisible OCR text layer over the image and writes a PDF:
aux4 image pdf receipt.png --output receipt
receipt.pdf
The output name is given without the .pdf extension. Multi-page documents are just multiple inputs, one --input per page:
aux4 image pdf \
--input page1.png --input page2.png --input page3.png \
--output statement
You now have statement.pdf — looks like the scan, but every word is selectable and searchable.
Step 3: pull structure back out with aux4/pdf
Once you have a PDF — whether scanned or a real fillable form — aux4/pdf turns it back into data. The simplest command extracts plain text:
aux4 pdf text receipt.pdf
GROCERY MART
742 Market Street
Milk 3.99
Bread 2.49
Eggs 4.25
Total 10.73
For fillable forms, aux4 pdf parse is the powerful one: it returns a JSON array of pages, each with its text and every form field — name, type, value, options for dropdowns, and a bounding box (ref):
aux4 pdf parse invoice.pdf
[
{
"page": 1,
"text": "Please enter your name: [Field:Name] ...",
"fields": [
{
"name": "Name",
"alternativeText": "",
"value": "",
"type": "TextField",
"ref": {
"x": 202.468,
"y": 587.91,
"width": 209.821,
"height": 22
}
},
{
"name": "Dropdown2",
"alternativeText": "",
"value": ["Choice 1"],
"type": "Dropdown",
"options": ["Choice 1", "Choice 2", "Choice 3", "Choice 4"],
"ref": {
"x": 71.6528,
"y": 524.831,
"width": 72.0002,
"height": 20
}
}
]
}
]
A couple of neighbors worth knowing: aux4 pdf count returns the page count, aux4 pdf search <file> --term <word> finds a term with its page and coordinates, and aux4 pdf image <file> --page 2 --image page2.png renders a page back to an image. (There's no pdf render — it's pdf image.)
Step 4: store the structured record
Now the payoff: persist the extracted fields somewhere you can query them. aux4/repository is a local JSON document store backed by SQLite — no server, no schema migrations. Each "repository" is a table; it's created the first time you write to it, and the database file defaults to .local.db in the current directory.
After shaping the OCR/parse output into a clean object (with jq, or by hand), write it in:
aux4 repository write invoices --id inv-001 \
--data '{"vendor":"Acme","total":129.50,"date":"2025-12-17"}' \
--metadata '{"source":"scan","ocr":"tesseract"}'
inv-001
The id becomes the primary key, and writes are upserts — re-running with the same id updates the record instead of duplicating it. You can also pipe JSON straight in and let it generate a UUID:
echo '{"vendor":"Globex","total":58.20}' | aux4 repository write invoices
Step 5: query like a database
This is what the whole pipeline was for. aux4 repository find takes a SQL-like expression where bare field names resolve into the stored JSON:
aux4 repository find invoices --expr "total > 100 and vendor = 'Acme'"
[
{ "id": "inv-001", "vendor": "Acme", "total": 129.50, "date": "2025-12-17" }
]
It supports the comparisons you'd expect (=, !=, <, >, <=, >=), like for fuzzy text, and/or, and parentheses:
aux4 repository find invoices --expr "vendor like '%Acme%' or total < 60"
aux4 repository read invoices --id inv-001 # fetch one by id
Your folder of dead scans is now a searchable ledger.
Conclusion
The path from scan to searchable data is four short hops:
aux4 image text— OCR the image to plain text.aux4 image pdf— archive it as a searchable PDF.aux4 pdf parse/pdf text— extract text and form fields as JSON.aux4 repository write/find— store the structured record and query it.
Every step is a small, focused command, so you can stop at any point — just OCR, just archive, or go the whole way to a queryable store. Point it at receipts, invoices, forms, or a backlog of scanned paperwork, and the documents finally become data.