Repository navigation
fix(flash): measure word gaps from each char code's font width - #553
Merged
Merged
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Flash split words such as "infe rence" and "Va riational" that the
reader it was ported from keeps whole. The gap before a char was
measured from the previous glyph's ink box whenever the ink reached
past the glyph's advance, as the tail of a bold-italic 'f' does. The
ligature band then held the pen at that ink edge, so the next letter
looked like a new word.
The original measures from the pen position instead: the font's width
for the char code the content stream draws. The code walk already pairs
each char with that code, so it now also reads the width from the font
dict (/Widths, else /MissingWidth; Identity-H /W, else /DW).
PDFium's GetGlyphWidth looks the width up by unicode and can land on
another glyph's width, e.g. a summation sign drawn with a letter code.
Type 3 fonts keep the box end, because their exact widths split
headings ("Typ es") that the box end keeps whole. Chars the walk can't
pair keep the old end as well.
On 16 documents (3,579 pages), checked against the original's text
items: 4,744 word gaps fixed, 7 worse, all in math or references where
the gap sits exactly on the 0.1 em threshold. A textbook outline gets
its titles back ("Monte Carlo inference"), and a paper's formula no
longer becomes a heading. CPU +4% on a 1,098-page book.
rejojer
force-pushed
the
fix/flash-code-widths
branch
from
October 7, 2026 09:26
c155a32 to
b382695
Compare
Rotated and mirrored text and right-to-left characters go back to the box-end measurement: a rotated pen does not move along +x, and PDFium returns RTL text in logical order, so the code walk pairs each char with its mirror's code. The latest walk now decides a char's width, so a char re-paired with a font that has no widths drops a stale one. A glyph PDFium drops after an overlapping one is re-emitted from the previous glyph's pen end instead of its ink edge, and advances by its own code width when no later glyph gives the advance. A 10-K drawn one glyph per show op read "the ef fects of".
Non-embedded Helvetica is drawn with the system's substitute, so whether PDFium drops the second of two overlapping one-glyph "f" objects differs between macOS and Linux. An "f" painted first at the same spot makes the drop certain on both. The advance of a dropped glyph with no later glyph is checked on _synthesize_dropped_glyphs directly.
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.
Flash split words such as "infe rence" and "Va riational" in page text and in titles, so a chapter heading like "Monte Carlo inference" no longer matched its bookmark.
Cause
The gap before a character was measured from the previous glyph's ink box whenever the ink reached past the glyph's advance, as the tail of a bold-italic "f" does. The ligature rule then held the pen at that ink edge, so the next letter looked like the start of a new word.
Fix
Measure from the pen position, as the extractor Flash was ported from does: the font's width for the character code the content stream draws. The existing code walk already pairs each character with that code, so it now also reads the width from the font dictionary (
/Widths, else/MissingWidth; Identity-H/W, else/DW). PDFium'sGetGlyphWidthlooks widths up by unicode and can land on another glyph's width, for example a summation sign drawn with a letter code.Only text whose pen moves along +x uses the code width: upright, with a positive font size, read left to right.
When PDFium drops a glyph that overlaps the one before it, such as the second "f" of an italic "ff" drawn one glyph per show op, Flash re-emits it. The re-emitted glyph now starts at the previous glyph's pen end rather than its ink edge. When no later glyph gives its advance, it advances by its own code width. Before, a 10-K read "the ef fects of".
Results
Measured on 16 documents (3,579 pages) against the original extractor's text items:
Rotated text in 34 other documents and 5,380 generated lines mixing Hebrew with Latin, punctuation and digits read exactly as before.
Tests
test_ink_past_the_advance_does_not_split_the_word: a narrowfwhose ink overlaps the next letter. It reads "infe rence" before the fix and "inference" after.test_dropped_glyph_after_ink_past_the_advance_does_not_split_the_word: an "f" painted where the word's second "f" lands makes PDFium drop that glyph. Without the fix the word reads "ef fects".test_dropped_glyph_with_no_next_survivor_advances_by_its_code_width: a dropped glyph with no later glyph starts at the previous glyph's pen end and advances by its own code width.test_rotated_text_ignores_code_widths: "O'Brien rock'n'roll" rotated 270°, which code widths would read as "O'B rien rock'n 'r oll".test_rtl_chars_ignore_code_widths: a Latin line with two Hebrew letters keeps its order and gaps.test_composite_font_widths_read_both_w_forms: both/Wforms,/DW, and a bounded range.build_pdfintests/conftest.pyalso accepts a page's raw content stream.