Skip to content

Implement Makie 0.25's layout_text protocol - #34

Open
dawbarton-ai wants to merge 1 commit into
mainfrom
feat/makie-layout-text-interface
Open

Implement Makie 0.25's layout_text protocol#34
dawbarton-ai wants to merge 1 commit into
mainfrom
feat/makie-layout-text-interface

Conversation

@dawbarton-ai

Copy link
Copy Markdown
Collaborator

Implements the public Makie.layout_text(handler, source, attributes) -> Makie.TextLayout protocol from MakieOrg/Makie.jl#5717, so Makie 0.25 users select TeXLayout explicitly instead of relying on the type piracy the legacy adapter needs.

with_theme(text_handler = TeXLayoutHandler()) do
    fig = Figure()
    Axis(fig[1, 1]; xlabel = L"x", ylabel = L"f(x)")
    fig
end

TeXLayoutHandler emits glyph IDs, exact FreeType faces, origins, extents, scales, block bounds, and LineSegments specs for horizontal and vertical rules. Makie keeps ownership of alignment, rotation, offset, validation, placement, and batching, so moving or rotating a label no longer re-runs layout. The handler claims only LaTeXString; plain strings and rich text fall through to Makie. A font family and LayoutOptions can be pinned per handler or left to resolve the session defaults.

Makie 0.24 keeps working through MathTeXEngineExt. Once the handler interface is present, its generate_tex_elements(::LaTeXString) specialisation delegates to MathTeXEngine's own implementation, so merely loading TeXLayout no longer changes Makie's default renderer.

Glyph and block metrics

Metrics follow Makie's contract rather than TeXLayout's ink bounds. Makie turns every GlyphExtent into (0, descender)(hadvance, ascender) and expects the font's line metrics there, so a block's height does not depend on which characters it holds. MathTeXEngine.TeXChar pads the same way, which is why the legacy path never showed this.

Reporting bare ink bounds made L"$x$" measure 9.06 px against Makie's 20.0 px at fontsize 20, scattered baselines under align = (:*, :center), and made Axis tick-label space depend on whether a label happened to have a descender. Extents are now padded to max(ink_top, font_ascender) / min(ink_bottom, font_descender), and the block bbox is the union of the placed padded glyph boxes and rule bounds — exactly what raw_string_boundingboxes unions downstream, so align and measurement cannot disagree.

Structure

Source routing and glyph resolution moved into src/render_support.jl, so both adapters share one implementation and one per-FontFamily runtime cache instead of carrying ~120 duplicated lines each. FontFamilyKey names the cache-key type so _font_family_key's arity is stated once.

Makie joins [extras]/[targets]: the handler tests were silently skipped because nothing in CI loaded MakieExt at all. Until Makie 0.25 is released only the 0.24 branch runs there, so handler changes still need a manual run against a PR checkout.

Also fixes inline-math delimiter stripping to use valid UTF-8 indices, so $α$ no longer raises a StringIndexError, and corrects a docs example that passed text_handler to Label, which rejects it.

Verification

  • Pkg.test(): 1571/1571 with Makie 0.24
  • Makie PR head 727b340b: 11/11 handler assertions, 9/9 routing
  • CairoMakie render pass covering fractions, radicals, mixed text/math, plain-string fall-through, and matrix rules
  • Runic clean; Documenter builds clean

Stress-suite makie_cairo references still need regenerating, since changing alignment is the point of the metric fix.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BYXUGdHB9mLnZemWvtkZo1

Adds `TeXLayoutHandler`, an implementation of the public
`Makie.layout_text(handler, source, attributes) -> Makie.TextLayout` protocol
from MakieOrg/Makie.jl#5717, so Makie 0.25 users select TeXLayout explicitly
instead of relying on the type piracy the legacy adapter needs. The handler
emits glyph IDs, exact FreeType faces, origins, extents, scales, block bounds,
and `LineSegments` specs for horizontal and vertical rules; Makie keeps
ownership of alignment, rotation, offset, validation, placement, and batching.

Makie 0.24 keeps working through `MathTeXEngineExt`. Once the handler interface
is present, its `generate_tex_elements(::LaTeXString)` specialisation delegates
to MathTeXEngine's own implementation, so merely loading TeXLayout no longer
changes Makie's default renderer.

Verified against PR head 727b340b: 11/11 handler assertions plus a CairoMakie
render pass covering fractions, radicals, mixed text/math, plain-string
fall-through, and matrix rules.

Glyph and block metrics follow Makie's contract rather than TeXLayout's ink
bounds. Makie turns every `GlyphExtent` into `(0, descender)`-`(hadvance,
ascender)` and expects the font's line metrics there so a block's height does
not depend on which characters it holds; `MathTeXEngine.TeXChar` pads the same
way, which is why the legacy path never showed this. Reporting bare ink bounds
made `L"$x$"` measure 9.06 px against Makie's 20.0 px at fontsize 20, scattered
baselines under `align = (:*, :center)`, and made `Axis` tick-label space depend
on whether a label happened to have a descender. Extents are now padded to
`max(ink_top, font_ascender)` / `min(ink_bottom, font_descender)`, and the block
bbox is the union of the placed padded glyph boxes and rule bounds, which is
exactly what `raw_string_boundingboxes` unions downstream.

Source routing and glyph resolution move into `src/render_support.jl` so the two
adapters share one implementation and one per-`FontFamily` runtime cache instead
of carrying ~120 duplicated lines each. `FontFamilyKey` names the cache-key type
so `_font_family_key`'s arity is stated once.

Makie joins `[extras]`/`[targets]`: the handler tests were silently skipped
because nothing in CI loaded `MakieExt` at all. Adding it immediately caught a
shadowed-binding bug in the legacy delegation path.

Also fixes inline-math delimiter stripping to use valid UTF-8 indices, so `$α$`
no longer raises a `StringIndexError`, and corrects a docs example that passed
`text_handler` to `Label`, which rejects it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYXUGdHB9mLnZemWvtkZo1
@codecov

codecov Bot commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 31.72043% with 127 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.09%. Comparing base (eac6b8b) to head (ae10d2c).

Files with missing lines Patch % Lines
ext/MakieExt.jl 0.00% 111 Missing ⚠️
src/render_support.jl 76.78% 13 Missing ⚠️
src/TeXLayout.jl 0.00% 2 Missing ⚠️
ext/MathTeXEngineExt.jl 93.75% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #34      +/-   ##
==========================================
- Coverage   89.39%   86.09%   -3.31%     
==========================================
  Files          21       23       +2     
  Lines        2915     3034     +119     
==========================================
+ Hits         2606     2612       +6     
- Misses        309      422     +113     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

2 participants