Skip to content

Read right-to-left /ActualText spans in reading order - #97

Merged
bobsingor merged 1 commit into
embedpdf/mainfrom
embedpdf/fix/arabic-actualtext-order
Nov 1, 2026
Merged

bobsingor merged 1 commit into
embedpdf/mainfrom
embedpdf/fix/arabic-actualtext-order

Conversation

@bobsingor

Copy link
Copy Markdown
Contributor

Upstream's ArabicActualTextOrdering (64df52afe) writes بِسْمِ as one /ActualText span per letter, in four orders. The fork read none of them as typed. Two causes:

  • CloseTempLine() passed a right-to-left segment that starts with a span forward whole, so spans drawn in visual order came out in visual order. A segment now reverses unit by unit: a span is one unit, kept as written, and every other character is its own. No span is reversed ahead of time any more (this replaces S8's FollowsLiteralRightToLeftText).
  • A mark drawn above or below its letter (harakat) started a new line. A line's reference glyph is now its last one that isn't a mark, and a mark joins a line within a font size of it (DrawsOnlyMarks()).

Results

  • Upstream's test: all four variants read بِسْمِ; the test expects that on every page.
  • Chrome-made PDFs: Chrome wraps each Arabic glyph in a one-letter /ActualText and draws them in visual order. Every Arabic line of our Proton (Chrome) fixture now reads as typed; before, whole words came out reversed (ابحرم for مرحبا).
  • Acrobat and Pages fixtures: identical output to main.
  • Still wrong, unchanged in kind: a bracket inside /ActualText is mirrored once too often (the pairs are now in place); marks drawn without /ActualText still break lines.

Tests (darwin-arm64)

  • Unit tests: 1315 pass.
  • Embedder tests: the same 12 Mac PNG pixel failures as S15; ActualTextRtl, ActualTextReadsAsWritten and the rich-text RTL round trips pass unchanged.
  • Layer mode: exit 0, same numbers as S15.
  • Tag check: 0 untagged. Globals: ok.

Upstream's ArabicActualTextOrdering (64df52a) writes بِسْمِ as one
/ActualText span per letter, in four orders; the fork read none of them as
typed. Two causes:

- CloseTempLine() passed a right-to-left segment that starts with a span
  forward whole, so spans drawn in visual order came out in visual order.
  A segment now reverses unit by unit: a span is one unit, kept as
  written, and every other character is its own. No span is reversed
  ahead of time any more (this replaces S8's FollowsLiteralRightToLeftText).
- A mark drawn above or below its letter started a new line. A line's
  reference glyph is now its last one that isn't a mark, and a mark joins
  a line within a font size of it.

All four variants read بِسْمِ. Chrome wraps each Arabic glyph in a
one-letter span, so in its output every Arabic line now reads as typed;
Acrobat's and Pages' output is unchanged.
@bobsingor
bobsingor merged commit 06ca752 into embedpdf/main Nov 1, 2026
2 of 4 checks passed
@bobsingor
bobsingor deleted the embedpdf/fix/arabic-actualtext-order branch November 1, 2026 15:53
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.

1 participant