Scan documents into clean, searchable PDFs, entirely on your device: page finding, flattening, clean-up and offline text recognition. No permissions, no upload. Free and open source.
翻译准备中
正在加载完整 README。
查看完整 README ↗Scan documents into clean, searchable PDFs, on your own device. Photograph a letter, form, receipt or ID: Flatbed finds the page, flattens and straightens it, cleans it up, reads the text and makes a PDF with no hidden details about you or your phone. No account, no server, no upload.
Use it now: flatbed.anonacondacodelab.com
A scanner sees your most private papers: passport copies for a visa or a rental, bank letters, tax forms, payslips, contracts, prescriptions and test results. Most scanner apps upload them to read the text. Flatbed can't: the web app blocks every network connection and the Android app has no internet permission. And the PDF it makes carries only the pages: no location, phone model or date from the photos, no author, app name, dates or document IDs in the file.
The whole web app is one file, dist/index.html, built by node build.mjs from src/. Its Content Security Policy
blocks every connection (connect-src 'none'); it allows workers only from blob: addresses that the page makes from
its own code, and WebAssembly ('wasm-unsafe-eval') for the text recognition. The engine and the language files are
embedded in the page (gzipped, about 6.5 MB of the 9 MB) and unpacked when text is first read.
src/scan.js: page detection (edges, convex hull, largest four-cornered shape that the edges follow), perspective
correction, straightening (projection profile) and clean-up (paper-brightness map). Plain JavaScript, no dependencies;
runs in a worker (src/work.js) and in Node for the tests.src/ocr-engine.js: drives tesseract.js-core directly in a worker,
handing it the engine and language data (so it never downloads anything).src/pdf.js: the PDF, with pdf-lib; the invisible text uses Tesseract's
half-kilobyte "glyphless" font, which works for any script.src/clean.js: copied unchanged from Unmarked (commit
2e038a5); tests/build.test.mjs notes if the two drift apart.dist/ is not committed: Cloudflare Pages runs node build.mjs and publishes dist/, including the
HTTP headers in dist/_headers (no framing by other sites, no connections, no referrer). Settings: build command
node build.mjs, output directory dist, environment variable NODE_VERSION=22. Web Analytics stays off.
node build.mjs # builds dist/
npm test # builds, then runs the tests (Node's built-in runner, no dependencies)
The tests run page detection on synthetic photos, text recognition in English and Thai with the bundled engine, and
check the PDFs (text layer, image-only, page sizes, no hidden data; pdftotext from poppler-utils is used when
present). Test images are made by tests/fixtures/make-fixtures.py (Pillow). The Android app is in mobile/ (see
mobile/README.md).
Flatbed is provided as is, without any warranty. Text recognition makes mistakes; check important text. Flatbed leaves hidden data out of its PDFs, but what's visible on the page is in the scan. You are responsible for what you share; Anonaconda Code Lab accepts no liability for any loss or harm from using this software.
Free software under the GNU General Public License v3 or later, with no warranty. © 2026 Anonaconda Code Lab. Third-party components: see NOTICE.md. Security and privacy: see SECURITY.md. Questions or problems: [email protected].