Turn your BOOX (or any Android e-ink reader) into a wireless network printer.
Documents printed from any device on your LAN are saved as PDF directly to the BOOX —
or, through the InkPrint EPUB printer, as a reflowable e-book.
| macOS — print to InkPrint from Chrome | Android app — file received |
|---|---|
![]() |
![]() |
InkPrint was inspired by my daughter, Kiki.
To look after her eyes, Kiki does her reading on an e-ink reader. One day she pointed out that if we cared about the environment, we should be wasting less paper — so why not "print" straight to the e-reader instead?
The idea turned out to be simple and practical. Every computer and phone already knows how to print, so printing to an e-ink reader needs no changes to the apps and systems people already use: pick the printer, press Print, and the document lands on the reader instead of on paper. That is how this app came to be.
Thank you, Kiki. 💚
InkPrint 的灵感来自我的女儿 Kiki。
为了保护视力,Kiki 一直用电纸书阅读。有一天她说,为了保护环境,我们应该少浪费一些纸张,那为什么不把要打印的东西直接“打印”到电纸书上呢?
这个想法简单又实用。电脑和手机本来就会打印,打印到电纸书不需要改造任何已有的应用和信息系统:选中打印机,点一下打印,文档就会出现在电纸书上,而不是纸上,既方便又环保。于是就有了这个 App。
谢谢你,Kiki。💚
macOS / Windows / Linux / iOS / Android
│ print via IPP over WiFi
▼
InkPrint (Android app on BOOX)
├── IPP/HTTP server (port 6310)
├── mDNS/Bonjour advertiser (_ipp._tcp + AirPrint subtype)
└── Saves PDF to /Documents/InkPrint/
- IPP 2.0 server implemented in Rust (
inkprint-corecrate) - AirPrint compatible — macOS, iOS, and iPadOS discover it with zero configuration
- Bonjour/mDNS advertisement broadcasts both
_ipp._tcpand_universal._sub._ipp._tcpso all major platforms can auto-discover the printer - Pure PDF storage — every print job is saved as a PDF file, ideal for e-ink reading
- Rust core compiled to
aarch64-linux-androidvia cargo-ndk, bridged to Kotlin via UniFFI
| Component | Version |
|---|---|
| Android (target device) | 8.0+ (API 26+) |
| Android NDK | r29 |
| Android SDK platform | 36 (compile & target) |
| Rust toolchain | nightly |
| cargo-ndk | latest |
| JDK | 17 |
| macOS build host | Apple Silicon recommended |
# Rust .so, UniFFI bindings, pdfium download (checksum-verified), debug APK
make android-debug
# Install on device
adb install -r android/app/build/outputs/apk/full/debug/app-full-debug.apkThe app has two flavors:
| Flavor | EPUB printer | Native deps | Build |
|---|---|---|---|
full (Play, GitHub releases) |
yes | pdfium (make fetch-native-deps), ONNX Runtime (Maven AAR), models in android/app/src/full/assets/models/ |
make android-release / make android-bundle |
fdroid |
no | none — Rust core built with --no-default-features |
make android-fdroid |
Tests: make rust-test runs everything that needs no native libraries;
make epub-test (macOS arm64) downloads pdfium and ONNX Runtime and also runs
the end-to-end PDF → EPUB conversions.
Note: Use the rustup nightly toolchain (
RUSTC=~/.rustup/toolchains/nightly-aarch64-apple-darwin/bin/rustc).
Homebrew'srustcdoes not include Android targets.
Google Play accepts an Android App Bundle, not an APK:
make android-bundle # -> android/app/build/outputs/bundle/fullRelease/app-full-release.aabSigning credentials are read from Gradle properties or the environment and are
never stored in the repository. Put them in ~/.gradle/gradle.properties:
INKPRINT_STORE_FILE=/absolute/path/to/inkprint-release.jks
INKPRINT_STORE_PASSWORD=...
INKPRINT_KEY_ALIAS=inkprint
INKPRINT_KEY_PASSWORD=...Without them the release build still succeeds, but the output is unsigned.
InkPrint collects nothing: no accounts, no analytics, no servers, no outbound connections. Everything stays on the device and the local network — including the EPUB printer's layout analysis and text recognition, whose models ship inside the app.
- Install the APK on your BOOX (or other android device) device
- Open InkPrint and tap Start Printer Service
- Add the printer on your computer/phone (see below)
- Print — the PDF appears in the app's file list and in
Documents/InkPrint/(or your chosen save folder) on the BOOX
Next to InkPrint, the app offers a second printer, InkPrint EPUB
(ipp://<device>:6310/ipp/epub). Print to it and the BOOX turns the document
into a reflowable EPUB in the background — the notification shows the page
being converted and has a Cancel button.
- Text-layer PDFs (almost anything printed from a computer) are rebuilt from their text: headings, paragraphs, lists, and pictures in reading order. About 0.6 s per page on a BOOX Note Air (Snapdragon 636).
- Scanned pages go through on-device OCR (PaddleOCR PP-OCRv6 tiny), about 3 s per page. Switch Recognise scanned pages off to keep them as pictures.
- Tables, charts and formulas are kept as pictures (grayscale, cut from the page).
- Running headers, footers and page numbers are dropped; paragraphs split by a page break are joined; long documents are split into chapters.
The EPUB replaces the PDF unless Also keep the PDF is on. If a conversion fails or is cancelled, the PDF is saved instead. Turning the EPUB printer off removes it from the network. The F-Droid build does not include it.
There are two printers: InkPrint saves a PDF, InkPrint EPUB saves a
reflowable e-book (/ipp/print and /ipp/epub on port 6310). Add one or both;
each platform below covers both.
- System Settings → Printers & Scanners → Add Printer, Scanner or Fax…
- InkPrint and InkPrint EPUB appear in the list automatically — select one
- Use: AirPrint is selected — click Add
- Repeat for the other printer if you want both
No driver download needed. Works on macOS Ventura, Sonoma, and Sequoia.
Manual — Terminal (by IP, e.g. over Tailscale/VPN, where auto discovery doesn't work):
lpadmin -p InkPrint -E \
-v ipp://<ANDROID_IP>:6310/ipp/print \
-m everywhere
# EPUB printer
lpadmin -p InkPrint-EPUB -E \
-v ipp://<ANDROID_IP>:6310/ipp/epub \
-m everywhereDon't add it with "Generic PostScript Printer": that sends PostScript, and InkPrint only accepts PDF.
Automatic:
- Settings → Bluetooth & devices → Printers & scanners
- Click Add device — InkPrint and InkPrint EPUB appear on the same network
- Click Add device next to the one you want (or both)
If InkPrint doesn't show up, add it manually:
Manual (add by IP):
- Settings → Printers & scanners → Add device
- "The printer that I want isn't listed" → Add a printer using an IP address or hostname
- Protocol: IPP / Hostname:
<ANDROID_IP>/ Port:6310/ Queue:ipp/print(EPUB printer:ipp/epub) - Driver: Microsoft IPP Class Driver
- For the EPUB printer, repeat with Queue
ipp/epuband name it InkPrint EPUB
Don't pick a "Generic / Text Only" or PostScript driver: those send text or PostScript instead of PDF, and InkPrint rejects the job.
The Windows setup hasn't been verified end-to-end yet. If jobs fail with "document format not supported", please open an issue.
# Add printer (driverless, IPP Everywhere)
sudo lpadmin -p InkPrint -E \
-v ipp://<ANDROID_IP>:6310/ipp/print \
-m everywhere
# EPUB printer
sudo lpadmin -p InkPrint-EPUB -E \
-v ipp://<ANDROID_IP>:6310/ipp/epub \
-m everywhere
# Set as default (optional)
sudo lpoptions -d InkPrint
# Print
lp -d InkPrint /path/to/document.pdfWorks on Ubuntu, Debian, Fedora, Arch, and any distro with CUPS.
GNOME / KDE GUI: Settings → Printers → Add a Printer → enter ipp://<ANDROID_IP>:6310/ipp/print (EPUB printer: .../ipp/epub) → driver IPP Everywhere (driverless). Don't pick a Generic, PostScript or vendor driver: those don't send PDF.
- Connect iPhone/iPad to the same WiFi network as the BOOX(ANDROID)
- In any app: Share → Print
- Tap Select Printer — InkPrint and InkPrint EPUB appear automatically
- Tap Print — InkPrint saves a PDF on the BOOX, InkPrint EPUB an e-book
⚠️ Personal Hotspot limitation: if your iPhone is sharing its hotspot, devices connected to it cannot discover the printer. Use a shared WiFi router.
Built-in Print Service (Android 8+):
- Settings → Connected devices → Connection preferences → Printing
- Default Print Service → Enable
- InkPrint and InkPrint EPUB appear automatically on the same WiFi
Manual:
- In Default Print Service, tap ⋮ → Add printer → Add printer by IP address
- Enter the address with the port:
<ANDROID_IP>:6310
Adding by IP only reaches the PDF printer (Android's dialog takes no printer path); use auto-discovery for InkPrint EPUB.
Auto discovery uses mDNS (Bonjour), which only works inside one Wi-Fi network — Tailscale and most VPNs don't carry it. You can still print by adding the printer manually with the device's Tailscale/VPN IP (100.x.y.z) using the per-platform Manual steps above. While the service runs, the app lists every address it can be reached at (Wi-Fi, Tailscale, VPN) with a copyable printer URL, and How to add this printer can show its steps for any of them.
The one thing to get right is the driver: InkPrint only stores PDF. Choose the driverless option (-m everywhere on macOS and Linux, Microsoft IPP Class Driver on Windows, <IP>:6310 in Android's Default Print Service). Generic PostScript, text-only or vendor drivers send other formats; InkPrint rejects those jobs with "document format not supported" instead of saving a file that won't open.
Older versions advertised PWG raster and JPEG support without actually accepting them, so a printer added with an older version can still send those formats (on macOS, for example, printing a JPEG fails with "the printer cannot print the supplied content"). After upgrading, remove InkPrint from your computer's printer list and add it again.
Tap Choose under Printed Files to pick a save folder (the picker opens at Documents/InkPrint/). Received PDFs are then moved straight into that folder, and the app lets you browse it: open subfolders, create folders, and share, rename, move or delete files — the same files your reader app sees.
Until a folder is chosen, every received PDF is saved twice on the device:
Documents/InkPrint/— shared folder, visible to readers and file managersAndroid/data/com.inkprint.app/files/InkPrint/— the app's own copy (moved into the save folder once you pick one)
inkprint/
├── inkprint-core/ # Rust library
│ ├── src/
│ │ ├── lib.rs # UniFFI entry point, public API
│ │ ├── ipp/ # IPP protocol: parser, types, operations, printer state
│ │ ├── server/ # HTTP server (hyper 1.x) + TCP listener
│ │ └── mdns/ # mDNS advertiser (mdns-sd, pure Rust)
│ ├── inkprint.udl # UniFFI interface definition
│ └── Cargo.toml
├── inkprint-epub/ # PDF → EPUB: pdfium, layout + OCR (oar-ocr / ONNX Runtime), EPUB writer
├── design/ # Design notes (epub-export.md)
├── spikes/ocr-bench/ # On-device OCR benchmark used for performance regressions
├── android/ # Android app
│ └── app/src/main/
│ ├── kotlin/com/inkprint/app/
│ │ ├── MainActivity.kt # Compose UI
│ │ ├── PrinterService.kt # Foreground service
│ │ ├── ConversionQueue.kt # Background PDF → EPUB conversion
│ │ ├── BootReceiver.kt # Auto-start on boot
│ │ └── InkPrintLib.kt # UniFFI wrapper
│ └── jniLibs/arm64-v8a/ # Compiled .so files
├── Makefile
└── README.md
- Port 6310 — Android blocks ports below 1024 for non-system apps; 631 (standard IPP) is not usable
- mDNS from Rust — Android's
NsdManagerAPI (< API 33) cannot register_universal._sub._ipp._tcpsubtypes required for AirPrint auto-selection. The Rustmdns-sdcrate advertises both_ipp._tcpand_universal._sub._ipp._tcpcorrectly - PDF-only storage — the
cupsFilter2PPD directive and IPPdocument-format-acceptedlist guide clients to send PDF; raw PostScript is accepted and stored but not rendered - UniFFI bridge — Rust callbacks (
PrintJobCallback) are implemented in Kotlin and called from the IPP job handler when a file is fully received - Two printers, one port —
/ipp/print(PDF) and/ipp/epub(EPUB) differ in name, UUID and output; the job callback tells Kotlin which one received the file - EPUB conversion —
inkprint-epubloadslibpdfium.soandlibonnxruntime.soat runtime (pdfium-render binding,ortload-dynamic), so the Rust build never links them. Seedesign/epub-export.md
MIT © 2026 charlie-xing
If you use InkPrint commercially, please drop a line to xjohn1666@gmail.com — I'd love to hear about it. This is a friendly request, not an extra condition: the MIT license alone governs your rights.
如将 InkPrint 用于商业用途,欢迎发邮件至 xjohn1666@gmail.com 告知。这只是请求,并非附加条件,您的权利仅以 MIT 协议为准。

