KDP PDF β Upload a Design, Get a PDF
Drop a cover image β the app uses AI to design a journal interior, stamps your cover on top, upscales to 300 DPI, and gives you a print-ready KDP PDF. One image-generation call per book.
Pipeline
Cover + theme note
β
βΌ
OpenRouter /v1/images (google/gemini-3.1-flash-lite-image)
β
βΌ
Pillow β stamp cover on top 1.5 in
β
βΌ
Pillow β LANCZOS upscale to 300 DPI + sharpen
β
βΌ
ReportLab β embed as N pages of a 6Γ9 in PDF
β
βΌ
outputs/{filename}.pdf
Quickstart
Install dependencies
pip install -r requirements.txtAdd the API key
Create
.streamlit/secrets.toml(gitignored):OPENROUTER_API_KEY = "sk-or-v1-YOUR-FRESH-KEY"Run
streamlit run app.pyUse
- Upload a cover image (PNG / JPG / WebP).
- (Optional) Add a short theme note, e.g. "golden retriever playing pickleball on a court".
- Click Generate PDF and download the result.
Cost
One image-generation call per book via OpenRouter. The user pays OpenRouter directly. The exact price depends on the model; see the OpenRouter models page.
If your API key was ever shared in chat, email, or a screenshot, rotate it in the OpenRouter dashboard before using.
Files
| File | What it does |
|---|---|
app.py |
Streamlit UI: upload, settings, generate, download |
engine.py |
Pipeline: OpenRouter call, stamp, upscale, PDF |
OPENROUTER_PROMPT.txt |
The one-shot prompt sent to the AI. Edit this to tune the design. |
requirements.txt |
reportlab, streamlit, Pillow, svglib, requests |
assets/ |
Sample cover images (reference only) |
outputs/ |
Generated PDFs (gitignored) |
PROMPT.txt |
V7 LLM-blueprint prompt (legacy, reference only) |
FLOW_PROMPT.txt |
Google Flow visual-mockup prompt (reference only) |
CLI usage
python engine.py --cover assets/golden_retriever_pickleball.png \
--out my_book \
--theme "golden retriever playing pickleball" \
--pages 1
Outputs outputs/my_book.pdf.
Deploy to Hugging Face Spaces
- Push the repo to a new Space (Streamlit).
- In the Space's Settings β Variables and secrets, add:
OPENROUTER_API_KEY(Secret)
- Space boots clean. Free tier supports Pillow + requests + reportlab (no torch / realesrgan required).
Tuning the design
Edit OPENROUTER_PROMPT.txt. The engine replaces {THEME_NOTE}
with whatever the user types in the UI. Common tweaks:
- Want denser pages? Add "no whitespace between elements" to the prompt.
- Want a specific vibe? Add a single adjective to the THEME section: "luxurious", "minimalist", "playful".
- Want different motifs? Replace the bullet list under "INTERIOR CONTENT".
- Want crisper lines from the AI? The prompt ends with a "FINAL" line demanding vector-crisp output β edit it to make it more or less aggressive.
8K quality pipeline
The AI image comes back at whatever resolution OpenRouter returns (typically ~1024 px wide). The cover is composited on the top 1.5 in of a 600 DPI (3600Γ5400 px) working canvas, then routed through a 4-pass sharpen chain:
LANCZOSresize to the target resolutionPIL.ImageFilter.UnsharpMask(radius=2, percent=150, threshold=3)PIL.ImageFilter.SHARPEN(kernel sharpen)PIL.ImageEnhance.Sharpness(img).enhance(2.0)
The color palette is then quantized to 32 flat colors (posterize + quantize) for a true Adobe Illustrator look β no anti-aliased halos around flat-color regions. The result is embedded in the PDF as N full-bleed pages.
Vectorize mode (optional)
Setting VECTORIZE=1 in the Space's environment variables enables
a potrace-based vector path that traces the line work to SVG and
re-rasterizes at the target DPI for pixel-perfect lines. Requires
the potrace system binary. Falls back gracefully to the raster
path if potrace is missing.
Limitations
- All pages are identical. Variation across pages is not supported in V8 (one image, repeated N times).
- The AI does not see the cover image. It only sees the text theme note. The cover is stamped in post-production.
- Pillow-only upscaling. For ultra-high-fidelity, replace
_upscale_to_printinengine.pywith a Real-ESRGAN call (will require PyTorch β won't fit HF Spaces free tier). - The default render is 600 DPI raster (4x the V8 baseline). For
perfect vector lines, set
VECTORIZE=1in the Space's environment variables (requirespotrace).
Security
- Never commit
.streamlit/secrets.tomlor any file containing an API key. The included.gitignoreblocks this. - Rotate any key that has been shared.