Thanks for your interest in contributing to ggtypst.
ggtypst is an R package with a Rust backend. It renders Typst text and math to SVG, then inserts that output into ggplot2 as annotations, geoms, and theme elements. Most contributions touch both the R package layer and the Rust rendering layer, so a good change usually starts with understanding how those two halves fit together.
Development setup
You will need:
- R >= 4.2
- Rust >= 1.89 with
cargoandrustc, better withrust-analyzer -
Airfor formatting R code andJarlfor checking R code - Recommended development packages such as
devtools,pkgdown,testthat, andvdiffr - Optional:
justto run development commands injustfile - Optional:
pre-commitorprekto run some checks on staged files before committing
Install package dependencies in your usual R environment, then use the project root as your working directory.
Build model
ggtypst is not a pure R package. The package builds a Rust staticlib under src/rust/, then links that library into the R package through extendr.
At a high level, the pipeline is:
- R user-facing functions collect arguments and normalize them into Typst source code strings.
- For LaTeX math code, we use
mitexto translate it into Typst.
- For LaTeX math code, we use
-
extendrforwards the Typst source code into Rust. - Rust compiles Typst source in an in-memory world, renders the first page to SVG, and returns dimensions plus diagnostics.
- R converts the SVG into grobs and plugs those grobs into
ggplot2.
That split is the key architectural idea of the package: R owns user-facing API design and ggplot2 integration; Rust owns rendering, Typst world setup, and diagnostics emitted by the Typst compiler.
Repository layout
-
R/: Rust-exported functions, helpers, grob construction, diagnostics, and ggplot2 integration -
src/rust/src/: Rust renderer, MiTeX conversion, embedded Typst assets, and error translation -
src/: generatedrextendrglue -
tests/testthat/: R tests -
vignettes/: pkgdown articles and documents -
tools/: development scripts used byrextendr
Architecture overview
R layer
The R API is organized around three feature families:
-
annotate_*()for one-off plot annotations -
geom_*()for data-driven Typst labels -
element_*()for Typst-rendered theme text
Other R files include:
-
build-source.R: assembles the Typst preamble and rendering directives around user code. -
diagnostic.R: formats Rust/Typst diagnostics into structuredcliwarnings and errors. -
typst-svg.R: thin R wrapper around the Rust SVG renderer entrypoint. -
grob.R: converts rendered SVG bytes into measured grid grobs ofggplot2. -
helper.R: shared validators, alias resolution, face normalization, unit conversion, and small utility helpers. Prefer reusing those helpers instead of open-coding validation. -
mitex.R: converts LaTeX math input to Typst source code with the embedded MiTeX scope. -
extendr-wrappers.R: generated.Call()wrappers for the Rust entrypoints; auto generated when runningrextendr::document()orjust document, never edit by hand. -
zzz.R: performs runtime registration that cannot be expressed statically, especially forggplot2generics that are owned outside this package.
Rust layer
The Rust crate lives in src/rust/ and currently exposes two exported entry points:
-
rs_typst_svg()compiles Typst and returns SVG output plus diagnostics -
rs_convert_latex_to_typst()converts LaTeX-style input through MiTeX
Important modules include:
-
world.rs: builds the in-memory Typst world and serves embedded Typst files -
render.rs: compiles Typst documents and converts the first page to SVG -
mitex.rs: bridges LaTeX-style input through MiTeX -
error.rs: translates Rust-side failures into structured R errors -
fonts.rs: loads and caches embedded/system fonts for Typst rendering
The three embedded Typst files under src/rust/specs/, which are from MiTeX’s repository, provide the MiTeX scope used when evaluating LaTeX-style formulas in Typst.
Auto-generated files
Do not hand-edit these auto-generated files. Instead, edit their source files and regenerate outputs.
README.md.github/CONTRIBUTING.mdNAMESPACEman/*.RdR/extendr-wrappers.R
Core development commands
The repository uses just as the main command runner:
-
just format/just fmt- runr-air format .andcargo fmt -
just check- runjarl check .,devtools::spell_check(), andcargo clippy -
just document/just doc- regeneraterextendrwrappers,NAMESPACE, and Rd files -
just test- run R tests plus Rust tests -
just build-check- runR CMD check . -
just site- rebuildREADME.md, community docs, and the pkgdown site
Useful direct commands while iterating:
Rscript -e "devtools::load_all('.')"Rscript -e "devtools::test(filter = 'geom-typst')"Rscript -e "testthat::test_file('tests/testthat/test-element-typst.R')"cargo test --manifest-path src/rust/Cargo.tomlcargo clippy --manifest-path src/rust/Cargo.toml
Submitting changes
Before submitting a PR, you should make sure that:
- Run
just format,just check,just documentandjust testfirst - Run
just siteif README, CONTRIBUTING, or vignettes changed - Keep commits focused on one logical change
Please follow Conventional Commits for PR titles, meaning that your PR titles must start with “feat:”, “fix:”, or another appropriate name (see the linked documentation). For example:
-
feat: A new feature. -
fix: A bug fix. -
docs: Documentation only changes. -
test: Adding missing tests or correcting existing tests. -
chore: Changes to the build process or auxiliary tools and libraries. -
refactor: A code change that neither fixes a bug nor adds a feature.
If you are unsure how to begin, you are welcome to open an issue or draft PR with your proposed approach first. That is especially helpful for changes that touch public API behavior, rendering semantics, or the Rust/R interface.
