AFFiNE icon indicating copy to clipboard operation
AFFiNE copied to clipboard

feat: implement export as PDF

Open UNIDY2002 opened this issue 3 weeks ago • 3 comments

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:

Getting Started.pdf

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.

UNIDY2002 avatar Dec 07 '25 05:12 UNIDY2002

[!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.lock is 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot] avatar Dec 07 '25 05:12 coderabbitai[bot]

package.json needs updating

darkskygit avatar Dec 07 '25 17:12 darkskygit

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.

Files with missing lines Patch % Lines
blocksuite/affine/shared/src/adapters/pdf/pdf.ts 41.75% 159 Missing :warning:
.../affine/shared/src/adapters/pdf/delta-converter.ts 34.78% 39 Missing and 6 partials :warning:
...uite/affine/shared/src/adapters/pdf/image-utils.ts 0.00% 45 Missing :warning:
...ksuite/affine/shared/src/adapters/pdf/css-utils.ts 0.00% 15 Missing :warning:
blocksuite/affine/shared/src/adapters/pdf/utils.ts 26.31% 14 Missing :warning:
...ksuite/affine/shared/src/adapters/pdf/svg-utils.ts 0.00% 10 Missing :warning:
.../affine/widgets/linked-doc/src/transformers/pdf.ts 11.11% 8 Missing :warning:
...locksuite/affine/shared/src/utils/number-prefix.ts 0.00% 4 Missing :warning:
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.

codecov[bot] avatar Dec 08 '25 04:12 codecov[bot]