# typst — composición tipográfica moderna: el hueco de LaTeX, que este catálogo no tenía de ninguna forma # # ── POR QUÉ ENTRA ────────────────────────────────────────────────────────────────────────────── # 56.592★, Apache-2.0, 435 crates, **Rust puro y CLI**: ni toolkit, ni muro gráfico, ni decisión de # distro. Y cubre una función que el catálogo **no tiene por ningún lado** —componer un documento # desde texto plano, con bibliografía, fórmulas y PDF reproducible—, que es justo la otra mitad de # lo que la suite ArtCraft trajo el 2026-10-13: `wordcraft` edita y exporta, `typst` COMPONE. # # El dato que la vuelve barata, y que sale del `Cargo.lock`, no del README: **cero dependencias C de # tipografía**. Su `fontconfig-parser` es un parser de los ficheros de fontconfig **escrito en Rust** # —no la `libfontconfig` de C— y las fuentes van por `fontdb` + `ttf-parser` + fontations # (`read-fonts`/`write-fonts`). Lo único C del árbol es `libdeflate-sys`, que trae su propio fuente y # lo compila con el crate `cc`; de ahí el envoltorio `AR` de abajo. # # ── LAS DOS PERILLAS QUE IMPORTAN, Y POR QUÉ SE DEJAN COMO ESTÁN ─────────────────────────────── # `typst-cli` trae `default = ["embedded-fonts", "http-server"]`: # · **`embedded-fonts` se queda**: mete las fuentes dentro del binario, así que `typst compile` # funciona en una imagen que no declare ni una familia tipográfica. Sin eso, el fallo sería el # clásico de esta casa —binario sellado, presente, y sin con qué dibujar una letra. # · **`self-update` NO está en el default y no se enciende**: se actualiza por `takana install`, # como todo acá, y encenderlo arrastraría `xz2` y `zip` de yapa. # · **`vendor-openssl` tampoco se enciende, pero NO porque openssl no haga falta.** Acá la primera # versión de esta receta se equivocó y el build lo dijo a la primera: # # error: failed to run custom build command for `openssl-sys v0.9.116` # # Yo había escrito que «en el lock no hay `openssl-sys`». **Lo había, y mi propio filtro de # triaje lo tapó**: la lista de crates `-sys` salía por un `head -8` y `openssl-sys` caía abajo. # Una vista RECORTADA leída como si fuera la verdad entera — la misma figura que el `provee.py` # apuntando al store vacío, y van dos en el mismo día. # La cadena real es `typst-cli` → `typst-kit/system-downloader` → `ureq` → `native-tls` → # `openssl`, y **no hay alternativa de rustls en typst-kit**: sus features son `native-tls` o # nada. Las tres salidas eran: (a) apagar `system-downloader` —y dejar a `typst` sin poder bajar # paquetes de Typst Universe, o sea una herramienta mutilada en silencio—, (b) `vendor-openssl`, # que compila OTRO openssl dentro de este build teniendo uno en el corpus, o (c) darle el del # corpus. Se eligió (c). # # ── ⚠ Y DE LAS DOS OPENSSL DEL CORPUS, LA QUE LLEVA THREADS ─────────────────────────────────── # `recipes/openssl.toml` se construye con `link = "static"`, y eso hace que el `Configure` de openssl # **apague `threads` y `pic` en silencio** (su línea 1514, ver [[link-static-apaga-threads-openssl]]: # es lo que dejó a `python3` sin `_ssl` durante meses). `recipes/openssl-threads.toml` es la misma # receta con `link = "dynamic"` —sigue emitiendo `.a`, o sea que no cambia el enlazado estático de # acá— y sí define `OPENSSL_THREADS`. Para una herramienta que abre conexiones de red, la variante # sin threads es una mina silenciosa: se usa la otra, que es la que ya usan `python3` y `rust`. name = "typst" version = "0.15.1" license = "Apache-2.0" [source] # `v0.15.1` (release del 2026-07-17). Se pinea el SHA del tag: un tag se mueve. # ⚠ El endpoint de tags de GitHub devuelve primero unos `v23-03-28` que son de 2023 y NO son la # versión vigente: hay que preguntarle a `releases/latest`, no a `tags`. repo = "https://github.com/typst/typst" commit = "9dfd3a08500b7896045f907433cf7b4b02434fad" # No committea `vendor/` ni usa `[patch.crates-io]` ⇒ el vendoreo va al `vendor/` por defecto sin # pisarle nada (la trampa que mató a `pdfcraft` no aplica; comprobado en el árbol del tag). [build] compiler = "zig-cc" target = "x86_64-linux-musl" # `static` y no `dynamic`: es una herramienta de línea de comandos, no carga nada por `dlopen`. link = "static" flags = ["-p", "typst-cli", "--bin", "typst"] [build.phases] compile = ''' set -e # El envoltorio de `AR` no es decorativo: el default de zig-cc exporta `AR="zig ar"` CON ESPACIO, y # el crate `cc` —que acá compila `libdeflate-sys`— ejecuta `$AR` como un binario ÚNICO ⇒ busca uno # llamado «zig ar» y falla. Es la cicatriz de `mise.toml`. printf '%s\n' '#!/bin/sh' 'for a do' 'case "$a" in --target=*) a=--target=x86_64-linux-musl ;; esac' 'set -- "$@" "$a"' 'shift' 'done' 'exec zig cc -mcpu=baseline "$@"' > "$PWD/.hammer-zig-cc" chmod +x "$PWD/.hammer-zig-cc" printf '%s\n' '#!/bin/sh' 'exec zig ar "$@"' > "$PWD/.hammer-zig-ar" chmod +x "$PWD/.hammer-zig-ar" RF="-C linker=$PWD/.hammer-zig-cc" rustc -vV | grep -q 'host: .*-alpine-' || RF="$RF -C link-self-contained=no" # `OPENSSL_NO_VENDOR` para que `openssl-sys` no caiga nunca al openssl vendoreado del crate (sería # un segundo openssl dentro del artefacto, distinto del del corpus), y `OPENSSL_STATIC` porque la # capa que se le monta trae `libssl.a`/`libcrypto.a` y nada de `.so`: sin esto, `openssl-sys` puede # decidir enlazado dinámico y el fallo aparece recién al LIGAR, lejos de la causa. CC="$PWD/.hammer-zig-cc" AR="$PWD/.hammer-zig-ar" RUSTFLAGS="$RF" OPENSSL_NO_VENDOR=1 OPENSSL_STATIC=1 cargo rustc --release --locked --offline -p typst-cli --bin typst -- -C target-feature=+crt-static -C relocation-model=static ''' install = ''' set -e test -x target/release/typst || { echo "no hay target/release/typst — ¿cambió el nombre del bin?" >&2; exit 1; } # ── LA PRUEBA DE QUE NO ES UN NOMBRE: COMPONER UN DOCUMENTO DE VERDAD ───────────────────────── # Un binario sellado, con contenido y reproducible puede igual no hacer NADA (memoria: # «subcomando SIN DRIVER = binario inerte»). Acá eso tiene una forma concreta y barata de medirse: # que componga. Si `typst compile` no produce un PDF, esta receta NO debe sellar — un typst que no # compone es exactamente la clase de artefacto que llega hasta el final diciendo que todo fue bien. mkdir -p "$PWD/.smoke" printf '%s\n' '#set page(width: 80pt, height: 40pt, margin: 4pt)' '= Takana' 'Compone.' > "$PWD/.smoke/p.typ" ./target/release/typst compile --font-path "$PWD/.smoke" "$PWD/.smoke/p.typ" "$PWD/.smoke/p.pdf" 2>"$PWD/.smoke/err" || { echo "typst compile FALLÓ:" >&2; cat "$PWD/.smoke/err" >&2; exit 1; } test -s "$PWD/.smoke/p.pdf" || { echo "typst compile salió 0 y el PDF quedó VACÍO" >&2; exit 1; } head -c 5 "$PWD/.smoke/p.pdf" | grep -q '%PDF-' || { echo "lo que salió no es un PDF" >&2; exit 1; } echo "smoke: typst compuso $(wc -c < "$PWD/.smoke/p.pdf") bytes de PDF con las fuentes EMBEBIDAS" install -Dm755 target/release/typst /out/usr/bin/typst install -Dm644 LICENSE /out/usr/share/licenses/typst/LICENSE install -Dm644 NOTICE /out/usr/share/licenses/typst/NOTICE test -s /out/usr/bin/typst ''' [deps] # Lo MÍNIMO que pide, medido tras el fallo de la primera corrida: openssl (con threads) y pkgconf # para que `openssl-sys` lo encuentre por `pkg-config`. Lo que sigue siendo cierto del triaje: **no # hay fontconfig ni freetype** —`fontconfig-parser` es un parser en Rust y las fuentes van # embebidas— y **no hay zlib del sistema**: `libz-rs-sys` es zlib EN RUST y `libdeflate-sys` compila # su propio C. build = ["openssl-threads", "pkgconf"]