feat(textkit): use best-fit line breaking for long text#3421
Conversation
🦋 Changeset detectedLatest commit: 8fc992e The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 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 |
8459ff7 to
24e5722
Compare
24e5722 to
afa73c4
Compare
There was a problem hiding this comment.
Pull request overview
This PR updates @react-pdf/textkit line breaking to avoid expensive Knuth–Plass optimization for very long, non-justified paragraphs by default, while also exposing a lineBreakStrategy option so callers can explicitly select auto, best-fit, or knuth-plass.
Changes:
- Add
lineBreakStrategyto public typing surfaces (@react-pdf/types,@react-pdf/layout,@react-pdf/renderer, and textkit’sLayoutOptions). - Update textkit’s linebreaker so
autouses best-fit for long ragged text, while keeping Knuth–Plass for justified text. - Add tests and README documentation for the new behavior and option.
Reviewed changes
Copilot reviewed 8 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/textkit/src/engines/linebreaker/index.ts | Adds auto strategy selection logic (best-fit for long non-justified text) and plumbs lineBreakStrategy through the engine. |
| packages/textkit/src/types.ts | Extends LayoutOptions with lineBreakStrategy. |
| packages/textkit/tests/engines/linebreaker.test.ts | Adds coverage intended to validate auto strategy selection for long text and justified text. |
| packages/textkit/README.md | Documents the new auto behavior and the lineBreakStrategy option. |
| packages/layout/src/text/layoutText.ts | Passes lineBreakStrategy from Text node props into textkit layout options. |
| packages/layout/src/svg/layoutText.ts | Passes lineBreakStrategy through for SVG text layout options. |
| packages/layout/src/types/text.ts | Exposes lineBreakStrategy on layout-side TextProps. |
| packages/renderer/index.d.ts | Exposes lineBreakStrategy on renderer-side text props typings/docs. |
| packages/types/node.d.ts | Exposes lineBreakStrategy in shared public TextProps typings. |
| .changeset/tiny-hats-brake.md | Declares patch releases for the affected packages and describes the change. |
Comments suppressed due to low confidence (1)
packages/textkit/src/engines/linebreaker/index.ts:152
- The function-level JSDoc says this engine "Performs Knuth & Plass" with fallback to best-fit, but the new
shouldUseBestFitpath can select best-fit as the primary algorithm (skipping Knuth‑Plass entirely). Updating this comment would prevent confusion for future maintainers.
/**
* Performs Knuth & Plass line breaking algorithm
* Fallbacks to best fit algorithm if latter not successful
*
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This changes textkit's default
autoline-breaking behavior for long non-justified paragraphs: short/justified text continues to use Knuth-Plass, while long ragged text uses the existing best-fit breaker to avoid expensive global optimization. It also exposeslineBreakStrategyso callers can forceauto,best-fit, orknuth-plass.Why: in a downstream PDF generator, customer-editable body copy can contain very long paragraphs. Profiling showed the hot path was textkit's Knuth-Plass active-node search. For a 50k-character paragraph, the linebreaker built ~20,650 syllables and ~30,431 nodes; node creation took ~6ms, but Knuth-Plass took ~769ms and the heap jumped to ~277MB.
Measured downstream with an equivalent best-fit shortcut for long non-justified text:
Together with #3420, this improves performance in the same downstream PDF generator by:
The generated PDF size was essentially unchanged (~180KB), so the win comes from avoiding intermediate layout work rather than output compression.