feat: add print options to PDF export#2296
Merged
Merged
Conversation
…, margins)
- PdfOptions type (backgroundColor, paperSize, orientation, margins) with
defaults that exactly match today's behaviour (white, A4, portrait, normal)
- buildHtml now accepts backgroundColor and inlines it on <body>
- PdfRenderService.renderHtml accepts optional PdfOptions; maps margins enum
to cm values and toggles landscape mode via puppeteer page.pdf()
- parsePdfOptions helper in ApkgController validates all four fields at the
HTTP boundary: backgroundColor must pass /^#[0-9a-f]{6}$/i or the request
is rejected 400; enum fields default silently when absent/invalid
- PrintForm gains four controls rendered in a responsive grid above the drop
zone; values are appended to FormData before each upload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 task
aalemayhu
added a commit
that referenced
this pull request
May 15, 2026
## What Designer-pass polish on #2296. - Changelog: `Printable PDFs — pick background color, paper size (A4, Letter, Legal), orientation, and margins before you download` → `PDF export — pick paper size (A4, Letter, Legal), orientation, margins, and page color`. - Color swatch: full-width control → 3 rem swatch + muted tabular-nums hex readout (`#FFFFFF`). - Field label: `Background color` → `Page background`. ## Why Three things landed in #2296 that drifted from the existing in-product voice and the rest of the changelog file: 1. The feature is "PDF export" everywhere else in product; "Printable PDFs" reintroduces a second name. 2. `before you download` is filler — the reader of the What's New page already knows when settings apply. 3. A full-width native `<input type="color">` swatch alongside three selects reads visually heavier than the selects and breaks the restrained productivity-tool feel. ## How - One-line edit in `web/src/pages/WhatsNewPage/changelog.ts`. - Color picker wrapped in a flex row: 3 rem `<input type="color">` plus a `<span>` showing the uppercase hex in `--color-text-secondary`, tabular-nums for digit alignment. ## Testing - `pnpm --filter 2anki-web typecheck` — green. - `pnpm --filter 2anki-web lint` — green. - Pure presentational change in a single component + a string; no logic to unit-test. No backend changes. ## Risks None — defaults preserved, FormData wiring unchanged, validation untouched. ## Goal alignment Mission: the print page is cleaner and the changelog reads consistently with the rest of the file. No behaviour change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- codesmith:footer --> --- <a href="https://app.blacksmith.sh/2anki/codesmith/server/pr/2297"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-in-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-in-codesmith-light.svg"><img alt="View in Codesmith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-in-codesmith-dark.svg"></picture></a> <sup>Need help on this PR? Tag <code>@codesmith</code> with what you need.</sup> - [ ] Let Codesmith autofix CI failures and bot reviews <!-- /codesmith:footer --> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What
Adds four user-configurable print options to the existing PDF export flow on the Print Page: background color (native color picker, default white), paper size (A4/Letter/Legal, default A4), orientation (portrait/landscape, default portrait), and margins (narrow 0.5 cm / normal 1 cm / wide 2 cm, default normal).
Controls render inline in a responsive grid above the drop zone. Defaults match today's behaviour exactly, so existing users see no change.
Why
The PDF export flow was useful but inflexible — users printing for binders needed wider margins, users printing in dark-mode needed a non-white background, and users outside A4 regions needed Letter/Legal. This closes the gap without adding clicks for users who don't care.
How
PdfOptionstype +DEFAULT_PDF_OPTIONSexported fromExportApkgToPdfUseCase.ts; theexecutemethod accepts an optional third argument defaulting to those values.buildHtmlnow takesbackgroundColorand inlines it on<body style="background:...">. The value is validated before it reaches this point so no re-escaping is needed.PdfRenderService.renderHtmlaccepts an optionalPdfOptions; maps themarginsenum to cm strings and toggleslandscapemode in puppeteer'spage.pdf()call.parsePdfOptions(body)inApkgController.tsvalidates all four fields at the HTTP boundary:backgroundColormust match/^#[0-9a-f]{6}$/ior the handler returns 400; enum fields silently default when absent/invalid (they cannot produce XSS).PrintForm.tsxgains four controls (<select>× 3,<input type="color">) rendered in a CSS grid above the drop zone; values are appended toFormDatabefore each upload call.Measuring success
Log line: no new instrumentation needed — the existing
console.errorinexportPdfwill surface any failures. Measure via the existing PDF download count metric; an increase after ship confirms uptake. IfparsePdfOptionsrejects a badbackgroundColor, the server returns 400 and the form shows the generic error message.Testing
ExportApkgToPdfUseCase.test.tsbackground:#ffffffin generated HTMLbackgroundColoris reflected in the HTML body stylepdfRenderService.renderHtmlas a second argumentparsePdfOptionsis a pure exported function and can be unit-tested; no new harness was fabricated for the controller path (consistent with existing pattern in this codebase)useState); all 463 web tests still passChangelog
{ type: 'feature', title: 'Printable PDFs — pick background color, paper size (A4, Letter, Legal), orientation, and margins before you download', date: '2026-05-15' }Risks
formatfield: puppeteer'sPDFOptions.formataccepts'A4' | 'Letter' | 'Legal'(and others) as strings matching our enum — confirmed against the puppeteer type definitions. No cast needed.pdfOptionsparameter defaults toDEFAULT_PDF_OPTIONS, which replicates today's hardcoded values exactly. Reverting to the previousexecute(fileBuffer, unlimitedAccess)call signature requires only removing the third argument — no migration, no DB change.backgroundColorvia allowlist regex, enum values viaArray.includes. No taint path to an unsanitized sink.Goal alignment
Makes the print-as-PDF flow more useful (binder margins, low-ink background, non-A4 paper) without adding steps for users who don't configure anything. Defaults are identical to today. Reduces friction for students printing study sheets — directly supports the 300K-user scale goal by improving retention of paying subscribers.
Need help on this PR? Tag
@codesmithwith what you need.