feat: LaTeX and Typst text handlers for Makie's text_handler hook#73
feat: LaTeX and Typst text handlers for Makie's text_handler hook#73jkrumbiegel wants to merge 74 commits into
Conversation
…List-based rendering
… drop unused GLMakie weakdep
| # a 30pt fontsize → ~density 4 (good visual fidelity without huge textures). | ||
| _density_for_size(s) = max(2, ceil(Int, maximum(s) / 8)) | ||
|
|
||
| function Makie.rasterize_marker_for_gpu(doc::MakieTeX.AbstractCachedDocument, scale) |
There was a problem hiding this comment.
Hmm what's scale here? Is it px_per_unit?
There was a problem hiding this comment.
I think it was markersize, but I don't think that part makes much sense yet.
The text recipe in Makie 0.25+ now routes `LaTeXString` content through a
`text_handler` extension hook with a two-phase `compile_text` /
`place_text!` protocol, so the standalone TeXImg / LTeX entry points and
the TEX/CachedTEX wrappers no longer carry their weight.
- Delete TeXImg recipe, LTeX block, text_utils helpers, and the
test_recipe.jl dev scratchpad.
- Delete TEXDocument, TeXDocument, CachedTEX, CachedTeX (and the
rendering/tex.jl compile_latex / latex2pdf helpers). LaTeX scaffolding
is now inlined in compile_text; the compute graph at the text-recipe
level provides the caching CachedTEX used to.
- Drop empty WGLMakie and RPRMakie extensions; GL/WGL rasterization
goes through Makie.rasterize_marker_for_gpu.
- CairoMakieExt: drop stale old-signature draw_marker methods (CachedSVG
variant and the legacy 8-arg overload) and the now-unreferenced
RENDER_EXTRASAFE switch.
Implement the new two-phase protocol:
- `AbstractLaTeX` supertype with shared `_compile_latex_block` /
`_escape_for_text_mode` helpers and a single `place_text!` method.
- `LaTeX` (claims `::LaTeXString` only — math/scientific use).
- `FullLaTeX` (claims `::LaTeXString` and plain `::AbstractString` with
text-mode escaping — matplotlib usetex style).
- Color, fontsize, and lineheight are baked into the LaTeX source via
`\definecolor` + `\color` and `\fontsize{}{}\selectfont`. The
resulting PDF is already correctly sized and colored; CairoMakie
renders the vector PDF directly via Poppler, GL backends rasterize
through `rasterize_marker_for_gpu`.
- Inline `\textcolor{...}{...}` / `\fontsize{...}{...}` in user-supplied
source override the outer wrapping in the natural LaTeX way.
Introduce AbstractPdfTextHandler supertype so the (CachedPDF, baseline_pt) pipeline can be reused by additional handlers. Subtype AbstractLaTeX under it and rename _latex_align_offset → _pdf_align_offset. Report ink_size (layout box) as the bbox rather than the full marker dim so axis title gaps and tick label padding don't include the crop_margin_pt antialias pad.
Mirror of the LaTeX handler for the Typst engine. `text(typst"...")` or plain strings (via FullTypst) compile through Typst and feed the same PDF marker pipeline. Implementation notes: - `top-edge: "ascender", bottom-edge: "descender"` gives a content- independent line box. - Math content doesn't inherit text edges, so a `#hide[Mp]` strut is prepended with a negative `#h(...)` retraction to establish text-line metrics on the line without adding horizontal whitespace. - Descender measured from the "Mp" reference glyph and emitted as metadata, queried back via `typst query <makietex-baseline>`. - Blank input returns `nothing` so empty Axis subtitles don't contribute phantom protrusions. Bump Typstry compat 0.3 → 0.7 (Typst 0.14).
Typstry and tectonic_jll are now [weakdeps] — MakieTeX no longer pulls them in by default. Users opt in by loading the respective package: using MakieTeX, tectonic_jll # activates MakieTeXLaTeXExt using MakieTeX, Typstry # activates MakieTeXTypstExt The LaTeX/Typst handler struct definitions (LaTeX, FullLaTeX, Typst, FullTypst) stay in core so they exist regardless of which extension is loaded. Concrete compile_text dispatches and the engine-running code live in the extensions. The shared place_text! / bbox / crop helpers move to src/pdf_text_handler.jl. Legacy TypstDocument / CachedTypst struct definitions stay in core; the constructors that need Typst compilation move to MakieTeXTypstExt. LaTeXStrings remains a hard dep (lightweight, transitively required by Typstry too).
Now that Makie no longer needs an explicit opt-in for non-AbstractString text types (single-arg `text()` shorthand removed), TypstString flows through `compile_text` dispatch on the handler with no trait line needed in our extension.
- Remove TypstDocument / CachedTypst / compile_typst / cached_doc /
cached_pdf / update_handle!(::CachedTypst) and the matching legacy
scatter-marker code in the Typst extension. The text_handler path is
the only supported way to render Typst now. Drop test/typst.jl.
- Collapse `LaTeX` + `FullLaTeX` and `Typst` + `FullTypst` into single
`LaTeX` and `Typst` structs with a `full::Bool` flag controlling
whether plain `AbstractString` inputs are routed through the engine.
- Don't export `LaTeX` / `Typst` — too generic, and `Typst` collides
with Typstry's export. Use as `MakieTeX.LaTeX` / `MakieTeX.Typst`.
- Typst handler gains a `font_paths::Vector{String}` field; the
extension prepends those to `TYPST_FONT_PATHS`. Drop the hardcoded
Julia Mono injection — Typstry's bundled font is no longer pulled in
by default.
- Simplify the Typst compile call: drop `ignorestatus` + try/catch,
let `run` fail naturally on engine errors.
- Use a multiline raw string literal for the default LaTeX preamble.
Wipe the legacy test suite (which referenced types we deleted earlier) and start over with reference-image tests modelled on AlgebraOfGraphics: - test/Project.toml pins the test env via [sources] to the Makie branch jk/breaking-0.25-work; drop once that branch lands. Test env is julia 1.12+ so [sources] is available; main package compat stays at 1.9. - test/reftest.jl: backend-aware reftest helper. Saves a recorded image, pixel-diffs against the committed reference, optionally updates via UPDATE_REFIMAGES=true. - test/reference_tests.jl: figures covering the rework — axes with LaTeX/Typst handlers in full and per-element modes, Labels surrounded by Boxes for bbox visualization, and alignment grids with a red cross anchor for each halign × valign combo (including :baseline). - test/runtests.jl runs both backends. Also drops SVG support entirely (SVGDocument, CachedSVG, all of the Rsvg-backed rendering path) — doesn't fit the text-primitive direction.
The LaTeX extension emits a \typeout{MAKIETEX_BASELINE_DEPTH=…} marker
and parses it from temp.log to compute the descender used by the
:baseline valign. tectonic discards its log unless --keep-logs is set,
so baseline_pt silently fell through to 0 and :baseline collapsed onto
the ink bottom (descenders sat on the anchor).
…o TGHM Drop the custom reftest.jl harness in favor of PixelMatch's @test_pixelmatch, which already handles save/compare/diff, JULIA_REFERENCETESTS_UPDATE, and interactive ref updates. Each builder in reference_tests.jl now returns a Makie.Figure; run_reftests calls a small compare() helper that renders via Makie.colorbuffer and forwards to the macro. Reference images are renamed to PixelMatch's underscore convention (<name>_ref.png) and the .gitignore patterns updated to match. Stale rec/diff intermediates removed. font_scaling now uses TeXGyreHerosMakie-Regular.otf (Makie ships TGHM in its asset dir) for all three handlers instead of the legacy Helvetica.otf file. The Typst handler points at a scratch dir holding only the Regular OTF because the four bundled TGHM variants share broken OS/2 metadata (usWeightClass=500 and fsSelection=REGULAR for every variant), so Typst can't pick a specific weight from the asset dir and lands on Bold.
The compile template prepended a hidden 'Mp' strut to give the line
proper text-line metrics. For inline / mixed / plain-text content the
strut sits on the same line as the body and is invisible. For display
math ($ … $) the body is a Typst block, so the strut becomes its own
empty text line above the equation — the cached bbox then includes a
tall band of whitespace.
Detect block equations inside the context block via b.func() ==
math.equation and b.at("block", default: false), and omit the strut
in that case.
page2img seeded the Cairo ARGB32 surface with ARGB32(1, 1, 1, 0). That violates Cairo's premultiplied-alpha invariant (alpha=0 requires rgb=0) and the leftover white rgb leaked into anti-aliased glyph edges as they were painted on top. The texture then carried gray-tinted edge pixels (e.g. (0.553, 0.553, 0.553, 0.447) instead of (0, 0, 0, 0.447)), which made GLMakie's rendered text look washed out compared to CairoMakie's vector path. Seed with ARGB32(0, 0, 0, 0) so AA edges stay pure black with partial alpha.
|
It looks like Claude got a little overzealous and dropped a bunch of functionality (notably svg and direct pdf rendering) which are part of the reason this package exists. |
|
Nah that wasn't Claude by itself, that was me. I felt like the main reason this package exists was to include TeX (it's in the name) or Typst as the modern alternative, and then mostly for rendering special text (mostly complex math), not arbitrary pdf markers like the Wikipedia or Gopher scatter examples. So I reduced it down to the essentials as it's a breaking version anyway with the main changes. We can also keep the pdf/svg API though if you feel like it's worth it. |
The parent project doesn't need to be instantiated separately; Pkg.test creates its own temp env from test/Project.toml's [sources] and installs whatever it actually needs. Skipping buildpkg avoids a stale parent manifest leaving the test env in an inconsistent state (which was the Windows FFTA-listed-but-not-installed symptom).
Drops the old CachedTEX / teximg / LTeX content (those APIs are gone) and starts over with four focused pages: - index.md — Vitepress hero + minimal LaTeX example. - latex.md — the LaTeX handler: quick start, full mode (matching fonts across the figure), and preambles with packages / macros (physics, siunitx). Realistic damped-oscillation and density-of-states examples. - typst.md — same shape for the Typst handler. Default font config + a preamble that defines a kB shortcut. - markers.md — PDF and SVG scatter markers using a committed Julia-dots asset (SVG + Typst-generated PDF), aspect-preservation note, mixing markers. api.md is a small @docs index of the public types. Old Documenter.jl references to deleted constants / cached types are gone. docs/Project.toml trims to what's actually needed (Makie, CairoMakie, ComputePipeline pinned to jk/breaking-0.25-work; Typstry + tectonic_jll so the engines load in @example blocks). Drops Data.toml + DataToolkit-era machinery.
- PDF marker page numbering is now 1-based (default `page = 1`). Internal conversion to Poppler's 0-based happens at the boundary. - Doc examples drop explicit fontsize / Figure size — defaults look fine. - Rewrote the latex.md 'Full mode' paragraph: the two paths use different fonts (not 'subtly different'), and the tradeoff is convenience vs speed. - Removed the 'Mixing markers' subsection — covered by the Makie scatter vector-input convention. - New 'Complex content in layouts' section on both latex.md and typst.md: tree-level e+e- → μ+μ- as a tikz-feynman / fletcher diagram in a `Label`, paired with the angular distribution as a plot. Shows the LaTeX preamble adding packages and Typst's `#import "@preview/..."` syntax.
Typst's #import can sit anywhere — no need to route it through the handler's preamble. Drops the wrapping handler config and reads more naturally.
LaTeX strings aren't cached across renders in the current compute-graph flow — every save re-runs the engine on each text element. Reword the 'full mode' tradeoff to reflect that.
Keeps the Label() call short so the layout structure (Label + Axis) is visible at a glance.
Documenter.deploydocs(target = "build") pushed the whole build dir including the build/<i>/ subdir that DocumenterVitepress writes per base, ending up at previews/PR73/1/. The DocumenterVitepress wrapper reads bases.txt and deploys each base into the correct subfolder.
\definecolor{maincolor}{...} is part of the unconditional document
template, so xcolor must be loaded — otherwise a user preamble that
forgets to include xcolor would error out. Move the \usepackage{xcolor}
above the user preamble so custom preambles can focus on their own
packages.
The point of the flag is that plain `String` inputs also get rendered through the engine (not just LaTeXString / TypstString). `full` was vague; `render_strings` says it.
set_theme! reads more linearly than a do-block on first sight, and render_strings = true keeps the figure in a single font for an all-in-one first impression. set_theme!() reset is hidden via # hide.
Adds python.svg (Wikimedia) and matlab.svg (devicon) to docs/src/assets/ and a new `@example` block right under the single-marker SVG example that scatter-plots six points with the three logos cycled per-point. Also hardens `MakieTeX.SVG` to fall back to the SVG's viewBox when the file has no explicit width/height attributes (which the devicon matlab.svg doesn't). Previously librsvg returned a 0×0 intrinsic size and the marker failed to render.
Adds _readme/ (README.qmd, make.jl, check.jl) and a readme.yml workflow that renders via Quarto, text-diffs README.md against HEAD, and PixelMatch's the rendered PNGs strictly (n_diff > 0 fails). On mismatch the script writes _ref/_diff PNGs and the workflow uploads README.md + README_files/ as a 'rendered-readme' artifact for download.
…the committed Manifest
Quarto's notebook runner enforces an exact Julia patch match against the manifest. Committing a Manifest pinned us to one Julia version forever; let Pkg.instantiate generate one matching whatever Julia the workflow's on, and the runner sees a self-consistent set.
Adds two text handlers —
MakieTeX.LaTeXandMakieTeX.Typst— for Makie's proposedtext_handlerattribute. Both compile content to PDF, cache it, and place it through Makie's text recipe withalign = (…, :baseline)support.Requires the Makie branch at MakieOrg/Makie.jl#5632.
API
MakieTeX.LaTeX(; full, preamble, classoptions, engine, border_pt, crop_margin_pt)— engine choice betweenlualatex/pdflatex/ bundledtectonic_jll.MakieTeX.Typst(; full, preamble, font, font_paths, crop_margin_pt).LaTeXString/TypstStringalways route through the matching handler.full = truealso routes plainAbstractStringinputs (matplotlib'stext.usetexanalogue).set_theme!,with_theme) or per-plot. Multiple handlers in one figure are fine.Engine code lives in package extensions keyed on
tectonic_jll/Typstry, so neither is a hard dep.Output
draw_marker(::CachedPDF, …).rasterize_marker_for_gpu.Checks
teximg/LTeXrecipes still work on Makie 0.25plot.text_handler = …test/reference_tests.jl)