Period after a separate text run can start the next line and be hyphenated

Open
#3,619 0 comments 0 reactions 0 assignees View on GitHub

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
Domain
backend, devtools

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

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from diegomura/react-pdf

All issues in diegomura/react-pdf

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.