Your notes never leave your device. No login. No upload. No mercy for xerox bills.
Translation progress (help translate → · 📣 Translate-a-thon →):
Every exam season, the same tragedy: 41 dark PowerPoint slides hit the xerox shop, come back as 41 ghostly sheets of wasted paper, and eat ₹200 of your pocket money.
Notes Printifier flips them bright, stacks 3 per sheet, and hands you a 14-page PDF. It even counts what you saved — paper, ink, trees, and rupees — because watching that number climb is half the fun.
🎉 Real numbers from the sample lecture PDF:
41 pages → 14 pages·86% ink saved·1.71 MB (60% smaller)·≈ ₹71 kept in your pocket
| 🧩 N-up print layouts | 1×1 up to 3×3 slides per A4/Letter sheet, gaps tuned for hole-punching |
| 🌓 Dark-slide inversion | Lecturer's black theme → bright white paper, one toggle |
| 🖋 Ink saver | 2-tone posterization, grayscale, tint removal, blue-mark cleanup, edge trim |
| 📖 Booklet mode | Saddle-stitch ordering + fold guide — print a real booklet |
| 🔁 Duplex pad | Blank page inserted so double-sided backs line up |
| 🧠 AI Print Doctor | Ask for advice ("ink", "pages", "readability") and apply a suggested recipe |
| 🎙 Voice commands | "nine up", "black and white", "save ink" — EN + हिन्दी |
| 🔊 Spoken receipt | The result step can read your savings out loud |
| ✍️ OCR + text repaint | Searchable text layer; Labs repaints pages as crisp black-on-white text |
| 📐 Auto deskew | Phone photos of notes get straightened per page |
| 🎛 Presets | Economy / Balanced / Exam-cram built-ins + your own, shareable |
| 🎯 Print budget | "I can afford 25 sheets" → the layout auto-fits your limit |
| 🌱 Impact meter | Lifetime sheets / ₹ / trees / CO₂ saved, streaks and milestones |
| 🕘 History re-run | Yesterday's file, different settings, one tap (originals stay on-device) |
| ☁️ Cloud import | Paste a Google Drive / Dropbox link (self-hosted server proxy, SSRF-hardened) |
| 📱 PWA + Android APK | Install to home screen, or sideload the offline APK — with Android share-sheet support |
| 🗣 Bilingual | Every string in English and हिन्दी, incl. the spoken receipt |
| 🎨 12 themes · 10 patterns | Dark/light, ⌘K command palette, animated theme flip |
| Where | What you get | |
|---|---|---|
| 🌍 Web | avithegod2010.github.io/Notes-printifier | The whole PDF pipeline: upload → arrange → enhance → layout → download/print/share. Installable PWA, works offline after first visit. |
| 🤖 Android APK | Releases → printifier.apk |
Same pipeline, fully offline, plus Android share-sheet ("share PDF → Printifier") and home-screen app icon. |
| 💻 Self-host | bun install && bun run dev |
Everything above plus the server features (AI doctor, voice, Listen, cloud import, preset links, community stats). |
The live site is hosted on GitHub Pages — free, fast, static file hosting with no backend. The core app is pure browser magic and runs 100% there. A handful of features need Printifier's small server brain:
AI Print Doctor · Voice commands · Listen (spoken receipt) · Cloud preset links & QR · Cloud import · Community stats
On the static site they stay visible — tap one and a friendly dialog explains the situation, with a Why? button for the full story (and how to get the full experience). Nothing is hidden; nothing fires doomed requests. Self-host (below) or use the server deployment to light them up.
# you need bun (https://bun.sh) — node+npm works too, bun is just faster
bun install
bun run dev # → http://localhost:3000 (full server features on)
bun run lint # keep it clean
bunx tsc --noEmit # types are not optional
# fully static export (what Pages hosts and the APK packages)
bun run build:static # → .next-static/
# signed offline APKs — see VERSIONING.md for the release ritual
bash android/build-apk.sh beta # → public/printifier.apk
bash android/build-apk.sh stable # → public/printifier-stable.apkThe APK pipeline is Gradle-free (aapt2 + ecj + d8 + apksigner) and needs Android
build-tools 34 + a JRE; override locations with ANDROID_SDK=… and ECJ_JAR=….
The keystore (android/printifier.keystore) and its password are not in this repo (checked the
whole git history — they never were). The build script reads the password from $KS_PASS or
android/signing.local (gitignored). On GitHub Actions, set the KEYSTORE_BASE64 and KS_PASS
repository secrets and every v* tag builds a signed APK automatically. Lose the keystore and users
must uninstall/reinstall to update — back it up.
Static builds need no database. The full server uses Prisma + SQLite for the optional server
features — copy .env.example → .env (it's committed, so git won't ignore it) and set up the
schema, then bun run dev:
cp .env.example .env # set DATABASE_URL (SQLite file path)
bun run db:migrate # create + apply migrations (or: bun run db:push for a quick sync)| Environment variable | Used by | Purpose |
|---|---|---|
DATABASE_URL |
Prisma (/api/*) |
SQLite file for usage stats + shared presets. Required for server mode. |
TRUST_PROXY |
rate limiting (src/lib/rate-limit.ts) |
Set to 1 only behind a reverse proxy that OVERWRITES X-Forwarded-For/X-Real-IP. Without it those headers are treated as client-spoofable and clients are throttled per user-agent bucket instead. |
FRAME_ANCESTORS |
security headers (next.config.ts) |
CSP frame-ancestors for server mode. Default 'self' (clickjacking-safe). Set * or a host list only if you must embed the app cross-origin. |
Z_AI_API_KEY (+ SDK vars) |
/api/ai/* |
Enables the LLM-backed Print Doctor / voice / TTS features via z-ai-web-dev-sdk. Without credentials those routes answer with local heuristics — nothing is sent to a third party. |
NEXT_PUBLIC_BASE_PATH |
build | Serve under a subpath (e.g. GitHub Pages /Notes-printifier). |
NEXT_PUBLIC_STATIC / STATIC_EXPORT |
build | Set by scripts/static-export.sh — compiles server-only features out of static exports and APKs. Don't set manually. |
Server features: anonymous usage stats (/api/prints — hashed file name only), shareable preset
links & QR codes (/api/presets, expire after 30 days), the Drive/Dropbox import proxy
(/api/cloud-import — 75 MB cap, per-hop SSRF validation + IP pinning, rate-limited) and the AI
routes (/api/ai/*).
src/app/ Next.js App Router (page + optional API routes)
src/components/printifier/ the app (upload → arrange → enhance → layout → finish)
src/lib/pdf/ PDF.js render, compose pipeline (worker), filters, OCR
src/lib/ i18n, zustand store, presets, theme, savings math
android/ WebView APK source + Gradle-free build script
scripts/ icon generators, static-export helper
docs/screenshots/ the images above
.github/workflows/ Pages deploy · CI · CodeQL · APK releases
- Files never leave your device. Parsing, filtering and PDF generation all run locally — load the page once and it even works offline.
- History, presets and the impact meter live in IndexedDB/localStorage. Clear them any time.
- Static builds (GitHub Pages / APK) make no server calls at all — server-only features are
compiled out at build time (
NEXT_PUBLIC_STATIC=1, enforced by a CI check). - Self-hosted server mode adds optional calls: an anonymous usage ping whose "file name" is a
16-hex truncated SHA-256 hash (the server REJECTS anything that isn't a hash, so titles can
never be stored), shared-preset links (settings only, expire in 30 days), the cloud-import
proxy (fetches the URL you paste, keeps nothing), and — only if you configured LLM
credentials — the AI features, which send the Print Doctor the document's file name and your
current settings, and voice commands the recorded audio, to the configured LLM provider.
Don't want that? Leave
Z_AI_*unset — the AI routes degrade to local heuristics. - Security model and reporting: SECURITY.md.
Two release lines from one codebase: stable (frozen artifacts from the stable branch) and
beta (rolling, from main). Every release lives in Releases — signed APKs as
assets, notes on what changed. See VERSIONING.md for the ritual.
PRs welcome! Keep it typed (bunx tsc --noEmit), linted (bun run lint), and remember: anything
that ships to the static site must work without a server. Bilingual strings live in
src/lib/i18n.ts as key: ['English', 'हिन्दी'] pairs.
🌍 Translator? Join the Translate-a-thon — claim a section for मराठी / தமிழ் / বাংলা (or a new language) via the translation issue template. Partial PRs welcome — untranslated keys fall back to English automatically.
MIT — print freely. 🖨️


