Skip to content

fix(pages): resolve RTL text shaping and layout in PDF export#9187

Open
danialshirali16 wants to merge 3 commits into
makeplane:previewfrom
danialshirali16:claude/zealous-carson-Zogxm
Open

fix(pages): resolve RTL text shaping and layout in PDF export#9187
danialshirali16 wants to merge 3 commits into
makeplane:previewfrom
danialshirali16:claude/zealous-carson-Zogxm

Conversation

@danialshirali16
Copy link
Copy Markdown

@danialshirali16 danialshirali16 commented Jun 1, 2026

Description

Pages' PDF export was broken for RTL languages (Persian, Arabic, Hebrew): exported text appeared as disconnected, isolated glyphs flowing left-to-right — unreadable and unusable.

Root cause (three compounding issues):

  1. Wrong font — only Inter Latin subsets were registered. Inter Latin carries no Arabic/Persian Unicode code points and no OpenType GSUB shaping tables. Without GSUB tables the renderer cannot connect letters into their contextual forms (initial/medial/final/isolated), so every character renders isolated.
  2. dir attribute ignored — TipTap sets dir="rtl" on paragraph and heading nodes for RTL content, but the PDF node renderers never read it. Layout always defaulted to LTR.
  3. No default alignment — RTL paragraphs with no explicit textAlign were left-aligned instead of right-aligned.

Fix:

  • plane-pdf-exporter.tsx — registers Vazirmatn (a permissively-licensed Persian/Arabic font with full GSUB/GPOS shaping tables) alongside Inter. Path is resolved relative to import.meta.url so it stays stable regardless of CWD.
  • node-renderers.tsxparagraph and heading renderers now read node.attrs.dir; when dir === "rtl", fontFamily: "Vazirmatn" is applied and textAlign defaults to "right" (explicit alignment by the user is always respected).
  • document.tsx — mirrors the same Vazirmatn Font.register for the frontend client-side PDF path.
  • editor.ts — adds a [dir='rtl'] rule to the HTML→PDF stylesheet used by react-pdf-html.

LTR content is completely unchanged — Inter is still used for all non-RTL text.

Self-hosted setup note: Vazirmatn .woff files must be placed at apps/live/fonts/vazirmatn/ and .ttf files at apps/web/app/assets/fonts/vazirmatn/ before building. Download from the Vazirmatn releases page.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Test Scenarios

  1. Write a Persian paragraph in a Page → export to PDF → verify letters are connected and text flows right-to-left.
  2. Mix Persian headings (H1–H3) with English body text → export → Persian headings right-aligned with Vazirmatn, English paragraphs left-aligned with Inter.
  3. Persian paragraph with explicit textAlign: center → export → center alignment preserved, not overridden to right.
  4. Export a purely English page → no visual regression, Inter used throughout.

References

Fixes #8922

Register Vazirmatn as a second font family for RTL content.
In the backend node renderers, read the TipTap `dir` attribute on
paragraph and heading nodes; when `dir === "rtl"`, apply
fontFamily "Vazirmatn" and default textAlign to "right" so that
Persian/Arabic characters are shaped correctly and flow right-to-left.
Mirror the same Vazirmatn Font.register and a `[dir='rtl']` stylesheet
rule in the frontend HTML-based export path.

LTR (Inter) behaviour is completely unchanged.

https://claude.ai/code/session_01BxEgURqex1hukdwboM3XF6
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Jun 1, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds RTL PDF support: Vazirmatn font registration (web + live), an editor RTL stylesheet rule, and RTL-aware paragraph and heading renderers that default to right alignment when dir="rtl".

Changes

RTL PDF Export with Vazirmatn Font Support

Layer / File(s) Summary
Font registration and imports
apps/web/core/components/editor/pdf/document.tsx, apps/live/src/lib/pdf/plane-pdf-exporter.tsx
Vazirmatn font family is registered in both the web document component and the live PDF exporter with regular and bold weights; fileURLToPath is used to resolve runtime paths in the live exporter.
RTL-aware text rendering
apps/live/src/lib/pdf/node-renderers.tsx
Adds getRtlStyle and updates paragraph and heading renderers to read dir, compute effectiveTextAlign (default right when RTL has no explicit alignment), apply alignment/flex styles, preserve background color logic, and include Vazirmatn font styling.
RTL styling configuration
apps/web/core/constants/editor.ts
Adds a [dir='rtl'] rule to EDITOR_PDF_FONT_FAMILY_STYLES to set fontFamily: Vazirmatn and textAlign: right for RTL PDFs.

Sequence Diagram

sequenceDiagram
  participant NodeRenderer as Paragraph/Heading Renderer
  participant RtlHelper as getRtlStyle Helper
  participant PdfOutput as PDF Text Styling
  NodeRenderer->>NodeRenderer: Read dir attribute
  NodeRenderer->>NodeRenderer: Compute effectiveTextAlign (default right for RTL)
  NodeRenderer->>RtlHelper: Get RTL font style for dir="rtl"
  RtlHelper-->>NodeRenderer: Return Vazirmatn or null
  NodeRenderer->>PdfOutput: Apply alignment, flex, background color, RTL font
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hop through fonts with eager cheer,
I find Vazirmatn when RTL is near,
Lines turn right and headings bow,
PDFs hum in Persian now,
A little rabbit signs with a smile.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main change: fixing RTL text shaping and layout in PDF export, which is the primary focus of all code changes.
Linked Issues check ✅ Passed All code changes directly address the requirements from issue #8922: registering Vazirmatn font for proper RTL glyph shaping, detecting RTL direction, and applying right-aligned text layout for RTL content.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing RTL text rendering in PDF exports. No unrelated modifications or scope creep are present in the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering all required template sections with detailed technical context, test scenarios, and clear implementation rationale.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/live/src/lib/pdf/plane-pdf-exporter.tsx`:
- Line 56: The vazirmatnFontDir constant uses process.cwd() which is brittle in
monorepos/runtimes; update vazirmatnFontDir so it resolves the fonts directory
relative to the source file rather than the current working directory (e.g.,
derive the base path from __dirname or from import.meta.url and then
path.resolve that base with "fonts/vazirmatn"), replacing the current definition
of vazirmatnFontDir in plane-pdf-exporter.tsx to ensure font lookup is stable
across environments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 86d27924-7f45-480c-894f-76148edaf127

📥 Commits

Reviewing files that changed from the base of the PR and between 3f57fef and eb15869.

📒 Files selected for processing (4)
  • apps/live/src/lib/pdf/node-renderers.tsx
  • apps/live/src/lib/pdf/plane-pdf-exporter.tsx
  • apps/web/core/components/editor/pdf/document.tsx
  • apps/web/core/constants/editor.ts

Comment thread apps/live/src/lib/pdf/plane-pdf-exporter.tsx Outdated
process.cwd() is fragile in monorepo/runtime environments. tsdown
bundles everything into dist/start.js, so resolving relative to
import.meta.url (one level up from dist/) gives a stable path to
apps/live/fonts/vazirmatn/ regardless of where the process is started.

Addresses CodeRabbit review on makeplane#9187.

https://claude.ai/code/session_01BxEgURqex1hukdwboM3XF6
danialshirali16 pushed a commit to danialshirali16/plane that referenced this pull request Jun 1, 2026
process.cwd() is fragile in monorepo/runtime environments. tsdown
bundles everything into dist/start.js, so resolving relative to
import.meta.url (one level up from dist/) gives a stable path to
apps/live/fonts/vazirmatn/ regardless of where the process is started.

Addresses CodeRabbit review on makeplane#9187.

https://claude.ai/code/session_01BxEgURqex1hukdwboM3XF6
…ont weights

- Inline `dir === "rtl"` directly into effectiveTextAlign in paragraph and
  heading renderers — isRtl was only used for that one expression
- Change Vazirmatn fontWeight registration from numeric (400/700) to string
  ("normal"/"bold") to match the weight strings mark-renderers.ts uses at
  render time, avoiding a potential font-lookup mismatch

https://claude.ai/code/session_01BxEgURqex1hukdwboM3XF6
@danialshirali16
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@danialshirali16
Copy link
Copy Markdown
Author

@cla-assistant recheck

@danialshirali16 danialshirali16 force-pushed the claude/zealous-carson-Zogxm branch from 036f737 to 2042583 Compare June 1, 2026 09:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug]: Problem with RTL content export in Pages

2 participants