We have built-in functions such as #upper()
, which can operate on content
parameters, for example:
#upper[
= Section
regular *bold*
]
How can this be implemented within Typst? Is there a way to traverse the children of a content
parameter and operate on the text
nodes? This #to-string()
function is able to traverse the tree, but it destroys its structure (by definition, as its goal is to flatten everything into a str
).
While there is no reason to reimplement #upper()
itself (apart from doing so for fun), I have a particular case in mind which generally speaking operates in a similar manner.
The case in question, if you find it relevant
I want to make a package that takes a fragment of text (or if this question is solved, even the whole document, which makes a content
after all, IIUC) and changes each letter into a random letter from an array. The goal is to make it possible to share the way a document looks without disclosing the content itself (for example, if itâs private). I have implemented a draft that works on strings, but I want to be able to wrap everything within a function or make a show
rule that renders the original text intentionally unrecoverable.
#import "@preview/suiji:0.3.0"
#let alphabet = ("A", "B", "C", "D", "E", "F", "G", "H")
#let punctuation = (" ", ",", ".", "!")
#let confound(rng, input) = {
let output = ""
let newrng = rng
for letter in input.clusters() {
if letter in punctuation {
output = output + letter
}
else {
let index
(newrng, index) = suiji.integers(newrng, low: 0, high: alphabet.len())
output = output + alphabet.at(index)
}
}
return (newrng, output)
}
#let rng = suiji.gen-rng(0)
#let confounded = ""
#{(_, confounded) = confound(rng, "Hello world!")}
#confounded // BHEAB EAECG!
// If you donât feed `rng` back you get the same result because Suiji is pure.
#{(rng, confounded) = confound(rng, "hello world!")}
#confounded // BHEAB EAECG!
// But if you do, you get new results
#{(rng, confounded) = confound(rng, "hello world!")}
#confounded // ACCHB ACFHD!
// every time.
#{(rng, confounded) = confound(rng, "hello world!")}
#confounded // HFDDH BHFEG!
One solution I thought about was to use a show
rule, like this:
#show regex("[^.,!? ]"): letter => {[X]}
= Section
regular *bold*
This one-liner works fine if you want to map every letter to some other content
([X]
in the code above), but since Suiji in particular (and Typst functions in general) is pure and shouldnât have side effects, you apparently cannot have a different rng
for each substitution (you cannot have global variables in Typst, lest you get an error saying âvariables from outside the function are read-only and cannot be modified
â).
BTW, I already have a name for the package⌠Babel
(It alludes to the Biblical Tower of Babel, and has the same name as the famous localisation and internationalisation package for LaTeXâŚ)
[/details]