Home

Awesome

typst-oxifmt (v0.2.1)

A Typst library that brings convenient string formatting and interpolation through the strfmt function. Its syntax is taken directly from Rust's format! syntax, so feel free to read its page for more information (https://doc.rust-lang.org/std/fmt/); however, this README should have enough information and examples for all expected uses of the library. Only a few things aren't supported from the Rust syntax, such as the p (pointer) format type, or the .* precision specifier.

A few extras (beyond the Rust-like syntax) will be added over time, though (feel free to drop suggestions at the repository: https://github.com/PgBiel/typst-oxifmt). The first "extra" so far is the fmt-decimal-separator: "string" parameter, which lets you customize the decimal separator for decimal numbers (floats) inserted into strings. E.g. strfmt("Result: {}", 5.8, fmt-decimal-separator: ",") will return the string "Result: 5,8" (comma instead of dot). See more below.

Compatible with: Typst v0.4.0+

Table of Contents

Usage

You can use this library through Typst's package manager (for Typst v0.6.0+):

#import "@preview/oxifmt:0.2.1": strfmt

For older Typst versions, download the oxifmt.typ file either from Releases or directly from the repository. Then, move it to your project's folder, and write at the top of your Typst file(s):

#import "oxifmt.typ": strfmt

Doing the above will give you access to the main function provided by this library (strfmt), which accepts a format string, followed by zero or more replacements to insert in that string (according to {...} formats inserted in that string), an optional fmt-decimal-separator parameter, and returns the formatted string, as described below.

Its syntax is almost identical to Rust's format! (as specified here: https://doc.rust-lang.org/std/fmt/). You can escape formats by duplicating braces ({{ and }} become { and }). Here's an example (see more examples in the file tests/strfmt-tests.typ):

#import "@preview/oxifmt:0.2.1": strfmt

#let s = strfmt("I'm {}. I have {num} cars. I'm {0}. {} is {{cool}}.", "John", "Carl", num: 10)
#assert.eq(s, "I'm John. I have 10 cars. I'm John. Carl is {cool}.")

Note that {} extracts positional arguments after the string sequentially (the first {} extracts the first one, the second {} extracts the second one, and so on), while {0}, {1}, etc. will always extract the first, the second etc. positional arguments after the string. Additionally, {bananas} will extract the named argument "bananas".

Formatting options

You can use {:spec} to customize your output. See the Rust docs linked above for more info, but a summary is below.

(You may also want to check out the examples at Examples.)

Some examples:

#import "@preview/oxifmt:0.2.1": strfmt

#let s1 = strfmt("{0:?}, {test:+012e}, {1:-<#8x}", "hi", -74, test: 569.4)
#assert.eq(s1, "\"hi\", +00005.694e2, -0x4a---")

#let s2 = strfmt("{:_>+11.5}", 59.4)
#assert.eq(s2, "__+59.40000")

#let s3 = strfmt("Dict: {:!<10?}", (a: 5))
#assert.eq(s3, "Dict: (a: 5)!!!!")

Examples

#import "@preview/oxifmt:0.2.1": strfmt

#let s = strfmt("First: {}, Second: {}, Fourth: {3}, Banana: {banana} (brackets: {{escaped}})", 1, 2.1, 3, label("four"), banana: "Banana!!")
#assert.eq(s, "First: 1, Second: 2.1, Fourth: four, Banana: Banana!! (brackets: {escaped})")
#import "@preview/oxifmt:0.2.1": strfmt

#let s = strfmt("The value is: {:?} | Also the label is {:?}", "something", label("label"))
#assert.eq(s, "The value is: \"something\" | Also the label is <label>")
#import "@preview/oxifmt:0.2.1": strfmt

#let s = strfmt("Values: {:?}, {1:?}, {stuff:?}", (test: 500), ("a", 5.1), stuff: [a])
#assert.eq(s, "Values: (test: 500), (\"a\", 5.1), [a]")
#import "@preview/oxifmt:0.2.1": strfmt

#let s = strfmt("Left5 {:-<5}, Right6 {:=>6}, Center10 {centered: ^10?}, Left3 {tleft:_<3}", "xx", 539, tleft: "okay", centered: [a])
#assert.eq(s, "Left5 xx---, Right6 ===539, Center10     [a]    , Left3 okay")
// note how 'okay' didn't suffer any padding at all (it already had at least the desired total width).
#import "@preview/oxifmt:0.2.1": strfmt

#let s = strfmt("Left-padded7 numbers: {:07} {:07} {:07} {3:07}", 123, -344, 44224059, 45.32)
#assert.eq(s, "Left-padded7 numbers: 0000123 -000344 44224059 0045.32")
#import "@preview/oxifmt:0.2.1": strfmt

#let s = strfmt("Padding depending on parameter: {0:02$} and {0:a>banana$}", 432, 0, 5, banana: 9)
#assert.eq(s, "Padding depending on parameter: 00432 aaaaaa432")  // widths 5 and 9
#import "@preview/oxifmt:0.2.1": strfmt

#let s = strfmt("Some numbers: {:+} {:+08}; With fill and align: {:_<+8}; Negative (no-op): {neg:+}", 123, 456, 4444, neg: -435)
#assert.eq(s, "Some numbers: +123 +0000456; With fill and align: +4444___; Negative (no-op): -435")

#import "@preview/oxifmt:0.2.1": strfmt

#let s = strfmt("Bases (10, 2, 8, 16(l), 16(U):) {0} {0:b} {0:o} {0:x} {0:X} | W/ prefixes and modifiers: {0:#b} {0:+#09o} {0:_>+#9X}", 124)
#assert.eq(s, "Bases (10, 2, 8, 16(l), 16(U):) 124 1111100 174 7c 7C | W/ prefixes and modifiers: 0b1111100 +0o000174 ____+0x7C")
#import "@preview/oxifmt:0.2.1": strfmt

#let s = strfmt("{0:.8} {0:.2$} {0:.potato$}", 1.234, 0, 2, potato: 5)
#assert.eq(s, "1.23400000 1.23 1.23400")
#import "@preview/oxifmt:0.2.1": strfmt

#let s = strfmt("{0:e} {0:E} {0:+.9e} | {1:e} | {2:.4E}", 124.2312, 50, -0.02)
#assert.eq(s, "1.242312e2 1.242312E2 +1.242312000e2 | 5e1 | -2.0000E-2")
#import "@preview/oxifmt:0.2.1": strfmt

#let s = strfmt("{0} {0:.6} {0:.5e}", 1.432, fmt-decimal-separator: ",")
#assert.eq(s, "1,432 1,432000 1,43200e0")

Grammar

Here's the grammar specification for valid format specs (in {name:spec}), which is basically Rust's format:

format_spec := [[fill]align][sign]['#']['0'][width]['.' precision]type
fill := character
align := '<' | '^' | '>'
sign := '+' | '-'
width := count
precision := count | '*'
type := '' | '?' | 'x?' | 'X?' | identifier
count := parameter | integer
parameter := argument '$'

Note, however, that precision of type .* is not supported yet and will raise an error.

Issues and Contributing

Please report any issues or send any contributions (through pull requests) to the repository at https://github.com/PgBiel/typst-oxifmt

Testing

If you wish to contribute, you may clone the repository and test this package with the following commands (from the project root folder):

git clone https://github.com/PgBiel/typst-oxifmt
cd typst-oxifmt/tests
typst c strfmt-tests.typ --root ..

The tests succeeded if you received no error messages from the last command (please ensure you're using a supported Typst version).

Changelog

v0.2.1

v0.2.0

v0.1.0

License

Licensed under MIT or Apache-2.0, at your option.