Session 17: Shipping Address, Font Fix, Lulu Download
Date: 2026-04-03 Focus: Add shipping address to order form, fix PDF font embedding bug, build one-click Lulu download for admin fulfillment
Work Completed
1. Shipping Address on Order Form
Added full shipping address collection to the print order flow:
- Database: New migration adds 7 columns to
print_orders:shipping_name,shipping_street,shipping_street2,shipping_city,shipping_state,shipping_zip,shipping_country - Order form (
PrintOrderCard.tsx): New "Ship To" section with name, street, apt/suite, city/state/zip, country dropdown (US/CA/GB/AU). All required fields validated before order button enables. - API route (
order-print.ts): Validates and stores shipping address on both Stripe and $0 orders - Email notification (
email.ts): Shipping address included in admin notification email (HTML table row + plain text) - Stripe webhook: Passes shipping from order record into paid notification
- Admin page (
admin/index.astro): New "Ship To" column in print orders table showing full formatted address

2. PDF Font Embedding Bug — Found and Fixed
Root cause (two issues):
Missing italic variants — The PDF uses
fontStyle: 'italic'for relationships, prompts, and back page quote. Five fonts (Crimson Pro, Playfair Display, Inter, Open Sans, Montserrat) had no italic variant registered. When any was the body font, PDF generation crashed:"Could not resolve font for X, fontWeight 400, fontStyle italic". Added italic variants for all 5 fonts from Google Fonts.Built-in fonts not embedded — Default body font was
'serif'→Times-Roman, a PDF standard font NOT embedded in output. Lulu requires all fonts embedded. Changed defaults to'crimson-pro'(Google Font, properly subset-embedded as TrueType). Fallback inpdfFontFamily()also changed fromTimes-RomantoCrimson Pro.
Also fixed: Interior page background changed from #FFFBF5 (warm cream) to #FFFFFF (pure white) — the tinted background triggered Lulu's high ink coverage warning on every page.
3. One-Click "Download for Lulu" (Admin Only)
Built a Lulu-ready PDF generation tool on the admin page, tied to specific paid orders:
New files:
src/lib/lulu-cover.ts— Spine width calculator (paperback formula + hardcover lookup table from Lulu guide), cover spread dimension calculator with softcover bleed (0.125") and hardcover casewrap wrap+hinge (0.875")src/components/LuluCoverPDF.tsx— React-pdf component rendering cover spread (back cover + spine + front cover) using book's cover designsrc/components/LuluDownloadButton.tsx— Binding picker + one-click generation of both PDFs with real page count validation
Admin workflow:
- Order comes in → appears in Print Orders table on
/admin - Paid/processing orders show "Download PDFs" action link
- Clicking opens a modal pre-loaded with that book's data
- Pick binding type (softcover 32+ pages / hardcover 24+ pages)
- Click "Download Interior + Cover" — generates actual PDF, counts real pages, generates matching cover spread
- Two files download:
{Name}_Interior.pdfand{Name}_Cover.pdf - Upload to Lulu: interior first, then cover — dimensions match exactly

Hardcover cover dimensions: Discovered Lulu adds 0.75" per edge for casewrap board/hinge beyond standard 0.125" bleed. Formula: 0.875 + trim + spine + trim + 0.875 wide, 0.875 + trim + 0.875 tall. Verified: 14.000" x 10.750" for 6x9" hardcover matches Lulu's expected dimensions exactly.
Not visible to customers — the download tool only appears on /admin (behind admin auth). Customers see only the "Order a Printed Copy" card with markup pricing.
4. Lulu Validation — Passed
Uploaded test PDFs to Lulu and confirmed acceptance:

- US Trade 6x9", 33 pages, Hardcover Case Wrap, Premium Color
- Interior: fonts embedded, white background, no warnings
- Cover: 14" x 10.75" dimensions accepted
- Print cost: $14.67 USD
5. Lulu Cover Upload Guide
Saved Lulu's cover upload specifications to docs/planning/lulu-cover-upload-guide.md:
- Interior and cover file specs
- Spine width formulas (paperback + hardcover lookup table)
- Cover spread layout diagrams
- Dust jacket flap dimensions
- Linen wrap foil stamp character limits
Decisions Made
- Pure white interior background —
#FFFFFFnot#FFFBF5. Tinted backgrounds trigger Lulu ink coverage warnings. - Crimson Pro as default body font — replaces Times-Roman (unembedded built-in) to satisfy Lulu's font embedding requirement.
- Hardcover wrap margin = 0.75" — reverse-engineered from Lulu's expected dimensions (14" x 10.75" for 6x9" casewrap).
- Lulu download on admin page, not customer page — tied to specific paid orders in the Print Orders table. No RBAC mixing.
- No dust jacket support yet — casewrap only. Dust jacket adds flaps (3.25" each), can add later.
- Real page count, not estimates — the download button renders the actual PDF first, counts pages from the blob, then validates against Lulu minimums before generating the cover.
Files Modified
| File | Change |
|---|---|
src/components/PrintOrderCard.tsx |
Shipping address form fields + validation |
src/pages/api/order-print.ts |
Accept/validate/store shipping address |
src/lib/email.ts |
Shipping in admin notification email |
src/pages/api/stripe-webhook.ts |
Pass shipping from order to notification |
src/pages/admin/index.astro |
Ship To column, Download PDFs action, Lulu modal |
src/lib/pdf-fonts.ts |
Italic variants for 5 fonts |
src/lib/cover-design.ts |
Default fonts → crimson-pro, fallback → Crimson Pro |
src/components/TributeBookPDF.tsx |
White background, default body font |
Files Created
| File | Purpose |
|---|---|
src/lib/lulu-cover.ts |
Spine width + cover dimension calculator |
src/components/LuluCoverPDF.tsx |
Cover spread PDF component |
src/components/LuluDownloadButton.tsx |
One-click Lulu download UI |
supabase/migrations/20260403000000_add_shipping_address.sql |
Shipping address columns |
docs/planning/lulu-cover-upload-guide.md |
Lulu cover specs reference |
Next Steps
- Test full Lulu upload flow with Sheri's real book data (C.C. Dudley)
- Add dust jacket cover support (linen wrap with flaps) if needed
- Wire up Lulu API for automated order submission
- Add Lulu
pod_package_idvalues to book-sizes.ts