Repository navigation
Arabic joins in the SVG preview: paint the shaped glyphs, not their characters - #293
Merged
Merged
Conversation
…start in a subroutine
A bare CFF program (/FontFile3 /CIDFontType0C) has no hmtx, so
wrap_bare_cff_as_sfnt synthesizes one from each charstring's optional
leading width operand, decided at the first stack-clearing operator.
cff_charstring_width_delta returned None both for "that operator had no
width operand" and for "the subroutine returned without one", so when the
first hint operator sat in a subroutine and had an even operand count, the
caller kept scanning past it and read the next hint group's count instead.
NotoSansJP's 語 starts with 21 stem operands and a callsubr; the subroutine
pushes one more and runs hstemhm (22, no width: defaultWidthX 1000). The
scanner went on to the next cntrmask, found an odd count and took the first
stem coordinate (-30) as the width: advance 0, so everything that pens
through a parsed line with these widths drew the rest of it on top of 語.
The scan now returns WidthScan::{Decided(Option<f64>), Returned}: only a
subroutine `return` lets the caller continue. A subroutine that cannot be
followed (no local subrs, bad index) falls back to defaultWidthX instead of
continuing in the caller.
tests/parsed_cff_widths.rs: from_html with NotoSansJP, save, parse; every
glyph of 日本語のテキスト keeps advance 1000 (語 was 0).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Arabic joins) The PDF from from_html was always shaped correctly: azul's text3 picks the contextual init/medi/fina forms and the lam-alef ligature, lays the run out right to left, and the writer puts exactly those glyph ids into the content stream with a matching ToUnicode. The SVG preview (PdfPage::to_svg, the web demo's page view) threw that away: render_text_items_to_svg wrote each TextItem::GlyphIds glyph back out as its Unicode text, one <text> per glyph, and the browser shaped every character again on its own. A lone letter has nothing to join to, so the demo showed the isolated form of every Arabic letter at the place of its contextual form; ligatures and Indic conjuncts fell apart the same way, and a parsed PDF's merged visual-order TJ run was bidi-reversed a second time. Glyph-id runs of an external font are now painted from the glyph outlines: one <path> per (font, glyph) in a <defs> at the end of the page, one <use> per glyph, the pen advanced through the run like a PDF viewer does (the glyph's /W width, which merged runs are kerned against, plus Tc and the TJ adjustment). The <text> is still written, with fill-opacity 0, so the preview stays selectable and searchable. TextItem::Text, the builtin fonts and faces without outlines are a painted <text> as before. serialize::get_scaled_glyph_width is pub(crate) for the /W width. tests/arabic_shaping.rs, with a 5 KB Vazirmatn subset (OFL, no Reserved Font Name) whose shaping is pinned to HarfBuzz's: - from_html gives HarfBuzz's 15 glyph ids left to right, no isolated cmap glyph, one lam-alef ligature, the first letter rightmost; - the content stream carries those ids (subset off) and ToUnicode maps the codes back to the text (subset on and off); - the preview paints exactly those ids at the PDF's positions, and no painted <text> holds Arabic (failed before: the browser drew the text); - the preview of the saved-and-parsed PDF paints the same outlines at the same positions (failed before). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Arabic in the SVG preview (
PdfPage::to_svg, the web demo's page view) showed every letter in its isolated form. The PDFs themselves were always shaped correctly. Two commits.What
render_text_items_to_svgwrote eachTextItem::GlyphIdsglyph back out as its Unicode character, one<text>per glyph, and the browser shaped every character again on its own. A lone letter can't join, so contextual forms, lam-alef and Indic conjuncts fell apart. A parsed PDF's visual-order TJ run was also bidi-reversed a second time. Glyph-id runs of an external font are now drawn from the glyph outlines: one<path>per (font, glyph) in<defs>, one<use>per glyph, advanced with the PDF's own/Wwidths, Tc and TJ adjustments. The<text>stays, withfill-opacity="0", so the preview is still selectable and searchable.TextItem::Text, the builtin fonts and faces without outlines render as before.cff_charstring_width_deltareturned 0 for a glyph whose hints start in a subroutine (語 in NotoSansJP), so the rest of a parsed Japanese line was drawn on top of it. The new preview depends on correct widths, which is how this showed up.Tests
tests/arabic_shaping.rs, with a 5 KB Vazirmatn subset (OFL, no Reserved Font Name), pinned to HarfBuzz's output for "مرحبا بالعالم لا":tests/parsed_cff_widths.rsfailed before the font fix.cargo test: 218 passed.--no-default-features: 110 passed. Thetext_layout-only check,--examplesand both wasm32 checks pass.Checked by eye
Persian and Urdu (Naskh), Hebrew, Hindi and Thai: the preview now matches the PDF.
Not fixed here (azul-layout, reported there)
🤖 Generated with Claude Code