Period after a separate text run can start the next line and be hyphenated
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 72/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- react, typescript
Research direction
Start in packages/textkit/src/engines/linebreaker/index.ts and trace how getNodes creates penalties between syllables and how breakLines handles them. Check the existing line breaker tests for coverage of run boundaries and punctuation. Done means a period after a separate text run stays with the preceding word without changing breaks for words that begin with periods after spaces.
Written by the indexing model from the issue text.
Description
Description
A period that follows a nested text run, such as a Link, can be placed alone at the start of the next line. The previous line can also gain a trailing hyphen. This still happens when hyphenation is disabled with Font.registerHyphenationCallback((word) => [word]).
Reproduction
import { Document, Font, Link, Page, Text } from '@react-pdf/renderer';
Font.registerHyphenationCallback((word) => [word]);
const doc = (
<Document>
<Page
size={{ width: 60, height: 200 }}
style={{ fontFamily: 'Helvetica', fontSize: 11, padding: 12 }}
>
<Text>
<Link src="#appendix">
<Text>Appendix</Text>
</Link>
.
</Text>
</Page>
</Document>
);
At this width, Appendix fits and Appendix. does not. The rendered lines are Appendix- and .. Slightly narrower widths omit the hyphen and still leave . on its own line. On a normal paragraph the same break happens when the last word of a link fits and the following period does not, for example See <Link><Text>Appendix F</Text></Link>.
Cause
wrapWords splits each run on spaces and then concatenates the syllables. Link text and the following . are different runs, so the syllables are Appendix and . with no space syllable between them.
In packages/textkit/src/engines/linebreaker/index.ts, getNodes treats every next syllable that is not a single space as a hyphenation point:
const hyphenated = syllables[index + 1] !== ' ';
// ...
if (syllables[index + 1] && hyphenated) {
acc.push(knuthPlass.penalty(hyphenWidth, hyphenPenalty, 1));
}
breakLines then inserts a hyphen glyph whenever it breaks on that penalty. The hyphenation callback never sees both runs, so returning [word] does not disable this break. bestFit can also split in front of the period box when the word fits and the period does not.
This is still present on master.
Suggested fix
Do not treat a boundary between runs as a hyphenation point. A penalty (and a hyphen glyph) should be inserted only where the hyphenation callback asked for a break, such as a soft hyphen inside one word.
Joining a leading . into the previous word box removes the bad break for Appendix + .. Do not join it into the preceding space glue. A word that already starts with a period after a space (.25, ..., .pdf) must stay its own box. Extending that glue draws the word past the line width, because a break at the glue slices through node.end while the glue width is not counted on the line it closes.
What I already checked
Existing hyphenation discussions (#3018, #3188, #3267) cover soft hyphens and breaking on a hyphen character inside a word. I did not find an issue for a punctuation character in the next run being hyphenated onto its own line.
- Dominant language
- TypeScript
- Stars
- 16.9k
- Forks
- 1.3k
- Avg merge
- 11m
- Merged PRs (30d)
- 2
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from diegomura/react-pdf
-
@react-pdf/font: published types import fontkit, but @types/fontkit is only a devDependencyPossibly taken @iliev-blnk claimed this 17 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 1 day
-
Unable to set flexShrink to 0Possibly taken @adarshsm claimed this 16 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
Maintainers usually reply within 1 day
-
@react-pdf/hyphenate: import-only exports map throws ERR_PACKAGE_PATH_NOT_EXPORTED in require-condition resolvers (every tsx script loading renderer >= 4.7 crashes)Possibly taken @cruzdanilo claimed this 45 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
diegomura/react-pdf#3551 · 2 reactions ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 52/100
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 72/100
Maintainers usually reply within 1 day
All issues in diegomura/react-pdf
Similar issues
-
area:cards enhancement ready-for-agent
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
fil-donadoni/tolaria#8883 ·
Maintainers usually reply within 1 day
-
xsd:integer cast from a string rejects negative numbers, a leading + and surrounding whitespaceOpenbug 🐛
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
comunica/comunica#1960 · 1 comment ·
Maintainers usually reply within 1 day
-
Waiting for: Product Owner
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
getsentry/sentry-javascript#26203 · 1 comment ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
bytedance/flowgram.ai#1235 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
devswha/herdr-web-ui#3325 ·