feat: implement export as PDF
I used pdfmake to implement an "export as PDF" feature, and I am happy to share with you!
This should fix #13577, fix #8846, and fix #13959.
A showcase:
Although it might miss rendering some properties currently, it can evolve in the long run and provide a more native experience for the users.
Summary by CodeRabbit
-
New Features
- "Export to PDF" added to the page export menu — generate downloadable PDFs.
-
Improvements
- Richer PDF rendering: titles/headers, lists and numbering, code blocks, callouts, links, inline styles/colors, SVG and raster image handling with better sizing, improved list/checkbox visuals.
- PDF export integrated into linked-document export flow.
-
Tests
- Comprehensive PDF export tests covering paragraphs, headers, lists, code, styles and title handling.
-
Chores
- Added runtime/dev dependencies required for PDF generation.
✏️ Tip: You can customize this high-level summary in your review settings.
[!NOTE]
Other AI code review bot(s) detected
CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.
Walkthrough
Adds a PDF export feature: new PdfAdapter and utilities to build pdfmake document definitions from snapshots, a PdfTransformer to produce downloadable PDF files, UI integration behind a feature flag, and unit tests for doc definition generation.
Changes
| Cohort / File(s) | Summary |
|---|---|
PDF Adapter Core & Utilities blocksuite/affine/shared/src/adapters/pdf/pdf.ts, blocksuite/affine/shared/src/adapters/pdf/utils.ts, blocksuite/affine/shared/src/adapters/pdf/index.ts |
New PdfAdapter class to convert Block/Doc/Slice snapshots into pdfMake docDefinitions and Blobs; utilities for layout/colors and re-export index. |
PDF Text/Image/SVG Helpers blocksuite/affine/shared/src/adapters/pdf/delta-converter.ts, blocksuite/affine/shared/src/adapters/pdf/image-utils.ts, blocksuite/affine/shared/src/adapters/pdf/svg-utils.ts, blocksuite/affine/shared/src/adapters/pdf/css-utils.ts |
New helpers: delta-to-inline-text extraction (styles/links/colors), image dimension extraction and SVG parsing, SVG icon generators for bullets/checkboxes/toggles, and CSS variable resolution. |
Shared Adapter Export blocksuite/affine/shared/src/adapters/index.ts |
Exports PdfAdapter from shared adapters index. |
Transformer & Integration blocksuite/affine/widgets/linked-doc/src/transformers/pdf.ts, blocksuite/affine/widgets/linked-doc/src/transformers/index.ts |
New PdfTransformer with exportDoc that builds snapshot, calls PdfAdapter, and triggers download of produced PDF file. |
Frontend UI & Hook packages/frontend/core/src/components/hooks/affine/use-export-page.ts, packages/frontend/core/src/components/page-list/operation-menu-items/export.tsx |
Adds 'pdf-export' export type, wires PdfTransformer.exportDoc in hook, and conditionally shows "Export to PDF" menu item controlled by feature flag. |
Feature Flags blocksuite/affine/shared/src/services/feature-flag-service.ts, packages/frontend/core/src/modules/feature-flag/constant.ts |
Introduces enable_pdfmake_export flag (default false) in feature-flag definitions and constants. |
Dependencies blocksuite/affine/shared/package.json |
Adds runtime dependency pdfmake@^0.2.20 and dev types @types/pdfmake@^0.2.12. |
Tests blocksuite/affine/all/src/__tests__/adapters/pdf.unit.spec.ts |
Adds unit tests for PdfAdapter.getDocDefinition covering paragraphs, code blocks, lists, headers, titled documents, and styles. |
Utilities & Imports blocksuite/affine/shared/src/utils/index.ts, blocksuite/affine/shared/src/utils/number-prefix.ts, blocksuite/affine/blocks/list/src/utils/get-list-icon.ts |
Re-exported number-prefix; internalized number2roman (no longer exported); adjusted import path for list icon utility. |
| Lint Suppressions & Minor Fixes multiple files (e.g., blocksuite/affine/blocks/table/src/table-cell.ts, various clipboard/Promise usages) |
Added ESLint suppression comments and small error-handling additions (no behavioral changes). |
Sequence Diagram
sequenceDiagram
participant User as User
participant UI as Export Menu
participant Hook as useExportPage Hook
participant Transformer as PdfTransformer
participant Adapter as PdfAdapter
participant PDFLib as pdfmake
participant Download as File Download
User->>UI: Click "Export to PDF"
UI->>Hook: exportHandler('pdf-export')
Hook->>Transformer: PdfTransformer.exportDoc(page)
Transformer->>Transformer: build pipeline (docLinkBaseURL, title, embed)
Transformer->>Transformer: convert page → snapshot
Transformer->>Adapter: fromDocSnapshot(snapshot, assets)
Adapter->>Adapter: _buildContent(blocks) / _blockToContent
Adapter->>Adapter: resolve CSS vars, convert deltas, compute image dims, generate SVGs
Adapter->>PDFLib: createPdf(docDefinition)
PDFLib-->>Adapter: PDF Blob
Adapter->>Transformer: return { blob, fileName }
Transformer->>Download: trigger download(blob, fileName)
Download->>User: PDF saved
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45 minutes
- Pay special attention to:
blocksuite/affine/shared/src/adapters/pdf/pdf.ts— rendering logic for many block types and pdfmake usage.blocksuite/affine/shared/src/adapters/pdf/delta-converter.ts— delta parsing, inline styles, and link/reference handling.- Image/SVG utilities (
image-utils.ts,svg-utils.ts) — dimension extraction and SVG correctness. - Feature-flag gating and UI wiring (
feature-flag-service.ts,export.tsx, hook). - Unit tests (
pdf.unit.spec.ts) for coverage accuracy and realistic snapshots.
Poem
🐇 I hopped through snapshots, tidy and spry,
turning blocks to PDFs with a twinkle in eye.
Lists, code, and images neatly align,
pdfmake hums; the pages look fine.
Hooray — a file to download and try! ✨📄
Pre-merge checks and finishing touches
❌ Failed checks (1 warning, 1 inconclusive)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 73.68% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
| Out of Scope Changes check | ❓ Inconclusive | Most changes are directly related to PDF export implementation. However, multiple unrelated eslint-disable comments added across different files (table-cell.ts, use-user-management.ts, cleanup.ts, etc.) and a number-prefix.ts refactoring appear to be incidental improvements rather than core PDF export requirements. | Clarify whether the eslint-disable comments and number-prefix refactoring are necessary for PDF export or should be separated into a distinct pull request. |
✅ Passed checks (3 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title clearly summarizes the main objective of the changeset: implementing PDF export functionality across the application. |
| Linked Issues check | ✅ Passed | The PR implements PDF export functionality (#13577), addresses missing PDF export option (#8846), and resolves multiple export-related bugs (#13959) by adding PdfAdapter, PdfTransformer, UI integration, and comprehensive support for rendering various block types to PDF. |
✨ Finishing touches
- [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
📜 Recent review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📥 Commits
Reviewing files that changed from the base of the PR and between c5123a4d86a1ac7492485628538c62d3ed31e398 and 9309851208317ab948269945b450e5b8abeace3b.
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (7)
blocksuite/affine/blocks/table/src/table-cell.ts(1 hunks)packages/frontend/admin/src/modules/accounts/components/use-user-management.ts(1 hunks)packages/frontend/core/src/blocksuite/ai/components/playground/content.ts(1 hunks)packages/frontend/core/src/bootstrap/cleanup.ts(1 hunks)packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/mcp-server/setting-panel.tsx(1 hunks)packages/frontend/media-capture-playground/web/components/saved-recording-item.tsx(1 hunks)tests/kit/src/utils/keyboard.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- packages/frontend/admin/src/modules/accounts/components/use-user-management.ts
- packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/mcp-server/setting-panel.tsx
- packages/frontend/media-capture-playground/web/components/saved-recording-item.tsx
- blocksuite/affine/blocks/table/src/table-cell.ts
- tests/kit/src/utils/keyboard.ts
- packages/frontend/core/src/blocksuite/ai/components/playground/content.ts
- packages/frontend/core/src/bootstrap/cleanup.ts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
package.json needs updating
Codecov Report
:x: Patch coverage is 32.43243% with 300 lines in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 55.78%. Comparing base (246e09e) to head (9309851).
:warning: Report is 1 commits behind head on canary.
Additional details and impacted files
@@ Coverage Diff @@
## canary #14057 +/- ##
==========================================
- Coverage 56.94% 55.78% -1.17%
==========================================
Files 2759 2753 -6
Lines 138567 138153 -414
Branches 21283 21277 -6
==========================================
- Hits 78908 77067 -1841
- Misses 57965 59503 +1538
+ Partials 1694 1583 -111
| Flag | Coverage Δ | |
|---|---|---|
| server-test | 77.02% <ø> (-1.19%) |
:arrow_down: |
| unittest | 30.35% <1.57%> (-1.47%) |
:arrow_down: |
Flags with carried forward coverage won't be shown. Click here to find out more.
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
- :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.