{"id":19635062,"url":"https://github.com/pgbiel/typst-oxifmt","last_synced_at":"2025-02-26T21:40:48.798Z","repository":{"id":172578324,"uuid":"648400698","full_name":"PgBiel/typst-oxifmt","owner":"PgBiel","description":"Convenient Rust-like string formatting in Typst (previously \"typst-strfmt\")","archived":false,"fork":false,"pushed_at":"2025-02-09T03:54:24.000Z","size":93,"stargazers_count":35,"open_issues_count":5,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-23T02:58:56.427Z","etag":null,"topics":["string-formatting","string-manipulation","typst"],"latest_commit_sha":null,"homepage":"","language":"Typst","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PgBiel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"PgBiel"}},"created_at":"2023-06-01T22:17:54.000Z","updated_at":"2025-02-17T10:38:46.000Z","dependencies_parsed_at":"2024-11-02T09:31:01.922Z","dependency_job_id":"5bd61d08-9cdc-41e3-bb01-5e740cc8c131","html_url":"https://github.com/PgBiel/typst-oxifmt","commit_stats":null,"previous_names":["pgbiel/typst-strfmt","pgbiel/typst-oxifmt"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PgBiel%2Ftypst-oxifmt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PgBiel%2Ftypst-oxifmt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PgBiel%2Ftypst-oxifmt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PgBiel%2Ftypst-oxifmt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PgBiel","download_url":"https://codeload.github.com/PgBiel/typst-oxifmt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240941484,"owners_count":19882062,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["string-formatting","string-manipulation","typst"],"created_at":"2024-11-11T12:23:40.822Z","updated_at":"2025-02-26T21:40:48.769Z","avatar_url":"https://github.com/PgBiel.png","language":"Typst","funding_links":["https://github.com/sponsors/PgBiel"],"categories":[],"sub_categories":[],"readme":"# typst-oxifmt (v0.2.1)\n\nA 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.\n\nA 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.\n\n**Compatible with:** [Typst](https://github.com/typst/typst) v0.4.0+\n\n## Table of Contents\n\n- [Usage](#usage)\n    - [Formatting options](#formatting-options)\n    - [Examples](#examples)\n    - [Grammar](#grammar)\n- [Issues and Contributing](#issues-and-contributing)\n- [Testing](#testing)\n- [Changelog](#changelog)\n- [License](#license)\n\n## Usage\n\nYou can use this library through Typst's package manager (for Typst v0.6.0+):\n\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n```\n\nFor 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):\n\n```typ\n#import \"oxifmt.typ\": strfmt\n```\n\nDoing 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.\n\nIts 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`):\n\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#let s = strfmt(\"I'm {}. I have {num} cars. I'm {0}. {} is {{cool}}.\", \"John\", \"Carl\", num: 10)\n#assert.eq(s, \"I'm John. I have 10 cars. I'm John. Carl is {cool}.\")\n```\n\nNote 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\".\n\n### Formatting options\n\nYou can use `{:spec}` to customize your output. See the Rust docs linked above for more info, but a summary is below.\n\n(You may also want to check out the examples at [Examples](#examples).)\n\n- Adding a `?` at the end of `spec` (that is, writing e.g. `{0:?}`) will call `repr()` to stringify your argument, instead of `str()`. Note that this only has an effect if your argument is a string, an integer, a float or a `label()` / `\u003clabel\u003e` - for all other types (such as booleans or elements), `repr()` is always called (as `str()` is unsupported for those).\n    - For strings, `?` (and thus `repr()`) has the effect of printing them with double quotes. For floats, this ensures a `.0` appears after it, even if it doesn't have decimal digits. For integers, this doesn't change anything. Finally, for labels, the `\u003clabel\u003e` (with `?`) is printed as `\u003clabel\u003e` instead of `label`.\n    - **TIP:** Prefer to always use `?` when you're inserting something that isn't a string, number or label, in order to ensure consistent results even if the library eventually changes the non-`?` representation.\n- After the `:`, add e.g. `_\u003c8` to align the string to the left, padding it with as many `_`s as necessary for it to be at least `8` characters long (for example). Replace `\u003c` by `\u003e` for right alignment, or `^` for center alignment. (If the `_` is omitted, it defaults to ' ' (aligns with spaces).)\n    - If you prefer to specify the minimum width (the `8` there) as a separate argument to `strfmt` instead, you can specify `argument$` in place of the width, which will extract it from the integer at `argument`. For example, `_^3$` will center align the output with `_`s, where the minimum width desired is specified by the fourth positional argument (index `3`), as an integer. This means that a call such as `strfmt(\"{:_^3$}\", 1, 2, 3, 4)` would produce `\"__1__\"`, as `3$` would evaluate to `4` (the value at the fourth positional argument/index `3`). Similarly, `named$` would take the width from the argument with name `named`, if it is an integer (otherwise, error).\n- **For numbers:**\n    - Specify `+` after the `:` to ensure zero or positive numbers are prefixed with `+` before them (instead of having no sign). `-` is also accepted but ignored (negative numbers always specify their sign anyways).\n    - Use something like `:09` to add zeroes to the left of the number until it has at least 9 digits / characters.\n        - The `9` here is also a width, so the same comment from before applies (you can add `$` to take it from an argument to the `strfmt` function).\n    - Use `:.5` to ensure your float is represented with 5 decimal digits of precision (zeroes are added to the right if needed; otherwise, it is rounded, **not truncated**).\n        - Note that floating point inaccuracies can be sometimes observed here, which is an unfortunate current limitation.\n        - Similarly to `width`, the precision can also be specified via an argument with the `$` syntax: `.5$` will take the precision from the integer at argument number 5 (the sixth one), while `.test$` will take it from the argument named `test`.\n    - **Integers only:** Add `x` (lowercase hex) or `X` (uppercase) at the end of the `spec` to convert the number to hexadecimal. Also, `b` will convert it to binary, while `o` will convert to octal.\n        - Specify a hashtag, e.g. `#x` or `#b`, to prepend the corresponding base prefix to the base-converted number, e.g. `0xABC` instead of `ABC`.\n    - Add `e` or `E` at the end of the `spec` to ensure the number is represented in scientific notation (with `e` or `E` as the exponent separator, respectively).\n    - For decimal numbers (floats), you can specify `fmt-decimal-separator: \",\"` to `strfmt` to have the decimal separator be a comma instead of a dot, for example.\n        - To have this be the default, you can alias `strfmt`, such as using `#let strfmt = strfmt.with(fmt-decimal-separator: \",\")`.\n    - Number spec arguments (such as `.5`) are ignored when the argument is not a number, but e.g. a string, even if it looks like a number (such as `\"5\"`).\n- Note that all spec arguments above **have to be specified in order** - if you mix up the order, it won't work properly!\n    - Check the grammar below for the proper order, but, in summary: fill (character) with align (`\u003c`, `\u003e` or `^`) -\u003e sign (`+` or `-`) -\u003e `#` -\u003e `0` (for 0 left-padding of numbers) -\u003e width (e.g. `8` from `08` or `9` from `-\u003c9`) -\u003e `.precision` -\u003e spec type (`?`, `x`, `X`, `b`, `o`, `e`, `E`)).\n\nSome examples:\n\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#let s1 = strfmt(\"{0:?}, {test:+012e}, {1:-\u003c#8x}\", \"hi\", -74, test: 569.4)\n#assert.eq(s1, \"\\\"hi\\\", +00005.694e2, -0x4a---\")\n\n#let s2 = strfmt(\"{:_\u003e+11.5}\", 59.4)\n#assert.eq(s2, \"__+59.40000\")\n\n#let s3 = strfmt(\"Dict: {:!\u003c10?}\", (a: 5))\n#assert.eq(s3, \"Dict: (a: 5)!!!!\")\n```\n\n### Examples\n\n- **Inserting labels, text and numbers into strings:**\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#let s = strfmt(\"First: {}, Second: {}, Fourth: {3}, Banana: {banana} (brackets: {{escaped}})\", 1, 2.1, 3, label(\"four\"), banana: \"Banana!!\")\n#assert.eq(s, \"First: 1, Second: 2.1, Fourth: four, Banana: Banana!! (brackets: {escaped})\")\n```\n\n- **Forcing `repr()` with `{:?}`** (which adds quotes around strings, and other things - basically represents a Typst value):\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#let s = strfmt(\"The value is: {:?} | Also the label is {:?}\", \"something\", label(\"label\"))\n#assert.eq(s, \"The value is: \\\"something\\\" | Also the label is \u003clabel\u003e\")\n```\n\n- **Inserting other types than numbers and strings** (for now, they will always use `repr()`, even without `{...:?}`, although that is more explicit):\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#let s = strfmt(\"Values: {:?}, {1:?}, {stuff:?}\", (test: 500), (\"a\", 5.1), stuff: [a])\n#assert.eq(s, \"Values: (test: 500), (\\\"a\\\", 5.1), [a]\")\n```\n\n- **Padding to a certain width with characters:** Use `{:x\u003c8}`, where `x` is the **character to pad with** (e.g. space or `_`, but can be anything), `\u003c` is the **alignment of the original text** relative to the padding (can be `\u003c` for left aligned (padding goes to the right), `\u003e` for right aligned (padded to its left) and `^` for center aligned (padded at both left and right)), and `8` is the **desired total width** (padding will add enough characters to reach this width; if the replacement string already has this width, no padding will be added):\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#let s = strfmt(\"Left5 {:-\u003c5}, Right6 {:=\u003e6}, Center10 {centered: ^10?}, Left3 {tleft:_\u003c3}\", \"xx\", 539, tleft: \"okay\", centered: [a])\n#assert.eq(s, \"Left5 xx---, Right6 ===539, Center10     [a]    , Left3 okay\")\n// note how 'okay' didn't suffer any padding at all (it already had at least the desired total width).\n```\n\n- **Padding numbers with zeroes to the left:** It's a similar functionality to the above, however you write `{:08}` for 8 characters (for instance) - note that any characters in the number's representation matter for width (including sign, dot and decimal part):\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#let s = strfmt(\"Left-padded7 numbers: {:07} {:07} {:07} {3:07}\", 123, -344, 44224059, 45.32)\n#assert.eq(s, \"Left-padded7 numbers: 0000123 -000344 44224059 0045.32\")\n```\n\n- **Defining padding-to width using parameters, not literals:** If you want the desired replacement width (the `8` in `{:08}` or `{: ^8}`) to be passed via parameter (instead of being hardcoded into the format string), you can specify `parameter$` in place of the width, e.g. `{:02$}` to take it from the third positional parameter, or `{:a\u003ebanana$}` to take it from the parameter named `banana` - note that the chosen parameter **must be an integer** (desired total width):\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#let s = strfmt(\"Padding depending on parameter: {0:02$} and {0:a\u003ebanana$}\", 432, 0, 5, banana: 9)\n#assert.eq(s, \"Padding depending on parameter: 00432 aaaaaa432\")  // widths 5 and 9\n```\n\n- **Displaying `+` on positive numbers:** Just add a `+` at the \"beginning\", i.e., before the `#0` (if either is there), or after the custom fill and align (if it's there and not `0` - see [Grammar](#grammar) for the exact positioning), like so:\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#let s = strfmt(\"Some numbers: {:+} {:+08}; With fill and align: {:_\u003c+8}; Negative (no-op): {neg:+}\", 123, 456, 4444, neg: -435)\n#assert.eq(s, \"Some numbers: +123 +0000456; With fill and align: +4444___; Negative (no-op): -435\")\n\n```\n\n- **Converting numbers to bases 2, 8 and 16:** Use one of the following specifier types (i.e., characters which always go at the very end of the format): `b` (binary), `o` (octal), `x` (lowercase hexadecimal) or `X` (uppercase hexadecimal). You can also add a `#` between `+` and `0` (see the exact position at the [Grammar](#grammar)) to display a **base prefix** before the number (i.e. `0b` for binary, `0o` for octal and `0x` for hexadecimal):\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#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:_\u003e+#9X}\", 124)\n#assert.eq(s, \"Bases (10, 2, 8, 16(l), 16(U):) 124 1111100 174 7c 7C | W/ prefixes and modifiers: 0b1111100 +0o000174 ____+0x7C\")\n```\n\n- **Picking float precision (right-extending with zeroes):** Add, at the end of the format (just before the spec type (such as `?`), if there's any), either `.precision` (hardcoded, e.g. `.8` for 8 decimal digits) or `.parameter$` (taking the precision value from the specified parameter, like with `width`):\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#let s = strfmt(\"{0:.8} {0:.2$} {0:.potato$}\", 1.234, 0, 2, potato: 5)\n#assert.eq(s, \"1.23400000 1.23 1.23400\")\n```\n\n- **Scientific notation:** Use `e` (lowercase) or `E` (uppercase) as specifier types (can be combined with precision):\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#let s = strfmt(\"{0:e} {0:E} {0:+.9e} | {1:e} | {2:.4E}\", 124.2312, 50, -0.02)\n#assert.eq(s, \"1.242312e2 1.242312E2 +1.242312000e2 | 5e1 | -2.0000E-2\")\n```\n\n- **Customizing the decimal separator on floats:** Just specify `fmt-decimal-separator: \",\"` (comma as an example):\n```typ\n#import \"@preview/oxifmt:0.2.1\": strfmt\n\n#let s = strfmt(\"{0} {0:.6} {0:.5e}\", 1.432, fmt-decimal-separator: \",\")\n#assert.eq(s, \"1,432 1,432000 1,43200e0\")\n```\n\n### Grammar\n\nHere's the grammar specification for valid format `spec`s (in `{name:spec}`), which is basically Rust's format:\n\n```\nformat_spec := [[fill]align][sign]['#']['0'][width]['.' precision]type\nfill := character\nalign := '\u003c' | '^' | '\u003e'\nsign := '+' | '-'\nwidth := count\nprecision := count | '*'\ntype := '' | '?' | 'x?' | 'X?' | identifier\ncount := parameter | integer\nparameter := argument '$'\n```\n\nNote, however, that precision of type `.*` is not supported yet and will raise an error.\n\n## Issues and Contributing\n\nPlease report any issues or send any contributions (through pull requests) to the repository at https://github.com/PgBiel/typst-oxifmt\n\n## Testing\n\nIf you wish to contribute, you may clone the repository and test this package with the following commands (from the project root folder):\n\n```sh\ngit clone https://github.com/PgBiel/typst-oxifmt\ncd typst-oxifmt/tests\ntypst c strfmt-tests.typ --root ..\n```\n\nThe tests succeeded if you received no error messages from the last command (please ensure you're using a supported Typst version).\n\n## Changelog\n\n### v0.2.1\n\n- Fixed formatting of UTF-8 strings. Before, strings with multi-byte UTF-8 codepoints would cause formatting inconsistencies or even crashes. ([Issue #6](https://github.com/PgBiel/typst-oxifmt/issues/6))\n- Fixed an inconsistency in negative number formatting. Now, it will always print a regular hyphen (e.g. '-2'), which is consistent with Rust's behavior; before, it would occasionally print a minus sign instead (as observed in a comment to [Issue #4](https://github.com/PgBiel/typst-oxifmt/issues/4)).\n- Added compatibility with Typst 0.8.0's new type system.\n\n### v0.2.0\n\n- The package's name is now `oxifmt`!\n- `oxifmt:0.2.0` is now available through Typst's Package Manager! You can now write `#import \"@preview/oxifmt:0.2.0\": strfmt` to use the library.\n- Greatly improved the README, adding a section for common examples.\n- Fixed negative numbers being formatted with two minus signs.\n- Fixed custom precision of floats not working when they are exact integers.\n\n### v0.1.0\n\n- Initial release, added `strfmt`.\n\n## License\n\nLicensed under MIT or Apache-2.0, at your option.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpgbiel%2Ftypst-oxifmt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpgbiel%2Ftypst-oxifmt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpgbiel%2Ftypst-oxifmt/lists"}