Feature/cjk vertical pdf renderer#3368
Open
Nando-Cardoso wants to merge 14 commits intodiegomura:masterfrom
Open
Feature/cjk vertical pdf renderer#3368Nando-Cardoso wants to merge 14 commits intodiegomura:masterfrom
Nando-Cardoso wants to merge 14 commits intodiegomura:masterfrom
Conversation
- Add WritingMode type ('horizontal-tb' | 'vertical-rl' | 'vertical-lr')
- Add BuiltInFontFamily type for IntelliSense autocomplete of built-in fonts
- Add FontFamily type using (string & {}) pattern for custom + built-in fonts
- Add writingMode to TextStyle and textkit Attributes types
- Add writingMode handler in stylesheet text resolver
- Add tests for writingMode style resolution
- Add CJK constant with CHINESE_SIMPLIFIED, CHINESE_TRADITIONAL, JAPANESE, KOREAN - Add CJK_FONT_FAMILIES as deprecated alias for backward compatibility - Add CJK_FONT_NAMES array of all CJK font family names - Register CJK fonts (Noto Sans SC/TC/JP/KR) in FontStore constructor - Fonts are lazily loaded from Google Fonts CDN with normal and bold weights
- Add vertical writing mode support in text layout engine - Swap container width/height for vertical text (characters flow top-to-bottom) - Use 999999 instead of Infinity to avoid NaN from IEEE 754 arithmetic - Transform horizontal line boxes back to vertical column boxes - Compute column height using per-glyph Math.max(xAdvance, fontSize) - Update linesHeight/linesWidth for vertical mode dimension semantics - Update measureText to handle vertical text measurement correctly - Add CJK codepoint detection utilities (containsCJK, getCJKFallbackFontFamilies) - Add CJK fonts as fallbacks in attributed string font resolution - Auto-detect and preload CJK fonts when CJK characters found in text - Add tests for vertical layout, linesHeight, and linesWidth
- Pass contentArea to splitText (was previously only passed to splitView) - Force at least one line onto current page when first line exceeds contentArea, preventing endless push-to-next-page loop - Return [current, null] when no lines remain for next page, stopping pagination when all text content has been consumed
- Add renderGlyphsVertical for top-to-bottom glyph positioning - Center glyphs horizontally within column using advanceWidth - Use Math.max(xAdvance, fontSize) per glyph to prevent overlap - Return total advance for inter-run CTM advancement - Update renderRun to handle vertical mode with CTM translation - Update renderLine to position at column (x, y) without lineAscent offset - Update renderText to detect writingMode and adjust initial translation - Support vertical background rendering for text runs
- Export CJK and CJK_FONT_FAMILIES from renderer package - Add TypeScript declarations for CJK constant with JSDoc - Add deprecated CJK_FONT_FAMILIES alias in type declarations
🦋 Changeset detectedLatest commit: dd510d6 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Pull request overview
Adds vertical writing mode support for <Text> (writingMode: horizontal-tb | vertical-rl | vertical-lr) and introduces built-in CJK Noto Sans font families that are auto-registered and can be auto-loaded when CJK text is detected.
Changes:
- Extend style/type systems to support
writingModeand built-in font-family IntelliSense. - Implement vertical text layout + measurement adjustments (line/column transforms, pagination safeguards).
- Add CJK font constants/registration and CJK detection + preloading during asset resolution; update rendering to support vertical glyph placement.
Reviewed changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/textkit/src/types.ts | Adds writingMode to textkit Attributes. |
| packages/stylesheet/tests/text.test.ts | Adds tests for writingMode style resolution. |
| packages/stylesheet/src/types.ts | Introduces WritingMode, built-in font family unions, and updates TextStyle.fontFamily typing. |
| packages/stylesheet/src/resolve/text.ts | Adds writingMode handler in text style resolver. |
| packages/renderer/src/index.js | Re-exports CJK and deprecated alias from renderer entry. |
| packages/renderer/index.d.ts | Adds TS declarations/JSDoc for CJK and CJK_FONT_FAMILIES. |
| packages/render/src/primitives/renderText.ts | Adds vertical glyph rendering path and vertical-aware line/run rendering hooks. |
| packages/layout/tests/text/linesWidth.test.ts | Adds tests for linesWidth behavior in vertical vs horizontal modes. |
| packages/layout/tests/text/linesHeight.test.ts | Adds tests for linesHeight behavior in vertical vs horizontal modes. |
| packages/layout/tests/text/layoutText.test.ts | Adds basic tests for vertical layout behavior and empty text. |
| packages/layout/src/text/splitText.ts | Adds contentArea parameter and safeguards against infinite pagination loops. |
| packages/layout/src/text/measureText.ts | Adds vertical-writing measure semantics for Yoga. |
| packages/layout/src/text/linesWidth.ts | Updates width calculation semantics for vertical columns. |
| packages/layout/src/text/linesHeight.ts | Updates height calculation semantics for vertical columns. |
| packages/layout/src/text/layoutText.ts | Swaps container dimensions for vertical layout and transforms line boxes into vertical columns. |
| packages/layout/src/text/getAttributedString.ts | Adds CJK fallback font families and passes writingMode down to textkit attributes. |
| packages/layout/src/text/cjk.ts | Adds CJK codepoint detection + fallback font family accessor. |
| packages/layout/src/steps/resolvePagination.ts | Passes contentArea into splitText for pagination safety. |
| packages/layout/src/steps/resolveAssets.ts | Detects CJK text content and preloads built-in CJK fonts. |
| packages/font/src/index.ts | Defines/exports CJK, registers CJK fonts (lazy-loaded from Google Fonts), and exports names list. |
| .changeset/vertical-text-cjk.md | Changeset documenting vertical writing mode + built-in CJK fonts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add 'Vertical Text' example tab with vertical-rl, vertical-lr, and auto-detected CJK demonstrations across Korean, Japanese, and Chinese - Add CJK font examples to 'Font Family Fallback' tab showing built-in CJK fonts, auto-detection, and mixed-script fallback chains
…yphsVertical Position offsets (xOffset, yOffset) are in font units (1000-em), not points. Apply the same scale factor used by horizontal renderGlyphs so kerning and ligature offsets are correctly converted to points in vertical mode.
…olumn Background was painted as (0, 0, column.width, column.height) for every run, overpainting subsequent runs and blank space. Now each run's background height is the sum of Math.max(pos.xAdvance, fontSize) for its glyphs — exactly the vertical space that run occupies before the CTM advances.
…cal-rl columnWidth was read once from lines[0].box.height and reused for all columns when computing the x position in vertical-rl mode. Mixed font sizes or line heights produce columns of different widths, causing incorrect x placement for all columns after the first. Now each column reads its own box.height.
…t user fonts The blanket try/catch in getAttributedString silently swallowed errors for all font families including user-specified ones, masking registration issues and silently falling back to Helvetica. Now only the internally-injected CJK fallback families (which may not be loaded yet) are wrapped in try/catch. User-specified fontFamily values preserve the original throwing behavior, consistent with how svg/layoutText.ts handles font resolution.
rewrite comments on vertical-mode to reflect current vertical width semantics. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…al mode Link hit-boxes were using horizontal run metrics (0, -height-descent, xAdvance, height) even in vertical mode, so annotations would not align with rendered text. In vertical mode the run occupies (0, 0, columnWidth, runAdvance) relative to the current CTM — x:0 is the column left edge, y:0 is the top of the run before the CTM translate, width is fontSize (one em = column width), and height is the pre-computed vertical advance from calcVerticalRunAdvance.
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.
feat: add vertical writing mode (
writingMode) and built-in CJK font supportSummary
This PR adds vertical text layout support (
writingMode: 'vertical-rl' | 'vertical-lr') and built-in CJK (Chinese, Japanese, Korean) font families that are auto-registered and lazily loaded from Google Fonts.Motivation
CJK typography traditionally uses vertical writing (top-to-bottom, columns right-to-left). Users currently have no way to render vertical text in react-pdf, and using CJK fonts requires manual
Font.register()with external URLs. This PR addresses both needs as a single cohesive feature.Screenshots
vertical-rlvertical-lr& auto-detectionUsage
Vertical Text
Supported values:
horizontal-tb(default) — standard left-to-right, top-to-bottomvertical-rl— top-to-bottom, columns from right to leftvertical-lr— top-to-bottom, columns from left to rightBuilt-in CJK Fonts
Available:
CJK.CHINESE_SIMPLIFIED,CJK.CHINESE_TRADITIONAL,CJK.JAPANESE,CJK.KOREAN.Font IntelliSense
fontFamilynow provides autocomplete for all built-in fonts (Helvetica, Courier, Times-Roman, and CJK Noto Sans families) while still accepting custom registered font names via the(string & {})pattern.Changes by Package
@react-pdf/stylesheetWritingModetype andwritingModeproperty toTextStyleBuiltInFontFamily/FontFamilytypes for IntelliSense autocompletewritingModehandler in style resolver@react-pdf/textkitwritingModetoAttributestype@react-pdf/fontCJKconstant with font family namesCJK_FONT_NAMESarray andCJK_FONT_SOURCESwith Google Fonts URLsFontStoreconstructor (lazy-loaded)CJK,CJK_FONT_FAMILIES(deprecated alias),CJK_FONT_NAMES@react-pdf/layoutverticalColumnHeight()using per-glyphMath.max(xAdvance, fontSize)999999instead ofInfinityto preventNaNfrom IEEE 754 arithmeticlinesHeight/linesWidth/measureTextfor vertical dimension semanticscontainsCJK,getCJKFallbackFontFamilies)contentAreatosplitTextand force forward progress for oversized lineslinesHeight,linesWidth@react-pdf/renderrenderGlyphsVertical()for top-to-bottom glyph positioning with centeringrenderRun,renderLine,renderTextfor vertical mode@react-pdf/rendererCJKandCJK_FONT_FAMILIESfrom the main entry pointTests
Commits
feat(stylesheet): add writingMode style property and built-in font typesfeat(font): add built-in CJK font families with auto-registrationfeat(layout): add vertical text layout with CJK auto-detectionfix(layout): prevent infinite pagination loop with oversized textfeat(render): add vertical text renderingfeat(renderer): export CJK font families APIchore: add changeset for vertical writing mode and CJK fonts