diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 730bf75f83..1b4f768718 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: [push, pull_request, merge_group] env: RUSTFLAGS: "-Dwarnings" RUSTDOCFLAGS: "-Dwarnings" + TYPST_TESTS_EXTENDED: true jobs: # This allows us to have one branch protection rule for the full test matrix. @@ -29,8 +30,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@1.77.0 - uses: Swatinem/rust-cache@v2 + - run: cargo test --workspace --no-run - run: cargo test --workspace --no-fail-fast checks: @@ -38,7 +40,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@1.77.0 + with: + components: clippy, rustfmt - uses: Swatinem/rust-cache@v2 - run: cargo clippy --workspace --all-targets --all-features - run: cargo fmt --check --all @@ -62,5 +66,5 @@ jobs: with: toolchain: nightly-2023-09-13 - uses: Swatinem/rust-cache@v2 - - run: cargo install cargo-fuzz + - run: cargo install --locked cargo-fuzz@0.12.0 - run: cd tests/fuzz && cargo fuzz build --dev diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c62ad56dbd..4e855064e5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,7 +37,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@1.77.0 with: target: ${{ matrix.target }} diff --git a/.gitignore b/.gitignore index 9a5deded35..f9daa3d7ea 100644 --- a/.gitignore +++ b/.gitignore @@ -6,11 +6,8 @@ desktop.ini .DS_Store # Tests and benchmarks -tests/png -tests/pdf -tests/svg -tests/target -tests/typ/**/*.pdf +tests/store +tests/suite/**/*.pdf tests/fuzz/target tests/fuzz/corpus tests/fuzz/artifacts @@ -23,6 +20,7 @@ tarpaulin-report.html # Node node_modules +tools/test-helper/dist package-lock.json # Nix diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0faf6436bd..006daff274 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,7 @@ Typst日本語ドキュメント翻訳プロジェクトにご興味をお持ち 2. `./docs`内のMarkdownファイル群は、Typstのチュートリアルや入門ガイドなど、言語リファレンス以外のページの本体です。**既存のMarkdownファイルを直接書き換えて翻訳してください**。 それに加えて、`./docs/src/lib.rs`ファイルの[`urlify`関数](https://github.com/search?q=repo%3Atypst-jp/typst-jp.github.io%20urlify&type=code)を編集して、中国語版の記事タイトルを日本語版のものに書き換えてください。このプロセスを抜かすと、WebページのURLが正しく生成されません。 3. 「サードパーティパッケージ」のページの翻訳を追加する場合は、`./static/assets/index2ja.json`も編集する必要があります。 -3. 翻訳の際は、[後述のガイドライン](#スタイルマニュアル)を参照し、[v0.11.0時点での公式ドキュメント](https://github.com/typst/typst/tree/v0.11.0/docs)から翻訳してください。 +3. 翻訳の際は、[後述のガイドライン](#スタイルマニュアル)を参照し、[v0.11.1時点での公式ドキュメント](https://github.com/typst/typst/tree/v0.11.1/docs)から翻訳してください。 4. 翻訳作業の途中でも、Draft Pull Requestを作成して、翻訳の進捗状況を共有することができます。 5. 翻訳作業が終わったら、Pull Requestを作成し、送信してください。 diff --git a/Cargo.lock b/Cargo.lock index 5e8e6a6d54..6bc69aa103 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -288,9 +288,9 @@ dependencies = [ [[package]] name = "citationberg" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82108f2b676c954076d2e5044f19a6a03887b24bd42804f322e0650d13035899" +checksum = "d259fe9fd78ffa05a119581d20fddb50bfba428311057b12741ffb9015123d0b" dependencies = [ "quick-xml", "serde", @@ -827,9 +827,9 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "hayagriva" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2e670de5191df083ddd112cd253049f8213277ccf0c15e18a8bf10e6c666cc" +checksum = "1d0d20c98b77b86ce737876b2a1653e2e6abbeee84afbb39d72111091191c97a" dependencies = [ "biblatex", "ciborium", @@ -2177,6 +2177,12 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + [[package]] name = "simd-adler32" version = "0.3.7" @@ -2587,7 +2593,7 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "typst" -version = "0.11.0" +version = "0.11.1" dependencies = [ "az", "bitflags 2.4.2", @@ -2648,13 +2654,13 @@ dependencies = [ [[package]] name = "typst-assets" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13f85360328da54847dd7fefaf272dfa5b6d1fdeb53f32938924c39bf5b2c6c" +checksum = "2b3061f8d268e8eec7481c9ab24540455cb4912983c49aae38fa6e8bf8ef4d9c" [[package]] name = "typst-cli" -version = "0.11.0" +version = "0.11.1" dependencies = [ "chrono", "clap", @@ -2682,6 +2688,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml 0.9.32", + "shell-escape", "tar", "tempfile", "toml", @@ -2699,12 +2706,12 @@ dependencies = [ [[package]] name = "typst-dev-assets" -version = "0.11.0" -source = "git+https://github.com/typst/typst-dev-assets?tag=v0.11.0#e0ef7ad46f28a440c41bc8e78563ace86cc02678" +version = "0.11.1" +source = "git+https://github.com/typst/typst-dev-assets?tag=v0.11.1#35caed3a870d46e827cffaa9dc450e38bede2a37" [[package]] name = "typst-docs" -version = "0.11.0" +version = "0.11.1" dependencies = [ "clap", "comemo", @@ -2730,7 +2737,7 @@ dependencies = [ [[package]] name = "typst-fuzz" -version = "0.11.0" +version = "0.11.1" dependencies = [ "comemo", "libfuzzer-sys", @@ -2742,20 +2749,23 @@ dependencies = [ [[package]] name = "typst-ide" -version = "0.11.0" +version = "0.11.1" dependencies = [ "comemo", "ecow", "if_chain", "log", + "once_cell", "serde", "typst", + "typst-assets", + "typst-dev-assets", "unscanny", ] [[package]] name = "typst-macros" -version = "0.11.0" +version = "0.11.1" dependencies = [ "heck", "proc-macro2", @@ -2765,7 +2775,7 @@ dependencies = [ [[package]] name = "typst-pdf" -version = "0.11.0" +version = "0.11.1" dependencies = [ "base64 0.22.0", "bytemuck", @@ -2789,7 +2799,7 @@ dependencies = [ [[package]] name = "typst-render" -version = "0.11.0" +version = "0.11.1" dependencies = [ "bytemuck", "comemo", @@ -2808,7 +2818,7 @@ dependencies = [ [[package]] name = "typst-svg" -version = "0.11.0" +version = "0.11.1" dependencies = [ "base64 0.22.0", "comemo", @@ -2824,7 +2834,7 @@ dependencies = [ [[package]] name = "typst-syntax" -version = "0.11.0" +version = "0.11.1" dependencies = [ "comemo", "ecow", @@ -2839,20 +2849,20 @@ dependencies = [ [[package]] name = "typst-tests" -version = "0.11.0" +version = "0.11.1" dependencies = [ "clap", "comemo", "ecow", "once_cell", "oxipng", + "parking_lot", "rayon", "tiny-skia", "ttf-parser", "typst", "typst-assets", "typst-dev-assets", - "typst-ide", "typst-pdf", "typst-render", "typst-svg", @@ -2862,7 +2872,7 @@ dependencies = [ [[package]] name = "typst-timing" -version = "0.11.0" +version = "0.11.1" dependencies = [ "parking_lot", "serde", diff --git a/Cargo.toml b/Cargo.toml index 6510678136..dee38c0712 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ default-members = ["crates/typst-cli"] resolver = "2" [workspace.package] -version = "0.11.0" +version = "0.11.1" rust-version = "1.74" # also change in ci.yml authors = ["The Typst Project Developers"] edition = "2021" @@ -16,17 +16,17 @@ keywords = ["typst"] readme = "README.md" [workspace.dependencies] -typst = { path = "crates/typst", version = "0.11.0" } -typst-cli = { path = "crates/typst-cli", version = "0.11.0" } -typst-ide = { path = "crates/typst-ide", version = "0.11.0" } -typst-macros = { path = "crates/typst-macros", version = "0.11.0" } -typst-pdf = { path = "crates/typst-pdf", version = "0.11.0" } -typst-render = { path = "crates/typst-render", version = "0.11.0" } -typst-svg = { path = "crates/typst-svg", version = "0.11.0" } -typst-syntax = { path = "crates/typst-syntax", version = "0.11.0" } -typst-timing = { path = "crates/typst-timing", version = "0.11.0" } -typst-assets = "0.11.0" -typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", tag = "v0.11.0" } +typst = { path = "crates/typst", version = "0.11.1" } +typst-cli = { path = "crates/typst-cli", version = "0.11.1" } +typst-ide = { path = "crates/typst-ide", version = "0.11.1" } +typst-macros = { path = "crates/typst-macros", version = "0.11.1" } +typst-pdf = { path = "crates/typst-pdf", version = "0.11.1" } +typst-render = { path = "crates/typst-render", version = "0.11.1" } +typst-svg = { path = "crates/typst-svg", version = "0.11.1" } +typst-syntax = { path = "crates/typst-syntax", version = "0.11.1" } +typst-timing = { path = "crates/typst-timing", version = "0.11.1" } +typst-assets = "0.11.1" +typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", tag = "v0.11.1" } az = "1.2" base64 = "0.22" bitflags = { version = "2", features = ["serde"] } @@ -47,7 +47,7 @@ env_proxy = "0.4" flate2 = "1" fontdb = { version = "0.16", default-features = false } fs_extra = "1.3" -hayagriva = "0.5.2" +hayagriva = "0.5.3" heck = "0.4" hypher = "0.1.4" icu_properties = { version = "1.4", features = ["serde"] } @@ -94,6 +94,7 @@ semver = "1" serde = { version = "1.0.184", features = ["derive"] } serde_json = "1" serde_yaml = "0.9" +shell-escape = "0.1.5" siphasher = "1" smallvec = { version = "1.11.1", features = ["union", "const_generics", "const_new"] } stacker = "0.1.15" diff --git a/README.en.md b/README.en.md index 9f62c97e14..0528fc99bd 100644 --- a/README.en.md +++ b/README.en.md @@ -5,7 +5,7 @@ This is an unofficial Japanese translation of the documentation for the typesetting system [Typst](https://typst.app/docs). It has been created with the permission of [Typst GmbH](https://typst.app/legal/). -The repository was forked from the [Chinese version](https://github.com/typst-doc-cn/typst-doc-cn.github.io) and is translated into Japanese based on the official documentation of [Typst v0.11.0](https://typst.app/docs/changelog/#v0.11.0) as of June 2024. +The repository was forked from the [Chinese version](https://github.com/typst-doc-cn/typst-doc-cn.github.io) and is translated into Japanese based on the official documentation of [Typst v0.11.1](https://typst.app/docs/changelog/#v0.11.1) as of June 2024. The actual working web version can be viewed at the following URL. > https://typst-jp.github.io/docs/ diff --git a/README.md b/README.md index 1c3213e4f4..a4a79909f3 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ 組版システム [Typst](https://typst.app/docs) の非公式な日本語ドキュメントです。[Typst GmbH](https://typst.app/legal/) の許諾を得て作成されています。 -このリポジトリは[中国語版](https://github.com/typst-doc-cn/typst-doc-cn.github.io)からフォークして作成され、2024年6月時点での最新版である [Typst v0.11.0](https://typst.app/docs/changelog/#v0.11.0) の公式ドキュメントを元に日本語訳を行います。 +このリポジトリは[中国語版](https://github.com/typst-doc-cn/typst-doc-cn.github.io)からフォークして作成され、2024年6月時点での最新版である [Typst v0.11.1](https://typst.app/docs/changelog/#v0.11.1) の公式ドキュメントを元に日本語訳を行います。 実際に作動している Web 版は、以下の URL から閲覧できます。 > https://typst-jp.github.io/docs/ diff --git a/crates/typst-cli/Cargo.toml b/crates/typst-cli/Cargo.toml index 57251a64a0..c2b23df069 100644 --- a/crates/typst-cli/Cargo.toml +++ b/crates/typst-cli/Cargo.toml @@ -15,9 +15,6 @@ readme = { workspace = true } [[bin]] name = "typst" path = "src/main.rs" -test = false -doctest = false -bench = false doc = false [dependencies] @@ -51,6 +48,7 @@ semver = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } +shell-escape = { workspace = true } tar = { workspace = true } tempfile = { workspace = true } toml = { workspace = true } diff --git a/crates/typst-cli/src/init.rs b/crates/typst-cli/src/init.rs index 01fdb02f0c..b0446bd1ea 100644 --- a/crates/typst-cli/src/init.rs +++ b/crates/typst-cli/src/init.rs @@ -104,11 +104,19 @@ fn print_summary( out.set_color(&gray)?; write!(out, "> ")?; out.reset()?; - writeln!(out, "cd {}", project_dir.display())?; + writeln!( + out, + "cd {}", + shell_escape::escape(project_dir.display().to_string().into()), + )?; out.set_color(&gray)?; write!(out, "> ")?; out.reset()?; - writeln!(out, "typst watch {}", template.entrypoint)?; + writeln!( + out, + "typst watch {}", + shell_escape::escape(template.entrypoint.to_string().into()), + )?; writeln!(out)?; Ok(()) } diff --git a/crates/typst-cli/src/main.rs b/crates/typst-cli/src/main.rs index da0a57fd55..c8bd391442 100644 --- a/crates/typst-cli/src/main.rs +++ b/crates/typst-cli/src/main.rs @@ -26,7 +26,7 @@ use crate::timings::Timer; thread_local! { /// The CLI's exit code. - static EXIT: Cell = Cell::new(ExitCode::SUCCESS); + static EXIT: Cell = const { Cell::new(ExitCode::SUCCESS) }; } /// The parsed commandline arguments. diff --git a/crates/typst-cli/src/world.rs b/crates/typst-cli/src/world.rs index 4e0bcd54ab..711fd02582 100644 --- a/crates/typst-cli/src/world.rs +++ b/crates/typst-cli/src/world.rs @@ -226,7 +226,7 @@ struct FileSlot { } impl FileSlot { - /// Create a new path slot. + /// Create a new file slot. fn new(id: FileId) -> Self { Self { id, file: SlotCell::new(), source: SlotCell::new() } } diff --git a/crates/typst-ide/Cargo.toml b/crates/typst-ide/Cargo.toml index b292756222..01f7a10620 100644 --- a/crates/typst-ide/Cargo.toml +++ b/crates/typst-ide/Cargo.toml @@ -12,11 +12,6 @@ categories = { workspace = true } keywords = { workspace = true } readme = { workspace = true } -[lib] -test = false -doctest = false -bench = false - [dependencies] typst = { workspace = true } comemo = { workspace = true } @@ -26,5 +21,10 @@ log = { workspace = true } serde = { workspace = true } unscanny = { workspace = true } +[dev-dependencies] +typst-assets = { workspace = true } +typst-dev-assets = { workspace = true } +once_cell = { workspace = true } + [lints] workspace = true diff --git a/crates/typst-ide/src/analyze.rs b/crates/typst-ide/src/analyze.rs index 9d67b322d7..47214481db 100644 --- a/crates/typst-ide/src/analyze.rs +++ b/crates/typst-ide/src/analyze.rs @@ -50,6 +50,9 @@ pub fn analyze_expr( /// Try to load a module from the current source file. pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option { + // Use span in the node for resolving imports with relative paths. + let source_span = source.span(); + let (source, _) = analyze_expr(world, source).into_iter().next()?; if source.scope().is_some() { return Some(source); @@ -73,7 +76,7 @@ pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option { Scopes::new(Some(world.library())), Span::detached(), ); - typst::eval::import(&mut vm, source, Span::detached(), true) + typst::eval::import(&mut vm, source, source_span, true) .ok() .map(Value::Module) } diff --git a/crates/typst-ide/src/complete.rs b/crates/typst-ide/src/complete.rs index 4e4b89182c..24cd747515 100644 --- a/crates/typst-ide/src/complete.rs +++ b/crates/typst-ide/src/complete.rs @@ -1053,7 +1053,7 @@ impl<'a> CompletionContext<'a> { /// A small window of context before the cursor. fn before_window(&self, size: usize) -> &str { - &self.before[self.cursor.saturating_sub(size)..] + Scanner::new(self.before).from(self.cursor.saturating_sub(size)) } /// Add a prefix and suffix to all applications. @@ -1403,3 +1403,42 @@ impl<'a> CompletionContext<'a> { } } } + +#[cfg(test)] +mod tests { + use typst::eval::Tracer; + + use super::autocomplete; + use crate::tests::TestWorld; + + #[track_caller] + fn test(text: &str, cursor: usize, contains: &[&str], excludes: &[&str]) { + let world = TestWorld::new(text); + let doc = typst::compile(&world, &mut Tracer::new()).ok(); + let (_, completions) = + autocomplete(&world, doc.as_ref(), &world.main, cursor, true) + .unwrap_or_default(); + + let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); + for item in contains { + assert!(labels.contains(item), "{item:?} was not contained in {labels:?}"); + } + for item in excludes { + assert!(!labels.contains(item), "{item:?} was not excluded in {labels:?}"); + } + } + + #[test] + fn test_autocomplete() { + test("#i", 2, &["int", "if conditional"], &["foo"]); + test("#().", 4, &["insert", "remove", "len", "all"], &["foo"]); + } + + #[test] + fn test_before_window_char_boundary() { + // Check that the `before_window` doesn't slice into invalid byte + // boundaries. + let s = "😀😀 #text(font: \"\")"; + test(s, s.len() - 2, &[], &[]); + } +} diff --git a/crates/typst-ide/src/lib.rs b/crates/typst-ide/src/lib.rs index bbfd56d950..3967aaad47 100644 --- a/crates/typst-ide/src/lib.rs +++ b/crates/typst-ide/src/lib.rs @@ -90,3 +90,88 @@ fn summarize_font_family<'a>(variants: impl Iterator) -> Ec detail } + +#[cfg(test)] +mod tests { + use comemo::Prehashed; + use once_cell::sync::Lazy; + use typst::diag::{FileError, FileResult}; + use typst::foundations::{Bytes, Datetime}; + use typst::syntax::{FileId, Source}; + use typst::text::{Font, FontBook}; + use typst::{Library, World}; + + /// A world for IDE testing. + pub struct TestWorld { + pub main: Source, + base: &'static TestBase, + } + + impl TestWorld { + /// Create a new world for a single test. + /// + /// This is cheap because the shared base for all test runs is lazily + /// initialized just once. + pub fn new(text: &str) -> Self { + static BASE: Lazy = Lazy::new(TestBase::default); + let main = Source::detached(text); + Self { main, base: &*BASE } + } + } + + impl World for TestWorld { + fn library(&self) -> &Prehashed { + &self.base.library + } + + fn book(&self) -> &Prehashed { + &self.base.book + } + + fn main(&self) -> Source { + self.main.clone() + } + + fn source(&self, id: FileId) -> FileResult { + if id == self.main.id() { + Ok(self.main.clone()) + } else { + Err(FileError::NotFound(id.vpath().as_rootless_path().into())) + } + } + + fn file(&self, id: FileId) -> FileResult { + Err(FileError::NotFound(id.vpath().as_rootless_path().into())) + } + + fn font(&self, index: usize) -> Option { + Some(self.base.fonts[index].clone()) + } + + fn today(&self, _: Option) -> Option { + None + } + } + + /// Shared foundation of all test worlds. + struct TestBase { + library: Prehashed, + book: Prehashed, + fonts: Vec, + } + + impl Default for TestBase { + fn default() -> Self { + let fonts: Vec<_> = typst_assets::fonts() + .chain(typst_dev_assets::fonts()) + .flat_map(|data| Font::iter(Bytes::from_static(data))) + .collect(); + + Self { + library: Prehashed::new(Library::default()), + book: Prehashed::new(FontBook::from_fonts(&fonts)), + fonts, + } + } + } +} diff --git a/crates/typst-macros/Cargo.toml b/crates/typst-macros/Cargo.toml index caef7eb4b3..b6b496cb37 100644 --- a/crates/typst-macros/Cargo.toml +++ b/crates/typst-macros/Cargo.toml @@ -14,9 +14,6 @@ readme = { workspace = true } [lib] proc-macro = true -test = false -doctest = false -bench = false [dependencies] heck = { workspace = true } diff --git a/crates/typst-macros/src/category.rs b/crates/typst-macros/src/category.rs index ac8c813df6..26ec879ccb 100644 --- a/crates/typst-macros/src/category.rs +++ b/crates/typst-macros/src/category.rs @@ -33,6 +33,7 @@ pub fn category(_: TokenStream, item: syn::Item) -> Result { } /// Parse a bare `pub static CATEGORY: Category;` item. +#[allow(dead_code)] pub struct BareStatic { pub attrs: Vec, pub vis: Visibility, diff --git a/crates/typst-macros/src/lib.rs b/crates/typst-macros/src/lib.rs index 35c48b4a3d..58d963863e 100644 --- a/crates/typst-macros/src/lib.rs +++ b/crates/typst-macros/src/lib.rs @@ -136,7 +136,7 @@ pub fn ty(stream: BoundaryStream, item: BoundaryStream) -> BoundaryStream { /// /// This implements `NativeElement` for the given type. /// -/// ``` +/// ```ignore /// /// A section heading. /// #[elem(Show, Count)] /// struct HeadingElem { diff --git a/crates/typst-macros/src/util.rs b/crates/typst-macros/src/util.rs index bfe222855d..b6acc7d81b 100644 --- a/crates/typst-macros/src/util.rs +++ b/crates/typst-macros/src/util.rs @@ -232,6 +232,7 @@ impl Parse for BlockWithReturn { } /// Parse a bare `type Name;` item. +#[allow(dead_code)] pub struct BareType { pub attrs: Vec, pub type_token: Token![type], diff --git a/crates/typst-pdf/Cargo.toml b/crates/typst-pdf/Cargo.toml index 709ed10880..99c52dc6ac 100644 --- a/crates/typst-pdf/Cargo.toml +++ b/crates/typst-pdf/Cargo.toml @@ -12,10 +12,6 @@ categories = { workspace = true } keywords = { workspace = true } readme = { workspace = true } -[lib] -doctest = false -bench = false - [dependencies] typst = { workspace = true } typst-assets = { workspace = true } diff --git a/crates/typst-pdf/src/lib.rs b/crates/typst-pdf/src/lib.rs index 49ec0d860e..e8b1c30a16 100644 --- a/crates/typst-pdf/src/lib.rs +++ b/crates/typst-pdf/src/lib.rs @@ -9,7 +9,6 @@ mod outline; mod page; mod pattern; -use std::cmp::Eq; use std::collections::{BTreeMap, HashMap, HashSet}; use std::hash::Hash; use std::sync::Arc; diff --git a/crates/typst-pdf/src/page.rs b/crates/typst-pdf/src/page.rs index 0342e9fe68..42358db516 100644 --- a/crates/typst-pdf/src/page.rs +++ b/crates/typst-pdf/src/page.rs @@ -229,17 +229,24 @@ fn write_page(ctx: &mut PdfContext, i: usize, resources_ref: Ref) { /// Write the page labels. pub(crate) fn write_page_labels(ctx: &mut PdfContext) -> Vec<(NonZeroUsize, Ref)> { + // If there is no page labeled, we skip the writing + if !ctx.pages.iter().any(|p| { + p.label + .as_ref() + .is_some_and(|l| l.prefix.is_some() || l.style.is_some()) + }) { + return Vec::new(); + } + let mut result = vec![]; + let empty_label = PdfPageLabel::default(); let mut prev: Option<&PdfPageLabel> = None; for (i, page) in ctx.pages.iter().enumerate() { let nr = NonZeroUsize::new(1 + i).unwrap(); - let Some(label) = &page.label else { continue }; - - // Don't create a label if neither style nor prefix are specified. - if label.prefix.is_none() && label.style.is_none() { - continue; - } + // If there are pages with empty labels between labeled pages, we must + // write empty PageLabel entries. + let label = page.label.as_ref().unwrap_or(&empty_label); if let Some(pre) = prev { if label.prefix == pre.prefix @@ -316,9 +323,7 @@ impl PdfPageLabel { return None; }; - let Some((prefix, kind, case)) = pat.pieces.first() else { - return None; - }; + let (prefix, kind, case) = pat.pieces.first()?; // If there is a suffix, we cannot use the common style optimisation, // since PDF does not provide a suffix field. @@ -372,7 +377,7 @@ pub struct EncodedPage { } /// Represents a resource being used in a PDF page by its name. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct PageResource { kind: ResourceKind, name: EcoString, @@ -385,7 +390,7 @@ impl PageResource { } /// A kind of resource being used in a PDF page. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub enum ResourceKind { XObject, Font, @@ -829,7 +834,7 @@ fn write_shape(ctx: &mut PageContext, pos: Point, shape: &Shape) { Geometry::Rect(size) => { let w = size.x.to_f32(); let h = size.y.to_f32(); - if w > 0.0 && h > 0.0 { + if w.abs() > f32::EPSILON && h.abs() > f32::EPSILON { ctx.content.rect(x, y, w, h); } } diff --git a/crates/typst-pdf/src/pattern.rs b/crates/typst-pdf/src/pattern.rs index 0829ef32db..5d5942bc96 100644 --- a/crates/typst-pdf/src/pattern.rs +++ b/crates/typst-pdf/src/pattern.rs @@ -73,7 +73,9 @@ pub(crate) fn write_patterns(ctx: &mut PdfContext) { resources_map.finish(); tiling_pattern .matrix(transform_to_array( - transform.pre_concat(Transform::scale(Ratio::one(), -Ratio::one())), + transform + .pre_concat(Transform::scale(Ratio::one(), -Ratio::one())) + .post_concat(Transform::translate(Abs::zero(), pattern.spacing().y)), )) .filter(Filter::FlateDecode); } @@ -82,7 +84,7 @@ pub(crate) fn write_patterns(ctx: &mut PdfContext) { /// A pattern and its transform. #[derive(Clone, PartialEq, Eq, Hash)] pub struct PdfPattern { - /// The transform to apply to the gradient. + /// The transform to apply to the pattern. pub transform: Transform, /// The pattern to paint. pub pattern: Pattern, @@ -116,13 +118,15 @@ fn register_pattern( // Render the body. let (_, content) = construct_page(ctx.parent, pattern.frame()); - let pdf_pattern = PdfPattern { + let mut pdf_pattern = PdfPattern { transform, pattern: pattern.clone(), content: content.content.wait().clone(), resources: content.resources.into_iter().collect(), }; + pdf_pattern.resources.sort(); + ctx.parent.pattern_map.insert(pdf_pattern) } diff --git a/crates/typst-render/Cargo.toml b/crates/typst-render/Cargo.toml index 2db9b5edae..cc58f785f9 100644 --- a/crates/typst-render/Cargo.toml +++ b/crates/typst-render/Cargo.toml @@ -12,10 +12,6 @@ categories = { workspace = true } keywords = { workspace = true } readme = { workspace = true } -[lib] -doctest = false -bench = false - [dependencies] typst = { workspace = true } typst-macros = { workspace = true } diff --git a/crates/typst-render/src/lib.rs b/crates/typst-render/src/lib.rs index 5d116e4958..28302180ad 100644 --- a/crates/typst-render/src/lib.rs +++ b/crates/typst-render/src/lib.rs @@ -42,13 +42,13 @@ pub fn render(frame: &Frame, pixel_per_pt: f32, fill: Color) -> sk::Pixmap { /// Export a document with potentially multiple pages into a single raster image. /// -/// The padding will be added around and between the individual frames. +/// The gap will be added between the individual frames. pub fn render_merged( document: &Document, pixel_per_pt: f32, frame_fill: Color, - padding: Abs, - padding_fill: Color, + gap: Abs, + gap_fill: Color, ) -> sk::Pixmap { let pixmaps: Vec<_> = document .pages @@ -56,19 +56,18 @@ pub fn render_merged( .map(|page| render(&page.frame, pixel_per_pt, frame_fill)) .collect(); - let padding = (pixel_per_pt * padding.to_f32()).round() as u32; - let pxw = - 2 * padding + pixmaps.iter().map(sk::Pixmap::width).max().unwrap_or_default(); - let pxh = - padding + pixmaps.iter().map(|pixmap| pixmap.height() + padding).sum::(); + let gap = (pixel_per_pt * gap.to_f32()).round() as u32; + let pxw = pixmaps.iter().map(sk::Pixmap::width).max().unwrap_or_default(); + let pxh = pixmaps.iter().map(|pixmap| pixmap.height()).sum::() + + gap * pixmaps.len().saturating_sub(1) as u32; let mut canvas = sk::Pixmap::new(pxw, pxh).unwrap(); - canvas.fill(to_sk_color(padding_fill)); + canvas.fill(to_sk_color(gap_fill)); - let [x, mut y] = [padding; 2]; + let mut y = 0; for pixmap in pixmaps { canvas.draw_pixmap( - x as i32, + 0, y as i32, pixmap.as_ref(), &sk::PixmapPaint::default(), @@ -76,7 +75,7 @@ pub fn render_merged( None, ); - y += pixmap.height() + padding; + y += pixmap.height() + gap; } canvas @@ -570,7 +569,18 @@ fn render_shape(canvas: &mut sk::Pixmap, state: State, shape: &Shape) -> Option< Geometry::Rect(size) => { let w = size.x.to_f32(); let h = size.y.to_f32(); - let rect = sk::Rect::from_xywh(0.0, 0.0, w, h)?; + let rect = if w < 0.0 || h < 0.0 { + // Skia doesn't normally allow for negative dimensions, but + // Typst supports them, so we apply a transform if needed + // Because this operation is expensive according to tiny-skia's + // docs, we prefer to not apply it if not needed + let transform = sk::Transform::from_scale(w.signum(), h.signum()); + let rect = sk::Rect::from_xywh(0.0, 0.0, w.abs(), h.abs())?; + rect.transform(transform)? + } else { + sk::Rect::from_xywh(0.0, 0.0, w, h)? + }; + sk::PathBuilder::from_rect(rect) } Geometry::Path(ref path) => convert_path(path)?, @@ -941,8 +951,10 @@ fn to_sk_paint<'a>( .container_transform .post_concat(state.transform.invert().unwrap()), }; - let width = (container_size.x.to_f32() * state.pixel_per_pt).ceil() as u32; - let height = (container_size.y.to_f32() * state.pixel_per_pt).ceil() as u32; + let width = + (container_size.x.to_f32().abs() * state.pixel_per_pt).ceil() as u32; + let height = + (container_size.y.to_f32().abs() * state.pixel_per_pt).ceil() as u32; *pixmap = Some(cached( gradient, @@ -958,8 +970,10 @@ fn to_sk_paint<'a>( sk::SpreadMode::Pad, sk::FilterQuality::Nearest, 1.0, - fill_transform - .pre_scale(1.0 / state.pixel_per_pt, 1.0 / state.pixel_per_pt), + fill_transform.pre_scale( + container_size.x.signum() as f32 / state.pixel_per_pt, + container_size.y.signum() as f32 / state.pixel_per_pt, + ), ); sk_paint.anti_alias = gradient.anti_alias(); @@ -1080,7 +1094,7 @@ impl OutlineBuilder for WrappedPathBuilder { } } -/// Additional methods for [`Length`]. +/// Additional methods for [`Abs`]. trait AbsExt { /// Convert to a number of points as f32. fn to_f32(self) -> f32; diff --git a/crates/typst-svg/Cargo.toml b/crates/typst-svg/Cargo.toml index 143e88ed77..df49a2b171 100644 --- a/crates/typst-svg/Cargo.toml +++ b/crates/typst-svg/Cargo.toml @@ -12,10 +12,6 @@ categories = { workspace = true } keywords = { workspace = true } readme = { workspace = true } -[lib] -doctest = false -bench = false - [dependencies] typst = { workspace = true } typst-macros = { workspace = true } diff --git a/crates/typst-syntax/Cargo.toml b/crates/typst-syntax/Cargo.toml index f92b6d9b9e..001d405c48 100644 --- a/crates/typst-syntax/Cargo.toml +++ b/crates/typst-syntax/Cargo.toml @@ -12,10 +12,6 @@ categories = { workspace = true } keywords = { workspace = true } readme = { workspace = true } -[lib] -doctest = false -bench = false - [dependencies] comemo = { workspace = true } ecow = { workspace = true } diff --git a/crates/typst-syntax/src/kind.rs b/crates/typst-syntax/src/kind.rs index c34f600292..c84e535838 100644 --- a/crates/typst-syntax/src/kind.rs +++ b/crates/typst-syntax/src/kind.rs @@ -32,7 +32,7 @@ pub enum SyntaxKind { RawLang, /// A raw delimiter consisting of 1 or 3+ backticks: `` ` ``. RawDelim, - /// A sequence of whitespace to ignore in a raw block: ` `. + /// A sequence of whitespace to ignore in a raw text: ` `. RawTrimmed, /// A hyperlink: `https://typst.org`. Link, diff --git a/crates/typst-syntax/src/lexer.rs b/crates/typst-syntax/src/lexer.rs index aacbee62ec..6e64beff30 100644 --- a/crates/typst-syntax/src/lexer.rs +++ b/crates/typst-syntax/src/lexer.rs @@ -88,8 +88,10 @@ impl Lexer<'_> { } } -/// Shared. +/// Shared methods with all [`LexMode`]. impl Lexer<'_> { + /// Proceed to the next token and return its [`SyntaxKind`]. Note the + /// token could be a [trivia](SyntaxKind::is_trivia). pub fn next(&mut self) -> SyntaxKind { if self.mode == LexMode::Raw { let Some((kind, end)) = self.raw.pop() else { @@ -121,6 +123,7 @@ impl Lexer<'_> { } } + /// Eat whitespace characters greedily. fn whitespace(&mut self, start: usize, c: char) -> SyntaxKind { let more = self.s.eat_while(|c| is_space(c, self.mode)); let newlines = match c { @@ -272,10 +275,7 @@ impl Lexer<'_> { if backticks >= 3 { self.blocky_raw(start, end, backticks); } else { - // Single backtick needs no trimming or extra fancyness. - self.s.jump(end - backticks); - self.push_raw(SyntaxKind::Text); - self.s.jump(end); + self.inline_raw(start, end, backticks); } // Closing delimiter. @@ -297,16 +297,12 @@ impl Lexer<'_> { self.push_raw(SyntaxKind::RawLang); } - // Determine inner content between backticks and with trimmed - // single spaces (line trimming comes later). + // Determine inner content between backticks. self.s.eat_if(' '); - let mut inner = self.s.to(end - backticks); - if inner.trim_end().ends_with('`') { - inner = inner.strip_suffix(' ').unwrap_or(inner); - } + let inner = self.s.to(end - backticks); // Determine dedent level. - let lines = split_newlines(inner); + let mut lines = split_newlines(inner); let dedent = lines .iter() .skip(1) @@ -317,6 +313,15 @@ impl Lexer<'_> { .min() .unwrap_or(0); + // Trim single space in last line if text ends with a backtick. The last + // line is the one directly before the closing backticks and if it is + // just whitespace, it will be completely trimmed below. + if inner.trim_end().ends_with('`') { + if let Some(last) = lines.last_mut() { + *last = last.strip_suffix(' ').unwrap_or(last); + } + } + let is_whitespace = |line: &&str| line.chars().all(char::is_whitespace); let starts_whitespace = lines.first().is_some_and(is_whitespace); let ends_whitespace = lines.last().is_some_and(is_whitespace); @@ -353,6 +358,25 @@ impl Lexer<'_> { self.s.jump(end); } + fn inline_raw(&mut self, start: usize, end: usize, backticks: usize) { + self.s.jump(start + backticks); + + while self.s.cursor() < end - backticks { + if self.s.at(is_newline) { + self.push_raw(SyntaxKind::Text); + self.s.eat_newline(); + self.push_raw(SyntaxKind::RawTrimmed); + continue; + } + self.s.eat(); + } + self.push_raw(SyntaxKind::Text); + + self.s.jump(end); + } + + /// Push the current cursor that marks the end of a raw segment of + /// the given `kind`. fn push_raw(&mut self, kind: SyntaxKind) { let end = self.s.cursor(); self.raw.push((kind, end)); @@ -760,7 +784,7 @@ impl ScannerExt for Scanner<'_> { } } -/// Whether a character will become a Space token in Typst +/// Whether a character will become a [`SyntaxKind::Space`] token. #[inline] fn is_space(character: char, mode: LexMode) -> bool { match mode { @@ -818,7 +842,7 @@ pub fn link_prefix(text: &str) -> (&str, bool) { (s.before(), brackets.is_empty()) } -/// Split text at newlines. +/// Split text at newlines. These newline characters are not kept. pub fn split_newlines(text: &str) -> Vec<&str> { let mut s = Scanner::new(text); let mut lines = Vec::new(); diff --git a/crates/typst-syntax/src/parser.rs b/crates/typst-syntax/src/parser.rs index 50032898e8..82a4c729f3 100644 --- a/crates/typst-syntax/src/parser.rs +++ b/crates/typst-syntax/src/parser.rs @@ -592,10 +592,23 @@ fn math_args(p: &mut Parser) { p.wrap(m, SyntaxKind::Args); } +/// Wrap math function arguments in a "Math" SyntaxKind to combine adjacent expressions +/// or create blank content. +/// +/// We don't wrap when `exprs == 1`, as there is only one expression, so the grouping +/// isn't needed, and this would change the type of the expression from potentially +/// non-content to content. +/// +/// Note that `exprs` might be 0 if we have whitespace or trivia before a comma i.e. +/// `mat(; ,)` or `sin(x, , , ,)`. This would create an empty Math element before that +/// trivia if we called `p.wrap()` -- breaking the expected AST for 2-d arguments -- so +/// we instead manually wrap to our current marker using `p.wrap_within()`. fn maybe_wrap_in_math(p: &mut Parser, arg: Marker, named: Option) { let exprs = p.post_process(arg).filter(|node| node.is::()).count(); if exprs != 1 { - p.wrap(arg, SyntaxKind::Math); + // Convert 0 exprs into a blank math element (so empty arguments are allowed). + // Convert 2+ exprs into a math element (so they become a joined sequence). + p.wrap_within(arg, p.marker(), SyntaxKind::Math); } if let Some(m) = named { @@ -1748,15 +1761,27 @@ impl<'s> Parser<'s> { } } + fn next_non_trivia(lexer: &mut Lexer<'s>) -> SyntaxKind { + loop { + let next = lexer.next(); + // Loop is terminatable, because SyntaxKind::Eof is not a trivia. + if !next.is_trivia() { + break next; + } + } + } + fn lex(&mut self) { self.current_start = self.lexer.cursor(); self.current = self.lexer.next(); + + // Special cases to handle newlines in code mode. if self.lexer.mode() == LexMode::Code && self.lexer.newline() && match self.newline_modes.last() { Some(NewlineMode::Continue) => false, Some(NewlineMode::Contextual) => !matches!( - self.lexer.clone().next(), + Self::next_non_trivia(&mut self.lexer.clone()), SyntaxKind::Else | SyntaxKind::Dot ), Some(NewlineMode::Stop) => true, diff --git a/crates/typst-syntax/src/source.rs b/crates/typst-syntax/src/source.rs index b4a80d3188..a68a53da34 100644 --- a/crates/typst-syntax/src/source.rs +++ b/crates/typst-syntax/src/source.rs @@ -2,6 +2,7 @@ use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; +use std::iter::zip; use std::ops::Range; use std::sync::Arc; @@ -76,12 +77,8 @@ impl Source { pub fn replace(&mut self, new: &str) -> Range { let old = self.text(); - let mut prefix = old - .as_bytes() - .iter() - .zip(new.as_bytes()) - .take_while(|(x, y)| x == y) - .count(); + let mut prefix = + zip(old.bytes(), new.bytes()).take_while(|(x, y)| x == y).count(); if prefix == old.len() && prefix == new.len() { return 0..0; @@ -91,11 +88,7 @@ impl Source { prefix -= 1; } - let mut suffix = old[prefix..] - .as_bytes() - .iter() - .zip(new[prefix..].as_bytes()) - .rev() + let mut suffix = zip(old[prefix..].bytes().rev(), new[prefix..].bytes().rev()) .take_while(|(x, y)| x == y) .count(); diff --git a/crates/typst/Cargo.toml b/crates/typst/Cargo.toml index 53a28ffff3..a2828fa2f4 100644 --- a/crates/typst/Cargo.toml +++ b/crates/typst/Cargo.toml @@ -11,10 +11,6 @@ homepage = { workspace = true } repository = { workspace = true } license = { workspace = true } -[lib] -doctest = false -bench = false - [dependencies] typst-macros = { workspace = true } typst-syntax = { workspace = true } diff --git a/crates/typst/src/diag.rs b/crates/typst/src/diag.rs index c6cd6acb46..6e119591a5 100644 --- a/crates/typst/src/diag.rs +++ b/crates/typst/src/diag.rs @@ -21,7 +21,7 @@ use crate::{World, WorldExt}; /// /// You can also emit hints with the `; hint: "..."` syntax. /// -/// ``` +/// ```ignore /// bail!("bailing with a {}", "string result"); /// bail!(span, "bailing with a {}", "source result"); /// bail!( @@ -81,7 +81,7 @@ macro_rules! __error { /// /// You can also emit hints with the `; hint: "..."` syntax. /// -/// ``` +/// ```ignore /// warning!(span, "warning with a {}", "source result"); /// warning!( /// span, "warning with a {}", "source result"; diff --git a/crates/typst/src/foundations/cast.rs b/crates/typst/src/foundations/cast.rs index 3b74f1fb17..b29e447322 100644 --- a/crates/typst/src/foundations/cast.rs +++ b/crates/typst/src/foundations/cast.rs @@ -44,9 +44,9 @@ pub trait Reflect { /// Produce an error message for an inacceptable value type. /// - /// ``` + /// ```ignore /// assert_eq!( - /// ::error(&Value::None), + /// ::error(&Value::None), /// "expected integer, found none", /// ); /// ``` diff --git a/crates/typst/src/foundations/int.rs b/crates/typst/src/foundations/int.rs index ef21f7853a..7b6c02638f 100644 --- a/crates/typst/src/foundations/int.rs +++ b/crates/typst/src/foundations/int.rs @@ -261,7 +261,14 @@ macro_rules! unsigned_int { ($($ty:ty)*) => { $(cast! { $ty, - self => Value::Int(self as _), + self => if let Ok(int) = i64::try_from(self) { + Value::Int(int) + } else { + // Some u64 are too large to be cast as i64 + // In that case, we accept that there may be a + // precision loss, and use a floating point number + Value::Float(self as _) + }, v: i64 => v.try_into().map_err(|_| { if v < 0 { "number must be at least zero" diff --git a/crates/typst/src/foundations/selector.rs b/crates/typst/src/foundations/selector.rs index 4dc8ab3b9d..90663500e9 100644 --- a/crates/typst/src/foundations/selector.rs +++ b/crates/typst/src/foundations/selector.rs @@ -15,10 +15,6 @@ use crate::symbols::Symbol; use crate::text::TextElem; /// A helper macro to create a field selector used in [`Selector::Elem`] -/// -/// ```ignore -/// select_where!(SequenceElem, Children => vec![]); -/// ``` #[macro_export] #[doc(hidden)] macro_rules! __select_where { diff --git a/crates/typst/src/foundations/str.rs b/crates/typst/src/foundations/str.rs index 897ca45b39..515a4e2102 100644 --- a/crates/typst/src/foundations/str.rs +++ b/crates/typst/src/foundations/str.rs @@ -491,11 +491,11 @@ impl Str { #[func] pub fn trim( &self, - /// The pattern to search for. + /// The pattern to search for. If `{none}`, trims white spaces. #[default] pattern: Option, - /// Can be `start` or `end` to only trim the start or end of the string. - /// If omitted, both sides are trimmed. + /// Can be `{start}` or `{end}` to only trim the start or end of the + /// string. If omitted, both sides are trimmed. #[named] at: Option, /// Whether to repeatedly removes matches of the pattern or just once. @@ -535,16 +535,16 @@ impl Str { } Some(StrPattern::Regex(re)) => { let s = self.as_str(); - let mut last = 0; + let mut last = None; let mut range = 0..s.len(); for m in re.find_iter(s) { // Does this match follow directly after the last one? - let consecutive = last == m.start(); + let consecutive = last == Some(m.start()); - // As long as we're consecutive and still trimming at the - // start, trim. - start &= consecutive; + // As long as we're at the beginning or in a consecutive run + // of matches, and we're still trimming at the start, trim. + start &= m.start() == 0 || consecutive; if start { range.start = m.end(); start &= repeat; @@ -556,11 +556,11 @@ impl Str { range.end = m.start(); } - last = m.end(); + last = Some(m.end()); } // Is the last match directly at the end? - if last < s.len() { + if last.is_some_and(|last| last < s.len()) { range.end = s.len(); } diff --git a/crates/typst/src/foundations/styles.rs b/crates/typst/src/foundations/styles.rs index dd09b60562..35460088c9 100644 --- a/crates/typst/src/foundations/styles.rs +++ b/crates/typst/src/foundations/styles.rs @@ -321,9 +321,6 @@ trait Blockable: Debug + Send + Sync + 'static { /// Equivalent to `downcast_ref` for the block. fn as_any(&self) -> &dyn Any; - /// Equivalent to `downcast_mut` for the block. - fn as_any_mut(&mut self) -> &mut dyn Any; - /// Equivalent to [`Hash`] for the block. fn dyn_hash(&self, state: &mut dyn Hasher); @@ -336,10 +333,6 @@ impl Blockable for T { self } - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } - fn dyn_hash(&self, mut state: &mut dyn Hasher) { // Also hash the TypeId since values with different types but // equal data should be different. diff --git a/crates/typst/src/introspection/here.rs b/crates/typst/src/introspection/here.rs index e921079b94..9d6133816e 100644 --- a/crates/typst/src/introspection/here.rs +++ b/crates/typst/src/introspection/here.rs @@ -20,8 +20,8 @@ use crate::introspection::Location; /// instance, have been reset after a preface. /// /// # Examples -/// Determining the current position in the document in combination with -/// [`locate`]: +/// Determining the current position in the document in combination with the +/// [`position`]($location.position) method: /// ```example /// #context [ /// I am located at diff --git a/crates/typst/src/introspection/metadata.rs b/crates/typst/src/introspection/metadata.rs index 2d6d8953fa..610c238e93 100644 --- a/crates/typst/src/introspection/metadata.rs +++ b/crates/typst/src/introspection/metadata.rs @@ -7,7 +7,8 @@ use crate::realize::{Behave, Behaviour}; /// Exposes a value to the query system without producing visible content. /// /// This element can be retrieved with the [`query`] function and from the -/// command line with [`typst query`]($reference/meta/query/#cli-queries). Its +/// command line with +/// [`typst query`]($reference/introspection/query/#command-line-queries). Its /// purpose is to expose an arbitrary value to the introspection system. To /// identify a metadata value among others, you can attach a [`label`] to it and /// query for that label. diff --git a/crates/typst/src/layout/abs.rs b/crates/typst/src/layout/abs.rs index b191679540..5c07c5a060 100644 --- a/crates/typst/src/layout/abs.rs +++ b/crates/typst/src/layout/abs.rs @@ -117,6 +117,11 @@ impl Abs { pub fn approx_eq(self, other: Self) -> bool { self == other || (self - other).to_raw().abs() < 1e-6 } + + /// Returns a number that represent the sign of this length + pub fn signum(self) -> f64 { + self.0.get().signum() + } } impl Numeric for Abs { diff --git a/crates/typst/src/layout/flow.rs b/crates/typst/src/layout/flow.rs index 8f0def1791..3cc53c6a4d 100644 --- a/crates/typst/src/layout/flow.rs +++ b/crates/typst/src/layout/flow.rs @@ -141,15 +141,6 @@ enum FlowItem { } impl FlowItem { - /// The inherent height of the item. - fn height(&self) -> Abs { - match self { - Self::Absolute(v, _) => *v, - Self::Fractional(_) | Self::Placed { .. } => Abs::zero(), - Self::Frame { frame, .. } | Self::Footnote(frame) => frame.height(), - } - } - /// Whether this item is out-of-flow. /// /// Out-of-flow items are guaranteed to have a [`Size::zero()`]. @@ -411,12 +402,16 @@ impl<'a> FlowLayouter<'a> { self.finish_region(engine, false)?; } + let in_last = self.regions.in_last(); self.regions.size.y -= height; if self.root && movable { let mut notes = Vec::new(); find_footnotes(&mut notes, frame); self.items.push(item); - if !self.handle_footnotes(engine, &mut notes, true, false)? { + + // When we are already in_last, we can directly force the + // footnotes. + if !self.handle_footnotes(engine, &mut notes, true, in_last)? { let item = self.items.pop(); self.finish_region(engine, false)?; self.items.extend(item); @@ -651,7 +646,16 @@ impl FlowLayouter<'_> { engine: &mut Engine, mut notes: Vec>, ) -> SourceResult<()> { - if self.root && !self.handle_footnotes(engine, &mut notes, false, false)? { + // When we are already in_last, we can directly force the + // footnotes. + if self.root + && !self.handle_footnotes( + engine, + &mut notes, + false, + self.regions.in_last(), + )? + { self.finish_region(engine, false)?; self.handle_footnotes(engine, &mut notes, false, true)?; } @@ -666,8 +670,11 @@ impl FlowLayouter<'_> { movable: bool, force: bool, ) -> SourceResult { - let items_len = self.items.len(); - let notes_len = notes.len(); + let prev_notes_len = notes.len(); + let prev_items_len = self.items.len(); + let prev_size = self.regions.size; + let prev_has_footnotes = self.has_footnotes; + let prev_locator = engine.locator.clone(); // Process footnotes one at a time. let mut k = 0; @@ -682,7 +689,6 @@ impl FlowLayouter<'_> { } self.regions.size.y -= self.footnote_config.gap; - let checkpoint = engine.locator.clone(); let frames = FootnoteEntry::new(notes[k].clone()) .pack() .layout(engine, self.styles, self.regions.with_root(false))? @@ -694,18 +700,12 @@ impl FlowLayouter<'_> { && (k == 0 || movable) && frames.first().is_some_and(Frame::is_empty) { - // Remove existing footnotes attempts because we need to - // move the item to the next page. - notes.truncate(notes_len); - - // Undo region modifications. - for item in self.items.drain(items_len..) { - self.regions.size.y -= item.height(); - } - - // Undo locator modifications. - *engine.locator = checkpoint; - + // Undo everything. + notes.truncate(prev_notes_len); + self.items.truncate(prev_items_len); + self.regions.size = prev_size; + self.has_footnotes = prev_has_footnotes; + *engine.locator = prev_locator; return Ok(false); } diff --git a/crates/typst/src/layout/grid/layout.rs b/crates/typst/src/layout/grid/layout.rs index 6dbe151e6f..398ad1bdc1 100644 --- a/crates/typst/src/layout/grid/layout.rs +++ b/crates/typst/src/layout/grid/layout.rs @@ -2692,6 +2692,10 @@ impl<'a> GridLayouter<'a> { height: Abs, y: usize, ) -> SourceResult { + if !self.width.is_finite() { + bail!(self.span, "cannot create grid with infinite width"); + } + if !height.is_finite() { bail!(self.span, "cannot create grid with infinite height"); } diff --git a/crates/typst/src/layout/grid/lines.rs b/crates/typst/src/layout/grid/lines.rs index c976da2d75..7ea021d794 100644 --- a/crates/typst/src/layout/grid/lines.rs +++ b/crates/typst/src/layout/grid/lines.rs @@ -597,7 +597,7 @@ pub(super) fn hline_stroke_at_column( #[cfg(test)] mod test { - use super::super::layout::{Entry, RowPiece}; + use super::super::layout::Entry; use super::*; use crate::foundations::Content; use crate::layout::{Axes, Cell, Sides, Sizing}; diff --git a/crates/typst/src/layout/inline/mod.rs b/crates/typst/src/layout/inline/mod.rs index 16da1539fd..4fe831e288 100644 --- a/crates/typst/src/layout/inline/mod.rs +++ b/crates/typst/src/layout/inline/mod.rs @@ -73,7 +73,8 @@ pub(crate) fn layout_inline( let lines = linebreak(&engine, &p, region.x - p.hang); // Stack the lines into one frame per region. - finalize(&mut engine, &p, &lines, region, expand) + let shrink = ParElem::shrink_in(styles); + finalize(&mut engine, &p, &lines, region, expand, shrink) } let fragment = cached( @@ -101,6 +102,13 @@ type Range = std::ops::Range; const SPACING_REPLACE: char = ' '; // Space const OBJ_REPLACE: char = '\u{FFFC}'; // Object Replacement Character +// Unicode BiDi control characters. +const LTR_EMBEDDING: char = '\u{202A}'; +const RTL_EMBEDDING: char = '\u{202B}'; +const POP_EMBEDDING: char = '\u{202C}'; +const LTR_ISOLATE: char = '\u{2066}'; +const POP_ISOLATE: char = '\u{2069}'; + /// A paragraph representation in which children are already layouted and text /// is already preshaped. /// @@ -189,7 +197,7 @@ enum Segment<'a> { /// Horizontal spacing between other segments. Spacing(Spacing), /// A mathematical equation. - Equation(&'a Packed, Vec), + Equation(Vec), /// A box with arbitrary content. Box(&'a Packed, bool), /// Metadata. @@ -205,9 +213,12 @@ impl Segment<'_> { Self::Box(_, frac) => { (if frac { SPACING_REPLACE } else { OBJ_REPLACE }).len_utf8() } - Self::Equation(_, ref par_items) => { - par_items.iter().map(MathParItem::text).map(char::len_utf8).sum() - } + Self::Equation(ref par_items) => par_items + .iter() + .map(MathParItem::text) + .chain([LTR_ISOLATE, POP_ISOLATE]) + .map(char::len_utf8) + .sum(), Self::Meta => 0, } } @@ -226,6 +237,9 @@ enum Item<'a> { Frame(Frame), /// Metadata. Meta(Frame), + /// An item that is invisible and needs to be skipped, e.g. a Unicode + /// isolate. + Skip(char), } impl<'a> Item<'a> { @@ -252,6 +266,7 @@ impl<'a> Item<'a> { Self::Absolute(_) | Self::Fractional(_, _) => SPACING_REPLACE.len_utf8(), Self::Frame(_) => OBJ_REPLACE.len_utf8(), Self::Meta(_) => 0, + Self::Skip(c) => c.len_utf8(), } } @@ -262,6 +277,7 @@ impl<'a> Item<'a> { Self::Absolute(v) => *v, Self::Frame(frame) => frame.width(), Self::Fractional(_, _) | Self::Meta(_) => Abs::zero(), + Self::Skip(_) => Abs::zero(), } } } @@ -449,10 +465,10 @@ fn collect<'a>( let prev = full.len(); let dir = TextElem::dir_in(styles); if dir != outer_dir { - // Insert "Explicit Directional Isolate". + // Insert "Explicit Directional Embedding". match dir { - Dir::LTR => full.push('\u{2066}'), - Dir::RTL => full.push('\u{2067}'), + Dir::LTR => full.push(LTR_EMBEDDING), + Dir::RTL => full.push(RTL_EMBEDDING), _ => {} } } @@ -464,8 +480,8 @@ fn collect<'a>( } if dir != outer_dir { - // Insert "Pop Directional Isolate". - full.push('\u{2069}'); + // Insert "Pop Directional Formatting". + full.push(POP_EMBEDDING); } Segment::Text(full.len() - prev) } else if let Some(elem) = child.to_packed::() { @@ -520,8 +536,10 @@ fn collect<'a>( let MathParItem::Frame(frame) = item else { continue }; frame.meta(styles, false); } + full.push(LTR_ISOLATE); full.extend(items.iter().map(MathParItem::text)); - Segment::Equation(elem, items) + full.push(POP_ISOLATE); + Segment::Equation(items) } else if let Some(elem) = child.to_packed::() { let frac = elem.width(styles).is_fractional(); full.push(if frac { SPACING_REPLACE } else { OBJ_REPLACE }); @@ -592,7 +610,8 @@ fn prepare<'a>( items.push(Item::Fractional(v, None)); } }, - Segment::Equation(_, par_items) => { + Segment::Equation(par_items) => { + items.push(Item::Skip(LTR_ISOLATE)); for item in par_items { match item { MathParItem::Space(s) => items.push(Item::Absolute(s)), @@ -602,6 +621,7 @@ fn prepare<'a>( } } } + items.push(Item::Skip(POP_ISOLATE)); } Segment::Box(elem, _) => { if let Sizing::Fr(v) = elem.width(styles) { @@ -1191,6 +1211,7 @@ fn finalize( lines: &[Line], region: Size, expand: bool, + shrink: bool, ) -> SourceResult { // Determine the paragraph's width: Full width of the region if we // should expand or there's fractional spacing, fit-to-width otherwise. @@ -1207,7 +1228,7 @@ fn finalize( // Stack the lines into one frame per region. let mut frames: Vec = lines .iter() - .map(|line| commit(engine, p, line, width, region.y)) + .map(|line| commit(engine, p, line, width, region.y, shrink)) .collect::>()?; // Prevent orphans. @@ -1243,6 +1264,7 @@ fn commit( line: &Line, width: Abs, full: Abs, + shrink: bool, ) -> SourceResult { let mut remaining = width - line.width - p.hang; let mut offset = Abs::zero(); @@ -1289,12 +1311,12 @@ fn commit( let mut justification_ratio = 0.0; let mut extra_justification = Abs::zero(); - let shrink = line.shrinkability(); + let shrinkability = line.shrinkability(); let stretch = line.stretchability(); - if remaining < Abs::zero() && shrink > Abs::zero() { + if remaining < Abs::zero() && shrinkability > Abs::zero() && shrink { // Attempt to reduce the length of the line, using shrinkability. - justification_ratio = (remaining / shrink).max(-1.0); - remaining = (remaining + shrink).min(Abs::zero()); + justification_ratio = (remaining / shrinkability).max(-1.0); + remaining = (remaining + shrinkability).min(Abs::zero()); } else if line.justify && fr.is_zero() { // Attempt to increase the length of the line, using stretchability. if stretch > Abs::zero() { @@ -1350,6 +1372,7 @@ fn commit( Item::Frame(frame) | Item::Meta(frame) => { push(&mut offset, frame.clone()); } + Item::Skip(_) => {} } } @@ -1415,7 +1438,7 @@ fn reorder<'a>(line: &'a Line<'a>) -> (Vec<&Item<'a>>, bool) { /// How much a character should hang into the end margin. /// /// For more discussion, see: -/// https://recoveringphysicist.com/21/ +/// fn overhang(c: char) -> f64 { match c { // Dashes. diff --git a/crates/typst/src/layout/inline/shaping.rs b/crates/typst/src/layout/inline/shaping.rs index c346233e30..82a33f9b09 100644 --- a/crates/typst/src/layout/inline/shaping.rs +++ b/crates/typst/src/layout/inline/shaping.rs @@ -189,14 +189,12 @@ impl ShapedGlyph { self.x_offset -= amount; self.x_advance -= amount; self.adjustability.shrinkability.0 -= amount; - self.adjustability.stretchability.0 += amount; } /// Shrink the width of glyph on the right side. pub fn shrink_right(&mut self, amount: Em) { self.x_advance -= amount; self.adjustability.shrinkability.1 -= amount; - self.adjustability.stretchability.1 += amount; } } diff --git a/crates/typst/src/layout/place.rs b/crates/typst/src/layout/place.rs index 176eaf472c..7d41b03760 100644 --- a/crates/typst/src/layout/place.rs +++ b/crates/typst/src/layout/place.rs @@ -30,8 +30,8 @@ use crate::realize::{Behave, Behaviour}; pub struct PlaceElem { /// Relative to which position in the parent container to place the content. /// - /// Cannot be `{auto}` if `float` is `{false}` and must be either - /// `{auto}`, `{top}`, or `{bottom}` if `float` is `{true}`. + /// - If `float` is `{false}`, then this can be any alignment other than `{auto}`. + /// - If `float` is `{true}`, then this must be `{auto}`, `{top}`, or `{bottom}`. /// /// When an axis of the page is `{auto}` sized, all alignments relative to /// that axis will be ignored, instead, the item will be placed in the @@ -77,9 +77,17 @@ pub struct PlaceElem { /// place(center, dx: amount - 32pt, dy: amount)[A] /// } /// ``` + /// + /// This does not affect the layout of in-flow content. + /// In other words, the placed content is treated as if it + /// were wrapped in a [`move`] element. pub dx: Rel, /// The vertical displacement of the placed content. + /// + /// This does not affect the layout of in-flow content. + /// In other words, the placed content is treated as if it + /// were wrapped in a [`move`] element. pub dy: Rel, /// The content to place. diff --git a/crates/typst/src/layout/stack.rs b/crates/typst/src/layout/stack.rs index caa78264ab..05b415faf8 100644 --- a/crates/typst/src/layout/stack.rs +++ b/crates/typst/src/layout/stack.rs @@ -1,6 +1,7 @@ use std::fmt::{self, Debug, Formatter}; +use typst_syntax::Span; -use crate::diag::SourceResult; +use crate::diag::{bail, SourceResult}; use crate::engine::Engine; use crate::foundations::{cast, elem, Content, Packed, Resolve, StyleChain, StyledElem}; use crate::layout::{ @@ -59,7 +60,8 @@ impl LayoutMultiple for Packed { styles: StyleChain, regions: Regions, ) -> SourceResult { - let mut layouter = StackLayouter::new(self.dir(styles), regions, styles); + let mut layouter = + StackLayouter::new(self.span(), self.dir(styles), regions, styles); let axis = layouter.dir.axis(); // Spacing to insert before the next block. @@ -97,7 +99,7 @@ impl LayoutMultiple for Packed { } } - Ok(layouter.finish()) + layouter.finish() } } @@ -131,6 +133,8 @@ cast! { /// Performs stack layout. struct StackLayouter<'a> { + /// The span to raise errors at during layout. + span: Span, /// The stacking direction. dir: Dir, /// The axis of the stacking direction. @@ -166,7 +170,12 @@ enum StackItem { impl<'a> StackLayouter<'a> { /// Create a new stack layouter. - fn new(dir: Dir, mut regions: Regions<'a>, styles: StyleChain<'a>) -> Self { + fn new( + span: Span, + dir: Dir, + mut regions: Regions<'a>, + styles: StyleChain<'a>, + ) -> Self { let axis = dir.axis(); let expand = regions.expand; @@ -174,6 +183,7 @@ impl<'a> StackLayouter<'a> { regions.expand.set(axis, false); Self { + span, dir, axis, regions, @@ -218,7 +228,7 @@ impl<'a> StackLayouter<'a> { styles: StyleChain, ) -> SourceResult<()> { if self.regions.is_full() { - self.finish_region(); + self.finish_region()?; } // Block-axis alignment of the `AlignElement` is respected by stacks. @@ -251,7 +261,7 @@ impl<'a> StackLayouter<'a> { self.items.push(StackItem::Frame(frame, align)); if i + 1 < len { - self.finish_region(); + self.finish_region()?; } } @@ -259,7 +269,7 @@ impl<'a> StackLayouter<'a> { } /// Advance to the next region. - fn finish_region(&mut self) { + fn finish_region(&mut self) -> SourceResult<()> { // Determine the size of the stack in this region depending on whether // the region expands. let mut size = self @@ -275,6 +285,10 @@ impl<'a> StackLayouter<'a> { size.set(self.axis, full); } + if !size.is_finite() { + bail!(self.span, "stack spacing is infinite"); + } + let mut output = Frame::hard(size); let mut cursor = Abs::zero(); let mut ruler: FixedAlignment = self.dir.start().into(); @@ -320,12 +334,14 @@ impl<'a> StackLayouter<'a> { self.used = Gen::zero(); self.fr = Fr::zero(); self.finished.push(output); + + Ok(()) } /// Finish layouting and return the resulting frames. - fn finish(mut self) -> Fragment { - self.finish_region(); - Fragment::frames(self.finished) + fn finish(mut self) -> SourceResult { + self.finish_region()?; + Ok(Fragment::frames(self.finished)) } } diff --git a/crates/typst/src/loading/cbor.rs b/crates/typst/src/loading/cbor.rs index ddf559f33f..bce0927122 100644 --- a/crates/typst/src/loading/cbor.rs +++ b/crates/typst/src/loading/cbor.rs @@ -14,6 +14,9 @@ use crate::World; /// equivalents, null-values (`null`, `~` or empty ``) will be converted into /// `{none}`, and numbers will be converted to floats or integers depending on /// whether they are whole numbers. +/// +/// Be aware that integers larger than 263-1 will be converted to +/// floating point numbers, which may result in an approximative value. #[func(scope, title = "CBOR")] pub fn cbor( /// The engine. diff --git a/crates/typst/src/loading/json.rs b/crates/typst/src/loading/json.rs index aae1f4ad5b..8e829594b0 100644 --- a/crates/typst/src/loading/json.rs +++ b/crates/typst/src/loading/json.rs @@ -9,13 +9,18 @@ use crate::World; /// Reads structured data from a JSON file. /// -/// The file must contain a valid JSON object or array. JSON objects will be -/// converted into Typst dictionaries, and JSON arrays will be converted into -/// Typst arrays. Strings and booleans will be converted into the Typst -/// equivalents, `null` will be converted into `{none}`, and numbers will be -/// converted to floats or integers depending on whether they are whole numbers. +/// The file must contain a valid JSON value, such as object or array. JSON +/// objects will be converted into Typst dictionaries, and JSON arrays will be +/// converted into Typst arrays. Strings and booleans will be converted into the +/// Typst equivalents, `null` will be converted into `{none}`, and numbers will +/// be converted to floats or integers depending on whether they are whole +/// numbers. /// -/// The function returns a dictionary or an array, depending on the JSON file. +/// Be aware that integers larger than 263-1 will be converted to +/// floating point numbers, which may result in an approximative value. +/// +/// The function returns a dictionary, an array or, depending on the JSON file, +/// another JSON data type. /// /// The JSON files in the example contain objects with the keys `temperature`, /// `unit`, and `weather`. diff --git a/crates/typst/src/loading/yaml.rs b/crates/typst/src/loading/yaml.rs index 501e306665..d578eda4cd 100644 --- a/crates/typst/src/loading/yaml.rs +++ b/crates/typst/src/loading/yaml.rs @@ -17,6 +17,9 @@ use crate::World; /// whether they are whole numbers. Custom YAML tags are ignored, though the /// loaded value will still be present. /// +/// Be aware that integers larger than 263-1 will be converted to +/// floating point numbers, which may give an approximative value. +/// /// The YAML files in the example contain objects with authors as keys, /// each with a sequence of their own submapping with the keys /// "title" and "published" diff --git a/crates/typst/src/math/equation.rs b/crates/typst/src/math/equation.rs index c5cb91322e..4a5d88e21b 100644 --- a/crates/typst/src/math/equation.rs +++ b/crates/typst/src/math/equation.rs @@ -224,6 +224,12 @@ impl Packed { vec![MathParItem::Frame(run.into_fragment(&ctx, styles).into_frame())] }; + // An empty equation should have a height, so we still create a frame + // (which is then resized in the loop). + if items.is_empty() { + items.push(MathParItem::Frame(Frame::soft(Size::zero()))); + } + for item in &mut items { let MathParItem::Frame(frame) = item else { continue }; diff --git a/crates/typst/src/math/lr.rs b/crates/typst/src/math/lr.rs index f92afbd570..671aa7df98 100644 --- a/crates/typst/src/math/lr.rs +++ b/crates/typst/src/math/lr.rs @@ -122,10 +122,12 @@ impl LayoutMath for Packed { MathFragment::Glyph(glyph) => { let mut new = glyph.clone().into_variant(); new.mid_stretched = Some(false); + new.class = MathClass::Fence; *fragment = MathFragment::Variant(new); } MathFragment::Variant(variant) => { variant.mid_stretched = Some(false); + variant.class = MathClass::Fence; } _ => {} } diff --git a/crates/typst/src/math/matrix.rs b/crates/typst/src/math/matrix.rs index 83d50dd6cd..9d2f65b391 100644 --- a/crates/typst/src/math/matrix.rs +++ b/crates/typst/src/math/matrix.rs @@ -10,7 +10,8 @@ use crate::layout::{ }; use crate::math::{ alignments, scaled_font_size, stack, style_for_denominator, AlignmentResult, - FrameFragment, GlyphFragment, LayoutMath, MathContext, Scaled, DELIM_SHORT_FALL, + FrameFragment, GlyphFragment, LayoutMath, LeftRightAlternator, MathContext, Scaled, + DELIM_SHORT_FALL, }; use crate::syntax::{Span, Spanned}; use crate::text::TextElem; @@ -67,6 +68,7 @@ impl LayoutMath for Packed { self.children(), FixedAlignment::Center, self.gap(styles), + LeftRightAlternator::Right, )?; layout_delimiters( @@ -324,6 +326,7 @@ impl LayoutMath for Packed { self.children(), FixedAlignment::Start, self.gap(styles), + LeftRightAlternator::None, )?; let (open, close) = if self.reverse(styles) { @@ -387,6 +390,7 @@ fn layout_vec_body( column: &[Content], align: FixedAlignment, row_gap: Rel, + alternator: LeftRightAlternator, ) -> SourceResult { let gap = row_gap.relative_to(ctx.regions.base().y); @@ -396,7 +400,7 @@ fn layout_vec_body( flat.push(ctx.layout_into_run(child, styles.chain(&denom_style))?); } - Ok(stack(flat, align, gap, 0)) + Ok(stack(flat, align, gap, 0, alternator)) } /// Layout the inner contents of a matrix. @@ -480,7 +484,7 @@ fn layout_mat_body( let mut y = Abs::zero(); for (cell, &(ascent, descent)) in col.into_iter().zip(&heights) { - let cell = cell.into_line_frame(&points, FixedAlignment::Center); + let cell = cell.into_line_frame(&points, LeftRightAlternator::Right); let pos = Point::new( if points.is_empty() { x + (rcol - cell.width()) / 2.0 } else { x }, y + ascent - cell.ascent(), diff --git a/crates/typst/src/math/root.rs b/crates/typst/src/math/root.rs index 90d3111b22..6a76044529 100644 --- a/crates/typst/src/math/root.rs +++ b/crates/typst/src/math/root.rs @@ -50,7 +50,7 @@ impl LayoutMath for Packed { /// Layout a root. /// /// TeXbook page 443, page 360 -/// See also: https://www.w3.org/TR/mathml-core/#radicals-msqrt-mroot +/// See also: fn layout( ctx: &mut MathContext, styles: StyleChain, diff --git a/crates/typst/src/math/row.rs b/crates/typst/src/math/row.rs index afed82316d..652cae12c7 100644 --- a/crates/typst/src/math/row.rs +++ b/crates/typst/src/math/row.rs @@ -3,7 +3,7 @@ use std::iter::once; use unicode_math_class::MathClass; use crate::foundations::{Resolve, StyleChain}; -use crate::layout::{Abs, AlignElem, Em, FixedAlignment, Frame, FrameKind, Point, Size}; +use crate::layout::{Abs, AlignElem, Em, Frame, Point, Size}; use crate::math::{ alignments, scaled_font_size, spacing, EquationElem, FrameFragment, MathContext, MathFragment, MathParItem, MathSize, @@ -140,7 +140,7 @@ impl MathRun { pub fn into_frame(self, ctx: &MathContext, styles: StyleChain) -> Frame { if !self.is_multiline() { - self.into_line_frame(&[], AlignElem::alignment_in(styles).resolve(styles).x) + self.into_line_frame(&[], LeftRightAlternator::Right) } else { self.multiline_frame_builder(ctx, styles).build() } @@ -181,7 +181,7 @@ impl MathRun { continue; } - let sub = row.into_line_frame(&alignments.points, align); + let sub = row.into_line_frame(&alignments.points, LeftRightAlternator::Right); if i > 0 { size.y += leading; } @@ -200,43 +200,37 @@ impl MathRun { /// Lay out [`MathFragment`]s into a one-row [`Frame`], using the /// caller-provided alignment points. - pub fn into_line_frame(self, points: &[Abs], align: FixedAlignment) -> Frame { + pub fn into_line_frame( + self, + points: &[Abs], + mut alternator: LeftRightAlternator, + ) -> Frame { let ascent = self.ascent(); let mut frame = Frame::soft(Size::new(Abs::zero(), ascent + self.descent())); frame.set_baseline(ascent); let mut next_x = { - let mut widths = Vec::new(); - if !points.is_empty() && align != FixedAlignment::Start { - let mut width = Abs::zero(); - for fragment in self.iter() { - if matches!(fragment, MathFragment::Align) { - widths.push(width); - width = Abs::zero(); - } else { - width += fragment.width(); - } - } - widths.push(width); - } - let widths = widths; + let widths: Vec = if points.is_empty() { + vec![] + } else { + self.iter() + .as_slice() + .split(|e| matches!(e, MathFragment::Align)) + .map(|chunk| chunk.iter().map(|e| e.width()).sum()) + .collect() + }; let mut prev_points = once(Abs::zero()).chain(points.iter().copied()); let mut point_widths = points.iter().copied().zip(widths); - let mut alternator = LeftRightAlternator::Right; - move || match align { - FixedAlignment::Start => prev_points.next(), - FixedAlignment::End => { - point_widths.next().map(|(point, width)| point - width) - } - _ => point_widths + move || { + point_widths .next() .zip(prev_points.next()) .zip(alternator.next()) .map(|(((point, width), prev_point), alternator)| match alternator { - LeftRightAlternator::Left => prev_point, LeftRightAlternator::Right => point - width, - }), + _ => prev_point, + }) } }; let mut x = next_x().unwrap_or_default(); @@ -263,7 +257,7 @@ impl MathRun { let mut x = Abs::zero(); let mut ascent = Abs::zero(); let mut descent = Abs::zero(); - let mut frame = Frame::new(Size::zero(), FrameKind::Soft); + let mut frame = Frame::soft(Size::zero()); let mut empty = true; let finalize_frame = |frame: &mut Frame, x, ascent, descent| { @@ -307,10 +301,8 @@ impl MathRun { || (class == MathClass::Relation && !iter.peek().map(is_relation).unwrap_or_default()) { - let mut frame_prev = std::mem::replace( - &mut frame, - Frame::new(Size::zero(), FrameKind::Soft), - ); + let mut frame_prev = + std::mem::replace(&mut frame, Frame::soft(Size::zero())); finalize_frame(&mut frame_prev, x, ascent, descent); items.push(MathParItem::Frame(frame_prev)); @@ -352,8 +344,11 @@ impl> From for MathRun { } } +/// An iterator that alternates between the `Left` and `Right` values, if the +/// initial value is not `None`. #[derive(Debug, Copy, Clone, Eq, PartialEq)] -enum LeftRightAlternator { +pub enum LeftRightAlternator { + None, Left, Right, } @@ -364,6 +359,7 @@ impl Iterator for LeftRightAlternator { fn next(&mut self) -> Option { let r = Some(*self); match self { + Self::None => {} Self::Left => *self = Self::Right, Self::Right => *self = Self::Left, } diff --git a/crates/typst/src/math/style.rs b/crates/typst/src/math/style.rs index bd910a7987..332ce58665 100644 --- a/crates/typst/src/math/style.rs +++ b/crates/typst/src/math/style.rs @@ -300,7 +300,8 @@ pub fn styled_char(styles: StyleChain, c: char, auto_italic: bool) -> char { c, 'a'..='z' | 'ı' | 'ȷ' | 'A'..='Z' | 'α'..='ω' | '∂' | 'ϵ' | 'ϑ' | 'ϰ' | 'ϕ' | 'ϱ' | 'ϖ' - ), + ) + && matches!(variant, Sans | Serif), ); if let Some(c) = basic_exception(c) { @@ -444,6 +445,11 @@ fn latin_exception( ('Q', Bb, ..) => 'ℚ', ('R', Bb, ..) => 'ℝ', ('Z', Bb, ..) => 'ℤ', + ('D', Bb, _, true) => 'ⅅ', + ('d', Bb, _, true) => 'ⅆ', + ('e', Bb, _, true) => 'ⅇ', + ('i', Bb, _, true) => 'ⅈ', + ('j', Bb, _, true) => 'ⅉ', ('h', Serif, false, true) => 'ℎ', ('e', Cal, false, _) => 'ℯ', ('g', Cal, false, _) => 'ℊ', @@ -462,15 +468,20 @@ fn greek_exception( ) -> Option { use MathVariant::*; let list = match c { - 'ϴ' => ['𝚹', '𝛳', '𝜭', '𝝧', '𝞡'], - '∇' => ['𝛁', '𝛻', '𝜵', '𝝯', '𝞩'], - '∂' => ['𝛛', '𝜕', '𝝏', '𝞉', '𝟃'], - 'ϵ' => ['𝛜', '𝜖', '𝝐', '𝞊', '𝟄'], - 'ϑ' => ['𝛝', '𝜗', '𝝑', '𝞋', '𝟅'], - 'ϰ' => ['𝛞', '𝜘', '𝝒', '𝞌', '𝟆'], - 'ϕ' => ['𝛟', '𝜙', '𝝓', '𝞍', '𝟇'], - 'ϱ' => ['𝛠', '𝜚', '𝝔', '𝞎', '𝟈'], - 'ϖ' => ['𝛡', '𝜛', '𝝕', '𝞏', '𝟉'], + 'ϴ' => ['𝚹', '𝛳', '𝜭', '𝝧', '𝞡', 'ϴ'], + '∇' => ['𝛁', '𝛻', '𝜵', '𝝯', '𝞩', '∇'], + '∂' => ['𝛛', '𝜕', '𝝏', '𝞉', '𝟃', '∂'], + 'ϵ' => ['𝛜', '𝜖', '𝝐', '𝞊', '𝟄', 'ϵ'], + 'ϑ' => ['𝛝', '𝜗', '𝝑', '𝞋', '𝟅', 'ϑ'], + 'ϰ' => ['𝛞', '𝜘', '𝝒', '𝞌', '𝟆', 'ϰ'], + 'ϕ' => ['𝛟', '𝜙', '𝝓', '𝞍', '𝟇', 'ϕ'], + 'ϱ' => ['𝛠', '𝜚', '𝝔', '𝞎', '𝟈', 'ϱ'], + 'ϖ' => ['𝛡', '𝜛', '𝝕', '𝞏', '𝟉', 'ϖ'], + 'Γ' => ['𝚪', '𝛤', '𝜞', '𝝘', '𝞒', 'ℾ'], + 'γ' => ['𝛄', '𝛾', '𝜸', '𝝲', '𝞬', 'ℽ'], + 'Π' => ['𝚷', '𝛱', '𝜫', '𝝥', '𝞟', 'ℿ'], + 'π' => ['𝛑', '𝜋', '𝝅', '𝝿', '𝞹', 'ℼ'], + '∑' => ['∑', '∑', '∑', '∑', '∑', '⅀'], _ => return None, }; @@ -480,6 +491,7 @@ fn greek_exception( (Serif, true, true) => list[2], (Sans, _, false) => list[3], (Sans, _, true) => list[4], + (Bb, ..) => list[5], _ => return None, }) } diff --git a/crates/typst/src/math/underover.rs b/crates/typst/src/math/underover.rs index 4117342bd9..6be86d9f5f 100644 --- a/crates/typst/src/math/underover.rs +++ b/crates/typst/src/math/underover.rs @@ -3,7 +3,8 @@ use crate::foundations::{elem, Content, Packed, StyleChain}; use crate::layout::{Abs, Em, FixedAlignment, Frame, FrameItem, Point, Size}; use crate::math::{ alignments, scaled_font_size, style_cramped, style_for_subscript, AlignmentResult, - FrameFragment, GlyphFragment, LayoutMath, MathContext, MathRun, Scaled, + FrameFragment, GlyphFragment, LayoutMath, LeftRightAlternator, MathContext, MathRun, + Scaled, }; use crate::syntax::Span; use crate::text::TextElem; @@ -290,7 +291,8 @@ fn layout_underoverspreader( baseline = rows.len() - 1; } - let frame = stack(rows, FixedAlignment::Center, gap, baseline); + let frame = + stack(rows, FixedAlignment::Center, gap, baseline, LeftRightAlternator::Right); ctx.push(FrameFragment::new(ctx, styles, frame).with_class(body_class)); Ok(()) @@ -298,28 +300,30 @@ fn layout_underoverspreader( /// Stack rows on top of each other. /// -/// Add a `gap` between each row and uses the baseline of the `baseline`th -/// row for the whole frame. +/// Add a `gap` between each row and uses the baseline of the `baseline`-th +/// row for the whole frame. `alternator` controls the left/right alternating +/// alignment behavior of `AlignPointElem` in the rows. pub(super) fn stack( rows: Vec, align: FixedAlignment, gap: Abs, baseline: usize, + alternator: LeftRightAlternator, ) -> Frame { let rows: Vec<_> = rows.into_iter().flat_map(|r| r.rows()).collect(); let AlignmentResult { points, width } = alignments(&rows); let rows: Vec<_> = rows .into_iter() - .map(|row| row.into_line_frame(&points, align)) + .map(|row| row.into_line_frame(&points, alternator)) .collect(); - let mut y = Abs::zero(); let mut frame = Frame::soft(Size::new( width, rows.iter().map(|row| row.height()).sum::() + rows.len().saturating_sub(1) as f64 * gap, )); + let mut y = Abs::zero(); for (i, row) in rows.into_iter().enumerate() { let x = align.position(width - row.width()); let pos = Point::new(x, y); diff --git a/crates/typst/src/model/footnote.rs b/crates/typst/src/model/footnote.rs index 44942341b0..4945ebb18f 100644 --- a/crates/typst/src/model/footnote.rs +++ b/crates/typst/src/model/footnote.rs @@ -167,11 +167,6 @@ cast! { /// This function is not intended to be called directly. Instead, it is used /// in set and show rules to customize footnote listings. /// -/// _Note:_ Set and show rules for `footnote.entry` must be defined at the -/// beginning of the document in order to work correctly. -/// See [here](https://github.com/typst/typst/issues/1348#issuecomment-1566316463) -/// for more information. -/// /// ```example /// #show footnote.entry: set text(red) /// @@ -179,6 +174,12 @@ cast! { /// #footnote[It's down here] /// has red text! /// ``` +/// +/// _Note:_ Set and show rules for `footnote.entry` must be defined at the +/// beginning of the document in order to work correctly. See [here][issue] for +/// more information. +/// +/// [issue]: https://github.com/typst/typst/issues/1467#issuecomment-1588799440 #[elem(name = "entry", title = "Footnote Entry", Show, ShowSet)] pub struct FootnoteEntry { /// The footnote for this entry. It's location can be used to determine diff --git a/crates/typst/src/model/outline.rs b/crates/typst/src/model/outline.rs index 6fb1f601f1..a85d1cdf5b 100644 --- a/crates/typst/src/model/outline.rs +++ b/crates/typst/src/model/outline.rs @@ -10,8 +10,10 @@ use crate::foundations::{ NativeElement, Packed, Show, ShowSet, Smart, StyleChain, Styles, }; use crate::introspection::{Counter, CounterKey, Locatable}; -use crate::layout::{BoxElem, Fr, HElem, HideElem, Length, Rel, RepeatElem, Spacing}; -use crate::model::{Destination, HeadingElem, NumberingPattern, ParbreakElem, Refable}; +use crate::layout::{BoxElem, Em, Fr, HElem, HideElem, Length, Rel, RepeatElem, Spacing}; +use crate::model::{ + Destination, HeadingElem, NumberingPattern, ParElem, ParbreakElem, Refable, +}; use crate::syntax::Span; use crate::text::{Lang, LinebreakElem, LocalName, Region, SpaceElem, TextElem}; use crate::util::{option_eq, NonZeroExt}; @@ -71,7 +73,6 @@ pub struct OutlineElem { /// The outline's heading will not be numbered by default, but you can /// force it to be with a show-set rule: /// `{show outline: set heading(numbering: "1.")}` - /// ``` #[default(Some(Smart::Auto))] pub title: Option>, @@ -265,6 +266,7 @@ impl ShowSet for Packed { let mut out = Styles::new(); out.set(HeadingElem::set_outlined(false)); out.set(HeadingElem::set_numbering(None)); + out.set(ParElem::set_first_line_indent(Em::new(0.0).into())); out } } diff --git a/crates/typst/src/model/par.rs b/crates/typst/src/model/par.rs index cc4a1fcb7b..f8a833aca3 100644 --- a/crates/typst/src/model/par.rs +++ b/crates/typst/src/model/par.rs @@ -96,6 +96,15 @@ pub struct ParElem { #[resolve] pub hanging_indent: Length, + /// Indicates wheter an overflowing line should be shrunk. + /// + /// This property is set to `false` on raw blocks, because shrinking a line + /// could visually break the indentation. + #[ghost] + #[internal] + #[default(true)] + pub shrink: bool, + /// The contents of the paragraph. #[external] #[required] diff --git a/crates/typst/src/realize/process.rs b/crates/typst/src/realize/process.rs index 8d3d01a614..a7bb7d1dd5 100644 --- a/crates/typst/src/realize/process.rs +++ b/crates/typst/src/realize/process.rs @@ -198,9 +198,14 @@ fn prepare( // Generate a location for the element, which uniquely identifies it in // the document. This has some overhead, so we only do it for elements // that are explicitly marked as locatable and labelled elements. - if target.can::() || target.label().is_some() { + // + // The element could already have a location even if it is not prepared + // when it stems from a query. + let mut located = target.location().is_some(); + if !located && (target.can::() || target.label().is_some()) { let location = engine.locator.locate(hash128(&target)); target.set_location(location); + located = true; } // Apply built-in show-set rules. User-defined show-set rules are already @@ -220,24 +225,24 @@ fn prepare( // available in rules. target.materialize(styles.chain(map)); + if located { + // Apply metadata to be able to find the element in the frames. Do this + // after synthesis and materialization, so that it includes the + // synthesized fields. Do it before marking as prepared so that show-set + // rules will apply to this element when queried. This adds a style to + // the whole element's subtree identifying it as belonging to the + // element. + map.set(MetaElem::set_data(smallvec![Meta::Elem(target.clone())])); + } + // Ensure that this preparation only runs once by marking the element as // prepared. target.mark_prepared(); - // Apply metadata be able to find the element in the frames. - // Do this after synthesis, so that it includes the synthesized fields. - if target.location().is_some() { - // Add a style to the whole element's subtree identifying it as - // belonging to the element. - map.set(MetaElem::set_data(smallvec![Meta::Elem(target.clone())])); - - // Return an extra meta elem that will be attached so that the metadata - // styles are not lost in case the element's show rule results in - // nothing. - return Ok(Some(Packed::new(MetaElem::new()).spanned(target.span()))); - } - - Ok(None) + // If the element is located, return an extra meta elem that will be + // attached so that the metadata styles are not lost in case the element's + // show rule results in nothing. + Ok(located.then(|| Packed::new(MetaElem::new()).spanned(target.span()))) } /// Apply a step. diff --git a/crates/typst/src/text/mod.rs b/crates/typst/src/text/mod.rs index bcfca08d1a..b06ec373cf 100644 --- a/crates/typst/src/text/mod.rs +++ b/crates/typst/src/text/mod.rs @@ -102,11 +102,16 @@ pub struct TextElem { /// - In the web app, you can see the list of available fonts by clicking on /// the "Ag" button. You can provide additional fonts by uploading `.ttf` /// or `.otf` files into your project. They will be discovered - /// automatically. - /// - /// - Locally, Typst uses your installed system fonts. In addition, you can - /// use the `--font-path` argument or `TYPST_FONT_PATHS` environment - /// variable to add directories that should be scanned for fonts. + /// automatically. The priority is: project fonts > server fonts. + /// + /// - Locally, Typst uses your installed system fonts or embedded fonts in + /// the CLI, which are `Linux Libertine`, `New Computer Modern`, + /// `New Computer Modern Math`, and `DejaVu Sans Mono`. In addition, you + /// can use the `--font-path` argument or `TYPST_FONT_PATHS` environment + /// variable to add directories that should be scanned for fonts. The + /// priority is: `--font-paths` > system fonts > embedded fonts. Run + /// `typst fonts` to see the fonts that Typst has discovered on your + /// system. /// /// ```example /// #set text(font: "PT Sans") diff --git a/crates/typst/src/text/raw.rs b/crates/typst/src/text/raw.rs index 424adb7220..58e64ce516 100644 --- a/crates/typst/src/text/raw.rs +++ b/crates/typst/src/text/raw.rs @@ -16,7 +16,7 @@ use crate::foundations::{ PlainText, Show, ShowSet, Smart, StyleChain, Styles, Synthesize, Value, }; use crate::layout::{BlockElem, Em, HAlignment}; -use crate::model::Figurable; +use crate::model::{Figurable, ParElem}; use crate::syntax::{split_newlines, LinkedNode, Span, Spanned}; use crate::text::{ FontFamily, FontList, Hyphenate, Lang, LinebreakElem, LocalName, Region, @@ -59,6 +59,8 @@ type LineFn<'a> = &'a mut dyn FnMut(usize, Range, &mut Vec); /// # Syntax /// This function also has dedicated syntax. You can enclose text in 1 or 3+ /// backticks (`` ` ``) to make it raw. Two backticks produce empty raw text. +/// This works both in markup and code. +/// /// When you use three or more backticks, you can additionally specify a /// language tag for syntax highlighting directly after the opening backticks. /// Within raw blocks, everything (except for the language tag, if applicable) @@ -300,22 +302,7 @@ impl Packed { #[comemo::memoize] fn highlight(&self, styles: StyleChain) -> Vec> { let elem = self.as_ref(); - - let text = elem.text(); - let lines = match text { - RawContent::Lines(lines) if !lines.iter().any(|(s, _)| s.contains('\t')) => { - lines.clone() - } - _ => { - let mut text = text.get(); - if text.contains('\t') { - let tab_size = RawElem::tab_size_in(styles); - text = align_tabs(&text, tab_size); - } - let lines = split_newlines(&text); - lines.into_iter().map(|line| (line.into(), self.span())).collect() - } - }; + let lines = preprocess(elem.text(), styles, self.span()); let count = lines.len() as i64; let lang = elem @@ -339,7 +326,8 @@ impl Packed { let mut seq = vec![]; if matches!(lang.as_deref(), Some("typ" | "typst" | "typc")) { - let text = text.get(); + let text = + lines.iter().map(|(s, _)| s.clone()).collect::>().join("\n"); let root = match lang.as_deref() { Some("typc") => syntax::parse_code(&text), _ => syntax::parse(&text), @@ -454,13 +442,17 @@ impl Show for Packed { } impl ShowSet for Packed { - fn show_set(&self, _: StyleChain) -> Styles { + fn show_set(&self, styles: StyleChain) -> Styles { let mut out = Styles::new(); out.set(TextElem::set_overhang(false)); + out.set(TextElem::set_lang(Lang::ENGLISH)); out.set(TextElem::set_hyphenate(Hyphenate(Smart::Custom(false)))); out.set(TextElem::set_size(TextSize(Em::new(0.8).into()))); out.set(TextElem::set_font(FontList(vec![FontFamily::new("DejaVu Sans Mono")]))); out.set(SmartQuoteElem::set_enabled(false)); + if self.block(styles) { + out.set(ParElem::set_shrink(false)); + } out } } @@ -683,6 +675,28 @@ impl<'a> ThemedHighlighter<'a> { } } +fn preprocess( + text: &RawContent, + styles: StyleChain, + span: Span, +) -> EcoVec<(EcoString, Span)> { + if let RawContent::Lines(lines) = text { + if lines.iter().all(|(s, _)| !s.contains('\t')) { + return lines.clone(); + } + } + + let mut text = text.get(); + if text.contains('\t') { + let tab_size = RawElem::tab_size_in(styles); + text = align_tabs(&text, tab_size); + } + split_newlines(&text) + .into_iter() + .map(|line| (line.into(), span)) + .collect() +} + /// Style a piece of text with a syntect style. fn styled( piece: &str, diff --git a/crates/typst/src/text/smartquote.rs b/crates/typst/src/text/smartquote.rs index dbcca6dc34..4bb5ca019f 100644 --- a/crates/typst/src/text/smartquote.rs +++ b/crates/typst/src/text/smartquote.rs @@ -2,7 +2,9 @@ use ecow::EcoString; use unicode_segmentation::UnicodeSegmentation; use crate::diag::{bail, StrResult}; -use crate::foundations::{array, cast, dict, elem, Array, Dict, FromValue, Smart, Str}; +use crate::foundations::{ + array, cast, dict, elem, Array, Dict, FromValue, Packed, PlainText, Smart, Str, +}; use crate::layout::Dir; use crate::syntax::is_newline; use crate::text::{Lang, Region}; @@ -26,7 +28,7 @@ use crate::text::{Lang, Region}; /// # Syntax /// This function also has dedicated syntax: The normal quote characters /// (`'` and `"`). Typst automatically makes your quotes smart. -#[elem(name = "smartquote")] +#[elem(name = "smartquote", PlainText)] pub struct SmartQuoteElem { /// Whether this should be a double quote. #[default(true)] @@ -85,6 +87,16 @@ pub struct SmartQuoteElem { pub quotes: Smart, } +impl PlainText for Packed { + fn plain_text(&self, text: &mut EcoString) { + if self.double.unwrap_or(true) { + text.push_str("\""); + } else { + text.push_str("'"); + } + } +} + /// State machine for smart quote substitution. #[derive(Debug, Clone)] pub struct SmartQuoter { diff --git a/crates/typst/src/visualize/color.rs b/crates/typst/src/visualize/color.rs index 365a069554..185e990b7a 100644 --- a/crates/typst/src/visualize/color.rs +++ b/crates/typst/src/visualize/color.rs @@ -32,7 +32,7 @@ pub type Luma = palette::luma::Lumaa; /// This is a minimal CMYK profile that only contains the necessary information /// to convert from CMYK to RGB. It is based on the CGATS TR 001-1995 /// specification. See -/// https://github.com/saucecontrol/Compact-ICC-Profiles#cmyk. +/// . static CMYK_TO_XYZ: Lazy> = Lazy::new(|| Profile::new_from_slice(typst_assets::icc::CMYK_TO_XYZ, false).unwrap()); @@ -117,13 +117,12 @@ static TO_SRGB: Lazy = Lazy::new(|| { /// columns: 9, /// gutter: 10pt, /// ..colors.map(name => { -/// let c = eval(name) -/// let cp = c.components() -/// let x = cp.sum() / cp.len() -/// set text(fill: white) if x < 50% -/// set square(stroke: black) if c == white +/// let col = eval(name) +/// let luminance = luma(col).components().first() +/// set text(fill: white) if luminance < 50% +/// set square(stroke: black) if col == white /// set align(center + horizon) -/// square(size: 50pt, fill: c, name) +/// square(size: 50pt, fill: col, name) /// }) /// ) /// ``` @@ -736,7 +735,7 @@ impl Color { /// /// ```example /// // note that the alpha component is included by default - /// #(rgb(40%, 60%, 80%).components() == (40%, 60%, 80%, 100%)) + /// #rgb(40%, 60%, 80%).components() /// ``` #[func] pub fn components( diff --git a/crates/typst/src/visualize/image/svg.rs b/crates/typst/src/visualize/image/svg.rs index 9685e45478..50c5cb3ad7 100644 --- a/crates/typst/src/visualize/image/svg.rs +++ b/crates/typst/src/visualize/image/svg.rs @@ -4,8 +4,9 @@ use std::sync::Arc; use comemo::Tracked; use ecow::EcoString; +use once_cell::sync::Lazy; use siphasher::sip128::Hasher128; -use usvg::{Node, PostProcessingSteps, TreeParsing, TreePostProc}; +use usvg::{ImageHrefResolver, Node, PostProcessingSteps, TreeParsing, TreePostProc}; use crate::diag::{format_xml_like_error, StrResult}; use crate::foundations::Bytes; @@ -30,7 +31,7 @@ impl SvgImage { /// Decode an SVG image without fonts. #[comemo::memoize] pub fn new(data: Bytes) -> StrResult { - let tree = usvg::Tree::from_data(&data, &options()).map_err(format_usvg_error)?; + let tree = usvg::Tree::from_data(&data, &OPTIONS).map_err(format_usvg_error)?; Ok(Self(Arc::new(Repr { data, size: tree_size(&tree), @@ -48,7 +49,7 @@ impl SvgImage { families: &[String], ) -> StrResult { let mut tree = - usvg::Tree::from_data(&data, &options()).map_err(format_usvg_error)?; + usvg::Tree::from_data(&data, &OPTIONS).map_err(format_usvg_error)?; let mut font_hash = 0; if tree.has_text_nodes() { let (fontdb, hash) = load_svg_fonts(world, &mut tree, families); @@ -122,20 +123,27 @@ impl Hash for Repr { } /// The conversion options. -fn options() -> usvg::Options { +static OPTIONS: Lazy = Lazy::new(|| usvg::Options { // Disable usvg's default to "Times New Roman". Instead, we default to // the empty family and later, when we traverse the SVG, we check for // empty and non-existing family names and replace them with the true - // fallback family. This way, we can memoize SVG decoding with and without - // fonts if the SVG does not contain text. - usvg::Options { - font_family: String::new(), - // We override the DPI here so that we get the correct the size when - // scaling the image to its natural size. - dpi: Image::DEFAULT_DPI as f32, - ..Default::default() - } -} + // fallback family. This way, we can memoize SVG decoding with and + // without fonts if the SVG does not contain text. + font_family: String::new(), + + // We override the DPI here so that we get the correct the size when + // scaling the image to its natural size. + dpi: Image::DEFAULT_DPI as f32, + + // Override usvg's resource loading defaults. + resources_dir: None, + image_href_resolver: ImageHrefResolver { + resolve_data: ImageHrefResolver::default_data_resolver(), + resolve_string: Box::new(|_, _| None), + }, + + ..Default::default() +}); /// Discover and load the fonts referenced by an SVG. fn load_svg_fonts( diff --git a/crates/typst/src/visualize/path.rs b/crates/typst/src/visualize/path.rs index 5ee9922f23..170a1386ab 100644 --- a/crates/typst/src/visualize/path.rs +++ b/crates/typst/src/visualize/path.rs @@ -6,8 +6,7 @@ use crate::foundations::{ array, cast, elem, Array, Packed, Reflect, Resolve, Smart, StyleChain, }; use crate::layout::{ - Abs, Axes, Fragment, Frame, FrameItem, LayoutMultiple, Length, Point, Regions, Rel, - Size, + Abs, Axes, Frame, FrameItem, LayoutSingle, Length, Point, Regions, Rel, Size, }; use crate::visualize::{FixedStroke, Geometry, Paint, Shape, Stroke}; @@ -26,7 +25,7 @@ use PathVertex::{AllControlPoints, MirroredControlPoint, Vertex}; /// ((50%, 0pt), (40pt, 0pt)), /// ) /// ``` -#[elem(LayoutMultiple)] +#[elem(LayoutSingle)] pub struct PathElem { /// How to fill the path. /// @@ -70,14 +69,14 @@ pub struct PathElem { pub vertices: Vec, } -impl LayoutMultiple for Packed { +impl LayoutSingle for Packed { #[typst_macros::time(name = "path", span = self.span())] fn layout( &self, _: &mut Engine, styles: StyleChain, regions: Regions, - ) -> SourceResult { + ) -> SourceResult { let resolve = |axes: Axes>| { axes.resolve(styles) .zip_map(regions.base(), Rel::relative_to) @@ -89,7 +88,7 @@ impl LayoutMultiple for Packed { let mut size = Size::zero(); if points.is_empty() { - return Ok(Fragment::frame(Frame::soft(size))); + return Ok(Frame::soft(size)); } // Only create a path if there are more than zero points. @@ -148,8 +147,7 @@ impl LayoutMultiple for Packed { let mut frame = Frame::soft(size); let shape = Shape { geometry: Geometry::Path(path), stroke, fill }; frame.push(Point::zero(), FrameItem::Shape(shape, self.span())); - - Ok(Fragment::frame(frame)) + Ok(frame) } } diff --git a/crates/typst/src/visualize/shape.rs b/crates/typst/src/visualize/shape.rs index 4567739e9b..e37e5f90cd 100644 --- a/crates/typst/src/visualize/shape.rs +++ b/crates/typst/src/visualize/shape.rs @@ -1108,7 +1108,7 @@ impl ControlPoints { + 2.0 * (o.y - c_i.y).to_raw() * (c_i.y - c_o.y).to_raw(); let c = (c_i.x - c_o.x).to_raw().powi(2) + (c_i.y - c_o.y).to_raw().powi(2) - r.to_raw().powi(2); - let t = (-b + (b * b - 4.0 * a * c).sqrt()) / (2.0 * a); + let t = (-b + (b * b - 4.0 * a * c).max(0.0).sqrt()) / (2.0 * a); c_i + t * (o - c_i) } diff --git a/docs/Cargo.toml b/docs/Cargo.toml index 27fe427c06..2e14a5ee2f 100644 --- a/docs/Cargo.toml +++ b/docs/Cargo.toml @@ -6,13 +6,10 @@ authors = { workspace = true } edition = { workspace = true } publish = false -[lib] -doctest = false -bench = false - [[bin]] name = "typst-docs" required-features = ["cli"] +doc = false [features] default = ["cli"] diff --git a/docs/changelog.md b/docs/changelog.md index 34652f4243..7821360533 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -5,6 +5,62 @@ description: | --- # Changelog +## Version 0.11.1 (May 17, 2024) { #v0.11.1 } +- Security + - Fixed a vulnerability where image files at known paths could be embedded + into the PDF even if they were outside of the project directory + +- Bibliography + - Fixed et-al handling in subsequent citations + - Fixed suppression of title for citations and bibliography references with no + author + - Fixed handling of initials in citation styles without a delimiter + - Fixed bug with citations in footnotes + +- Text and Layout + - Fixed interaction of [`first-line-indent`]($par.first-line-indent) and + [`outline`] + - Fixed compression of CJK punctuation marks at line start and end + - Fixed handling of [rectangles]($rect) with negative dimensions + - Fixed layout of [`path`] in explicitly sized container + - Fixed broken [`raw`] text in right-to-left paragraphs + - Fixed tab rendering in `raw` text with language `typ` or `typc` + - Fixed highlighting of multi-line `raw` text enclosed by single backticks + - Fixed indentation of overflowing lines in `raw` blocks + - Fixed extra space when `raw` text ends with a backtick + +- Math + - Fixed broken [equations]($math.equation) in right-to-left paragraphs + - Fixed missing [blackboard bold]($math.bb) letters + - Fixed error on empty arguments in 2D math argument list + - Fixed stretching via [`mid`]($math.mid) for various characters + - Fixed that alignment points in equations were affected by `{set align(..)}` + +- Export + - Fixed [smart quotes]($smartquote) in PDF outline + - Fixed [patterns]($pattern) with spacing in PDF + - Fixed wrong PDF page labels when [page numbering]($page.numbering) was + disabled after being previously enabled + +- Scripting + - Fixed overflow for large numbers in external data files (by converting to + floats instead) + - Fixed [`{str.trim(regex, at: end)}`]($str.trim) when the whole string is + matched + +- Miscellaneous + - Fixed deformed strokes for specific shapes and thicknesses + - Fixed newline handling in code mode: There can now be comments within + chained method calls and between an `if` branch and the `else` keyword + - Fixed inefficiency with incremental reparsing + - Fixed autocompletions for relative file imports + - Fixed crash in autocompletion handler + - Fixed a bug where the path and entrypoint printed by `typst init` were not + properly escaped + - Fixed various documentation errors + + + ## Version 0.11.0 (March 15, 2024) { #v0.11.0 } - Tables (thanks to [@PgBiel](https://github.com/PgBiel)) - Tables are now _much_ more flexible, read the new diff --git a/docs/guides/tables.md b/docs/guides/tables.md index df6f0ea794..dbeb3955a4 100644 --- a/docs/guides/tables.md +++ b/docs/guides/tables.md @@ -115,7 +115,7 @@ limitation of Typst that will be fixed in a future release. Congratulations, you have created your first table! Now you can proceed to [change column sizes](#column-sizes), [adjust the strokes](#strokes), [add -striped rows](#striped-rows-and-columns), and more! +striped rows](#fills), and more! ## How to change the column sizes? { #column-sizes } If you create a table and specify the number of columns, Typst will make each @@ -997,7 +997,7 @@ by the `table.cell` set rule). Finally, we align all the content of all table cells in the body in the center. If you want to know more about the functions passed to `align`, `stroke`, and `fill`, you can check out the sections on [alignment], [strokes](#stroke-functions), and [striped -tables](#striped-rows-and-columns). +tables](#fills). This table would be a great candidate for fully automated generation from an external data source! Check out the [section about importing @@ -1311,7 +1311,7 @@ row. In our table, we have added our custom header that tells the reader that we've applied a logarithm to the values. Then, we spread the flattened data as above. -We also styled the table with [stripes](#striped-rows-and-columns), a +We also styled the table with [stripes](#fills), a [horizontal line](#individual-lines) below the first row, [aligned](#alignment) everything to the right, and emboldened the first column. Click on the links to go to the relevant guide sections and see how it's done! diff --git a/docs/i18n/category/data-loading-en.yaml b/docs/i18n/category/data-loading-en.yaml index 3d1ab09a31..fcf8d9b15f 100644 --- a/docs/i18n/category/data-loading-en.yaml +++ b/docs/i18n/category/data-loading-en.yaml @@ -96,6 +96,8 @@ children: equivalents, null-values (null, ~ or empty ``) will be converted into none, and numbers will be converted to floats or integers depending on whether they are whole numbers.

+

Be aware that integers larger than 263-1 will be converted to + floating point numbers, which may result in an approximative value.

example: null self: false params: @@ -394,12 +396,16 @@ children: contextual: false details: |-

Reads structured data from a JSON file.

-

The file must contain a valid JSON object or array. JSON objects will be - converted into Typst dictionaries, and JSON arrays will be converted into - Typst arrays. Strings and booleans will be converted into the Typst - equivalents, null will be converted into none, and numbers will be - converted to floats or integers depending on whether they are whole numbers.

-

The function returns a dictionary or an array, depending on the JSON file.

+

The file must contain a valid JSON value, such as object or array. JSON + objects will be converted into Typst dictionaries, and JSON arrays will be + converted into Typst arrays. Strings and booleans will be converted into the + Typst equivalents, null will be converted into none, and numbers will + be converted to floats or integers depending on whether they are whole + numbers.

+

Be aware that integers larger than 263-1 will be converted to + floating point numbers, which may result in an approximative value.

+

The function returns a dictionary, an array or, depending on the JSON file, + another JSON data type.

The JSON files in the example contain objects with the keys temperature, unit, and weather.

Example

@@ -918,6 +924,8 @@ children: none, and numbers will be converted to floats or integers depending on whether they are whole numbers. Custom YAML tags are ignored, though the loaded value will still be present.

+

Be aware that integers larger than 263-1 will be converted to + floating point numbers, which may give an approximative value.

The YAML files in the example contain objects with authors as keys, each with a sequence of their own submapping with the keys "title" and "published"

diff --git a/docs/i18n/category/foundations-en.yaml b/docs/i18n/category/foundations-en.yaml index da827581d0..e3c91c72af 100644 --- a/docs/i18n/category/foundations-en.yaml +++ b/docs/i18n/category/foundations-en.yaml @@ -31,6 +31,10 @@ body: route: /docs/reference/foundations/assert/ oneliner: Ensures that a condition is fulfilled. code: true + - name: auto + route: /docs/reference/foundations/auto/ + oneliner: A value that indicates a smart default. + code: true - name: bool route: /docs/reference/foundations/bool/ oneliner: A type with two states. @@ -83,6 +87,10 @@ body: route: /docs/reference/foundations/module/ oneliner: An evaluated module, either built-in or resulting from a file. code: true + - name: none + route: /docs/reference/foundations/none/ + oneliner: A value that indicates the absence of any other value. + code: true - name: panic route: /docs/reference/foundations/panic/ oneliner: Fails with an error. @@ -1709,6 +1717,31 @@ children: returns: [] scope: [] children: [] +- route: /docs/reference/foundations/auto/ + title: Auto + description: Documentation for the Auto type. + part: null + outline: + - id: summary + name: Summary + children: [] + body: + kind: type + content: + name: auto + title: Auto + keywords: [] + oneliner: A value that indicates a smart default. + details: |- +

A value that indicates a smart default.

+

The auto type has exactly one value: auto.

+

Parameters that support the auto value have some smart default or + contextual behaviour. A good example is the text direction + parameter. Setting it to auto lets Typst automatically determine the + direction from the text language.

+ constructor: null + scope: [] + children: [] - route: /docs/reference/foundations/bool/ title: Boolean description: Documentation for the Boolean type. @@ -5851,6 +5884,36 @@ children: constructor: null scope: [] children: [] +- route: /docs/reference/foundations/none/ + title: None + description: Documentation for the None type. + part: null + outline: + - id: summary + name: Summary + children: [] + - id: example + name: Example + children: [] + body: + kind: type + content: + name: none + title: None + keywords: [] + oneliner: A value that indicates the absence of any other value. + details: |- +

A value that indicates the absence of any other value.

+

The none type has exactly one value: none.

+

When inserted into the document, it is not visible. This is also the value + that is produced by empty code blocks. It can be + joined with any value, yielding the other value.

+

Example

+
Not visible: #none
+        
Preview
+ constructor: null + scope: [] + children: [] - route: /docs/reference/foundations/panic/ title: Panic description: Documentation for the `panic` function. @@ -7300,7 +7363,8 @@ children: self: true params: - name: pattern - details:

The pattern to search for.

+ details:

The pattern to search for. If none, + trims white spaces.

example: null types: - none @@ -7315,8 +7379,8 @@ children: settable: false - name: at details: |- -

Can be start or end to only trim the start or end of the string. - If omitted, both sides are trimmed.

+

Can be start or end to only trim the start or end of the + string. If omitted, both sides are trimmed.

example: null types: - alignment diff --git a/docs/i18n/category/introspection-en.yaml b/docs/i18n/category/introspection-en.yaml index dbe647d66c..33aca181da 100644 --- a/docs/i18n/category/introspection-en.yaml +++ b/docs/i18n/category/introspection-en.yaml @@ -594,8 +594,8 @@ children: page number, typically you want the logical page number that may, for instance, have been reset after a preface.

Examples

-

Determining the current position in the document in combination with - locate:

+

Determining the current position in the document in combination with the + position method:

#context [
           I am located at
           #here().position()
@@ -848,7 +848,8 @@ children:
       details: |-
         

Exposes a value to the query system without producing visible content.

This element can be retrieved with the query function and from the - command line with typst query. Its + command line with + typst query. Its purpose is to expose an arbitrary value to the introspection system. To identify a metadata value among others, you can attach a label to it and query for that label.

diff --git a/docs/i18n/category/layout-en.yaml b/docs/i18n/category/layout-en.yaml index 145ebc9597..1b9d632b76 100644 --- a/docs/i18n/category/layout-en.yaml +++ b/docs/i18n/category/layout-en.yaml @@ -3918,8 +3918,10 @@ children: - name: alignment details: |-

Relative to which position in the parent container to place the content.

-

Cannot be auto if float is false and must be either - auto, top, or bottom if float is true.

+
    +
  • If float is false, then this can be any alignment other than auto.
  • +
  • If float is true, then this must be auto, top, or bottom.
  • +

When an axis of the page is auto sized, all alignments relative to that axis will be ignored, instead, the item will be placed in the origin of the axis.

@@ -3985,6 +3987,9 @@ children: place(center, dx: amount - 32pt, dy: amount)[A] }
Preview
+

This does not affect the layout of in-flow content. + In other words, the placed content is treated as if it + were wrapped in a move element.

types: - relative strings: [] @@ -3995,7 +4000,11 @@ children: variadic: false settable: true - name: dy - details:

The vertical displacement of the placed content.

+ details: |- +

The vertical displacement of the placed content.

+

This does not affect the layout of in-flow content. + In other words, the placed content is treated as if it + were wrapped in a move element.

example: null types: - relative diff --git a/docs/i18n/category/model-en.yaml b/docs/i18n/category/model-en.yaml index 2dd68ba825..566adc4cbc 100644 --- a/docs/i18n/category/model-en.yaml +++ b/docs/i18n/category/model-en.yaml @@ -1725,10 +1725,6 @@ children:

An entry in a footnote list.

This function is not intended to be called directly. Instead, it is used in set and show rules to customize footnote listings.

-

Note: Set and show rules for footnote.entry must be defined at the - beginning of the document in order to work correctly. - See here - for more information.

example: |-
#show footnote.entry: set text(red)
 
@@ -1736,6 +1732,9 @@ children:
           #footnote[It's down here]
           has red text!
           
Preview
+

Note: Set and show rules for footnote.entry must be defined at the + beginning of the document in order to work correctly. See here for + more information.

self: false params: - name: note @@ -2810,7 +2809,7 @@ children:

The outline's heading will not be numbered by default, but you can force it to be with a show-set rule: show outline: set heading(numbering: "1.")

- example:

+        example: null
         types:
         - none
         - auto
diff --git a/docs/i18n/category/text-en.yaml b/docs/i18n/category/text-en.yaml
index c9963cf17c..4c335ffc72 100644
--- a/docs/i18n/category/text-en.yaml
+++ b/docs/i18n/category/text-en.yaml
@@ -719,7 +719,8 @@ children:
         

Syntax

This function also has dedicated syntax. You can enclose text in 1 or 3+ backticks (`) to make it raw. Two backticks produce empty raw text. - When you use three or more backticks, you can additionally specify a + This works both in markup and code.

+

When you use three or more backticks, you can additionally specify a language tag for syntax highlighting directly after the opening backticks. Within raw blocks, everything (except for the language tag, if applicable) is rendered as is, in particular, there are no escape sequences.

@@ -1738,12 +1739,17 @@ children:

In the web app, you can see the list of available fonts by clicking on the "Ag" button. You can provide additional fonts by uploading .ttf or .otf files into your project. They will be discovered - automatically.

+ automatically. The priority is: project fonts > server fonts.

  • -

    Locally, Typst uses your installed system fonts. In addition, you can - use the --font-path argument or TYPST_FONT_PATHS environment - variable to add directories that should be scanned for fonts.

    +

    Locally, Typst uses your installed system fonts or embedded fonts in + the CLI, which are Linux Libertine, New Computer Modern, + New Computer Modern Math, and DejaVu Sans Mono. In addition, you + can use the --font-path argument or TYPST_FONT_PATHS environment + variable to add directories that should be scanned for fonts. The + priority is: --font-paths > system fonts > embedded fonts. Run + typst fonts to see the fonts that Typst has discovered on your + system.

  • example: |- diff --git a/docs/i18n/category/visualize-en.yaml b/docs/i18n/category/visualize-en.yaml index 9ac2243279..0046404d19 100644 --- a/docs/i18n/category/visualize-en.yaml +++ b/docs/i18n/category/visualize-en.yaml @@ -1378,8 +1378,8 @@ children: component is included.

    example: |-
    // note that the alpha component is included by default
    -          #(rgb(40%, 60%, 80%).components() == (40%, 60%, 80%, 100%))
    -          
    Preview
    + #rgb(40%, 60%, 80%).components() +
    Preview
    self: true params: - name: alpha diff --git a/docs/reference/syntax.md b/docs/reference/syntax.md index 251514ada2..1604e1031e 100644 --- a/docs/reference/syntax.md +++ b/docs/reference/syntax.md @@ -48,10 +48,10 @@ Typstは、最も一般的な文書要素に対する組み込みのマークア | 数式 | `[$x^2$]` | [Math]($category/math) | | 改行 | `[\]` | [`linebreak`]($linebreak) | | スマートクオート | `['single' or "double"]` | [`smartquote`]($smartquote) | -| 短縮記号 | `[~, ---]` | [Symbols]($category/symbols/sym) | +| 短縮記号 | `[~]`, `[---]` | [Symbols]($category/symbols/sym) | | コード構文 | `[#rect(width: 1cm)]` | [Scripting]($scripting/#expressions) | -| 文字エスケープ | `[Tweet at us \#ad]` | [Below]($category/syntax/#escapes) | -| コメント | `[/* block */, // line]` | [Below]($category/syntax/#comments) | +| 文字エスケープ | `[Tweet at us \#ad]` | [Below]($category/syntax/#escapes) | +| コメント | `[/* block */]`, `[// line]` | [Below]($category/syntax/#comments) | ## 数式モード { #math } @@ -74,12 +74,12 @@ Typstは、最も一般的な文書要素に対する組み込みのマークア | 変数アクセス | `[$#x$, $pi$]` | [Math]($category/math) | | フィールドアクセス | `[$arrow.r.long$]` | [Scripting]($scripting/#fields) | | 暗黙の乗算 | `[$x y$]` | [Math]($category/math) | -| 短縮記号 | `[$->, !=$]` | [Symbols]($category/symbols/sym) | +| 短縮記号 | `[$->$]`, `[$!=$]` | [Symbols]($category/symbols/sym) | | 数式内のテキスト/文字列 | `[$a "is natural"$]` | [Math]($category/math) | | 数式関数呼び出し | `[$floor(x)$]` | [Math]($category/math) | | コード構文 | `[$#rect(width: 1cm)$]` | [Scripting]($scripting/#expressions) | -| 文字エスケープ | `[$x\^2$]` | [Below]($category/syntax/#escapes) | -| コメント | `[$/* comment */$]` | [Below]($category/syntax/#comments) | +| 文字エスケープ | `[$x\^2$]` | [Below]($category/syntax/#escapes) | +| コメント | `[$/* comment */$]` | [Below]($category/syntax/#comments) | ## コードモード { #code } @@ -87,21 +87,21 @@ Typstは、最も一般的な文書要素に対する組み込みのマークア 多くの構文要素は式に特有のものです。 以下に、コードモードで利用可能なすべての構文の一覧表を示します。 -| 名称 | 例 | 参照 | -| -------------------------- | ----------------------------- | ---------------------------------------- | -| none | `{none}` | [`none`]($reference/foundations/none) | -| 自動 | `{auto}` | [`auto`]($reference/foundations/auto) | -| ブール値 | `{false}`, `{true}` | [`bool`]($reference/foundations/bool) | -| 整数 | `{10}`, `{0xff}` | [`int`]($reference/foundations/int) | -| 浮動小数点数 | `{3.14}`, `{1e5}` | [`float`]($reference/foundations/float) | -| 長さ | `{2pt}`, `{3mm}`, `{1em}`, .. | [`length`]($reference/layout/length) | -| 角度 | `{90deg}`, `{1rad}` | [`angle`]($reference/layout/angle) | +| 名称 | 例 | 参照 | +| -------------------------- | ----------------------------- | ------------------------------------- | +| none | `{none}` | [`none`]($reference/foundations/none) | +| 自動 | `{auto}` | [`auto`]($reference/foundations/auto) | +| ブール値 | `{false}`, `{true}` | [`bool`]($reference/foundations/bool) | +| 整数 | `{10}`, `{0xff}` | [`int`]($reference/foundations/int) | +| 浮動小数点数 | `{3.14}`, `{1e5}` | [`float`]($reference/foundations/float) | +| 長さ | `{2pt}`, `{3mm}`, `{1em}`, .. | [`length`]($reference/layout/length) | +| 角度 | `{90deg}`, `{1rad}` | [`angle`]($reference/layout/angle) | | 比率 | `{2fr}` | [`fraction`]($reference/layout/fraction) | -| 割合 | `{50%}` | [`ratio`]($reference/layout/ratio) | -| 文字列 | `{"hello"}` | [`str`]($reference/foundations/str) | -| ラベル | `{}` | [`label`]($reference/foundations/label) | -| 数式 | `[$x^2$]` | [Math]($category/math) | -| rawテキスト | ``[`print(1)`]`` | [`raw`]($reference/text/raw) | +| 割合 | `{50%}` | [`ratio`]($reference/layout/ratio) | +| 文字列 | `{"hello"}` | [`str`]($reference/foundations/str) | +| ラベル | `{}` | [`label`]($reference/foundations/label) | +| 数式 | `[$x^2$]` | [Math]($category/math) | +| rawテキスト | ``[`print(1)`]`` | [`raw`]($reference/text/raw) | | 変数アクセス | `{x}` | [Scripting]($scripting/#blocks) | | コードブロック | `{{ let x = 1; x + 2 }}` | [Scripting]($scripting/#blocks) | | コンテンツブロック | `{[*Hello*]}` | [Scripting]($scripting/#blocks) | @@ -120,7 +120,7 @@ Typstは、最も一般的な文書要素に対する組み込みのマークア | 名前付き関数 | `{let f(x) = 2 * x}` | [Function]($type/$function) | | setルール | `{set text(14pt)}` | [Styling]($styling/#set-rules) | | set-ifルール | `{set text(..) if .. }` | [Styling]($styling/#set-rules) | -| show-setルール | `{show heading: set block(..)}` | [Styling]($styling/#show-rules) | +| show-setルール | `{show par: set block(..)}` | [Styling]($styling/#show-rules) | | 関数付きshowルール | `{show raw: it => {..}}` | [Styling]($styling/#show-rules) | | show-everythingルール | `{show: columns.with(2)}` | [Styling]($styling/#show-rules) | | コンテキスト式 | `{context text.lang}` | [Context]($context) | diff --git a/docs/src/contribs.rs b/docs/src/contribs.rs index 58a730e222..d9824c39ae 100644 --- a/docs/src/contribs.rs +++ b/docs/src/contribs.rs @@ -9,6 +9,7 @@ use crate::{Html, Resolver}; /// Build HTML detailing the contributors between two tags. pub fn contributors(resolver: &dyn Resolver, from: &str, to: &str) -> Option { let staff = ["laurmaedje", "reknih"]; + let bots = ["dependabot[bot]"]; // Determine number of contributions per person. let mut contributors = HashMap::::new(); @@ -26,7 +27,10 @@ pub fn contributors(resolver: &dyn Resolver, from: &str, to: &str) -> Option = contributors .into_values() - .filter(|c| !staff.contains(&c.login.as_str())) + .filter(|c| { + let login = c.login.as_str(); + !staff.contains(&login) && !bots.contains(&login) + }) .collect(); // Sort by highest number of commits. diff --git a/docs/src/lib.rs b/docs/src/lib.rs index c6c9731ea2..cf582fed31 100644 --- a/docs/src/lib.rs +++ b/docs/src/lib.rs @@ -15,7 +15,9 @@ use once_cell::sync::Lazy; use serde::Deserialize; use serde_yaml as yaml; use typst::diag::{bail, StrResult}; +use typst::foundations::AutoValue; use typst::foundations::Bytes; +use typst::foundations::NoneValue; use typst::foundations::{ CastInfo, Category, Func, Module, ParamInfo, Repr, Scope, Smart, Type, Value, FOUNDATIONS, @@ -56,12 +58,21 @@ static GROUPS: Lazy> = Lazy::new(|| { static LIBRARY: Lazy> = Lazy::new(|| { let mut lib = Library::default(); + let scope = lib.global.scope_mut(); + + // Add those types, so that they show up in the docs. + scope.category(FOUNDATIONS); + scope.define_type::(); + scope.define_type::(); + + // Adjust the default look. lib.styles .set(PageElem::set_width(Smart::Custom(Abs::pt(240.0).into()))); lib.styles.set(PageElem::set_height(Smart::Auto)); lib.styles.set(PageElem::set_margin(Margin::splat(Some(Smart::Custom( Abs::pt(15.0).into(), ))))); + Prehashed::new(lib) }); @@ -242,7 +253,7 @@ fn category_page(resolver: &dyn Resolver, category: Category) -> PageModel { shorthands = Some(ShorthandsModel { markup, math }); } - // Add functions. + // Add values and types. let scope = module.scope(); for (name, value) in scope.iter() { if scope.get_category(name) != Some(category) { diff --git a/templates/base_template.html.j2 b/templates/base_template.html.j2 index d064df45ab..6d642fc458 100644 --- a/templates/base_template.html.j2 +++ b/templates/base_template.html.j2 @@ -42,9 +42,9 @@
    - 注意 当サイトは、Typst v0.11.0 公式ドキュメントを、日本語コミュニティが非公式に翻訳したものです。誤訳・未訳・古い情報が含まれている可能性があるため、公式ドキュメント との併用を推奨します。このサイトの内容に誤りを発見された方は、GitHubリポジトリまでご報告を頂けましたら幸いです。我々のコミュニティにご興味のある方は、ぜひ非公式Discordサーバー「くみはんクラブ」にご参加ください。 + 注意 当サイトは、Typst v0.11.1 公式ドキュメントを、日本語コミュニティが非公式に翻訳したものです。誤訳・未訳・古い情報が含まれている可能性があるため、公式ドキュメント との併用を推奨します。このサイトの内容に誤りを発見された方は、GitHubリポジトリまでご報告を頂けましたら幸いです。我々のコミュニティにご興味のある方は、ぜひ非公式Discordサーバー「くみはんクラブ」にご参加ください。
    - Warning: This site provides an unofficial translation of the Typst v0.11.0 documentation by the Japanese Community. Please note that there may be some inaccuracies, untranslated sections or outdated information. We highly recommend referring to the latest official documentation as well. If you find any errors in the content, please let us know through our GitHub repository. If you are interested in our community, feel free to join our unofficial Discord server, “Kumihan Club.” + Warning: This site provides an unofficial translation of the Typst v0.11.1 documentation by the Japanese Community. Please note that there may be some inaccuracies, untranslated sections or outdated information. We highly recommend referring to the latest official documentation as well. If you find any errors in the content, please let us know through our GitHub repository. If you are interested in our community, feel free to join our unofficial Discord server, “Kumihan Club.”
    diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 1f650c9761..62e7a493ad 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -6,29 +6,29 @@ authors = { workspace = true } edition = { workspace = true } publish = false -[dev-dependencies] +[[test]] +name = "tests" +path = "src/tests.rs" +harness = false + +[dependencies] typst = { workspace = true } typst-assets = { workspace = true, features = ["fonts"] } typst-dev-assets = { workspace = true } typst-pdf = { workspace = true } typst-render = { workspace = true } typst-svg = { workspace = true } -typst-ide = { workspace = true } clap = { workspace = true } comemo = { workspace = true } ecow = { workspace = true } once_cell = { workspace = true } oxipng = { workspace = true } +parking_lot = { workspace = true } rayon = { workspace = true } tiny-skia = { workspace = true } ttf-parser = { workspace = true } unscanny = { workspace = true } walkdir = { workspace = true } -[[test]] -name = "tests" -path = "src/tests.rs" -harness = false - [lints] workspace = true diff --git a/tests/README.md b/tests/README.md index 68b72f2dff..6cc79217a3 100644 --- a/tests/README.md +++ b/tests/README.md @@ -3,13 +3,10 @@ ## Directory structure Top level directory structure: - `src`: Testing code. -- `typ`: Input files. The tests in `compiler` specifically test the compiler - while the others test the standard library (but also the compiler - indirectly). +- `suite`: Input files. Mostly organize in parallel to the code. - `ref`: Reference images which the output is compared with to determine whether a test passed or failed. -- `png`: PNG files produced by tests. -- `pdf`: PDF files produced by tests. +- `store`: Store for PNG, PDF, and SVG output files produced by the tests. ## Running the tests Running all tests (including unit tests): @@ -37,11 +34,6 @@ Running a test with the exact filename `page.typ`. testit --exact page.typ ``` -Debug-printing the layout trees for all executed tests. -```bash -testit --debug empty.typ -``` - To make the integration tests go faster they don't generate PDFs by default. Pass the `--pdf` flag to generate those. Mind that PDFs are not tested automatically at the moment, so you should always check the output manually when @@ -50,18 +42,48 @@ making changes. testit --pdf ``` -## Update expected images +## Writing tests +The syntax for an individual test is `--- {name} ---` followed by some Typst +code that should be tested. The name must be globally unique in the test suite, +so that tests can be easily migrated across files. + +There are, broadly speaking, three kinds of tests: + +- Tests that just ensure that the code runs successfully: Those typically make + use of `test` or `assert.eq` (both are very similar, `test` is just shorter) + to ensure certain properties hold when executing the Typst code. + +- Tests that ensure the code fails with a particular error: Those have inline + annotations like `// Error: 2-7 thing was wrong`. An annotation can be + either an "Error", a "Warning", or a "Hint". The range designates where + in the next non-comment line the error is and after it follows the message. + If you the error is in a line further below, you can also write ranges like + `3:2-3:7` to indicate the 2-7 column in the 3rd non-comment line. + +- Tests that ensure certain visual output is produced: Those render the result + of the test with the `typst-render` crate and compare against a reference + image stored in the repository. The test runner automatically detects whether + a test has visual output and requires a reference image in this case. + + To prevent bloat, it is important that the test images are kept as small as + possible. To that effect, the test runner enforces a maximum size of 20 KiB. + If truly necessary, this limit can however be lifted by adding `// LARGE` as + the first line of a test. + +If you have the choice between writing a test using assertions or using +reference images, prefer assertions. This makes the test easier to understand +in isolation and prevents bloat due to images. + +## Updating reference images If you created a new test or fixed a bug in an existing test, you need to update -the reference image used for comparison. For this, you can use the -`UPDATE_EXPECT` environment variable or the `--update` flag: +the reference image used for comparison. For this, you can use the `--update` +flag: ```bash testit mytest --update ``` If you use the VS Code test helper extension (see the `tools` folder), you can -alternatively use the checkmark button to update the reference image. In that -case you should also install `oxipng` on your system so that the test helper -can optimize the reference images. +alternatively use the save button to update the reference image. ## Making an alias If you want to have a quicker way to run the tests, consider adding a shortcut diff --git a/tests/ref/align-center-in-flow.png b/tests/ref/align-center-in-flow.png new file mode 100644 index 0000000000..ecfe49dc53 Binary files /dev/null and b/tests/ref/align-center-in-flow.png differ diff --git a/tests/ref/align-in-stack.png b/tests/ref/align-in-stack.png new file mode 100644 index 0000000000..556721ab09 Binary files /dev/null and b/tests/ref/align-in-stack.png differ diff --git a/tests/ref/align-right.png b/tests/ref/align-right.png new file mode 100644 index 0000000000..edab885100 Binary files /dev/null and b/tests/ref/align-right.png differ diff --git a/tests/ref/align-start-and-end.png b/tests/ref/align-start-and-end.png new file mode 100644 index 0000000000..cf3faeae6f Binary files /dev/null and b/tests/ref/align-start-and-end.png differ diff --git a/tests/ref/array-basic-syntax.png b/tests/ref/array-basic-syntax.png new file mode 100644 index 0000000000..6eb95305c4 Binary files /dev/null and b/tests/ref/array-basic-syntax.png differ diff --git a/tests/ref/array-insert-and-remove.png b/tests/ref/array-insert-and-remove.png new file mode 100644 index 0000000000..ea4b8cf2a3 Binary files /dev/null and b/tests/ref/array-insert-and-remove.png differ diff --git a/tests/ref/array-join-content.png b/tests/ref/array-join-content.png new file mode 100644 index 0000000000..4d08142ebb Binary files /dev/null and b/tests/ref/array-join-content.png differ diff --git a/tests/ref/baseline-box.png b/tests/ref/baseline-box.png new file mode 100644 index 0000000000..8d7627c609 Binary files /dev/null and b/tests/ref/baseline-box.png differ diff --git a/tests/ref/baseline-text.png b/tests/ref/baseline-text.png new file mode 100644 index 0000000000..72beac7959 Binary files /dev/null and b/tests/ref/baseline-text.png differ diff --git a/tests/ref/bibliography-basic.png b/tests/ref/bibliography-basic.png new file mode 100644 index 0000000000..eeb773bfe8 Binary files /dev/null and b/tests/ref/bibliography-basic.png differ diff --git a/tests/ref/bibliography-before-content.png b/tests/ref/bibliography-before-content.png new file mode 100644 index 0000000000..806daa089c Binary files /dev/null and b/tests/ref/bibliography-before-content.png differ diff --git a/tests/ref/bibliography-full.png b/tests/ref/bibliography-full.png new file mode 100644 index 0000000000..1da15d1668 Binary files /dev/null and b/tests/ref/bibliography-full.png differ diff --git a/tests/ref/bibliography-math.png b/tests/ref/bibliography-math.png new file mode 100644 index 0000000000..3fc36efca6 Binary files /dev/null and b/tests/ref/bibliography-math.png differ diff --git a/tests/ref/bibliography-multiple-files.png b/tests/ref/bibliography-multiple-files.png new file mode 100644 index 0000000000..1293ba22b4 Binary files /dev/null and b/tests/ref/bibliography-multiple-files.png differ diff --git a/tests/ref/bibliography-ordering.png b/tests/ref/bibliography-ordering.png new file mode 100644 index 0000000000..b1e14c9adf Binary files /dev/null and b/tests/ref/bibliography-ordering.png differ diff --git a/tests/ref/bidi-consecutive-embedded-ltr-runs.png b/tests/ref/bidi-consecutive-embedded-ltr-runs.png new file mode 100644 index 0000000000..dbaaff07db Binary files /dev/null and b/tests/ref/bidi-consecutive-embedded-ltr-runs.png differ diff --git a/tests/ref/bidi-consecutive-embedded-rtl-runs.png b/tests/ref/bidi-consecutive-embedded-rtl-runs.png new file mode 100644 index 0000000000..4cf62d3e0f Binary files /dev/null and b/tests/ref/bidi-consecutive-embedded-rtl-runs.png differ diff --git a/tests/ref/bidi-en-he-top-level.png b/tests/ref/bidi-en-he-top-level.png new file mode 100644 index 0000000000..abab54f4b4 Binary files /dev/null and b/tests/ref/bidi-en-he-top-level.png differ diff --git a/tests/ref/bidi-explicit-dir.png b/tests/ref/bidi-explicit-dir.png new file mode 100644 index 0000000000..8b813be0cc Binary files /dev/null and b/tests/ref/bidi-explicit-dir.png differ diff --git a/tests/ref/bidi-manual-linebreak.png b/tests/ref/bidi-manual-linebreak.png new file mode 100644 index 0000000000..4d0eb6f890 Binary files /dev/null and b/tests/ref/bidi-manual-linebreak.png differ diff --git a/tests/ref/bidi-nesting.png b/tests/ref/bidi-nesting.png new file mode 100644 index 0000000000..e18d6c0a00 Binary files /dev/null and b/tests/ref/bidi-nesting.png differ diff --git a/tests/ref/bidi-obj.png b/tests/ref/bidi-obj.png new file mode 100644 index 0000000000..8cc41528ab Binary files /dev/null and b/tests/ref/bidi-obj.png differ diff --git a/tests/ref/bidi-raw.png b/tests/ref/bidi-raw.png new file mode 100644 index 0000000000..24503ee932 Binary files /dev/null and b/tests/ref/bidi-raw.png differ diff --git a/tests/ref/bidi-spacing.png b/tests/ref/bidi-spacing.png new file mode 100644 index 0000000000..44ede76f10 Binary files /dev/null and b/tests/ref/bidi-spacing.png differ diff --git a/tests/ref/bidi-whitespace-reset.png b/tests/ref/bidi-whitespace-reset.png new file mode 100644 index 0000000000..7d64012f9e Binary files /dev/null and b/tests/ref/bidi-whitespace-reset.png differ diff --git a/tests/ref/block-box-fill.png b/tests/ref/block-box-fill.png new file mode 100644 index 0000000000..fe4f725802 Binary files /dev/null and b/tests/ref/block-box-fill.png differ diff --git a/tests/ref/block-clip-svg-glyphs.png b/tests/ref/block-clip-svg-glyphs.png new file mode 100644 index 0000000000..d8db5b61ef Binary files /dev/null and b/tests/ref/block-clip-svg-glyphs.png differ diff --git a/tests/ref/block-clip-text.png b/tests/ref/block-clip-text.png new file mode 100644 index 0000000000..7cd86ddbe7 Binary files /dev/null and b/tests/ref/block-clip-text.png differ diff --git a/tests/ref/block-clipping-multiple-pages.png b/tests/ref/block-clipping-multiple-pages.png new file mode 100644 index 0000000000..9c9aa89b43 Binary files /dev/null and b/tests/ref/block-clipping-multiple-pages.png differ diff --git a/tests/ref/block-fixed-height.png b/tests/ref/block-fixed-height.png new file mode 100644 index 0000000000..95c3be1e59 Binary files /dev/null and b/tests/ref/block-fixed-height.png differ diff --git a/tests/ref/block-multiple-pages.png b/tests/ref/block-multiple-pages.png new file mode 100644 index 0000000000..c2f192bd61 Binary files /dev/null and b/tests/ref/block-multiple-pages.png differ diff --git a/tests/ref/block-sizing.png b/tests/ref/block-sizing.png new file mode 100644 index 0000000000..76cb04dfa0 Binary files /dev/null and b/tests/ref/block-sizing.png differ diff --git a/tests/ref/block-spacing-basic.png b/tests/ref/block-spacing-basic.png new file mode 100644 index 0000000000..875410acb1 Binary files /dev/null and b/tests/ref/block-spacing-basic.png differ diff --git a/tests/ref/block-spacing-collapse-text-style.png b/tests/ref/block-spacing-collapse-text-style.png new file mode 100644 index 0000000000..6c631457bc Binary files /dev/null and b/tests/ref/block-spacing-collapse-text-style.png differ diff --git a/tests/ref/block-spacing-maximum.png b/tests/ref/block-spacing-maximum.png new file mode 100644 index 0000000000..755b1cc306 Binary files /dev/null and b/tests/ref/block-spacing-maximum.png differ diff --git a/tests/ref/block-spacing-table.png b/tests/ref/block-spacing-table.png new file mode 100644 index 0000000000..1591acb7c0 Binary files /dev/null and b/tests/ref/block-spacing-table.png differ diff --git a/tests/ref/box-clip-radius-without-stroke.png b/tests/ref/box-clip-radius-without-stroke.png new file mode 100644 index 0000000000..121373582e Binary files /dev/null and b/tests/ref/box-clip-radius-without-stroke.png differ diff --git a/tests/ref/box-clip-radius.png b/tests/ref/box-clip-radius.png new file mode 100644 index 0000000000..da20fa5bfc Binary files /dev/null and b/tests/ref/box-clip-radius.png differ diff --git a/tests/ref/box-clip-rect.png b/tests/ref/box-clip-rect.png new file mode 100644 index 0000000000..49a4e4abc6 Binary files /dev/null and b/tests/ref/box-clip-rect.png differ diff --git a/tests/ref/box-width-fr.png b/tests/ref/box-width-fr.png new file mode 100644 index 0000000000..30d4816316 Binary files /dev/null and b/tests/ref/box-width-fr.png differ diff --git a/tests/ref/box.png b/tests/ref/box.png new file mode 100644 index 0000000000..fde288a80d Binary files /dev/null and b/tests/ref/box.png differ diff --git a/tests/ref/bugs/1050-terms-indent.png b/tests/ref/bugs/1050-terms-indent.png deleted file mode 100644 index 58a7feae88..0000000000 Binary files a/tests/ref/bugs/1050-terms-indent.png and /dev/null differ diff --git a/tests/ref/bugs/1240-stack-fr.png b/tests/ref/bugs/1240-stack-fr.png deleted file mode 100644 index 29df5d44a5..0000000000 Binary files a/tests/ref/bugs/1240-stack-fr.png and /dev/null differ diff --git a/tests/ref/bugs/1597-cite-footnote.png b/tests/ref/bugs/1597-cite-footnote.png deleted file mode 100644 index c2e219f275..0000000000 Binary files a/tests/ref/bugs/1597-cite-footnote.png and /dev/null differ diff --git a/tests/ref/bugs/2044-invalid-parsed-ident.png b/tests/ref/bugs/2044-invalid-parsed-ident.png deleted file mode 100644 index 327150e788..0000000000 Binary files a/tests/ref/bugs/2044-invalid-parsed-ident.png and /dev/null differ diff --git a/tests/ref/bugs/2105-linebreak-tofu.png b/tests/ref/bugs/2105-linebreak-tofu.png deleted file mode 100644 index 78f937eb2c..0000000000 Binary files a/tests/ref/bugs/2105-linebreak-tofu.png and /dev/null differ diff --git a/tests/ref/bugs/2595-float-overlap.png b/tests/ref/bugs/2595-float-overlap.png deleted file mode 100644 index 6d8eaf9415..0000000000 Binary files a/tests/ref/bugs/2595-float-overlap.png and /dev/null differ diff --git a/tests/ref/bugs/2650-cjk-latin-spacing-meta.png b/tests/ref/bugs/2650-cjk-latin-spacing-meta.png deleted file mode 100644 index 35ff0e62d8..0000000000 Binary files a/tests/ref/bugs/2650-cjk-latin-spacing-meta.png and /dev/null differ diff --git a/tests/ref/bugs/2715-float-order.png b/tests/ref/bugs/2715-float-order.png deleted file mode 100644 index 0a4f8812eb..0000000000 Binary files a/tests/ref/bugs/2715-float-order.png and /dev/null differ diff --git a/tests/ref/bugs/3082-chinese-punctuation.png b/tests/ref/bugs/3082-chinese-punctuation.png deleted file mode 100644 index c187d49558..0000000000 Binary files a/tests/ref/bugs/3082-chinese-punctuation.png and /dev/null differ diff --git a/tests/ref/bugs/3641-float-loop.png b/tests/ref/bugs/3641-float-loop.png deleted file mode 100644 index 092b2ff510..0000000000 Binary files a/tests/ref/bugs/3641-float-loop.png and /dev/null differ diff --git a/tests/ref/bugs/3650-italic-equation.png b/tests/ref/bugs/3650-italic-equation.png deleted file mode 100644 index 41f071ab80..0000000000 Binary files a/tests/ref/bugs/3650-italic-equation.png and /dev/null differ diff --git a/tests/ref/bugs/3658-math-size.png b/tests/ref/bugs/3658-math-size.png deleted file mode 100644 index 94c8d38816..0000000000 Binary files a/tests/ref/bugs/3658-math-size.png and /dev/null differ diff --git a/tests/ref/bugs/870-image-rotation.png b/tests/ref/bugs/870-image-rotation.png deleted file mode 100644 index 83d9267d1c..0000000000 Binary files a/tests/ref/bugs/870-image-rotation.png and /dev/null differ diff --git a/tests/ref/bugs/args-sink.png b/tests/ref/bugs/args-sink.png deleted file mode 100644 index 564c59a2b3..0000000000 Binary files a/tests/ref/bugs/args-sink.png and /dev/null differ diff --git a/tests/ref/bugs/bibliography-math.png b/tests/ref/bugs/bibliography-math.png deleted file mode 100644 index 0ab308dc98..0000000000 Binary files a/tests/ref/bugs/bibliography-math.png and /dev/null differ diff --git a/tests/ref/bugs/bidi-tofus.png b/tests/ref/bugs/bidi-tofus.png deleted file mode 100644 index 1b7a7d8b4b..0000000000 Binary files a/tests/ref/bugs/bidi-tofus.png and /dev/null differ diff --git a/tests/ref/bugs/block-width-box.png b/tests/ref/bugs/block-width-box.png deleted file mode 100644 index 9cb27a5def..0000000000 Binary files a/tests/ref/bugs/block-width-box.png and /dev/null differ diff --git a/tests/ref/bugs/cite-locate.png b/tests/ref/bugs/cite-locate.png deleted file mode 100644 index bd31df7db6..0000000000 Binary files a/tests/ref/bugs/cite-locate.png and /dev/null differ diff --git a/tests/ref/bugs/cite-show-set.png b/tests/ref/bugs/cite-show-set.png deleted file mode 100644 index 566186a4cf..0000000000 Binary files a/tests/ref/bugs/cite-show-set.png and /dev/null differ diff --git a/tests/ref/bugs/clamp-panic.png b/tests/ref/bugs/clamp-panic.png deleted file mode 100644 index c0c4912e36..0000000000 Binary files a/tests/ref/bugs/clamp-panic.png and /dev/null differ diff --git a/tests/ref/bugs/columns-1.png b/tests/ref/bugs/columns-1.png deleted file mode 100644 index 4b462b60a8..0000000000 Binary files a/tests/ref/bugs/columns-1.png and /dev/null differ diff --git a/tests/ref/bugs/emoji-linebreak.png b/tests/ref/bugs/emoji-linebreak.png deleted file mode 100644 index 6944233d8f..0000000000 Binary files a/tests/ref/bugs/emoji-linebreak.png and /dev/null differ diff --git a/tests/ref/bugs/flow-1.png b/tests/ref/bugs/flow-1.png deleted file mode 100644 index 662a7b1472..0000000000 Binary files a/tests/ref/bugs/flow-1.png and /dev/null differ diff --git a/tests/ref/bugs/flow-2.png b/tests/ref/bugs/flow-2.png deleted file mode 100644 index c7ece30839..0000000000 Binary files a/tests/ref/bugs/flow-2.png and /dev/null differ diff --git a/tests/ref/bugs/flow-3.png b/tests/ref/bugs/flow-3.png deleted file mode 100644 index 25acc06dfe..0000000000 Binary files a/tests/ref/bugs/flow-3.png and /dev/null differ diff --git a/tests/ref/bugs/flow-4.png b/tests/ref/bugs/flow-4.png deleted file mode 100644 index 2adcbe1573..0000000000 Binary files a/tests/ref/bugs/flow-4.png and /dev/null differ diff --git a/tests/ref/bugs/flow-5.png b/tests/ref/bugs/flow-5.png deleted file mode 100644 index 648c8c44fa..0000000000 Binary files a/tests/ref/bugs/flow-5.png and /dev/null differ diff --git a/tests/ref/bugs/fold-vector.png b/tests/ref/bugs/fold-vector.png deleted file mode 100644 index d8503a8eb9..0000000000 Binary files a/tests/ref/bugs/fold-vector.png and /dev/null differ diff --git a/tests/ref/bugs/footnote-keep-multiple.png b/tests/ref/bugs/footnote-keep-multiple.png deleted file mode 100644 index f3b67a7456..0000000000 Binary files a/tests/ref/bugs/footnote-keep-multiple.png and /dev/null differ diff --git a/tests/ref/bugs/footnote-list.png b/tests/ref/bugs/footnote-list.png deleted file mode 100644 index 1b56f2274f..0000000000 Binary files a/tests/ref/bugs/footnote-list.png and /dev/null differ diff --git a/tests/ref/bugs/gradient-cmyk-encode.png b/tests/ref/bugs/gradient-cmyk-encode.png deleted file mode 100644 index 5002442f7b..0000000000 Binary files a/tests/ref/bugs/gradient-cmyk-encode.png and /dev/null differ diff --git a/tests/ref/bugs/grid-1.png b/tests/ref/bugs/grid-1.png deleted file mode 100644 index f60ad7f475..0000000000 Binary files a/tests/ref/bugs/grid-1.png and /dev/null differ diff --git a/tests/ref/bugs/grid-2.png b/tests/ref/bugs/grid-2.png deleted file mode 100644 index 882e0d6ad1..0000000000 Binary files a/tests/ref/bugs/grid-2.png and /dev/null differ diff --git a/tests/ref/bugs/grid-3.png b/tests/ref/bugs/grid-3.png deleted file mode 100644 index 6b5ae649ef..0000000000 Binary files a/tests/ref/bugs/grid-3.png and /dev/null differ diff --git a/tests/ref/bugs/grid-4.png b/tests/ref/bugs/grid-4.png deleted file mode 100644 index 475f561ea7..0000000000 Binary files a/tests/ref/bugs/grid-4.png and /dev/null differ diff --git a/tests/ref/bugs/hide-meta.png b/tests/ref/bugs/hide-meta.png deleted file mode 100644 index 76b4671a06..0000000000 Binary files a/tests/ref/bugs/hide-meta.png and /dev/null differ diff --git a/tests/ref/bugs/justify-hanging-indent.png b/tests/ref/bugs/justify-hanging-indent.png deleted file mode 100644 index 015cc44e61..0000000000 Binary files a/tests/ref/bugs/justify-hanging-indent.png and /dev/null differ diff --git a/tests/ref/bugs/line-align.png b/tests/ref/bugs/line-align.png deleted file mode 100644 index 1117ed6bf4..0000000000 Binary files a/tests/ref/bugs/line-align.png and /dev/null differ diff --git a/tests/ref/bugs/linebreak-no-justifiables.png b/tests/ref/bugs/linebreak-no-justifiables.png deleted file mode 100644 index 3f934592ab..0000000000 Binary files a/tests/ref/bugs/linebreak-no-justifiables.png and /dev/null differ diff --git a/tests/ref/bugs/mat-aug-color.png b/tests/ref/bugs/mat-aug-color.png deleted file mode 100644 index 472c196864..0000000000 Binary files a/tests/ref/bugs/mat-aug-color.png and /dev/null differ diff --git a/tests/ref/bugs/math-eval.png b/tests/ref/bugs/math-eval.png deleted file mode 100644 index b673e50394..0000000000 Binary files a/tests/ref/bugs/math-eval.png and /dev/null differ diff --git a/tests/ref/bugs/math-hide.png b/tests/ref/bugs/math-hide.png deleted file mode 100644 index 7ac5d2f10c..0000000000 Binary files a/tests/ref/bugs/math-hide.png and /dev/null differ diff --git a/tests/ref/bugs/math-number-spacing.png b/tests/ref/bugs/math-number-spacing.png deleted file mode 100644 index 5ec65df306..0000000000 Binary files a/tests/ref/bugs/math-number-spacing.png and /dev/null differ diff --git a/tests/ref/bugs/math-realize.png b/tests/ref/bugs/math-realize.png deleted file mode 100644 index e972e099c1..0000000000 Binary files a/tests/ref/bugs/math-realize.png and /dev/null differ diff --git a/tests/ref/bugs/math-shift.png b/tests/ref/bugs/math-shift.png deleted file mode 100644 index d6a2ef3b17..0000000000 Binary files a/tests/ref/bugs/math-shift.png and /dev/null differ diff --git a/tests/ref/bugs/math-text-break.png b/tests/ref/bugs/math-text-break.png deleted file mode 100644 index 768ca65f6b..0000000000 Binary files a/tests/ref/bugs/math-text-break.png and /dev/null differ diff --git a/tests/ref/bugs/new-cm-svg.png b/tests/ref/bugs/new-cm-svg.png deleted file mode 100644 index d75a6dbb0e..0000000000 Binary files a/tests/ref/bugs/new-cm-svg.png and /dev/null differ diff --git a/tests/ref/bugs/newline-mode.png b/tests/ref/bugs/newline-mode.png deleted file mode 100644 index d4b6c6d85e..0000000000 Binary files a/tests/ref/bugs/newline-mode.png and /dev/null differ diff --git a/tests/ref/bugs/pagebreak-bibliography.png b/tests/ref/bugs/pagebreak-bibliography.png deleted file mode 100644 index 43de157446..0000000000 Binary files a/tests/ref/bugs/pagebreak-bibliography.png and /dev/null differ diff --git a/tests/ref/bugs/pagebreak-numbering.png b/tests/ref/bugs/pagebreak-numbering.png deleted file mode 100644 index 96f047a889..0000000000 Binary files a/tests/ref/bugs/pagebreak-numbering.png and /dev/null differ diff --git a/tests/ref/bugs/pagebreak-set-style.png b/tests/ref/bugs/pagebreak-set-style.png deleted file mode 100644 index f81b8c2f20..0000000000 Binary files a/tests/ref/bugs/pagebreak-set-style.png and /dev/null differ diff --git a/tests/ref/bugs/place-base.png b/tests/ref/bugs/place-base.png deleted file mode 100644 index 4442b173cf..0000000000 Binary files a/tests/ref/bugs/place-base.png and /dev/null differ diff --git a/tests/ref/bugs/place-nested.png b/tests/ref/bugs/place-nested.png deleted file mode 100644 index b59dc5d3d8..0000000000 Binary files a/tests/ref/bugs/place-nested.png and /dev/null differ diff --git a/tests/ref/bugs/place-pagebreak.png b/tests/ref/bugs/place-pagebreak.png deleted file mode 100644 index 2aa3d6b00a..0000000000 Binary files a/tests/ref/bugs/place-pagebreak.png and /dev/null differ diff --git a/tests/ref/bugs/place-spacing.png b/tests/ref/bugs/place-spacing.png deleted file mode 100644 index d14ce6ec13..0000000000 Binary files a/tests/ref/bugs/place-spacing.png and /dev/null differ diff --git a/tests/ref/bugs/raw-color-overwrite.png b/tests/ref/bugs/raw-color-overwrite.png deleted file mode 100644 index b01d86a49c..0000000000 Binary files a/tests/ref/bugs/raw-color-overwrite.png and /dev/null differ diff --git a/tests/ref/bugs/smartquotes-in-outline.png b/tests/ref/bugs/smartquotes-in-outline.png deleted file mode 100644 index 8a2cbc6aea..0000000000 Binary files a/tests/ref/bugs/smartquotes-in-outline.png and /dev/null differ diff --git a/tests/ref/bugs/smartquotes-on-newline.png b/tests/ref/bugs/smartquotes-on-newline.png deleted file mode 100644 index fdf4623ad0..0000000000 Binary files a/tests/ref/bugs/smartquotes-on-newline.png and /dev/null differ diff --git a/tests/ref/bugs/spacing-behaviour.png b/tests/ref/bugs/spacing-behaviour.png deleted file mode 100644 index 08fcfa7394..0000000000 Binary files a/tests/ref/bugs/spacing-behaviour.png and /dev/null differ diff --git a/tests/ref/bugs/square-base.png b/tests/ref/bugs/square-base.png deleted file mode 100644 index 290ee54e5f..0000000000 Binary files a/tests/ref/bugs/square-base.png and /dev/null differ diff --git a/tests/ref/bugs/table-lines.png b/tests/ref/bugs/table-lines.png deleted file mode 100644 index 600391cb54..0000000000 Binary files a/tests/ref/bugs/table-lines.png and /dev/null differ diff --git a/tests/ref/bugs/table-row-missing.png b/tests/ref/bugs/table-row-missing.png deleted file mode 100644 index 90c46d3235..0000000000 Binary files a/tests/ref/bugs/table-row-missing.png and /dev/null differ diff --git a/tests/ref/call-basic.png b/tests/ref/call-basic.png new file mode 100644 index 0000000000..9016e9e8d1 Binary files /dev/null and b/tests/ref/call-basic.png differ diff --git a/tests/ref/circle-auto-sizing.png b/tests/ref/circle-auto-sizing.png new file mode 100644 index 0000000000..377dbe1ddb Binary files /dev/null and b/tests/ref/circle-auto-sizing.png differ diff --git a/tests/ref/circle-directly-in-rect.png b/tests/ref/circle-directly-in-rect.png new file mode 100644 index 0000000000..cb74496d78 Binary files /dev/null and b/tests/ref/circle-directly-in-rect.png differ diff --git a/tests/ref/circle-relative-sizing.png b/tests/ref/circle-relative-sizing.png new file mode 100644 index 0000000000..efff34cf95 Binary files /dev/null and b/tests/ref/circle-relative-sizing.png differ diff --git a/tests/ref/circle-sizing-options.png b/tests/ref/circle-sizing-options.png new file mode 100644 index 0000000000..778a824957 Binary files /dev/null and b/tests/ref/circle-sizing-options.png differ diff --git a/tests/ref/circle.png b/tests/ref/circle.png new file mode 100644 index 0000000000..8a86e1948e Binary files /dev/null and b/tests/ref/circle.png differ diff --git a/tests/ref/cite-footnote.png b/tests/ref/cite-footnote.png new file mode 100644 index 0000000000..5bc6433e05 Binary files /dev/null and b/tests/ref/cite-footnote.png differ diff --git a/tests/ref/cite-form.png b/tests/ref/cite-form.png new file mode 100644 index 0000000000..c35a357355 Binary files /dev/null and b/tests/ref/cite-form.png differ diff --git a/tests/ref/cite-group.png b/tests/ref/cite-group.png new file mode 100644 index 0000000000..70feb4e1e0 Binary files /dev/null and b/tests/ref/cite-group.png differ diff --git a/tests/ref/cite-grouping-and-ordering.png b/tests/ref/cite-grouping-and-ordering.png new file mode 100644 index 0000000000..6a70539db6 Binary files /dev/null and b/tests/ref/cite-grouping-and-ordering.png differ diff --git a/tests/ref/cjk-punctuation-adjustment-1.png b/tests/ref/cjk-punctuation-adjustment-1.png new file mode 100644 index 0000000000..a68274cf31 Binary files /dev/null and b/tests/ref/cjk-punctuation-adjustment-1.png differ diff --git a/tests/ref/cjk-punctuation-adjustment-2.png b/tests/ref/cjk-punctuation-adjustment-2.png new file mode 100644 index 0000000000..925c0f3c58 Binary files /dev/null and b/tests/ref/cjk-punctuation-adjustment-2.png differ diff --git a/tests/ref/cjk-punctuation-adjustment-3.png b/tests/ref/cjk-punctuation-adjustment-3.png new file mode 100644 index 0000000000..e5eb70a941 Binary files /dev/null and b/tests/ref/cjk-punctuation-adjustment-3.png differ diff --git a/tests/ref/closure-capture-in-lvalue.png b/tests/ref/closure-capture-in-lvalue.png new file mode 100644 index 0000000000..5f3ab0356d Binary files /dev/null and b/tests/ref/closure-capture-in-lvalue.png differ diff --git a/tests/ref/closure-path-resolve-in-layout-phase.png b/tests/ref/closure-path-resolve-in-layout-phase.png new file mode 100644 index 0000000000..e56e23a025 Binary files /dev/null and b/tests/ref/closure-path-resolve-in-layout-phase.png differ diff --git a/tests/ref/closure-without-params-non-atomic.png b/tests/ref/closure-without-params-non-atomic.png new file mode 100644 index 0000000000..7d01ea3cd9 Binary files /dev/null and b/tests/ref/closure-without-params-non-atomic.png differ diff --git a/tests/ref/code-block-basic-syntax.png b/tests/ref/code-block-basic-syntax.png new file mode 100644 index 0000000000..7b2e6045ef Binary files /dev/null and b/tests/ref/code-block-basic-syntax.png differ diff --git a/tests/ref/color-cmyk-ops.png b/tests/ref/color-cmyk-ops.png new file mode 100644 index 0000000000..4f799efa19 Binary files /dev/null and b/tests/ref/color-cmyk-ops.png differ diff --git a/tests/ref/color-luma.png b/tests/ref/color-luma.png new file mode 100644 index 0000000000..7bacc74432 Binary files /dev/null and b/tests/ref/color-luma.png differ diff --git a/tests/ref/color-outside-srgb-gamut.png b/tests/ref/color-outside-srgb-gamut.png new file mode 100644 index 0000000000..3a2806c5de Binary files /dev/null and b/tests/ref/color-outside-srgb-gamut.png differ diff --git a/tests/ref/color-rotate-hue.png b/tests/ref/color-rotate-hue.png new file mode 100644 index 0000000000..a21397141f Binary files /dev/null and b/tests/ref/color-rotate-hue.png differ diff --git a/tests/ref/color-saturation.png b/tests/ref/color-saturation.png new file mode 100644 index 0000000000..ccac48287e Binary files /dev/null and b/tests/ref/color-saturation.png differ diff --git a/tests/ref/color-spaces.png b/tests/ref/color-spaces.png new file mode 100644 index 0000000000..ade861ccb9 Binary files /dev/null and b/tests/ref/color-spaces.png differ diff --git a/tests/ref/columns-colbreak-after-place.png b/tests/ref/columns-colbreak-after-place.png new file mode 100644 index 0000000000..f6a8a63dc5 Binary files /dev/null and b/tests/ref/columns-colbreak-after-place.png differ diff --git a/tests/ref/columns-empty-second-column.png b/tests/ref/columns-empty-second-column.png new file mode 100644 index 0000000000..a00d5fb2a2 Binary files /dev/null and b/tests/ref/columns-empty-second-column.png differ diff --git a/tests/ref/columns-in-auto-sized-rect.png b/tests/ref/columns-in-auto-sized-rect.png new file mode 100644 index 0000000000..00088b7ebc Binary files /dev/null and b/tests/ref/columns-in-auto-sized-rect.png differ diff --git a/tests/ref/columns-in-fixed-size-rect.png b/tests/ref/columns-in-fixed-size-rect.png new file mode 100644 index 0000000000..28cb97cb29 Binary files /dev/null and b/tests/ref/columns-in-fixed-size-rect.png differ diff --git a/tests/ref/columns-more-with-gutter.png b/tests/ref/columns-more-with-gutter.png new file mode 100644 index 0000000000..e89c6a0b2f Binary files /dev/null and b/tests/ref/columns-more-with-gutter.png differ diff --git a/tests/ref/columns-one.png b/tests/ref/columns-one.png new file mode 100644 index 0000000000..02abf6590a Binary files /dev/null and b/tests/ref/columns-one.png differ diff --git a/tests/ref/columns-page-height-auto.png b/tests/ref/columns-page-height-auto.png new file mode 100644 index 0000000000..9b3f1f8555 Binary files /dev/null and b/tests/ref/columns-page-height-auto.png differ diff --git a/tests/ref/columns-page-width-auto.png b/tests/ref/columns-page-width-auto.png new file mode 100644 index 0000000000..04d88bc10b Binary files /dev/null and b/tests/ref/columns-page-width-auto.png differ diff --git a/tests/ref/columns-rtl.png b/tests/ref/columns-rtl.png new file mode 100644 index 0000000000..7efa57f57e Binary files /dev/null and b/tests/ref/columns-rtl.png differ diff --git a/tests/ref/columns-set-page-colbreak-pagebreak.png b/tests/ref/columns-set-page-colbreak-pagebreak.png new file mode 100644 index 0000000000..48d2fd7b15 Binary files /dev/null and b/tests/ref/columns-set-page-colbreak-pagebreak.png differ diff --git a/tests/ref/columns-set-page.png b/tests/ref/columns-set-page.png new file mode 100644 index 0000000000..42b5bea704 Binary files /dev/null and b/tests/ref/columns-set-page.png differ diff --git a/tests/ref/coma.png b/tests/ref/coma.png index fc0f6ba1d2..96f9e4d9b6 100644 Binary files a/tests/ref/coma.png and b/tests/ref/coma.png differ diff --git a/tests/ref/comment-end-of-line.png b/tests/ref/comment-end-of-line.png new file mode 100644 index 0000000000..94da23cb10 Binary files /dev/null and b/tests/ref/comment-end-of-line.png differ diff --git a/tests/ref/comments.png b/tests/ref/comments.png new file mode 100644 index 0000000000..892ff5e474 Binary files /dev/null and b/tests/ref/comments.png differ diff --git a/tests/ref/compiler/array.png b/tests/ref/compiler/array.png deleted file mode 100644 index 9b6bf8b308..0000000000 Binary files a/tests/ref/compiler/array.png and /dev/null differ diff --git a/tests/ref/compiler/block.png b/tests/ref/compiler/block.png deleted file mode 100644 index 21a38de2c7..0000000000 Binary files a/tests/ref/compiler/block.png and /dev/null differ diff --git a/tests/ref/compiler/break-continue.png b/tests/ref/compiler/break-continue.png deleted file mode 100644 index 9751d39516..0000000000 Binary files a/tests/ref/compiler/break-continue.png and /dev/null differ diff --git a/tests/ref/compiler/call.png b/tests/ref/compiler/call.png deleted file mode 100644 index 2c5d1e78ff..0000000000 Binary files a/tests/ref/compiler/call.png and /dev/null differ diff --git a/tests/ref/compiler/closure.png b/tests/ref/compiler/closure.png deleted file mode 100644 index 07c171c58e..0000000000 Binary files a/tests/ref/compiler/closure.png and /dev/null differ diff --git a/tests/ref/compiler/color.png b/tests/ref/compiler/color.png deleted file mode 100644 index 2b416f6413..0000000000 Binary files a/tests/ref/compiler/color.png and /dev/null differ diff --git a/tests/ref/compiler/comment.png b/tests/ref/compiler/comment.png deleted file mode 100644 index 608df6eae0..0000000000 Binary files a/tests/ref/compiler/comment.png and /dev/null differ diff --git a/tests/ref/compiler/construct.png b/tests/ref/compiler/construct.png deleted file mode 100644 index f1acf66541..0000000000 Binary files a/tests/ref/compiler/construct.png and /dev/null differ diff --git a/tests/ref/compiler/content-field.png b/tests/ref/compiler/content-field.png deleted file mode 100644 index 3095ba8c91..0000000000 Binary files a/tests/ref/compiler/content-field.png and /dev/null differ diff --git a/tests/ref/compiler/dict.png b/tests/ref/compiler/dict.png deleted file mode 100644 index c97b2dbf34..0000000000 Binary files a/tests/ref/compiler/dict.png and /dev/null differ diff --git a/tests/ref/compiler/for.png b/tests/ref/compiler/for.png deleted file mode 100644 index 5608248f82..0000000000 Binary files a/tests/ref/compiler/for.png and /dev/null differ diff --git a/tests/ref/compiler/highlight.png b/tests/ref/compiler/highlight.png deleted file mode 100644 index ccbbc0560a..0000000000 Binary files a/tests/ref/compiler/highlight.png and /dev/null differ diff --git a/tests/ref/compiler/if.png b/tests/ref/compiler/if.png deleted file mode 100644 index bd3adc88b3..0000000000 Binary files a/tests/ref/compiler/if.png and /dev/null differ diff --git a/tests/ref/compiler/import.png b/tests/ref/compiler/import.png deleted file mode 100644 index 5c6132d2f7..0000000000 Binary files a/tests/ref/compiler/import.png and /dev/null differ diff --git a/tests/ref/compiler/include.png b/tests/ref/compiler/include.png deleted file mode 100644 index 7fdb0310b4..0000000000 Binary files a/tests/ref/compiler/include.png and /dev/null differ diff --git a/tests/ref/compiler/label.png b/tests/ref/compiler/label.png deleted file mode 100644 index 21764f978b..0000000000 Binary files a/tests/ref/compiler/label.png and /dev/null differ diff --git a/tests/ref/compiler/let.png b/tests/ref/compiler/let.png deleted file mode 100644 index 4423fe0a30..0000000000 Binary files a/tests/ref/compiler/let.png and /dev/null differ diff --git a/tests/ref/compiler/ops.png b/tests/ref/compiler/ops.png deleted file mode 100644 index 51fb9d1a5d..0000000000 Binary files a/tests/ref/compiler/ops.png and /dev/null differ diff --git a/tests/ref/compiler/repr-color-gradient.png b/tests/ref/compiler/repr-color-gradient.png deleted file mode 100644 index 11bde774ed..0000000000 Binary files a/tests/ref/compiler/repr-color-gradient.png and /dev/null differ diff --git a/tests/ref/compiler/repr.png b/tests/ref/compiler/repr.png deleted file mode 100644 index c90cc3cdb1..0000000000 Binary files a/tests/ref/compiler/repr.png and /dev/null differ diff --git a/tests/ref/compiler/return.png b/tests/ref/compiler/return.png deleted file mode 100644 index e8fa3ab2b8..0000000000 Binary files a/tests/ref/compiler/return.png and /dev/null differ diff --git a/tests/ref/compiler/select-where-styles.png b/tests/ref/compiler/select-where-styles.png deleted file mode 100644 index ffdc4babf0..0000000000 Binary files a/tests/ref/compiler/select-where-styles.png and /dev/null differ diff --git a/tests/ref/compiler/selector-logical.png b/tests/ref/compiler/selector-logical.png deleted file mode 100644 index eafa93c811..0000000000 Binary files a/tests/ref/compiler/selector-logical.png and /dev/null differ diff --git a/tests/ref/compiler/set.png b/tests/ref/compiler/set.png deleted file mode 100644 index 264093968a..0000000000 Binary files a/tests/ref/compiler/set.png and /dev/null differ diff --git a/tests/ref/compiler/shorthand.png b/tests/ref/compiler/shorthand.png deleted file mode 100644 index 4507177bcc..0000000000 Binary files a/tests/ref/compiler/shorthand.png and /dev/null differ diff --git a/tests/ref/compiler/show-bare.png b/tests/ref/compiler/show-bare.png deleted file mode 100644 index c6a1e1013f..0000000000 Binary files a/tests/ref/compiler/show-bare.png and /dev/null differ diff --git a/tests/ref/compiler/show-node.png b/tests/ref/compiler/show-node.png deleted file mode 100644 index 396e5429e8..0000000000 Binary files a/tests/ref/compiler/show-node.png and /dev/null differ diff --git a/tests/ref/compiler/show-recursive.png b/tests/ref/compiler/show-recursive.png deleted file mode 100644 index a5a153c07c..0000000000 Binary files a/tests/ref/compiler/show-recursive.png and /dev/null differ diff --git a/tests/ref/compiler/show-selector-logical.png b/tests/ref/compiler/show-selector-logical.png deleted file mode 100644 index a7a8005303..0000000000 Binary files a/tests/ref/compiler/show-selector-logical.png and /dev/null differ diff --git a/tests/ref/compiler/show-selector.png b/tests/ref/compiler/show-selector.png deleted file mode 100644 index 52e99c9ac7..0000000000 Binary files a/tests/ref/compiler/show-selector.png and /dev/null differ diff --git a/tests/ref/compiler/show-set-func.png b/tests/ref/compiler/show-set-func.png deleted file mode 100644 index c5ff24893b..0000000000 Binary files a/tests/ref/compiler/show-set-func.png and /dev/null differ diff --git a/tests/ref/compiler/show-set-text.png b/tests/ref/compiler/show-set-text.png deleted file mode 100644 index 27803e8a41..0000000000 Binary files a/tests/ref/compiler/show-set-text.png and /dev/null differ diff --git a/tests/ref/compiler/show-set.png b/tests/ref/compiler/show-set.png deleted file mode 100644 index e87fc60021..0000000000 Binary files a/tests/ref/compiler/show-set.png and /dev/null differ diff --git a/tests/ref/compiler/show-text.png b/tests/ref/compiler/show-text.png deleted file mode 100644 index 2026cc35ca..0000000000 Binary files a/tests/ref/compiler/show-text.png and /dev/null differ diff --git a/tests/ref/compiler/while.png b/tests/ref/compiler/while.png deleted file mode 100644 index d0f8647369..0000000000 Binary files a/tests/ref/compiler/while.png and /dev/null differ diff --git a/tests/ref/compute/construct.png b/tests/ref/compute/construct.png deleted file mode 100644 index e17174733e..0000000000 Binary files a/tests/ref/compute/construct.png and /dev/null differ diff --git a/tests/ref/compute/data.png b/tests/ref/compute/data.png deleted file mode 100644 index 2dab6875b7..0000000000 Binary files a/tests/ref/compute/data.png and /dev/null differ diff --git a/tests/ref/compute/eval-path.png b/tests/ref/compute/eval-path.png deleted file mode 100644 index c59dd2aa47..0000000000 Binary files a/tests/ref/compute/eval-path.png and /dev/null differ diff --git a/tests/ref/compute/foundations.png b/tests/ref/compute/foundations.png deleted file mode 100644 index 5d6ba7441a..0000000000 Binary files a/tests/ref/compute/foundations.png and /dev/null differ diff --git a/tests/ref/container-layoutable-child.png b/tests/ref/container-layoutable-child.png new file mode 100644 index 0000000000..972ccb6153 Binary files /dev/null and b/tests/ref/container-layoutable-child.png differ diff --git a/tests/ref/content-field-materialized-heading.png b/tests/ref/content-field-materialized-heading.png new file mode 100644 index 0000000000..722016278b Binary files /dev/null and b/tests/ref/content-field-materialized-heading.png differ diff --git a/tests/ref/content-field-materialized-query.png b/tests/ref/content-field-materialized-query.png new file mode 100644 index 0000000000..2d2a14806f Binary files /dev/null and b/tests/ref/content-field-materialized-query.png differ diff --git a/tests/ref/content-field-materialized-table.png b/tests/ref/content-field-materialized-table.png new file mode 100644 index 0000000000..09efe75389 Binary files /dev/null and b/tests/ref/content-field-materialized-table.png differ diff --git a/tests/ref/content-fields-complex.png b/tests/ref/content-fields-complex.png new file mode 100644 index 0000000000..624a8b332e Binary files /dev/null and b/tests/ref/content-fields-complex.png differ diff --git a/tests/ref/content-label-field-access.png b/tests/ref/content-label-field-access.png new file mode 100644 index 0000000000..bdb7c0f2fe Binary files /dev/null and b/tests/ref/content-label-field-access.png differ diff --git a/tests/ref/content-label-fields-method.png b/tests/ref/content-label-fields-method.png new file mode 100644 index 0000000000..bdb7c0f2fe Binary files /dev/null and b/tests/ref/content-label-fields-method.png differ diff --git a/tests/ref/content-label-has-method.png b/tests/ref/content-label-has-method.png new file mode 100644 index 0000000000..bdb7c0f2fe Binary files /dev/null and b/tests/ref/content-label-has-method.png differ diff --git a/tests/ref/context-compatibility-locate.png b/tests/ref/context-compatibility-locate.png new file mode 100644 index 0000000000..4c8944ab4d Binary files /dev/null and b/tests/ref/context-compatibility-locate.png differ diff --git a/tests/ref/context-compatibility-styling.png b/tests/ref/context-compatibility-styling.png new file mode 100644 index 0000000000..aee16c3ab6 Binary files /dev/null and b/tests/ref/context-compatibility-styling.png differ diff --git a/tests/ref/counter-basic-1.png b/tests/ref/counter-basic-1.png new file mode 100644 index 0000000000..9228259480 Binary files /dev/null and b/tests/ref/counter-basic-1.png differ diff --git a/tests/ref/counter-figure.png b/tests/ref/counter-figure.png new file mode 100644 index 0000000000..5e4a4a5f39 Binary files /dev/null and b/tests/ref/counter-figure.png differ diff --git a/tests/ref/counter-heading.png b/tests/ref/counter-heading.png new file mode 100644 index 0000000000..96dafd6ad4 Binary files /dev/null and b/tests/ref/counter-heading.png differ diff --git a/tests/ref/counter-label.png b/tests/ref/counter-label.png new file mode 100644 index 0000000000..6fea90df7d Binary files /dev/null and b/tests/ref/counter-label.png differ diff --git a/tests/ref/counter-page.png b/tests/ref/counter-page.png new file mode 100644 index 0000000000..be1653ebdd Binary files /dev/null and b/tests/ref/counter-page.png differ diff --git a/tests/ref/csv.png b/tests/ref/csv.png new file mode 100644 index 0000000000..fd0c9a1cd5 Binary files /dev/null and b/tests/ref/csv.png differ diff --git a/tests/ref/destructuring-during-loop-continue.png b/tests/ref/destructuring-during-loop-continue.png new file mode 100644 index 0000000000..9ea8e3c1f0 Binary files /dev/null and b/tests/ref/destructuring-during-loop-continue.png differ diff --git a/tests/ref/dict-basic-methods.png b/tests/ref/dict-basic-methods.png new file mode 100644 index 0000000000..20410cc3bf Binary files /dev/null and b/tests/ref/dict-basic-methods.png differ diff --git a/tests/ref/dict-basic-syntax.png b/tests/ref/dict-basic-syntax.png new file mode 100644 index 0000000000..02effef673 Binary files /dev/null and b/tests/ref/dict-basic-syntax.png differ diff --git a/tests/ref/dict-remove-order.png b/tests/ref/dict-remove-order.png new file mode 100644 index 0000000000..20410cc3bf Binary files /dev/null and b/tests/ref/dict-remove-order.png differ diff --git a/tests/ref/document-set-title.png b/tests/ref/document-set-title.png new file mode 100644 index 0000000000..74bcfe191a Binary files /dev/null and b/tests/ref/document-set-title.png differ diff --git a/tests/ref/ellipse-auto-sizing.png b/tests/ref/ellipse-auto-sizing.png new file mode 100644 index 0000000000..ed20152153 Binary files /dev/null and b/tests/ref/ellipse-auto-sizing.png differ diff --git a/tests/ref/ellipse.png b/tests/ref/ellipse.png new file mode 100644 index 0000000000..0f4e92cace Binary files /dev/null and b/tests/ref/ellipse.png differ diff --git a/tests/ref/emph-and-strong-call-in-word.png b/tests/ref/emph-and-strong-call-in-word.png new file mode 100644 index 0000000000..4720f9949b Binary files /dev/null and b/tests/ref/emph-and-strong-call-in-word.png differ diff --git a/tests/ref/emph-double-underscore-empty-hint.png b/tests/ref/emph-double-underscore-empty-hint.png new file mode 100644 index 0000000000..a940dfb6ab Binary files /dev/null and b/tests/ref/emph-double-underscore-empty-hint.png differ diff --git a/tests/ref/emph-syntax.png b/tests/ref/emph-syntax.png new file mode 100644 index 0000000000..66f117a8e7 Binary files /dev/null and b/tests/ref/emph-syntax.png differ diff --git a/tests/ref/empty.png b/tests/ref/empty.png deleted file mode 100644 index db3a66950b..0000000000 Binary files a/tests/ref/empty.png and /dev/null differ diff --git a/tests/ref/enum-built-in-loop.png b/tests/ref/enum-built-in-loop.png new file mode 100644 index 0000000000..298518da33 Binary files /dev/null and b/tests/ref/enum-built-in-loop.png differ diff --git a/tests/ref/enum-function-call.png b/tests/ref/enum-function-call.png new file mode 100644 index 0000000000..a451f27c7f Binary files /dev/null and b/tests/ref/enum-function-call.png differ diff --git a/tests/ref/enum-number-align-2d.png b/tests/ref/enum-number-align-2d.png new file mode 100644 index 0000000000..e205844ff5 Binary files /dev/null and b/tests/ref/enum-number-align-2d.png differ diff --git a/tests/ref/enum-number-align-default.png b/tests/ref/enum-number-align-default.png new file mode 100644 index 0000000000..c47f9001a6 Binary files /dev/null and b/tests/ref/enum-number-align-default.png differ diff --git a/tests/ref/enum-number-align-specified.png b/tests/ref/enum-number-align-specified.png new file mode 100644 index 0000000000..b2f2d619a4 Binary files /dev/null and b/tests/ref/enum-number-align-specified.png differ diff --git a/tests/ref/enum-number-align-unaffected.png b/tests/ref/enum-number-align-unaffected.png new file mode 100644 index 0000000000..3abcaaab79 Binary files /dev/null and b/tests/ref/enum-number-align-unaffected.png differ diff --git a/tests/ref/enum-number-align-unfolded.png b/tests/ref/enum-number-align-unfolded.png new file mode 100644 index 0000000000..8c4f294338 Binary files /dev/null and b/tests/ref/enum-number-align-unfolded.png differ diff --git a/tests/ref/enum-number-override-nested.png b/tests/ref/enum-number-override-nested.png new file mode 100644 index 0000000000..22bb7611eb Binary files /dev/null and b/tests/ref/enum-number-override-nested.png differ diff --git a/tests/ref/enum-number-override.png b/tests/ref/enum-number-override.png new file mode 100644 index 0000000000..65c0f9d845 Binary files /dev/null and b/tests/ref/enum-number-override.png differ diff --git a/tests/ref/enum-numbering-closure-nested-complex.png b/tests/ref/enum-numbering-closure-nested-complex.png new file mode 100644 index 0000000000..a756f37cd5 Binary files /dev/null and b/tests/ref/enum-numbering-closure-nested-complex.png differ diff --git a/tests/ref/enum-numbering-closure-nested.png b/tests/ref/enum-numbering-closure-nested.png new file mode 100644 index 0000000000..25a5c42d59 Binary files /dev/null and b/tests/ref/enum-numbering-closure-nested.png differ diff --git a/tests/ref/enum-numbering-closure.png b/tests/ref/enum-numbering-closure.png new file mode 100644 index 0000000000..bf86f55418 Binary files /dev/null and b/tests/ref/enum-numbering-closure.png differ diff --git a/tests/ref/enum-numbering-full.png b/tests/ref/enum-numbering-full.png new file mode 100644 index 0000000000..46449e5730 Binary files /dev/null and b/tests/ref/enum-numbering-full.png differ diff --git a/tests/ref/enum-numbering-pattern.png b/tests/ref/enum-numbering-pattern.png new file mode 100644 index 0000000000..4ecb9e4a2d Binary files /dev/null and b/tests/ref/enum-numbering-pattern.png differ diff --git a/tests/ref/enum-syntax-at-start.png b/tests/ref/enum-syntax-at-start.png new file mode 100644 index 0000000000..ce9f3967e1 Binary files /dev/null and b/tests/ref/enum-syntax-at-start.png differ diff --git a/tests/ref/enum-syntax-edge-cases.png b/tests/ref/enum-syntax-edge-cases.png new file mode 100644 index 0000000000..496dc8e3df Binary files /dev/null and b/tests/ref/enum-syntax-edge-cases.png differ diff --git a/tests/ref/escape.png b/tests/ref/escape.png new file mode 100644 index 0000000000..0b49606ca8 Binary files /dev/null and b/tests/ref/escape.png differ diff --git a/tests/ref/eval-in-show-rule.png b/tests/ref/eval-in-show-rule.png new file mode 100644 index 0000000000..91a038683f Binary files /dev/null and b/tests/ref/eval-in-show-rule.png differ diff --git a/tests/ref/eval-mode.png b/tests/ref/eval-mode.png new file mode 100644 index 0000000000..94357ff4ff Binary files /dev/null and b/tests/ref/eval-mode.png differ diff --git a/tests/ref/eval-path-resolve-in-show-rule.png b/tests/ref/eval-path-resolve-in-show-rule.png new file mode 100644 index 0000000000..cf5c183ad8 Binary files /dev/null and b/tests/ref/eval-path-resolve-in-show-rule.png differ diff --git a/tests/ref/eval-path-resolve.png b/tests/ref/eval-path-resolve.png new file mode 100644 index 0000000000..cf5c183ad8 Binary files /dev/null and b/tests/ref/eval-path-resolve.png differ diff --git a/tests/ref/field-function.png b/tests/ref/field-function.png new file mode 100644 index 0000000000..261fb39571 Binary files /dev/null and b/tests/ref/field-function.png differ diff --git a/tests/ref/figure-and-caption-show.png b/tests/ref/figure-and-caption-show.png new file mode 100644 index 0000000000..daf8f2b620 Binary files /dev/null and b/tests/ref/figure-and-caption-show.png differ diff --git a/tests/ref/figure-basic.png b/tests/ref/figure-basic.png new file mode 100644 index 0000000000..22a841db5e Binary files /dev/null and b/tests/ref/figure-basic.png differ diff --git a/tests/ref/figure-breakable.png b/tests/ref/figure-breakable.png new file mode 100644 index 0000000000..40cb3ec577 Binary files /dev/null and b/tests/ref/figure-breakable.png differ diff --git a/tests/ref/figure-caption-separator.png b/tests/ref/figure-caption-separator.png new file mode 100644 index 0000000000..e645f01fed Binary files /dev/null and b/tests/ref/figure-caption-separator.png differ diff --git a/tests/ref/figure-caption-show.png b/tests/ref/figure-caption-show.png new file mode 100644 index 0000000000..4ed6443ac3 Binary files /dev/null and b/tests/ref/figure-caption-show.png differ diff --git a/tests/ref/figure-caption-where-selector.png b/tests/ref/figure-caption-where-selector.png new file mode 100644 index 0000000000..08eb46f60e Binary files /dev/null and b/tests/ref/figure-caption-where-selector.png differ diff --git a/tests/ref/figure-localization-fr.png b/tests/ref/figure-localization-fr.png new file mode 100644 index 0000000000..665b35522e Binary files /dev/null and b/tests/ref/figure-localization-fr.png differ diff --git a/tests/ref/figure-localization-gr.png b/tests/ref/figure-localization-gr.png new file mode 100644 index 0000000000..46b52b051c Binary files /dev/null and b/tests/ref/figure-localization-gr.png differ diff --git a/tests/ref/figure-localization-ru.png b/tests/ref/figure-localization-ru.png new file mode 100644 index 0000000000..102df59718 Binary files /dev/null and b/tests/ref/figure-localization-ru.png differ diff --git a/tests/ref/figure-localization-zh.png b/tests/ref/figure-localization-zh.png new file mode 100644 index 0000000000..f7625b1b40 Binary files /dev/null and b/tests/ref/figure-localization-zh.png differ diff --git a/tests/ref/figure-table.png b/tests/ref/figure-table.png new file mode 100644 index 0000000000..9a09f65900 Binary files /dev/null and b/tests/ref/figure-table.png differ diff --git a/tests/ref/figure-theorem.png b/tests/ref/figure-theorem.png new file mode 100644 index 0000000000..10d6eeacb6 Binary files /dev/null and b/tests/ref/figure-theorem.png differ diff --git a/tests/ref/float-display.png b/tests/ref/float-display.png new file mode 100644 index 0000000000..6c33b372f0 Binary files /dev/null and b/tests/ref/float-display.png differ diff --git a/tests/ref/float-repr.png b/tests/ref/float-repr.png new file mode 100644 index 0000000000..8b51096973 Binary files /dev/null and b/tests/ref/float-repr.png differ diff --git a/tests/ref/flow-first-region-counter-update-and-placed.png b/tests/ref/flow-first-region-counter-update-and-placed.png new file mode 100644 index 0000000000..213167199c Binary files /dev/null and b/tests/ref/flow-first-region-counter-update-and-placed.png differ diff --git a/tests/ref/flow-first-region-counter-update-placed-and-line.png b/tests/ref/flow-first-region-counter-update-placed-and-line.png new file mode 100644 index 0000000000..95ca518e40 Binary files /dev/null and b/tests/ref/flow-first-region-counter-update-placed-and-line.png differ diff --git a/tests/ref/flow-first-region-counter-update.png b/tests/ref/flow-first-region-counter-update.png new file mode 100644 index 0000000000..8e883335ae Binary files /dev/null and b/tests/ref/flow-first-region-counter-update.png differ diff --git a/tests/ref/flow-first-region-no-item.png b/tests/ref/flow-first-region-no-item.png new file mode 100644 index 0000000000..e888898c01 Binary files /dev/null and b/tests/ref/flow-first-region-no-item.png differ diff --git a/tests/ref/flow-first-region-placed.png b/tests/ref/flow-first-region-placed.png new file mode 100644 index 0000000000..cae4aa32f6 Binary files /dev/null and b/tests/ref/flow-first-region-placed.png differ diff --git a/tests/ref/flow-first-region-zero-sized-item.png b/tests/ref/flow-first-region-zero-sized-item.png new file mode 100644 index 0000000000..2e75fcfe79 Binary files /dev/null and b/tests/ref/flow-first-region-zero-sized-item.png differ diff --git a/tests/ref/flow-fr.png b/tests/ref/flow-fr.png new file mode 100644 index 0000000000..b09a960424 Binary files /dev/null and b/tests/ref/flow-fr.png differ diff --git a/tests/ref/flow-heading-no-orphan.png b/tests/ref/flow-heading-no-orphan.png new file mode 100644 index 0000000000..87789ea111 Binary files /dev/null and b/tests/ref/flow-heading-no-orphan.png differ diff --git a/tests/ref/flow-par-no-orphan-and-widow-lines.png b/tests/ref/flow-par-no-orphan-and-widow-lines.png new file mode 100644 index 0000000000..cace5d4488 Binary files /dev/null and b/tests/ref/flow-par-no-orphan-and-widow-lines.png differ diff --git a/tests/ref/fold-vec-order-meta.png b/tests/ref/fold-vec-order-meta.png new file mode 100644 index 0000000000..36e3cd5155 Binary files /dev/null and b/tests/ref/fold-vec-order-meta.png differ diff --git a/tests/ref/fold-vec-order-text-decos.png b/tests/ref/fold-vec-order-text-decos.png new file mode 100644 index 0000000000..62c9e1af98 Binary files /dev/null and b/tests/ref/fold-vec-order-text-decos.png differ diff --git a/tests/ref/fold-vec-order-text-features.png b/tests/ref/fold-vec-order-text-features.png new file mode 100644 index 0000000000..f2ff6f25a4 Binary files /dev/null and b/tests/ref/fold-vec-order-text-features.png differ diff --git a/tests/ref/footnote-basic.png b/tests/ref/footnote-basic.png new file mode 100644 index 0000000000..3562438bd4 Binary files /dev/null and b/tests/ref/footnote-basic.png differ diff --git a/tests/ref/footnote-break-across-pages.png b/tests/ref/footnote-break-across-pages.png new file mode 100644 index 0000000000..8ec55418c4 Binary files /dev/null and b/tests/ref/footnote-break-across-pages.png differ diff --git a/tests/ref/footnote-duplicate.png b/tests/ref/footnote-duplicate.png new file mode 100644 index 0000000000..7c83b8de63 Binary files /dev/null and b/tests/ref/footnote-duplicate.png differ diff --git a/tests/ref/footnote-entry.png b/tests/ref/footnote-entry.png new file mode 100644 index 0000000000..e62315c464 Binary files /dev/null and b/tests/ref/footnote-entry.png differ diff --git a/tests/ref/footnote-in-caption.png b/tests/ref/footnote-in-caption.png new file mode 100644 index 0000000000..8d548c5962 Binary files /dev/null and b/tests/ref/footnote-in-caption.png differ diff --git a/tests/ref/footnote-in-columns.png b/tests/ref/footnote-in-columns.png new file mode 100644 index 0000000000..e16b4ebcfa Binary files /dev/null and b/tests/ref/footnote-in-columns.png differ diff --git a/tests/ref/footnote-in-table.png b/tests/ref/footnote-in-table.png new file mode 100644 index 0000000000..0fd0acc7f0 Binary files /dev/null and b/tests/ref/footnote-in-table.png differ diff --git a/tests/ref/footnote-invariant.png b/tests/ref/footnote-invariant.png new file mode 100644 index 0000000000..c49c268d44 Binary files /dev/null and b/tests/ref/footnote-invariant.png differ diff --git a/tests/ref/footnote-nested-same-frame.png b/tests/ref/footnote-nested-same-frame.png new file mode 100644 index 0000000000..b22276d5cc Binary files /dev/null and b/tests/ref/footnote-nested-same-frame.png differ diff --git a/tests/ref/footnote-nested.png b/tests/ref/footnote-nested.png new file mode 100644 index 0000000000..fecf2e8de7 Binary files /dev/null and b/tests/ref/footnote-nested.png differ diff --git a/tests/ref/footnote-ref-call.png b/tests/ref/footnote-ref-call.png new file mode 100644 index 0000000000..3c795302e3 Binary files /dev/null and b/tests/ref/footnote-ref-call.png differ diff --git a/tests/ref/footnote-ref-forward.png b/tests/ref/footnote-ref-forward.png new file mode 100644 index 0000000000..e67671bec4 Binary files /dev/null and b/tests/ref/footnote-ref-forward.png differ diff --git a/tests/ref/footnote-ref-in-footnote.png b/tests/ref/footnote-ref-in-footnote.png new file mode 100644 index 0000000000..4718a0887d Binary files /dev/null and b/tests/ref/footnote-ref-in-footnote.png differ diff --git a/tests/ref/footnote-ref-multiple.png b/tests/ref/footnote-ref-multiple.png new file mode 100644 index 0000000000..fc6f11cf54 Binary files /dev/null and b/tests/ref/footnote-ref-multiple.png differ diff --git a/tests/ref/footnote-ref.png b/tests/ref/footnote-ref.png new file mode 100644 index 0000000000..517d997aaf Binary files /dev/null and b/tests/ref/footnote-ref.png differ diff --git a/tests/ref/footnote-space-collapsing.png b/tests/ref/footnote-space-collapsing.png new file mode 100644 index 0000000000..d7d02704dc Binary files /dev/null and b/tests/ref/footnote-space-collapsing.png differ diff --git a/tests/ref/footnote-styling.png b/tests/ref/footnote-styling.png new file mode 100644 index 0000000000..fd7684af7a Binary files /dev/null and b/tests/ref/footnote-styling.png differ diff --git a/tests/ref/for-loop-basic.png b/tests/ref/for-loop-basic.png new file mode 100644 index 0000000000..42d611eff1 Binary files /dev/null and b/tests/ref/for-loop-basic.png differ diff --git a/tests/ref/gradient-conic-angled.png b/tests/ref/gradient-conic-angled.png new file mode 100644 index 0000000000..163366e6c4 Binary files /dev/null and b/tests/ref/gradient-conic-angled.png differ diff --git a/tests/ref/gradient-conic-center-shifted-1.png b/tests/ref/gradient-conic-center-shifted-1.png new file mode 100644 index 0000000000..5964b124a7 Binary files /dev/null and b/tests/ref/gradient-conic-center-shifted-1.png differ diff --git a/tests/ref/gradient-conic-center-shifted-2.png b/tests/ref/gradient-conic-center-shifted-2.png new file mode 100644 index 0000000000..53e5da9822 Binary files /dev/null and b/tests/ref/gradient-conic-center-shifted-2.png differ diff --git a/tests/ref/gradient-conic-hsl.png b/tests/ref/gradient-conic-hsl.png new file mode 100644 index 0000000000..321a3b07cc Binary files /dev/null and b/tests/ref/gradient-conic-hsl.png differ diff --git a/tests/ref/gradient-conic-hsv.png b/tests/ref/gradient-conic-hsv.png new file mode 100644 index 0000000000..648e1fb501 Binary files /dev/null and b/tests/ref/gradient-conic-hsv.png differ diff --git a/tests/ref/gradient-conic-oklab.png b/tests/ref/gradient-conic-oklab.png new file mode 100644 index 0000000000..e567eacc16 Binary files /dev/null and b/tests/ref/gradient-conic-oklab.png differ diff --git a/tests/ref/gradient-conic-oklch.png b/tests/ref/gradient-conic-oklch.png new file mode 100644 index 0000000000..f712defab1 Binary files /dev/null and b/tests/ref/gradient-conic-oklch.png differ diff --git a/tests/ref/gradient-conic-relative-parent.png b/tests/ref/gradient-conic-relative-parent.png new file mode 100644 index 0000000000..1685ca446e Binary files /dev/null and b/tests/ref/gradient-conic-relative-parent.png differ diff --git a/tests/ref/gradient-conic-relative-self.png b/tests/ref/gradient-conic-relative-self.png new file mode 100644 index 0000000000..108fe43a61 Binary files /dev/null and b/tests/ref/gradient-conic-relative-self.png differ diff --git a/tests/ref/gradient-conic-stroke.png b/tests/ref/gradient-conic-stroke.png new file mode 100644 index 0000000000..ae631fd4fc Binary files /dev/null and b/tests/ref/gradient-conic-stroke.png differ diff --git a/tests/ref/gradient-conic-text.png b/tests/ref/gradient-conic-text.png new file mode 100644 index 0000000000..1abef3cb84 Binary files /dev/null and b/tests/ref/gradient-conic-text.png differ diff --git a/tests/ref/gradient-conic.png b/tests/ref/gradient-conic.png new file mode 100644 index 0000000000..0f5f5bada5 Binary files /dev/null and b/tests/ref/gradient-conic.png differ diff --git a/tests/ref/gradient-fill-and-stroke.png b/tests/ref/gradient-fill-and-stroke.png new file mode 100644 index 0000000000..7856351716 Binary files /dev/null and b/tests/ref/gradient-fill-and-stroke.png differ diff --git a/tests/ref/gradient-linear-angled.png b/tests/ref/gradient-linear-angled.png new file mode 100644 index 0000000000..b195b1281d Binary files /dev/null and b/tests/ref/gradient-linear-angled.png differ diff --git a/tests/ref/gradient-linear-hsl.png b/tests/ref/gradient-linear-hsl.png new file mode 100644 index 0000000000..7bfe958bf4 Binary files /dev/null and b/tests/ref/gradient-linear-hsl.png differ diff --git a/tests/ref/gradient-linear-hsv.png b/tests/ref/gradient-linear-hsv.png new file mode 100644 index 0000000000..56b446f280 Binary files /dev/null and b/tests/ref/gradient-linear-hsv.png differ diff --git a/tests/ref/gradient-linear-line.png b/tests/ref/gradient-linear-line.png new file mode 100644 index 0000000000..d32aba8923 Binary files /dev/null and b/tests/ref/gradient-linear-line.png differ diff --git a/tests/ref/gradient-linear-oklab.png b/tests/ref/gradient-linear-oklab.png new file mode 100644 index 0000000000..6f963c7729 Binary files /dev/null and b/tests/ref/gradient-linear-oklab.png differ diff --git a/tests/ref/gradient-linear-oklch.png b/tests/ref/gradient-linear-oklch.png new file mode 100644 index 0000000000..394d0935e3 Binary files /dev/null and b/tests/ref/gradient-linear-oklch.png differ diff --git a/tests/ref/gradient-linear-relative-parent.png b/tests/ref/gradient-linear-relative-parent.png new file mode 100644 index 0000000000..2ad1286e80 Binary files /dev/null and b/tests/ref/gradient-linear-relative-parent.png differ diff --git a/tests/ref/gradient-linear-relative-self.png b/tests/ref/gradient-linear-relative-self.png new file mode 100644 index 0000000000..d573a892fd Binary files /dev/null and b/tests/ref/gradient-linear-relative-self.png differ diff --git a/tests/ref/gradient-linear-repeat-and-mirror-1.png b/tests/ref/gradient-linear-repeat-and-mirror-1.png new file mode 100644 index 0000000000..9640d5e203 Binary files /dev/null and b/tests/ref/gradient-linear-repeat-and-mirror-1.png differ diff --git a/tests/ref/gradient-linear-repeat-and-mirror-2.png b/tests/ref/gradient-linear-repeat-and-mirror-2.png new file mode 100644 index 0000000000..98cf254349 Binary files /dev/null and b/tests/ref/gradient-linear-repeat-and-mirror-2.png differ diff --git a/tests/ref/gradient-linear-repeat-and-mirror-3.png b/tests/ref/gradient-linear-repeat-and-mirror-3.png new file mode 100644 index 0000000000..641e54c9c6 Binary files /dev/null and b/tests/ref/gradient-linear-repeat-and-mirror-3.png differ diff --git a/tests/ref/gradient-linear-sharp-and-repeat.png b/tests/ref/gradient-linear-sharp-and-repeat.png new file mode 100644 index 0000000000..e46af7a05e Binary files /dev/null and b/tests/ref/gradient-linear-sharp-and-repeat.png differ diff --git a/tests/ref/gradient-linear-sharp-and-smooth.png b/tests/ref/gradient-linear-sharp-and-smooth.png new file mode 100644 index 0000000000..5bd74d2476 Binary files /dev/null and b/tests/ref/gradient-linear-sharp-and-smooth.png differ diff --git a/tests/ref/gradient-linear-sharp-repeat-and-mirror.png b/tests/ref/gradient-linear-sharp-repeat-and-mirror.png new file mode 100644 index 0000000000..5b4b9817b0 Binary files /dev/null and b/tests/ref/gradient-linear-sharp-repeat-and-mirror.png differ diff --git a/tests/ref/gradient-linear-sharp.png b/tests/ref/gradient-linear-sharp.png new file mode 100644 index 0000000000..4d63884f75 Binary files /dev/null and b/tests/ref/gradient-linear-sharp.png differ diff --git a/tests/ref/gradient-linear-stroke.png b/tests/ref/gradient-linear-stroke.png new file mode 100644 index 0000000000..490ffec265 Binary files /dev/null and b/tests/ref/gradient-linear-stroke.png differ diff --git a/tests/ref/gradient-math-cancel.png b/tests/ref/gradient-math-cancel.png new file mode 100644 index 0000000000..0769d6d36b Binary files /dev/null and b/tests/ref/gradient-math-cancel.png differ diff --git a/tests/ref/gradient-math-conic.png b/tests/ref/gradient-math-conic.png new file mode 100644 index 0000000000..88ff7a85c6 Binary files /dev/null and b/tests/ref/gradient-math-conic.png differ diff --git a/tests/ref/gradient-math-dir.png b/tests/ref/gradient-math-dir.png new file mode 100644 index 0000000000..5ed1918216 Binary files /dev/null and b/tests/ref/gradient-math-dir.png differ diff --git a/tests/ref/gradient-math-frac.png b/tests/ref/gradient-math-frac.png new file mode 100644 index 0000000000..1316dc47af Binary files /dev/null and b/tests/ref/gradient-math-frac.png differ diff --git a/tests/ref/gradient-math-mat.png b/tests/ref/gradient-math-mat.png new file mode 100644 index 0000000000..aa3332b96c Binary files /dev/null and b/tests/ref/gradient-math-mat.png differ diff --git a/tests/ref/gradient-math-misc.png b/tests/ref/gradient-math-misc.png new file mode 100644 index 0000000000..b8fbdd7455 Binary files /dev/null and b/tests/ref/gradient-math-misc.png differ diff --git a/tests/ref/gradient-math-radial.png b/tests/ref/gradient-math-radial.png new file mode 100644 index 0000000000..c9b966b29c Binary files /dev/null and b/tests/ref/gradient-math-radial.png differ diff --git a/tests/ref/gradient-math-root.png b/tests/ref/gradient-math-root.png new file mode 100644 index 0000000000..4c2e4272e7 Binary files /dev/null and b/tests/ref/gradient-math-root.png differ diff --git a/tests/ref/gradient-math-underover.png b/tests/ref/gradient-math-underover.png new file mode 100644 index 0000000000..8909805112 Binary files /dev/null and b/tests/ref/gradient-math-underover.png differ diff --git a/tests/ref/gradient-presets.png b/tests/ref/gradient-presets.png new file mode 100644 index 0000000000..0c7fabdd7d Binary files /dev/null and b/tests/ref/gradient-presets.png differ diff --git a/tests/ref/gradient-radial-center.png b/tests/ref/gradient-radial-center.png new file mode 100644 index 0000000000..e89e1f30bb Binary files /dev/null and b/tests/ref/gradient-radial-center.png differ diff --git a/tests/ref/gradient-radial-focal-center-and-radius.png b/tests/ref/gradient-radial-focal-center-and-radius.png new file mode 100644 index 0000000000..4bc8a5d6d4 Binary files /dev/null and b/tests/ref/gradient-radial-focal-center-and-radius.png differ diff --git a/tests/ref/gradient-radial-hsl.png b/tests/ref/gradient-radial-hsl.png new file mode 100644 index 0000000000..4a2ded1862 Binary files /dev/null and b/tests/ref/gradient-radial-hsl.png differ diff --git a/tests/ref/gradient-radial-radius.png b/tests/ref/gradient-radial-radius.png new file mode 100644 index 0000000000..1037e63f23 Binary files /dev/null and b/tests/ref/gradient-radial-radius.png differ diff --git a/tests/ref/gradient-radial-relative-parent.png b/tests/ref/gradient-radial-relative-parent.png new file mode 100644 index 0000000000..f8addbe01b Binary files /dev/null and b/tests/ref/gradient-radial-relative-parent.png differ diff --git a/tests/ref/gradient-radial-relative-self.png b/tests/ref/gradient-radial-relative-self.png new file mode 100644 index 0000000000..f5fc683640 Binary files /dev/null and b/tests/ref/gradient-radial-relative-self.png differ diff --git a/tests/ref/gradient-radial-text.png b/tests/ref/gradient-radial-text.png new file mode 100644 index 0000000000..6da0987801 Binary files /dev/null and b/tests/ref/gradient-radial-text.png differ diff --git a/tests/ref/gradient-repr.png b/tests/ref/gradient-repr.png new file mode 100644 index 0000000000..04908e5945 Binary files /dev/null and b/tests/ref/gradient-repr.png differ diff --git a/tests/ref/gradient-text-decoration.png b/tests/ref/gradient-text-decoration.png new file mode 100644 index 0000000000..d1713c99de Binary files /dev/null and b/tests/ref/gradient-text-decoration.png differ diff --git a/tests/ref/gradient-text-dir.png b/tests/ref/gradient-text-dir.png new file mode 100644 index 0000000000..eab56d66e7 Binary files /dev/null and b/tests/ref/gradient-text-dir.png differ diff --git a/tests/ref/gradient-text-global.png b/tests/ref/gradient-text-global.png new file mode 100644 index 0000000000..7892fbb2c2 Binary files /dev/null and b/tests/ref/gradient-text-global.png differ diff --git a/tests/ref/gradient-text-in-container.png b/tests/ref/gradient-text-in-container.png new file mode 100644 index 0000000000..9122a556da Binary files /dev/null and b/tests/ref/gradient-text-in-container.png differ diff --git a/tests/ref/gradient-text-rotate.png b/tests/ref/gradient-text-rotate.png new file mode 100644 index 0000000000..a32cacf851 Binary files /dev/null and b/tests/ref/gradient-text-rotate.png differ diff --git a/tests/ref/gradient-transformed.png b/tests/ref/gradient-transformed.png new file mode 100644 index 0000000000..2ad1286e80 Binary files /dev/null and b/tests/ref/gradient-transformed.png differ diff --git a/tests/ref/grid-align.png b/tests/ref/grid-align.png new file mode 100644 index 0000000000..f85abf6949 Binary files /dev/null and b/tests/ref/grid-align.png differ diff --git a/tests/ref/grid-auto-shrink.png b/tests/ref/grid-auto-shrink.png new file mode 100644 index 0000000000..27813e2611 Binary files /dev/null and b/tests/ref/grid-auto-shrink.png differ diff --git a/tests/ref/grid-breaking-expand-vertically.png b/tests/ref/grid-breaking-expand-vertically.png new file mode 100644 index 0000000000..14434d7c43 Binary files /dev/null and b/tests/ref/grid-breaking-expand-vertically.png differ diff --git a/tests/ref/grid-calendar.png b/tests/ref/grid-calendar.png new file mode 100644 index 0000000000..0609b84f05 Binary files /dev/null and b/tests/ref/grid-calendar.png differ diff --git a/tests/ref/grid-cell-align-override.png b/tests/ref/grid-cell-align-override.png new file mode 100644 index 0000000000..8ffde97f57 Binary files /dev/null and b/tests/ref/grid-cell-align-override.png differ diff --git a/tests/ref/grid-cell-breaking.png b/tests/ref/grid-cell-breaking.png new file mode 100644 index 0000000000..c91a399321 Binary files /dev/null and b/tests/ref/grid-cell-breaking.png differ diff --git a/tests/ref/grid-cell-folding.png b/tests/ref/grid-cell-folding.png new file mode 100644 index 0000000000..ce1108c69a Binary files /dev/null and b/tests/ref/grid-cell-folding.png differ diff --git a/tests/ref/grid-cell-override-in-header-and-footer-with-gutter.png b/tests/ref/grid-cell-override-in-header-and-footer-with-gutter.png new file mode 100644 index 0000000000..a475bf90d4 Binary files /dev/null and b/tests/ref/grid-cell-override-in-header-and-footer-with-gutter.png differ diff --git a/tests/ref/grid-cell-override-in-header-and-footer.png b/tests/ref/grid-cell-override-in-header-and-footer.png new file mode 100644 index 0000000000..4d31e3796a Binary files /dev/null and b/tests/ref/grid-cell-override-in-header-and-footer.png differ diff --git a/tests/ref/grid-cell-override.png b/tests/ref/grid-cell-override.png new file mode 100644 index 0000000000..d6f37d632f Binary files /dev/null and b/tests/ref/grid-cell-override.png differ diff --git a/tests/ref/grid-cell-position-automatic-skip-manual.png b/tests/ref/grid-cell-position-automatic-skip-manual.png new file mode 100644 index 0000000000..ec615c9774 Binary files /dev/null and b/tests/ref/grid-cell-position-automatic-skip-manual.png differ diff --git a/tests/ref/grid-cell-position-extra-rows.png b/tests/ref/grid-cell-position-extra-rows.png new file mode 100644 index 0000000000..4d73c3f7c4 Binary files /dev/null and b/tests/ref/grid-cell-position-extra-rows.png differ diff --git a/tests/ref/grid-cell-position-out-of-order.png b/tests/ref/grid-cell-position-out-of-order.png new file mode 100644 index 0000000000..d6bdad462f Binary files /dev/null and b/tests/ref/grid-cell-position-out-of-order.png differ diff --git a/tests/ref/grid-cell-position-partial.png b/tests/ref/grid-cell-position-partial.png new file mode 100644 index 0000000000..3012c5b5ef Binary files /dev/null and b/tests/ref/grid-cell-position-partial.png differ diff --git a/tests/ref/grid-cell-set.png b/tests/ref/grid-cell-set.png new file mode 100644 index 0000000000..8795e53efb Binary files /dev/null and b/tests/ref/grid-cell-set.png differ diff --git a/tests/ref/grid-cell-show-and-override.png b/tests/ref/grid-cell-show-and-override.png new file mode 100644 index 0000000000..6af555964c Binary files /dev/null and b/tests/ref/grid-cell-show-and-override.png differ diff --git a/tests/ref/grid-cell-show-based-on-position.png b/tests/ref/grid-cell-show-based-on-position.png new file mode 100644 index 0000000000..26ad62849d Binary files /dev/null and b/tests/ref/grid-cell-show-based-on-position.png differ diff --git a/tests/ref/grid-cell-show-emph.png b/tests/ref/grid-cell-show-emph.png new file mode 100644 index 0000000000..bfc03d6d32 Binary files /dev/null and b/tests/ref/grid-cell-show-emph.png differ diff --git a/tests/ref/grid-cell-show-x-y.png b/tests/ref/grid-cell-show-x-y.png new file mode 100644 index 0000000000..0fb4c2c55e Binary files /dev/null and b/tests/ref/grid-cell-show-x-y.png differ diff --git a/tests/ref/grid-cell-show.png b/tests/ref/grid-cell-show.png new file mode 100644 index 0000000000..9ac6d2695d Binary files /dev/null and b/tests/ref/grid-cell-show.png differ diff --git a/tests/ref/grid-cell-various-overrides.png b/tests/ref/grid-cell-various-overrides.png new file mode 100644 index 0000000000..74490e8464 Binary files /dev/null and b/tests/ref/grid-cell-various-overrides.png differ diff --git a/tests/ref/grid-colspan-gutter.png b/tests/ref/grid-colspan-gutter.png new file mode 100644 index 0000000000..2ba9c217ac Binary files /dev/null and b/tests/ref/grid-colspan-gutter.png differ diff --git a/tests/ref/grid-colspan-multiple-regions.png b/tests/ref/grid-colspan-multiple-regions.png new file mode 100644 index 0000000000..22811acaeb Binary files /dev/null and b/tests/ref/grid-colspan-multiple-regions.png differ diff --git a/tests/ref/grid-colspan-over-all-fr-columns-page-width-auto.png b/tests/ref/grid-colspan-over-all-fr-columns-page-width-auto.png new file mode 100644 index 0000000000..b5cf6cacf5 Binary files /dev/null and b/tests/ref/grid-colspan-over-all-fr-columns-page-width-auto.png differ diff --git a/tests/ref/grid-colspan-over-all-fr-columns.png b/tests/ref/grid-colspan-over-all-fr-columns.png new file mode 100644 index 0000000000..c152f3cc79 Binary files /dev/null and b/tests/ref/grid-colspan-over-all-fr-columns.png differ diff --git a/tests/ref/grid-colspan-over-some-fr-columns.png b/tests/ref/grid-colspan-over-some-fr-columns.png new file mode 100644 index 0000000000..5d8157c201 Binary files /dev/null and b/tests/ref/grid-colspan-over-some-fr-columns.png differ diff --git a/tests/ref/grid-colspan-thick-stroke.png b/tests/ref/grid-colspan-thick-stroke.png new file mode 100644 index 0000000000..7348551e07 Binary files /dev/null and b/tests/ref/grid-colspan-thick-stroke.png differ diff --git a/tests/ref/grid-colspan.png b/tests/ref/grid-colspan.png new file mode 100644 index 0000000000..419d23b2f4 Binary files /dev/null and b/tests/ref/grid-colspan.png differ diff --git a/tests/ref/grid-column-sizing-auto-base.png b/tests/ref/grid-column-sizing-auto-base.png new file mode 100644 index 0000000000..75664027d2 Binary files /dev/null and b/tests/ref/grid-column-sizing-auto-base.png differ diff --git a/tests/ref/grid-column-sizing-fr-base.png b/tests/ref/grid-column-sizing-fr-base.png new file mode 100644 index 0000000000..d4a44be706 Binary files /dev/null and b/tests/ref/grid-column-sizing-fr-base.png differ diff --git a/tests/ref/grid-column-sizing-mixed-base.png b/tests/ref/grid-column-sizing-mixed-base.png new file mode 100644 index 0000000000..dc92564d3a Binary files /dev/null and b/tests/ref/grid-column-sizing-mixed-base.png differ diff --git a/tests/ref/grid-columns-sizings-rect.png b/tests/ref/grid-columns-sizings-rect.png new file mode 100644 index 0000000000..9381103d77 Binary files /dev/null and b/tests/ref/grid-columns-sizings-rect.png differ diff --git a/tests/ref/grid-complete-rows.png b/tests/ref/grid-complete-rows.png new file mode 100644 index 0000000000..192aa911b0 Binary files /dev/null and b/tests/ref/grid-complete-rows.png differ diff --git a/tests/ref/grid-consecutive-rows-breaking.png b/tests/ref/grid-consecutive-rows-breaking.png new file mode 100644 index 0000000000..6000271d83 Binary files /dev/null and b/tests/ref/grid-consecutive-rows-breaking.png differ diff --git a/tests/ref/grid-exam.png b/tests/ref/grid-exam.png new file mode 100644 index 0000000000..97edd52eb0 Binary files /dev/null and b/tests/ref/grid-exam.png differ diff --git a/tests/ref/grid-fill-func.png b/tests/ref/grid-fill-func.png new file mode 100644 index 0000000000..388a52df4f Binary files /dev/null and b/tests/ref/grid-fill-func.png differ diff --git a/tests/ref/grid-finance.png b/tests/ref/grid-finance.png new file mode 100644 index 0000000000..2ea485945e Binary files /dev/null and b/tests/ref/grid-finance.png differ diff --git a/tests/ref/grid-footer-bare-1.png b/tests/ref/grid-footer-bare-1.png new file mode 100644 index 0000000000..e8c8b21a35 Binary files /dev/null and b/tests/ref/grid-footer-bare-1.png differ diff --git a/tests/ref/grid-footer-bare-2.png b/tests/ref/grid-footer-bare-2.png new file mode 100644 index 0000000000..bad6a3dd8a Binary files /dev/null and b/tests/ref/grid-footer-bare-2.png differ diff --git a/tests/ref/grid-footer-below-rowspans.png b/tests/ref/grid-footer-below-rowspans.png new file mode 100644 index 0000000000..5c3a2b26d0 Binary files /dev/null and b/tests/ref/grid-footer-below-rowspans.png differ diff --git a/tests/ref/grid-footer-cell-with-y.png b/tests/ref/grid-footer-cell-with-y.png new file mode 100644 index 0000000000..3237ea69d0 Binary files /dev/null and b/tests/ref/grid-footer-cell-with-y.png differ diff --git a/tests/ref/grid-footer-expand.png b/tests/ref/grid-footer-expand.png new file mode 100644 index 0000000000..118765d5c4 Binary files /dev/null and b/tests/ref/grid-footer-expand.png differ diff --git a/tests/ref/grid-footer-gutter-and-no-repeat.png b/tests/ref/grid-footer-gutter-and-no-repeat.png new file mode 100644 index 0000000000..ea36ae034e Binary files /dev/null and b/tests/ref/grid-footer-gutter-and-no-repeat.png differ diff --git a/tests/ref/grid-footer-hline-and-vline-1.png b/tests/ref/grid-footer-hline-and-vline-1.png new file mode 100644 index 0000000000..a4d9a68123 Binary files /dev/null and b/tests/ref/grid-footer-hline-and-vline-1.png differ diff --git a/tests/ref/grid-footer-hline-and-vline-2.png b/tests/ref/grid-footer-hline-and-vline-2.png new file mode 100644 index 0000000000..0ad2bacc56 Binary files /dev/null and b/tests/ref/grid-footer-hline-and-vline-2.png differ diff --git a/tests/ref/grid-footer-relative-row-sizes.png b/tests/ref/grid-footer-relative-row-sizes.png new file mode 100644 index 0000000000..b533f13f32 Binary files /dev/null and b/tests/ref/grid-footer-relative-row-sizes.png differ diff --git a/tests/ref/grid-footer-rowspan.png b/tests/ref/grid-footer-rowspan.png new file mode 100644 index 0000000000..369e4d0795 Binary files /dev/null and b/tests/ref/grid-footer-rowspan.png differ diff --git a/tests/ref/grid-footer-stroke-edge-cases.png b/tests/ref/grid-footer-stroke-edge-cases.png new file mode 100644 index 0000000000..c3db98e780 Binary files /dev/null and b/tests/ref/grid-footer-stroke-edge-cases.png differ diff --git a/tests/ref/grid-footer-top-stroke.png b/tests/ref/grid-footer-top-stroke.png new file mode 100644 index 0000000000..ff9aa9f01c Binary files /dev/null and b/tests/ref/grid-footer-top-stroke.png differ diff --git a/tests/ref/grid-footer.png b/tests/ref/grid-footer.png new file mode 100644 index 0000000000..196563c7f2 Binary files /dev/null and b/tests/ref/grid-footer.png differ diff --git a/tests/ref/grid-funcs-gutter.png b/tests/ref/grid-funcs-gutter.png new file mode 100644 index 0000000000..ee6723ef90 Binary files /dev/null and b/tests/ref/grid-funcs-gutter.png differ diff --git a/tests/ref/grid-gutter-fr.png b/tests/ref/grid-gutter-fr.png new file mode 100644 index 0000000000..2fce694920 Binary files /dev/null and b/tests/ref/grid-gutter-fr.png differ diff --git a/tests/ref/grid-header-and-footer-containing-rowspan.png b/tests/ref/grid-header-and-footer-containing-rowspan.png new file mode 100644 index 0000000000..705d72a470 Binary files /dev/null and b/tests/ref/grid-header-and-footer-containing-rowspan.png differ diff --git a/tests/ref/grid-header-and-footer-empty.png b/tests/ref/grid-header-and-footer-empty.png new file mode 100644 index 0000000000..c4e7bb0e4d Binary files /dev/null and b/tests/ref/grid-header-and-footer-empty.png differ diff --git a/tests/ref/grid-header-and-footer-lack-of-space.png b/tests/ref/grid-header-and-footer-lack-of-space.png new file mode 100644 index 0000000000..78705776d5 Binary files /dev/null and b/tests/ref/grid-header-and-footer-lack-of-space.png differ diff --git a/tests/ref/grid-header-and-footer-orphan-prevention.png b/tests/ref/grid-header-and-footer-orphan-prevention.png new file mode 100644 index 0000000000..8253b65726 Binary files /dev/null and b/tests/ref/grid-header-and-footer-orphan-prevention.png differ diff --git a/tests/ref/grid-header-and-rowspan-non-contiguous-1.png b/tests/ref/grid-header-and-rowspan-non-contiguous-1.png new file mode 100644 index 0000000000..d5088a12f6 Binary files /dev/null and b/tests/ref/grid-header-and-rowspan-non-contiguous-1.png differ diff --git a/tests/ref/grid-header-and-rowspan-non-contiguous-2.png b/tests/ref/grid-header-and-rowspan-non-contiguous-2.png new file mode 100644 index 0000000000..4894d14186 Binary files /dev/null and b/tests/ref/grid-header-and-rowspan-non-contiguous-2.png differ diff --git a/tests/ref/grid-header-and-rowspan-non-contiguous-3.png b/tests/ref/grid-header-and-rowspan-non-contiguous-3.png new file mode 100644 index 0000000000..36e9a3c316 Binary files /dev/null and b/tests/ref/grid-header-and-rowspan-non-contiguous-3.png differ diff --git a/tests/ref/grid-header-block-with-fixed-height.png b/tests/ref/grid-header-block-with-fixed-height.png new file mode 100644 index 0000000000..b7f2eedb3e Binary files /dev/null and b/tests/ref/grid-header-block-with-fixed-height.png differ diff --git a/tests/ref/grid-header-cell-with-y.png b/tests/ref/grid-header-cell-with-y.png new file mode 100644 index 0000000000..e54e35fa22 Binary files /dev/null and b/tests/ref/grid-header-cell-with-y.png differ diff --git a/tests/ref/grid-header-containing-rowspan.png b/tests/ref/grid-header-containing-rowspan.png new file mode 100644 index 0000000000..3cabff9e23 Binary files /dev/null and b/tests/ref/grid-header-containing-rowspan.png differ diff --git a/tests/ref/grid-header-empty.png b/tests/ref/grid-header-empty.png new file mode 100644 index 0000000000..20e4d92cac Binary files /dev/null and b/tests/ref/grid-header-empty.png differ diff --git a/tests/ref/grid-header-expand.png b/tests/ref/grid-header-expand.png new file mode 100644 index 0000000000..465724417a Binary files /dev/null and b/tests/ref/grid-header-expand.png differ diff --git a/tests/ref/grid-header-footer-and-rowspan-non-contiguous-1.png b/tests/ref/grid-header-footer-and-rowspan-non-contiguous-1.png new file mode 100644 index 0000000000..e7b153c828 Binary files /dev/null and b/tests/ref/grid-header-footer-and-rowspan-non-contiguous-1.png differ diff --git a/tests/ref/grid-header-footer-and-rowspan-non-contiguous-2.png b/tests/ref/grid-header-footer-and-rowspan-non-contiguous-2.png new file mode 100644 index 0000000000..525475ac5b Binary files /dev/null and b/tests/ref/grid-header-footer-and-rowspan-non-contiguous-2.png differ diff --git a/tests/ref/grid-header-footer-block-with-fixed-height.png b/tests/ref/grid-header-footer-block-with-fixed-height.png new file mode 100644 index 0000000000..1f2e7c204a Binary files /dev/null and b/tests/ref/grid-header-footer-block-with-fixed-height.png differ diff --git a/tests/ref/grid-header-hline-and-vline.png b/tests/ref/grid-header-hline-and-vline.png new file mode 100644 index 0000000000..a01fc00b30 Binary files /dev/null and b/tests/ref/grid-header-hline-and-vline.png differ diff --git a/tests/ref/grid-header-hline-bottom-manually.png b/tests/ref/grid-header-hline-bottom-manually.png new file mode 100644 index 0000000000..d944f7b5e0 Binary files /dev/null and b/tests/ref/grid-header-hline-bottom-manually.png differ diff --git a/tests/ref/grid-header-hline-bottom.png b/tests/ref/grid-header-hline-bottom.png new file mode 100644 index 0000000000..f13612420c Binary files /dev/null and b/tests/ref/grid-header-hline-bottom.png differ diff --git a/tests/ref/grid-header-lack-of-space.png b/tests/ref/grid-header-lack-of-space.png new file mode 100644 index 0000000000..4d2b483f19 Binary files /dev/null and b/tests/ref/grid-header-lack-of-space.png differ diff --git a/tests/ref/grid-header-last-child.png b/tests/ref/grid-header-last-child.png new file mode 100644 index 0000000000..4fa1ff7c77 Binary files /dev/null and b/tests/ref/grid-header-last-child.png differ diff --git a/tests/ref/grid-header-nested.png b/tests/ref/grid-header-nested.png new file mode 100644 index 0000000000..9078090f58 Binary files /dev/null and b/tests/ref/grid-header-nested.png differ diff --git a/tests/ref/grid-header-orphan-prevention.png b/tests/ref/grid-header-orphan-prevention.png new file mode 100644 index 0000000000..fa903e42d8 Binary files /dev/null and b/tests/ref/grid-header-orphan-prevention.png differ diff --git a/tests/ref/grid-header-relative-row-sizes.png b/tests/ref/grid-header-relative-row-sizes.png new file mode 100644 index 0000000000..69ed1d1e04 Binary files /dev/null and b/tests/ref/grid-header-relative-row-sizes.png differ diff --git a/tests/ref/grid-header-rowspan-base.png b/tests/ref/grid-header-rowspan-base.png new file mode 100644 index 0000000000..1ab83591e0 Binary files /dev/null and b/tests/ref/grid-header-rowspan-base.png differ diff --git a/tests/ref/grid-header-stroke-edge-cases.png b/tests/ref/grid-header-stroke-edge-cases.png new file mode 100644 index 0000000000..b86eb63263 Binary files /dev/null and b/tests/ref/grid-header-stroke-edge-cases.png differ diff --git a/tests/ref/grid-headers-gutter.png b/tests/ref/grid-headers-gutter.png new file mode 100644 index 0000000000..c2a48a66e4 Binary files /dev/null and b/tests/ref/grid-headers-gutter.png differ diff --git a/tests/ref/grid-headers-no-repeat.png b/tests/ref/grid-headers-no-repeat.png new file mode 100644 index 0000000000..32d281a1cb Binary files /dev/null and b/tests/ref/grid-headers-no-repeat.png differ diff --git a/tests/ref/grid-headers.png b/tests/ref/grid-headers.png new file mode 100644 index 0000000000..13e88dbeca Binary files /dev/null and b/tests/ref/grid-headers.png differ diff --git a/tests/ref/grid-inset-folding.png b/tests/ref/grid-inset-folding.png new file mode 100644 index 0000000000..7f9942646e Binary files /dev/null and b/tests/ref/grid-inset-folding.png differ diff --git a/tests/ref/grid-inset.png b/tests/ref/grid-inset.png new file mode 100644 index 0000000000..d31197d03a Binary files /dev/null and b/tests/ref/grid-inset.png differ diff --git a/tests/ref/grid-nested-breaking.png b/tests/ref/grid-nested-breaking.png new file mode 100644 index 0000000000..b203c2300a Binary files /dev/null and b/tests/ref/grid-nested-breaking.png differ diff --git a/tests/ref/grid-nested-footers.png b/tests/ref/grid-nested-footers.png new file mode 100644 index 0000000000..1af85a00fd Binary files /dev/null and b/tests/ref/grid-nested-footers.png differ diff --git a/tests/ref/grid-nested-headers.png b/tests/ref/grid-nested-headers.png new file mode 100644 index 0000000000..e714dcc45e Binary files /dev/null and b/tests/ref/grid-nested-headers.png differ diff --git a/tests/ref/grid-nested-with-footers.png b/tests/ref/grid-nested-with-footers.png new file mode 100644 index 0000000000..5ceae87707 Binary files /dev/null and b/tests/ref/grid-nested-with-footers.png differ diff --git a/tests/ref/grid-nested-with-headers.png b/tests/ref/grid-nested-with-headers.png new file mode 100644 index 0000000000..6b7ef14bbb Binary files /dev/null and b/tests/ref/grid-nested-with-headers.png differ diff --git a/tests/ref/grid-row-sizing-manual-align.png b/tests/ref/grid-row-sizing-manual-align.png new file mode 100644 index 0000000000..68b0911ed8 Binary files /dev/null and b/tests/ref/grid-row-sizing-manual-align.png differ diff --git a/tests/ref/grid-rowspan-block-full-height.png b/tests/ref/grid-rowspan-block-full-height.png new file mode 100644 index 0000000000..078cbda421 Binary files /dev/null and b/tests/ref/grid-rowspan-block-full-height.png differ diff --git a/tests/ref/grid-rowspan-block-overflow.png b/tests/ref/grid-rowspan-block-overflow.png new file mode 100644 index 0000000000..78e26d720b Binary files /dev/null and b/tests/ref/grid-rowspan-block-overflow.png differ diff --git a/tests/ref/grid-rowspan-cell-coordinates.png b/tests/ref/grid-rowspan-cell-coordinates.png new file mode 100644 index 0000000000..ebe19fd494 Binary files /dev/null and b/tests/ref/grid-rowspan-cell-coordinates.png differ diff --git a/tests/ref/grid-rowspan-cell-order.png b/tests/ref/grid-rowspan-cell-order.png new file mode 100644 index 0000000000..c9b1f5546a Binary files /dev/null and b/tests/ref/grid-rowspan-cell-order.png differ diff --git a/tests/ref/grid-rowspan-excessive-gutter.png b/tests/ref/grid-rowspan-excessive-gutter.png new file mode 100644 index 0000000000..8688364c9b Binary files /dev/null and b/tests/ref/grid-rowspan-excessive-gutter.png differ diff --git a/tests/ref/grid-rowspan-excessive.png b/tests/ref/grid-rowspan-excessive.png new file mode 100644 index 0000000000..1e6b412829 Binary files /dev/null and b/tests/ref/grid-rowspan-excessive.png differ diff --git a/tests/ref/grid-rowspan-fixed-size.png b/tests/ref/grid-rowspan-fixed-size.png new file mode 100644 index 0000000000..c9ae3fa126 Binary files /dev/null and b/tests/ref/grid-rowspan-fixed-size.png differ diff --git a/tests/ref/grid-rowspan-gutter.png b/tests/ref/grid-rowspan-gutter.png new file mode 100644 index 0000000000..b37a1cab2a Binary files /dev/null and b/tests/ref/grid-rowspan-gutter.png differ diff --git a/tests/ref/grid-rowspan-in-all-columns-stroke-gutter.png b/tests/ref/grid-rowspan-in-all-columns-stroke-gutter.png new file mode 100644 index 0000000000..edad2f01f0 Binary files /dev/null and b/tests/ref/grid-rowspan-in-all-columns-stroke-gutter.png differ diff --git a/tests/ref/grid-rowspan-in-all-columns-stroke.png b/tests/ref/grid-rowspan-in-all-columns-stroke.png new file mode 100644 index 0000000000..135d1911aa Binary files /dev/null and b/tests/ref/grid-rowspan-in-all-columns-stroke.png differ diff --git a/tests/ref/grid-rowspan-over-auto-row.png b/tests/ref/grid-rowspan-over-auto-row.png new file mode 100644 index 0000000000..450373824a Binary files /dev/null and b/tests/ref/grid-rowspan-over-auto-row.png differ diff --git a/tests/ref/grid-rowspan-over-fr-row-at-end.png b/tests/ref/grid-rowspan-over-fr-row-at-end.png new file mode 100644 index 0000000000..1cf8b9fc8d Binary files /dev/null and b/tests/ref/grid-rowspan-over-fr-row-at-end.png differ diff --git a/tests/ref/grid-rowspan-over-fr-row-at-start.png b/tests/ref/grid-rowspan-over-fr-row-at-start.png new file mode 100644 index 0000000000..577db9165a Binary files /dev/null and b/tests/ref/grid-rowspan-over-fr-row-at-start.png differ diff --git a/tests/ref/grid-rowspan-split-1.png b/tests/ref/grid-rowspan-split-1.png new file mode 100644 index 0000000000..e99b105f93 Binary files /dev/null and b/tests/ref/grid-rowspan-split-1.png differ diff --git a/tests/ref/grid-rowspan-split-10.png b/tests/ref/grid-rowspan-split-10.png new file mode 100644 index 0000000000..0b907e7dfc Binary files /dev/null and b/tests/ref/grid-rowspan-split-10.png differ diff --git a/tests/ref/grid-rowspan-split-11.png b/tests/ref/grid-rowspan-split-11.png new file mode 100644 index 0000000000..202665d6d0 Binary files /dev/null and b/tests/ref/grid-rowspan-split-11.png differ diff --git a/tests/ref/grid-rowspan-split-12.png b/tests/ref/grid-rowspan-split-12.png new file mode 100644 index 0000000000..3d8985f2d2 Binary files /dev/null and b/tests/ref/grid-rowspan-split-12.png differ diff --git a/tests/ref/grid-rowspan-split-13.png b/tests/ref/grid-rowspan-split-13.png new file mode 100644 index 0000000000..f4e9d694e6 Binary files /dev/null and b/tests/ref/grid-rowspan-split-13.png differ diff --git a/tests/ref/grid-rowspan-split-14.png b/tests/ref/grid-rowspan-split-14.png new file mode 100644 index 0000000000..1500a89b03 Binary files /dev/null and b/tests/ref/grid-rowspan-split-14.png differ diff --git a/tests/ref/grid-rowspan-split-15.png b/tests/ref/grid-rowspan-split-15.png new file mode 100644 index 0000000000..445f0a959e Binary files /dev/null and b/tests/ref/grid-rowspan-split-15.png differ diff --git a/tests/ref/grid-rowspan-split-16.png b/tests/ref/grid-rowspan-split-16.png new file mode 100644 index 0000000000..fff83aebe9 Binary files /dev/null and b/tests/ref/grid-rowspan-split-16.png differ diff --git a/tests/ref/grid-rowspan-split-17.png b/tests/ref/grid-rowspan-split-17.png new file mode 100644 index 0000000000..2224c194c2 Binary files /dev/null and b/tests/ref/grid-rowspan-split-17.png differ diff --git a/tests/ref/grid-rowspan-split-2.png b/tests/ref/grid-rowspan-split-2.png new file mode 100644 index 0000000000..43a5eed7c8 Binary files /dev/null and b/tests/ref/grid-rowspan-split-2.png differ diff --git a/tests/ref/grid-rowspan-split-3.png b/tests/ref/grid-rowspan-split-3.png new file mode 100644 index 0000000000..0d7c3359c9 Binary files /dev/null and b/tests/ref/grid-rowspan-split-3.png differ diff --git a/tests/ref/grid-rowspan-split-4.png b/tests/ref/grid-rowspan-split-4.png new file mode 100644 index 0000000000..2af887bba7 Binary files /dev/null and b/tests/ref/grid-rowspan-split-4.png differ diff --git a/tests/ref/grid-rowspan-split-5.png b/tests/ref/grid-rowspan-split-5.png new file mode 100644 index 0000000000..3aa79cda33 Binary files /dev/null and b/tests/ref/grid-rowspan-split-5.png differ diff --git a/tests/ref/grid-rowspan-split-6.png b/tests/ref/grid-rowspan-split-6.png new file mode 100644 index 0000000000..fbf5bf28cc Binary files /dev/null and b/tests/ref/grid-rowspan-split-6.png differ diff --git a/tests/ref/grid-rowspan-split-7.png b/tests/ref/grid-rowspan-split-7.png new file mode 100644 index 0000000000..00e03f025f Binary files /dev/null and b/tests/ref/grid-rowspan-split-7.png differ diff --git a/tests/ref/grid-rowspan-split-8.png b/tests/ref/grid-rowspan-split-8.png new file mode 100644 index 0000000000..405b54235d Binary files /dev/null and b/tests/ref/grid-rowspan-split-8.png differ diff --git a/tests/ref/grid-rowspan-split-9.png b/tests/ref/grid-rowspan-split-9.png new file mode 100644 index 0000000000..5346be718c Binary files /dev/null and b/tests/ref/grid-rowspan-split-9.png differ diff --git a/tests/ref/grid-rowspan-unbreakable-1.png b/tests/ref/grid-rowspan-unbreakable-1.png new file mode 100644 index 0000000000..6112c06926 Binary files /dev/null and b/tests/ref/grid-rowspan-unbreakable-1.png differ diff --git a/tests/ref/grid-rowspan-unbreakable-2.png b/tests/ref/grid-rowspan-unbreakable-2.png new file mode 100644 index 0000000000..8e4a222aa6 Binary files /dev/null and b/tests/ref/grid-rowspan-unbreakable-2.png differ diff --git a/tests/ref/grid-rowspan.png b/tests/ref/grid-rowspan.png new file mode 100644 index 0000000000..87ad418078 Binary files /dev/null and b/tests/ref/grid-rowspan.png differ diff --git a/tests/ref/grid-rtl-colspan-stroke.png b/tests/ref/grid-rtl-colspan-stroke.png new file mode 100644 index 0000000000..248a575cbf Binary files /dev/null and b/tests/ref/grid-rtl-colspan-stroke.png differ diff --git a/tests/ref/grid-rtl-colspan.png b/tests/ref/grid-rtl-colspan.png new file mode 100644 index 0000000000..886e276dfe Binary files /dev/null and b/tests/ref/grid-rtl-colspan.png differ diff --git a/tests/ref/grid-rtl-complex.png b/tests/ref/grid-rtl-complex.png new file mode 100644 index 0000000000..a4177548d1 Binary files /dev/null and b/tests/ref/grid-rtl-complex.png differ diff --git a/tests/ref/grid-rtl-header.png b/tests/ref/grid-rtl-header.png new file mode 100644 index 0000000000..1ed532c386 Binary files /dev/null and b/tests/ref/grid-rtl-header.png differ diff --git a/tests/ref/grid-rtl-multiple-regions.png b/tests/ref/grid-rtl-multiple-regions.png new file mode 100644 index 0000000000..a9ec7340ef Binary files /dev/null and b/tests/ref/grid-rtl-multiple-regions.png differ diff --git a/tests/ref/grid-rtl-rowspan.png b/tests/ref/grid-rtl-rowspan.png new file mode 100644 index 0000000000..2465164b1e Binary files /dev/null and b/tests/ref/grid-rtl-rowspan.png differ diff --git a/tests/ref/grid-rtl-vline-position.png b/tests/ref/grid-rtl-vline-position.png new file mode 100644 index 0000000000..3612fc9fe5 Binary files /dev/null and b/tests/ref/grid-rtl-vline-position.png differ diff --git a/tests/ref/grid-rtl.png b/tests/ref/grid-rtl.png new file mode 100644 index 0000000000..c40fc58850 Binary files /dev/null and b/tests/ref/grid-rtl.png differ diff --git a/tests/ref/grid-same-row-multiple-columns-breaking.png b/tests/ref/grid-same-row-multiple-columns-breaking.png new file mode 100644 index 0000000000..b440f3368e Binary files /dev/null and b/tests/ref/grid-same-row-multiple-columns-breaking.png differ diff --git a/tests/ref/grid-stroke-array.png b/tests/ref/grid-stroke-array.png new file mode 100644 index 0000000000..6f8e28b099 Binary files /dev/null and b/tests/ref/grid-stroke-array.png differ diff --git a/tests/ref/grid-stroke-automatically-positioned-lines.png b/tests/ref/grid-stroke-automatically-positioned-lines.png new file mode 100644 index 0000000000..2118112c0c Binary files /dev/null and b/tests/ref/grid-stroke-automatically-positioned-lines.png differ diff --git a/tests/ref/grid-stroke-border-partial.png b/tests/ref/grid-stroke-border-partial.png new file mode 100644 index 0000000000..ffd8835f11 Binary files /dev/null and b/tests/ref/grid-stroke-border-partial.png differ diff --git a/tests/ref/grid-stroke-complex.png b/tests/ref/grid-stroke-complex.png new file mode 100644 index 0000000000..e68fd5f3b8 Binary files /dev/null and b/tests/ref/grid-stroke-complex.png differ diff --git a/tests/ref/grid-stroke-field-in-show.png b/tests/ref/grid-stroke-field-in-show.png new file mode 100644 index 0000000000..695868c045 Binary files /dev/null and b/tests/ref/grid-stroke-field-in-show.png differ diff --git a/tests/ref/grid-stroke-folding.png b/tests/ref/grid-stroke-folding.png new file mode 100644 index 0000000000..0f2d59602b Binary files /dev/null and b/tests/ref/grid-stroke-folding.png differ diff --git a/tests/ref/grid-stroke-func.png b/tests/ref/grid-stroke-func.png new file mode 100644 index 0000000000..954e90df53 Binary files /dev/null and b/tests/ref/grid-stroke-func.png differ diff --git a/tests/ref/grid-stroke-hline-position-bottom-gutter.png b/tests/ref/grid-stroke-hline-position-bottom-gutter.png new file mode 100644 index 0000000000..23c7def41d Binary files /dev/null and b/tests/ref/grid-stroke-hline-position-bottom-gutter.png differ diff --git a/tests/ref/grid-stroke-hline-position-bottom.png b/tests/ref/grid-stroke-hline-position-bottom.png new file mode 100644 index 0000000000..25c003c813 Binary files /dev/null and b/tests/ref/grid-stroke-hline-position-bottom.png differ diff --git a/tests/ref/grid-stroke-hline-rowspan.png b/tests/ref/grid-stroke-hline-rowspan.png new file mode 100644 index 0000000000..2faf707916 Binary files /dev/null and b/tests/ref/grid-stroke-hline-rowspan.png differ diff --git a/tests/ref/grid-stroke-manually-positioned-lines.png b/tests/ref/grid-stroke-manually-positioned-lines.png new file mode 100644 index 0000000000..a8a75ee07e Binary files /dev/null and b/tests/ref/grid-stroke-manually-positioned-lines.png differ diff --git a/tests/ref/grid-stroke-none.png b/tests/ref/grid-stroke-none.png new file mode 100644 index 0000000000..3f978bd3a0 Binary files /dev/null and b/tests/ref/grid-stroke-none.png differ diff --git a/tests/ref/grid-stroke-pattern.png b/tests/ref/grid-stroke-pattern.png new file mode 100644 index 0000000000..15e846eafb Binary files /dev/null and b/tests/ref/grid-stroke-pattern.png differ diff --git a/tests/ref/grid-stroke-priority-cell.png b/tests/ref/grid-stroke-priority-cell.png new file mode 100644 index 0000000000..2c28e9e813 Binary files /dev/null and b/tests/ref/grid-stroke-priority-cell.png differ diff --git a/tests/ref/grid-stroke-priority-line-cell.png b/tests/ref/grid-stroke-priority-line-cell.png new file mode 100644 index 0000000000..064dc1c98a Binary files /dev/null and b/tests/ref/grid-stroke-priority-line-cell.png differ diff --git a/tests/ref/grid-stroke-priority-line.png b/tests/ref/grid-stroke-priority-line.png new file mode 100644 index 0000000000..1bcaa2ee89 Binary files /dev/null and b/tests/ref/grid-stroke-priority-line.png differ diff --git a/tests/ref/grid-stroke-set-on-cell-and-line.png b/tests/ref/grid-stroke-set-on-cell-and-line.png new file mode 100644 index 0000000000..d43752f05d Binary files /dev/null and b/tests/ref/grid-stroke-set-on-cell-and-line.png differ diff --git a/tests/ref/grid-stroke-vline-colspan.png b/tests/ref/grid-stroke-vline-colspan.png new file mode 100644 index 0000000000..7b38143725 Binary files /dev/null and b/tests/ref/grid-stroke-vline-colspan.png differ diff --git a/tests/ref/grid-stroke-vline-position-left-and-right.png b/tests/ref/grid-stroke-vline-position-left-and-right.png new file mode 100644 index 0000000000..852fcf297b Binary files /dev/null and b/tests/ref/grid-stroke-vline-position-left-and-right.png differ diff --git a/tests/ref/grid-trailing-linebreak-region-overflow.png b/tests/ref/grid-trailing-linebreak-region-overflow.png new file mode 100644 index 0000000000..4f7bc852f3 Binary files /dev/null and b/tests/ref/grid-trailing-linebreak-region-overflow.png differ diff --git a/tests/ref/heading-basic.png b/tests/ref/heading-basic.png new file mode 100644 index 0000000000..74a8f2cea7 Binary files /dev/null and b/tests/ref/heading-basic.png differ diff --git a/tests/ref/heading-block.png b/tests/ref/heading-block.png new file mode 100644 index 0000000000..595f18f5d1 Binary files /dev/null and b/tests/ref/heading-block.png differ diff --git a/tests/ref/heading-offset-and-level.png b/tests/ref/heading-offset-and-level.png new file mode 100644 index 0000000000..9277e770a4 Binary files /dev/null and b/tests/ref/heading-offset-and-level.png differ diff --git a/tests/ref/heading-offset.png b/tests/ref/heading-offset.png new file mode 100644 index 0000000000..3a3670cc39 Binary files /dev/null and b/tests/ref/heading-offset.png differ diff --git a/tests/ref/heading-show-where.png b/tests/ref/heading-show-where.png new file mode 100644 index 0000000000..609e6ec9a2 Binary files /dev/null and b/tests/ref/heading-show-where.png differ diff --git a/tests/ref/heading-syntax-at-start.png b/tests/ref/heading-syntax-at-start.png new file mode 100644 index 0000000000..29b824e09e Binary files /dev/null and b/tests/ref/heading-syntax-at-start.png differ diff --git a/tests/ref/heading-syntax-edge-cases.png b/tests/ref/heading-syntax-edge-cases.png new file mode 100644 index 0000000000..372e1a65d7 Binary files /dev/null and b/tests/ref/heading-syntax-edge-cases.png differ diff --git a/tests/ref/hide-image.png b/tests/ref/hide-image.png new file mode 100644 index 0000000000..78bc690c8e Binary files /dev/null and b/tests/ref/hide-image.png differ diff --git a/tests/ref/hide-line.png b/tests/ref/hide-line.png new file mode 100644 index 0000000000..7d8fa6cd27 Binary files /dev/null and b/tests/ref/hide-line.png differ diff --git a/tests/ref/hide-list.png b/tests/ref/hide-list.png new file mode 100644 index 0000000000..055f7b66f4 Binary files /dev/null and b/tests/ref/hide-list.png differ diff --git a/tests/ref/hide-polygon.png b/tests/ref/hide-polygon.png new file mode 100644 index 0000000000..5c74eb41ab Binary files /dev/null and b/tests/ref/hide-polygon.png differ diff --git a/tests/ref/hide-rect.png b/tests/ref/hide-rect.png new file mode 100644 index 0000000000..62372c21f8 Binary files /dev/null and b/tests/ref/hide-rect.png differ diff --git a/tests/ref/hide-table.png b/tests/ref/hide-table.png new file mode 100644 index 0000000000..e3d890d7bd Binary files /dev/null and b/tests/ref/hide-table.png differ diff --git a/tests/ref/hide-text.png b/tests/ref/hide-text.png new file mode 100644 index 0000000000..1136038cc3 Binary files /dev/null and b/tests/ref/hide-text.png differ diff --git a/tests/ref/highlight-bounds.png b/tests/ref/highlight-bounds.png new file mode 100644 index 0000000000..ed868c29b9 Binary files /dev/null and b/tests/ref/highlight-bounds.png differ diff --git a/tests/ref/highlight-edges-bounds.png b/tests/ref/highlight-edges-bounds.png new file mode 100644 index 0000000000..f78f3cc39b Binary files /dev/null and b/tests/ref/highlight-edges-bounds.png differ diff --git a/tests/ref/highlight-edges.png b/tests/ref/highlight-edges.png new file mode 100644 index 0000000000..ca48707f03 Binary files /dev/null and b/tests/ref/highlight-edges.png differ diff --git a/tests/ref/highlight-radius.png b/tests/ref/highlight-radius.png new file mode 100644 index 0000000000..3baa3e6d9f Binary files /dev/null and b/tests/ref/highlight-radius.png differ diff --git a/tests/ref/highlight-stroke.png b/tests/ref/highlight-stroke.png new file mode 100644 index 0000000000..5a8ad3b596 Binary files /dev/null and b/tests/ref/highlight-stroke.png differ diff --git a/tests/ref/highlight.png b/tests/ref/highlight.png new file mode 100644 index 0000000000..0047b7f49c Binary files /dev/null and b/tests/ref/highlight.png differ diff --git a/tests/ref/hyphenate-between-shape-runs.png b/tests/ref/hyphenate-between-shape-runs.png new file mode 100644 index 0000000000..a365af24d5 Binary files /dev/null and b/tests/ref/hyphenate-between-shape-runs.png differ diff --git a/tests/ref/hyphenate-off-temporarily.png b/tests/ref/hyphenate-off-temporarily.png new file mode 100644 index 0000000000..48e3caa963 Binary files /dev/null and b/tests/ref/hyphenate-off-temporarily.png differ diff --git a/tests/ref/hyphenate-punctuation.png b/tests/ref/hyphenate-punctuation.png new file mode 100644 index 0000000000..897a15a043 Binary files /dev/null and b/tests/ref/hyphenate-punctuation.png differ diff --git a/tests/ref/hyphenate-shy.png b/tests/ref/hyphenate-shy.png new file mode 100644 index 0000000000..a548c711a4 Binary files /dev/null and b/tests/ref/hyphenate-shy.png differ diff --git a/tests/ref/hyphenate.png b/tests/ref/hyphenate.png new file mode 100644 index 0000000000..c01c902114 Binary files /dev/null and b/tests/ref/hyphenate.png differ diff --git a/tests/ref/if-condition-complex.png b/tests/ref/if-condition-complex.png new file mode 100644 index 0000000000..4cbebc5e02 Binary files /dev/null and b/tests/ref/if-condition-complex.png differ diff --git a/tests/ref/if-markup.png b/tests/ref/if-markup.png new file mode 100644 index 0000000000..57eb47da18 Binary files /dev/null and b/tests/ref/if-markup.png differ diff --git a/tests/ref/image-baseline-with-box.png b/tests/ref/image-baseline-with-box.png new file mode 100644 index 0000000000..4112806982 Binary files /dev/null and b/tests/ref/image-baseline-with-box.png differ diff --git a/tests/ref/image-decode-detect-format.png b/tests/ref/image-decode-detect-format.png new file mode 100644 index 0000000000..6f12e8b474 Binary files /dev/null and b/tests/ref/image-decode-detect-format.png differ diff --git a/tests/ref/image-decode-specify-format.png b/tests/ref/image-decode-specify-format.png new file mode 100644 index 0000000000..6f12e8b474 Binary files /dev/null and b/tests/ref/image-decode-specify-format.png differ diff --git a/tests/ref/image-decode-svg.png b/tests/ref/image-decode-svg.png new file mode 100644 index 0000000000..b7cfcb17c3 Binary files /dev/null and b/tests/ref/image-decode-svg.png differ diff --git a/tests/ref/image-fit.png b/tests/ref/image-fit.png new file mode 100644 index 0000000000..5a3bdec186 Binary files /dev/null and b/tests/ref/image-fit.png differ diff --git a/tests/ref/image-jump-to-next-page.png b/tests/ref/image-jump-to-next-page.png new file mode 100644 index 0000000000..d8f03b3f4e Binary files /dev/null and b/tests/ref/image-jump-to-next-page.png differ diff --git a/tests/ref/image-natural-dpi-sizing.png b/tests/ref/image-natural-dpi-sizing.png new file mode 100644 index 0000000000..3b9f3fa5de Binary files /dev/null and b/tests/ref/image-natural-dpi-sizing.png differ diff --git a/tests/ref/image-rgba-png-and-jpeg.png b/tests/ref/image-rgba-png-and-jpeg.png new file mode 100644 index 0000000000..601271705e Binary files /dev/null and b/tests/ref/image-rgba-png-and-jpeg.png differ diff --git a/tests/ref/image-sizing.png b/tests/ref/image-sizing.png new file mode 100644 index 0000000000..7419de141b Binary files /dev/null and b/tests/ref/image-sizing.png differ diff --git a/tests/ref/image-svg-complex.png b/tests/ref/image-svg-complex.png new file mode 100644 index 0000000000..1ac4547745 Binary files /dev/null and b/tests/ref/image-svg-complex.png differ diff --git a/tests/ref/image-svg-text-font.png b/tests/ref/image-svg-text-font.png new file mode 100644 index 0000000000..2e3b0a0f5c Binary files /dev/null and b/tests/ref/image-svg-text-font.png differ diff --git a/tests/ref/image-svg-text.png b/tests/ref/image-svg-text.png new file mode 100644 index 0000000000..2e41f905e4 Binary files /dev/null and b/tests/ref/image-svg-text.png differ diff --git a/tests/ref/import-basic.png b/tests/ref/import-basic.png new file mode 100644 index 0000000000..674c4ecf95 Binary files /dev/null and b/tests/ref/import-basic.png differ diff --git a/tests/ref/import-from-function-scope.png b/tests/ref/import-from-function-scope.png new file mode 100644 index 0000000000..f6169d8c2e Binary files /dev/null and b/tests/ref/import-from-function-scope.png differ diff --git a/tests/ref/import-source-field-access.png b/tests/ref/import-source-field-access.png new file mode 100644 index 0000000000..e42bf2091c Binary files /dev/null and b/tests/ref/import-source-field-access.png differ diff --git a/tests/ref/include-file.png b/tests/ref/include-file.png new file mode 100644 index 0000000000..57c3aca1e0 Binary files /dev/null and b/tests/ref/include-file.png differ diff --git a/tests/ref/int-display.png b/tests/ref/int-display.png new file mode 100644 index 0000000000..bfb0464806 Binary files /dev/null and b/tests/ref/int-display.png differ diff --git a/tests/ref/int-repr.png b/tests/ref/int-repr.png new file mode 100644 index 0000000000..a2ee4ee08b Binary files /dev/null and b/tests/ref/int-repr.png differ diff --git a/tests/ref/issue-1041-smartquotes-in-outline.png b/tests/ref/issue-1041-smartquotes-in-outline.png new file mode 100644 index 0000000000..29ba4065a5 Binary files /dev/null and b/tests/ref/issue-1041-smartquotes-in-outline.png differ diff --git a/tests/ref/issue-1050-terms-indent.png b/tests/ref/issue-1050-terms-indent.png new file mode 100644 index 0000000000..ca0521c76e Binary files /dev/null and b/tests/ref/issue-1050-terms-indent.png differ diff --git a/tests/ref/issue-1052-math-number-spacing.png b/tests/ref/issue-1052-math-number-spacing.png new file mode 100644 index 0000000000..79df2c9f90 Binary files /dev/null and b/tests/ref/issue-1052-math-number-spacing.png differ diff --git a/tests/ref/issue-1216-clamp-panic.png b/tests/ref/issue-1216-clamp-panic.png new file mode 100644 index 0000000000..d51f134c20 Binary files /dev/null and b/tests/ref/issue-1216-clamp-panic.png differ diff --git a/tests/ref/issue-1240-stack-h-fr.png b/tests/ref/issue-1240-stack-h-fr.png new file mode 100644 index 0000000000..ae1ba41e08 Binary files /dev/null and b/tests/ref/issue-1240-stack-h-fr.png differ diff --git a/tests/ref/issue-1240-stack-v-fr.png b/tests/ref/issue-1240-stack-v-fr.png new file mode 100644 index 0000000000..a9ac36e8b9 Binary files /dev/null and b/tests/ref/issue-1240-stack-v-fr.png differ diff --git a/tests/ref/issue-1368-place-pagebreak.png b/tests/ref/issue-1368-place-pagebreak.png new file mode 100644 index 0000000000..920cd20306 Binary files /dev/null and b/tests/ref/issue-1368-place-pagebreak.png differ diff --git a/tests/ref/issue-1373-bidi-tofus.png b/tests/ref/issue-1373-bidi-tofus.png new file mode 100644 index 0000000000..783eb473d8 Binary files /dev/null and b/tests/ref/issue-1373-bidi-tofus.png differ diff --git a/tests/ref/issue-1388-table-row-missing.png b/tests/ref/issue-1388-table-row-missing.png new file mode 100644 index 0000000000..dd08eb4645 Binary files /dev/null and b/tests/ref/issue-1388-table-row-missing.png differ diff --git a/tests/ref/issue-1398-line-align.png b/tests/ref/issue-1398-line-align.png new file mode 100644 index 0000000000..778aa72c9f Binary files /dev/null and b/tests/ref/issue-1398-line-align.png differ diff --git a/tests/ref/issue-1433-footnote-in-list.png b/tests/ref/issue-1433-footnote-in-list.png new file mode 100644 index 0000000000..28a6e77ff2 Binary files /dev/null and b/tests/ref/issue-1433-footnote-in-list.png differ diff --git a/tests/ref/issue-1540-smartquotes-across-newlines.png b/tests/ref/issue-1540-smartquotes-across-newlines.png new file mode 100644 index 0000000000..10fe733796 Binary files /dev/null and b/tests/ref/issue-1540-smartquotes-across-newlines.png differ diff --git a/tests/ref/issue-1597-cite-footnote.png b/tests/ref/issue-1597-cite-footnote.png new file mode 100644 index 0000000000..bdd9f22573 Binary files /dev/null and b/tests/ref/issue-1597-cite-footnote.png differ diff --git a/tests/ref/issue-1825-rect-overflow.png b/tests/ref/issue-1825-rect-overflow.png new file mode 100644 index 0000000000..70f09e1208 Binary files /dev/null and b/tests/ref/issue-1825-rect-overflow.png differ diff --git a/tests/ref/issue-183-table-lines.png b/tests/ref/issue-183-table-lines.png new file mode 100644 index 0000000000..e43692624b Binary files /dev/null and b/tests/ref/issue-183-table-lines.png differ diff --git a/tests/ref/issue-1948-math-text-break.png b/tests/ref/issue-1948-math-text-break.png new file mode 100644 index 0000000000..6e3e9e85b4 Binary files /dev/null and b/tests/ref/issue-1948-math-text-break.png differ diff --git a/tests/ref/issue-2044-invalid-parsed-ident.png b/tests/ref/issue-2044-invalid-parsed-ident.png new file mode 100644 index 0000000000..7e37ce2c61 Binary files /dev/null and b/tests/ref/issue-2044-invalid-parsed-ident.png differ diff --git a/tests/ref/issue-2051-new-cm-svg.png b/tests/ref/issue-2051-new-cm-svg.png new file mode 100644 index 0000000000..6535286027 Binary files /dev/null and b/tests/ref/issue-2051-new-cm-svg.png differ diff --git a/tests/ref/issue-2055-math-eval.png b/tests/ref/issue-2055-math-eval.png new file mode 100644 index 0000000000..168b89139c Binary files /dev/null and b/tests/ref/issue-2055-math-eval.png differ diff --git a/tests/ref/issue-2095-pagebreak-numbering.png b/tests/ref/issue-2095-pagebreak-numbering.png new file mode 100644 index 0000000000..e3a515b76f Binary files /dev/null and b/tests/ref/issue-2095-pagebreak-numbering.png differ diff --git a/tests/ref/issue-2105-linebreak-tofu.png b/tests/ref/issue-2105-linebreak-tofu.png new file mode 100644 index 0000000000..197412b971 Binary files /dev/null and b/tests/ref/issue-2105-linebreak-tofu.png differ diff --git a/tests/ref/issue-2128-block-width-box.png b/tests/ref/issue-2128-block-width-box.png new file mode 100644 index 0000000000..40fe6b4f88 Binary files /dev/null and b/tests/ref/issue-2128-block-width-box.png differ diff --git a/tests/ref/issue-2134-pagebreak-bibliography.png b/tests/ref/issue-2134-pagebreak-bibliography.png new file mode 100644 index 0000000000..ad0fb1653f Binary files /dev/null and b/tests/ref/issue-2134-pagebreak-bibliography.png differ diff --git a/tests/ref/issue-2162-pagebreak-set-style.png b/tests/ref/issue-2162-pagebreak-set-style.png new file mode 100644 index 0000000000..4ea6f56ff2 Binary files /dev/null and b/tests/ref/issue-2162-pagebreak-set-style.png differ diff --git a/tests/ref/issue-2199-place-spacing-bottom.png b/tests/ref/issue-2199-place-spacing-bottom.png new file mode 100644 index 0000000000..1f27559bc4 Binary files /dev/null and b/tests/ref/issue-2199-place-spacing-bottom.png differ diff --git a/tests/ref/issue-2199-place-spacing-default.png b/tests/ref/issue-2199-place-spacing-default.png new file mode 100644 index 0000000000..565a830283 Binary files /dev/null and b/tests/ref/issue-2199-place-spacing-default.png differ diff --git a/tests/ref/issue-2214-baseline-math.png b/tests/ref/issue-2214-baseline-math.png new file mode 100644 index 0000000000..9a3e6f3c6a Binary files /dev/null and b/tests/ref/issue-2214-baseline-math.png differ diff --git a/tests/ref/issue-2259-raw-color-overwrite.png b/tests/ref/issue-2259-raw-color-overwrite.png new file mode 100644 index 0000000000..9cf42c4377 Binary files /dev/null and b/tests/ref/issue-2259-raw-color-overwrite.png differ diff --git a/tests/ref/issue-2268-mat-augment-color.png b/tests/ref/issue-2268-mat-augment-color.png new file mode 100644 index 0000000000..5aca29cae1 Binary files /dev/null and b/tests/ref/issue-2268-mat-augment-color.png differ diff --git a/tests/ref/issue-2419-justify-hanging-indent.png b/tests/ref/issue-2419-justify-hanging-indent.png new file mode 100644 index 0000000000..bb478ba45c Binary files /dev/null and b/tests/ref/issue-2419-justify-hanging-indent.png differ diff --git a/tests/ref/issue-2530-enum-item-panic.png b/tests/ref/issue-2530-enum-item-panic.png new file mode 100644 index 0000000000..4f6130bad5 Binary files /dev/null and b/tests/ref/issue-2530-enum-item-panic.png differ diff --git a/tests/ref/issue-2530-figure-caption-panic.png b/tests/ref/issue-2530-figure-caption-panic.png new file mode 100644 index 0000000000..025449efeb Binary files /dev/null and b/tests/ref/issue-2530-figure-caption-panic.png differ diff --git a/tests/ref/issue-2530-list-item-panic.png b/tests/ref/issue-2530-list-item-panic.png new file mode 100644 index 0000000000..14d2f57006 Binary files /dev/null and b/tests/ref/issue-2530-list-item-panic.png differ diff --git a/tests/ref/issue-2530-term-item-panic.png b/tests/ref/issue-2530-term-item-panic.png new file mode 100644 index 0000000000..85b3e92f6c Binary files /dev/null and b/tests/ref/issue-2530-term-item-panic.png differ diff --git a/tests/ref/issue-2531-cite-show-set.png b/tests/ref/issue-2531-cite-show-set.png new file mode 100644 index 0000000000..25723f4ded Binary files /dev/null and b/tests/ref/issue-2531-cite-show-set.png differ diff --git a/tests/ref/issue-2538-cjk-latin-spacing-before-linebreak.png b/tests/ref/issue-2538-cjk-latin-spacing-before-linebreak.png new file mode 100644 index 0000000000..5957164268 Binary files /dev/null and b/tests/ref/issue-2538-cjk-latin-spacing-before-linebreak.png differ diff --git a/tests/ref/issue-2595-float-overlap.png b/tests/ref/issue-2595-float-overlap.png new file mode 100644 index 0000000000..4b460579d5 Binary files /dev/null and b/tests/ref/issue-2595-float-overlap.png differ diff --git a/tests/ref/issue-2650-cjk-latin-spacing-meta.png b/tests/ref/issue-2650-cjk-latin-spacing-meta.png new file mode 100644 index 0000000000..d346b73f90 Binary files /dev/null and b/tests/ref/issue-2650-cjk-latin-spacing-meta.png differ diff --git a/tests/ref/issue-2715-float-order.png b/tests/ref/issue-2715-float-order.png new file mode 100644 index 0000000000..01599d250c Binary files /dev/null and b/tests/ref/issue-2715-float-order.png differ diff --git a/tests/ref/issue-2902-gradient-oklab-panic.png b/tests/ref/issue-2902-gradient-oklab-panic.png new file mode 100644 index 0000000000..f8e18f7ce0 Binary files /dev/null and b/tests/ref/issue-2902-gradient-oklab-panic.png differ diff --git a/tests/ref/issue-2902-gradient-oklch-panic.png b/tests/ref/issue-2902-gradient-oklch-panic.png new file mode 100644 index 0000000000..1af7200e6e Binary files /dev/null and b/tests/ref/issue-2902-gradient-oklch-panic.png differ diff --git a/tests/ref/issue-3082-chinese-punctuation.png b/tests/ref/issue-3082-chinese-punctuation.png new file mode 100644 index 0000000000..642013d09b Binary files /dev/null and b/tests/ref/issue-3082-chinese-punctuation.png differ diff --git a/tests/ref/issue-3191-raw-indent-shrink.png b/tests/ref/issue-3191-raw-indent-shrink.png new file mode 100644 index 0000000000..e7ac73b739 Binary files /dev/null and b/tests/ref/issue-3191-raw-indent-shrink.png differ diff --git a/tests/ref/issue-3191-raw-normal-paragraphs-still-shrink.png b/tests/ref/issue-3191-raw-normal-paragraphs-still-shrink.png new file mode 100644 index 0000000000..1eb4999598 Binary files /dev/null and b/tests/ref/issue-3191-raw-normal-paragraphs-still-shrink.png differ diff --git a/tests/ref/issue-3232-dict-empty.png b/tests/ref/issue-3232-dict-empty.png new file mode 100644 index 0000000000..f8d3f324f3 Binary files /dev/null and b/tests/ref/issue-3232-dict-empty.png differ diff --git a/tests/ref/issue-3264-rect-negative-dimensions.png b/tests/ref/issue-3264-rect-negative-dimensions.png new file mode 100644 index 0000000000..44a046811d Binary files /dev/null and b/tests/ref/issue-3264-rect-negative-dimensions.png differ diff --git a/tests/ref/issue-3363-json-large-number.png b/tests/ref/issue-3363-json-large-number.png new file mode 100644 index 0000000000..3e13dea16b Binary files /dev/null and b/tests/ref/issue-3363-json-large-number.png differ diff --git a/tests/ref/issue-3481-cite-location.png b/tests/ref/issue-3481-cite-location.png new file mode 100644 index 0000000000..cfc13db512 Binary files /dev/null and b/tests/ref/issue-3481-cite-location.png differ diff --git a/tests/ref/issue-3586-figure-caption-separator.png b/tests/ref/issue-3586-figure-caption-separator.png new file mode 100644 index 0000000000..1d038fe3ff Binary files /dev/null and b/tests/ref/issue-3586-figure-caption-separator.png differ diff --git a/tests/ref/issue-3624-spacing-behaviour.png b/tests/ref/issue-3624-spacing-behaviour.png new file mode 100644 index 0000000000..c7db67538c Binary files /dev/null and b/tests/ref/issue-3624-spacing-behaviour.png differ diff --git a/tests/ref/issue-3641-float-loop.png b/tests/ref/issue-3641-float-loop.png new file mode 100644 index 0000000000..4490d30a53 Binary files /dev/null and b/tests/ref/issue-3641-float-loop.png differ diff --git a/tests/ref/issue-3650-italic-equation.png b/tests/ref/issue-3650-italic-equation.png new file mode 100644 index 0000000000..484457e828 Binary files /dev/null and b/tests/ref/issue-3650-italic-equation.png differ diff --git a/tests/ref/issue-3658-math-size.png b/tests/ref/issue-3658-math-size.png new file mode 100644 index 0000000000..db8fccf9aa Binary files /dev/null and b/tests/ref/issue-3658-math-size.png differ diff --git a/tests/ref/issue-3662-pdf-smartquotes.png b/tests/ref/issue-3662-pdf-smartquotes.png new file mode 100644 index 0000000000..ff73cbc809 Binary files /dev/null and b/tests/ref/issue-3662-pdf-smartquotes.png differ diff --git a/tests/ref/issue-3696-equation-rtl.png b/tests/ref/issue-3696-equation-rtl.png new file mode 100644 index 0000000000..1ebf2dc217 Binary files /dev/null and b/tests/ref/issue-3696-equation-rtl.png differ diff --git a/tests/ref/issue-3699-cite-twice-et-al.png b/tests/ref/issue-3699-cite-twice-et-al.png new file mode 100644 index 0000000000..0fe48a48ce Binary files /dev/null and b/tests/ref/issue-3699-cite-twice-et-al.png differ diff --git a/tests/ref/issue-3700-deformed-stroke.png b/tests/ref/issue-3700-deformed-stroke.png new file mode 100644 index 0000000000..9578a675dd Binary files /dev/null and b/tests/ref/issue-3700-deformed-stroke.png differ diff --git a/tests/ref/issue-3726-query-show-set.png b/tests/ref/issue-3726-query-show-set.png new file mode 100644 index 0000000000..3f5c88846f Binary files /dev/null and b/tests/ref/issue-3726-query-show-set.png differ diff --git a/tests/ref/issue-3774-math-call-empty-2d-args.png b/tests/ref/issue-3774-math-call-empty-2d-args.png new file mode 100644 index 0000000000..ce4b4eb8c2 Binary files /dev/null and b/tests/ref/issue-3774-math-call-empty-2d-args.png differ diff --git a/tests/ref/issue-3820-raw-space-when-end-with-backtick.png b/tests/ref/issue-3820-raw-space-when-end-with-backtick.png new file mode 100644 index 0000000000..1ba3fb182e Binary files /dev/null and b/tests/ref/issue-3820-raw-space-when-end-with-backtick.png differ diff --git a/tests/ref/issue-3841-tabs-in-raw-type-code.png b/tests/ref/issue-3841-tabs-in-raw-type-code.png new file mode 100644 index 0000000000..b7a7b1bae4 Binary files /dev/null and b/tests/ref/issue-3841-tabs-in-raw-type-code.png differ diff --git a/tests/ref/issue-3973-math-equation-align.png b/tests/ref/issue-3973-math-equation-align.png new file mode 100644 index 0000000000..91b48849ac Binary files /dev/null and b/tests/ref/issue-3973-math-equation-align.png differ diff --git a/tests/ref/issue-622-hide-meta-cite.png b/tests/ref/issue-622-hide-meta-cite.png new file mode 100644 index 0000000000..8918f668a6 Binary files /dev/null and b/tests/ref/issue-622-hide-meta-cite.png differ diff --git a/tests/ref/issue-622-hide-meta-outline.png b/tests/ref/issue-622-hide-meta-outline.png new file mode 100644 index 0000000000..72a82e4d8b Binary files /dev/null and b/tests/ref/issue-622-hide-meta-outline.png differ diff --git a/tests/ref/issue-785-cite-locate.png b/tests/ref/issue-785-cite-locate.png new file mode 100644 index 0000000000..7c2a650a46 Binary files /dev/null and b/tests/ref/issue-785-cite-locate.png differ diff --git a/tests/ref/issue-80-emoji-linebreak.png b/tests/ref/issue-80-emoji-linebreak.png new file mode 100644 index 0000000000..d35a62b3c2 Binary files /dev/null and b/tests/ref/issue-80-emoji-linebreak.png differ diff --git a/tests/ref/issue-852-mat-type.png b/tests/ref/issue-852-mat-type.png new file mode 100644 index 0000000000..81af3bb5b7 Binary files /dev/null and b/tests/ref/issue-852-mat-type.png differ diff --git a/tests/ref/issue-870-image-rotation.png b/tests/ref/issue-870-image-rotation.png new file mode 100644 index 0000000000..c321a1a93f Binary files /dev/null and b/tests/ref/issue-870-image-rotation.png differ diff --git a/tests/ref/issue-886-args-sink.png b/tests/ref/issue-886-args-sink.png new file mode 100644 index 0000000000..2ef08adf9a Binary files /dev/null and b/tests/ref/issue-886-args-sink.png differ diff --git a/tests/ref/issue-columns-heading.png b/tests/ref/issue-columns-heading.png new file mode 100644 index 0000000000..700972bc06 Binary files /dev/null and b/tests/ref/issue-columns-heading.png differ diff --git a/tests/ref/issue-flow-frame-placement.png b/tests/ref/issue-flow-frame-placement.png new file mode 100644 index 0000000000..27469c2794 Binary files /dev/null and b/tests/ref/issue-flow-frame-placement.png differ diff --git a/tests/ref/issue-flow-layout-index-out-of-bounds.png b/tests/ref/issue-flow-layout-index-out-of-bounds.png new file mode 100644 index 0000000000..8746cbfc99 Binary files /dev/null and b/tests/ref/issue-flow-layout-index-out-of-bounds.png differ diff --git a/tests/ref/issue-flow-overlarge-frames.png b/tests/ref/issue-flow-overlarge-frames.png new file mode 100644 index 0000000000..016af525dd Binary files /dev/null and b/tests/ref/issue-flow-overlarge-frames.png differ diff --git a/tests/ref/issue-flow-trailing-leading.png b/tests/ref/issue-flow-trailing-leading.png new file mode 100644 index 0000000000..4245d42fbf Binary files /dev/null and b/tests/ref/issue-flow-trailing-leading.png differ diff --git a/tests/ref/issue-flow-weak-spacing.png b/tests/ref/issue-flow-weak-spacing.png new file mode 100644 index 0000000000..e37a5ae33d Binary files /dev/null and b/tests/ref/issue-flow-weak-spacing.png differ diff --git a/tests/ref/issue-footnotes-skip-first-page.png b/tests/ref/issue-footnotes-skip-first-page.png new file mode 100644 index 0000000000..d24387e3ba Binary files /dev/null and b/tests/ref/issue-footnotes-skip-first-page.png differ diff --git a/tests/ref/issue-gradient-cmyk-encode.png b/tests/ref/issue-gradient-cmyk-encode.png new file mode 100644 index 0000000000..065d1a3b4a Binary files /dev/null and b/tests/ref/issue-gradient-cmyk-encode.png differ diff --git a/tests/ref/issue-grid-base-auto-row-list.png b/tests/ref/issue-grid-base-auto-row-list.png new file mode 100644 index 0000000000..8da3adf5d9 Binary files /dev/null and b/tests/ref/issue-grid-base-auto-row-list.png differ diff --git a/tests/ref/issue-grid-base-auto-row.png b/tests/ref/issue-grid-base-auto-row.png new file mode 100644 index 0000000000..0e05577de1 Binary files /dev/null and b/tests/ref/issue-grid-base-auto-row.png differ diff --git a/tests/ref/issue-grid-double-skip.png b/tests/ref/issue-grid-double-skip.png new file mode 100644 index 0000000000..2901f29ae9 Binary files /dev/null and b/tests/ref/issue-grid-double-skip.png differ diff --git a/tests/ref/issue-grid-gutter-skip.png b/tests/ref/issue-grid-gutter-skip.png new file mode 100644 index 0000000000..3404fd1014 Binary files /dev/null and b/tests/ref/issue-grid-gutter-skip.png differ diff --git a/tests/ref/issue-grid-skip-list.png b/tests/ref/issue-grid-skip-list.png new file mode 100644 index 0000000000..bd67433765 Binary files /dev/null and b/tests/ref/issue-grid-skip-list.png differ diff --git a/tests/ref/issue-grid-skip.png b/tests/ref/issue-grid-skip.png new file mode 100644 index 0000000000..1b46fd1a33 Binary files /dev/null and b/tests/ref/issue-grid-skip.png differ diff --git a/tests/ref/issue-math-realize-hide.png b/tests/ref/issue-math-realize-hide.png new file mode 100644 index 0000000000..729e9f00c2 Binary files /dev/null and b/tests/ref/issue-math-realize-hide.png differ diff --git a/tests/ref/issue-math-realize-scripting.png b/tests/ref/issue-math-realize-scripting.png new file mode 100644 index 0000000000..a29b0364de Binary files /dev/null and b/tests/ref/issue-math-realize-scripting.png differ diff --git a/tests/ref/issue-math-realize-show.png b/tests/ref/issue-math-realize-show.png new file mode 100644 index 0000000000..d6b727c1de Binary files /dev/null and b/tests/ref/issue-math-realize-show.png differ diff --git a/tests/ref/issue-multiple-footnote-in-one-line.png b/tests/ref/issue-multiple-footnote-in-one-line.png new file mode 100644 index 0000000000..1d8c017d43 Binary files /dev/null and b/tests/ref/issue-multiple-footnote-in-one-line.png differ diff --git a/tests/ref/issue-non-atomic-closure.png b/tests/ref/issue-non-atomic-closure.png new file mode 100644 index 0000000000..f60b465489 Binary files /dev/null and b/tests/ref/issue-non-atomic-closure.png differ diff --git a/tests/ref/issue-path-in-sized-container.png b/tests/ref/issue-path-in-sized-container.png new file mode 100644 index 0000000000..ebecc122b5 Binary files /dev/null and b/tests/ref/issue-path-in-sized-container.png differ diff --git a/tests/ref/issue-place-base.png b/tests/ref/issue-place-base.png new file mode 100644 index 0000000000..45517fe94e Binary files /dev/null and b/tests/ref/issue-place-base.png differ diff --git a/tests/ref/issue-rtl-safe-to-break-panic.png b/tests/ref/issue-rtl-safe-to-break-panic.png new file mode 100644 index 0000000000..5cd9920cc2 Binary files /dev/null and b/tests/ref/issue-rtl-safe-to-break-panic.png differ diff --git a/tests/ref/justify-avoid-runts.png b/tests/ref/justify-avoid-runts.png new file mode 100644 index 0000000000..70513939cd Binary files /dev/null and b/tests/ref/justify-avoid-runts.png differ diff --git a/tests/ref/justify-chinese.png b/tests/ref/justify-chinese.png new file mode 100644 index 0000000000..0284e8b9d3 Binary files /dev/null and b/tests/ref/justify-chinese.png differ diff --git a/tests/ref/justify-code-blocks.png b/tests/ref/justify-code-blocks.png new file mode 100644 index 0000000000..088e8b6365 Binary files /dev/null and b/tests/ref/justify-code-blocks.png differ diff --git a/tests/ref/justify-japanese.png b/tests/ref/justify-japanese.png new file mode 100644 index 0000000000..addeba5485 Binary files /dev/null and b/tests/ref/justify-japanese.png differ diff --git a/tests/ref/justify-justified-linebreak.png b/tests/ref/justify-justified-linebreak.png new file mode 100644 index 0000000000..8792e9e2ef Binary files /dev/null and b/tests/ref/justify-justified-linebreak.png differ diff --git a/tests/ref/justify-knuth-story.png b/tests/ref/justify-knuth-story.png new file mode 100644 index 0000000000..9fbcc3c3e0 Binary files /dev/null and b/tests/ref/justify-knuth-story.png differ diff --git a/tests/ref/justify-manual-linebreak.png b/tests/ref/justify-manual-linebreak.png new file mode 100644 index 0000000000..144a62c73b Binary files /dev/null and b/tests/ref/justify-manual-linebreak.png differ diff --git a/tests/ref/justify-no-leading-spaces.png b/tests/ref/justify-no-leading-spaces.png new file mode 100644 index 0000000000..9d2557b5c0 Binary files /dev/null and b/tests/ref/justify-no-leading-spaces.png differ diff --git a/tests/ref/justify-punctuation-adjustment.png b/tests/ref/justify-punctuation-adjustment.png new file mode 100644 index 0000000000..28d4ef0474 Binary files /dev/null and b/tests/ref/justify-punctuation-adjustment.png differ diff --git a/tests/ref/justify-shrink-last-line.png b/tests/ref/justify-shrink-last-line.png new file mode 100644 index 0000000000..f839e92edf Binary files /dev/null and b/tests/ref/justify-shrink-last-line.png differ diff --git a/tests/ref/justify-variants.png b/tests/ref/justify-variants.png new file mode 100644 index 0000000000..81fcc700e6 Binary files /dev/null and b/tests/ref/justify-variants.png differ diff --git a/tests/ref/justify-whitespace-adjustment.png b/tests/ref/justify-whitespace-adjustment.png new file mode 100644 index 0000000000..4ea6829ca6 Binary files /dev/null and b/tests/ref/justify-whitespace-adjustment.png differ diff --git a/tests/ref/justify-without-justifiables.png b/tests/ref/justify-without-justifiables.png new file mode 100644 index 0000000000..77e5bf1b3d Binary files /dev/null and b/tests/ref/justify-without-justifiables.png differ diff --git a/tests/ref/justify.png b/tests/ref/justify.png new file mode 100644 index 0000000000..4e4fdbf542 Binary files /dev/null and b/tests/ref/justify.png differ diff --git a/tests/ref/label-after-expression.png b/tests/ref/label-after-expression.png new file mode 100644 index 0000000000..5ceaf34202 Binary files /dev/null and b/tests/ref/label-after-expression.png differ diff --git a/tests/ref/label-after-parbreak.png b/tests/ref/label-after-parbreak.png new file mode 100644 index 0000000000..9339c65c27 Binary files /dev/null and b/tests/ref/label-after-parbreak.png differ diff --git a/tests/ref/label-dynamic-show-set.png b/tests/ref/label-dynamic-show-set.png new file mode 100644 index 0000000000..25681b9299 Binary files /dev/null and b/tests/ref/label-dynamic-show-set.png differ diff --git a/tests/ref/label-in-block.png b/tests/ref/label-in-block.png new file mode 100644 index 0000000000..e97bd72570 Binary files /dev/null and b/tests/ref/label-in-block.png differ diff --git a/tests/ref/label-on-text.png b/tests/ref/label-on-text.png new file mode 100644 index 0000000000..67fb1aa81f Binary files /dev/null and b/tests/ref/label-on-text.png differ diff --git a/tests/ref/label-show-where-selector.png b/tests/ref/label-show-where-selector.png new file mode 100644 index 0000000000..61e90a9adc Binary files /dev/null and b/tests/ref/label-show-where-selector.png differ diff --git a/tests/ref/label-unclosed-is-text.png b/tests/ref/label-unclosed-is-text.png new file mode 100644 index 0000000000..051db0cf92 Binary files /dev/null and b/tests/ref/label-unclosed-is-text.png differ diff --git a/tests/ref/layout-in-fixed-size-block.png b/tests/ref/layout-in-fixed-size-block.png new file mode 100644 index 0000000000..6cc321b361 Binary files /dev/null and b/tests/ref/layout-in-fixed-size-block.png differ diff --git a/tests/ref/layout-in-page-call.png b/tests/ref/layout-in-page-call.png new file mode 100644 index 0000000000..9bc75ae79d Binary files /dev/null and b/tests/ref/layout-in-page-call.png differ diff --git a/tests/ref/layout/align.png b/tests/ref/layout/align.png deleted file mode 100644 index a01135976a..0000000000 Binary files a/tests/ref/layout/align.png and /dev/null differ diff --git a/tests/ref/layout/block-sizing.png b/tests/ref/layout/block-sizing.png deleted file mode 100644 index 7d57a0d83e..0000000000 Binary files a/tests/ref/layout/block-sizing.png and /dev/null differ diff --git a/tests/ref/layout/block-spacing.png b/tests/ref/layout/block-spacing.png deleted file mode 100644 index d73abac02f..0000000000 Binary files a/tests/ref/layout/block-spacing.png and /dev/null differ diff --git a/tests/ref/layout/cjk-latin-spacing.png b/tests/ref/layout/cjk-latin-spacing.png deleted file mode 100644 index 629145e46f..0000000000 Binary files a/tests/ref/layout/cjk-latin-spacing.png and /dev/null differ diff --git a/tests/ref/layout/cjk-punctuation-adjustment.png b/tests/ref/layout/cjk-punctuation-adjustment.png deleted file mode 100644 index 1da08f23ff..0000000000 Binary files a/tests/ref/layout/cjk-punctuation-adjustment.png and /dev/null differ diff --git a/tests/ref/layout/clip.png b/tests/ref/layout/clip.png deleted file mode 100644 index f37bf9ad15..0000000000 Binary files a/tests/ref/layout/clip.png and /dev/null differ diff --git a/tests/ref/layout/columns.png b/tests/ref/layout/columns.png deleted file mode 100644 index 38912f1bf6..0000000000 Binary files a/tests/ref/layout/columns.png and /dev/null differ diff --git a/tests/ref/layout/container-fill.png b/tests/ref/layout/container-fill.png deleted file mode 100644 index 74fdc73df5..0000000000 Binary files a/tests/ref/layout/container-fill.png and /dev/null differ diff --git a/tests/ref/layout/container.png b/tests/ref/layout/container.png deleted file mode 100644 index 0cd56b2dfb..0000000000 Binary files a/tests/ref/layout/container.png and /dev/null differ diff --git a/tests/ref/layout/enum-align.png b/tests/ref/layout/enum-align.png deleted file mode 100644 index 18e392f2f4..0000000000 Binary files a/tests/ref/layout/enum-align.png and /dev/null differ diff --git a/tests/ref/layout/enum-numbering.png b/tests/ref/layout/enum-numbering.png deleted file mode 100644 index e1b2103bb5..0000000000 Binary files a/tests/ref/layout/enum-numbering.png and /dev/null differ diff --git a/tests/ref/layout/enum.png b/tests/ref/layout/enum.png deleted file mode 100644 index 62f1e4abfe..0000000000 Binary files a/tests/ref/layout/enum.png and /dev/null differ diff --git a/tests/ref/layout/flow-orphan.png b/tests/ref/layout/flow-orphan.png deleted file mode 100644 index 434636c45c..0000000000 Binary files a/tests/ref/layout/flow-orphan.png and /dev/null differ diff --git a/tests/ref/layout/grid-1.png b/tests/ref/layout/grid-1.png deleted file mode 100644 index 9e33772c82..0000000000 Binary files a/tests/ref/layout/grid-1.png and /dev/null differ diff --git a/tests/ref/layout/grid-2.png b/tests/ref/layout/grid-2.png deleted file mode 100644 index ac1f7014bf..0000000000 Binary files a/tests/ref/layout/grid-2.png and /dev/null differ diff --git a/tests/ref/layout/grid-3.png b/tests/ref/layout/grid-3.png deleted file mode 100644 index 0f54f2ccd0..0000000000 Binary files a/tests/ref/layout/grid-3.png and /dev/null differ diff --git a/tests/ref/layout/grid-4.png b/tests/ref/layout/grid-4.png deleted file mode 100644 index 35a05ab52c..0000000000 Binary files a/tests/ref/layout/grid-4.png and /dev/null differ diff --git a/tests/ref/layout/grid-5.png b/tests/ref/layout/grid-5.png deleted file mode 100644 index 233ebb00c7..0000000000 Binary files a/tests/ref/layout/grid-5.png and /dev/null differ diff --git a/tests/ref/layout/grid-auto-shrink.png b/tests/ref/layout/grid-auto-shrink.png deleted file mode 100644 index 34995215bf..0000000000 Binary files a/tests/ref/layout/grid-auto-shrink.png and /dev/null differ diff --git a/tests/ref/layout/grid-cell.png b/tests/ref/layout/grid-cell.png deleted file mode 100644 index 07508b4001..0000000000 Binary files a/tests/ref/layout/grid-cell.png and /dev/null differ diff --git a/tests/ref/layout/grid-colspan.png b/tests/ref/layout/grid-colspan.png deleted file mode 100644 index e16ca347ee..0000000000 Binary files a/tests/ref/layout/grid-colspan.png and /dev/null differ diff --git a/tests/ref/layout/grid-footers-1.png b/tests/ref/layout/grid-footers-1.png deleted file mode 100644 index 331cf7ad5d..0000000000 Binary files a/tests/ref/layout/grid-footers-1.png and /dev/null differ diff --git a/tests/ref/layout/grid-footers-2.png b/tests/ref/layout/grid-footers-2.png deleted file mode 100644 index 60e9689c62..0000000000 Binary files a/tests/ref/layout/grid-footers-2.png and /dev/null differ diff --git a/tests/ref/layout/grid-footers-3.png b/tests/ref/layout/grid-footers-3.png deleted file mode 100644 index cc4948b804..0000000000 Binary files a/tests/ref/layout/grid-footers-3.png and /dev/null differ diff --git a/tests/ref/layout/grid-footers-4.png b/tests/ref/layout/grid-footers-4.png deleted file mode 100644 index 29a6430bd0..0000000000 Binary files a/tests/ref/layout/grid-footers-4.png and /dev/null differ diff --git a/tests/ref/layout/grid-footers-5.png b/tests/ref/layout/grid-footers-5.png deleted file mode 100644 index 6cae5592c9..0000000000 Binary files a/tests/ref/layout/grid-footers-5.png and /dev/null differ diff --git a/tests/ref/layout/grid-headers-1.png b/tests/ref/layout/grid-headers-1.png deleted file mode 100644 index 7ae2d8d34e..0000000000 Binary files a/tests/ref/layout/grid-headers-1.png and /dev/null differ diff --git a/tests/ref/layout/grid-headers-2.png b/tests/ref/layout/grid-headers-2.png deleted file mode 100644 index 3dbc07c884..0000000000 Binary files a/tests/ref/layout/grid-headers-2.png and /dev/null differ diff --git a/tests/ref/layout/grid-headers-3.png b/tests/ref/layout/grid-headers-3.png deleted file mode 100644 index 9ee77d50b5..0000000000 Binary files a/tests/ref/layout/grid-headers-3.png and /dev/null differ diff --git a/tests/ref/layout/grid-headers-4.png b/tests/ref/layout/grid-headers-4.png deleted file mode 100644 index 1f3e4b10fb..0000000000 Binary files a/tests/ref/layout/grid-headers-4.png and /dev/null differ diff --git a/tests/ref/layout/grid-positioning.png b/tests/ref/layout/grid-positioning.png deleted file mode 100644 index cac93f4038..0000000000 Binary files a/tests/ref/layout/grid-positioning.png and /dev/null differ diff --git a/tests/ref/layout/grid-rowspan-basic.png b/tests/ref/layout/grid-rowspan-basic.png deleted file mode 100644 index b464d8b40a..0000000000 Binary files a/tests/ref/layout/grid-rowspan-basic.png and /dev/null differ diff --git a/tests/ref/layout/grid-rowspan-split-1.png b/tests/ref/layout/grid-rowspan-split-1.png deleted file mode 100644 index 12cd5fc6f5..0000000000 Binary files a/tests/ref/layout/grid-rowspan-split-1.png and /dev/null differ diff --git a/tests/ref/layout/grid-rowspan-split-2.png b/tests/ref/layout/grid-rowspan-split-2.png deleted file mode 100644 index e55c5e2300..0000000000 Binary files a/tests/ref/layout/grid-rowspan-split-2.png and /dev/null differ diff --git a/tests/ref/layout/grid-rowspan-split-3.png b/tests/ref/layout/grid-rowspan-split-3.png deleted file mode 100644 index c3ff4bd150..0000000000 Binary files a/tests/ref/layout/grid-rowspan-split-3.png and /dev/null differ diff --git a/tests/ref/layout/grid-rtl.png b/tests/ref/layout/grid-rtl.png deleted file mode 100644 index d628ee8aed..0000000000 Binary files a/tests/ref/layout/grid-rtl.png and /dev/null differ diff --git a/tests/ref/layout/grid-stroke.png b/tests/ref/layout/grid-stroke.png deleted file mode 100644 index fbba379e85..0000000000 Binary files a/tests/ref/layout/grid-stroke.png and /dev/null differ diff --git a/tests/ref/layout/grid-styling.png b/tests/ref/layout/grid-styling.png deleted file mode 100644 index dc50dd9018..0000000000 Binary files a/tests/ref/layout/grid-styling.png and /dev/null differ diff --git a/tests/ref/layout/hide.png b/tests/ref/layout/hide.png deleted file mode 100644 index d89800494f..0000000000 Binary files a/tests/ref/layout/hide.png and /dev/null differ diff --git a/tests/ref/layout/list-attach.png b/tests/ref/layout/list-attach.png deleted file mode 100644 index 4a6a457372..0000000000 Binary files a/tests/ref/layout/list-attach.png and /dev/null differ diff --git a/tests/ref/layout/list-marker.png b/tests/ref/layout/list-marker.png deleted file mode 100644 index 19d6ed5fab..0000000000 Binary files a/tests/ref/layout/list-marker.png and /dev/null differ diff --git a/tests/ref/layout/list.png b/tests/ref/layout/list.png deleted file mode 100644 index 269243eb90..0000000000 Binary files a/tests/ref/layout/list.png and /dev/null differ diff --git a/tests/ref/layout/out-of-flow-in-block.png b/tests/ref/layout/out-of-flow-in-block.png deleted file mode 100644 index 97637145ff..0000000000 Binary files a/tests/ref/layout/out-of-flow-in-block.png and /dev/null differ diff --git a/tests/ref/layout/pad.png b/tests/ref/layout/pad.png deleted file mode 100644 index d228f07fb0..0000000000 Binary files a/tests/ref/layout/pad.png and /dev/null differ diff --git a/tests/ref/layout/page-binding.png b/tests/ref/layout/page-binding.png deleted file mode 100644 index 5b6d06576e..0000000000 Binary files a/tests/ref/layout/page-binding.png and /dev/null differ diff --git a/tests/ref/layout/page-margin.png b/tests/ref/layout/page-margin.png deleted file mode 100644 index f690724b28..0000000000 Binary files a/tests/ref/layout/page-margin.png and /dev/null differ diff --git a/tests/ref/layout/page-marginals.png b/tests/ref/layout/page-marginals.png deleted file mode 100644 index bbe6358ed6..0000000000 Binary files a/tests/ref/layout/page-marginals.png and /dev/null differ diff --git a/tests/ref/layout/page-number-align.png b/tests/ref/layout/page-number-align.png deleted file mode 100644 index b05ca45456..0000000000 Binary files a/tests/ref/layout/page-number-align.png and /dev/null differ diff --git a/tests/ref/layout/page-style.png b/tests/ref/layout/page-style.png deleted file mode 100644 index ac6b602c18..0000000000 Binary files a/tests/ref/layout/page-style.png and /dev/null differ diff --git a/tests/ref/layout/page.png b/tests/ref/layout/page.png deleted file mode 100644 index bcf325261b..0000000000 Binary files a/tests/ref/layout/page.png and /dev/null differ diff --git a/tests/ref/layout/pagebreak-parity.png b/tests/ref/layout/pagebreak-parity.png deleted file mode 100644 index 0dbabe7aed..0000000000 Binary files a/tests/ref/layout/pagebreak-parity.png and /dev/null differ diff --git a/tests/ref/layout/pagebreak-weak.png b/tests/ref/layout/pagebreak-weak.png deleted file mode 100644 index 412c4e8d65..0000000000 Binary files a/tests/ref/layout/pagebreak-weak.png and /dev/null differ diff --git a/tests/ref/layout/pagebreak.png b/tests/ref/layout/pagebreak.png deleted file mode 100644 index ab05564335..0000000000 Binary files a/tests/ref/layout/pagebreak.png and /dev/null differ diff --git a/tests/ref/layout/par-bidi.png b/tests/ref/layout/par-bidi.png deleted file mode 100644 index 4750ccb88e..0000000000 Binary files a/tests/ref/layout/par-bidi.png and /dev/null differ diff --git a/tests/ref/layout/par-indent.png b/tests/ref/layout/par-indent.png deleted file mode 100644 index cceaa3b915..0000000000 Binary files a/tests/ref/layout/par-indent.png and /dev/null differ diff --git a/tests/ref/layout/par-justify-cjk.png b/tests/ref/layout/par-justify-cjk.png deleted file mode 100644 index a9baf14c5b..0000000000 Binary files a/tests/ref/layout/par-justify-cjk.png and /dev/null differ diff --git a/tests/ref/layout/par-justify.png b/tests/ref/layout/par-justify.png deleted file mode 100644 index 0cd9cbcdcf..0000000000 Binary files a/tests/ref/layout/par-justify.png and /dev/null differ diff --git a/tests/ref/layout/par-knuth.png b/tests/ref/layout/par-knuth.png deleted file mode 100644 index f3da17531e..0000000000 Binary files a/tests/ref/layout/par-knuth.png and /dev/null differ diff --git a/tests/ref/layout/par-simple.png b/tests/ref/layout/par-simple.png deleted file mode 100644 index a645bfd892..0000000000 Binary files a/tests/ref/layout/par-simple.png and /dev/null differ diff --git a/tests/ref/layout/par.png b/tests/ref/layout/par.png deleted file mode 100644 index f25f56d2bd..0000000000 Binary files a/tests/ref/layout/par.png and /dev/null differ diff --git a/tests/ref/layout/place-background.png b/tests/ref/layout/place-background.png deleted file mode 100644 index d9c1c42f35..0000000000 Binary files a/tests/ref/layout/place-background.png and /dev/null differ diff --git a/tests/ref/layout/place-float-auto.png b/tests/ref/layout/place-float-auto.png deleted file mode 100644 index f2e4ee92e4..0000000000 Binary files a/tests/ref/layout/place-float-auto.png and /dev/null differ diff --git a/tests/ref/layout/place-float-columns.png b/tests/ref/layout/place-float-columns.png deleted file mode 100644 index 186b79d147..0000000000 Binary files a/tests/ref/layout/place-float-columns.png and /dev/null differ diff --git a/tests/ref/layout/place-float-figure.png b/tests/ref/layout/place-float-figure.png deleted file mode 100644 index bf9d21b4f9..0000000000 Binary files a/tests/ref/layout/place-float-figure.png and /dev/null differ diff --git a/tests/ref/layout/place-nested.png b/tests/ref/layout/place-nested.png deleted file mode 100644 index 864830d854..0000000000 Binary files a/tests/ref/layout/place-nested.png and /dev/null differ diff --git a/tests/ref/layout/place.png b/tests/ref/layout/place.png deleted file mode 100644 index 2ef85a4de5..0000000000 Binary files a/tests/ref/layout/place.png and /dev/null differ diff --git a/tests/ref/layout/repeat.png b/tests/ref/layout/repeat.png deleted file mode 100644 index 8e21f1022d..0000000000 Binary files a/tests/ref/layout/repeat.png and /dev/null differ diff --git a/tests/ref/layout/spacing.png b/tests/ref/layout/spacing.png deleted file mode 100644 index 9bab536a20..0000000000 Binary files a/tests/ref/layout/spacing.png and /dev/null differ diff --git a/tests/ref/layout/stack-1.png b/tests/ref/layout/stack-1.png deleted file mode 100644 index 1a3133b821..0000000000 Binary files a/tests/ref/layout/stack-1.png and /dev/null differ diff --git a/tests/ref/layout/stack-2.png b/tests/ref/layout/stack-2.png deleted file mode 100644 index 6cb0aad2f1..0000000000 Binary files a/tests/ref/layout/stack-2.png and /dev/null differ diff --git a/tests/ref/layout/table-cell.png b/tests/ref/layout/table-cell.png deleted file mode 100644 index 647a2e1058..0000000000 Binary files a/tests/ref/layout/table-cell.png and /dev/null differ diff --git a/tests/ref/layout/table.png b/tests/ref/layout/table.png deleted file mode 100644 index ddd0e0434d..0000000000 Binary files a/tests/ref/layout/table.png and /dev/null differ diff --git a/tests/ref/layout/terms.png b/tests/ref/layout/terms.png deleted file mode 100644 index e0cd013ab6..0000000000 Binary files a/tests/ref/layout/terms.png and /dev/null differ diff --git a/tests/ref/layout/transform-layout.png b/tests/ref/layout/transform-layout.png deleted file mode 100644 index 576824f065..0000000000 Binary files a/tests/ref/layout/transform-layout.png and /dev/null differ diff --git a/tests/ref/layout/transform.png b/tests/ref/layout/transform.png deleted file mode 100644 index 83b7d13ab4..0000000000 Binary files a/tests/ref/layout/transform.png and /dev/null differ diff --git a/tests/ref/let-basic.png b/tests/ref/let-basic.png new file mode 100644 index 0000000000..ded47a5a98 Binary files /dev/null and b/tests/ref/let-basic.png differ diff --git a/tests/ref/let-termination.png b/tests/ref/let-termination.png new file mode 100644 index 0000000000..552bb4ce40 Binary files /dev/null and b/tests/ref/let-termination.png differ diff --git a/tests/ref/line-basic.png b/tests/ref/line-basic.png new file mode 100644 index 0000000000..007672e120 Binary files /dev/null and b/tests/ref/line-basic.png differ diff --git a/tests/ref/line-positioning.png b/tests/ref/line-positioning.png new file mode 100644 index 0000000000..65678caa64 Binary files /dev/null and b/tests/ref/line-positioning.png differ diff --git a/tests/ref/line-stroke-dash.png b/tests/ref/line-stroke-dash.png new file mode 100644 index 0000000000..f245e32fdc Binary files /dev/null and b/tests/ref/line-stroke-dash.png differ diff --git a/tests/ref/line-stroke-set.png b/tests/ref/line-stroke-set.png new file mode 100644 index 0000000000..f82f899eb3 Binary files /dev/null and b/tests/ref/line-stroke-set.png differ diff --git a/tests/ref/line-stroke.png b/tests/ref/line-stroke.png new file mode 100644 index 0000000000..d0213002e2 Binary files /dev/null and b/tests/ref/line-stroke.png differ diff --git a/tests/ref/linebreak-cite-punctuation.png b/tests/ref/linebreak-cite-punctuation.png new file mode 100644 index 0000000000..64d930c6d1 Binary files /dev/null and b/tests/ref/linebreak-cite-punctuation.png differ diff --git a/tests/ref/linebreak-hyphen-nbsp.png b/tests/ref/linebreak-hyphen-nbsp.png new file mode 100644 index 0000000000..ee88ae5869 Binary files /dev/null and b/tests/ref/linebreak-hyphen-nbsp.png differ diff --git a/tests/ref/linebreak-link-end.png b/tests/ref/linebreak-link-end.png new file mode 100644 index 0000000000..f11e91d60e Binary files /dev/null and b/tests/ref/linebreak-link-end.png differ diff --git a/tests/ref/linebreak-link-justify.png b/tests/ref/linebreak-link-justify.png new file mode 100644 index 0000000000..8007cf3e2a Binary files /dev/null and b/tests/ref/linebreak-link-justify.png differ diff --git a/tests/ref/linebreak-link.png b/tests/ref/linebreak-link.png new file mode 100644 index 0000000000..d5ba8c9e30 Binary files /dev/null and b/tests/ref/linebreak-link.png differ diff --git a/tests/ref/linebreak-manual-consecutive.png b/tests/ref/linebreak-manual-consecutive.png new file mode 100644 index 0000000000..0dbef35b9f Binary files /dev/null and b/tests/ref/linebreak-manual-consecutive.png differ diff --git a/tests/ref/linebreak-manual-directly-after-automatic.png b/tests/ref/linebreak-manual-directly-after-automatic.png new file mode 100644 index 0000000000..006e3ef263 Binary files /dev/null and b/tests/ref/linebreak-manual-directly-after-automatic.png differ diff --git a/tests/ref/linebreak-manual-justified.png b/tests/ref/linebreak-manual-justified.png new file mode 100644 index 0000000000..f74ea3fd0b Binary files /dev/null and b/tests/ref/linebreak-manual-justified.png differ diff --git a/tests/ref/linebreak-manual-trailing-multiple.png b/tests/ref/linebreak-manual-trailing-multiple.png new file mode 100644 index 0000000000..edf3a94991 Binary files /dev/null and b/tests/ref/linebreak-manual-trailing-multiple.png differ diff --git a/tests/ref/linebreak-manual.png b/tests/ref/linebreak-manual.png new file mode 100644 index 0000000000..37aca398ac Binary files /dev/null and b/tests/ref/linebreak-manual.png differ diff --git a/tests/ref/linebreak-math-punctuation.png b/tests/ref/linebreak-math-punctuation.png new file mode 100644 index 0000000000..93b77d2af0 Binary files /dev/null and b/tests/ref/linebreak-math-punctuation.png differ diff --git a/tests/ref/linebreak-narrow-nbsp.png b/tests/ref/linebreak-narrow-nbsp.png new file mode 100644 index 0000000000..81cf82f866 Binary files /dev/null and b/tests/ref/linebreak-narrow-nbsp.png differ diff --git a/tests/ref/linebreak-overflow-double.png b/tests/ref/linebreak-overflow-double.png new file mode 100644 index 0000000000..04a5bbaaab Binary files /dev/null and b/tests/ref/linebreak-overflow-double.png differ diff --git a/tests/ref/linebreak-overflow.png b/tests/ref/linebreak-overflow.png new file mode 100644 index 0000000000..1dfcbc27ec Binary files /dev/null and b/tests/ref/linebreak-overflow.png differ diff --git a/tests/ref/linebreak-shape-run.png b/tests/ref/linebreak-shape-run.png new file mode 100644 index 0000000000..ebfb87f066 Binary files /dev/null and b/tests/ref/linebreak-shape-run.png differ diff --git a/tests/ref/linebreak-thai.png b/tests/ref/linebreak-thai.png new file mode 100644 index 0000000000..8053a2128e Binary files /dev/null and b/tests/ref/linebreak-thai.png differ diff --git a/tests/ref/link-basic.png b/tests/ref/link-basic.png new file mode 100644 index 0000000000..d16c7ef15c Binary files /dev/null and b/tests/ref/link-basic.png differ diff --git a/tests/ref/link-bracket-balanced.png b/tests/ref/link-bracket-balanced.png new file mode 100644 index 0000000000..048a7c52b2 Binary files /dev/null and b/tests/ref/link-bracket-balanced.png differ diff --git a/tests/ref/link-bracket-unbalanced-closing.png b/tests/ref/link-bracket-unbalanced-closing.png new file mode 100644 index 0000000000..e1c1341ce1 Binary files /dev/null and b/tests/ref/link-bracket-unbalanced-closing.png differ diff --git a/tests/ref/link-on-block.png b/tests/ref/link-on-block.png new file mode 100644 index 0000000000..9076983def Binary files /dev/null and b/tests/ref/link-on-block.png differ diff --git a/tests/ref/link-show.png b/tests/ref/link-show.png new file mode 100644 index 0000000000..59542bad8f Binary files /dev/null and b/tests/ref/link-show.png differ diff --git a/tests/ref/link-to-label.png b/tests/ref/link-to-label.png new file mode 100644 index 0000000000..f607552610 Binary files /dev/null and b/tests/ref/link-to-label.png differ diff --git a/tests/ref/link-to-page.png b/tests/ref/link-to-page.png new file mode 100644 index 0000000000..bbd2f10350 Binary files /dev/null and b/tests/ref/link-to-page.png differ diff --git a/tests/ref/link-trailing-period.png b/tests/ref/link-trailing-period.png new file mode 100644 index 0000000000..4dd11f3402 Binary files /dev/null and b/tests/ref/link-trailing-period.png differ diff --git a/tests/ref/link-transformed.png b/tests/ref/link-transformed.png new file mode 100644 index 0000000000..6b94b5cb35 Binary files /dev/null and b/tests/ref/link-transformed.png differ diff --git a/tests/ref/list-attached-above-spacing.png b/tests/ref/list-attached-above-spacing.png new file mode 100644 index 0000000000..0f499769bd Binary files /dev/null and b/tests/ref/list-attached-above-spacing.png differ diff --git a/tests/ref/list-attached.png b/tests/ref/list-attached.png new file mode 100644 index 0000000000..c1735fd019 Binary files /dev/null and b/tests/ref/list-attached.png differ diff --git a/tests/ref/list-basic.png b/tests/ref/list-basic.png new file mode 100644 index 0000000000..edf69cac74 Binary files /dev/null and b/tests/ref/list-basic.png differ diff --git a/tests/ref/list-content-block.png b/tests/ref/list-content-block.png new file mode 100644 index 0000000000..18b003e577 Binary files /dev/null and b/tests/ref/list-content-block.png differ diff --git a/tests/ref/list-indent-specifics.png b/tests/ref/list-indent-specifics.png new file mode 100644 index 0000000000..212e45ed05 Binary files /dev/null and b/tests/ref/list-indent-specifics.png differ diff --git a/tests/ref/list-marker-align-unaffected.png b/tests/ref/list-marker-align-unaffected.png new file mode 100644 index 0000000000..90f9ad45b9 Binary files /dev/null and b/tests/ref/list-marker-align-unaffected.png differ diff --git a/tests/ref/list-marker-bare-hyphen.png b/tests/ref/list-marker-bare-hyphen.png new file mode 100644 index 0000000000..37830fd67d Binary files /dev/null and b/tests/ref/list-marker-bare-hyphen.png differ diff --git a/tests/ref/list-marker-closure.png b/tests/ref/list-marker-closure.png new file mode 100644 index 0000000000..4dba3b9d8d Binary files /dev/null and b/tests/ref/list-marker-closure.png differ diff --git a/tests/ref/list-marker-cycle.png b/tests/ref/list-marker-cycle.png new file mode 100644 index 0000000000..ef219f074b Binary files /dev/null and b/tests/ref/list-marker-cycle.png differ diff --git a/tests/ref/list-marker-dash.png b/tests/ref/list-marker-dash.png new file mode 100644 index 0000000000..10abc8a672 Binary files /dev/null and b/tests/ref/list-marker-dash.png differ diff --git a/tests/ref/list-mix.png b/tests/ref/list-mix.png new file mode 100644 index 0000000000..0f2b03cf9e Binary files /dev/null and b/tests/ref/list-mix.png differ diff --git a/tests/ref/list-mixed-tabs-and-spaces.png b/tests/ref/list-mixed-tabs-and-spaces.png new file mode 100644 index 0000000000..fcddff42bd Binary files /dev/null and b/tests/ref/list-mixed-tabs-and-spaces.png differ diff --git a/tests/ref/list-nested.png b/tests/ref/list-nested.png new file mode 100644 index 0000000000..22f73ecb3e Binary files /dev/null and b/tests/ref/list-nested.png differ diff --git a/tests/ref/list-non-attached-followed-by-attached.png b/tests/ref/list-non-attached-followed-by-attached.png new file mode 100644 index 0000000000..22db4e3881 Binary files /dev/null and b/tests/ref/list-non-attached-followed-by-attached.png differ diff --git a/tests/ref/list-rtl.png b/tests/ref/list-rtl.png new file mode 100644 index 0000000000..db1e754632 Binary files /dev/null and b/tests/ref/list-rtl.png differ diff --git a/tests/ref/list-syntax-edge-cases.png b/tests/ref/list-syntax-edge-cases.png new file mode 100644 index 0000000000..460462e3cd Binary files /dev/null and b/tests/ref/list-syntax-edge-cases.png differ diff --git a/tests/ref/list-tabs.png b/tests/ref/list-tabs.png new file mode 100644 index 0000000000..1fce74c35a Binary files /dev/null and b/tests/ref/list-tabs.png differ diff --git a/tests/ref/list-tight-non-attached-tight.png b/tests/ref/list-tight-non-attached-tight.png new file mode 100644 index 0000000000..96d5181330 Binary files /dev/null and b/tests/ref/list-tight-non-attached-tight.png differ diff --git a/tests/ref/list-top-level-indent.png b/tests/ref/list-top-level-indent.png new file mode 100644 index 0000000000..beb17ede95 Binary files /dev/null and b/tests/ref/list-top-level-indent.png differ diff --git a/tests/ref/list-wide-cannot-attach.png b/tests/ref/list-wide-cannot-attach.png new file mode 100644 index 0000000000..600041a77e Binary files /dev/null and b/tests/ref/list-wide-cannot-attach.png differ diff --git a/tests/ref/list-wide-really-cannot-attach.png b/tests/ref/list-wide-really-cannot-attach.png new file mode 100644 index 0000000000..89680c092f Binary files /dev/null and b/tests/ref/list-wide-really-cannot-attach.png differ diff --git a/tests/ref/locate-element-selector.png b/tests/ref/locate-element-selector.png new file mode 100644 index 0000000000..fc36ddff61 Binary files /dev/null and b/tests/ref/locate-element-selector.png differ diff --git a/tests/ref/locate-position.png b/tests/ref/locate-position.png new file mode 100644 index 0000000000..fc36ddff61 Binary files /dev/null and b/tests/ref/locate-position.png differ diff --git a/tests/ref/loop-break-join-in-first-arg.png b/tests/ref/loop-break-join-in-first-arg.png new file mode 100644 index 0000000000..fbad2125e6 Binary files /dev/null and b/tests/ref/loop-break-join-in-first-arg.png differ diff --git a/tests/ref/loop-break-join-in-nested-blocks.png b/tests/ref/loop-break-join-in-nested-blocks.png new file mode 100644 index 0000000000..6e2af47a60 Binary files /dev/null and b/tests/ref/loop-break-join-in-nested-blocks.png differ diff --git a/tests/ref/loop-break-join-in-set-rule-args.png b/tests/ref/loop-break-join-in-set-rule-args.png new file mode 100644 index 0000000000..37e1377320 Binary files /dev/null and b/tests/ref/loop-break-join-in-set-rule-args.png differ diff --git a/tests/ref/loop-break-join-set-and-show.png b/tests/ref/loop-break-join-set-and-show.png new file mode 100644 index 0000000000..8c81c1472c Binary files /dev/null and b/tests/ref/loop-break-join-set-and-show.png differ diff --git a/tests/ref/lorem-pars.png b/tests/ref/lorem-pars.png new file mode 100644 index 0000000000..5ff0a34513 Binary files /dev/null and b/tests/ref/lorem-pars.png differ diff --git a/tests/ref/lorem.png b/tests/ref/lorem.png new file mode 100644 index 0000000000..197acb1ce2 Binary files /dev/null and b/tests/ref/lorem.png differ diff --git a/tests/ref/math-accent-align.png b/tests/ref/math-accent-align.png new file mode 100644 index 0000000000..84e8dc8ccd Binary files /dev/null and b/tests/ref/math-accent-align.png differ diff --git a/tests/ref/math-accent-bounds.png b/tests/ref/math-accent-bounds.png new file mode 100644 index 0000000000..d987618287 Binary files /dev/null and b/tests/ref/math-accent-bounds.png differ diff --git a/tests/ref/math-accent-func.png b/tests/ref/math-accent-func.png new file mode 100644 index 0000000000..00821f70de Binary files /dev/null and b/tests/ref/math-accent-func.png differ diff --git a/tests/ref/math-accent-high-base.png b/tests/ref/math-accent-high-base.png new file mode 100644 index 0000000000..f4d7580f1e Binary files /dev/null and b/tests/ref/math-accent-high-base.png differ diff --git a/tests/ref/math-accent-sized.png b/tests/ref/math-accent-sized.png new file mode 100644 index 0000000000..76783b2547 Binary files /dev/null and b/tests/ref/math-accent-sized.png differ diff --git a/tests/ref/math-accent-superscript.png b/tests/ref/math-accent-superscript.png new file mode 100644 index 0000000000..8ddf113d5d Binary files /dev/null and b/tests/ref/math-accent-superscript.png differ diff --git a/tests/ref/math-accent-sym-call.png b/tests/ref/math-accent-sym-call.png new file mode 100644 index 0000000000..0837a86c9e Binary files /dev/null and b/tests/ref/math-accent-sym-call.png differ diff --git a/tests/ref/math-accent-wide-base.png b/tests/ref/math-accent-wide-base.png new file mode 100644 index 0000000000..af716bf45f Binary files /dev/null and b/tests/ref/math-accent-wide-base.png differ diff --git a/tests/ref/math-align-aligned-in-source.png b/tests/ref/math-align-aligned-in-source.png new file mode 100644 index 0000000000..958a42c5d2 Binary files /dev/null and b/tests/ref/math-align-aligned-in-source.png differ diff --git a/tests/ref/math-align-basic.png b/tests/ref/math-align-basic.png new file mode 100644 index 0000000000..cf4a8d6ae6 Binary files /dev/null and b/tests/ref/math-align-basic.png differ diff --git a/tests/ref/math-align-cases.png b/tests/ref/math-align-cases.png new file mode 100644 index 0000000000..4ea9a264c6 Binary files /dev/null and b/tests/ref/math-align-cases.png differ diff --git a/tests/ref/math-align-implicit.png b/tests/ref/math-align-implicit.png new file mode 100644 index 0000000000..05a0d98d82 Binary files /dev/null and b/tests/ref/math-align-implicit.png differ diff --git a/tests/ref/math-align-lines-mixed.png b/tests/ref/math-align-lines-mixed.png new file mode 100644 index 0000000000..d50af28c58 Binary files /dev/null and b/tests/ref/math-align-lines-mixed.png differ diff --git a/tests/ref/math-align-post-fix.png b/tests/ref/math-align-post-fix.png new file mode 100644 index 0000000000..33bc3da7f4 Binary files /dev/null and b/tests/ref/math-align-post-fix.png differ diff --git a/tests/ref/math-align-toggle.png b/tests/ref/math-align-toggle.png new file mode 100644 index 0000000000..24448ab5ff Binary files /dev/null and b/tests/ref/math-align-toggle.png differ diff --git a/tests/ref/math-align-weird.png b/tests/ref/math-align-weird.png new file mode 100644 index 0000000000..672742ec1a Binary files /dev/null and b/tests/ref/math-align-weird.png differ diff --git a/tests/ref/math-align-wider-first-column.png b/tests/ref/math-align-wider-first-column.png new file mode 100644 index 0000000000..46c9c3ff16 Binary files /dev/null and b/tests/ref/math-align-wider-first-column.png differ diff --git a/tests/ref/math-at-line-end.png b/tests/ref/math-at-line-end.png new file mode 100644 index 0000000000..6e4173a7f9 Binary files /dev/null and b/tests/ref/math-at-line-end.png differ diff --git a/tests/ref/math-at-line-start.png b/tests/ref/math-at-line-start.png new file mode 100644 index 0000000000..05221db179 Binary files /dev/null and b/tests/ref/math-at-line-start.png differ diff --git a/tests/ref/math-at-par-end.png b/tests/ref/math-at-par-end.png new file mode 100644 index 0000000000..dd3393faa6 Binary files /dev/null and b/tests/ref/math-at-par-end.png differ diff --git a/tests/ref/math-at-par-start.png b/tests/ref/math-at-par-start.png new file mode 100644 index 0000000000..d69b214a8e Binary files /dev/null and b/tests/ref/math-at-par-start.png differ diff --git a/tests/ref/math-attach-default-placement.png b/tests/ref/math-attach-default-placement.png new file mode 100644 index 0000000000..685fb550ac Binary files /dev/null and b/tests/ref/math-attach-default-placement.png differ diff --git a/tests/ref/math-attach-descender-collision.png b/tests/ref/math-attach-descender-collision.png new file mode 100644 index 0000000000..71654916b2 Binary files /dev/null and b/tests/ref/math-attach-descender-collision.png differ diff --git a/tests/ref/math-attach-followed-by-func-call.png b/tests/ref/math-attach-followed-by-func-call.png new file mode 100644 index 0000000000..71d78c16e7 Binary files /dev/null and b/tests/ref/math-attach-followed-by-func-call.png differ diff --git a/tests/ref/math-attach-force-scripts-and-limits.png b/tests/ref/math-attach-force-scripts-and-limits.png new file mode 100644 index 0000000000..21a1050f21 Binary files /dev/null and b/tests/ref/math-attach-force-scripts-and-limits.png differ diff --git a/tests/ref/math-attach-high.png b/tests/ref/math-attach-high.png new file mode 100644 index 0000000000..4bb6cb29fc Binary files /dev/null and b/tests/ref/math-attach-high.png differ diff --git a/tests/ref/math-attach-horizontal-align.png b/tests/ref/math-attach-horizontal-align.png new file mode 100644 index 0000000000..507cb0ffda Binary files /dev/null and b/tests/ref/math-attach-horizontal-align.png differ diff --git a/tests/ref/math-attach-integral.png b/tests/ref/math-attach-integral.png new file mode 100644 index 0000000000..baebf44c6c Binary files /dev/null and b/tests/ref/math-attach-integral.png differ diff --git a/tests/ref/math-attach-large-operator.png b/tests/ref/math-attach-large-operator.png new file mode 100644 index 0000000000..774b603c01 Binary files /dev/null and b/tests/ref/math-attach-large-operator.png differ diff --git a/tests/ref/math-attach-limit.png b/tests/ref/math-attach-limit.png new file mode 100644 index 0000000000..5f9f24d7c8 Binary files /dev/null and b/tests/ref/math-attach-limit.png differ diff --git a/tests/ref/math-attach-mixed.png b/tests/ref/math-attach-mixed.png new file mode 100644 index 0000000000..4be327e3e9 Binary files /dev/null and b/tests/ref/math-attach-mixed.png differ diff --git a/tests/ref/math-attach-nested.png b/tests/ref/math-attach-nested.png new file mode 100644 index 0000000000..8b4309cf8c Binary files /dev/null and b/tests/ref/math-attach-nested.png differ diff --git a/tests/ref/math-attach-postscripts.png b/tests/ref/math-attach-postscripts.png new file mode 100644 index 0000000000..bd94e4bd77 Binary files /dev/null and b/tests/ref/math-attach-postscripts.png differ diff --git a/tests/ref/math-attach-prescripts.png b/tests/ref/math-attach-prescripts.png new file mode 100644 index 0000000000..cd105e9df4 Binary files /dev/null and b/tests/ref/math-attach-prescripts.png differ diff --git a/tests/ref/math-attach-show-limit.png b/tests/ref/math-attach-show-limit.png new file mode 100644 index 0000000000..4ce2b3fbc3 Binary files /dev/null and b/tests/ref/math-attach-show-limit.png differ diff --git a/tests/ref/math-attach-subscript-multiline.png b/tests/ref/math-attach-subscript-multiline.png new file mode 100644 index 0000000000..7f9aec2e4b Binary files /dev/null and b/tests/ref/math-attach-subscript-multiline.png differ diff --git a/tests/ref/math-attach-to-group.png b/tests/ref/math-attach-to-group.png new file mode 100644 index 0000000000..a3d1923eb0 Binary files /dev/null and b/tests/ref/math-attach-to-group.png differ diff --git a/tests/ref/math-binom-multiple.png b/tests/ref/math-binom-multiple.png new file mode 100644 index 0000000000..7eb60be011 Binary files /dev/null and b/tests/ref/math-binom-multiple.png differ diff --git a/tests/ref/math-binom.png b/tests/ref/math-binom.png new file mode 100644 index 0000000000..85ab08f92a Binary files /dev/null and b/tests/ref/math-binom.png differ diff --git a/tests/ref/math-box-with-baseline.png b/tests/ref/math-box-with-baseline.png new file mode 100644 index 0000000000..e38e6442fa Binary files /dev/null and b/tests/ref/math-box-with-baseline.png differ diff --git a/tests/ref/math-box-without-baseline.png b/tests/ref/math-box-without-baseline.png new file mode 100644 index 0000000000..275495878e Binary files /dev/null and b/tests/ref/math-box-without-baseline.png differ diff --git a/tests/ref/math-call-2d-semicolon-priority.png b/tests/ref/math-call-2d-semicolon-priority.png new file mode 100644 index 0000000000..18807e0bf3 Binary files /dev/null and b/tests/ref/math-call-2d-semicolon-priority.png differ diff --git a/tests/ref/math-call-empty-args-non-func.png b/tests/ref/math-call-empty-args-non-func.png new file mode 100644 index 0000000000..5ca90df5c0 Binary files /dev/null and b/tests/ref/math-call-empty-args-non-func.png differ diff --git a/tests/ref/math-call-non-func.png b/tests/ref/math-call-non-func.png new file mode 100644 index 0000000000..da46efc964 Binary files /dev/null and b/tests/ref/math-call-non-func.png differ diff --git a/tests/ref/math-call-pass-to-box.png b/tests/ref/math-call-pass-to-box.png new file mode 100644 index 0000000000..0ce1b3d08f Binary files /dev/null and b/tests/ref/math-call-pass-to-box.png differ diff --git a/tests/ref/math-cancel-angle-absolute.png b/tests/ref/math-cancel-angle-absolute.png new file mode 100644 index 0000000000..125e59fb11 Binary files /dev/null and b/tests/ref/math-cancel-angle-absolute.png differ diff --git a/tests/ref/math-cancel-angle-func.png b/tests/ref/math-cancel-angle-func.png new file mode 100644 index 0000000000..54f6e75955 Binary files /dev/null and b/tests/ref/math-cancel-angle-func.png differ diff --git a/tests/ref/math-cancel-cross.png b/tests/ref/math-cancel-cross.png new file mode 100644 index 0000000000..49fba664e8 Binary files /dev/null and b/tests/ref/math-cancel-cross.png differ diff --git a/tests/ref/math-cancel-customized.png b/tests/ref/math-cancel-customized.png new file mode 100644 index 0000000000..9fa5045d7c Binary files /dev/null and b/tests/ref/math-cancel-customized.png differ diff --git a/tests/ref/math-cancel-display.png b/tests/ref/math-cancel-display.png new file mode 100644 index 0000000000..30d30a5968 Binary files /dev/null and b/tests/ref/math-cancel-display.png differ diff --git a/tests/ref/math-cancel-inline.png b/tests/ref/math-cancel-inline.png new file mode 100644 index 0000000000..4d92bc5e3b Binary files /dev/null and b/tests/ref/math-cancel-inline.png differ diff --git a/tests/ref/math-cancel-inverted.png b/tests/ref/math-cancel-inverted.png new file mode 100644 index 0000000000..129d53a053 Binary files /dev/null and b/tests/ref/math-cancel-inverted.png differ diff --git a/tests/ref/math-cases-gap.png b/tests/ref/math-cases-gap.png new file mode 100644 index 0000000000..e357913079 Binary files /dev/null and b/tests/ref/math-cases-gap.png differ diff --git a/tests/ref/math-cases.png b/tests/ref/math-cases.png new file mode 100644 index 0000000000..2e8c260a38 Binary files /dev/null and b/tests/ref/math-cases.png differ diff --git a/tests/ref/math-class-chars.png b/tests/ref/math-class-chars.png new file mode 100644 index 0000000000..a4f7d29b11 Binary files /dev/null and b/tests/ref/math-class-chars.png differ diff --git a/tests/ref/math-class-content.png b/tests/ref/math-class-content.png new file mode 100644 index 0000000000..47603fb4be Binary files /dev/null and b/tests/ref/math-class-content.png differ diff --git a/tests/ref/math-class-exceptions.png b/tests/ref/math-class-exceptions.png new file mode 100644 index 0000000000..8b3ecc8161 Binary files /dev/null and b/tests/ref/math-class-exceptions.png differ diff --git a/tests/ref/math-class-limits.png b/tests/ref/math-class-limits.png new file mode 100644 index 0000000000..140acf956f Binary files /dev/null and b/tests/ref/math-class-limits.png differ diff --git a/tests/ref/math-class-nested.png b/tests/ref/math-class-nested.png new file mode 100644 index 0000000000..5847868eae Binary files /dev/null and b/tests/ref/math-class-nested.png differ diff --git a/tests/ref/math-common-symbols.png b/tests/ref/math-common-symbols.png new file mode 100644 index 0000000000..22da84b526 Binary files /dev/null and b/tests/ref/math-common-symbols.png differ diff --git a/tests/ref/math-consecutive.png b/tests/ref/math-consecutive.png new file mode 100644 index 0000000000..63209657fa Binary files /dev/null and b/tests/ref/math-consecutive.png differ diff --git a/tests/ref/math-dif.png b/tests/ref/math-dif.png new file mode 100644 index 0000000000..dfe88b3c3b Binary files /dev/null and b/tests/ref/math-dif.png differ diff --git a/tests/ref/math-equation-align-numbered.png b/tests/ref/math-equation-align-numbered.png new file mode 100644 index 0000000000..e43054c8cf Binary files /dev/null and b/tests/ref/math-equation-align-numbered.png differ diff --git a/tests/ref/math-equation-align-unnumbered.png b/tests/ref/math-equation-align-unnumbered.png new file mode 100644 index 0000000000..413da12009 Binary files /dev/null and b/tests/ref/math-equation-align-unnumbered.png differ diff --git a/tests/ref/math-equation-auto-wrapping.png b/tests/ref/math-equation-auto-wrapping.png new file mode 100644 index 0000000000..9c600172e6 Binary files /dev/null and b/tests/ref/math-equation-auto-wrapping.png differ diff --git a/tests/ref/math-equation-font.png b/tests/ref/math-equation-font.png new file mode 100644 index 0000000000..b105d9e353 Binary files /dev/null and b/tests/ref/math-equation-font.png differ diff --git a/tests/ref/math-equation-number-align-end.png b/tests/ref/math-equation-number-align-end.png new file mode 100644 index 0000000000..f60a15ec1e Binary files /dev/null and b/tests/ref/math-equation-number-align-end.png differ diff --git a/tests/ref/math-equation-number-align-left.png b/tests/ref/math-equation-number-align-left.png new file mode 100644 index 0000000000..a8ed40a5e3 Binary files /dev/null and b/tests/ref/math-equation-number-align-left.png differ diff --git a/tests/ref/math-equation-number-align-multiline-bottom.png b/tests/ref/math-equation-number-align-multiline-bottom.png new file mode 100644 index 0000000000..cb0e5daa97 Binary files /dev/null and b/tests/ref/math-equation-number-align-multiline-bottom.png differ diff --git a/tests/ref/math-equation-number-align-multiline-expand.png b/tests/ref/math-equation-number-align-multiline-expand.png new file mode 100644 index 0000000000..3c3cdc0513 Binary files /dev/null and b/tests/ref/math-equation-number-align-multiline-expand.png differ diff --git a/tests/ref/math-equation-number-align-multiline-top-start.png b/tests/ref/math-equation-number-align-multiline-top-start.png new file mode 100644 index 0000000000..43346de95a Binary files /dev/null and b/tests/ref/math-equation-number-align-multiline-top-start.png differ diff --git a/tests/ref/math-equation-number-align-multiline.png b/tests/ref/math-equation-number-align-multiline.png new file mode 100644 index 0000000000..a46bc1e985 Binary files /dev/null and b/tests/ref/math-equation-number-align-multiline.png differ diff --git a/tests/ref/math-equation-number-align-right.png b/tests/ref/math-equation-number-align-right.png new file mode 100644 index 0000000000..e3d588c4e4 Binary files /dev/null and b/tests/ref/math-equation-number-align-right.png differ diff --git a/tests/ref/math-equation-number-align-start.png b/tests/ref/math-equation-number-align-start.png new file mode 100644 index 0000000000..67ed3c4c28 Binary files /dev/null and b/tests/ref/math-equation-number-align-start.png differ diff --git a/tests/ref/math-equation-number-align.png b/tests/ref/math-equation-number-align.png new file mode 100644 index 0000000000..f60a15ec1e Binary files /dev/null and b/tests/ref/math-equation-number-align.png differ diff --git a/tests/ref/math-equation-numbering.png b/tests/ref/math-equation-numbering.png new file mode 100644 index 0000000000..b1e6b10e32 Binary files /dev/null and b/tests/ref/math-equation-numbering.png differ diff --git a/tests/ref/math-equation-show-rule.png b/tests/ref/math-equation-show-rule.png new file mode 100644 index 0000000000..26da7cd19c Binary files /dev/null and b/tests/ref/math-equation-show-rule.png differ diff --git a/tests/ref/math-font-fallback.png b/tests/ref/math-font-fallback.png new file mode 100644 index 0000000000..50fa85c7e8 Binary files /dev/null and b/tests/ref/math-font-fallback.png differ diff --git a/tests/ref/math-font-features.png b/tests/ref/math-font-features.png new file mode 100644 index 0000000000..0fd5e6e131 Binary files /dev/null and b/tests/ref/math-font-features.png differ diff --git a/tests/ref/math-font-switch.png b/tests/ref/math-font-switch.png new file mode 100644 index 0000000000..4c572ea547 Binary files /dev/null and b/tests/ref/math-font-switch.png differ diff --git a/tests/ref/math-frac-associativity.png b/tests/ref/math-frac-associativity.png new file mode 100644 index 0000000000..a5daca5963 Binary files /dev/null and b/tests/ref/math-frac-associativity.png differ diff --git a/tests/ref/math-frac-baseline.png b/tests/ref/math-frac-baseline.png new file mode 100644 index 0000000000..d65e2c3376 Binary files /dev/null and b/tests/ref/math-frac-baseline.png differ diff --git a/tests/ref/math-frac-large.png b/tests/ref/math-frac-large.png new file mode 100644 index 0000000000..ff9520f376 Binary files /dev/null and b/tests/ref/math-frac-large.png differ diff --git a/tests/ref/math-frac-paren-removal.png b/tests/ref/math-frac-paren-removal.png new file mode 100644 index 0000000000..4f58f1d390 Binary files /dev/null and b/tests/ref/math-frac-paren-removal.png differ diff --git a/tests/ref/math-frac-precedence.png b/tests/ref/math-frac-precedence.png new file mode 100644 index 0000000000..236b9989e2 Binary files /dev/null and b/tests/ref/math-frac-precedence.png differ diff --git a/tests/ref/math-linebreaking-after-binop-and-rel.png b/tests/ref/math-linebreaking-after-binop-and-rel.png new file mode 100644 index 0000000000..1cdd73c4f9 Binary files /dev/null and b/tests/ref/math-linebreaking-after-binop-and-rel.png differ diff --git a/tests/ref/math-linebreaking-after-relation-without-space.png b/tests/ref/math-linebreaking-after-relation-without-space.png new file mode 100644 index 0000000000..7c569ad1fd Binary files /dev/null and b/tests/ref/math-linebreaking-after-relation-without-space.png differ diff --git a/tests/ref/math-linebreaking-between-consecutive-relations.png b/tests/ref/math-linebreaking-between-consecutive-relations.png new file mode 100644 index 0000000000..ba222c5739 Binary files /dev/null and b/tests/ref/math-linebreaking-between-consecutive-relations.png differ diff --git a/tests/ref/math-linebreaking-empty.png b/tests/ref/math-linebreaking-empty.png new file mode 100644 index 0000000000..2b0917a67e Binary files /dev/null and b/tests/ref/math-linebreaking-empty.png differ diff --git a/tests/ref/math-linebreaking-in-box.png b/tests/ref/math-linebreaking-in-box.png new file mode 100644 index 0000000000..e026f1a22a Binary files /dev/null and b/tests/ref/math-linebreaking-in-box.png differ diff --git a/tests/ref/math-linebreaking-lr.png b/tests/ref/math-linebreaking-lr.png new file mode 100644 index 0000000000..69f08e7eac Binary files /dev/null and b/tests/ref/math-linebreaking-lr.png differ diff --git a/tests/ref/math-linebreaking-multiline.png b/tests/ref/math-linebreaking-multiline.png new file mode 100644 index 0000000000..cd5f2fcea8 Binary files /dev/null and b/tests/ref/math-linebreaking-multiline.png differ diff --git a/tests/ref/math-linebreaking-trailing-linebreak.png b/tests/ref/math-linebreaking-trailing-linebreak.png new file mode 100644 index 0000000000..4a50832baa Binary files /dev/null and b/tests/ref/math-linebreaking-trailing-linebreak.png differ diff --git a/tests/ref/math-lr-call.png b/tests/ref/math-lr-call.png new file mode 100644 index 0000000000..baf668d4d7 Binary files /dev/null and b/tests/ref/math-lr-call.png differ diff --git a/tests/ref/math-lr-color.png b/tests/ref/math-lr-color.png new file mode 100644 index 0000000000..6687681920 Binary files /dev/null and b/tests/ref/math-lr-color.png differ diff --git a/tests/ref/math-lr-fences.png b/tests/ref/math-lr-fences.png new file mode 100644 index 0000000000..32314cb4b1 Binary files /dev/null and b/tests/ref/math-lr-fences.png differ diff --git a/tests/ref/math-lr-half.png b/tests/ref/math-lr-half.png new file mode 100644 index 0000000000..311188b4c0 Binary files /dev/null and b/tests/ref/math-lr-half.png differ diff --git a/tests/ref/math-lr-matching.png b/tests/ref/math-lr-matching.png new file mode 100644 index 0000000000..e5fd4c7b50 Binary files /dev/null and b/tests/ref/math-lr-matching.png differ diff --git a/tests/ref/math-lr-mid.png b/tests/ref/math-lr-mid.png new file mode 100644 index 0000000000..1e14759e67 Binary files /dev/null and b/tests/ref/math-lr-mid.png differ diff --git a/tests/ref/math-lr-shorthands.png b/tests/ref/math-lr-shorthands.png new file mode 100644 index 0000000000..d8961672eb Binary files /dev/null and b/tests/ref/math-lr-shorthands.png differ diff --git a/tests/ref/math-lr-size.png b/tests/ref/math-lr-size.png new file mode 100644 index 0000000000..09d2442152 Binary files /dev/null and b/tests/ref/math-lr-size.png differ diff --git a/tests/ref/math-lr-symbol-unmatched.png b/tests/ref/math-lr-symbol-unmatched.png new file mode 100644 index 0000000000..38d0a988a7 Binary files /dev/null and b/tests/ref/math-lr-symbol-unmatched.png differ diff --git a/tests/ref/math-lr-unbalanced.png b/tests/ref/math-lr-unbalanced.png new file mode 100644 index 0000000000..eff579ba4c Binary files /dev/null and b/tests/ref/math-lr-unbalanced.png differ diff --git a/tests/ref/math-lr-unmatched.png b/tests/ref/math-lr-unmatched.png new file mode 100644 index 0000000000..9a0f3275eb Binary files /dev/null and b/tests/ref/math-lr-unmatched.png differ diff --git a/tests/ref/math-lr-weak-spacing.png b/tests/ref/math-lr-weak-spacing.png new file mode 100644 index 0000000000..871aaa2eba Binary files /dev/null and b/tests/ref/math-lr-weak-spacing.png differ diff --git a/tests/ref/math-mat-align-complex.png b/tests/ref/math-mat-align-complex.png new file mode 100644 index 0000000000..682fed2286 Binary files /dev/null and b/tests/ref/math-mat-align-complex.png differ diff --git a/tests/ref/math-mat-align-explicit--alternating.png b/tests/ref/math-mat-align-explicit--alternating.png new file mode 100644 index 0000000000..cb29eb067e Binary files /dev/null and b/tests/ref/math-mat-align-explicit--alternating.png differ diff --git a/tests/ref/math-mat-align-explicit-left.png b/tests/ref/math-mat-align-explicit-left.png new file mode 100644 index 0000000000..97fe0a1f42 Binary files /dev/null and b/tests/ref/math-mat-align-explicit-left.png differ diff --git a/tests/ref/math-mat-align-explicit-right.png b/tests/ref/math-mat-align-explicit-right.png new file mode 100644 index 0000000000..80966e5246 Binary files /dev/null and b/tests/ref/math-mat-align-explicit-right.png differ diff --git a/tests/ref/math-mat-align-implicit.png b/tests/ref/math-mat-align-implicit.png new file mode 100644 index 0000000000..0c14f1a790 Binary files /dev/null and b/tests/ref/math-mat-align-implicit.png differ diff --git a/tests/ref/math-mat-align-signed-numbers.png b/tests/ref/math-mat-align-signed-numbers.png new file mode 100644 index 0000000000..02a3c58203 Binary files /dev/null and b/tests/ref/math-mat-align-signed-numbers.png differ diff --git a/tests/ref/math-mat-augment-set.png b/tests/ref/math-mat-augment-set.png new file mode 100644 index 0000000000..f3827c41f0 Binary files /dev/null and b/tests/ref/math-mat-augment-set.png differ diff --git a/tests/ref/math-mat-augment.png b/tests/ref/math-mat-augment.png new file mode 100644 index 0000000000..3a272ce903 Binary files /dev/null and b/tests/ref/math-mat-augment.png differ diff --git a/tests/ref/math-mat-baseline.png b/tests/ref/math-mat-baseline.png new file mode 100644 index 0000000000..51e90a1f32 Binary files /dev/null and b/tests/ref/math-mat-baseline.png differ diff --git a/tests/ref/math-mat-delim-direct.png b/tests/ref/math-mat-delim-direct.png new file mode 100644 index 0000000000..b40fd36cf7 Binary files /dev/null and b/tests/ref/math-mat-delim-direct.png differ diff --git a/tests/ref/math-mat-delim-set.png b/tests/ref/math-mat-delim-set.png new file mode 100644 index 0000000000..fc92fd4b39 Binary files /dev/null and b/tests/ref/math-mat-delim-set.png differ diff --git a/tests/ref/math-mat-gap.png b/tests/ref/math-mat-gap.png new file mode 100644 index 0000000000..5eb8460d7f Binary files /dev/null and b/tests/ref/math-mat-gap.png differ diff --git a/tests/ref/math-mat-gaps.png b/tests/ref/math-mat-gaps.png new file mode 100644 index 0000000000..38cf524754 Binary files /dev/null and b/tests/ref/math-mat-gaps.png differ diff --git a/tests/ref/math-mat-semicolon.png b/tests/ref/math-mat-semicolon.png new file mode 100644 index 0000000000..abb5d1df8b Binary files /dev/null and b/tests/ref/math-mat-semicolon.png differ diff --git a/tests/ref/math-mat-sparse.png b/tests/ref/math-mat-sparse.png new file mode 100644 index 0000000000..4d077931b1 Binary files /dev/null and b/tests/ref/math-mat-sparse.png differ diff --git a/tests/ref/math-multiline-multiple-trailing-linebreaks.png b/tests/ref/math-multiline-multiple-trailing-linebreaks.png new file mode 100644 index 0000000000..2c6484c7a8 Binary files /dev/null and b/tests/ref/math-multiline-multiple-trailing-linebreaks.png differ diff --git a/tests/ref/math-multiline-no-trailing-linebreak.png b/tests/ref/math-multiline-no-trailing-linebreak.png new file mode 100644 index 0000000000..8ad6204d46 Binary files /dev/null and b/tests/ref/math-multiline-no-trailing-linebreak.png differ diff --git a/tests/ref/math-multiline-trailing-linebreak.png b/tests/ref/math-multiline-trailing-linebreak.png new file mode 100644 index 0000000000..364d8624df Binary files /dev/null and b/tests/ref/math-multiline-trailing-linebreak.png differ diff --git a/tests/ref/math-nested-normal-layout.png b/tests/ref/math-nested-normal-layout.png new file mode 100644 index 0000000000..4ec7d46ebb Binary files /dev/null and b/tests/ref/math-nested-normal-layout.png differ diff --git a/tests/ref/math-non-math-content.png b/tests/ref/math-non-math-content.png new file mode 100644 index 0000000000..66896d1864 Binary files /dev/null and b/tests/ref/math-non-math-content.png differ diff --git a/tests/ref/math-op-call.png b/tests/ref/math-op-call.png new file mode 100644 index 0000000000..2fcdf2cb30 Binary files /dev/null and b/tests/ref/math-op-call.png differ diff --git a/tests/ref/math-op-custom.png b/tests/ref/math-op-custom.png new file mode 100644 index 0000000000..fbba241d5d Binary files /dev/null and b/tests/ref/math-op-custom.png differ diff --git a/tests/ref/math-op-predefined.png b/tests/ref/math-op-predefined.png new file mode 100644 index 0000000000..bfede9e73a Binary files /dev/null and b/tests/ref/math-op-predefined.png differ diff --git a/tests/ref/math-op-scripts-vs-limits.png b/tests/ref/math-op-scripts-vs-limits.png new file mode 100644 index 0000000000..418974169c Binary files /dev/null and b/tests/ref/math-op-scripts-vs-limits.png differ diff --git a/tests/ref/math-op-styled.png b/tests/ref/math-op-styled.png new file mode 100644 index 0000000000..c6890d74e7 Binary files /dev/null and b/tests/ref/math-op-styled.png differ diff --git a/tests/ref/math-optical-size-frac-script-script.png b/tests/ref/math-optical-size-frac-script-script.png new file mode 100644 index 0000000000..893b343495 Binary files /dev/null and b/tests/ref/math-optical-size-frac-script-script.png differ diff --git a/tests/ref/math-optical-size-nested-scripts.png b/tests/ref/math-optical-size-nested-scripts.png new file mode 100644 index 0000000000..8ca35c6e41 Binary files /dev/null and b/tests/ref/math-optical-size-nested-scripts.png differ diff --git a/tests/ref/math-optical-size-prime-large-operator.png b/tests/ref/math-optical-size-prime-large-operator.png new file mode 100644 index 0000000000..b38a934edc Binary files /dev/null and b/tests/ref/math-optical-size-prime-large-operator.png differ diff --git a/tests/ref/math-optical-size-primes.png b/tests/ref/math-optical-size-primes.png new file mode 100644 index 0000000000..8fc199aa89 Binary files /dev/null and b/tests/ref/math-optical-size-primes.png differ diff --git a/tests/ref/math-primes-after-code-expr.png b/tests/ref/math-primes-after-code-expr.png new file mode 100644 index 0000000000..5ec3bc8cf0 Binary files /dev/null and b/tests/ref/math-primes-after-code-expr.png differ diff --git a/tests/ref/math-primes-attach.png b/tests/ref/math-primes-attach.png new file mode 100644 index 0000000000..95b77882e9 Binary files /dev/null and b/tests/ref/math-primes-attach.png differ diff --git a/tests/ref/math-primes-complex.png b/tests/ref/math-primes-complex.png new file mode 100644 index 0000000000..5f5558eb21 Binary files /dev/null and b/tests/ref/math-primes-complex.png differ diff --git a/tests/ref/math-primes-limits.png b/tests/ref/math-primes-limits.png new file mode 100644 index 0000000000..f2c5cec263 Binary files /dev/null and b/tests/ref/math-primes-limits.png differ diff --git a/tests/ref/math-primes-scripts.png b/tests/ref/math-primes-scripts.png new file mode 100644 index 0000000000..2a9121809b Binary files /dev/null and b/tests/ref/math-primes-scripts.png differ diff --git a/tests/ref/math-primes-spaces.png b/tests/ref/math-primes-spaces.png new file mode 100644 index 0000000000..890cc34844 Binary files /dev/null and b/tests/ref/math-primes-spaces.png differ diff --git a/tests/ref/math-primes.png b/tests/ref/math-primes.png new file mode 100644 index 0000000000..f3323197b7 Binary files /dev/null and b/tests/ref/math-primes.png differ diff --git a/tests/ref/math-root-basic.png b/tests/ref/math-root-basic.png new file mode 100644 index 0000000000..b8b891eb03 Binary files /dev/null and b/tests/ref/math-root-basic.png differ diff --git a/tests/ref/math-root-large-body.png b/tests/ref/math-root-large-body.png new file mode 100644 index 0000000000..3dd4d848e4 Binary files /dev/null and b/tests/ref/math-root-large-body.png differ diff --git a/tests/ref/math-root-large-index.png b/tests/ref/math-root-large-index.png new file mode 100644 index 0000000000..8037222cb2 Binary files /dev/null and b/tests/ref/math-root-large-index.png differ diff --git a/tests/ref/math-root-precomposed.png b/tests/ref/math-root-precomposed.png new file mode 100644 index 0000000000..e09f187816 Binary files /dev/null and b/tests/ref/math-root-precomposed.png differ diff --git a/tests/ref/math-root-radical-attachment.png b/tests/ref/math-root-radical-attachment.png new file mode 100644 index 0000000000..4cb447ec09 Binary files /dev/null and b/tests/ref/math-root-radical-attachment.png differ diff --git a/tests/ref/math-root-syntax.png b/tests/ref/math-root-syntax.png new file mode 100644 index 0000000000..492554934c Binary files /dev/null and b/tests/ref/math-root-syntax.png differ diff --git a/tests/ref/math-shorthands.png b/tests/ref/math-shorthands.png new file mode 100644 index 0000000000..ff26ce96f1 Binary files /dev/null and b/tests/ref/math-shorthands.png differ diff --git a/tests/ref/math-size.png b/tests/ref/math-size.png new file mode 100644 index 0000000000..b44e4c7458 Binary files /dev/null and b/tests/ref/math-size.png differ diff --git a/tests/ref/math-spacing-basic.png b/tests/ref/math-spacing-basic.png new file mode 100644 index 0000000000..5567b08756 Binary files /dev/null and b/tests/ref/math-spacing-basic.png differ diff --git a/tests/ref/math-spacing-decorated.png b/tests/ref/math-spacing-decorated.png new file mode 100644 index 0000000000..e34066ea22 Binary files /dev/null and b/tests/ref/math-spacing-decorated.png differ diff --git a/tests/ref/math-spacing-kept-spaces.png b/tests/ref/math-spacing-kept-spaces.png new file mode 100644 index 0000000000..bb433d4fc5 Binary files /dev/null and b/tests/ref/math-spacing-kept-spaces.png differ diff --git a/tests/ref/math-spacing-predefined.png b/tests/ref/math-spacing-predefined.png new file mode 100644 index 0000000000..244e764287 Binary files /dev/null and b/tests/ref/math-spacing-predefined.png differ diff --git a/tests/ref/math-spacing-set-comprehension.png b/tests/ref/math-spacing-set-comprehension.png new file mode 100644 index 0000000000..63ef46ca92 Binary files /dev/null and b/tests/ref/math-spacing-set-comprehension.png differ diff --git a/tests/ref/math-spacing-weak.png b/tests/ref/math-spacing-weak.png new file mode 100644 index 0000000000..71af322217 Binary files /dev/null and b/tests/ref/math-spacing-weak.png differ diff --git a/tests/ref/math-style-exceptions.png b/tests/ref/math-style-exceptions.png new file mode 100644 index 0000000000..bdeabb6732 Binary files /dev/null and b/tests/ref/math-style-exceptions.png differ diff --git a/tests/ref/math-style-greek-exceptions.png b/tests/ref/math-style-greek-exceptions.png new file mode 100644 index 0000000000..93ae6309bc Binary files /dev/null and b/tests/ref/math-style-greek-exceptions.png differ diff --git a/tests/ref/math-style-hebrew-exceptions.png b/tests/ref/math-style-hebrew-exceptions.png new file mode 100644 index 0000000000..723466e8a3 Binary files /dev/null and b/tests/ref/math-style-hebrew-exceptions.png differ diff --git a/tests/ref/math-style-italic-default.png b/tests/ref/math-style-italic-default.png new file mode 100644 index 0000000000..0a25f6facb Binary files /dev/null and b/tests/ref/math-style-italic-default.png differ diff --git a/tests/ref/math-style.png b/tests/ref/math-style.png new file mode 100644 index 0000000000..f514bd18dd Binary files /dev/null and b/tests/ref/math-style.png differ diff --git a/tests/ref/math-symbol-show-rule.png b/tests/ref/math-symbol-show-rule.png new file mode 100644 index 0000000000..68faf937a3 Binary files /dev/null and b/tests/ref/math-symbol-show-rule.png differ diff --git a/tests/ref/math-table.png b/tests/ref/math-table.png new file mode 100644 index 0000000000..5eb9321815 Binary files /dev/null and b/tests/ref/math-table.png differ diff --git a/tests/ref/math-text-color.png b/tests/ref/math-text-color.png new file mode 100644 index 0000000000..33ff00f379 Binary files /dev/null and b/tests/ref/math-text-color.png differ diff --git a/tests/ref/math-underover-brace.png b/tests/ref/math-underover-brace.png new file mode 100644 index 0000000000..d4a3f4a1c0 Binary files /dev/null and b/tests/ref/math-underover-brace.png differ diff --git a/tests/ref/math-underover-brackets.png b/tests/ref/math-underover-brackets.png new file mode 100644 index 0000000000..03419bc3ba Binary files /dev/null and b/tests/ref/math-underover-brackets.png differ diff --git a/tests/ref/math-underover-line-bracket.png b/tests/ref/math-underover-line-bracket.png new file mode 100644 index 0000000000..08d8df2027 Binary files /dev/null and b/tests/ref/math-underover-line-bracket.png differ diff --git a/tests/ref/math-unicode.png b/tests/ref/math-unicode.png new file mode 100644 index 0000000000..e74429ebf6 Binary files /dev/null and b/tests/ref/math-unicode.png differ diff --git a/tests/ref/math-vec-align-explicit-alternating.png b/tests/ref/math-vec-align-explicit-alternating.png new file mode 100644 index 0000000000..cb29eb067e Binary files /dev/null and b/tests/ref/math-vec-align-explicit-alternating.png differ diff --git a/tests/ref/math-vec-delim-set.png b/tests/ref/math-vec-delim-set.png new file mode 100644 index 0000000000..8024d594c4 Binary files /dev/null and b/tests/ref/math-vec-delim-set.png differ diff --git a/tests/ref/math-vec-gap.png b/tests/ref/math-vec-gap.png new file mode 100644 index 0000000000..06f8cf7de2 Binary files /dev/null and b/tests/ref/math-vec-gap.png differ diff --git a/tests/ref/math-vec-wide.png b/tests/ref/math-vec-wide.png new file mode 100644 index 0000000000..30853a0019 Binary files /dev/null and b/tests/ref/math-vec-wide.png differ diff --git a/tests/ref/math/accent.png b/tests/ref/math/accent.png deleted file mode 100644 index 52a7037e3b..0000000000 Binary files a/tests/ref/math/accent.png and /dev/null differ diff --git a/tests/ref/math/alignment.png b/tests/ref/math/alignment.png deleted file mode 100644 index 4bf739a4f6..0000000000 Binary files a/tests/ref/math/alignment.png and /dev/null differ diff --git a/tests/ref/math/attach-p1.png b/tests/ref/math/attach-p1.png deleted file mode 100644 index 45c465ce57..0000000000 Binary files a/tests/ref/math/attach-p1.png and /dev/null differ diff --git a/tests/ref/math/attach-p2.png b/tests/ref/math/attach-p2.png deleted file mode 100644 index 3820f33e91..0000000000 Binary files a/tests/ref/math/attach-p2.png and /dev/null differ diff --git a/tests/ref/math/attach-p3.png b/tests/ref/math/attach-p3.png deleted file mode 100644 index 69e0a7dd71..0000000000 Binary files a/tests/ref/math/attach-p3.png and /dev/null differ diff --git a/tests/ref/math/call.png b/tests/ref/math/call.png deleted file mode 100644 index 907a1a2be4..0000000000 Binary files a/tests/ref/math/call.png and /dev/null differ diff --git a/tests/ref/math/cancel.png b/tests/ref/math/cancel.png deleted file mode 100644 index 4f0de13603..0000000000 Binary files a/tests/ref/math/cancel.png and /dev/null differ diff --git a/tests/ref/math/cases.png b/tests/ref/math/cases.png deleted file mode 100644 index e222ae17ae..0000000000 Binary files a/tests/ref/math/cases.png and /dev/null differ diff --git a/tests/ref/math/class.png b/tests/ref/math/class.png deleted file mode 100644 index a4d6e86c1a..0000000000 Binary files a/tests/ref/math/class.png and /dev/null differ diff --git a/tests/ref/math/content.png b/tests/ref/math/content.png deleted file mode 100644 index c27a17ea67..0000000000 Binary files a/tests/ref/math/content.png and /dev/null differ diff --git a/tests/ref/math/delimited.png b/tests/ref/math/delimited.png deleted file mode 100644 index 6126f58ad9..0000000000 Binary files a/tests/ref/math/delimited.png and /dev/null differ diff --git a/tests/ref/math/equation-block-align.png b/tests/ref/math/equation-block-align.png deleted file mode 100644 index 8736312a7c..0000000000 Binary files a/tests/ref/math/equation-block-align.png and /dev/null differ diff --git a/tests/ref/math/equation-number.png b/tests/ref/math/equation-number.png deleted file mode 100644 index 8ba915902f..0000000000 Binary files a/tests/ref/math/equation-number.png and /dev/null differ diff --git a/tests/ref/math/equation-show.png b/tests/ref/math/equation-show.png deleted file mode 100644 index 79a70dc026..0000000000 Binary files a/tests/ref/math/equation-show.png and /dev/null differ diff --git a/tests/ref/math/font-features.png b/tests/ref/math/font-features.png deleted file mode 100644 index 0ab2c06d01..0000000000 Binary files a/tests/ref/math/font-features.png and /dev/null differ diff --git a/tests/ref/math/frac.png b/tests/ref/math/frac.png deleted file mode 100644 index 3e08f7e5bf..0000000000 Binary files a/tests/ref/math/frac.png and /dev/null differ diff --git a/tests/ref/math/linebreak.png b/tests/ref/math/linebreak.png deleted file mode 100644 index f3212a4a10..0000000000 Binary files a/tests/ref/math/linebreak.png and /dev/null differ diff --git a/tests/ref/math/matrix-alignment.png b/tests/ref/math/matrix-alignment.png deleted file mode 100644 index cdf174639e..0000000000 Binary files a/tests/ref/math/matrix-alignment.png and /dev/null differ diff --git a/tests/ref/math/matrix-gaps.png b/tests/ref/math/matrix-gaps.png deleted file mode 100644 index 16788969f2..0000000000 Binary files a/tests/ref/math/matrix-gaps.png and /dev/null differ diff --git a/tests/ref/math/matrix.png b/tests/ref/math/matrix.png deleted file mode 100644 index b8ea19e206..0000000000 Binary files a/tests/ref/math/matrix.png and /dev/null differ diff --git a/tests/ref/math/multiline.png b/tests/ref/math/multiline.png deleted file mode 100644 index 185724af9a..0000000000 Binary files a/tests/ref/math/multiline.png and /dev/null differ diff --git a/tests/ref/math/numbering.png b/tests/ref/math/numbering.png deleted file mode 100644 index 813f5f8c35..0000000000 Binary files a/tests/ref/math/numbering.png and /dev/null differ diff --git a/tests/ref/math/op.png b/tests/ref/math/op.png deleted file mode 100644 index ab3f35f6e5..0000000000 Binary files a/tests/ref/math/op.png and /dev/null differ diff --git a/tests/ref/math/opticalsize.png b/tests/ref/math/opticalsize.png deleted file mode 100644 index 303f7bee7d..0000000000 Binary files a/tests/ref/math/opticalsize.png and /dev/null differ diff --git a/tests/ref/math/prime.png b/tests/ref/math/prime.png deleted file mode 100644 index 81a4764367..0000000000 Binary files a/tests/ref/math/prime.png and /dev/null differ diff --git a/tests/ref/math/root.png b/tests/ref/math/root.png deleted file mode 100644 index 51fdf2e8f2..0000000000 Binary files a/tests/ref/math/root.png and /dev/null differ diff --git a/tests/ref/math/spacing.png b/tests/ref/math/spacing.png deleted file mode 100644 index d8198bbfb2..0000000000 Binary files a/tests/ref/math/spacing.png and /dev/null differ diff --git a/tests/ref/math/style.png b/tests/ref/math/style.png deleted file mode 100644 index 5201181eee..0000000000 Binary files a/tests/ref/math/style.png and /dev/null differ diff --git a/tests/ref/math/syntax.png b/tests/ref/math/syntax.png deleted file mode 100644 index 3855fa9b82..0000000000 Binary files a/tests/ref/math/syntax.png and /dev/null differ diff --git a/tests/ref/math/unbalanced.png b/tests/ref/math/unbalanced.png deleted file mode 100644 index 84f51837ce..0000000000 Binary files a/tests/ref/math/unbalanced.png and /dev/null differ diff --git a/tests/ref/math/underover.png b/tests/ref/math/underover.png deleted file mode 100644 index e974302f42..0000000000 Binary files a/tests/ref/math/underover.png and /dev/null differ diff --git a/tests/ref/math/vec.png b/tests/ref/math/vec.png deleted file mode 100644 index f2371e5b3f..0000000000 Binary files a/tests/ref/math/vec.png and /dev/null differ diff --git a/tests/ref/meta/bibliography-full.png b/tests/ref/meta/bibliography-full.png deleted file mode 100644 index d8778c09e1..0000000000 Binary files a/tests/ref/meta/bibliography-full.png and /dev/null differ diff --git a/tests/ref/meta/bibliography-ordering.png b/tests/ref/meta/bibliography-ordering.png deleted file mode 100644 index 116c561d08..0000000000 Binary files a/tests/ref/meta/bibliography-ordering.png and /dev/null differ diff --git a/tests/ref/meta/bibliography.png b/tests/ref/meta/bibliography.png deleted file mode 100644 index 8fbd09bc81..0000000000 Binary files a/tests/ref/meta/bibliography.png and /dev/null differ diff --git a/tests/ref/meta/cite-footnote.png b/tests/ref/meta/cite-footnote.png deleted file mode 100644 index 3a7a0094da..0000000000 Binary files a/tests/ref/meta/cite-footnote.png and /dev/null differ diff --git a/tests/ref/meta/cite-form.png b/tests/ref/meta/cite-form.png deleted file mode 100644 index 8adeac92f0..0000000000 Binary files a/tests/ref/meta/cite-form.png and /dev/null differ diff --git a/tests/ref/meta/cite-group.png b/tests/ref/meta/cite-group.png deleted file mode 100644 index 8d02a9036d..0000000000 Binary files a/tests/ref/meta/cite-group.png and /dev/null differ diff --git a/tests/ref/meta/counter-page.png b/tests/ref/meta/counter-page.png deleted file mode 100644 index 869718bcaa..0000000000 Binary files a/tests/ref/meta/counter-page.png and /dev/null differ diff --git a/tests/ref/meta/counter.png b/tests/ref/meta/counter.png deleted file mode 100644 index 6c29ac17f8..0000000000 Binary files a/tests/ref/meta/counter.png and /dev/null differ diff --git a/tests/ref/meta/document.png b/tests/ref/meta/document.png deleted file mode 100644 index 6db265112f..0000000000 Binary files a/tests/ref/meta/document.png and /dev/null differ diff --git a/tests/ref/meta/figure-caption.png b/tests/ref/meta/figure-caption.png deleted file mode 100644 index 8a1d4a599f..0000000000 Binary files a/tests/ref/meta/figure-caption.png and /dev/null differ diff --git a/tests/ref/meta/figure-localization.png b/tests/ref/meta/figure-localization.png deleted file mode 100644 index 5fcbd2b7c1..0000000000 Binary files a/tests/ref/meta/figure-localization.png and /dev/null differ diff --git a/tests/ref/meta/figure.png b/tests/ref/meta/figure.png deleted file mode 100644 index bcdd0d2fa7..0000000000 Binary files a/tests/ref/meta/figure.png and /dev/null differ diff --git a/tests/ref/meta/footnote-break.png b/tests/ref/meta/footnote-break.png deleted file mode 100644 index 625305c88f..0000000000 Binary files a/tests/ref/meta/footnote-break.png and /dev/null differ diff --git a/tests/ref/meta/footnote-columns.png b/tests/ref/meta/footnote-columns.png deleted file mode 100644 index 528ec66418..0000000000 Binary files a/tests/ref/meta/footnote-columns.png and /dev/null differ diff --git a/tests/ref/meta/footnote-container.png b/tests/ref/meta/footnote-container.png deleted file mode 100644 index 9327e7eecc..0000000000 Binary files a/tests/ref/meta/footnote-container.png and /dev/null differ diff --git a/tests/ref/meta/footnote-invariant.png b/tests/ref/meta/footnote-invariant.png deleted file mode 100644 index 66b4118255..0000000000 Binary files a/tests/ref/meta/footnote-invariant.png and /dev/null differ diff --git a/tests/ref/meta/footnote-refs.png b/tests/ref/meta/footnote-refs.png deleted file mode 100644 index 3fab7bd5d6..0000000000 Binary files a/tests/ref/meta/footnote-refs.png and /dev/null differ diff --git a/tests/ref/meta/footnote-table.png b/tests/ref/meta/footnote-table.png deleted file mode 100644 index 023f8008be..0000000000 Binary files a/tests/ref/meta/footnote-table.png and /dev/null differ diff --git a/tests/ref/meta/footnote.png b/tests/ref/meta/footnote.png deleted file mode 100644 index 4c67bbd78b..0000000000 Binary files a/tests/ref/meta/footnote.png and /dev/null differ diff --git a/tests/ref/meta/heading.png b/tests/ref/meta/heading.png deleted file mode 100644 index 8467ea5387..0000000000 Binary files a/tests/ref/meta/heading.png and /dev/null differ diff --git a/tests/ref/meta/link.png b/tests/ref/meta/link.png deleted file mode 100644 index 3c3ecd2c09..0000000000 Binary files a/tests/ref/meta/link.png and /dev/null differ diff --git a/tests/ref/meta/numbering.png b/tests/ref/meta/numbering.png deleted file mode 100644 index fa5b520fa0..0000000000 Binary files a/tests/ref/meta/numbering.png and /dev/null differ diff --git a/tests/ref/meta/outline-entry.png b/tests/ref/meta/outline-entry.png deleted file mode 100644 index f8f5412f0a..0000000000 Binary files a/tests/ref/meta/outline-entry.png and /dev/null differ diff --git a/tests/ref/meta/outline-indent.png b/tests/ref/meta/outline-indent.png deleted file mode 100644 index 816d86a5e7..0000000000 Binary files a/tests/ref/meta/outline-indent.png and /dev/null differ diff --git a/tests/ref/meta/outline.png b/tests/ref/meta/outline.png deleted file mode 100644 index 047bcc801a..0000000000 Binary files a/tests/ref/meta/outline.png and /dev/null differ diff --git a/tests/ref/meta/page-label.png b/tests/ref/meta/page-label.png deleted file mode 100644 index 301d626ab3..0000000000 Binary files a/tests/ref/meta/page-label.png and /dev/null differ diff --git a/tests/ref/meta/query-before-after.png b/tests/ref/meta/query-before-after.png deleted file mode 100644 index 80f8fe1f73..0000000000 Binary files a/tests/ref/meta/query-before-after.png and /dev/null differ diff --git a/tests/ref/meta/query-figure.png b/tests/ref/meta/query-figure.png deleted file mode 100644 index 2537ebf00d..0000000000 Binary files a/tests/ref/meta/query-figure.png and /dev/null differ diff --git a/tests/ref/meta/query-header.png b/tests/ref/meta/query-header.png deleted file mode 100644 index c2dc468929..0000000000 Binary files a/tests/ref/meta/query-header.png and /dev/null differ diff --git a/tests/ref/meta/ref.png b/tests/ref/meta/ref.png deleted file mode 100644 index 51563f54b6..0000000000 Binary files a/tests/ref/meta/ref.png and /dev/null differ diff --git a/tests/ref/meta/state.png b/tests/ref/meta/state.png deleted file mode 100644 index 25faa0d991..0000000000 Binary files a/tests/ref/meta/state.png and /dev/null differ diff --git a/tests/ref/newline-continuation-code.png b/tests/ref/newline-continuation-code.png new file mode 100644 index 0000000000..46a6afd56f Binary files /dev/null and b/tests/ref/newline-continuation-code.png differ diff --git a/tests/ref/newline-continuation-markup.png b/tests/ref/newline-continuation-markup.png new file mode 100644 index 0000000000..268e5f8408 Binary files /dev/null and b/tests/ref/newline-continuation-markup.png differ diff --git a/tests/ref/numbering-chinese.png b/tests/ref/numbering-chinese.png new file mode 100644 index 0000000000..06b3133423 Binary files /dev/null and b/tests/ref/numbering-chinese.png differ diff --git a/tests/ref/numbering-hebrew.png b/tests/ref/numbering-hebrew.png new file mode 100644 index 0000000000..d761422591 Binary files /dev/null and b/tests/ref/numbering-hebrew.png differ diff --git a/tests/ref/numbering-japanese-aiueo.png b/tests/ref/numbering-japanese-aiueo.png new file mode 100644 index 0000000000..b06d5c69fc Binary files /dev/null and b/tests/ref/numbering-japanese-aiueo.png differ diff --git a/tests/ref/numbering-japanese-iroha.png b/tests/ref/numbering-japanese-iroha.png new file mode 100644 index 0000000000..2018802f2d Binary files /dev/null and b/tests/ref/numbering-japanese-iroha.png differ diff --git a/tests/ref/numbering-korean.png b/tests/ref/numbering-korean.png new file mode 100644 index 0000000000..281f2ec28f Binary files /dev/null and b/tests/ref/numbering-korean.png differ diff --git a/tests/ref/numbering-latin.png b/tests/ref/numbering-latin.png new file mode 100644 index 0000000000..e154735a19 Binary files /dev/null and b/tests/ref/numbering-latin.png differ diff --git a/tests/ref/numbering-symbol-and-roman.png b/tests/ref/numbering-symbol-and-roman.png new file mode 100644 index 0000000000..979f3b90b2 Binary files /dev/null and b/tests/ref/numbering-symbol-and-roman.png differ diff --git a/tests/ref/numbers.png b/tests/ref/numbers.png new file mode 100644 index 0000000000..e6e7215b58 Binary files /dev/null and b/tests/ref/numbers.png differ diff --git a/tests/ref/ops-add-content.png b/tests/ref/ops-add-content.png new file mode 100644 index 0000000000..bdb8cb5ea5 Binary files /dev/null and b/tests/ref/ops-add-content.png differ diff --git a/tests/ref/ops-multiply-inf-with-length.png b/tests/ref/ops-multiply-inf-with-length.png new file mode 100644 index 0000000000..749be0564f Binary files /dev/null and b/tests/ref/ops-multiply-inf-with-length.png differ diff --git a/tests/ref/outline-entry-complex.png b/tests/ref/outline-entry-complex.png new file mode 100644 index 0000000000..c885cacc07 Binary files /dev/null and b/tests/ref/outline-entry-complex.png differ diff --git a/tests/ref/outline-entry.png b/tests/ref/outline-entry.png new file mode 100644 index 0000000000..94e7a5a740 Binary files /dev/null and b/tests/ref/outline-entry.png differ diff --git a/tests/ref/outline-first-line-indent.png b/tests/ref/outline-first-line-indent.png new file mode 100644 index 0000000000..dd995f3194 Binary files /dev/null and b/tests/ref/outline-first-line-indent.png differ diff --git a/tests/ref/outline-indent-no-numbering.png b/tests/ref/outline-indent-no-numbering.png new file mode 100644 index 0000000000..62bd80a3c5 Binary files /dev/null and b/tests/ref/outline-indent-no-numbering.png differ diff --git a/tests/ref/outline-indent-numbering.png b/tests/ref/outline-indent-numbering.png new file mode 100644 index 0000000000..6c9368271a Binary files /dev/null and b/tests/ref/outline-indent-numbering.png differ diff --git a/tests/ref/outline.png b/tests/ref/outline.png new file mode 100644 index 0000000000..e5c24a9806 Binary files /dev/null and b/tests/ref/outline.png differ diff --git a/tests/ref/overhang-lone.png b/tests/ref/overhang-lone.png new file mode 100644 index 0000000000..b48618fb29 Binary files /dev/null and b/tests/ref/overhang-lone.png differ diff --git a/tests/ref/overhang.png b/tests/ref/overhang.png new file mode 100644 index 0000000000..b97ef30ce8 Binary files /dev/null and b/tests/ref/overhang.png differ diff --git a/tests/ref/overline-background.png b/tests/ref/overline-background.png new file mode 100644 index 0000000000..8efd147ea6 Binary files /dev/null and b/tests/ref/overline-background.png differ diff --git a/tests/ref/pad-basic.png b/tests/ref/pad-basic.png new file mode 100644 index 0000000000..f8c40088f8 Binary files /dev/null and b/tests/ref/pad-basic.png differ diff --git a/tests/ref/pad-expanding-contents.png b/tests/ref/pad-expanding-contents.png new file mode 100644 index 0000000000..1bef4a81be Binary files /dev/null and b/tests/ref/pad-expanding-contents.png differ diff --git a/tests/ref/pad-followed-by-content.png b/tests/ref/pad-followed-by-content.png new file mode 100644 index 0000000000..f0f06a6ce5 Binary files /dev/null and b/tests/ref/pad-followed-by-content.png differ diff --git a/tests/ref/page-call-followed-by-pagebreak.png b/tests/ref/page-call-followed-by-pagebreak.png new file mode 100644 index 0000000000..87cd9735ea Binary files /dev/null and b/tests/ref/page-call-followed-by-pagebreak.png differ diff --git a/tests/ref/page-call-styled-empty.png b/tests/ref/page-call-styled-empty.png new file mode 100644 index 0000000000..6a24b1bc10 Binary files /dev/null and b/tests/ref/page-call-styled-empty.png differ diff --git a/tests/ref/page-fill.png b/tests/ref/page-fill.png new file mode 100644 index 0000000000..0c7ab27788 Binary files /dev/null and b/tests/ref/page-fill.png differ diff --git a/tests/ref/page-large.png b/tests/ref/page-large.png new file mode 100644 index 0000000000..a57dceec70 Binary files /dev/null and b/tests/ref/page-large.png differ diff --git a/tests/ref/page-margin-binding-from-text-lang.png b/tests/ref/page-margin-binding-from-text-lang.png new file mode 100644 index 0000000000..8d12ff2f4c Binary files /dev/null and b/tests/ref/page-margin-binding-from-text-lang.png differ diff --git a/tests/ref/page-margin-individual.png b/tests/ref/page-margin-individual.png new file mode 100644 index 0000000000..0bc0f51bc2 Binary files /dev/null and b/tests/ref/page-margin-individual.png differ diff --git a/tests/ref/page-margin-inside-outside-override.png b/tests/ref/page-margin-inside-outside-override.png new file mode 100644 index 0000000000..5aa8bf12ae Binary files /dev/null and b/tests/ref/page-margin-inside-outside-override.png differ diff --git a/tests/ref/page-margin-inside-with-binding.png b/tests/ref/page-margin-inside-with-binding.png new file mode 100644 index 0000000000..5b9ec04fde Binary files /dev/null and b/tests/ref/page-margin-inside-with-binding.png differ diff --git a/tests/ref/page-margin-inside.png b/tests/ref/page-margin-inside.png new file mode 100644 index 0000000000..d70b860470 Binary files /dev/null and b/tests/ref/page-margin-inside.png differ diff --git a/tests/ref/page-margin-uniform.png b/tests/ref/page-margin-uniform.png new file mode 100644 index 0000000000..8a06fb749d Binary files /dev/null and b/tests/ref/page-margin-uniform.png differ diff --git a/tests/ref/page-marginals.png b/tests/ref/page-marginals.png new file mode 100644 index 0000000000..cab886b385 Binary files /dev/null and b/tests/ref/page-marginals.png differ diff --git a/tests/ref/page-number-align-bottom-left.png b/tests/ref/page-number-align-bottom-left.png new file mode 100644 index 0000000000..396f6e98fe Binary files /dev/null and b/tests/ref/page-number-align-bottom-left.png differ diff --git a/tests/ref/page-number-align-top-right.png b/tests/ref/page-number-align-top-right.png new file mode 100644 index 0000000000..3c7e5579a7 Binary files /dev/null and b/tests/ref/page-number-align-top-right.png differ diff --git a/tests/ref/page-numbering-pdf-label.png b/tests/ref/page-numbering-pdf-label.png new file mode 100644 index 0000000000..a1cae72076 Binary files /dev/null and b/tests/ref/page-numbering-pdf-label.png differ diff --git a/tests/ref/page-set-empty.png b/tests/ref/page-set-empty.png new file mode 100644 index 0000000000..6a24b1bc10 Binary files /dev/null and b/tests/ref/page-set-empty.png differ diff --git a/tests/ref/page-set-forces-break.png b/tests/ref/page-set-forces-break.png new file mode 100644 index 0000000000..4654ef6c07 Binary files /dev/null and b/tests/ref/page-set-forces-break.png differ diff --git a/tests/ref/page-set-only-pagebreak.png b/tests/ref/page-set-only-pagebreak.png new file mode 100644 index 0000000000..9bf379d6e9 Binary files /dev/null and b/tests/ref/page-set-only-pagebreak.png differ diff --git a/tests/ref/page-set-override-and-mix.png b/tests/ref/page-set-override-and-mix.png new file mode 100644 index 0000000000..d9df6acd09 Binary files /dev/null and b/tests/ref/page-set-override-and-mix.png differ diff --git a/tests/ref/page-set-override-thrice.png b/tests/ref/page-set-override-thrice.png new file mode 100644 index 0000000000..99173ced31 Binary files /dev/null and b/tests/ref/page-set-override-thrice.png differ diff --git a/tests/ref/pagebreak-around-set-page.png b/tests/ref/pagebreak-around-set-page.png new file mode 100644 index 0000000000..2c1ce508ab Binary files /dev/null and b/tests/ref/pagebreak-around-set-page.png differ diff --git a/tests/ref/pagebreak-followed-by-page-call.png b/tests/ref/pagebreak-followed-by-page-call.png new file mode 100644 index 0000000000..ee435cdcab Binary files /dev/null and b/tests/ref/pagebreak-followed-by-page-call.png differ diff --git a/tests/ref/pagebreak-meta.png b/tests/ref/pagebreak-meta.png new file mode 100644 index 0000000000..7953dc51ad Binary files /dev/null and b/tests/ref/pagebreak-meta.png differ diff --git a/tests/ref/pagebreak-set-page-mixed.png b/tests/ref/pagebreak-set-page-mixed.png new file mode 100644 index 0000000000..3502ee42b1 Binary files /dev/null and b/tests/ref/pagebreak-set-page-mixed.png differ diff --git a/tests/ref/pagebreak-to-auto-sized.png b/tests/ref/pagebreak-to-auto-sized.png new file mode 100644 index 0000000000..f3e2df45e7 Binary files /dev/null and b/tests/ref/pagebreak-to-auto-sized.png differ diff --git a/tests/ref/pagebreak-to-multiple-pages.png b/tests/ref/pagebreak-to-multiple-pages.png new file mode 100644 index 0000000000..a7af0a9a87 Binary files /dev/null and b/tests/ref/pagebreak-to-multiple-pages.png differ diff --git a/tests/ref/pagebreak-to.png b/tests/ref/pagebreak-to.png new file mode 100644 index 0000000000..62a4ee20a9 Binary files /dev/null and b/tests/ref/pagebreak-to.png differ diff --git a/tests/ref/pagebreak-weak-after-set-page.png b/tests/ref/pagebreak-weak-after-set-page.png new file mode 100644 index 0000000000..c8014df171 Binary files /dev/null and b/tests/ref/pagebreak-weak-after-set-page.png differ diff --git a/tests/ref/pagebreak-weak-meta.png b/tests/ref/pagebreak-weak-meta.png new file mode 100644 index 0000000000..aa69e606f7 Binary files /dev/null and b/tests/ref/pagebreak-weak-meta.png differ diff --git a/tests/ref/pagebreak-weak-place.png b/tests/ref/pagebreak-weak-place.png new file mode 100644 index 0000000000..f85bdf02b7 Binary files /dev/null and b/tests/ref/pagebreak-weak-place.png differ diff --git a/tests/ref/pagebreak.png b/tests/ref/pagebreak.png new file mode 100644 index 0000000000..d07473d90a Binary files /dev/null and b/tests/ref/pagebreak.png differ diff --git a/tests/ref/par-basic.png b/tests/ref/par-basic.png new file mode 100644 index 0000000000..ffd9de9a36 Binary files /dev/null and b/tests/ref/par-basic.png differ diff --git a/tests/ref/par-first-line-indent.png b/tests/ref/par-first-line-indent.png new file mode 100644 index 0000000000..e6d7ed761b Binary files /dev/null and b/tests/ref/par-first-line-indent.png differ diff --git a/tests/ref/par-hanging-indent-manual-linebreak.png b/tests/ref/par-hanging-indent-manual-linebreak.png new file mode 100644 index 0000000000..e9c666cd76 Binary files /dev/null and b/tests/ref/par-hanging-indent-manual-linebreak.png differ diff --git a/tests/ref/par-hanging-indent-rtl.png b/tests/ref/par-hanging-indent-rtl.png new file mode 100644 index 0000000000..849e0a012f Binary files /dev/null and b/tests/ref/par-hanging-indent-rtl.png differ diff --git a/tests/ref/par-hanging-indent.png b/tests/ref/par-hanging-indent.png new file mode 100644 index 0000000000..49455a78db Binary files /dev/null and b/tests/ref/par-hanging-indent.png differ diff --git a/tests/ref/par-leading-and-block-spacing.png b/tests/ref/par-leading-and-block-spacing.png new file mode 100644 index 0000000000..faaa31164a Binary files /dev/null and b/tests/ref/par-leading-and-block-spacing.png differ diff --git a/tests/ref/par-spacing-and-first-line-indent.png b/tests/ref/par-spacing-and-first-line-indent.png new file mode 100644 index 0000000000..c322f63094 Binary files /dev/null and b/tests/ref/par-spacing-and-first-line-indent.png differ diff --git a/tests/ref/parser-backtracking-destructuring-whitespace.png b/tests/ref/parser-backtracking-destructuring-whitespace.png new file mode 100644 index 0000000000..d5d72888ed Binary files /dev/null and b/tests/ref/parser-backtracking-destructuring-whitespace.png differ diff --git a/tests/ref/path.png b/tests/ref/path.png new file mode 100644 index 0000000000..9643a476c0 Binary files /dev/null and b/tests/ref/path.png differ diff --git a/tests/ref/pattern-line.png b/tests/ref/pattern-line.png new file mode 100644 index 0000000000..b891b6d7cc Binary files /dev/null and b/tests/ref/pattern-line.png differ diff --git a/tests/ref/pattern-lines.png b/tests/ref/pattern-lines.png new file mode 100644 index 0000000000..008d357e7b Binary files /dev/null and b/tests/ref/pattern-lines.png differ diff --git a/tests/ref/pattern-relative-parent.png b/tests/ref/pattern-relative-parent.png new file mode 100644 index 0000000000..786057ef3b Binary files /dev/null and b/tests/ref/pattern-relative-parent.png differ diff --git a/tests/ref/pattern-relative-self.png b/tests/ref/pattern-relative-self.png new file mode 100644 index 0000000000..2840808117 Binary files /dev/null and b/tests/ref/pattern-relative-self.png differ diff --git a/tests/ref/pattern-small.png b/tests/ref/pattern-small.png new file mode 100644 index 0000000000..0406252d82 Binary files /dev/null and b/tests/ref/pattern-small.png differ diff --git a/tests/ref/pattern-spacing-negative.png b/tests/ref/pattern-spacing-negative.png new file mode 100644 index 0000000000..659c22837e Binary files /dev/null and b/tests/ref/pattern-spacing-negative.png differ diff --git a/tests/ref/pattern-spacing-positive.png b/tests/ref/pattern-spacing-positive.png new file mode 100644 index 0000000000..3e475eeea8 Binary files /dev/null and b/tests/ref/pattern-spacing-positive.png differ diff --git a/tests/ref/pattern-spacing-zero.png b/tests/ref/pattern-spacing-zero.png new file mode 100644 index 0000000000..5118471ab1 Binary files /dev/null and b/tests/ref/pattern-spacing-zero.png differ diff --git a/tests/ref/pattern-stroke.png b/tests/ref/pattern-stroke.png new file mode 100644 index 0000000000..8b03783b5d Binary files /dev/null and b/tests/ref/pattern-stroke.png differ diff --git a/tests/ref/pattern-text.png b/tests/ref/pattern-text.png new file mode 100644 index 0000000000..de9bfc2ec8 Binary files /dev/null and b/tests/ref/pattern-text.png differ diff --git a/tests/ref/place-background.png b/tests/ref/place-background.png new file mode 100644 index 0000000000..7d732717b2 Binary files /dev/null and b/tests/ref/place-background.png differ diff --git a/tests/ref/place-basic.png b/tests/ref/place-basic.png new file mode 100644 index 0000000000..07642c347d Binary files /dev/null and b/tests/ref/place-basic.png differ diff --git a/tests/ref/place-block-spacing.png b/tests/ref/place-block-spacing.png new file mode 100644 index 0000000000..fb01d1b69d Binary files /dev/null and b/tests/ref/place-block-spacing.png differ diff --git a/tests/ref/place-bottom-in-box.png b/tests/ref/place-bottom-in-box.png new file mode 100644 index 0000000000..fdd8c010ef Binary files /dev/null and b/tests/ref/place-bottom-in-box.png differ diff --git a/tests/ref/place-bottom-right-in-box.png b/tests/ref/place-bottom-right-in-box.png new file mode 100644 index 0000000000..49c4088625 Binary files /dev/null and b/tests/ref/place-bottom-right-in-box.png differ diff --git a/tests/ref/place-float-columns.png b/tests/ref/place-float-columns.png new file mode 100644 index 0000000000..97065b68fe Binary files /dev/null and b/tests/ref/place-float-columns.png differ diff --git a/tests/ref/place-float-figure.png b/tests/ref/place-float-figure.png new file mode 100644 index 0000000000..5411178a0a Binary files /dev/null and b/tests/ref/place-float-figure.png differ diff --git a/tests/ref/place-float.png b/tests/ref/place-float.png new file mode 100644 index 0000000000..ddd49c47f4 Binary files /dev/null and b/tests/ref/place-float.png differ diff --git a/tests/ref/place-horizon-in-boxes.png b/tests/ref/place-horizon-in-boxes.png new file mode 100644 index 0000000000..b6d333bfcd Binary files /dev/null and b/tests/ref/place-horizon-in-boxes.png differ diff --git a/tests/ref/place-top-left-in-box.png b/tests/ref/place-top-left-in-box.png new file mode 100644 index 0000000000..914ffa58d5 Binary files /dev/null and b/tests/ref/place-top-left-in-box.png differ diff --git a/tests/ref/polygon-line-join.png b/tests/ref/polygon-line-join.png new file mode 100644 index 0000000000..0f65fa7045 Binary files /dev/null and b/tests/ref/polygon-line-join.png differ diff --git a/tests/ref/polygon.png b/tests/ref/polygon.png new file mode 100644 index 0000000000..1dc110831b Binary files /dev/null and b/tests/ref/polygon.png differ diff --git a/tests/ref/query-and-or.png b/tests/ref/query-and-or.png new file mode 100644 index 0000000000..39cfd0765c Binary files /dev/null and b/tests/ref/query-and-or.png differ diff --git a/tests/ref/query-before-after.png b/tests/ref/query-before-after.png new file mode 100644 index 0000000000..33fde9859e Binary files /dev/null and b/tests/ref/query-before-after.png differ diff --git a/tests/ref/query-complex.png b/tests/ref/query-complex.png new file mode 100644 index 0000000000..f71dcce565 Binary files /dev/null and b/tests/ref/query-complex.png differ diff --git a/tests/ref/query-list-of-figures.png b/tests/ref/query-list-of-figures.png new file mode 100644 index 0000000000..c94ccd00e6 Binary files /dev/null and b/tests/ref/query-list-of-figures.png differ diff --git a/tests/ref/query-running-header.png b/tests/ref/query-running-header.png new file mode 100644 index 0000000000..210c781036 Binary files /dev/null and b/tests/ref/query-running-header.png differ diff --git a/tests/ref/quote-block-spacing.png b/tests/ref/quote-block-spacing.png new file mode 100644 index 0000000000..3efae5abeb Binary files /dev/null and b/tests/ref/quote-block-spacing.png differ diff --git a/tests/ref/quote-cite-format-author-date.png b/tests/ref/quote-cite-format-author-date.png new file mode 100644 index 0000000000..43816f8ccb Binary files /dev/null and b/tests/ref/quote-cite-format-author-date.png differ diff --git a/tests/ref/quote-cite-format-label-or-numeric.png b/tests/ref/quote-cite-format-label-or-numeric.png new file mode 100644 index 0000000000..f0f5f90f3a Binary files /dev/null and b/tests/ref/quote-cite-format-label-or-numeric.png differ diff --git a/tests/ref/quote-cite-format-note.png b/tests/ref/quote-cite-format-note.png new file mode 100644 index 0000000000..1092ffdbfa Binary files /dev/null and b/tests/ref/quote-cite-format-note.png differ diff --git a/tests/ref/quote-dir-align.png b/tests/ref/quote-dir-align.png new file mode 100644 index 0000000000..0341f87c99 Binary files /dev/null and b/tests/ref/quote-dir-align.png differ diff --git a/tests/ref/quote-dir-author-pos.png b/tests/ref/quote-dir-author-pos.png new file mode 100644 index 0000000000..842796a251 Binary files /dev/null and b/tests/ref/quote-dir-author-pos.png differ diff --git a/tests/ref/quote-inline.png b/tests/ref/quote-inline.png new file mode 100644 index 0000000000..4dbc9720e1 Binary files /dev/null and b/tests/ref/quote-inline.png differ diff --git a/tests/ref/quote-nesting-custom.png b/tests/ref/quote-nesting-custom.png new file mode 100644 index 0000000000..e26b6258ec Binary files /dev/null and b/tests/ref/quote-nesting-custom.png differ diff --git a/tests/ref/quote-nesting.png b/tests/ref/quote-nesting.png new file mode 100644 index 0000000000..dcd1e3780e Binary files /dev/null and b/tests/ref/quote-nesting.png differ diff --git a/tests/ref/raw-align-default.png b/tests/ref/raw-align-default.png new file mode 100644 index 0000000000..84c5122974 Binary files /dev/null and b/tests/ref/raw-align-default.png differ diff --git a/tests/ref/raw-align-specified.png b/tests/ref/raw-align-specified.png new file mode 100644 index 0000000000..18b48deca0 Binary files /dev/null and b/tests/ref/raw-align-specified.png differ diff --git a/tests/ref/raw-block-no-parbreaks.png b/tests/ref/raw-block-no-parbreaks.png new file mode 100644 index 0000000000..401cc5a90d Binary files /dev/null and b/tests/ref/raw-block-no-parbreaks.png differ diff --git a/tests/ref/raw-consecutive-single-backticks.png b/tests/ref/raw-consecutive-single-backticks.png new file mode 100644 index 0000000000..159d0edac2 Binary files /dev/null and b/tests/ref/raw-consecutive-single-backticks.png differ diff --git a/tests/ref/raw-dedent-empty-line.png b/tests/ref/raw-dedent-empty-line.png new file mode 100644 index 0000000000..c3c88e7ab8 Binary files /dev/null and b/tests/ref/raw-dedent-empty-line.png differ diff --git a/tests/ref/raw-dedent-first-line.png b/tests/ref/raw-dedent-first-line.png new file mode 100644 index 0000000000..c6eee5ce2d Binary files /dev/null and b/tests/ref/raw-dedent-first-line.png differ diff --git a/tests/ref/raw-dedent-last-line.png b/tests/ref/raw-dedent-last-line.png new file mode 100644 index 0000000000..2b1fe747f3 Binary files /dev/null and b/tests/ref/raw-dedent-last-line.png differ diff --git a/tests/ref/raw-empty.png b/tests/ref/raw-empty.png new file mode 100644 index 0000000000..a47eb85561 Binary files /dev/null and b/tests/ref/raw-empty.png differ diff --git a/tests/ref/raw-highlight-typ.png b/tests/ref/raw-highlight-typ.png new file mode 100644 index 0000000000..f80bbf898f Binary files /dev/null and b/tests/ref/raw-highlight-typ.png differ diff --git a/tests/ref/raw-highlight.png b/tests/ref/raw-highlight.png new file mode 100644 index 0000000000..2f99b4507b Binary files /dev/null and b/tests/ref/raw-highlight.png differ diff --git a/tests/ref/raw-inline-multiline.png b/tests/ref/raw-inline-multiline.png new file mode 100644 index 0000000000..7db3126d85 Binary files /dev/null and b/tests/ref/raw-inline-multiline.png differ diff --git a/tests/ref/raw-line-alternating-fill.png b/tests/ref/raw-line-alternating-fill.png new file mode 100644 index 0000000000..b805312920 Binary files /dev/null and b/tests/ref/raw-line-alternating-fill.png differ diff --git a/tests/ref/raw-line-text-fill.png b/tests/ref/raw-line-text-fill.png new file mode 100644 index 0000000000..5b3c4d199b Binary files /dev/null and b/tests/ref/raw-line-text-fill.png differ diff --git a/tests/ref/raw-line.png b/tests/ref/raw-line.png new file mode 100644 index 0000000000..c8ada95d30 Binary files /dev/null and b/tests/ref/raw-line.png differ diff --git a/tests/ref/raw-more-backticks.png b/tests/ref/raw-more-backticks.png new file mode 100644 index 0000000000..ab836011a6 Binary files /dev/null and b/tests/ref/raw-more-backticks.png differ diff --git a/tests/ref/raw-show-set.png b/tests/ref/raw-show-set.png new file mode 100644 index 0000000000..8a82c2e952 Binary files /dev/null and b/tests/ref/raw-show-set.png differ diff --git a/tests/ref/raw-single-backtick-lang.png b/tests/ref/raw-single-backtick-lang.png new file mode 100644 index 0000000000..b420627e32 Binary files /dev/null and b/tests/ref/raw-single-backtick-lang.png differ diff --git a/tests/ref/raw-syntaxes.png b/tests/ref/raw-syntaxes.png new file mode 100644 index 0000000000..4e14cd0661 Binary files /dev/null and b/tests/ref/raw-syntaxes.png differ diff --git a/tests/ref/raw-tab-size.png b/tests/ref/raw-tab-size.png new file mode 100644 index 0000000000..132a10b32a Binary files /dev/null and b/tests/ref/raw-tab-size.png differ diff --git a/tests/ref/raw-theme.png b/tests/ref/raw-theme.png new file mode 100644 index 0000000000..78561ac660 Binary files /dev/null and b/tests/ref/raw-theme.png differ diff --git a/tests/ref/raw-trimming.png b/tests/ref/raw-trimming.png new file mode 100644 index 0000000000..58d90b7fed Binary files /dev/null and b/tests/ref/raw-trimming.png differ diff --git a/tests/ref/raw-typst-lang.png b/tests/ref/raw-typst-lang.png new file mode 100644 index 0000000000..3dcafafbdd Binary files /dev/null and b/tests/ref/raw-typst-lang.png differ diff --git a/tests/ref/rect-customization.png b/tests/ref/rect-customization.png new file mode 100644 index 0000000000..93808920c6 Binary files /dev/null and b/tests/ref/rect-customization.png differ diff --git a/tests/ref/rect-fill-stroke.png b/tests/ref/rect-fill-stroke.png new file mode 100644 index 0000000000..28a47c122e Binary files /dev/null and b/tests/ref/rect-fill-stroke.png differ diff --git a/tests/ref/rect-stroke.png b/tests/ref/rect-stroke.png new file mode 100644 index 0000000000..7d59c049bb Binary files /dev/null and b/tests/ref/rect-stroke.png differ diff --git a/tests/ref/rect.png b/tests/ref/rect.png new file mode 100644 index 0000000000..04e435ed4c Binary files /dev/null and b/tests/ref/rect.png differ diff --git a/tests/ref/ref-basic.png b/tests/ref/ref-basic.png new file mode 100644 index 0000000000..94d9478940 Binary files /dev/null and b/tests/ref/ref-basic.png differ diff --git a/tests/ref/ref-supplements.png b/tests/ref/ref-supplements.png new file mode 100644 index 0000000000..46d1524a1c Binary files /dev/null and b/tests/ref/ref-supplements.png differ diff --git a/tests/ref/repeat-align-and-dir.png b/tests/ref/repeat-align-and-dir.png new file mode 100644 index 0000000000..16797d04bb Binary files /dev/null and b/tests/ref/repeat-align-and-dir.png differ diff --git a/tests/ref/repeat-basic.png b/tests/ref/repeat-basic.png new file mode 100644 index 0000000000..61e7f50f20 Binary files /dev/null and b/tests/ref/repeat-basic.png differ diff --git a/tests/ref/repeat-dots-rtl.png b/tests/ref/repeat-dots-rtl.png new file mode 100644 index 0000000000..a0f1a9192b Binary files /dev/null and b/tests/ref/repeat-dots-rtl.png differ diff --git a/tests/ref/repeat-empty.png b/tests/ref/repeat-empty.png new file mode 100644 index 0000000000..c23d7fa4d3 Binary files /dev/null and b/tests/ref/repeat-empty.png differ diff --git a/tests/ref/repeat-unboxed.png b/tests/ref/repeat-unboxed.png new file mode 100644 index 0000000000..91678ceaaf Binary files /dev/null and b/tests/ref/repeat-unboxed.png differ diff --git a/tests/ref/repr-color.png b/tests/ref/repr-color.png new file mode 100644 index 0000000000..3425f7d4de Binary files /dev/null and b/tests/ref/repr-color.png differ diff --git a/tests/ref/repr-literals.png b/tests/ref/repr-literals.png new file mode 100644 index 0000000000..1e8e85a410 Binary files /dev/null and b/tests/ref/repr-literals.png differ diff --git a/tests/ref/repr-misc.png b/tests/ref/repr-misc.png new file mode 100644 index 0000000000..f4ac7c5fd8 Binary files /dev/null and b/tests/ref/repr-misc.png differ diff --git a/tests/ref/repr-numerical.png b/tests/ref/repr-numerical.png new file mode 100644 index 0000000000..f1a6fe8e52 Binary files /dev/null and b/tests/ref/repr-numerical.png differ diff --git a/tests/ref/return-in-nested-content-block.png b/tests/ref/return-in-nested-content-block.png new file mode 100644 index 0000000000..d688741c23 Binary files /dev/null and b/tests/ref/return-in-nested-content-block.png differ diff --git a/tests/ref/set-if.png b/tests/ref/set-if.png new file mode 100644 index 0000000000..08dc5e8263 Binary files /dev/null and b/tests/ref/set-if.png differ diff --git a/tests/ref/set-instantiation-site-markup.png b/tests/ref/set-instantiation-site-markup.png new file mode 100644 index 0000000000..180444b99a Binary files /dev/null and b/tests/ref/set-instantiation-site-markup.png differ diff --git a/tests/ref/set-instantiation-site.png b/tests/ref/set-instantiation-site.png new file mode 100644 index 0000000000..593d3e2df3 Binary files /dev/null and b/tests/ref/set-instantiation-site.png differ diff --git a/tests/ref/set-scoped-in-code-block.png b/tests/ref/set-scoped-in-code-block.png new file mode 100644 index 0000000000..8941f6c4a4 Binary files /dev/null and b/tests/ref/set-scoped-in-code-block.png differ diff --git a/tests/ref/set-text-override.png b/tests/ref/set-text-override.png new file mode 100644 index 0000000000..8362387609 Binary files /dev/null and b/tests/ref/set-text-override.png differ diff --git a/tests/ref/set-vs-construct-1.png b/tests/ref/set-vs-construct-1.png new file mode 100644 index 0000000000..597e967495 Binary files /dev/null and b/tests/ref/set-vs-construct-1.png differ diff --git a/tests/ref/set-vs-construct-2.png b/tests/ref/set-vs-construct-2.png new file mode 100644 index 0000000000..2fedd0b427 Binary files /dev/null and b/tests/ref/set-vs-construct-2.png differ diff --git a/tests/ref/set-vs-construct-3.png b/tests/ref/set-vs-construct-3.png new file mode 100644 index 0000000000..dff0c8af6b Binary files /dev/null and b/tests/ref/set-vs-construct-3.png differ diff --git a/tests/ref/set-vs-construct-4.png b/tests/ref/set-vs-construct-4.png new file mode 100644 index 0000000000..1f6834ef5d Binary files /dev/null and b/tests/ref/set-vs-construct-4.png differ diff --git a/tests/ref/shaping-emoji-bad-zwj.png b/tests/ref/shaping-emoji-bad-zwj.png new file mode 100644 index 0000000000..544d64eea8 Binary files /dev/null and b/tests/ref/shaping-emoji-bad-zwj.png differ diff --git a/tests/ref/shaping-emoji-basic.png b/tests/ref/shaping-emoji-basic.png new file mode 100644 index 0000000000..090ea61118 Binary files /dev/null and b/tests/ref/shaping-emoji-basic.png differ diff --git a/tests/ref/shaping-font-fallback.png b/tests/ref/shaping-font-fallback.png new file mode 100644 index 0000000000..813e39151d Binary files /dev/null and b/tests/ref/shaping-font-fallback.png differ diff --git a/tests/ref/shaping-forced-script-font-feature-enabled.png b/tests/ref/shaping-forced-script-font-feature-enabled.png new file mode 100644 index 0000000000..0a10087a3c Binary files /dev/null and b/tests/ref/shaping-forced-script-font-feature-enabled.png differ diff --git a/tests/ref/shaping-forced-script-font-feature-inhibited.png b/tests/ref/shaping-forced-script-font-feature-inhibited.png new file mode 100644 index 0000000000..77d8010ea8 Binary files /dev/null and b/tests/ref/shaping-forced-script-font-feature-inhibited.png differ diff --git a/tests/ref/shaping-script-separation.png b/tests/ref/shaping-script-separation.png new file mode 100644 index 0000000000..68170dd998 Binary files /dev/null and b/tests/ref/shaping-script-separation.png differ diff --git a/tests/ref/shorthand-dashes.png b/tests/ref/shorthand-dashes.png new file mode 100644 index 0000000000..f8b4191fef Binary files /dev/null and b/tests/ref/shorthand-dashes.png differ diff --git a/tests/ref/shorthand-ellipsis.png b/tests/ref/shorthand-ellipsis.png new file mode 100644 index 0000000000..df9a9241ed Binary files /dev/null and b/tests/ref/shorthand-ellipsis.png differ diff --git a/tests/ref/shorthand-nbsp-and-shy-hyphen.png b/tests/ref/shorthand-nbsp-and-shy-hyphen.png new file mode 100644 index 0000000000..e8c81aaa6e Binary files /dev/null and b/tests/ref/shorthand-nbsp-and-shy-hyphen.png differ diff --git a/tests/ref/shorthand-nbsp-width.png b/tests/ref/shorthand-nbsp-width.png new file mode 100644 index 0000000000..a92988cf13 Binary files /dev/null and b/tests/ref/shorthand-nbsp-width.png differ diff --git a/tests/ref/shorthands-math.png b/tests/ref/shorthands-math.png new file mode 100644 index 0000000000..0514fa6227 Binary files /dev/null and b/tests/ref/shorthands-math.png differ diff --git a/tests/ref/show-bare-basic.png b/tests/ref/show-bare-basic.png new file mode 100644 index 0000000000..e389b50632 Binary files /dev/null and b/tests/ref/show-bare-basic.png differ diff --git a/tests/ref/show-bare-content-block.png b/tests/ref/show-bare-content-block.png new file mode 100644 index 0000000000..2631092bb6 Binary files /dev/null and b/tests/ref/show-bare-content-block.png differ diff --git a/tests/ref/show-bare-replace-with-content.png b/tests/ref/show-bare-replace-with-content.png new file mode 100644 index 0000000000..51e36a4921 Binary files /dev/null and b/tests/ref/show-bare-replace-with-content.png differ diff --git a/tests/ref/show-bare-vs-set-text.png b/tests/ref/show-bare-vs-set-text.png new file mode 100644 index 0000000000..b1e15d98ff Binary files /dev/null and b/tests/ref/show-bare-vs-set-text.png differ diff --git a/tests/ref/show-function-order-with-set.png b/tests/ref/show-function-order-with-set.png new file mode 100644 index 0000000000..a59f727416 Binary files /dev/null and b/tests/ref/show-function-order-with-set.png differ diff --git a/tests/ref/show-function-set-on-it.png b/tests/ref/show-function-set-on-it.png new file mode 100644 index 0000000000..6c545e9544 Binary files /dev/null and b/tests/ref/show-function-set-on-it.png differ diff --git a/tests/ref/show-in-show.png b/tests/ref/show-in-show.png new file mode 100644 index 0000000000..65280ad7b6 Binary files /dev/null and b/tests/ref/show-in-show.png differ diff --git a/tests/ref/show-multiple-rules.png b/tests/ref/show-multiple-rules.png new file mode 100644 index 0000000000..c92b6269ef Binary files /dev/null and b/tests/ref/show-multiple-rules.png differ diff --git a/tests/ref/show-nested-scopes.png b/tests/ref/show-nested-scopes.png new file mode 100644 index 0000000000..ac0a812560 Binary files /dev/null and b/tests/ref/show-nested-scopes.png differ diff --git a/tests/ref/show-recursive-identity.png b/tests/ref/show-recursive-identity.png new file mode 100644 index 0000000000..6c545e9544 Binary files /dev/null and b/tests/ref/show-recursive-identity.png differ diff --git a/tests/ref/show-recursive-multiple.png b/tests/ref/show-recursive-multiple.png new file mode 100644 index 0000000000..b56b089c98 Binary files /dev/null and b/tests/ref/show-recursive-multiple.png differ diff --git a/tests/ref/show-rule-in-function.png b/tests/ref/show-rule-in-function.png new file mode 100644 index 0000000000..97aa2845e0 Binary files /dev/null and b/tests/ref/show-rule-in-function.png differ diff --git a/tests/ref/show-selector-basic.png b/tests/ref/show-selector-basic.png new file mode 100644 index 0000000000..870166d9e6 Binary files /dev/null and b/tests/ref/show-selector-basic.png differ diff --git a/tests/ref/show-selector-discard.png b/tests/ref/show-selector-discard.png new file mode 100644 index 0000000000..13c9f0d655 Binary files /dev/null and b/tests/ref/show-selector-discard.png differ diff --git a/tests/ref/show-selector-element-or-label.png b/tests/ref/show-selector-element-or-label.png new file mode 100644 index 0000000000..32cd992d20 Binary files /dev/null and b/tests/ref/show-selector-element-or-label.png differ diff --git a/tests/ref/show-selector-or-elements-with-set.png b/tests/ref/show-selector-or-elements-with-set.png new file mode 100644 index 0000000000..f561cad887 Binary files /dev/null and b/tests/ref/show-selector-or-elements-with-set.png differ diff --git a/tests/ref/show-selector-realistic.png b/tests/ref/show-selector-realistic.png new file mode 100644 index 0000000000..ae4f4a9a8b Binary files /dev/null and b/tests/ref/show-selector-realistic.png differ diff --git a/tests/ref/show-selector-replace-and-show-set.png b/tests/ref/show-selector-replace-and-show-set.png new file mode 100644 index 0000000000..47a7ae3367 Binary files /dev/null and b/tests/ref/show-selector-replace-and-show-set.png differ diff --git a/tests/ref/show-selector-replace.png b/tests/ref/show-selector-replace.png new file mode 100644 index 0000000000..c00a88e8c8 Binary files /dev/null and b/tests/ref/show-selector-replace.png differ diff --git a/tests/ref/show-selector-where.png b/tests/ref/show-selector-where.png new file mode 100644 index 0000000000..4cb02efd07 Binary files /dev/null and b/tests/ref/show-selector-where.png differ diff --git a/tests/ref/show-set-on-layoutable-element.png b/tests/ref/show-set-on-layoutable-element.png new file mode 100644 index 0000000000..701bea5087 Binary files /dev/null and b/tests/ref/show-set-on-layoutable-element.png differ diff --git a/tests/ref/show-set-on-same-element.png b/tests/ref/show-set-on-same-element.png new file mode 100644 index 0000000000..9459fca0c8 Binary files /dev/null and b/tests/ref/show-set-on-same-element.png differ diff --git a/tests/ref/show-set-override.png b/tests/ref/show-set-override.png new file mode 100644 index 0000000000..e7831b90a9 Binary files /dev/null and b/tests/ref/show-set-override.png differ diff --git a/tests/ref/show-set-same-element-and-order.png b/tests/ref/show-set-same-element-and-order.png new file mode 100644 index 0000000000..d55d5e14fb Binary files /dev/null and b/tests/ref/show-set-same-element-and-order.png differ diff --git a/tests/ref/show-set-same-element-matched-field.png b/tests/ref/show-set-same-element-matched-field.png new file mode 100644 index 0000000000..aa44baee96 Binary files /dev/null and b/tests/ref/show-set-same-element-matched-field.png differ diff --git a/tests/ref/show-set-same-element-matching-interaction.png b/tests/ref/show-set-same-element-matching-interaction.png new file mode 100644 index 0000000000..bc06103850 Binary files /dev/null and b/tests/ref/show-set-same-element-matching-interaction.png differ diff --git a/tests/ref/show-set-same-element-synthesized-matched-field.png b/tests/ref/show-set-same-element-synthesized-matched-field.png new file mode 100644 index 0000000000..c3918e8f31 Binary files /dev/null and b/tests/ref/show-set-same-element-synthesized-matched-field.png differ diff --git a/tests/ref/show-set-text-order-adjacent-1.png b/tests/ref/show-set-text-order-adjacent-1.png new file mode 100644 index 0000000000..1bc95e3b9b Binary files /dev/null and b/tests/ref/show-set-text-order-adjacent-1.png differ diff --git a/tests/ref/show-set-text-order-adjacent-2.png b/tests/ref/show-set-text-order-adjacent-2.png new file mode 100644 index 0000000000..caada91a04 Binary files /dev/null and b/tests/ref/show-set-text-order-adjacent-2.png differ diff --git a/tests/ref/show-set-text-order-contained-1.png b/tests/ref/show-set-text-order-contained-1.png new file mode 100644 index 0000000000..8deaaacdf5 Binary files /dev/null and b/tests/ref/show-set-text-order-contained-1.png differ diff --git a/tests/ref/show-set-text-order-contained-2.png b/tests/ref/show-set-text-order-contained-2.png new file mode 100644 index 0000000000..00ea3fb83d Binary files /dev/null and b/tests/ref/show-set-text-order-contained-2.png differ diff --git a/tests/ref/show-set-text-order-contained-3.png b/tests/ref/show-set-text-order-contained-3.png new file mode 100644 index 0000000000..1bc95e3b9b Binary files /dev/null and b/tests/ref/show-set-text-order-contained-3.png differ diff --git a/tests/ref/show-set-text-order-contained-4.png b/tests/ref/show-set-text-order-contained-4.png new file mode 100644 index 0000000000..0946f92218 Binary files /dev/null and b/tests/ref/show-set-text-order-contained-4.png differ diff --git a/tests/ref/show-set-text-order-overlapping-1.png b/tests/ref/show-set-text-order-overlapping-1.png new file mode 100644 index 0000000000..71222567bf Binary files /dev/null and b/tests/ref/show-set-text-order-overlapping-1.png differ diff --git a/tests/ref/show-set-text-order-overlapping-2.png b/tests/ref/show-set-text-order-overlapping-2.png new file mode 100644 index 0000000000..f1b658f203 Binary files /dev/null and b/tests/ref/show-set-text-order-overlapping-2.png differ diff --git a/tests/ref/show-set-vs-construct.png b/tests/ref/show-set-vs-construct.png new file mode 100644 index 0000000000..a0ec96bf68 Binary files /dev/null and b/tests/ref/show-set-vs-construct.png differ diff --git a/tests/ref/show-set-where-override.png b/tests/ref/show-set-where-override.png new file mode 100644 index 0000000000..7f1ec60d4c Binary files /dev/null and b/tests/ref/show-set-where-override.png differ diff --git a/tests/ref/show-text-basic.png b/tests/ref/show-text-basic.png new file mode 100644 index 0000000000..29bb5840ce Binary files /dev/null and b/tests/ref/show-text-basic.png differ diff --git a/tests/ref/show-text-cyclic-raw.png b/tests/ref/show-text-cyclic-raw.png new file mode 100644 index 0000000000..b7521c4492 Binary files /dev/null and b/tests/ref/show-text-cyclic-raw.png differ diff --git a/tests/ref/show-text-cyclic.png b/tests/ref/show-text-cyclic.png new file mode 100644 index 0000000000..4c4c48868e Binary files /dev/null and b/tests/ref/show-text-cyclic.png differ diff --git a/tests/ref/show-text-exactly-once.png b/tests/ref/show-text-exactly-once.png new file mode 100644 index 0000000000..f681f72160 Binary files /dev/null and b/tests/ref/show-text-exactly-once.png differ diff --git a/tests/ref/show-text-get-text-on-it.png b/tests/ref/show-text-get-text-on-it.png new file mode 100644 index 0000000000..5c75b9de9c Binary files /dev/null and b/tests/ref/show-text-get-text-on-it.png differ diff --git a/tests/ref/show-text-in-other-show.png b/tests/ref/show-text-in-other-show.png new file mode 100644 index 0000000000..f29de999b6 Binary files /dev/null and b/tests/ref/show-text-in-other-show.png differ diff --git a/tests/ref/show-text-indirectly-cyclic.png b/tests/ref/show-text-indirectly-cyclic.png new file mode 100644 index 0000000000..de166dcaa4 Binary files /dev/null and b/tests/ref/show-text-indirectly-cyclic.png differ diff --git a/tests/ref/show-text-path-resolving.png b/tests/ref/show-text-path-resolving.png new file mode 100644 index 0000000000..1a04f9e6e7 Binary files /dev/null and b/tests/ref/show-text-path-resolving.png differ diff --git a/tests/ref/show-text-regex-case-insensitive.png b/tests/ref/show-text-regex-case-insensitive.png new file mode 100644 index 0000000000..70d70d343e Binary files /dev/null and b/tests/ref/show-text-regex-case-insensitive.png differ diff --git a/tests/ref/show-text-regex-character-class.png b/tests/ref/show-text-regex-character-class.png new file mode 100644 index 0000000000..946c5d2259 Binary files /dev/null and b/tests/ref/show-text-regex-character-class.png differ diff --git a/tests/ref/show-text-regex-word-boundary.png b/tests/ref/show-text-regex-word-boundary.png new file mode 100644 index 0000000000..c171ac0270 Binary files /dev/null and b/tests/ref/show-text-regex-word-boundary.png differ diff --git a/tests/ref/show-text-regex.png b/tests/ref/show-text-regex.png new file mode 100644 index 0000000000..85db10a334 Binary files /dev/null and b/tests/ref/show-text-regex.png differ diff --git a/tests/ref/show-where-folding-stroke.png b/tests/ref/show-where-folding-stroke.png new file mode 100644 index 0000000000..186ce68103 Binary files /dev/null and b/tests/ref/show-where-folding-stroke.png differ diff --git a/tests/ref/show-where-folding-text-size.png b/tests/ref/show-where-folding-text-size.png new file mode 100644 index 0000000000..9fbe3ff980 Binary files /dev/null and b/tests/ref/show-where-folding-text-size.png differ diff --git a/tests/ref/show-where-optional-field-raw.png b/tests/ref/show-where-optional-field-raw.png new file mode 100644 index 0000000000..dd3816108a Binary files /dev/null and b/tests/ref/show-where-optional-field-raw.png differ diff --git a/tests/ref/show-where-optional-field-text.png b/tests/ref/show-where-optional-field-text.png new file mode 100644 index 0000000000..b1367d0922 Binary files /dev/null and b/tests/ref/show-where-optional-field-text.png differ diff --git a/tests/ref/show-where-resolving-hyphenate.png b/tests/ref/show-where-resolving-hyphenate.png new file mode 100644 index 0000000000..052a2eda5c Binary files /dev/null and b/tests/ref/show-where-resolving-hyphenate.png differ diff --git a/tests/ref/show-where-resolving-length.png b/tests/ref/show-where-resolving-length.png new file mode 100644 index 0000000000..4c77f2acd2 Binary files /dev/null and b/tests/ref/show-where-resolving-length.png differ diff --git a/tests/ref/smallcaps.png b/tests/ref/smallcaps.png new file mode 100644 index 0000000000..b5ee12b790 Binary files /dev/null and b/tests/ref/smallcaps.png differ diff --git a/tests/ref/smartquote-apostrophe.png b/tests/ref/smartquote-apostrophe.png new file mode 100644 index 0000000000..d2cc1ebf76 Binary files /dev/null and b/tests/ref/smartquote-apostrophe.png differ diff --git a/tests/ref/smartquote-custom-complex.png b/tests/ref/smartquote-custom-complex.png new file mode 100644 index 0000000000..7204a997b5 Binary files /dev/null and b/tests/ref/smartquote-custom-complex.png differ diff --git a/tests/ref/smartquote-custom.png b/tests/ref/smartquote-custom.png new file mode 100644 index 0000000000..6a6bd9d1a0 Binary files /dev/null and b/tests/ref/smartquote-custom.png differ diff --git a/tests/ref/smartquote-disable.png b/tests/ref/smartquote-disable.png new file mode 100644 index 0000000000..0218b7acba Binary files /dev/null and b/tests/ref/smartquote-disable.png differ diff --git a/tests/ref/smartquote-disabled-temporarily.png b/tests/ref/smartquote-disabled-temporarily.png new file mode 100644 index 0000000000..84bc5e32b7 Binary files /dev/null and b/tests/ref/smartquote-disabled-temporarily.png differ diff --git a/tests/ref/smartquote-empty.png b/tests/ref/smartquote-empty.png new file mode 100644 index 0000000000..f9f19989cf Binary files /dev/null and b/tests/ref/smartquote-empty.png differ diff --git a/tests/ref/smartquote-escape.png b/tests/ref/smartquote-escape.png new file mode 100644 index 0000000000..45d8f6027e Binary files /dev/null and b/tests/ref/smartquote-escape.png differ diff --git a/tests/ref/smartquote-nesting.png b/tests/ref/smartquote-nesting.png new file mode 100644 index 0000000000..1f38c09758 Binary files /dev/null and b/tests/ref/smartquote-nesting.png differ diff --git a/tests/ref/smartquote.png b/tests/ref/smartquote.png new file mode 100644 index 0000000000..070e04876a Binary files /dev/null and b/tests/ref/smartquote.png differ diff --git a/tests/ref/space-collapsing-comments.png b/tests/ref/space-collapsing-comments.png new file mode 100644 index 0000000000..b35d9fec9a Binary files /dev/null and b/tests/ref/space-collapsing-comments.png differ diff --git a/tests/ref/space-collapsing-linebreaks.png b/tests/ref/space-collapsing-linebreaks.png new file mode 100644 index 0000000000..b1f4a3af92 Binary files /dev/null and b/tests/ref/space-collapsing-linebreaks.png differ diff --git a/tests/ref/space-collapsing-stringy-linebreak.png b/tests/ref/space-collapsing-stringy-linebreak.png new file mode 100644 index 0000000000..ceec6da7cb Binary files /dev/null and b/tests/ref/space-collapsing-stringy-linebreak.png differ diff --git a/tests/ref/space-collapsing-with-h.png b/tests/ref/space-collapsing-with-h.png new file mode 100644 index 0000000000..c2e253e71b Binary files /dev/null and b/tests/ref/space-collapsing-with-h.png differ diff --git a/tests/ref/space-collapsing.png b/tests/ref/space-collapsing.png new file mode 100644 index 0000000000..32bd6039de Binary files /dev/null and b/tests/ref/space-collapsing.png differ diff --git a/tests/ref/space-ideographic-kept.png b/tests/ref/space-ideographic-kept.png new file mode 100644 index 0000000000..cd292e2da0 Binary files /dev/null and b/tests/ref/space-ideographic-kept.png differ diff --git a/tests/ref/space-thin-kept.png b/tests/ref/space-thin-kept.png new file mode 100644 index 0000000000..6ed3504bef Binary files /dev/null and b/tests/ref/space-thin-kept.png differ diff --git a/tests/ref/space-trailing-linebreak.png b/tests/ref/space-trailing-linebreak.png new file mode 100644 index 0000000000..42b2826430 Binary files /dev/null and b/tests/ref/space-trailing-linebreak.png differ diff --git a/tests/ref/spacing-h-and-v.png b/tests/ref/spacing-h-and-v.png new file mode 100644 index 0000000000..2c9a296067 Binary files /dev/null and b/tests/ref/spacing-h-and-v.png differ diff --git a/tests/ref/spacing-rtl.png b/tests/ref/spacing-rtl.png new file mode 100644 index 0000000000..a9cbbca6e1 Binary files /dev/null and b/tests/ref/spacing-rtl.png differ diff --git a/tests/ref/square-auto-sized.png b/tests/ref/square-auto-sized.png new file mode 100644 index 0000000000..a2c4a36e12 Binary files /dev/null and b/tests/ref/square-auto-sized.png differ diff --git a/tests/ref/square-base.png b/tests/ref/square-base.png new file mode 100644 index 0000000000..3ef753f237 Binary files /dev/null and b/tests/ref/square-base.png differ diff --git a/tests/ref/square-circle-alignment.png b/tests/ref/square-circle-alignment.png new file mode 100644 index 0000000000..3fff9e66f6 Binary files /dev/null and b/tests/ref/square-circle-alignment.png differ diff --git a/tests/ref/square-circle-overspecified.png b/tests/ref/square-circle-overspecified.png new file mode 100644 index 0000000000..6dde5e511d Binary files /dev/null and b/tests/ref/square-circle-overspecified.png differ diff --git a/tests/ref/square-contents-overflow.png b/tests/ref/square-contents-overflow.png new file mode 100644 index 0000000000..ae65b0a8c2 Binary files /dev/null and b/tests/ref/square-contents-overflow.png differ diff --git a/tests/ref/square-height-limited-stack.png b/tests/ref/square-height-limited-stack.png new file mode 100644 index 0000000000..f52c608d62 Binary files /dev/null and b/tests/ref/square-height-limited-stack.png differ diff --git a/tests/ref/square-height-limited.png b/tests/ref/square-height-limited.png new file mode 100644 index 0000000000..c01dc426a6 Binary files /dev/null and b/tests/ref/square-height-limited.png differ diff --git a/tests/ref/square-overflow.png b/tests/ref/square-overflow.png new file mode 100644 index 0000000000..6169f305ba Binary files /dev/null and b/tests/ref/square-overflow.png differ diff --git a/tests/ref/square-rect-rounded.png b/tests/ref/square-rect-rounded.png new file mode 100644 index 0000000000..678ba819b1 Binary files /dev/null and b/tests/ref/square-rect-rounded.png differ diff --git a/tests/ref/square-relative-size.png b/tests/ref/square-relative-size.png new file mode 100644 index 0000000000..96e744e6b7 Binary files /dev/null and b/tests/ref/square-relative-size.png differ diff --git a/tests/ref/square-relatively-sized-child.png b/tests/ref/square-relatively-sized-child.png new file mode 100644 index 0000000000..3ffe310571 Binary files /dev/null and b/tests/ref/square-relatively-sized-child.png differ diff --git a/tests/ref/square.png b/tests/ref/square.png new file mode 100644 index 0000000000..e6f8f5c8d3 Binary files /dev/null and b/tests/ref/square.png differ diff --git a/tests/ref/stack-basic.png b/tests/ref/stack-basic.png new file mode 100644 index 0000000000..b5f38a833b Binary files /dev/null and b/tests/ref/stack-basic.png differ diff --git a/tests/ref/stack-fr.png b/tests/ref/stack-fr.png new file mode 100644 index 0000000000..e34dd9b11a Binary files /dev/null and b/tests/ref/stack-fr.png differ diff --git a/tests/ref/stack-overflow.png b/tests/ref/stack-overflow.png new file mode 100644 index 0000000000..43b3625afb Binary files /dev/null and b/tests/ref/stack-overflow.png differ diff --git a/tests/ref/stack-rtl-align-and-fr.png b/tests/ref/stack-rtl-align-and-fr.png new file mode 100644 index 0000000000..653ade6fda Binary files /dev/null and b/tests/ref/stack-rtl-align-and-fr.png differ diff --git a/tests/ref/stack-spacing.png b/tests/ref/stack-spacing.png new file mode 100644 index 0000000000..9667f65770 Binary files /dev/null and b/tests/ref/stack-spacing.png differ diff --git a/tests/ref/state-basic.png b/tests/ref/state-basic.png new file mode 100644 index 0000000000..0c67a751db Binary files /dev/null and b/tests/ref/state-basic.png differ diff --git a/tests/ref/state-multiple-calls-same-key.png b/tests/ref/state-multiple-calls-same-key.png new file mode 100644 index 0000000000..077b67929d Binary files /dev/null and b/tests/ref/state-multiple-calls-same-key.png differ diff --git a/tests/ref/state-nested.png b/tests/ref/state-nested.png new file mode 100644 index 0000000000..cc7016001e Binary files /dev/null and b/tests/ref/state-nested.png differ diff --git a/tests/ref/state-no-convergence.png b/tests/ref/state-no-convergence.png new file mode 100644 index 0000000000..dd44b9e179 Binary files /dev/null and b/tests/ref/state-no-convergence.png differ diff --git a/tests/ref/strike-background.png b/tests/ref/strike-background.png new file mode 100644 index 0000000000..01861d255a Binary files /dev/null and b/tests/ref/strike-background.png differ diff --git a/tests/ref/strike-with.png b/tests/ref/strike-with.png new file mode 100644 index 0000000000..59a84150ed Binary files /dev/null and b/tests/ref/strike-with.png differ diff --git a/tests/ref/stroke-composition.png b/tests/ref/stroke-composition.png new file mode 100644 index 0000000000..a6c7ce70fe Binary files /dev/null and b/tests/ref/stroke-composition.png differ diff --git a/tests/ref/stroke-folding.png b/tests/ref/stroke-folding.png new file mode 100644 index 0000000000..b4f1b1a9a3 Binary files /dev/null and b/tests/ref/stroke-folding.png differ diff --git a/tests/ref/stroke-text.png b/tests/ref/stroke-text.png new file mode 100644 index 0000000000..ac09053a99 Binary files /dev/null and b/tests/ref/stroke-text.png differ diff --git a/tests/ref/stroke-zero-thickness.png b/tests/ref/stroke-zero-thickness.png new file mode 100644 index 0000000000..6d305eaf40 Binary files /dev/null and b/tests/ref/stroke-zero-thickness.png differ diff --git a/tests/ref/strong-delta.png b/tests/ref/strong-delta.png new file mode 100644 index 0000000000..d32459f60d Binary files /dev/null and b/tests/ref/strong-delta.png differ diff --git a/tests/ref/strong-double-star-empty-hint.png b/tests/ref/strong-double-star-empty-hint.png new file mode 100644 index 0000000000..29cbb90f4e Binary files /dev/null and b/tests/ref/strong-double-star-empty-hint.png differ diff --git a/tests/ref/sub-super-non-typographic.png b/tests/ref/sub-super-non-typographic.png new file mode 100644 index 0000000000..e5a8b67330 Binary files /dev/null and b/tests/ref/sub-super-non-typographic.png differ diff --git a/tests/ref/sub-super.png b/tests/ref/sub-super.png new file mode 100644 index 0000000000..9359cf015c Binary files /dev/null and b/tests/ref/sub-super.png differ diff --git a/tests/ref/super-underline.png b/tests/ref/super-underline.png new file mode 100644 index 0000000000..99c1c30908 Binary files /dev/null and b/tests/ref/super-underline.png differ diff --git a/tests/ref/symbol-constructor.png b/tests/ref/symbol-constructor.png new file mode 100644 index 0000000000..e6db9491d1 Binary files /dev/null and b/tests/ref/symbol-constructor.png differ diff --git a/tests/ref/symbol.png b/tests/ref/symbol.png new file mode 100644 index 0000000000..37339d591a Binary files /dev/null and b/tests/ref/symbol.png differ diff --git a/tests/ref/table-align-array.png b/tests/ref/table-align-array.png new file mode 100644 index 0000000000..9242ae1208 Binary files /dev/null and b/tests/ref/table-align-array.png differ diff --git a/tests/ref/table-cell-align-override.png b/tests/ref/table-cell-align-override.png new file mode 100644 index 0000000000..dfab2bb038 Binary files /dev/null and b/tests/ref/table-cell-align-override.png differ diff --git a/tests/ref/table-cell-folding.png b/tests/ref/table-cell-folding.png new file mode 100644 index 0000000000..94897a9278 Binary files /dev/null and b/tests/ref/table-cell-folding.png differ diff --git a/tests/ref/table-cell-override.png b/tests/ref/table-cell-override.png new file mode 100644 index 0000000000..d6f37d632f Binary files /dev/null and b/tests/ref/table-cell-override.png differ diff --git a/tests/ref/table-cell-set.png b/tests/ref/table-cell-set.png new file mode 100644 index 0000000000..ef720ab36c Binary files /dev/null and b/tests/ref/table-cell-set.png differ diff --git a/tests/ref/table-cell-show-and-override.png b/tests/ref/table-cell-show-and-override.png new file mode 100644 index 0000000000..df74580223 Binary files /dev/null and b/tests/ref/table-cell-show-and-override.png differ diff --git a/tests/ref/table-cell-show-based-on-position.png b/tests/ref/table-cell-show-based-on-position.png new file mode 100644 index 0000000000..db46e2600d Binary files /dev/null and b/tests/ref/table-cell-show-based-on-position.png differ diff --git a/tests/ref/table-cell-show-emph.png b/tests/ref/table-cell-show-emph.png new file mode 100644 index 0000000000..1afc833ba0 Binary files /dev/null and b/tests/ref/table-cell-show-emph.png differ diff --git a/tests/ref/table-cell-show.png b/tests/ref/table-cell-show.png new file mode 100644 index 0000000000..9ac6d2695d Binary files /dev/null and b/tests/ref/table-cell-show.png differ diff --git a/tests/ref/table-cell-various-overrides.png b/tests/ref/table-cell-various-overrides.png new file mode 100644 index 0000000000..c8540dfed1 Binary files /dev/null and b/tests/ref/table-cell-various-overrides.png differ diff --git a/tests/ref/table-fill-basic.png b/tests/ref/table-fill-basic.png new file mode 100644 index 0000000000..bc12f8ae8d Binary files /dev/null and b/tests/ref/table-fill-basic.png differ diff --git a/tests/ref/table-gutters.png b/tests/ref/table-gutters.png new file mode 100644 index 0000000000..697ddd488c Binary files /dev/null and b/tests/ref/table-gutters.png differ diff --git a/tests/ref/table-inset-fold.png b/tests/ref/table-inset-fold.png new file mode 100644 index 0000000000..f2985c9ed8 Binary files /dev/null and b/tests/ref/table-inset-fold.png differ diff --git a/tests/ref/table-inset.png b/tests/ref/table-inset.png new file mode 100644 index 0000000000..a8a9adda62 Binary files /dev/null and b/tests/ref/table-inset.png differ diff --git a/tests/ref/table-newlines.png b/tests/ref/table-newlines.png new file mode 100644 index 0000000000..a4da25f3d8 Binary files /dev/null and b/tests/ref/table-newlines.png differ diff --git a/tests/ref/table-stroke-vline-position-left-and-right.png b/tests/ref/table-stroke-vline-position-left-and-right.png new file mode 100644 index 0000000000..53b48a101a Binary files /dev/null and b/tests/ref/table-stroke-vline-position-left-and-right.png differ diff --git a/tests/ref/terms-built-in-loop.png b/tests/ref/terms-built-in-loop.png new file mode 100644 index 0000000000..dc103af92d Binary files /dev/null and b/tests/ref/terms-built-in-loop.png differ diff --git a/tests/ref/terms-constructor.png b/tests/ref/terms-constructor.png new file mode 100644 index 0000000000..fe16150587 Binary files /dev/null and b/tests/ref/terms-constructor.png differ diff --git a/tests/ref/terms-grid.png b/tests/ref/terms-grid.png new file mode 100644 index 0000000000..6142becfc4 Binary files /dev/null and b/tests/ref/terms-grid.png differ diff --git a/tests/ref/terms-multiline.png b/tests/ref/terms-multiline.png new file mode 100644 index 0000000000..b5baea4add Binary files /dev/null and b/tests/ref/terms-multiline.png differ diff --git a/tests/ref/terms-rtl.png b/tests/ref/terms-rtl.png new file mode 100644 index 0000000000..538571ddbb Binary files /dev/null and b/tests/ref/terms-rtl.png differ diff --git a/tests/ref/terms-style-change-interrupted.png b/tests/ref/terms-style-change-interrupted.png new file mode 100644 index 0000000000..846e45e14e Binary files /dev/null and b/tests/ref/terms-style-change-interrupted.png differ diff --git a/tests/ref/terms-syntax-edge-cases.png b/tests/ref/terms-syntax-edge-cases.png new file mode 100644 index 0000000000..e2a557c1aa Binary files /dev/null and b/tests/ref/terms-syntax-edge-cases.png differ diff --git a/tests/ref/text-alternates-and-stylistic-sets.png b/tests/ref/text-alternates-and-stylistic-sets.png new file mode 100644 index 0000000000..877542fc23 Binary files /dev/null and b/tests/ref/text-alternates-and-stylistic-sets.png differ diff --git a/tests/ref/text-call-body.png b/tests/ref/text-call-body.png new file mode 100644 index 0000000000..24cdeb9f90 Binary files /dev/null and b/tests/ref/text-call-body.png differ diff --git a/tests/ref/text-chinese-basic.png b/tests/ref/text-chinese-basic.png new file mode 100644 index 0000000000..ea4a0b829a Binary files /dev/null and b/tests/ref/text-chinese-basic.png differ diff --git a/tests/ref/text-cjk-latin-spacing.png b/tests/ref/text-cjk-latin-spacing.png new file mode 100644 index 0000000000..1906bf761e Binary files /dev/null and b/tests/ref/text-cjk-latin-spacing.png differ diff --git a/tests/ref/text-copy-paste-ligatures.png b/tests/ref/text-copy-paste-ligatures.png new file mode 100644 index 0000000000..f0f36a8691 Binary files /dev/null and b/tests/ref/text-copy-paste-ligatures.png differ diff --git a/tests/ref/text-edge.png b/tests/ref/text-edge.png new file mode 100644 index 0000000000..0953ededf8 Binary files /dev/null and b/tests/ref/text-edge.png differ diff --git a/tests/ref/text-features.png b/tests/ref/text-features.png new file mode 100644 index 0000000000..7b0b391f77 Binary files /dev/null and b/tests/ref/text-features.png differ diff --git a/tests/ref/text-font-change-after-space.png b/tests/ref/text-font-change-after-space.png new file mode 100644 index 0000000000..83d2ceb621 Binary files /dev/null and b/tests/ref/text-font-change-after-space.png differ diff --git a/tests/ref/text-font-just-a-space.png b/tests/ref/text-font-just-a-space.png new file mode 100644 index 0000000000..3c91db3ce0 Binary files /dev/null and b/tests/ref/text-font-just-a-space.png differ diff --git a/tests/ref/text-font-properties.png b/tests/ref/text-font-properties.png new file mode 100644 index 0000000000..3c65fa33c2 Binary files /dev/null and b/tests/ref/text-font-properties.png differ diff --git a/tests/ref/text-kerning.png b/tests/ref/text-kerning.png new file mode 100644 index 0000000000..1bd3a00122 Binary files /dev/null and b/tests/ref/text-kerning.png differ diff --git a/tests/ref/text-lang-hyphenate.png b/tests/ref/text-lang-hyphenate.png new file mode 100644 index 0000000000..6315d6e217 Binary files /dev/null and b/tests/ref/text-lang-hyphenate.png differ diff --git a/tests/ref/text-lang-region.png b/tests/ref/text-lang-region.png new file mode 100644 index 0000000000..a273657845 Binary files /dev/null and b/tests/ref/text-lang-region.png differ diff --git a/tests/ref/text-lang-script-shaping.png b/tests/ref/text-lang-script-shaping.png new file mode 100644 index 0000000000..6beaece405 Binary files /dev/null and b/tests/ref/text-lang-script-shaping.png differ diff --git a/tests/ref/text-lang-shaping.png b/tests/ref/text-lang-shaping.png new file mode 100644 index 0000000000..b892fcd5dc Binary files /dev/null and b/tests/ref/text-lang-shaping.png differ diff --git a/tests/ref/text-lang-unknown-region.png b/tests/ref/text-lang-unknown-region.png new file mode 100644 index 0000000000..de63013eca Binary files /dev/null and b/tests/ref/text-lang-unknown-region.png differ diff --git a/tests/ref/text-lang.png b/tests/ref/text-lang.png new file mode 100644 index 0000000000..de63013eca Binary files /dev/null and b/tests/ref/text-lang.png differ diff --git a/tests/ref/text-ligatures.png b/tests/ref/text-ligatures.png new file mode 100644 index 0000000000..6f0e286c69 Binary files /dev/null and b/tests/ref/text-ligatures.png differ diff --git a/tests/ref/text-number-type.png b/tests/ref/text-number-type.png new file mode 100644 index 0000000000..beb6ba6c48 Binary files /dev/null and b/tests/ref/text-number-type.png differ diff --git a/tests/ref/text-number-width.png b/tests/ref/text-number-width.png new file mode 100644 index 0000000000..62d8c61b24 Binary files /dev/null and b/tests/ref/text-number-width.png differ diff --git a/tests/ref/text-size-em-nesting.png b/tests/ref/text-size-em-nesting.png new file mode 100644 index 0000000000..34ae35fe1a Binary files /dev/null and b/tests/ref/text-size-em-nesting.png differ diff --git a/tests/ref/text-size-em.png b/tests/ref/text-size-em.png new file mode 100644 index 0000000000..944bdd2987 Binary files /dev/null and b/tests/ref/text-size-em.png differ diff --git a/tests/ref/text-slashed-zero-and-fractions.png b/tests/ref/text-slashed-zero-and-fractions.png new file mode 100644 index 0000000000..a25ca02326 Binary files /dev/null and b/tests/ref/text-slashed-zero-and-fractions.png differ diff --git a/tests/ref/text-spacing-relative.png b/tests/ref/text-spacing-relative.png new file mode 100644 index 0000000000..ccd2f14057 Binary files /dev/null and b/tests/ref/text-spacing-relative.png differ diff --git a/tests/ref/text-spacing.png b/tests/ref/text-spacing.png new file mode 100644 index 0000000000..240c69c077 Binary files /dev/null and b/tests/ref/text-spacing.png differ diff --git a/tests/ref/text-tracking-arabic.png b/tests/ref/text-tracking-arabic.png new file mode 100644 index 0000000000..a4e450ff65 Binary files /dev/null and b/tests/ref/text-tracking-arabic.png differ diff --git a/tests/ref/text-tracking-changed-temporarily.png b/tests/ref/text-tracking-changed-temporarily.png new file mode 100644 index 0000000000..f27849b43e Binary files /dev/null and b/tests/ref/text-tracking-changed-temporarily.png differ diff --git a/tests/ref/text-tracking-mark-placement.png b/tests/ref/text-tracking-mark-placement.png new file mode 100644 index 0000000000..7fc8bb19eb Binary files /dev/null and b/tests/ref/text-tracking-mark-placement.png differ diff --git a/tests/ref/text-tracking-negative.png b/tests/ref/text-tracking-negative.png new file mode 100644 index 0000000000..9658988780 Binary files /dev/null and b/tests/ref/text-tracking-negative.png differ diff --git a/tests/ref/text/baseline.png b/tests/ref/text/baseline.png deleted file mode 100644 index dcd6eb121b..0000000000 Binary files a/tests/ref/text/baseline.png and /dev/null differ diff --git a/tests/ref/text/chinese.png b/tests/ref/text/chinese.png deleted file mode 100644 index 0c3ddd0024..0000000000 Binary files a/tests/ref/text/chinese.png and /dev/null differ diff --git a/tests/ref/text/copy-paste.png b/tests/ref/text/copy-paste.png deleted file mode 100644 index ae4a5ad992..0000000000 Binary files a/tests/ref/text/copy-paste.png and /dev/null differ diff --git a/tests/ref/text/deco.png b/tests/ref/text/deco.png deleted file mode 100644 index 3a11e72f27..0000000000 Binary files a/tests/ref/text/deco.png and /dev/null differ diff --git a/tests/ref/text/edge.png b/tests/ref/text/edge.png deleted file mode 100644 index 1daf4c2fc7..0000000000 Binary files a/tests/ref/text/edge.png and /dev/null differ diff --git a/tests/ref/text/em.png b/tests/ref/text/em.png deleted file mode 100644 index 04cccd5303..0000000000 Binary files a/tests/ref/text/em.png and /dev/null differ diff --git a/tests/ref/text/emoji.png b/tests/ref/text/emoji.png deleted file mode 100644 index 1dbbba79aa..0000000000 Binary files a/tests/ref/text/emoji.png and /dev/null differ diff --git a/tests/ref/text/emphasis.png b/tests/ref/text/emphasis.png deleted file mode 100644 index c19f6ebb01..0000000000 Binary files a/tests/ref/text/emphasis.png and /dev/null differ diff --git a/tests/ref/text/escape.png b/tests/ref/text/escape.png deleted file mode 100644 index c94bc52f91..0000000000 Binary files a/tests/ref/text/escape.png and /dev/null differ diff --git a/tests/ref/text/fallback.png b/tests/ref/text/fallback.png deleted file mode 100644 index 7f1e3e385a..0000000000 Binary files a/tests/ref/text/fallback.png and /dev/null differ diff --git a/tests/ref/text/features.png b/tests/ref/text/features.png deleted file mode 100644 index 566694c68c..0000000000 Binary files a/tests/ref/text/features.png and /dev/null differ diff --git a/tests/ref/text/font.png b/tests/ref/text/font.png deleted file mode 100644 index 39c8a951bd..0000000000 Binary files a/tests/ref/text/font.png and /dev/null differ diff --git a/tests/ref/text/hyphenate.png b/tests/ref/text/hyphenate.png deleted file mode 100644 index 7b386a5127..0000000000 Binary files a/tests/ref/text/hyphenate.png and /dev/null differ diff --git a/tests/ref/text/lang-with-region.png b/tests/ref/text/lang-with-region.png deleted file mode 100644 index c7753104ad..0000000000 Binary files a/tests/ref/text/lang-with-region.png and /dev/null differ diff --git a/tests/ref/text/lang.png b/tests/ref/text/lang.png deleted file mode 100644 index a5ae89796a..0000000000 Binary files a/tests/ref/text/lang.png and /dev/null differ diff --git a/tests/ref/text/linebreak-link.png b/tests/ref/text/linebreak-link.png deleted file mode 100644 index ffe39caa39..0000000000 Binary files a/tests/ref/text/linebreak-link.png and /dev/null differ diff --git a/tests/ref/text/linebreak-obj.png b/tests/ref/text/linebreak-obj.png deleted file mode 100644 index 127ee6872a..0000000000 Binary files a/tests/ref/text/linebreak-obj.png and /dev/null differ diff --git a/tests/ref/text/linebreak.png b/tests/ref/text/linebreak.png deleted file mode 100644 index 3dd2fc156a..0000000000 Binary files a/tests/ref/text/linebreak.png and /dev/null differ diff --git a/tests/ref/text/lorem.png b/tests/ref/text/lorem.png deleted file mode 100644 index 9d55df22e6..0000000000 Binary files a/tests/ref/text/lorem.png and /dev/null differ diff --git a/tests/ref/text/microtype.png b/tests/ref/text/microtype.png deleted file mode 100644 index 87622b0f8f..0000000000 Binary files a/tests/ref/text/microtype.png and /dev/null differ diff --git a/tests/ref/text/numbers.png b/tests/ref/text/numbers.png deleted file mode 100644 index 9fc76aae22..0000000000 Binary files a/tests/ref/text/numbers.png and /dev/null differ diff --git a/tests/ref/text/quote-nesting.png b/tests/ref/text/quote-nesting.png deleted file mode 100644 index fb16002de9..0000000000 Binary files a/tests/ref/text/quote-nesting.png and /dev/null differ diff --git a/tests/ref/text/quote.png b/tests/ref/text/quote.png deleted file mode 100644 index 653f2d17aa..0000000000 Binary files a/tests/ref/text/quote.png and /dev/null differ diff --git a/tests/ref/text/quotes.png b/tests/ref/text/quotes.png deleted file mode 100644 index 535c28297f..0000000000 Binary files a/tests/ref/text/quotes.png and /dev/null differ diff --git a/tests/ref/text/raw-align.png b/tests/ref/text/raw-align.png deleted file mode 100644 index 6d1044f7e7..0000000000 Binary files a/tests/ref/text/raw-align.png and /dev/null differ diff --git a/tests/ref/text/raw-code.png b/tests/ref/text/raw-code.png deleted file mode 100644 index d3373f5f76..0000000000 Binary files a/tests/ref/text/raw-code.png and /dev/null differ diff --git a/tests/ref/text/raw-line.png b/tests/ref/text/raw-line.png deleted file mode 100644 index b76eb8087c..0000000000 Binary files a/tests/ref/text/raw-line.png and /dev/null differ diff --git a/tests/ref/text/raw-syntaxes.png b/tests/ref/text/raw-syntaxes.png deleted file mode 100644 index ada751e09b..0000000000 Binary files a/tests/ref/text/raw-syntaxes.png and /dev/null differ diff --git a/tests/ref/text/raw-tabs.png b/tests/ref/text/raw-tabs.png deleted file mode 100644 index cac265e982..0000000000 Binary files a/tests/ref/text/raw-tabs.png and /dev/null differ diff --git a/tests/ref/text/raw-theme.png b/tests/ref/text/raw-theme.png deleted file mode 100644 index 0ce1776059..0000000000 Binary files a/tests/ref/text/raw-theme.png and /dev/null differ diff --git a/tests/ref/text/raw.png b/tests/ref/text/raw.png deleted file mode 100644 index 27120d746b..0000000000 Binary files a/tests/ref/text/raw.png and /dev/null differ diff --git a/tests/ref/text/shaping.png b/tests/ref/text/shaping.png deleted file mode 100644 index 69cba132d4..0000000000 Binary files a/tests/ref/text/shaping.png and /dev/null differ diff --git a/tests/ref/text/shift.png b/tests/ref/text/shift.png deleted file mode 100644 index 09d68bacd5..0000000000 Binary files a/tests/ref/text/shift.png and /dev/null differ diff --git a/tests/ref/text/smartquotes.png b/tests/ref/text/smartquotes.png deleted file mode 100644 index a6a8cbb564..0000000000 Binary files a/tests/ref/text/smartquotes.png and /dev/null differ diff --git a/tests/ref/text/space.png b/tests/ref/text/space.png deleted file mode 100644 index bae0e0a833..0000000000 Binary files a/tests/ref/text/space.png and /dev/null differ diff --git a/tests/ref/text/stroke.png b/tests/ref/text/stroke.png deleted file mode 100644 index d6d85c28e7..0000000000 Binary files a/tests/ref/text/stroke.png and /dev/null differ diff --git a/tests/ref/text/symbol.png b/tests/ref/text/symbol.png deleted file mode 100644 index 04d9d77f1c..0000000000 Binary files a/tests/ref/text/symbol.png and /dev/null differ diff --git a/tests/ref/text/tracking-spacing.png b/tests/ref/text/tracking-spacing.png deleted file mode 100644 index 68d802130f..0000000000 Binary files a/tests/ref/text/tracking-spacing.png and /dev/null differ diff --git a/tests/ref/transform-rotate-and-scale.png b/tests/ref/transform-rotate-and-scale.png new file mode 100644 index 0000000000..0dcf67ed2e Binary files /dev/null and b/tests/ref/transform-rotate-and-scale.png differ diff --git a/tests/ref/transform-rotate-origin.png b/tests/ref/transform-rotate-origin.png new file mode 100644 index 0000000000..152b1e1f82 Binary files /dev/null and b/tests/ref/transform-rotate-origin.png differ diff --git a/tests/ref/transform-rotate-relative-sizing.png b/tests/ref/transform-rotate-relative-sizing.png new file mode 100644 index 0000000000..9b81c3865d Binary files /dev/null and b/tests/ref/transform-rotate-relative-sizing.png differ diff --git a/tests/ref/transform-rotate.png b/tests/ref/transform-rotate.png new file mode 100644 index 0000000000..3990ed5b8e Binary files /dev/null and b/tests/ref/transform-rotate.png differ diff --git a/tests/ref/transform-scale-origin.png b/tests/ref/transform-scale-origin.png new file mode 100644 index 0000000000..10e1cfe280 Binary files /dev/null and b/tests/ref/transform-scale-origin.png differ diff --git a/tests/ref/transform-scale-relative-sizing.png b/tests/ref/transform-scale-relative-sizing.png new file mode 100644 index 0000000000..d10bd3ff4f Binary files /dev/null and b/tests/ref/transform-scale-relative-sizing.png differ diff --git a/tests/ref/transform-scale.png b/tests/ref/transform-scale.png new file mode 100644 index 0000000000..c95b90f1d8 Binary files /dev/null and b/tests/ref/transform-scale.png differ diff --git a/tests/ref/transform-tex-logo.png b/tests/ref/transform-tex-logo.png new file mode 100644 index 0000000000..5d16ffb4f4 Binary files /dev/null and b/tests/ref/transform-tex-logo.png differ diff --git a/tests/ref/underline-background.png b/tests/ref/underline-background.png new file mode 100644 index 0000000000..33ba381a20 Binary files /dev/null and b/tests/ref/underline-background.png differ diff --git a/tests/ref/underline-overline-strike.png b/tests/ref/underline-overline-strike.png new file mode 100644 index 0000000000..2567fca4e6 Binary files /dev/null and b/tests/ref/underline-overline-strike.png differ diff --git a/tests/ref/underline-stroke-folding.png b/tests/ref/underline-stroke-folding.png new file mode 100644 index 0000000000..32119e5c59 Binary files /dev/null and b/tests/ref/underline-stroke-folding.png differ diff --git a/tests/ref/visualize/gradient-conic.png b/tests/ref/visualize/gradient-conic.png deleted file mode 100644 index ff4a0ca2e6..0000000000 Binary files a/tests/ref/visualize/gradient-conic.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-dir.png b/tests/ref/visualize/gradient-dir.png deleted file mode 100644 index bda3eb171f..0000000000 Binary files a/tests/ref/visualize/gradient-dir.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-hue-rotation.png b/tests/ref/visualize/gradient-hue-rotation.png deleted file mode 100644 index 2d786f710f..0000000000 Binary files a/tests/ref/visualize/gradient-hue-rotation.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-math.png b/tests/ref/visualize/gradient-math.png deleted file mode 100644 index 470e6138af..0000000000 Binary files a/tests/ref/visualize/gradient-math.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-presets.png b/tests/ref/visualize/gradient-presets.png deleted file mode 100644 index e6f7f73a1c..0000000000 Binary files a/tests/ref/visualize/gradient-presets.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-radial.png b/tests/ref/visualize/gradient-radial.png deleted file mode 100644 index 2e8e9af3e0..0000000000 Binary files a/tests/ref/visualize/gradient-radial.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-relative-conic.png b/tests/ref/visualize/gradient-relative-conic.png deleted file mode 100644 index 232c5f0af4..0000000000 Binary files a/tests/ref/visualize/gradient-relative-conic.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-relative-linear.png b/tests/ref/visualize/gradient-relative-linear.png deleted file mode 100644 index 56e46119e1..0000000000 Binary files a/tests/ref/visualize/gradient-relative-linear.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-relative-radial.png b/tests/ref/visualize/gradient-relative-radial.png deleted file mode 100644 index 210ea7b0f8..0000000000 Binary files a/tests/ref/visualize/gradient-relative-radial.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-repeat.png b/tests/ref/visualize/gradient-repeat.png deleted file mode 100644 index 6be7dc660f..0000000000 Binary files a/tests/ref/visualize/gradient-repeat.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-sharp.png b/tests/ref/visualize/gradient-sharp.png deleted file mode 100644 index b7698cfa45..0000000000 Binary files a/tests/ref/visualize/gradient-sharp.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-stroke.png b/tests/ref/visualize/gradient-stroke.png deleted file mode 100644 index 69317f7325..0000000000 Binary files a/tests/ref/visualize/gradient-stroke.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-text-decorations.png b/tests/ref/visualize/gradient-text-decorations.png deleted file mode 100644 index 887cd50086..0000000000 Binary files a/tests/ref/visualize/gradient-text-decorations.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-text-other.png b/tests/ref/visualize/gradient-text-other.png deleted file mode 100644 index 78555b18f2..0000000000 Binary files a/tests/ref/visualize/gradient-text-other.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-text.png b/tests/ref/visualize/gradient-text.png deleted file mode 100644 index 478a058635..0000000000 Binary files a/tests/ref/visualize/gradient-text.png and /dev/null differ diff --git a/tests/ref/visualize/gradient-transform.png b/tests/ref/visualize/gradient-transform.png deleted file mode 100644 index a55ad91e81..0000000000 Binary files a/tests/ref/visualize/gradient-transform.png and /dev/null differ diff --git a/tests/ref/visualize/image-scale.png b/tests/ref/visualize/image-scale.png deleted file mode 100644 index 95e9157ec3..0000000000 Binary files a/tests/ref/visualize/image-scale.png and /dev/null differ diff --git a/tests/ref/visualize/image.png b/tests/ref/visualize/image.png deleted file mode 100644 index ec53fa980d..0000000000 Binary files a/tests/ref/visualize/image.png and /dev/null differ diff --git a/tests/ref/visualize/line.png b/tests/ref/visualize/line.png deleted file mode 100644 index d19dea0ea1..0000000000 Binary files a/tests/ref/visualize/line.png and /dev/null differ diff --git a/tests/ref/visualize/path.png b/tests/ref/visualize/path.png deleted file mode 100644 index c7f710c942..0000000000 Binary files a/tests/ref/visualize/path.png and /dev/null differ diff --git a/tests/ref/visualize/pattern-relative.png b/tests/ref/visualize/pattern-relative.png deleted file mode 100644 index 7958bf7f64..0000000000 Binary files a/tests/ref/visualize/pattern-relative.png and /dev/null differ diff --git a/tests/ref/visualize/pattern-simple.png b/tests/ref/visualize/pattern-simple.png deleted file mode 100644 index ac473a7560..0000000000 Binary files a/tests/ref/visualize/pattern-simple.png and /dev/null differ diff --git a/tests/ref/visualize/pattern-small.png b/tests/ref/visualize/pattern-small.png deleted file mode 100644 index 6af592dd8d..0000000000 Binary files a/tests/ref/visualize/pattern-small.png and /dev/null differ diff --git a/tests/ref/visualize/pattern-spacing.png b/tests/ref/visualize/pattern-spacing.png deleted file mode 100644 index 4c95a3b027..0000000000 Binary files a/tests/ref/visualize/pattern-spacing.png and /dev/null differ diff --git a/tests/ref/visualize/pattern-stroke.png b/tests/ref/visualize/pattern-stroke.png deleted file mode 100644 index d71f1c920f..0000000000 Binary files a/tests/ref/visualize/pattern-stroke.png and /dev/null differ diff --git a/tests/ref/visualize/pattern-text.png b/tests/ref/visualize/pattern-text.png deleted file mode 100644 index 2ecf2fdabc..0000000000 Binary files a/tests/ref/visualize/pattern-text.png and /dev/null differ diff --git a/tests/ref/visualize/polygon.png b/tests/ref/visualize/polygon.png deleted file mode 100644 index 234aeb1488..0000000000 Binary files a/tests/ref/visualize/polygon.png and /dev/null differ diff --git a/tests/ref/visualize/shape-aspect.png b/tests/ref/visualize/shape-aspect.png deleted file mode 100644 index 918a5e7315..0000000000 Binary files a/tests/ref/visualize/shape-aspect.png and /dev/null differ diff --git a/tests/ref/visualize/shape-circle.png b/tests/ref/visualize/shape-circle.png deleted file mode 100644 index a2ee279df2..0000000000 Binary files a/tests/ref/visualize/shape-circle.png and /dev/null differ diff --git a/tests/ref/visualize/shape-ellipse.png b/tests/ref/visualize/shape-ellipse.png deleted file mode 100644 index 6de5e9f61d..0000000000 Binary files a/tests/ref/visualize/shape-ellipse.png and /dev/null differ diff --git a/tests/ref/visualize/shape-fill-stroke.png b/tests/ref/visualize/shape-fill-stroke.png deleted file mode 100644 index d4a4817af2..0000000000 Binary files a/tests/ref/visualize/shape-fill-stroke.png and /dev/null differ diff --git a/tests/ref/visualize/shape-rect.png b/tests/ref/visualize/shape-rect.png deleted file mode 100644 index 3eda642f8f..0000000000 Binary files a/tests/ref/visualize/shape-rect.png and /dev/null differ diff --git a/tests/ref/visualize/shape-rounded.png b/tests/ref/visualize/shape-rounded.png deleted file mode 100644 index ec926d0a56..0000000000 Binary files a/tests/ref/visualize/shape-rounded.png and /dev/null differ diff --git a/tests/ref/visualize/shape-square.png b/tests/ref/visualize/shape-square.png deleted file mode 100644 index 46e243e1f8..0000000000 Binary files a/tests/ref/visualize/shape-square.png and /dev/null differ diff --git a/tests/ref/visualize/stroke.png b/tests/ref/visualize/stroke.png deleted file mode 100644 index bdfcae9f76..0000000000 Binary files a/tests/ref/visualize/stroke.png and /dev/null differ diff --git a/tests/ref/visualize/svg-text.png b/tests/ref/visualize/svg-text.png deleted file mode 100644 index b2bbe320b3..0000000000 Binary files a/tests/ref/visualize/svg-text.png and /dev/null differ diff --git a/tests/ref/while-loop-basic.png b/tests/ref/while-loop-basic.png new file mode 100644 index 0000000000..3a0e6d242b Binary files /dev/null and b/tests/ref/while-loop-basic.png differ diff --git a/tests/src/args.rs b/tests/src/args.rs new file mode 100644 index 0000000000..ddd088facd --- /dev/null +++ b/tests/src/args.rs @@ -0,0 +1,64 @@ +use clap::{Parser, Subcommand}; + +/// Typst's test runner. +#[derive(Debug, Clone, Parser)] +#[clap(name = "typst-test", author)] +pub struct CliArguments { + /// The command to run. + #[command(subcommand)] + pub command: Option, + /// All the tests that contain the filter string will be run. + pub filter: Vec, + /// Runs only the tests with the exact specified `filter` names. + #[arg(short, long)] + pub exact: bool, + /// Whether to update the reference images of non-passing tests. + #[arg(short, long)] + pub update: bool, + /// The scaling factor to render the output image with. + /// + /// Does not affect the comparison or the reference image. + #[arg(short, long, default_value_t = 1.0)] + pub scale: f32, + /// Whether to run the tests in extended mode, including PDF and SVG + /// export. + /// + /// This is used in CI. + #[arg(long, env = "TYPST_TESTS_EXTENDED")] + pub extended: bool, + /// Runs PDF export. + #[arg(long)] + pub pdf: bool, + /// Runs SVG export. + #[arg(long)] + pub svg: bool, + /// Whether to display the syntax tree. + #[arg(long)] + pub syntax: bool, + /// Prevents the terminal from being cleared of test names. + #[arg(short, long)] + pub verbose: bool, + /// How many threads to spawn when running the tests. + #[arg(short = 'j', long)] + pub num_threads: Option, +} + +impl CliArguments { + /// Whether to run PDF export. + pub fn pdf(&self) -> bool { + self.pdf || self.extended + } + + /// Whether to run SVG export. + pub fn svg(&self) -> bool { + self.svg || self.extended + } +} + +/// What to do. +#[derive(Debug, Clone, Subcommand)] +#[command()] +pub enum Command { + /// Clears the on-disk test artifact store. + Clean, +} diff --git a/tests/src/collect.rs b/tests/src/collect.rs new file mode 100644 index 0000000000..44a325f20c --- /dev/null +++ b/tests/src/collect.rs @@ -0,0 +1,420 @@ +use std::collections::{HashMap, HashSet}; +use std::fmt::{self, Display, Formatter}; +use std::ops::Range; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +use ecow::{eco_format, EcoString}; +use typst::syntax::package::PackageVersion; +use typst::syntax::{is_id_continue, is_ident, is_newline, FileId, Source, VirtualPath}; +use unscanny::Scanner; + +/// Collects all tests from all files. +/// +/// Returns: +/// - the tests and the number of skipped tests in the success case. +/// - parsing errors in the failure case. +pub fn collect() -> Result<(Vec, usize), Vec> { + Collector::new().collect() +} + +/// A single test. +pub struct Test { + pub pos: FilePos, + pub name: EcoString, + pub source: Source, + pub notes: Vec, + pub large: bool, +} + +impl Display for Test { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{} ({})", self.name, self.pos) + } +} + +/// A position in a file. +#[derive(Clone)] +pub struct FilePos { + pub path: PathBuf, + pub line: usize, +} + +impl FilePos { + fn new(path: impl Into, line: usize) -> Self { + Self { path: path.into(), line } + } +} + +impl Display for FilePos { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}:{}", self.path.display(), self.line) + } +} + +/// The size of a file. +pub struct FileSize(pub usize); + +impl Display for FileSize { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{:.2} KiB", (self.0 as f64) / 1024.0) + } +} + +/// An annotation like `// Error: 2-6 message` in a test. +pub struct Note { + pub pos: FilePos, + pub kind: NoteKind, + pub range: Option>, + pub message: String, +} + +/// A kind of annotation in a test. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum NoteKind { + Error, + Warning, + Hint, +} + +impl FromStr for NoteKind { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "Error" => Self::Error, + "Warning" => Self::Warning, + "Hint" => Self::Hint, + _ => return Err(()), + }) + } +} + +impl Display for NoteKind { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.pad(match self { + Self::Error => "Error", + Self::Warning => "Warning", + Self::Hint => "Hint", + }) + } +} + +/// Collects all tests from all files. +struct Collector { + tests: Vec, + errors: Vec, + seen: HashMap, + large: HashSet, + skipped: usize, +} + +impl Collector { + /// Creates a new test collector. + fn new() -> Self { + Self { + tests: vec![], + errors: vec![], + seen: HashMap::new(), + large: HashSet::new(), + skipped: 0, + } + } + + /// Collects tests from all files. + fn collect(mut self) -> Result<(Vec, usize), Vec> { + self.walk_files(); + self.walk_references(); + + if self.errors.is_empty() { + Ok((self.tests, self.skipped)) + } else { + Err(self.errors) + } + } + + /// Walks through all test files and collects the tests. + fn walk_files(&mut self) { + for entry in walkdir::WalkDir::new(crate::SUITE_PATH).sort_by_file_name() { + let entry = entry.unwrap(); + let path = entry.path(); + if !path.extension().is_some_and(|ext| ext == "typ") { + continue; + } + + let text = std::fs::read_to_string(path).unwrap(); + if text.starts_with("// SKIP") { + continue; + } + + Parser::new(self, path, &text).parse(); + } + } + + /// Walks through all reference images and ensure that a test exists for + /// each one. + fn walk_references(&mut self) { + for entry in walkdir::WalkDir::new(crate::REF_PATH).sort_by_file_name() { + let entry = entry.unwrap(); + let path = entry.path(); + if !path.extension().is_some_and(|ext| ext == "png") { + continue; + } + + let stem = path.file_stem().unwrap().to_string_lossy(); + let name = &*stem; + + let Some(pos) = self.seen.get(name) else { + self.errors.push(TestParseError { + pos: FilePos::new(path, 0), + message: "dangling reference image".into(), + }); + continue; + }; + + let len = path.metadata().unwrap().len() as usize; + if !self.large.contains(name) && len > crate::REF_LIMIT { + self.errors.push(TestParseError { + pos: pos.clone(), + message: format!( + "reference image size exceeds {}, but the test is not marked as `// LARGE`", + FileSize(crate::REF_LIMIT), + ), + }); + } + } + } +} + +/// Parses a single test file. +struct Parser<'a> { + collector: &'a mut Collector, + path: &'a Path, + s: Scanner<'a>, + test_start_line: usize, + line: usize, +} + +impl<'a> Parser<'a> { + /// Creates a new parser for a file. + fn new(collector: &'a mut Collector, path: &'a Path, source: &'a str) -> Self { + Self { + collector, + path, + s: Scanner::new(source), + test_start_line: 1, + line: 1, + } + } + + /// Parses an individual file. + fn parse(&mut self) { + self.skip_preamble(); + + while !self.s.done() { + let mut name = EcoString::new(); + let mut notes = vec![]; + if self.s.eat_if("---") { + self.s.eat_while(' '); + name = self.s.eat_until(char::is_whitespace).into(); + self.s.eat_while(' '); + + if name.is_empty() { + self.error("expected test name"); + } else if !is_ident(&name) { + self.error(format!("test name `{name}` is not a valid identifier")); + } else if !self.s.eat_if("---") { + self.error("expected closing ---"); + } + } else { + self.error("expected opening ---"); + } + + if self.collector.seen.contains_key(&name) { + self.error(format!("duplicate test {name}")); + } + + if self.s.eat_newline() { + self.line += 1; + } + + let start = self.s.cursor(); + self.test_start_line = self.line; + + let pos = FilePos::new(self.path, self.test_start_line); + self.collector.seen.insert(name.clone(), pos.clone()); + + while !self.s.done() && !self.s.at("---") { + self.s.eat_until(is_newline); + if self.s.eat_newline() { + self.line += 1; + } + } + + let text = self.s.from(start); + let large = text.starts_with("// LARGE"); + if large { + self.collector.large.insert(name.clone()); + } + + if !filtered(&name) { + self.collector.skipped += 1; + continue; + } + + let vpath = VirtualPath::new(self.path); + let source = Source::new(FileId::new(None, vpath), text.into()); + + self.s.jump(start); + self.line = self.test_start_line; + + while !self.s.done() && !self.s.at("---") { + self.s.eat_while(' '); + if self.s.eat_if("// ") { + notes.extend(self.parse_note(&source)); + } + + self.s.eat_until(is_newline); + if self.s.eat_newline() { + self.line += 1; + } + } + + self.collector.tests.push(Test { pos, name, source, notes, large }); + } + } + + /// Skips the preamble of a test. + fn skip_preamble(&mut self) { + let mut errored = false; + while !self.s.done() && !self.s.at("---") { + let line = self.s.eat_until(is_newline).trim(); + if !errored && !line.is_empty() && !line.starts_with("//") { + self.error("test preamble may only contain comments and blank lines"); + errored = true; + } + if self.s.eat_newline() { + self.line += 1; + } + } + } + + /// Parses an annotation in a test. + fn parse_note(&mut self, source: &Source) -> Option { + let head = self.s.eat_while(is_id_continue); + if !self.s.eat_if(':') { + return None; + } + + let kind: NoteKind = head.parse().ok()?; + self.s.eat_if(' '); + + let mut range = None; + if self.s.at('-') || self.s.at(char::is_numeric) { + range = self.parse_range(source); + if range.is_none() { + self.error("range is malformed"); + return None; + } + } + + let message = self + .s + .eat_until(is_newline) + .trim() + .replace("VERSION", &eco_format!("{}", PackageVersion::compiler())); + + Some(Note { + pos: FilePos::new(self.path, self.line), + kind, + range, + message, + }) + } + + /// Parse a range, optionally abbreviated as just a position if the range + /// is empty. + fn parse_range(&mut self, source: &Source) -> Option> { + let start = self.parse_position(source)?; + let end = if self.s.eat_if('-') { self.parse_position(source)? } else { start }; + Some(start..end) + } + + /// Parses a relative `(line:)?column` position. + fn parse_position(&mut self, source: &Source) -> Option { + let first = self.parse_number()?; + let (line_delta, column) = + if self.s.eat_if(':') { (first, self.parse_number()?) } else { (1, first) }; + + let text = source.text(); + let line_idx_in_test = self.line - self.test_start_line; + let comments = text + .lines() + .skip(line_idx_in_test + 1) + .take_while(|line| line.trim().starts_with("//")) + .count(); + + let line_idx = (line_idx_in_test + comments).checked_add_signed(line_delta)?; + let column_idx = if column < 0 { + // Negative column index is from the back. + let range = source.line_to_range(line_idx)?; + text[range].chars().count().saturating_add_signed(column) + } else { + usize::try_from(column).ok()?.checked_sub(1)? + }; + + source.line_column_to_byte(line_idx, column_idx) + } + + /// Parse a number. + fn parse_number(&mut self) -> Option { + let start = self.s.cursor(); + self.s.eat_if('-'); + self.s.eat_while(char::is_numeric); + self.s.from(start).parse().ok() + } + + /// Stores a test parsing error. + fn error(&mut self, message: impl Into) { + self.collector.errors.push(TestParseError { + pos: FilePos::new(self.path, self.line), + message: message.into(), + }); + } +} + +/// Whether a test is within the filtered set. +fn filtered(name: &str) -> bool { + let exact = crate::ARGS.exact; + let filter = &crate::ARGS.filter; + filter.is_empty() + || filter + .iter() + .any(|v| if exact { name == v } else { name.contains(v) }) +} + +/// An error in a test file. +pub struct TestParseError { + pos: FilePos, + message: String, +} + +impl Display for TestParseError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{} ({})", self.message, self.pos) + } +} + +trait ScannerExt { + fn eat_newline(&mut self) -> bool; +} + +impl ScannerExt for Scanner<'_> { + fn eat_newline(&mut self) -> bool { + let ate = self.eat_if(is_newline); + if ate && self.before().ends_with('\r') { + self.eat_if('\n'); + } + ate + } +} diff --git a/tests/src/logger.rs b/tests/src/logger.rs new file mode 100644 index 0000000000..c48650a763 --- /dev/null +++ b/tests/src/logger.rs @@ -0,0 +1,141 @@ +use std::io::{self, IsTerminal, StderrLock, Write}; +use std::time::{Duration, Instant}; + +use crate::collect::Test; +use crate::run::TestResult; + +/// Receives status updates by individual test runs. +pub struct Logger<'a> { + filtered: usize, + passed: usize, + failed: usize, + skipped: usize, + mismatched_image: bool, + active: Vec<&'a Test>, + last_change: Instant, + temp_lines: usize, + terminal: bool, +} + +impl<'a> Logger<'a> { + /// Create a new logger. + pub fn new(filtered: usize, skipped: usize) -> Self { + Self { + filtered, + passed: 0, + failed: 0, + skipped, + mismatched_image: false, + active: vec![], + temp_lines: 0, + last_change: Instant::now(), + terminal: std::io::stderr().is_terminal(), + } + } + + /// Register the start of a test. + pub fn start(&mut self, test: &'a Test) { + self.active.push(test); + self.last_change = Instant::now(); + self.refresh(); + } + + /// Register a finished test. + pub fn end(&mut self, test: &'a Test, result: std::thread::Result) { + self.active.retain(|t| t.name != test.name); + + let result = match result { + Ok(result) => result, + Err(_) => { + self.failed += 1; + self.temp_lines = 0; + self.print(move |out| { + writeln!(out, "❌ {test} panicked")?; + Ok(()) + }) + .unwrap(); + return; + } + }; + + if result.is_ok() { + self.passed += 1; + } else { + self.failed += 1; + } + + self.mismatched_image |= result.mismatched_image; + self.last_change = Instant::now(); + + self.print(move |out| { + if !result.errors.is_empty() { + writeln!(out, "❌ {test}")?; + for line in result.errors.lines() { + writeln!(out, " {line}")?; + } + } else if crate::ARGS.verbose || !result.infos.is_empty() { + writeln!(out, "✅ {test}")?; + } + for line in result.infos.lines() { + writeln!(out, " {line}")?; + } + Ok(()) + }) + .unwrap(); + } + + /// Prints a summary and returns whether the test suite passed. + pub fn finish(&self) -> bool { + let Self { filtered, passed, failed, skipped, .. } = *self; + + eprintln!("{passed} passed, {failed} failed, {skipped} skipped"); + assert_eq!(filtered, passed + failed, "not all tests were executed succesfully"); + + if self.mismatched_image { + eprintln!(" pass the --update flag to update the reference images"); + } + + self.failed == 0 + } + + /// Refresh the status. + pub fn refresh(&mut self) { + self.print(|_| Ok(())).unwrap(); + } + + /// Refresh the status print. + fn print( + &mut self, + inner: impl FnOnce(&mut StderrLock<'_>) -> io::Result<()>, + ) -> io::Result<()> { + let mut out = std::io::stderr().lock(); + + // Clear the status lines. + for _ in 0..self.temp_lines { + write!(out, "\x1B[1F\x1B[0J")?; + self.temp_lines = 0; + } + + // Print the result of a finished test. + inner(&mut out)?; + + // Print the status line. + let done = self.failed + self.passed; + if done < self.filtered { + if self.last_change.elapsed() > Duration::from_secs(2) { + for test in &self.active { + writeln!(out, "⏰ {test} is taking a long time ...")?; + if self.terminal { + self.temp_lines += 1; + } + } + } + if self.terminal { + writeln!(out, "💨 {done} / {}", self.filtered)?; + self.temp_lines += 1; + } + } + + Ok(()) + } +} diff --git a/tests/src/metadata.rs b/tests/src/metadata.rs deleted file mode 100644 index 53cbbdffb9..0000000000 --- a/tests/src/metadata.rs +++ /dev/null @@ -1,334 +0,0 @@ -use std::collections::HashSet; -use std::fmt::{self, Display, Formatter}; -use std::ops::Range; -use std::str::FromStr; - -use ecow::EcoString; -use typst::syntax::package::PackageVersion; -use typst::syntax::Source; -use unscanny::Scanner; - -/// Each test and subset may contain metadata. -#[derive(Debug)] -pub struct TestMetadata { - /// Configures how the test is run. - pub config: TestConfig, - /// Declares properties that must hold for a test. - /// - /// For instance, `// Warning: 1-3 no text within underscores` - /// will fail the test if the warning isn't generated by your test. - pub annotations: HashSet, -} - -/// Configuration of a test or subtest. -#[derive(Debug, Default)] -pub struct TestConfig { - /// Reference images will be generated and compared. - /// - /// Defaults to `true`, can be disabled with `Ref: false`. - pub compare_ref: Option, - /// Hint annotations will be compared to compiler hints. - /// - /// Defaults to `true`, can be disabled with `Hints: false`. - pub validate_hints: Option, - /// Autocompletion annotations will be validated against autocompletions. - /// Mutually exclusive with error and hint annotations. - /// - /// Defaults to `false`, can be enabled with `Autocomplete: true`. - pub validate_autocomplete: Option, -} - -/// Parsing error when the metadata is invalid. -pub(crate) enum InvalidMetadata { - /// An invalid annotation and it's error message. - InvalidAnnotation(Annotation, String), - /// Setting metadata can only be done with `true` or `false` as a value. - InvalidSet(String), -} - -impl InvalidMetadata { - pub(crate) fn write( - invalid_data: Vec, - output: &mut String, - print_annotation: &mut impl FnMut(&Annotation, &mut String), - ) { - use std::fmt::Write; - for data in invalid_data.into_iter() { - let (annotation, error) = match data { - InvalidMetadata::InvalidAnnotation(a, e) => (Some(a), e), - InvalidMetadata::InvalidSet(e) => (None, e), - }; - write!(output, "{error}",).unwrap(); - if let Some(annotation) = annotation { - print_annotation(&annotation, output) - } else { - writeln!(output).unwrap(); - } - } - } -} - -/// Annotation of the form `// KIND: RANGE TEXT`. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct Annotation { - /// Which kind of annotation this is. - pub kind: AnnotationKind, - /// May be written as: - /// - `{line}:{col}-{line}:{col}`, e.g. `0:4-0:6`. - /// - `{col}-{col}`, e.g. `4-6`: - /// The line is assumed to be the line after the annotation. - /// - `-1`: Produces a range of length zero at the end of the next line. - /// Mostly useful for autocompletion tests which require an index. - pub range: Option>, - /// The raw text after the annotation. - pub text: EcoString, -} - -/// The different kinds of in-test annotations. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum AnnotationKind { - Error, - Warning, - Hint, - AutocompleteContains, - AutocompleteExcludes, -} - -impl AnnotationKind { - /// Returns the user-facing string for this annotation. - pub fn as_str(self) -> &'static str { - match self { - AnnotationKind::Error => "Error", - AnnotationKind::Warning => "Warning", - AnnotationKind::Hint => "Hint", - AnnotationKind::AutocompleteContains => "Autocomplete contains", - AnnotationKind::AutocompleteExcludes => "Autocomplete excludes", - } - } -} - -impl FromStr for AnnotationKind { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - Ok(match s { - "Error" => AnnotationKind::Error, - "Warning" => AnnotationKind::Warning, - "Hint" => AnnotationKind::Hint, - "Autocomplete contains" => AnnotationKind::AutocompleteContains, - "Autocomplete excludes" => AnnotationKind::AutocompleteExcludes, - _ => return Err("invalid annotatino"), - }) - } -} - -impl Display for AnnotationKind { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.pad(self.as_str()) - } -} - -/// Parse metadata for a test. -pub fn parse_part_metadata( - source: &Source, - is_header: bool, -) -> Result> { - let mut config = TestConfig::default(); - let mut annotations = HashSet::default(); - let mut invalid_data = vec![]; - - let lines = source_to_lines(source); - - for (i, line) in lines.iter().enumerate() { - if let Some((key, value)) = parse_metadata_line(line) { - let key = key.trim(); - match key { - "Ref" => validate_set_annotation( - value, - &mut config.compare_ref, - &mut invalid_data, - ), - "Hints" => validate_set_annotation( - value, - &mut config.validate_hints, - &mut invalid_data, - ), - "Autocomplete" => validate_set_annotation( - value, - &mut config.validate_autocomplete, - &mut invalid_data, - ), - annotation_key => { - let Ok(kind) = AnnotationKind::from_str(annotation_key) else { - continue; - }; - let mut s = Scanner::new(value); - let range = parse_range(&mut s, i, source); - let rest = if range.is_some() { s.after() } else { s.string() }; - let message = rest - .trim() - .replace("VERSION", &PackageVersion::compiler().to_string()) - .into(); - - let annotation = - Annotation { kind, range: range.clone(), text: message }; - - if is_header { - invalid_data.push(InvalidMetadata::InvalidAnnotation( - annotation, - format!( - "Error: header may not contain annotations of type {kind}" - ), - )); - continue; - } - - if matches!( - kind, - AnnotationKind::AutocompleteContains - | AnnotationKind::AutocompleteExcludes - ) { - if let Some(range) = range { - if range.start != range.end { - invalid_data.push(InvalidMetadata::InvalidAnnotation( - annotation, - "Error: found range in Autocomplete annotation where range.start != range.end, range.end would be ignored." - .to_string() - )); - continue; - } - } else { - invalid_data.push(InvalidMetadata::InvalidAnnotation( - annotation, - "Error: autocomplete annotation but no range specified" - .to_string(), - )); - continue; - } - } - annotations.insert(annotation); - } - } - } - } - if invalid_data.is_empty() { - Ok(TestMetadata { config, annotations }) - } else { - Err(invalid_data) - } -} - -/// Extract key and value for a metadata line of the form: `// KEY: VALUE`. -fn parse_metadata_line(line: &str) -> Option<(&str, &str)> { - let mut s = Scanner::new(line); - if !s.eat_if("// ") { - return None; - } - - let key = s.eat_until(':').trim(); - if !s.eat_if(':') { - return None; - } - - let value = s.eat_until('\n').trim(); - Some((key, value)) -} - -/// Parse a quoted string. -fn parse_string<'a>(s: &mut Scanner<'a>) -> Option<&'a str> { - if !s.eat_if('"') { - return None; - } - let sub = s.eat_until('"'); - if !s.eat_if('"') { - return None; - } - - Some(sub) -} - -/// Parse a number. -fn parse_num(s: &mut Scanner) -> Option { - let mut first = true; - let n = &s.eat_while(|c: char| { - let valid = first && c == '-' || c.is_numeric(); - first = false; - valid - }); - n.parse().ok() -} - -/// Parse a comma-separated list of strings. -pub fn parse_string_list(text: &str) -> HashSet<&str> { - let mut s = Scanner::new(text); - let mut result = HashSet::new(); - while let Some(sub) = parse_string(&mut s) { - result.insert(sub); - s.eat_whitespace(); - if !s.eat_if(',') { - break; - } - s.eat_whitespace(); - } - result -} - -/// Parse a position. -fn parse_pos(s: &mut Scanner, i: usize, source: &Source) -> Option { - let first = parse_num(s)? - 1; - let (delta, column) = - if s.eat_if(':') { (first, parse_num(s)? - 1) } else { (0, first) }; - let line = (i + comments_until_code(source, i)).checked_add_signed(delta)?; - source.line_column_to_byte(line, usize::try_from(column).ok()?) -} - -/// Parse a range. -fn parse_range(s: &mut Scanner, i: usize, source: &Source) -> Option> { - let lines = source_to_lines(source); - s.eat_whitespace(); - if s.eat_if("-1") { - let mut add = 1; - while let Some(line) = lines.get(i + add) { - if !line.starts_with("//") { - break; - } - add += 1; - } - let next_line = lines.get(i + add)?; - let col = next_line.chars().count(); - - let index = source.line_column_to_byte(i + add, col)?; - s.eat_whitespace(); - return Some(index..index); - } - let start = parse_pos(s, i, source)?; - let end = if s.eat_if('-') { parse_pos(s, i, source)? } else { start }; - s.eat_whitespace(); - Some(start..end) -} - -/// Returns the number of lines of comment from line i to next line of code. -fn comments_until_code(source: &Source, i: usize) -> usize { - source_to_lines(source)[i..] - .iter() - .take_while(|line| line.starts_with("//")) - .count() -} - -fn source_to_lines(source: &Source) -> Vec<&str> { - source.text().lines().map(str::trim).collect() -} - -fn validate_set_annotation( - value: &str, - flag: &mut Option, - invalid_data: &mut Vec, -) { - let value = value.trim(); - if value != "false" && value != "true" { - invalid_data.push( - InvalidMetadata::InvalidSet(format!("Error: trying to set Ref, Hints, or Autocomplete with value {value:?} != true, != false."))) - } else { - *flag = Some(value == "true") - } -} diff --git a/tests/src/run.rs b/tests/src/run.rs new file mode 100644 index 0000000000..d0d86ea6be --- /dev/null +++ b/tests/src/run.rs @@ -0,0 +1,442 @@ +use std::fmt::Write; +use std::ops::Range; +use std::path::Path; + +use ecow::eco_vec; +use tiny_skia as sk; +use typst::diag::SourceDiagnostic; +use typst::eval::Tracer; +use typst::foundations::Smart; +use typst::introspection::Meta; +use typst::layout::{Abs, Frame, FrameItem, Page, Transform}; +use typst::model::Document; +use typst::visualize::Color; +use typst::WorldExt; + +use crate::collect::{FileSize, NoteKind, Test}; +use crate::world::TestWorld; + +/// Runs a single test. +/// +/// Returns whether the test passed. +pub fn run(test: &Test) -> TestResult { + Runner::new(test).run() +} + +/// The result of running a single test. +pub struct TestResult { + /// The error log for this test. If empty, the test passed. + pub errors: String, + /// The info log for this test. + pub infos: String, + /// Whether the image was mismatched. + pub mismatched_image: bool, +} + +impl TestResult { + /// Whether the test passed. + pub fn is_ok(&self) -> bool { + self.errors.is_empty() + } +} + +/// Write a line to a log sink, defaulting to the test's error log. +macro_rules! log { + (into: $sink:expr, $($tts:tt)*) => { + writeln!($sink, $($tts)*).unwrap(); + }; + ($runner:expr, $($tts:tt)*) => { + writeln!(&mut $runner.result.errors, $($tts)*).unwrap(); + }; +} + +/// Runs a single test. +pub struct Runner<'a> { + test: &'a Test, + world: TestWorld, + seen: Vec, + result: TestResult, + not_annotated: String, +} + +impl<'a> Runner<'a> { + /// Create a new test runner. + fn new(test: &'a Test) -> Self { + Self { + test, + world: TestWorld::new(test.source.clone()), + seen: vec![false; test.notes.len()], + result: TestResult { + errors: String::new(), + infos: String::new(), + mismatched_image: false, + }, + not_annotated: String::new(), + } + } + + /// Run the test. + fn run(mut self) -> TestResult { + if crate::ARGS.syntax { + log!(into: self.result.infos, "tree: {:#?}", self.test.source.root()); + } + + let mut tracer = Tracer::new(); + let (doc, errors) = match typst::compile(&self.world, &mut tracer) { + Ok(doc) => (Some(doc), eco_vec![]), + Err(errors) => (None, errors), + }; + + let warnings = tracer.warnings(); + if doc.is_none() && errors.is_empty() { + log!(self, "no document, but also no errors"); + } + + self.check_document(doc.as_ref()); + + for error in &errors { + self.check_diagnostic(NoteKind::Error, error); + } + + for warning in &warnings { + self.check_diagnostic(NoteKind::Warning, warning); + } + + self.handle_not_emitted(); + self.handle_not_annotated(); + + self.result + } + + /// Handle errors that weren't annotated. + fn handle_not_annotated(&mut self) { + if !self.not_annotated.is_empty() { + log!(self, "not annotated"); + self.result.errors.push_str(&self.not_annotated); + } + } + + /// Handle notes that weren't handled before. + fn handle_not_emitted(&mut self) { + let mut first = true; + for (note, &seen) in self.test.notes.iter().zip(&self.seen) { + if seen { + continue; + } + let note_range = self.format_range(¬e.range); + if first { + log!(self, "not emitted"); + first = false; + } + log!(self, " {}: {note_range} {} ({})", note.kind, note.message, note.pos,); + } + } + + /// Check that the document output is correct. + fn check_document(&mut self, document: Option<&Document>) { + let live_path = format!("{}/render/{}.png", crate::STORE_PATH, self.test.name); + let ref_path = format!("{}/{}.png", crate::REF_PATH, self.test.name); + let has_ref = Path::new(&ref_path).exists(); + + let Some(document) = document else { + if has_ref { + log!(self, "missing document"); + log!(self, " ref | {ref_path}"); + } + return; + }; + + let skippable = match document.pages.as_slice() { + [page] => skippable(page), + _ => false, + }; + + // Tests without visible output and no reference image don't need to be + // compared. + if skippable && !has_ref { + std::fs::remove_file(&live_path).ok(); + return; + } + + // Render the live version. + let pixmap = render(document, 1.0); + + // Save live version, possibly rerendering if different scale is + // requested. + let mut pixmap_live = &pixmap; + let slot; + let scale = crate::ARGS.scale; + if scale != 1.0 { + slot = render(document, scale); + pixmap_live = &slot; + } + let data = pixmap_live.encode_png().unwrap(); + std::fs::write(&live_path, data).unwrap(); + + // Write PDF if requested. + if crate::ARGS.pdf() { + let pdf_path = format!("{}/pdf/{}.pdf", crate::STORE_PATH, self.test.name); + let pdf = typst_pdf::pdf(document, Smart::Auto, None); + std::fs::write(pdf_path, pdf).unwrap(); + } + + // Write SVG if requested. + if crate::ARGS.svg() { + let svg_path = format!("{}/svg/{}.svg", crate::STORE_PATH, self.test.name); + let svg = typst_svg::svg_merged(document, Abs::pt(5.0)); + std::fs::write(svg_path, svg).unwrap(); + } + + // Compare against reference image if available. + let equal = has_ref && { + let ref_data = std::fs::read(&ref_path).unwrap(); + let ref_pixmap = sk::Pixmap::decode_png(&ref_data).unwrap(); + approx_equal(&pixmap, &ref_pixmap) + }; + + // Test that is ok doesn't need to be updated. + if equal { + return; + } + + if crate::ARGS.update { + if skippable { + std::fs::remove_file(&ref_path).unwrap(); + log!( + into: self.result.infos, + "removed reference image ({ref_path})" + ); + } else { + let opts = oxipng::Options::max_compression(); + let data = pixmap.encode_png().unwrap(); + let ref_data = oxipng::optimize_from_memory(&data, &opts).unwrap(); + if !self.test.large && ref_data.len() > crate::REF_LIMIT { + log!(self, "reference image would exceed maximum size"); + log!(self, " maximum | {}", FileSize(crate::REF_LIMIT)); + log!(self, " size | {}", FileSize(ref_data.len())); + log!(self, "please try to minimize the size of the test (smaller pages, less text, etc.)"); + log!(self, "if you think the test cannot be reasonably minimized, mark it as `// LARGE`"); + return; + } + std::fs::write(&ref_path, &ref_data).unwrap(); + log!( + into: self.result.infos, + "Updated reference image ({ref_path}, {})", + FileSize(ref_data.len()), + ); + } + } else { + self.result.mismatched_image = true; + if has_ref { + log!(self, "mismatched rendering"); + log!(self, " live | {live_path}"); + log!(self, " ref | {ref_path}"); + } else { + log!(self, "missing reference image"); + log!(self, " live | {live_path}"); + } + } + } + + /// Compare a subset of notes with a given kind against diagnostics of + /// that same kind. + fn check_diagnostic(&mut self, kind: NoteKind, diag: &SourceDiagnostic) { + // Ignore diagnostics from other sources than the test file itself. + if diag.span.id().is_some_and(|id| id != self.test.source.id()) { + return; + } + + let message = diag.message.replace("\\", "/"); + let range = self.world.range(diag.span); + self.validate_note(kind, range.clone(), &message); + + // Check hints. + for hint in &diag.hints { + self.validate_note(NoteKind::Hint, range.clone(), hint); + } + } + + /// Try to find a matching note for the given `kind`, `range`, and + /// `message`. + /// + /// - If found, marks it as seen and returns it. + /// - If none was found, emits a "Not annotated" error and returns nothing. + fn validate_note( + &mut self, + kind: NoteKind, + range: Option>, + message: &str, + ) { + // Try to find perfect match. + if let Some((i, _)) = self.test.notes.iter().enumerate().find(|&(i, note)| { + !self.seen[i] + && note.kind == kind + && note.range == range + && note.message == message + }) { + self.seen[i] = true; + return; + } + + // Try to find closely matching annotation. If the note has the same + // range or message, it's most likely the one we're interested in. + let Some((i, note)) = self.test.notes.iter().enumerate().find(|&(i, note)| { + !self.seen[i] + && note.kind == kind + && (note.range == range || note.message == message) + }) else { + // Not even a close match, diagnostic is not annotated. + let diag_range = self.format_range(&range); + log!(into: self.not_annotated, " {kind}: {diag_range} {}", message); + return; + }; + + // Mark this annotation as visited and return it. + self.seen[i] = true; + + // Range is wrong. + if range != note.range { + let note_range = self.format_range(¬e.range); + let note_text = self.text_for_range(¬e.range); + let diag_range = self.format_range(&range); + let diag_text = self.text_for_range(&range); + log!(self, "mismatched range ({}):", note.pos); + log!(self, " message | {}", note.message); + log!(self, " annotated | {note_range:<9} | {note_text}"); + log!(self, " emitted | {diag_range:<9} | {diag_text}"); + } + + // Message is wrong. + if message != note.message { + log!(self, "mismatched message ({}):", note.pos); + log!(self, " annotated | {}", note.message); + log!(self, " emitted | {message}"); + } + } + + /// Display the text for a range. + fn text_for_range(&self, range: &Option>) -> String { + let Some(range) = range else { return "No text".into() }; + if range.is_empty() { + "(empty)".into() + } else { + format!("`{}`", self.test.source.text()[range.clone()].replace('\n', "\\n")) + } + } + + /// Display a byte range as a line:column range. + fn format_range(&self, range: &Option>) -> String { + let Some(range) = range else { return "No range".into() }; + if range.start == range.end { + self.format_pos(range.start) + } else { + format!("{}-{}", self.format_pos(range.start,), self.format_pos(range.end,)) + } + } + + /// Display a position as a line:column pair. + fn format_pos(&self, pos: usize) -> String { + if let (Some(line_idx), Some(column_idx)) = + (self.test.source.byte_to_line(pos), self.test.source.byte_to_column(pos)) + { + let line = self.test.pos.line + line_idx; + let column = column_idx + 1; + if line == 1 { + format!("{column}") + } else { + format!("{line}:{column}") + } + } else { + "oob".into() + } + } +} + +/// Draw all frames into one image with padding in between. +fn render(document: &Document, pixel_per_pt: f32) -> sk::Pixmap { + for page in &document.pages { + let limit = Abs::cm(100.0); + if page.frame.width() > limit || page.frame.height() > limit { + panic!("overlarge frame: {:?}", page.frame.size()); + } + } + + let gap = Abs::pt(1.0); + let mut pixmap = typst_render::render_merged( + document, + pixel_per_pt, + Color::WHITE, + gap, + Color::BLACK, + ); + + let gap = (pixel_per_pt * gap.to_pt() as f32).round(); + + let mut y = 0.0; + for page in &document.pages { + let ts = + sk::Transform::from_scale(pixel_per_pt, pixel_per_pt).post_translate(0.0, y); + render_links(&mut pixmap, ts, &page.frame); + y += (pixel_per_pt * page.frame.height().to_pt() as f32).round().max(1.0) + gap; + } + + pixmap +} + +/// Draw extra boxes for links so we can see whether they are there. +fn render_links(canvas: &mut sk::Pixmap, ts: sk::Transform, frame: &Frame) { + for (pos, item) in frame.items() { + let ts = ts.pre_translate(pos.x.to_pt() as f32, pos.y.to_pt() as f32); + match *item { + FrameItem::Group(ref group) => { + let ts = ts.pre_concat(to_sk_transform(&group.transform)); + render_links(canvas, ts, &group.frame); + } + FrameItem::Meta(Meta::Link(_), size) => { + let w = size.x.to_pt() as f32; + let h = size.y.to_pt() as f32; + let rect = sk::Rect::from_xywh(0.0, 0.0, w, h).unwrap(); + let mut paint = sk::Paint::default(); + paint.set_color_rgba8(40, 54, 99, 40); + canvas.fill_rect(rect, &paint, ts, None); + } + _ => {} + } + } +} + +/// Whether rendering of a frame can be skipped. +fn skippable(page: &Page) -> bool { + page.frame.width().approx_eq(Abs::pt(120.0)) + && page.frame.height().approx_eq(Abs::pt(20.0)) + && skippable_frame(&page.frame) +} + +/// Whether rendering of a frame can be skipped. +fn skippable_frame(frame: &Frame) -> bool { + frame.items().all(|(_, item)| match item { + FrameItem::Group(group) => skippable_frame(&group.frame), + FrameItem::Meta(..) => true, + _ => false, + }) +} + +/// Whether to pixel images are approximately equal. +fn approx_equal(a: &sk::Pixmap, b: &sk::Pixmap) -> bool { + a.width() == b.width() + && a.height() == b.height() + && a.data().iter().zip(b.data()).all(|(&a, &b)| a.abs_diff(b) <= 1) +} + +/// Convert a Typst transform to a tiny-skia transform. +fn to_sk_transform(transform: &Transform) -> sk::Transform { + let Transform { sx, ky, kx, sy, tx, ty } = *transform; + sk::Transform::from_row( + sx.get() as _, + ky.get() as _, + kx.get() as _, + sy.get() as _, + tx.to_pt() as f32, + ty.to_pt() as f32, + ) +} diff --git a/tests/src/tests.rs b/tests/src/tests.rs index e4f60bb656..6d58e969e9 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -1,1127 +1,112 @@ -/*! This is Typst's test runner. +//! Typst's test runner. -Tests are Typst files composed of a header part followed by subtests. +mod args; +mod collect; +mod logger; +mod run; +mod world; -The header may contain: -- a small description `// tests that features X works well` -- metadata (see [metadata::TestConfiguration]) - -The subtests may use extra testing functions defined in [library], most -importantly, `test(x, y)` which will fail the test `if x != y`. -*/ - -#![allow(clippy::comparison_chain)] -mod metadata; - -use self::metadata::*; - -use std::borrow::Cow; -use std::collections::{HashMap, HashSet}; -use std::ffi::OsStr; -use std::fmt::Write as _; -use std::io::{self, IsTerminal, Write as _}; -use std::ops::Range; -use std::path::{Path, PathBuf, MAIN_SEPARATOR_STR}; -use std::sync::{OnceLock, RwLock}; -use std::{env, fs}; +use std::path::Path; +use std::time::Duration; use clap::Parser; -use comemo::{Prehashed, Track}; -use oxipng::{InFile, Options, OutFile}; +use once_cell::sync::Lazy; +use parking_lot::Mutex; use rayon::iter::{ParallelBridge, ParallelIterator}; -use tiny_skia as sk; -use typst::diag::{bail, FileError, FileResult, Severity, SourceDiagnostic, StrResult}; -use typst::eval::Tracer; -use typst::foundations::{func, Bytes, Datetime, NoneValue, Repr, Smart, Value}; -use typst::introspection::Meta; -use typst::layout::{Abs, Frame, FrameItem, Margin, Page, PageElem, Transform}; -use typst::model::Document; -use typst::syntax::{FileId, Source, SyntaxNode, VirtualPath}; -use typst::text::{Font, FontBook, TextElem, TextSize}; -use typst::visualize::Color; -use typst::{Library, World, WorldExt}; -use walkdir::WalkDir; -// These directories are all relative to the tests/ directory. -const TYP_DIR: &str = "typ"; -const REF_DIR: &str = "ref"; -const PNG_DIR: &str = "png"; -const PDF_DIR: &str = "pdf"; -const SVG_DIR: &str = "svg"; +use crate::args::{CliArguments, Command}; +use crate::logger::Logger; -/// Arguments that modify test behaviour. -/// -/// Specify them like this when developing: -/// `cargo test --workspace --test tests -- --help` -#[derive(Debug, Clone, Parser)] -#[clap(name = "typst-test", author)] -struct Args { - /// All the tests that contains a filter string will be run (unless - /// `--exact` is specified, which is even stricter). - filter: Vec, - /// Runs only the specified subtest. - #[arg(short, long)] - #[arg(allow_hyphen_values = true)] - subtest: Option, - /// Runs only the test with the exact name specified in your command. - /// - /// Example: - /// `cargo test --workspace --test tests -- compiler/bytes.typ --exact` - #[arg(long)] - exact: bool, - /// Updates the reference images in `tests/ref`. - #[arg(long, default_value_t = env::var_os("UPDATE_EXPECT").is_some())] - update: bool, - /// Exports the tests as PDF into `tests/pdf`. - #[arg(long)] - pdf: bool, - /// Configuration of what to print. - #[command(flatten)] - print: PrintConfig, - /// Running `cargo test --workspace -- --nocapture` for the unit tests would - /// fail the test runner without argument. - // TODO: would it really still happen? - #[arg(long)] - nocapture: bool, - /// Prevents the terminal from being cleared of test names and includes - /// non-essential test messages. - #[arg(short, long)] - verbose: bool, -} +/// The parsed command line arguments. +static ARGS: Lazy = Lazy::new(CliArguments::parse); -/// Which things to print out for debugging. -#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Parser)] -struct PrintConfig { - /// Print the syntax tree. - #[arg(long)] - syntax: bool, - /// Print the content model. - #[arg(long)] - model: bool, - /// Print the layouted frames. - #[arg(long)] - frames: bool, -} +/// The directory where the test suite is located. +const SUITE_PATH: &str = "tests/suite"; -impl Args { - fn matches(&self, canonicalized_path: &Path) -> bool { - let path = canonicalized_path.to_string_lossy(); - if !self.exact { - return self.filter.is_empty() - || self.filter.iter().any(|v| path.contains(v)); - } +/// The directory where the full test results are stored. +const STORE_PATH: &str = "tests/store"; - self.filter.iter().any(|v| match path.strip_suffix(v) { - None => false, - Some(residual) => { - residual.is_empty() || residual.ends_with(MAIN_SEPARATOR_STR) - } - }) - } -} - -/// Tests all test files and prints a summary. -fn main() { - let args = Args::parse(); - - // Create loader and context. - let world = TestWorld::new(args.print); - - println!("Running tests..."); - let results = WalkDir::new(TYP_DIR) - .sort_by_file_name() - .into_iter() - .par_bridge() - .filter_map(|entry| { - let entry = entry.unwrap(); - if entry.depth() == 0 { - return None; - } +/// The directory where the reference images are stored. +const REF_PATH: &str = "tests/ref"; - if entry.path().starts_with("typ/benches") { - return None; - } - - let src_path = entry.into_path(); // Relative to TYP_DIR. - if src_path.extension() != Some(OsStr::new("typ")) { - return None; - } - - if args.matches(&src_path.canonicalize().unwrap()) { - Some(src_path) - } else { - None - } - }) - .map_with(world, |world, src_path| { - let path = src_path.strip_prefix(TYP_DIR).unwrap(); - let png_path = Path::new(PNG_DIR).join(path).with_extension("png"); - let ref_path = Path::new(REF_DIR).join(path).with_extension("png"); - let svg_path = Path::new(SVG_DIR).join(path).with_extension("svg"); - let pdf_path = - args.pdf.then(|| Path::new(PDF_DIR).join(path).with_extension("pdf")); - - test( - world, - &src_path, - &png_path, - &ref_path, - pdf_path.as_deref(), - &svg_path, - &args, - ) as usize - }) - .collect::>(); - - let len = results.len(); - let ok = results.iter().sum::(); - if len > 0 { - println!("{ok} / {len} test{} passed.", if len > 1 { "s" } else { "" }); - } else { - println!("No test ran."); - } - - if ok != len { - println!( - "Set the UPDATE_EXPECT environment variable or pass the \ - --update flag to update the reference image(s)." - ); - } - - if ok < len { - std::process::exit(1); - } -} +/// The maximum size of reference images that aren't marked as `// LARGE`. +const REF_LIMIT: usize = 20 * 1024; -fn library() -> Library { - #[func] - fn test(lhs: Value, rhs: Value) -> StrResult { - if lhs != rhs { - bail!("Assertion failed: {} != {}", lhs.repr(), rhs.repr()); - } - Ok(NoneValue) - } - - #[func] - fn test_repr(lhs: Value, rhs: Value) -> StrResult { - if lhs.repr() != rhs.repr() { - bail!("Assertion failed: {} != {}", lhs.repr(), rhs.repr()); - } - Ok(NoneValue) - } - - #[func] - fn print(#[variadic] values: Vec) -> NoneValue { - let mut stdout = io::stdout().lock(); - write!(stdout, "> ").unwrap(); - for (i, value) in values.into_iter().enumerate() { - if i > 0 { - write!(stdout, ", ").unwrap(); - } - write!(stdout, "{value:?}").unwrap(); - } - writeln!(stdout).unwrap(); - NoneValue - } - - // Set page width to 120pt with 10pt margins, so that the inner page is - // exactly 100pt wide. Page height is unbounded and font size is 10pt so - // that it multiplies to nice round numbers. - let mut lib = Library::default(); - lib.styles - .set(PageElem::set_width(Smart::Custom(Abs::pt(120.0).into()))); - lib.styles.set(PageElem::set_height(Smart::Auto)); - lib.styles.set(PageElem::set_margin(Margin::splat(Some(Smart::Custom( - Abs::pt(10.0).into(), - ))))); - lib.styles.set(TextElem::set_size(TextSize(Abs::pt(10.0).into()))); - - // Hook up helpers into the global scope. - lib.global.scope_mut().define_func::(); - lib.global.scope_mut().define_func::(); - lib.global.scope_mut().define_func::(); - lib.global - .scope_mut() - .define("conifer", Color::from_u8(0x9f, 0xEB, 0x52, 0xFF)); - lib.global - .scope_mut() - .define("forest", Color::from_u8(0x43, 0xA1, 0x27, 0xFF)); - - lib -} - -/// A world that provides access to the tests environment. -struct TestWorld { - print: PrintConfig, - main: FileId, - library: Prehashed, - book: Prehashed, - fonts: Vec, - slots: RwLock>, -} - -#[derive(Clone)] -struct FileSlot { - source: OnceLock>, - buffer: OnceLock>, -} - -impl TestWorld { - fn new(print: PrintConfig) -> Self { - let fonts: Vec<_> = typst_assets::fonts() - .chain(typst_dev_assets::fonts()) - .flat_map(|data| Font::iter(Bytes::from_static(data))) - .collect(); - - Self { - print, - main: FileId::new(None, VirtualPath::new("main.typ")), - library: Prehashed::new(library()), - book: Prehashed::new(FontBook::from_fonts(&fonts)), - fonts, - slots: RwLock::new(HashMap::new()), - } - } -} - -impl World for TestWorld { - fn library(&self) -> &Prehashed { - &self.library - } - - fn book(&self) -> &Prehashed { - &self.book - } - - fn main(&self) -> Source { - self.source(self.main).unwrap() - } - - fn source(&self, id: FileId) -> FileResult { - self.slot(id, |slot| { - slot.source - .get_or_init(|| { - let buf = read(&system_path(id)?)?; - let text = String::from_utf8(buf.into_owned())?; - Ok(Source::new(id, text)) - }) - .clone() - }) - } - - fn file(&self, id: FileId) -> FileResult { - self.slot(id, |slot| { - slot.buffer - .get_or_init(|| { - read(&system_path(id)?).map(|cow| match cow { - Cow::Owned(buf) => buf.into(), - Cow::Borrowed(buf) => Bytes::from_static(buf), - }) - }) - .clone() - }) - } - - fn font(&self, id: usize) -> Option { - Some(self.fonts[id].clone()) - } - - fn today(&self, _: Option) -> Option { - Some(Datetime::from_ymd(1970, 1, 1).unwrap()) - } -} - -impl TestWorld { - fn set(&mut self, path: &Path, text: String) -> Source { - self.main = FileId::new(None, VirtualPath::new(path)); - let source = Source::new(self.main, text); - self.slot(self.main, |slot| { - slot.source = OnceLock::from(Ok(source.clone())); - source - }) - } - - fn slot(&self, id: FileId, f: F) -> T - where - F: FnOnce(&mut FileSlot) -> T, - { - f(self.slots.write().unwrap().entry(id).or_insert_with(|| FileSlot { - source: OnceLock::new(), - buffer: OnceLock::new(), - })) - } -} +fn main() { + setup(); -impl Clone for TestWorld { - fn clone(&self) -> Self { - Self { - print: self.print, - main: self.main, - library: self.library.clone(), - book: self.book.clone(), - fonts: self.fonts.clone(), - slots: RwLock::new(self.slots.read().unwrap().clone()), - } + match &ARGS.command { + None => test(), + Some(Command::Clean) => std::fs::remove_dir_all(STORE_PATH).unwrap(), } } -/// The file system path for a file ID. -fn system_path(id: FileId) -> FileResult { - let root: PathBuf = match id.package() { - Some(spec) => format!("packages/{}-{}", spec.name, spec.version).into(), - None => PathBuf::new(), - }; - - id.vpath().resolve(&root).ok_or(FileError::AccessDenied) -} - -/// Read a file. -fn read(path: &Path) -> FileResult> { - // Basically symlinks `assets/files` to `tests/files` so that the assets - // are within the test project root. - let resolved = path.to_path_buf(); - if let Ok(suffix) = path.strip_prefix("assets/") { - return typst_dev_assets::get(&suffix.to_string_lossy()) - .map(Cow::Borrowed) - .ok_or_else(|| FileError::NotFound(path.into())); - } - - let f = |e| FileError::from_io(e, path); - if fs::metadata(&resolved).map_err(f)?.is_dir() { - Err(FileError::IsDirectory) - } else { - fs::read(&resolved).map(Cow::Owned).map_err(f) - } -} +fn setup() { + // Make all paths relative to the workspace. That's nicer for IDEs when + // clicking on paths printed to the terminal. + std::env::set_current_dir("..").unwrap(); -/// Tests a test file and prints the result. -/// -/// Also tests that the header of each test is written correctly. -/// See [parse_part_metadata] for more details. -fn test( - world: &mut TestWorld, - src_path: &Path, - png_path: &Path, - ref_path: &Path, - pdf_path: Option<&Path>, - svg_path: &Path, - args: &Args, -) -> bool { - struct PanicGuard<'a>(&'a Path); - impl Drop for PanicGuard<'_> { - fn drop(&mut self) { - if std::thread::panicking() { - println!("Panicked in {}", self.0.display()); - } - } + // Create the storage. + for ext in ["render", "pdf", "svg"] { + std::fs::create_dir_all(Path::new(STORE_PATH).join(ext)).unwrap(); } - let name = src_path.strip_prefix(TYP_DIR).unwrap_or(src_path); - let text = fs::read_to_string(src_path).unwrap(); - let _guard = PanicGuard(name); - - let mut output = String::new(); - let mut ok = true; - let mut updated = false; - let mut pages = vec![]; - let mut line = 0; - let mut header_configuration = None; - let mut compare_ever = false; - let mut rng = LinearShift::new(); - - let parts: Vec<_> = text - .split("\n---") - .map(|s| s.strip_suffix('\r').unwrap_or(s)) - .collect(); - - for (i, &part) in parts.iter().enumerate() { - if let Some(x) = args.subtest { - let x = usize::try_from( - x.rem_euclid(isize::try_from(parts.len()).unwrap_or_default()), - ) + // Set up the thread pool. + if let Some(num_threads) = ARGS.num_threads { + rayon::ThreadPoolBuilder::new() + .num_threads(num_threads) + .build_global() .unwrap(); - if x != i { - writeln!(output, " Skipped subtest {i}.").unwrap(); - continue; - } - } - let is_header = i == 0 - && parts.len() > 1 - && part - .lines() - .all(|s| s.starts_with("//") || s.chars().all(|c| c.is_whitespace())); - - if is_header { - let source = Source::detached(part.to_string()); - let metadata = parse_part_metadata(&source, true); - match metadata { - Ok(metadata) => { - header_configuration = Some(metadata.config); - } - Err(invalid_data) => { - ok = false; - writeln!( - output, - " Test {}: invalid metadata in header, failing the test:", - name.display() - ) - .unwrap(); - InvalidMetadata::write( - invalid_data, - &mut output, - &mut |annotation, output| { - print_annotation(output, &source, line, annotation) - }, - ); - } - } - } else { - let (part_ok, compare_here, part_frames) = test_part( - &mut output, - world, - src_path, - part.into(), - line, - i, - header_configuration.as_ref().unwrap_or(&Default::default()), - &mut rng, - args.verbose, - ); - - ok &= part_ok; - compare_ever |= compare_here; - pages.extend(part_frames); - } - - line += part.lines().count() + 1; - } - - let document = Document { pages, ..Default::default() }; - if compare_ever { - if let Some(pdf_path) = pdf_path { - let pdf_data = typst_pdf::pdf( - &document, - Smart::Custom(&format!("typst-test: {}", name.display())), - world.today(Some(0)), - ); - fs::create_dir_all(pdf_path.parent().unwrap()).unwrap(); - fs::write(pdf_path, pdf_data).unwrap(); - } - - if world.print.frames { - for frame in &document.pages { - writeln!(output, "{frame:#?}\n").unwrap(); - } - } - - let canvas = render(&document); - fs::create_dir_all(png_path.parent().unwrap()).unwrap(); - canvas.save_png(png_path).unwrap(); - - let svg = typst_svg::svg_merged(&document, Abs::pt(5.0)); - - fs::create_dir_all(svg_path.parent().unwrap()).unwrap(); - std::fs::write(svg_path, svg.as_bytes()).unwrap(); - - if let Ok(ref_pixmap) = sk::Pixmap::load_png(ref_path) { - if canvas.width() != ref_pixmap.width() - || canvas.height() != ref_pixmap.height() - || canvas - .data() - .iter() - .zip(ref_pixmap.data()) - .any(|(&a, &b)| a.abs_diff(b) > 2) - { - if args.update { - update_image(png_path, ref_path); - updated = true; - } else { - writeln!(output, " Does not match reference image.").unwrap(); - ok = false; - } - } - } else if !document.pages.is_empty() { - if args.update { - update_image(png_path, ref_path); - updated = true; - } else { - writeln!(output, " Failed to open reference image.").unwrap(); - ok = false; - } - } } - - { - let mut stdout = io::stdout().lock(); - stdout.write_all(name.to_string_lossy().as_bytes()).unwrap(); - if ok { - writeln!(stdout, " ✔").unwrap(); - // Don't clear the line when in verbose mode or when the reference image - // was updated, to show in the output which test had its image updated. - if !updated && !args.verbose && stdout.is_terminal() { - // ANSI escape codes: cursor moves up and clears the line. - write!(stdout, "\x1b[1A\x1b[2K").unwrap(); - } - } else { - writeln!(stdout, " ❌").unwrap(); - } - if updated { - writeln!(stdout, " Updated reference image.").unwrap(); - } - if !output.is_empty() { - stdout.write_all(output.as_bytes()).unwrap(); - } - } - - ok } -fn update_image(png_path: &Path, ref_path: &Path) { - oxipng::optimize( - &InFile::Path(png_path.to_owned()), - &OutFile::from_path(ref_path.to_owned()), - &Options::max_compression(), - ) - .unwrap(); -} - -#[allow(clippy::too_many_arguments)] -fn test_part( - output: &mut String, - world: &mut TestWorld, - src_path: &Path, - text: String, - line: usize, - i: usize, - header_configuration: &TestConfig, - rng: &mut LinearShift, - verbose: bool, -) -> (bool, bool, Vec) { - let source = world.set(src_path, text); - if world.print.syntax { - writeln!(output, "Syntax Tree:\n{:#?}\n", source.root()).unwrap(); - } - - if world.print.model { - print_model(world, &source, output); - } - - let mut tracer = Tracer::new(); - let (mut frames, diagnostics) = match typst::compile(world, &mut tracer) { - Ok(document) => (document.pages, tracer.warnings()), +fn test() { + let (tests, skipped) = match crate::collect::collect() { + Ok(output) => output, Err(errors) => { - let mut warnings = tracer.warnings(); - warnings.extend(errors); - (vec![], warnings) - } - }; - - let metadata = parse_part_metadata(&source, false); - match metadata { - Ok(metadata) => { - let mut ok = true; - let compare_ref = metadata - .config - .compare_ref - .unwrap_or(header_configuration.compare_ref.unwrap_or(true)); - let validate_hints = metadata - .config - .validate_hints - .unwrap_or(header_configuration.validate_hints.unwrap_or(true)); - let validate_autocomplete = metadata - .config - .validate_autocomplete - .unwrap_or(header_configuration.validate_autocomplete.unwrap_or(false)); - - if verbose { - writeln!(output, "Subtest {i} runs with compare_ref={compare_ref}; validate_hints={validate_hints}; validate_autocomplete={validate_autocomplete};").unwrap(); - } - ok &= test_spans(output, source.root()); - ok &= test_reparse(output, source.text(), i, rng); - - // Don't retain frames if we don't want to compare with reference images. - if !compare_ref { - frames.clear(); - } - - // we never check autocomplete and error at the same time - - let diagnostic_annotations = metadata - .annotations - .iter() - .filter(|a| { - !matches!( - a.kind, - AnnotationKind::AutocompleteContains - | AnnotationKind::AutocompleteExcludes - ) - }) - .cloned() - .collect::>(); - - if validate_autocomplete { - // warns and ignores diagnostics - if !diagnostic_annotations.is_empty() { - writeln!( - output, - " Subtest {i} contains diagnostics but is in autocomplete mode." - ) - .unwrap(); - for annotation in diagnostic_annotations { - write!(output, " Ignored | ").unwrap(); - print_annotation(output, &source, line, &annotation); - } - } - - test_autocomplete( - output, - world, - &source, - line, - i, - &mut ok, - metadata.annotations.iter(), - ); - } else { - test_diagnostics( - output, - world, - &source, - line, - i, - &mut ok, - validate_hints, - diagnostics.iter(), - &diagnostic_annotations, - ); - } - - (ok, compare_ref, frames) - } - Err(invalid_data) => { - writeln!(output, " Subtest {i} has invalid metadata, failing the test:") - .unwrap(); - InvalidMetadata::write( - invalid_data, - output, - &mut |annotation: &Annotation, output: &mut String| { - print_annotation(output, &source, line, annotation) - }, - ); - - (false, false, frames) - } - } -} - -#[allow(clippy::too_many_arguments)] -fn test_autocomplete<'a>( - output: &mut String, - world: &mut TestWorld, - source: &Source, - line: usize, - i: usize, - ok: &mut bool, - annotations: impl Iterator, -) { - for annotation in annotations.filter(|a| { - matches!( - a.kind, - AnnotationKind::AutocompleteContains | AnnotationKind::AutocompleteExcludes - ) - }) { - // Ok cause we checked in parsing that range was Some for this annotation - let cursor = annotation.range.as_ref().unwrap().start; - - // todo, use document if is_some to test labels autocomplete - let completions = typst_ide::autocomplete(world, None, source, cursor, true) - .map(|(_, c)| c) - .unwrap_or_default() - .into_iter() - .map(|c| c.label.to_string()) - .collect::>(); - let completions = - completions.iter().map(|s| s.as_str()).collect::>(); - - let must_contain_or_exclude = parse_string_list(&annotation.text); - let missing = - must_contain_or_exclude.difference(&completions).collect::>(); - - if !missing.is_empty() - && matches!(annotation.kind, AnnotationKind::AutocompleteContains) - { - writeln!(output, " Subtest {i} does not match expected completions.") - .unwrap(); - write!(output, " for annotation | ").unwrap(); - print_annotation(output, source, line, annotation); - - write!(output, " Not contained // ").unwrap(); - for item in missing { - write!(output, "{item:?}, ").unwrap() + eprintln!("failed to collect tests"); + for error in errors { + eprintln!("❌ {error}"); } - writeln!(output).unwrap(); - *ok = false; + std::process::exit(1); } - - let undesired = - must_contain_or_exclude.intersection(&completions).collect::>(); - - if !undesired.is_empty() - && matches!(annotation.kind, AnnotationKind::AutocompleteExcludes) - { - writeln!(output, " Subtest {i} does not match expected completions.") - .unwrap(); - write!(output, " for annotation | ").unwrap(); - print_annotation(output, source, line, annotation); - - write!(output, " Not excluded // ").unwrap(); - for item in undesired { - write!(output, "{item:?}, ").unwrap() - } - writeln!(output).unwrap(); - *ok = false; - } - } -} - -#[allow(clippy::too_many_arguments)] -fn test_diagnostics<'a>( - output: &mut String, - world: &mut TestWorld, - source: &Source, - line: usize, - i: usize, - ok: &mut bool, - validate_hints: bool, - diagnostics: impl Iterator, - diagnostic_annotations: &HashSet, -) { - // Map diagnostics to range and message format, discard traces and errors from - // other files, collect hints. - // - // This has one caveat: due to the format of the expected hints, we can not - // verify if a hint belongs to a diagnostic or not. That should be irrelevant - // however, as the line of the hint is still verified. - let mut actual_diagnostics = HashSet::new(); - for diagnostic in diagnostics { - // Ignore diagnostics from other files. - if diagnostic.span.id().is_some_and(|id| id != source.id()) { - continue; - } - - let annotation = Annotation { - kind: match diagnostic.severity { - Severity::Error => AnnotationKind::Error, - Severity::Warning => AnnotationKind::Warning, - }, - range: world.range(diagnostic.span), - text: diagnostic.message.replace("\\", "/"), - }; - - if validate_hints { - for hint in &diagnostic.hints { - actual_diagnostics.insert(Annotation { - kind: AnnotationKind::Hint, - text: hint.clone(), - range: annotation.range.clone(), - }); - } - } - - actual_diagnostics.insert(annotation); - } - - // Basically symmetric_difference, but we need to know where an item is coming from. - let mut unexpected_outputs = actual_diagnostics - .difference(diagnostic_annotations) - .collect::>(); - let mut missing_outputs = diagnostic_annotations - .difference(&actual_diagnostics) - .collect::>(); - - unexpected_outputs.sort_by_key(|&v| v.range.as_ref().map(|r| r.start)); - missing_outputs.sort_by_key(|&v| v.range.as_ref().map(|r| r.start)); - - // This prints all unexpected emits first, then all missing emits. - // Is this reasonable or subject to change? - if !(unexpected_outputs.is_empty() && missing_outputs.is_empty()) { - writeln!(output, " Subtest {i} does not match expected errors.").unwrap(); - *ok = false; - - for unexpected in unexpected_outputs { - write!(output, " Not annotated // ").unwrap(); - print_annotation(output, source, line, unexpected) - } - - for missing in missing_outputs { - write!(output, " Not emitted // ").unwrap(); - print_annotation(output, source, line, missing) - } - } -} - -fn print_model(world: &mut TestWorld, source: &Source, output: &mut String) { - let world = (world as &dyn World).track(); - let route = typst::engine::Route::default(); - let mut tracer = typst::eval::Tracer::new(); - - let module = - typst::eval::eval(world, route.track(), tracer.track_mut(), source).unwrap(); - writeln!(output, "Model:\n{:#?}\n", module.content()).unwrap(); -} - -fn print_annotation( - output: &mut String, - source: &Source, - line: usize, - annotation: &Annotation, -) { - let Annotation { range, text, kind } = annotation; - write!(output, "{kind}: ").unwrap(); - if let Some(range) = range { - let start_line = 1 + line + source.byte_to_line(range.start).unwrap(); - let start_col = 1 + source.byte_to_column(range.start).unwrap(); - let end_line = 1 + line + source.byte_to_line(range.end).unwrap(); - let end_col = 1 + source.byte_to_column(range.end).unwrap(); - write!(output, "{start_line}:{start_col}-{end_line}:{end_col} ").unwrap(); - } - writeln!(output, "{text}").unwrap(); -} - -/// Pseudorandomly edit the source file and test whether a reparse produces the -/// same result as a clean parse. -/// -/// The method will first inject 10 strings once every 400 source characters -/// and then select 5 leaf node boundaries to inject an additional, randomly -/// chosen string from the injection list. -fn test_reparse( - output: &mut String, - text: &str, - i: usize, - rng: &mut LinearShift, -) -> bool { - let supplements = [ - "[", - "]", - "{", - "}", - "(", - ")", - "#rect()", - "a word", - ", a: 1", - "10.0", - ":", - "if i == 0 {true}", - "for", - "* hello *", - "//", - "/*", - "\\u{12e4}", - "```typst", - " ", - "trees", - "\\", - "$ a $", - "2.", - "-", - "5", - ]; - - let mut ok = true; - let mut apply = |replace: Range, with| { - let mut incr_source = Source::detached(text); - if incr_source.root().len() != text.len() { - println!( - " Subtest {i} tree length {} does not match string length {} ❌", - incr_source.root().len(), - text.len(), - ); - return false; - } - - incr_source.edit(replace.clone(), with); - - let edited_src = incr_source.text(); - let ref_source = Source::detached(edited_src); - let ref_root = ref_source.root(); - let incr_root = incr_source.root(); - - // Ensures that the span numbering invariants hold. - let spans_ok = test_spans(output, ref_root) && test_spans(output, incr_root); - - // Ensure that the reference and incremental trees are the same. - let tree_ok = ref_root.spanless_eq(incr_root); - - if !tree_ok { - writeln!( - output, - " Subtest {i} reparse differs from clean parse when inserting '{with}' at {}-{} ❌\n", - replace.start, replace.end, - ).unwrap(); - writeln!(output, " Expected reference tree:\n{ref_root:#?}\n").unwrap(); - writeln!(output, " Found incremental tree:\n{incr_root:#?}").unwrap(); - writeln!( - output, - " Full source ({}):\n\"{edited_src:?}\"", - edited_src.len() - ) - .unwrap(); - } - - spans_ok && tree_ok - }; - - let mut pick = |range: Range| { - let ratio = rng.next(); - (range.start as f64 + ratio * (range.end - range.start) as f64).floor() as usize }; - let insertions = (text.len() as f64 / 400.0).ceil() as usize; - for _ in 0..insertions { - let supplement = supplements[pick(0..supplements.len())]; - let start = pick(0..text.len()); - let end = pick(start..text.len()); - - if !text.is_char_boundary(start) || !text.is_char_boundary(end) { - continue; - } - - ok &= apply(start..end, supplement); - } - - let source = Source::detached(text); - let leafs = leafs(source.root()); - let start = source.find(leafs[pick(0..leafs.len())].span()).unwrap().offset(); - let supplement = supplements[pick(0..supplements.len())]; - ok &= apply(start..start, supplement); - - ok -} - -/// Returns all leaf descendants of a node (may include itself). -fn leafs(node: &SyntaxNode) -> Vec { - if node.children().len() == 0 { - vec![node.clone()] - } else { - node.children().flat_map(leafs).collect() - } -} - -/// Ensure that all spans are properly ordered (and therefore unique). -#[track_caller] -fn test_spans(output: &mut String, root: &SyntaxNode) -> bool { - test_spans_impl(output, root, 0..u64::MAX) -} - -#[track_caller] -fn test_spans_impl(output: &mut String, node: &SyntaxNode, within: Range) -> bool { - if !within.contains(&node.span().number()) { - writeln!(output, " Node: {node:#?}").unwrap(); - writeln!( - output, - " Wrong span order: {} not in {within:?} ❌", - node.span().number() - ) - .unwrap(); - } - - let start = node.span().number() + 1; - let mut children = node.children().peekable(); - while let Some(child) = children.next() { - let end = children.peek().map_or(within.end, |next| next.span().number()); - if !test_spans_impl(output, child, start..end) { - return false; - } - } - - true -} - -/// Draw all frames into one image with padding in between. -fn render(document: &Document) -> sk::Pixmap { - let pixel_per_pt = 2.0; - let padding = Abs::pt(5.0); - - for page in &document.pages { - let limit = Abs::cm(100.0); - if page.frame.width() > limit || page.frame.height() > limit { - panic!("overlarge frame: {:?}", page.frame.size()); - } - } - - let mut pixmap = typst_render::render_merged( - document, - pixel_per_pt, - Color::WHITE, - padding, - Color::BLACK, - ); - - let padding = (pixel_per_pt * padding.to_pt() as f32).round(); - let [x, mut y] = [padding; 2]; - for page in &document.pages { - let ts = - sk::Transform::from_scale(pixel_per_pt, pixel_per_pt).post_translate(x, y); - render_links(&mut pixmap, ts, &page.frame); - y += (pixel_per_pt * page.frame.height().to_pt() as f32).round().max(1.0) - + padding; - } - - pixmap -} - -/// Draw extra boxes for links so we can see whether they are there. -fn render_links(canvas: &mut sk::Pixmap, ts: sk::Transform, frame: &Frame) { - for (pos, item) in frame.items() { - let ts = ts.pre_translate(pos.x.to_pt() as f32, pos.y.to_pt() as f32); - match *item { - FrameItem::Group(ref group) => { - let ts = ts.pre_concat(to_sk_transform(&group.transform)); - render_links(canvas, ts, &group.frame); - } - FrameItem::Meta(Meta::Link(_), size) => { - let w = size.x.to_pt() as f32; - let h = size.y.to_pt() as f32; - let rect = sk::Rect::from_xywh(0.0, 0.0, w, h).unwrap(); - let mut paint = sk::Paint::default(); - paint.set_color_rgba8(40, 54, 99, 40); - canvas.fill_rect(rect, &paint, ts, None); - } - _ => {} - } - } -} - -fn to_sk_transform(transform: &Transform) -> sk::Transform { - let Transform { sx, ky, kx, sy, tx, ty } = *transform; - sk::Transform::from_row( - sx.get() as _, - ky.get() as _, - kx.get() as _, - sy.get() as _, - tx.to_pt() as f32, - ty.to_pt() as f32, - ) -} - -/// A Linear-feedback shift register using XOR as its shifting function. -/// Can be used as PRNG. -struct LinearShift(u64); - -impl LinearShift { - /// Initialize the shift register with a pre-set seed. - pub fn new() -> Self { - Self(0xACE5) - } - - /// Return a pseudo-random number between `0.0` and `1.0`. - pub fn next(&mut self) -> f64 { - self.0 ^= self.0 >> 3; - self.0 ^= self.0 << 14; - self.0 ^= self.0 >> 28; - self.0 ^= self.0 << 36; - self.0 ^= self.0 >> 52; - self.0 as f64 / u64::MAX as f64 + let filtered = tests.len(); + if filtered == 0 { + eprintln!("no test selected"); + return; + } + + // Run the tests. + let logger = Mutex::new(Logger::new(filtered, skipped)); + std::thread::scope(|scope| { + let logger = &logger; + let (sender, receiver) = std::sync::mpsc::channel(); + + // Regularly refresh the logger in case we make no progress. + scope.spawn(move || { + while receiver.recv_timeout(Duration::from_millis(500)).is_err() { + logger.lock().refresh(); + } + }); + + // Run the tests. + // + // We use `par_bridge` instead of `par_iter` because the former + // results in a stack overflow during PDF export. Probably related + // to `typst::util::Deferred` yielding. + tests.iter().par_bridge().for_each(|test| { + logger.lock().start(test); + let result = std::panic::catch_unwind(|| run::run(test)); + logger.lock().end(test, result); + }); + + sender.send(()).unwrap(); + }); + + let passed = logger.into_inner().finish(); + if !passed { + std::process::exit(1); } } diff --git a/tests/src/world.rs b/tests/src/world.rs new file mode 100644 index 0000000000..86ee8da6ac --- /dev/null +++ b/tests/src/world.rs @@ -0,0 +1,229 @@ +use std::borrow::Cow; +use std::collections::HashMap; +use std::fs; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::sync::OnceLock; + +use comemo::Prehashed; +use once_cell::sync::Lazy; +use parking_lot::Mutex; +use typst::diag::{bail, FileError, FileResult, StrResult}; +use typst::foundations::{func, Bytes, Datetime, NoneValue, Repr, Smart, Value}; +use typst::layout::{Abs, Margin, PageElem}; +use typst::syntax::{FileId, Source}; +use typst::text::{Font, FontBook, TextElem, TextSize}; +use typst::visualize::Color; +use typst::{Library, World}; + +/// A world that provides access to the tests environment. +#[derive(Clone)] +pub struct TestWorld { + main: Source, + base: &'static TestBase, +} + +impl TestWorld { + /// Create a new world for a single test. + /// + /// This is cheap because the shared base for all test runs is lazily + /// initialized just once. + pub fn new(source: Source) -> Self { + static BASE: Lazy = Lazy::new(TestBase::default); + Self { main: source, base: &*BASE } + } +} + +impl World for TestWorld { + fn library(&self) -> &Prehashed { + &self.base.library + } + + fn book(&self) -> &Prehashed { + &self.base.book + } + + fn main(&self) -> Source { + self.main.clone() + } + + fn source(&self, id: FileId) -> FileResult { + if id == self.main.id() { + Ok(self.main.clone()) + } else { + self.slot(id, FileSlot::source) + } + } + + fn file(&self, id: FileId) -> FileResult { + self.slot(id, FileSlot::file) + } + + fn font(&self, index: usize) -> Option { + Some(self.base.fonts[index].clone()) + } + + fn today(&self, _: Option) -> Option { + Some(Datetime::from_ymd(1970, 1, 1).unwrap()) + } +} + +impl TestWorld { + /// Access the canonical slot for the given file id. + fn slot(&self, id: FileId, f: F) -> T + where + F: FnOnce(&mut FileSlot) -> T, + { + let mut map = self.base.slots.lock(); + f(map.entry(id).or_insert_with(|| FileSlot::new(id))) + } +} + +/// Shared foundation of all test worlds. +struct TestBase { + library: Prehashed, + book: Prehashed, + fonts: Vec, + slots: Mutex>, +} + +impl Default for TestBase { + fn default() -> Self { + let fonts: Vec<_> = typst_assets::fonts() + .chain(typst_dev_assets::fonts()) + .flat_map(|data| Font::iter(Bytes::from_static(data))) + .collect(); + + Self { + library: Prehashed::new(library()), + book: Prehashed::new(FontBook::from_fonts(&fonts)), + fonts, + slots: Mutex::new(HashMap::new()), + } + } +} + +/// Holds the processed data for a file ID. +#[derive(Clone)] +struct FileSlot { + id: FileId, + source: OnceLock>, + file: OnceLock>, +} + +impl FileSlot { + /// Create a new file slot. + fn new(id: FileId) -> Self { + Self { id, file: OnceLock::new(), source: OnceLock::new() } + } + + /// Retrieve the source for this file. + fn source(&mut self) -> FileResult { + self.source + .get_or_init(|| { + let buf = read(&system_path(self.id)?)?; + let text = String::from_utf8(buf.into_owned())?; + Ok(Source::new(self.id, text)) + }) + .clone() + } + + /// Retrieve the file's bytes. + fn file(&mut self) -> FileResult { + self.file + .get_or_init(|| { + read(&system_path(self.id)?).map(|cow| match cow { + Cow::Owned(buf) => buf.into(), + Cow::Borrowed(buf) => Bytes::from_static(buf), + }) + }) + .clone() + } +} + +/// The file system path for a file ID. +fn system_path(id: FileId) -> FileResult { + let root: PathBuf = match id.package() { + Some(spec) => format!("tests/packages/{}-{}", spec.name, spec.version).into(), + None => PathBuf::new(), + }; + + id.vpath().resolve(&root).ok_or(FileError::AccessDenied) +} + +/// Read a file. +fn read(path: &Path) -> FileResult> { + // Resolve asset. + if let Ok(suffix) = path.strip_prefix("assets/") { + return typst_dev_assets::get(&suffix.to_string_lossy()) + .map(Cow::Borrowed) + .ok_or_else(|| FileError::NotFound(path.into())); + } + + let f = |e| FileError::from_io(e, path); + if fs::metadata(path).map_err(f)?.is_dir() { + Err(FileError::IsDirectory) + } else { + fs::read(path).map(Cow::Owned).map_err(f) + } +} + +/// The extended standard library for testing. +fn library() -> Library { + // Set page width to 120pt with 10pt margins, so that the inner page is + // exactly 100pt wide. Page height is unbounded and font size is 10pt so + // that it multiplies to nice round numbers. + let mut lib = Library::default(); + + #[func] + fn test(lhs: Value, rhs: Value) -> StrResult { + if lhs != rhs { + bail!("Assertion failed: {} != {}", lhs.repr(), rhs.repr()); + } + Ok(NoneValue) + } + + #[func] + fn test_repr(lhs: Value, rhs: Value) -> StrResult { + if lhs.repr() != rhs.repr() { + bail!("Assertion failed: {} != {}", lhs.repr(), rhs.repr()); + } + Ok(NoneValue) + } + + #[func] + fn print(#[variadic] values: Vec) -> NoneValue { + let mut out = std::io::stdout().lock(); + write!(out, "> ").unwrap(); + for (i, value) in values.into_iter().enumerate() { + if i > 0 { + write!(out, ", ").unwrap(); + } + write!(out, "{value:?}").unwrap(); + } + writeln!(out).unwrap(); + NoneValue + } + + // Hook up helpers into the global scope. + lib.global.scope_mut().define_func::(); + lib.global.scope_mut().define_func::(); + lib.global.scope_mut().define_func::(); + lib.global + .scope_mut() + .define("conifer", Color::from_u8(0x9f, 0xEB, 0x52, 0xFF)); + lib.global + .scope_mut() + .define("forest", Color::from_u8(0x43, 0xA1, 0x27, 0xFF)); + + // Hook up default styles. + lib.styles + .set(PageElem::set_width(Smart::Custom(Abs::pt(120.0).into()))); + lib.styles.set(PageElem::set_height(Smart::Auto)); + lib.styles.set(PageElem::set_margin(Margin::splat(Some(Smart::Custom( + Abs::pt(10.0).into(), + ))))); + lib.styles.set(TextElem::set_size(TextSize(Abs::pt(10.0).into()))); + + lib +} diff --git a/tests/suite/foundations/array.typ b/tests/suite/foundations/array.typ new file mode 100644 index 0000000000..d4a2f151ee --- /dev/null +++ b/tests/suite/foundations/array.typ @@ -0,0 +1,472 @@ +// Test arrays. + +--- array-basic-syntax --- +#set page(width: 150pt) + +// Empty. +#() + +// Not an array, just a parenthesized expression. +#(1) + +// One item and trailing comma. +#(-1,) + +// No trailing comma. +#(true, false) + +// Multiple lines and items and trailing comma. +#("1" + , rgb("002") + ,) + +--- array-bad-token --- +// Error: 4-6 unexpected end of block comment +#(1*/2) + +--- array-bad-number-suffix --- +// Error: 6-8 invalid number suffix: u +#(1, 1u 2) + +--- array-leading-comma --- +// Error: 3-4 unexpected comma +#(,1) + +--- array-incomplete-pair --- +// Missing expression makes named pair incomplete, making this an empty array. +// Error: 5 expected expression +#(a:) + +--- array-named-pair --- +// Named pair after this is already identified as an array. +// Error: 6-10 expected expression, found named pair +#(1, b: 2) + +--- array-keyed-pair --- +// Keyed pair after this is already identified as an array. +// Error: 6-14 expected expression, found keyed pair +#(1, "key": 2) + +--- array-bad-conversion-from-string --- +// Error: 8-15 expected array, bytes, or version, found string +#array("hello") + +--- spread-into-array --- +// Test spreading into array and dictionary. +#{ + let l = (1, 2, 3) + let r = (5, 6, 7) + test((..l, 4, ..r), range(1, 8)) + test((..none), ()) +} + +--- spread-dict-into-array --- +// Error: 9-17 cannot spread dictionary into array +#(1, 2, ..(a: 1)) + +--- array-len --- +// Test the `len` method. +#test(().len(), 0) +#test(("A", "B", "C").len(), 3) + +--- array-at-lvalue --- +// Test lvalue and rvalue access. +#{ + let array = (1, 2) + array.at(1) += 5 + array.at(0) + test(array, (1, 8)) +} + +--- array-first-and-at-lvalue --- +// Test different lvalue method. +#{ + let array = (1, 2, 3) + array.first() = 7 + array.at(1) *= 8 + test(array, (7, 16, 3)) +} + +--- array-at-out-of-bounds --- +// Test rvalue out of bounds. +// Error: 2-17 array index out of bounds (index: 5, len: 3) and no default value was specified +#(1, 2, 3).at(5) + +--- array-at-out-of-bounds-negative --- +// Error: 2-18 array index out of bounds (index: -4, len: 3) and no default value was specified +#(1, 2, 3).at(-4) + +--- array-at-out-of-bounds-lvalue --- +// Test lvalue out of bounds. +#{ + let array = (1, 2, 3) + // Error: 3-14 array index out of bounds (index: 3, len: 3) + array.at(3) = 5 +} + +--- array-at-with-default --- +// Test default value. +#test((1, 2, 3).at(2, default: 5), 3) +#test((1, 2, 3).at(3, default: 5), 5) + +--- array-remove-with-default --- +// Test remove with default value. + +#{ + let array = (1, 2, 3) + test(array.remove(2, default: 5), 3) +} + +#{ + let array = (1, 2, 3) + test(array.remove(3, default: 5), 5) +} + +--- array-range --- +// Test the `range` function. +#test(range(4), (0, 1, 2, 3)) +#test(range(1, 4), (1, 2, 3)) +#test(range(-4, 2), (-4, -3, -2, -1, 0, 1)) +#test(range(10, 5), ()) +#test(range(10, step: 3), (0, 3, 6, 9)) +#test(range(1, 4, step: 1), (1, 2, 3)) +#test(range(1, 8, step: 2), (1, 3, 5, 7)) +#test(range(5, 2, step: -1), (5, 4, 3)) +#test(range(10, 0, step: -3), (10, 7, 4, 1)) + +--- array-range-end-missing --- +// Error: 2-9 missing argument: end +#range() + +--- array-range-float-invalid --- +// Error: 11-14 expected integer, found float +#range(1, 2.0) + +--- array-range-bad-step-type --- +// Error: 17-22 expected integer, found string +#range(4, step: "one") + +--- array-range-step-zero --- +// Error: 18-19 number must not be zero +#range(10, step: 0) + +--- array-bad-method-lvalue --- +// Test bad lvalue. +// Error: 2:3-2:14 cannot mutate a temporary value +#let array = (1, 2, 3) +#(array.len() = 4) + +--- array-unknown-method-lvalue --- +// Test bad lvalue. +// Error: 2:9-2:13 type array has no method `yolo` +#let array = (1, 2, 3) +#(array.yolo() = 4) + +--- array-negative-indices --- +// Test negative indices. +#{ + let array = (1, 2, 3, 4) + test(array.at(0), 1) + test(array.at(-1), 4) + test(array.at(-2), 3) + test(array.at(-3), 2) + test(array.at(-4), 1) +} + +--- array-first-and-last --- +// The the `first` and `last` methods. +#test((1,).first(), 1) +#test((2,).last(), 2) +#test((1, 2, 3).first(), 1) +#test((1, 2, 3).last(), 3) + +--- array-first-empty --- +// Error: 2-12 array is empty +#().first() + +--- array-last-empty --- +// Error: 2-11 array is empty +#().last() + +--- array-push-and-pop --- +// Test the `push` and `pop` methods. +#{ + let tasks = (a: (1, 2, 3), b: (4, 5, 6)) + test(tasks.at("a").pop(), 3) + tasks.b.push(7) + test(tasks.a, (1, 2)) + test(tasks.at("b"), (4, 5, 6, 7)) +} + +--- array-insert-and-remove --- +// Test the `insert` and `remove` methods. +#{ + let array = (0, 1, 2, 4, 5) + array.insert(3, 3) + test(array, range(6)) + array.remove(1) + test(array, (0, 2, 3, 4, 5)) +} + +--- array-insert-missing-index --- +// Error: 2:2-2:18 missing argument: index +#let numbers = () +#numbers.insert() + +--- array-slice --- +// Test the `slice` method. +#test((1, 2, 3, 4).slice(2), (3, 4)) +#test(range(10).slice(2, 6), (2, 3, 4, 5)) +#test(range(10).slice(4, count: 3), (4, 5, 6)) +#test(range(10).slice(-5, count: 2), (5, 6)) +#test((1, 2, 3).slice(2, -2), ()) +#test((1, 2, 3).slice(-2, 2), (2,)) +#test((1, 2, 3).slice(-3, 2), (1, 2)) +#test("ABCD".split("").slice(1, -1).join("-"), "A-B-C-D") + +--- array-slice-out-of-bounds --- +// Error: 2-30 array index out of bounds (index: 12, len: 10) +#range(10).slice(9, count: 3) + +--- array-slice-out-of-bounds-negative --- +// Error: 2-24 array index out of bounds (index: -4, len: 3) +#(1, 2, 3).slice(0, -4) + +--- array-position --- +// Test the `position` method. +#test(("Hi", "❤️", "Love").position(s => s == "❤️"), 1) +#test(("Bye", "💘", "Apart").position(s => s == "❤️"), none) +#test(("A", "B", "CDEF", "G").position(v => v.len() > 2), 2) + +--- array-filter --- +// Test the `filter` method. +#test(().filter(calc.even), ()) +#test((1, 2, 3, 4).filter(calc.even), (2, 4)) +#test((7, 3, 2, 5, 1).filter(x => x < 5), (3, 2, 1)) + +--- array-map --- +// Test the `map` method. +#test(().map(x => x * 2), ()) +#test((2, 3).map(x => x * 2), (4, 6)) + +--- array-fold --- +// Test the `fold` method. +#test(().fold("hi", grid), "hi") +#test((1, 2, 3, 4).fold(0, (s, x) => s + x), 10) + +--- array-fold-closure-without-params --- +// Error: 20-22 unexpected argument +#(1, 2, 3).fold(0, () => none) + +--- array-sum --- +// Test the `sum` method. +#test(().sum(default: 0), 0) +#test(().sum(default: []), []) +#test((1, 2, 3).sum(), 6) + +--- array-sum-empty --- +// Error: 2-10 cannot calculate sum of empty array with no default +#().sum() + +--- array-product --- +// Test the `product` method. +#test(().product(default: 0), 0) +#test(().product(default: []), []) +#test(([ab], 3).product(), [ab]*3) +#test((1, 2, 3).product(), 6) + +--- array-product-empty --- +// Error: 2-14 cannot calculate product of empty array with no default +#().product() + +--- array-rev --- +// Test the `rev` method. +#test(range(3).rev(), (2, 1, 0)) + +--- array-join --- +// Test the `join` method. +#test(().join(), none) +#test((1,).join(), 1) +#test(("a", "b", "c").join(), "abc") +#test("(" + ("a", "b", "c").join(", ") + ")", "(a, b, c)") + +--- array-join-bad-values --- +// Error: 2-22 cannot join boolean with boolean +#(true, false).join() + +--- array-join-bad-separator --- +// Error: 2-20 cannot join string with integer +#("a", "b").join(1) + +--- array-join-content --- +// Test joining content. +#([One], [Two], [Three]).join([, ], last: [ and ]). + +--- array-intersperse --- +// Test the `intersperse` method +#test(().intersperse("a"), ()) +#test((1,).intersperse("a"), (1,)) +#test((1, 2).intersperse("a"), (1, "a", 2)) +#test((1, 2, "b").intersperse("a"), (1, "a", 2, "a", "b")) + +--- array-chunks --- +// Test the `chunks` method. +#test(().chunks(10), ()) +#test((1, 2, 3).chunks(10), ((1, 2, 3),)) +#test((1, 2, 3, 4, 5, 6).chunks(3), ((1, 2, 3), (4, 5, 6))) +#test((1, 2, 3, 4, 5, 6, 7, 8).chunks(3), ((1, 2, 3), (4, 5, 6), (7, 8))) + +#test(().chunks(10, exact: true), ()) +#test((1, 2, 3).chunks(10, exact: true), ()) +#test((1, 2, 3, 4, 5, 6).chunks(3, exact: true), ((1, 2, 3), (4, 5, 6))) +#test((1, 2, 3, 4, 5, 6, 7, 8).chunks(3, exact: true), ((1, 2, 3), (4, 5, 6))) + +--- array-chunks-size-zero --- +// Error: 19-20 number must be positive +#(1, 2, 3).chunks(0) + +--- array-chunks-size-negative --- +// Error: 19-21 number must be positive +#(1, 2, 3).chunks(-5) + +--- array-sorted --- +// Test the `sorted` method. +#test(().sorted(), ()) +#test(().sorted(key: x => x), ()) +#test(((true, false) * 10).sorted(), (false,) * 10 + (true,) * 10) +#test(("it", "the", "hi", "text").sorted(), ("hi", "it", "text", "the")) +#test(("I", "the", "hi", "text").sorted(key: x => x), ("I", "hi", "text", "the")) +#test(("I", "the", "hi", "text").sorted(key: x => x.len()), ("I", "hi", "the", "text")) +#test((2, 1, 3, 10, 5, 8, 6, -7, 2).sorted(), (-7, 1, 2, 2, 3, 5, 6, 8, 10)) +#test((2, 1, 3, -10, -5, 8, 6, -7, 2).sorted(key: x => x), (-10, -7, -5, 1, 2, 2, 3, 6, 8)) +#test((2, 1, 3, -10, -5, 8, 6, -7, 2).sorted(key: x => x * x), (1, 2, 2, 3, -5, 6, -7, 8, -10)) + +--- array-sorted-key-function-positional-1 --- +// Error: 12-18 unexpected argument +#().sorted(x => x) + +--- array-zip --- +// Test the `zip` method. +#test(().zip(()), ()) +#test((1,).zip(()), ()) +#test((1,).zip((2,)), ((1, 2),)) +#test((1, 2).zip((3, 4)), ((1, 3), (2, 4))) +#test((1, 2, 3, 4).zip((5, 6)), ((1, 5), (2, 6))) +#test(((1, 2), 3).zip((4, 5)), (((1, 2), 4), (3, 5))) +#test((1, "hi").zip((true, false)), ((1, true), ("hi", false))) +#test((1, 2, 3).zip((3, 4, 5), (6, 7, 8)), ((1, 3, 6), (2, 4, 7), (3, 5, 8))) +#test(().zip((), ()), ()) +#test((1,).zip((2,), (3,)), ((1, 2, 3),)) +#test((1, 2, 3).zip(), ((1,), (2,), (3,))) +#test(array.zip(()), ()) + +--- array-enumerate --- +// Test the `enumerate` method. +#test(().enumerate(), ()) +#test(().enumerate(start: 5), ()) +#test(("a", "b", "c").enumerate(), ((0, "a"), (1, "b"), (2, "c"))) +#test(("a", "b", "c").enumerate(start: 1), ((1, "a"), (2, "b"), (3, "c"))) +#test(("a", "b", "c").enumerate(start: 42), ((42, "a"), (43, "b"), (44, "c"))) +#test(("a", "b", "c").enumerate(start: -7), ((-7, "a"), (-6, "b"), (-5, "c"))) + +--- array-dedup --- +// Test the `dedup` method. +#test(().dedup(), ()) +#test((1,).dedup(), (1,)) +#test((1, 1).dedup(), (1,)) +#test((1, 2, 1).dedup(), (1, 2)) +#test(("Jane", "John", "Eric").dedup(), ("Jane", "John", "Eric")) +#test(("Jane", "John", "Eric", "John").dedup(), ("Jane", "John", "Eric")) + +--- array-dedup-key --- +// Test the `dedup` method with the `key` argument. +#test((1, 2, 3, 4, 5, 6).dedup(key: x => calc.rem(x, 2)), (1, 2)) +#test((1, 2, 3, 4, 5, 6).dedup(key: x => calc.rem(x, 3)), (1, 2, 3)) +#test(("Hello", "World", "Hi", "There").dedup(key: x => x.len()), ("Hello", "Hi")) +#test(("Hello", "World", "Hi", "There").dedup(key: x => x.at(0)), ("Hello", "World", "There")) + +--- array-zip-positional-and-named-argument --- +// Error: 13-30 unexpected argument: val +#().zip((), val: "applicable") + +--- array-sorted-bad-key --- +// Error: 32-37 cannot divide by zero +#(1, 2, 0, 3).sorted(key: x => 5 / x) + +--- array-sorted-uncomparable --- +// Error: 2-26 cannot compare content and content +#([Hi], [There]).sorted() + +--- array-sorted-uncomparable-lengths --- +// Error: 2-26 cannot compare 3em with 2pt +#(1pt, 2pt, 3em).sorted() + +--- array-sorted-key-function-positional-2 --- +// Error: 42-52 unexpected argument +#((k: "a", v: 2), (k: "b", v: 1)).sorted(it => it.v) + +--- issue-3014-mix-array-dictionary --- +// Error: 8-17 expected expression, found named pair +#(box, fill: red) + +--- issue-3154-array-first-empty --- +#{ + let array = () + // Error: 3-16 array is empty + array.first() +} + +--- issue-3154-array-first-mutable-empty --- +#{ + let array = () + // Error: 3-16 array is empty + array.first() = 9 +} + +--- issue-3154-array-last-empty --- +#{ + let array = () + // Error: 3-15 array is empty + array.last() +} + +--- issue-3154-array-last-mutable-empty --- +#{ + let array = () + // Error: 3-15 array is empty + array.last() = 9 +} + +--- issue-3154-array-at-out-of-bounds --- +#{ + let array = (1,) + // Error: 3-14 array index out of bounds (index: 1, len: 1) and no default value was specified + array.at(1) +} + +--- issue-3154-array-at-out-of-bounds-default --- +#{ + let array = (1,) + test(array.at(1, default: 0), 0) +} + +--- issue-3154-array-at-out-of-bounds-mutable --- +#{ + let array = (1,) + // Error: 3-14 array index out of bounds (index: 1, len: 1) + array.at(1) = 9 +} + +--- issue-3154-array-at-out-of-bounds-mutable-default --- +#{ + let array = (1,) + // Error: 3-26 array index out of bounds (index: 1, len: 1) + array.at(1, default: 0) = 9 +} + +--- array-unopened --- +// Error: 2-3 unclosed delimiter +#{)} + +--- array-unclosed --- +// Error: 3-4 unclosed delimiter +#{(} diff --git a/tests/suite/foundations/assert.typ b/tests/suite/foundations/assert.typ new file mode 100644 index 0000000000..5de0f3878a --- /dev/null +++ b/tests/suite/foundations/assert.typ @@ -0,0 +1,40 @@ +--- assert-fail --- +// Test failing assertions. +// Error: 2-16 assertion failed +#assert(1 == 2) + +--- assert-fail-message --- +// Test failing assertions. +// Error: 2-51 assertion failed: two is smaller than one +#assert(2 < 1, message: "two is smaller than one") + +--- assert-bad-type --- +// Test failing assertions. +// Error: 9-15 expected boolean, found string +#assert("true") + +--- assert-eq-fail --- +// Test failing assertions. +// Error: 2-19 equality assertion failed: value 10 was not equal to 11 +#assert.eq(10, 11) + +--- assert-eq-fail-message --- +// Test failing assertions. +// Error: 2-55 equality assertion failed: 10 and 12 are not equal +#assert.eq(10, 12, message: "10 and 12 are not equal") + +--- assert-ne-fail --- +// Test failing assertions. +// Error: 2-19 inequality assertion failed: value 11 was equal to 11 +#assert.ne(11, 11) + +--- assert-ne-fail-message --- +// Test failing assertions. +// Error: 2-57 inequality assertion failed: must be different from 11 +#assert.ne(11, 11, message: "must be different from 11") + +--- assert-success --- +// Test successful assertions. +#assert(5 > 3) +#assert.eq(15, 15) +#assert.ne(10, 12) diff --git a/tests/typ/compiler/bytes.typ b/tests/suite/foundations/bytes.typ similarity index 80% rename from tests/typ/compiler/bytes.typ rename to tests/suite/foundations/bytes.typ index a9249bddd0..c708927804 100644 --- a/tests/typ/compiler/bytes.typ +++ b/tests/suite/foundations/bytes.typ @@ -1,32 +1,31 @@ // Test the bytes type. -// Ref: false ---- +--- bytes-basic --- #let data = read("/assets/images/rhino.png", encoding: none) #test(data.len(), 232243) #test(data.slice(0, count: 5), bytes((137, 80, 78, 71, 13))) #test(str(data.slice(1, 4)), "PNG") #test(repr(data), "bytes(232243)") ---- +--- bytes-string-conversion --- #test(str(bytes(range(0x41, 0x50))), "ABCDEFGHIJKLMNO") + +--- bytes-array-conversion --- #test(array(bytes("Hello")), (0x48, 0x65, 0x6C, 0x6C, 0x6F)) ---- +--- bytes-addition --- // Test addition and joining. #test(bytes((1, 2)) + bytes(()), bytes((1, 2))) #test(bytes((1, 2)) + bytes((3, 4)), bytes((1, 2, 3, 4))) #test(bytes(()) + bytes((3, 4)), bytes((3, 4))) + +--- bytes-joining --- #test(str({ bytes("Hello") bytes((0x20,)) bytes("World") }), "Hello World") ---- +--- bytes-bad-conversion-from-dict --- // Error: 8-14 expected string, array, or bytes, found dictionary #bytes((a: 1)) - ---- -// Error: 8-15 expected array, bytes, or version, found string -#array("hello") diff --git a/tests/suite/foundations/calc.typ b/tests/suite/foundations/calc.typ new file mode 100644 index 0000000000..e702be9f6e --- /dev/null +++ b/tests/suite/foundations/calc.typ @@ -0,0 +1,261 @@ +--- calc-round --- +#test(calc.round(calc.e, digits: 2), 2.72) +#test(calc.round(calc.pi, digits: 2), 3.14) + +--- calc-abs --- +// Test the `abs` function. +#test(calc.abs(-3), 3) +#test(calc.abs(3), 3) +#test(calc.abs(-0.0), 0.0) +#test(calc.abs(0.0), -0.0) +#test(calc.abs(-3.14), 3.14) +#test(calc.abs(50%), 50%) +#test(calc.abs(-25%), 25%) + +--- cals-abs-bad-type --- +// Error: 11-22 expected integer, float, length, angle, ratio, or fraction, found string +#calc.abs("no number") + +--- calc-even-and-odd --- +// Test the `even` and `odd` functions. +#test(calc.even(2), true) +#test(calc.odd(2), false) +#test(calc.odd(-1), true) +#test(calc.even(-11), false) + +--- calc-rem --- +// Test the `rem` function. +#test(calc.rem(1, 1), 0) +#test(calc.rem(5, 3), 2) +#test(calc.rem(5, -3), 2) +#test(calc.rem(22.5, 10), 2.5) +#test(calc.rem(9, 4.5), 0) + +--- calc-rem-divisor-zero-1 --- +// Error: 14-15 divisor must not be zero +#calc.rem(5, 0) + +--- calc-rem-divisor-zero-2 --- +// Error: 16-19 divisor must not be zero +#calc.rem(3.0, 0.0) + +--- calc-div-euclid --- +// Test the `div-euclid` function. +#test(calc.div-euclid(7, 3), 2) +#test(calc.div-euclid(7, -3), -2) +#test(calc.div-euclid(-7, 3), -3) +#test(calc.div-euclid(-7, -3), 3) +#test(calc.div-euclid(2.5, 2), 1) + +--- calc-div-euclid-divisor-zero-1 --- +// Error: 21-22 divisor must not be zero +#calc.div-euclid(5, 0) + +--- calc-div-euclid-divisor-zero-2 --- +// Error: 23-26 divisor must not be zero +#calc.div-euclid(3.0, 0.0) + +--- calc-rem-euclid --- +// Test the `rem-euclid` function. +#test(calc.rem-euclid(7, 3), 1) +#test(calc.rem-euclid(7, -3), 1) +#test(calc.rem-euclid(-7, 3), 2) +#test(calc.rem-euclid(-7, -3), 2) +#test(calc.rem-euclid(2.5, 2), 0.5) + +--- calc-rem-euclid-divisor-zero-1 --- +// Error: 21-22 divisor must not be zero +#calc.rem-euclid(5, 0) + +--- calc-rem-euclid-divisor-zero-2 --- +// Error: 23-26 divisor must not be zero +#calc.rem-euclid(3.0, 0.0) + +--- calc-quo --- +// Test the `quo` function. +#test(calc.quo(1, 1), 1) +#test(calc.quo(5, 3), 1) +#test(calc.quo(5, -3), -1) +#test(calc.quo(22.5, 10), 2) +#test(calc.quo(9, 4.5), 2) + +--- calc-quo-divisor-zero-1 --- +// Error: 14-15 divisor must not be zero +#calc.quo(5, 0) + +--- calc-quo-divisor-zero-2 --- +// Error: 16-19 divisor must not be zero +#calc.quo(3.0, 0.0) + +--- calc-min-and-max --- +// Test the `min` and `max` functions. +#test(calc.min(2, -4), -4) +#test(calc.min(3.5, 1e2, -0.1, 3), -0.1) +#test(calc.max(-3, 11), 11) +#test(calc.min("hi"), "hi") + +--- calc-pow-log-exp-ln --- +// Test the `pow`, `log`, `exp`, and `ln` functions. +#test(calc.pow(10, 0), 1) +#test(calc.pow(2, 4), 16) +#test(calc.exp(2), calc.pow(calc.e, 2)) +#test(calc.ln(10), calc.log(10, base: calc.e)) + +--- calc-bit-logical --- +// Test the `bit-not`, `bit-and`, `bit-or` and `bit-xor` functions. +#test(64.bit-not(), -65) +#test(0.bit-not(), -1) +#test((-56).bit-not(), 55) +#test(128.bit-and(192), 128) +#test(192.bit-and(224), 192) +#test((-1).bit-and(325532), 325532) +#test(0.bit-and(-53), 0) +#test(0.bit-or(-1), -1) +#test(5.bit-or(3), 7) +#test((-50).bit-or(3), -49) +#test(64.bit-or(32), 96) +#test((-1).bit-xor(1), -2) +#test(64.bit-xor(96), 32) +#test((-1).bit-xor(-7), 6) +#test(0.bit-xor(492), 492) + +--- calc-bit-shift --- +// Test the `bit-lshift` and `bit-rshift` functions. +#test(32.bit-lshift(2), 128) +#test(694.bit-lshift(0), 694) +#test(128.bit-rshift(2), 32) +#test(128.bit-rshift(12345), 0) +#test((-7).bit-rshift(2), -2) +#test((-7).bit-rshift(12345), -1) +#test(128.bit-rshift(2, logical: true), 32) +#test((-7).bit-rshift(61, logical: true), 7) +#test(128.bit-rshift(12345, logical: true), 0) +#test((-7).bit-rshift(12345, logical: true), 0) + +--- calc-bit-shift-too-large --- +// Error: 2-18 the result is too large +#1.bit-lshift(64) + +--- calc-bit-lshift-negative --- +// Error: 15-17 number must be at least zero +#1.bit-lshift(-1) + +--- calc-bit-rshift-negative --- +// Error: 15-17 number must be at least zero +#1.bit-rshift(-1) + +--- calc-pow-zero-to-power-of-zero --- +// Error: 2-16 zero to the power of zero is undefined +#calc.pow(0, 0) + +--- calc-pow-exponent-too-large --- +// Error: 14-31 exponent is too large +#calc.pow(2, 10000000000000000) + +--- calc-pow-too-large --- +// Error: 2-25 the result is too large +#calc.pow(2, 2147483647) + +--- calc-pow-bad-exponent --- +// Error: 14-36 exponent may not be infinite, subnormal, or NaN +#calc.pow(2, calc.pow(2.0, 10000.0)) + +--- calc-pow-not-real --- +// Error: 2-19 the result is not a real number +#calc.pow(-1, 0.5) + +--- calc-sqrt-not-real --- +// Error: 12-14 cannot take square root of negative number +#calc.sqrt(-1) + +--- calc-root --- +#test(calc.root(12.0, 1), 12.0) +#test(calc.root(9.0, 2), 3.0) +#test(calc.root(27.0, 3), 3.0) +#test(calc.root(-27.0, 3), -3.0) +// 100^(-1/2) = (100^(1/2))^-1 = 1/sqrt(100) +#test(calc.root(100.0, -2), 0.1) + +--- calc-root-zeroth --- +// Error: 17-18 cannot take the 0th root of a number +#calc.root(1.0, 0) + +--- calc-root-negative-even --- +// Error: 24-25 negative numbers do not have a real nth root when n is even +#test(calc.root(-27.0, 4), -3.0) + +--- calc-log-negative --- +// Error: 11-13 value must be strictly positive +#calc.log(-1) + +--- calc-log-bad-base --- +// Error: 20-21 base may not be zero, NaN, infinite, or subnormal +#calc.log(1, base: 0) + +--- calc-log-not-real --- +// Error: 2-24 the result is not a real number +#calc.log(10, base: -1) + +--- calc-fact --- +// Test the `fact` function. +#test(calc.fact(0), 1) +#test(calc.fact(5), 120) + +--- calc-fact-too-large --- +// Error: 2-15 the result is too large +#calc.fact(21) + +--- calc-perm --- +// Test the `perm` function. +#test(calc.perm(0, 0), 1) +#test(calc.perm(5, 3), 60) +#test(calc.perm(5, 5), 120) +#test(calc.perm(5, 6), 0) + +--- calc-perm-too-large --- +// Error: 2-19 the result is too large +#calc.perm(21, 21) + +--- calc-binom --- +// Test the `binom` function. +#test(calc.binom(0, 0), 1) +#test(calc.binom(5, 3), 10) +#test(calc.binom(5, 5), 1) +#test(calc.binom(5, 6), 0) +#test(calc.binom(6, 2), 15) + +--- calc-gcd --- +// Test the `gcd` function. +#test(calc.gcd(112, 77), 7) +#test(calc.gcd(12, 96), 12) +#test(calc.gcd(13, 9), 1) +#test(calc.gcd(13, -9), 1) +#test(calc.gcd(272557, 272557), 272557) +#test(calc.gcd(0, 0), 0) +#test(calc.gcd(7, 0), 7) + +--- calc-lcm --- +// Test the `lcm` function. +#test(calc.lcm(112, 77), 1232) +#test(calc.lcm(12, 96), 96) +#test(calc.lcm(13, 9), 117) +#test(calc.lcm(13, -9), 117) +#test(calc.lcm(272557, 272557), 272557) +#test(calc.lcm(0, 0), 0) +#test(calc.lcm(8, 0), 0) + +--- calc-lcm-too-large --- +// Error: 2-41 the result is too large +#calc.lcm(15486487489457, 4874879896543) + +--- calc-min-nothing --- +// Error: 2-12 expected at least one value +#calc.min() + +--- calc-min-uncomparable --- +// Error: 14-18 cannot compare string and integer +#calc.min(1, "hi") + +--- calc-max-uncomparable --- +// Error: 16-19 cannot compare 1pt with 1em +#calc.max(1em, 1pt) diff --git a/tests/suite/foundations/content.typ b/tests/suite/foundations/content.typ new file mode 100644 index 0000000000..afecc124f8 --- /dev/null +++ b/tests/suite/foundations/content.typ @@ -0,0 +1,120 @@ +--- content-at-default --- +// Test .at() default values for content. +#test(auto, [a].at("doesn't exist", default: auto)) + +--- content-field-syntax --- +// Test fields on elements. +#show list: it => { + test(it.children.len(), 3) +} + +- A +- B +- C + +--- content-field-missing --- +// Error: 25-28 content does not contain field "fun" +#show heading: it => it.fun += A + +--- content-fields --- +// Test content fields method. +#test([a].fields(), (text: "a")) +#test([a *b*].fields(), (children: ([a], [ ], strong[b]))) + +--- content-fields-mutable-invalid --- +#{ + let object = [hi] + // Error: 3-9 cannot mutate fields on content + object.property = "value" +} + +--- content-field-materialized-table --- +// Ensure that fields from set rules are materialized into the element before +// a show rule runs. +#set table(columns: (10pt, auto)) +#show table: it => it.columns +#table[A][B][C][D] + +--- content-field-materialized-heading --- +// Test it again with a different element. +#set heading(numbering: "(I)") +#show heading: set text(size: 11pt, weight: "regular") +#show heading: it => it.numbering += Heading + +--- content-field-materialized-query --- +// Test it with query. +#set raw(lang: "rust") +#context query().first().lang +`raw` + +--- content-fields-complex --- +// Integrated test for content fields. +#let compute(equation, ..vars) = { + let vars = vars.named() + let f(elem) = { + let func = elem.func() + if func == text { + let text = elem.text + if regex("^\d+$") in text { + int(text) + } else if text in vars { + int(vars.at(text)) + } else { + panic("unknown math variable: " + text) + } + } else if func == math.attach { + let value = f(elem.base) + if elem.has("t") { + value = calc.pow(value, f(elem.t)) + } + value + } else if elem.has("children") { + elem + .children + .filter(v => v != [ ]) + .split[+] + .map(xs => xs.fold(1, (prod, v) => prod * f(v))) + .fold(0, (sum, v) => sum + v) + } + } + let result = f(equation.body) + [With ] + vars + .pairs() + .map(p => $#p.first() = #p.last()$) + .join(", ", last: " and ") + [ we have:] + $ equation = result $ +} + +#compute($x y + y^2$, x: 2, y: 3) + +--- content-label-has-method --- +// Test whether the label is accessible through the `has` method. +#show heading: it => { + assert(it.has("label")) + it +} + += Hello, world! + +--- content-label-field-access --- +// Test whether the label is accessible through field syntax. +#show heading: it => { + assert(str(it.label) == "my-label") + it +} + += Hello, world! + +--- content-label-fields-method --- +// Test whether the label is accessible through the fields method. +#show heading: it => { + assert("label" in it.fields()) + assert(str(it.fields().label) == "my-label") + it +} + += Hello, world! diff --git a/tests/suite/foundations/context.typ b/tests/suite/foundations/context.typ new file mode 100644 index 0000000000..fea9f544c6 --- /dev/null +++ b/tests/suite/foundations/context.typ @@ -0,0 +1,65 @@ +// Test context expressions. + +--- context-body-atomic-in-markup --- +// Test that context body is parsed as atomic expression. +#let c = [#context "hello".] +#test(c.children.first().func(), (context none).func()) +#test(c.children.last(), [.]) + +--- context-element-constructor-forbidden --- +// Test that manual construction is forbidden. +// Error: 2-25 cannot be constructed manually +#(context none).func()() + +--- context-in-show-rule --- +// Test that show rule establishes context. +#set heading(numbering: "1.") +#show heading: it => test( + counter(heading).get(), + (intro: (1,), back: (2,)).at(str(it.label)), +) + += Introduction += Background + +--- context-in-show-rule-query --- +// Test that show rule on non-locatable element allows `query`. +// Error: 18-47 Assertion failed: 2 != 3 +#show emph: _ => test(query(heading).len(), 3) +#show strong: _ => test(query(heading).len(), 2) += Introduction += Background +*Hi* _there_ + +--- context-assign-to-captured-variable --- +// Test error when captured variable is assigned to. +#let i = 0 +// Error: 11-12 variables from outside the context expression are read-only and cannot be modified +#context (i = 1) + +--- context-compatibility-locate --- +#let s = state("x", 0) +#let compute(expr) = [ + #s.update(x => + eval(expr.replace("x", str(x))) + ) + New value is #s.display(). +] + +#locate(loc => { + let elem = query(, loc).first() + test(s.at(elem.location()), 13) +}) + +#compute("10") \ +#compute("x + 3") \ +*Here.* \ +#compute("x * 2") \ +#compute("x - 5") + +--- context-compatibility-styling --- +#style(styles => measure([it], styles).width < 20pt) + +--- context-compatibility-counter-display --- +#counter(heading).update(10) +#counter(heading).display(n => test(n, 10)) diff --git a/tests/suite/foundations/datetime.typ b/tests/suite/foundations/datetime.typ new file mode 100644 index 0000000000..b54c11f3f3 --- /dev/null +++ b/tests/suite/foundations/datetime.typ @@ -0,0 +1,93 @@ +--- datetime-constructor-empty --- +// Error: 2-12 at least one of date or time must be fully specified +#datetime() + +--- datetime-constructor-time-invalid --- +// Error: 2-42 time is invalid +#datetime(hour: 25, minute: 0, second: 0) + +--- datetime-constructor-date-invalid --- +// Error: 2-41 date is invalid +#datetime(year: 2000, month: 2, day: 30) + +--- datetime-display --- +// Test displaying of dates. +#test(datetime(year: 2023, month: 4, day: 29).display(), "2023-04-29") +#test(datetime(year: 2023, month: 4, day: 29).display("[year]"), "2023") +#test( + datetime(year: 2023, month: 4, day: 29) + .display("[year repr:last_two]"), + "23", +) +#test( + datetime(year: 2023, month: 4, day: 29) + .display("[year] [month repr:long] [day] [week_number] [weekday]"), + "2023 April 29 17 Saturday", +) + +// Test displaying of times +#test(datetime(hour: 14, minute: 26, second: 50).display(), "14:26:50") +#test(datetime(hour: 14, minute: 26, second: 50).display("[hour]"), "14") +#test( + datetime(hour: 14, minute: 26, second: 50) + .display("[hour repr:12 padding:none]"), + "2", +) +#test( + datetime(hour: 14, minute: 26, second: 50) + .display("[hour], [minute], [second]"), "14, 26, 50", +) + +// Test displaying of datetimes +#test( + datetime(year: 2023, month: 4, day: 29, hour: 14, minute: 26, second: 50).display(), + "2023-04-29 14:26:50", +) + +// Test getting the year/month/day etc. of a datetime +#let d = datetime(year: 2023, month: 4, day: 29, hour: 14, minute: 26, second: 50) +#test(d.year(), 2023) +#test(d.month(), 4) +#test(d.weekday(), 6) +#test(d.day(), 29) +#test(d.hour(), 14) +#test(d.minute(), 26) +#test(d.second(), 50) + +#let e = datetime(year: 2023, month: 4, day: 29) +#test(e.hour(), none) +#test(e.minute(), none) +#test(e.second(), none) + +// Test today +#test(datetime.today().display(), "1970-01-01") +#test(datetime.today(offset: auto).display(), "1970-01-01") +#test(datetime.today(offset: 2).display(), "1970-01-01") + +--- datetime-ordinal --- +// Test date methods. +#test(datetime(day: 1, month: 1, year: 2000).ordinal(), 1); +#test(datetime(day: 1, month: 3, year: 2000).ordinal(), 31 + 29 + 1); +#test(datetime(day: 31, month: 12, year: 2000).ordinal(), 366); +#test(datetime(day: 1, month: 3, year: 2001).ordinal(), 31 + 28 + 1); +#test(datetime(day: 31, month: 12, year: 2001).ordinal(), 365); + +--- datetime-display-missing-closing-bracket --- +// Error: 27-34 missing closing bracket for bracket at index 0 +#datetime.today().display("[year") + +--- datetime-display-invalid-component --- +// Error: 27-38 invalid component name 'nothing' at index 1 +#datetime.today().display("[nothing]") + +--- datetime-display-invalid-modifier --- +// Error: 27-50 invalid modifier 'wrong' at index 6 +#datetime.today().display("[year wrong:last_two]") + +--- datetime-display-expected-component --- +// Error: 27-33 expected component name at index 2 +#datetime.today().display(" []") + +--- datetime-display-insufficient-information --- +// Error: 2-36 failed to format datetime (insufficient information) +#datetime.today().display("[hour]") diff --git a/tests/suite/foundations/dict.typ b/tests/suite/foundations/dict.typ new file mode 100644 index 0000000000..d75b916297 --- /dev/null +++ b/tests/suite/foundations/dict.typ @@ -0,0 +1,266 @@ +// Test dictionaries. + +--- dict-basic-syntax --- + +// Empty +#(:) + +// Two pairs and string key. +#let dict = (normal: 1, "spacy key": 2) +#dict + +#test(dict.normal, 1) +#test(dict.at("spacy key"), 2) + +--- dict-fields --- +// Test field on dictionary. +#let dict = (nothing: "ness", hello: "world") +#test(dict.nothing, "ness") +#{ + let world = dict + .hello + + test(world, "world") +} + +--- dict-missing-field --- +// Error: 6-13 dictionary does not contain key "invalid" +#(:).invalid + +--- dict-bad-key --- +// Error: 3-7 expected string, found boolean +// Error: 16-18 expected string, found integer +#(true: false, 42: 3) + +--- dict-duplicate-key --- +// Error: 24-29 duplicate key: first +#(first: 1, second: 2, first: 3) + +--- dict-duplicate-key-stringy --- +// Error: 17-20 duplicate key: a +#(a: 1, "b": 2, "a": 3) + +--- dict-bad-expression --- +// Simple expression after already being identified as a dictionary. +// Error: 9-10 expected named or keyed pair, found identifier +#(a: 1, b) + +--- dict-leading-colon --- +// Identified as dictionary due to initial colon. +// The boolean key is allowed for now since it will only cause an error at the evaluation stage. +// Error: 4-5 expected named or keyed pair, found integer +// Error: 17 expected expression +#(:1 b:"", true:) + +--- spread-into-dict --- +#{ + let x = (a: 1) + let y = (b: 2) + let z = (a: 3) + test((:..x, ..y, ..z), (a: 3, b: 2)) + test((..(a: 1), b: 2), (a: 1, b: 2)) +} + +--- spread-array-into-dict --- +// Error: 3-11 cannot spread array into dictionary +#(..(1, 2), a: 1) + +--- dict-at-lvalue --- +// Test lvalue and rvalue access. +#{ + let dict = (a: 1, "b b": 1) + dict.at("b b") += 1 + dict.state = (ok: true, err: false) + test(dict, (a: 1, "b b": 2, state: (ok: true, err: false))) + test(dict.state.ok, true) + dict.at("state").ok = false + test(dict.state.ok, false) + test(dict.state.err, false) +} + +--- dict-at-missing-key --- +// Test rvalue missing key. +#{ + let dict = (a: 1, b: 2) + // Error: 11-23 dictionary does not contain key "c" and no default value was specified + let x = dict.at("c") +} + +--- dict-at-default --- +// Test default value. +#test((a: 1, b: 2).at("b", default: 3), 2) +#test((a: 1, b: 2).at("c", default: 3), 3) + +--- dict-insert --- +// Test insert. +#{ + let dict = (a: 1, b: 2) + dict.insert("b", 3) + test(dict, (a: 1, b: 3)) + dict.insert("c", 5) + test(dict, (a: 1, b: 3, c: 5)) +} + +--- dict-remove-with-default --- +// Test remove with default value. +#{ + let dict = (a: 1, b: 2) + test(dict.remove("b", default: 3), 2) +} + +#{ + let dict = (a: 1, b: 2) + test(dict.remove("c", default: 3), 3) +} + +--- dict-missing-lvalue --- +// Missing lvalue is not automatically none-initialized. +#{ + let dict = (:) + // Error: 3-9 dictionary does not contain key "b" + // Hint: 3-9 use `insert` to add or update values + dict.b += 1 +} + +--- dict-basic-methods --- +// Test dictionary methods. +#let dict = (a: 3, c: 2, b: 1) +#test("c" in dict, true) +#test(dict.len(), 3) +#test(dict.values(), (3, 2, 1)) +#test(dict.pairs().map(p => p.first() + str(p.last())).join(), "a3c2b1") + +#dict.remove("c") +#test("c" in dict, false) +#test(dict, (a: 3, b: 1)) + +--- dict-from-module --- +// Test dictionary constructor +#test(type(dictionary(sys).at("version")), version) +#test(dictionary(sys).at("no-crash", default: none), none) + +--- dict-remove-order --- +// Test that removal keeps order. +#let dict = (a: 1, b: 2, c: 3, d: 4) +#dict.remove("b") +#test(dict.keys(), ("a", "c", "d")) + +--- dict-temporary-lvalue --- +// Error: 3-15 cannot mutate a temporary value +#((key: "val").other = "some") + +--- dict-function-item-not-a-method --- +#{ + let dict = ( + call-me: () => 1, + ) + // Error: 8-15 type dictionary has no method `call-me` + // Hint: 8-15 to call the function stored in the dictionary, surround the field access with parentheses, e.g. `(dict.call-me)(..)` + dict.call-me() +} + +--- dict-item-missing-method --- +#{ + let dict = ( + nonfunc: 1 + ) + + // Error: 8-15 type dictionary has no method `nonfunc` + // Hint: 8-15 did you mean to access the field `nonfunc`? + dict.nonfunc() +} + +--- dict-dynamic-uplicate-key --- +#let a = "hello" +#let b = "world" +#let c = "value" +#let d = "conflict" + +#assert.eq(((a): b), ("hello": "world")) +#assert.eq(((a): 1, (a): 2), ("hello": 2)) +#assert.eq((hello: 1, (a): 2), ("hello": 2)) +#assert.eq((a + b: c, (a + b): d, (a): "value2", a: "value3"), ("helloworld": "conflict", "hello": "value2", "a": "value3")) + +--- issue-1338-dictionary-underscore --- +#let foo = "foo" +#let bar = "bar" +// Error: 8-9 expected expression, found underscore +// Error: 16-17 expected expression, found underscore +#(foo: _, bar: _) + +--- issue-1342-dictionary-bare-expressions --- +// Error: 5-8 expected named or keyed pair, found identifier +// Error: 10-13 expected named or keyed pair, found identifier +#(: foo, bar) + +--- issue-3154-dict-at-not-contained --- +#{ + let dict = (a: 1) + // Error: 3-15 dictionary does not contain key "b" and no default value was specified + dict.at("b") +} + +--- issue-3154-dict-at-missing-default --- +#{ + let dict = (a: 1) + test(dict.at("b", default: 0), 0) +} + +--- issue-3154-dict-at-missing-mutable --- +#{ + let dict = (a: 1) + // Error: 3-15 dictionary does not contain key "b" + // Hint: 3-15 use `insert` to add or update values + dict.at("b") = 9 +} + +--- issue-3154-dict-at-missing-mutable-default --- +#{ + let dict = (a: 1) + // Error: 3-27 dictionary does not contain key "b" + // Hint: 3-27 use `insert` to add or update values + dict.at("b", default: 0) = 9 +} + +--- issue-3154-dict-syntax-missing --- +#{ + let dict = (a: 1) + // Error: 8-9 dictionary does not contain key "b" + dict.b +} + +--- issue-3154-dict-syntax-missing-mutable --- +#{ + let dict = (a: 1) + dict.b = 9 + test(dict, (a: 1, b: 9)) +} + +--- issue-3154-dict-syntax-missing-add-assign --- +#{ + let dict = (a: 1) + // Error: 3-9 dictionary does not contain key "b" + // Hint: 3-9 use `insert` to add or update values + dict.b += 9 +} + +--- issue-3232-dict-unexpected-keys-sides --- +// Confusing "expected relative length or dictionary, found dictionary" +// Error: 16-58 unexpected keys "unexpected" and "unexpected-too" +#block(outset: (unexpected: 0.5em, unexpected-too: 0.2em), [Hi]) + +--- issue-3232-dict-unexpected-keys-corners --- +// Error: 14-56 unexpected keys "unexpected" and "unexpected-too" +#box(radius: (unexpected: 0.5em, unexpected-too: 0.5em), [Hi]) + +--- issue-3232-dict-unexpected-key-sides --- +// Error: 16-49 unexpected key "unexpected", valid keys are "left", "top", "right", "bottom", "x", "y", and "rest" +#block(outset: (unexpected: 0.2em, right: 0.5em), [Hi]) // The 1st key is unexpected + +--- issue-3232-dict-unexpected-key-corners --- +// Error: 14-50 unexpected key "unexpected", valid keys are "top-left", "top-right", "bottom-right", "bottom-left", "left", "top", "right", "bottom", and "rest" +#box(radius: (top-left: 0.5em, unexpected: 0.5em), [Hi]) // The 2nd key is unexpected + +--- issue-3232-dict-empty --- +#block(outset: (:), [Hi]) // Ok +#box(radius: (:), [Hi]) // Ok diff --git a/tests/typ/compiler/duration.typ b/tests/suite/foundations/duration.typ similarity index 93% rename from tests/typ/compiler/duration.typ rename to tests/suite/foundations/duration.typ index 1d831a6fa8..7e53f70369 100644 --- a/tests/typ/compiler/duration.typ +++ b/tests/suite/foundations/duration.typ @@ -1,17 +1,16 @@ // Test durations. -// Ref: false ---- +--- duration-negate --- // Test negating durations. #test(-duration(hours: 2), duration(hours: -2)) ---- +--- duration-add-and-subtract --- // Test adding and subtracting durations. #test(duration(weeks: 1, hours: 1), duration(weeks: 1) + duration(hours: 1)) #test(duration(weeks: 1, hours: -1), duration(weeks: 1) - duration(hours: 1)) #test(duration(days: 6, hours: 23), duration(weeks: 1) - duration(hours: 1)) ---- +--- duration-add-and-subtract-dates --- // Test adding and subtracting durations and dates. #let d = datetime(day: 1, month: 1, year: 2000) #let d2 = datetime(day: 1, month: 2, year: 2000) @@ -33,7 +32,7 @@ datetime(day: 1, month: 1, year: 2001), ) ---- +--- duration-add-and-subtract-times --- // Test adding and subtracting durations and times. #let a = datetime(hour: 12, minute: 0, second: 0) #test(a + duration(hours: 1, minutes: -60), datetime(hour: 12, minute: 0, second: 0)) @@ -51,7 +50,7 @@ datetime(hour: 13, minute: 13, second: 13), ) ---- +--- duration-add-and-subtract-datetimes --- // Test adding and subtracting durations and datetimes. #test( datetime(day: 1, month: 1, year: 2000, hour: 12, minute: 0, second: 0) @@ -65,7 +64,7 @@ datetime(day: 10, month: 1, year: 2000, hour: 23, minute: 9, second: 50), ) ---- +--- duration-from-date-subtraction --- // Test subtracting dates. #let a = datetime(hour: 12, minute: 0, second: 0) #let b = datetime(day: 1, month: 1, year: 2000) @@ -75,7 +74,7 @@ #test(datetime(day: 1, month: 2, year: 2000) - b, duration(days: 31)) #test(datetime(day: 15, month: 1, year: 2000) - b, duration(weeks: 2)) ---- +--- duration-multiply-with-number --- // Test multiplying and dividing durations with numbers. #test(duration(minutes: 10) * 6, duration(hours: 1)) #test(duration(minutes: 10) * 2, duration(minutes: 20)) @@ -83,13 +82,13 @@ #test(duration(minutes: 10) / 2, duration(minutes: 5)) #test(duration(minutes: 10) / 2.5, duration(minutes: 4)) ---- +--- duration-divide --- // Test dividing durations with durations #test(duration(minutes: 20) / duration(hours: 1), 1 / 3) #test(duration(minutes: 20) / duration(minutes: 10), 2) #test(duration(minutes: 20) / duration(minutes: 8), 2.5) ---- +--- duration-compare --- // Test comparing durations #test(duration(minutes: 20) > duration(minutes: 10), true) #test(duration(minutes: 20) >= duration(minutes: 10), true) diff --git a/tests/suite/foundations/eval.typ b/tests/suite/foundations/eval.typ new file mode 100644 index 0000000000..f85146b235 --- /dev/null +++ b/tests/suite/foundations/eval.typ @@ -0,0 +1,54 @@ +--- eval --- +// Test the eval function. +#test(eval("1 + 2"), 3) +#test(eval("1 + x", scope: (x: 3)), 4) +#test(eval("let x = x + 1; x + 1", scope: (x: 1)), 3) + +--- eval-mode --- +// Test evaluation in other modes. +#eval("[_Hello" + " World!_]") \ +#eval("_Hello" + " World!_", mode: "markup") \ +#eval("RR_1^NN", mode: "math", scope: (RR: math.NN, NN: math.RR)) + +--- eval-syntax-error-1 --- +// Error: 7-12 expected pattern +#eval("let") + +--- eval-in-show-rule --- +#show raw: it => text(font: "PT Sans", eval("[" + it.text + "]")) + +Interacting +``` +#set text(blue) +Blue #move(dy: -0.15em)[🌊] +``` + +--- eval-runtime-error --- +// Error: 7-17 cannot continue outside of loop +#eval("continue") + +--- eval-syntax-error-2 --- +// Error: 7-12 expected semicolon or line break +#eval("1 2") + +--- eval-path-resolve --- +// Test absolute path. +#eval("image(\"/assets/images/tiger.jpg\", width: 50%)") + +--- eval-path-resolve-in-show-rule --- +#show raw: it => eval(it.text, mode: "markup") + +``` +#show emph: image("/assets/images/tiger.jpg", width: 50%) +_Tiger!_ +``` + +--- eval-path-resolve-relative --- +// Test relative path. +#test(eval(`"HELLO" in read("./eval.typ")`.text), true) + +--- issue-2055-math-eval --- +// Evaluating a math expr should renders the same as an equation +#eval(mode: "math", "f(a) = cases(a + b\, space space x >= 3,a + b\, space space x = 5)") + +$f(a) = cases(a + b\, space space x >= 3,a + b\, space space x = 5)$ diff --git a/tests/suite/foundations/float.typ b/tests/suite/foundations/float.typ new file mode 100644 index 0000000000..770533b9a6 --- /dev/null +++ b/tests/suite/foundations/float.typ @@ -0,0 +1,66 @@ +--- float-constructor --- +#test(float(10), 10.0) +#test(float(50% * 30%), 0.15) +#test(float("31.4e-1"), 3.14) +#test(float("31.4e\u{2212}1"), 3.14) +#test(float("3.1415"), 3.1415) +#test(float("-7654.321"), -7654.321) +#test(float("\u{2212}7654.321"), -7654.321) +#test(type(float(10)), float) + +--- float-constructor-bad-type --- +// Error: 8-13 expected float, boolean, integer, ratio, or string, found type +#float(float) + +--- float-constructor-bad-value --- +// Error: 8-15 invalid float: 1.2.3 +#float("1.2.3") + +--- float-is-nan --- +// Test float `is-nan()`. +#test(float(calc.nan).is-nan(), true) +#test(float(10).is-nan(), false) + +--- float-is-infinite --- +// Test float `is-infinite()`. +#test(float(calc.inf).is-infinite(), true) +#test(float(-calc.inf).is-infinite(), true) +#test(float(10).is-infinite(), false) +#test(float(-10).is-infinite(), false) + +--- float-signum --- +// Test float `signum()` +#test(float(0.0).signum(), 1.0) +#test(float(1.0).signum(), 1.0) +#test(float(-1.0).signum(), -1.0) +#test(float(10.0).signum(), 1.0) +#test(float(-10.0).signum(), -1.0) +#test(float(calc.nan).signum().is-nan(), true) + +--- float-repr --- +// Test the `repr` function with floats. +#repr(12.0) \ +#repr(3.14) \ +#repr(1234567890.0) \ +#repr(0123456789.0) \ +#repr(0.0) \ +#repr(-0.0) \ +#repr(-1.0) \ +#repr(-9876543210.0) \ +#repr(-0987654321.0) \ +#repr(-3.14) \ +#repr(4.0 - 8.0) + +--- float-display --- +// Test floats. +#12.0 \ +#3.14 \ +#1234567890.0 \ +#0123456789.0 \ +#0.0 \ +#(-0.0) \ +#(-1.0) \ +#(-9876543210.0) \ +#(-0987654321.0) \ +#(-3.14) \ +#(4.0 - 8.0) diff --git a/tests/suite/foundations/int.typ b/tests/suite/foundations/int.typ new file mode 100644 index 0000000000..0c85dcabab --- /dev/null +++ b/tests/suite/foundations/int.typ @@ -0,0 +1,73 @@ +--- int-base-alternative --- +// Test numbers with alternative bases. +#test(0x10, 16) +#test(0b1101, 13) +#test(0xA + 0xa, 0x14) + +--- int-base-binary-invalid --- +// Error: 2-7 invalid binary number: 0b123 +#0b123 + +--- int-base-hex-invalid --- +// Error: 2-8 invalid hexadecimal number: 0x123z +#0x123z + +--- int-constructor --- +// Test conversion to numbers. +#test(int(false), 0) +#test(int(true), 1) +#test(int(10), 10) +#test(int("150"), 150) +#test(int("-834"), -834) +#test(int("\u{2212}79"), -79) +#test(int(10 / 3), 3) + +--- int-constructor-bad-type --- +// Error: 6-10 expected integer, boolean, float, or string, found length +#int(10pt) + +--- int-constructor-bad-value --- +// Error: 6-12 invalid integer: nope +#int("nope") + +--- int-signum --- +// Test int `signum()` +#test(int(0).signum(), 0) +#test(int(1.0).signum(), 1) +#test(int(-1.0).signum(), -1) +#test(int(10.0).signum(), 1) +#test(int(-10.0).signum(), -1) + +--- int-repr --- +// Test the `repr` function with integers. +#repr(12) \ +#repr(1234567890) \ +#repr(0123456789) \ +#repr(0) \ +#repr(-0) \ +#repr(-1) \ +#repr(-9876543210) \ +#repr(-0987654321) \ +#repr(4 - 8) + +--- int-display --- +// Test integers. +#12 \ +#1234567890 \ +#0123456789 \ +#0 \ +#(-0) \ +#(-1) \ +#(-9876543210) \ +#(-0987654321) \ +#(4 - 8) + +--- issue-int-constructor --- +// Test that integer -> integer conversion doesn't do a roundtrip through float. +#let x = 9223372036854775800 +#test(type(x), int) +#test(int(x), x) + +--- number-invalid-suffix --- +// Error: 2-4 invalid number suffix: u +#1u diff --git a/tests/typ/compiler/label.typ b/tests/suite/foundations/label.typ similarity index 81% rename from tests/typ/compiler/label.typ rename to tests/suite/foundations/label.typ index fabbac80f1..2cde102c3a 100644 --- a/tests/typ/compiler/label.typ +++ b/tests/suite/foundations/label.typ @@ -1,6 +1,6 @@ // Test labels. ---- +--- label-show-where-selector --- // Test labelled headings. #show heading: set text(10pt) #show heading.where(label: ): underline @@ -11,7 +11,7 @@ The beginning. = Conclusion The end. ---- +--- label-after-expression --- // Test label after expression. #show strong.where(label: ): set text(red) @@ -19,7 +19,7 @@ The end. #let b = [*B*] #a #b ---- +--- label-on-text --- // Test labelled text. #show "t": it => { set text(blue) if it.has("label") and it.label == @@ -28,14 +28,14 @@ The end. This is a thing #[that ] happened. ---- +--- label-dynamic-show-set --- // Test abusing dynamic labels for styling. #show : set text(red) #show : set text(blue) *A* *B* *C* #label("bl" + "ue") *D* ---- +--- label-after-parbreak --- // Test that label ignores parbreak. #show : none @@ -47,26 +47,24 @@ _Hidden_ _Visible_ ---- +--- label-in-block --- // Test that label only works within one content block. #show : strike *This is* #[] *protected.* *This is not.* ---- +--- label-unclosed-is-text --- // Test that incomplete label is text. 1 < 2 is #if 1 < 2 [not] a label. ---- +--- label-text-styled-and-sequence --- // Test label on text, styled, and sequence. -// Ref: false #test([Hello].label, ) #test([#[A *B* C]].label, ) #test([#text(red)[Hello]].label, ) ---- +--- label-string-conversion --- // Test getting the name of a label. -// Ref: false #test(str(), "hey") #test(str(label("hey")), "hey") #test(str([Hmm].label), "hey") diff --git a/tests/suite/foundations/panic.typ b/tests/suite/foundations/panic.typ new file mode 100644 index 0000000000..5d9d40468e --- /dev/null +++ b/tests/suite/foundations/panic.typ @@ -0,0 +1,14 @@ +--- panic --- +// Test panic. +// Error: 2-9 panicked +#panic() + +--- panic-with-int --- +// Test panic. +// Error: 2-12 panicked with: 123 +#panic(123) + +--- panic-with-str --- +// Test panic. +// Error: 2-24 panicked with: "this is wrong" +#panic("this is wrong") diff --git a/tests/suite/foundations/plugin.typ b/tests/suite/foundations/plugin.typ new file mode 100644 index 0000000000..0842980ecd --- /dev/null +++ b/tests/suite/foundations/plugin.typ @@ -0,0 +1,47 @@ +// Test WebAssembly plugins. + +--- plugin-basic --- +#let p = plugin("/assets/plugins/hello.wasm") +#test(p.hello(), bytes("Hello from wasm!!!")) +#test(p.double_it(bytes("hey!")), bytes("hey!.hey!")) +#test( + p.shuffle(bytes("value1"), bytes("value2"), bytes("value3")), + bytes("value3-value1-value2"), +) + +--- plugin-wrong-number-of-arguments --- +#let p = plugin("/assets/plugins/hello.wasm") + +// Error: 2-20 plugin function takes 0 arguments, but 1 was given +#p.hello(bytes("")) + +--- plugin-wrong-argument-type --- +#let p = plugin("/assets/plugins/hello.wasm") + +// Error: 10-14 expected bytes, found boolean +// Error: 27-29 expected bytes, found integer +#p.hello(true, bytes(()), 10) + +--- plugin-error --- +#let p = plugin("/assets/plugins/hello.wasm") + +// Error: 2-17 plugin errored with: This is an `Err` +#p.returns_err() + +--- plugin-panic --- +#let p = plugin("/assets/plugins/hello.wasm") + +// Error: 2-16 plugin panicked: wasm `unreachable` instruction executed +#p.will_panic() + +--- plugin-out-of-bounds-read --- +#let p = plugin("/assets/plugins/plugin-oob.wasm") + +// Error: 2-14 plugin tried to read out of bounds: pointer 0x40000000 is out of bounds for read of length 1 +#p.read_oob() + +--- plugin-out-of-bounds-write --- +#let p = plugin("/assets/plugins/plugin-oob.wasm") + +// Error: 2-27 plugin tried to write out of bounds: pointer 0x40000000 is out of bounds for write of length 3 +#p.write_oob(bytes("xyz")) diff --git a/tests/typ/compiler/repr.typ b/tests/suite/foundations/repr.typ similarity index 78% rename from tests/typ/compiler/repr.typ rename to tests/suite/foundations/repr.typ index 13593a868d..f9346faacb 100644 --- a/tests/typ/compiler/repr.typ +++ b/tests/suite/foundations/repr.typ @@ -1,13 +1,15 @@ -// Test representation of values in the document. +--- repr --- +#test(repr(ltr), "ltr") +#test(repr((1, 2, false, )), "(1, 2, false)") ---- +--- repr-literals --- // Literal values. #auto \ #none (empty) \ #true \ #false ---- +--- repr-numerical --- // Numerical values. #1 \ #1.0e-4 \ @@ -24,7 +26,7 @@ #(2em + 10pt) \ #2.3fr ---- +--- repr-misc --- // Colors and strokes. #set text(0.8em) #rgb("f7a205") \ diff --git a/tests/suite/foundations/str.typ b/tests/suite/foundations/str.typ new file mode 100644 index 0000000000..025ec53d36 --- /dev/null +++ b/tests/suite/foundations/str.typ @@ -0,0 +1,315 @@ +// Test the string methods. + +--- str-constructor --- +// Test conversion to string. +#test(str(123), "123") +#test(str(123, base: 3), "11120") +#test(str(-123, base: 16), "−7b") +#test(str(9223372036854775807, base: 36), "1y2p0ij32e8e7") +#test(str(50.14), "50.14") +#test(str(10 / 3).len() > 10, true) + +--- str-from-float --- +// Test the `str` function with floats. +#test(str(12.0), "12") +#test(str(3.14), "3.14") +#test(str(1234567890.0), "1234567890") +#test(str(0123456789.0), "123456789") +#test(str(0.0), "0") +#test(str(-0.0), "0") +#test(str(-1.0), "−1") +#test(str(-9876543210.0), "−9876543210") +#test(str(-0987654321.0), "−987654321") +#test(str(-3.14), "−3.14") +#test(str(4.0 - 8.0), "−4") + +--- str-from-int --- +// Test the `str` function with integers. +#test(str(12), "12") +#test(str(1234567890), "1234567890") +#test(str(0123456789), "123456789") +#test(str(0), "0") +#test(str(-0), "0") +#test(str(-1), "−1") +#test(str(-9876543210), "−9876543210") +#test(str(-0987654321), "−987654321") +#test(str(4 - 8), "−4") + +--- str-constructor-bad-type --- +// Error: 6-8 expected integer, float, version, bytes, label, type, or string, found content +#str([]) + +--- str-constructor-bad-base --- +// Error: 17-19 base must be between 2 and 36 +#str(123, base: 99) + +--- str-constructor-unsupported-base --- +// Error: 18-19 base is only supported for integers +#str(1.23, base: 2) + +--- str-from-and-to-unicode --- +// Test the unicode function. +#test(str.from-unicode(97), "a") +#test(str.to-unicode("a"), 97) + +--- str-from-unicode-bad-type --- +// Error: 19-22 expected integer, found content +#str.from-unicode([a]) + +--- str-to-unicode-bad-type --- +// Error: 17-21 expected exactly one character +#str.to-unicode("ab") + +--- str-from-unicode-negative --- +// Error: 19-21 number must be at least zero +#str.from-unicode(-1) + +--- str-from-unicode-bad-value --- +// Error: 2-28 0x110000 is not a valid codepoint +#str.from-unicode(0x110000) // 0x10ffff is the highest valid code point + +--- string-len --- +// Test the `len` method. +#test("Hello World!".len(), 12) + +--- string-first-and-last --- +// Test the `first` and `last` methods. +#test("Hello".first(), "H") +#test("Hello".last(), "o") +#test("🏳️‍🌈A🏳️‍⚧️".first(), "🏳️‍🌈") +#test("🏳️‍🌈A🏳️‍⚧️".last(), "🏳️‍⚧️") + +--- string-first-empty --- +// Error: 2-12 string is empty +#"".first() + +--- string-last-empty --- +// Error: 2-11 string is empty +#"".last() + +--- string-at --- +// Test the `at` method. +#test("Hello".at(1), "e") +#test("Hello".at(4), "o") +#test("Hello".at(-1), "o") +#test("Hello".at(-2), "l") +#test("Hey: 🏳️‍🌈 there!".at(5), "🏳️‍🌈") + +--- string-at-default --- +// Test `at`'s 'default' parameter. +#test("z", "Hello".at(5, default: "z")) + +--- string-at-not-a-char-boundary --- +// Error: 2-14 string index 2 is not a character boundary +#"🏳️‍🌈".at(2) + +--- string-at-out-of-bounds --- +// Error: 2-15 no default value was specified and string index out of bounds (index: 5, len: 5) +#"Hello".at(5) + +--- string-at-at-default-other-type --- +#test("Hello".at(5, default: (a: 10)), (a: 10)) + +--- string-slice --- +// Test the `slice` method. +#test("abc".slice(1, 2), "b") +#test("abc🏡def".slice(2, 7), "c🏡") +#test("abc🏡def".slice(2, -2), "c🏡d") +#test("abc🏡def".slice(-3, -1), "de") + +--- string-slice-not-a-char-boundary --- +// Error: 2-21 string index -1 is not a character boundary +#"🏳️‍🌈".slice(0, -1) + +--- string-clusters --- +// Test the `clusters` and `codepoints` methods. +#test("abc".clusters(), ("a", "b", "c")) +#test("abc".clusters(), ("a", "b", "c")) +#test("🏳️‍🌈!".clusters(), ("🏳️‍🌈", "!")) + +--- string-codepoints --- +#test("🏳️‍🌈!".codepoints(), ("🏳", "\u{fe0f}", "\u{200d}", "🌈", "!")) + +--- string-contains --- +// Test the `contains` method. +#test("abc".contains("b"), true) +#test("b" in "abc", true) +#test("1234f".contains(regex("\d")), true) +#test(regex("\d") in "1234f", true) +#test("abc".contains("d"), false) +#test("1234g" in "1234f", false) +#test("abc".contains(regex("^[abc]$")), false) +#test("abc".contains(regex("^[abc]+$")), true) + +--- string-starts-with --- +// Test the `starts-with` and `ends-with` methods. +#test("Typst".starts-with("Ty"), true) +#test("Typst".starts-with(regex("[Tt]ys")), false) +#test("Typst".starts-with("st"), false) + +--- string-ends-with --- +#test("Typst".ends-with("st"), true) +#test("Typst".ends-with(regex("\d*")), true) +#test("Typst".ends-with(regex("\d+")), false) +#test("Typ12".ends-with(regex("\d+")), true) +#test("typst13".ends-with(regex("1[0-9]")), true) +#test("typst113".ends-with(regex("1[0-9]")), true) +#test("typst23".ends-with(regex("1[0-9]")), false) + +--- string-find-and-position --- +// Test the `find` and `position` methods. +#let date = regex("\d{2}:\d{2}") +#test("Hello World".find("World"), "World") +#test("Hello World".position("World"), 6) +#test("It's 12:13 now".find(date), "12:13") +#test("It's 12:13 now".position(date), 5) + +--- string-match --- +// Test the `match` method. +#test("Is there a".match("for this?"), none) +#test( + "The time of my life.".match(regex("[mit]+e")), + (start: 4, end: 8, text: "time", captures: ()), +) + +--- string-matches --- +// Test the `matches` method. +#test("Hello there".matches("\d"), ()) +#test("Day by Day.".matches("Day"), ( + (start: 0, end: 3, text: "Day", captures: ()), + (start: 7, end: 10, text: "Day", captures: ()), +)) + +// Compute the sum of all timestamps in the text. +#let timesum(text) = { + let time = 0 + for match in text.matches(regex("(\d+):(\d+)")) { + let caps = match.captures + time += 60 * int(caps.at(0)) + int(caps.at(1)) + } + str(int(time / 60)) + ":" + str(calc.rem(time, 60)) +} + +#test(timesum(""), "0:0") +#test(timesum("2:70"), "3:10") +#test(timesum("1:20, 2:10, 0:40"), "4:10") + +--- stgring-replace --- +// Test the `replace` method with `Str` replacements. +#test("ABC".replace("", "-"), "-A-B-C-") +#test("Ok".replace("Ok", "Nope", count: 0), "Ok") +#test("to add?".replace("", "How ", count: 1), "How to add?") +#test("AB C DEF GH J".replace(" ", ",", count: 2), "AB,C,DEF GH J") +#test("Walcemo" + .replace("o", "k") + .replace("e", "o") + .replace("k", "e") + .replace("a", "e"), + "Welcome" +) +#test("123".replace(regex("\d$"), "_"), "12_") +#test("123".replace(regex("\d{1,2}$"), "__"), "1__") + +--- string-replace-function --- +// Test the `replace` method with `Func` replacements. + +#test("abc".replace(regex("[a-z]"), m => { + str(m.start) + m.text + str(m.end) +}), "0a11b22c3") +#test("abcd, efgh".replace(regex("\w+"), m => { + upper(m.text) +}), "ABCD, EFGH") +#test("hello : world".replace(regex("^(.+)\s*(:)\s*(.+)$"), m => { + upper(m.captures.at(0)) + m.captures.at(1) + " " + upper(m.captures.at(2)) +}), "HELLO : WORLD") +#test("hello world, lorem ipsum".replace(regex("(\w+) (\w+)"), m => { + m.captures.at(1) + " " + m.captures.at(0) +}), "world hello, ipsum lorem") +#test("hello world, lorem ipsum".replace(regex("(\w+) (\w+)"), count: 1, m => { + m.captures.at(1) + " " + m.captures.at(0) +}), "world hello, lorem ipsum") +#test("123 456".replace(regex("[a-z]+"), "a"), "123 456") + +#test("abc".replace("", m => "-"), "-a-b-c-") +#test("abc".replace("", m => "-", count: 1), "-abc") +#test("123".replace("abc", m => ""), "123") +#test("123".replace("abc", m => "", count: 2), "123") +#test("a123b123c".replace("123", m => { + str(m.start) + "-" + str(m.end) +}), "a1-4b5-8c") +#test("halla warld".replace("a", m => { + if m.start == 1 { "e" } + else if m.start == 4 or m.start == 7 { "o" } +}), "hello world") +#test("aaa".replace("a", m => str(m.captures.len())), "000") + +--- string-replace-function-bad-type --- +// Error: 23-24 expected string, found integer +#"123".replace("123", m => 1) + +--- string-replace-bad-type --- +// Error: 23-32 expected string or function, found array +#"123".replace("123", (1, 2, 3)) + +--- string-trim-basic --- +// Test the `trim` method; the pattern is not provided. +#let str = "Typst, LaTeX, Word, InDesign" +#let array = ("Typst", "LaTeX", "Word", "InDesign") +#test(str.split(",").map(s => s.trim()), array) +#test("".trim(), "") +#test(" ".trim(), "") +#test("\t".trim(), "") +#test("\n".trim(), "") +#test("\t \n".trim(), "") +#test(" abc ".trim(at: start), "abc ") +#test("\tabc ".trim(at: start), "abc ") +#test("abc\n".trim(at: end), "abc") +#test(" abc ".trim(at: end, repeat: true), " abc") +#test(" abc".trim(at: start, repeat: false), "abc") + +--- string-trim-pattern-str --- +// Test the `trim` method; the pattern is a string. +#test("aabcaa".trim("a", repeat: false), "abca") +#test("aabca".trim("a", at: start), "bca") +#test("aabcaa".trim("a", at: end, repeat: false), "aabca") +#test(" abc\n".trim("\n"), " abc") +#test("whole".trim("whole", at: start), "") + +--- string-trim-pattern-regex --- +// Test the `trim` method; the pattern is a regex. +#test("".trim(regex(".")), "") +#test("123abc456".trim(regex("\d")), "abc") +#test("123abc456".trim(regex("\d"), repeat: false), "23abc45") +#test("123a4b5c678".trim(regex("\d"), repeat: true), "a4b5c") +#test("123a4b5c678".trim(regex("\d"), repeat: false), "23a4b5c67") +#test("123abc456".trim(regex("\d"), at: start), "abc456") +#test("123abc456".trim(regex("\d"), at: end), "123abc") +#test("123abc456".trim(regex("\d+"), at: end, repeat: false), "123abc") +#test("123abc456".trim(regex("\d{1,2}$"), repeat: false), "123abc4") +#test("hello world".trim(regex(".")), "") +#test("12306".trim(regex("\d"), at: start), "") +#test("12306abc".trim(regex("\d"), at: start), "abc") +#test("whole".trim(regex("whole"), at: start), "") +#test("12306".trim(regex("\d"), at: end), "") +#test("abc12306".trim(regex("\d"), at: end), "abc") +#test("whole".trim(regex("whole"), at: end), "") + +--- string-trim-at-bad-alignment --- +// Error: 17-21 expected either `start` or `end` +#"abc".trim(at: left) + +--- string-split --- +// Test the `split` method. +#test("abc".split(""), ("", "a", "b", "c", "")) +#test("abc".split("b"), ("a", "c")) +#test("a123c".split(regex("\d")), ("a", "", "", "c")) +#test("a123c".split(regex("\d+")), ("a", "c")) + +--- string-rev --- +// Test the `rev` method. +#test("abc".rev(), "cba") +#test("ax̂e".rev(), "ex̂a") + +--- string-unclosed --- +// Error: 2-2:1 unclosed string +#"hello\" diff --git a/tests/suite/foundations/type.typ b/tests/suite/foundations/type.typ new file mode 100644 index 0000000000..f2a9884506 --- /dev/null +++ b/tests/suite/foundations/type.typ @@ -0,0 +1,25 @@ +--- type --- +#test(type(1), int) +#test(type(ltr), direction) +#test(type(10 / 3), float) + +--- type-string-compatibility --- +#test(type(10), int) +#test(type(10), "integer") +#test("is " + type(10), "is integer") +#test(int in ("integer", "string"), true) +#test(int in "integers or strings", true) +#test(str in "integers or strings", true) + +--- issue-3110-type-constructor --- +// Let the error message report the type name. +// Error: 2-9 type content does not have a constructor +#content() + +--- issue-3110-associated-field --- +// Error: 6-12 type integer does not contain field `MAXVAL` +#int.MAXVAL + +--- issue-3110-associated-function --- +// Error: 6-18 type string does not contain field `from-unïcode` +#str.from-unïcode(97) diff --git a/tests/typ/compute/version.typ b/tests/suite/foundations/version.typ similarity index 77% rename from tests/typ/compute/version.typ rename to tests/suite/foundations/version.typ index e33eeb6f60..bf2cadb18f 100644 --- a/tests/typ/compute/version.typ +++ b/tests/suite/foundations/version.typ @@ -1,27 +1,27 @@ // Test versions. -// Ref: false ---- +--- version-constructor --- // Test version constructor. // Empty. #version() // Plain. -#version(1, 2) +#test(version(1, 2).major, 1) // Single Array argument. -#version((1, 2)) +#test(version((1, 2)).minor, 2) // Mixed arguments. -#version(1, (2, 3), 4, (5, 6), 7) +#test(version(1, (2, 3), 4, (5, 6), 7).at(5), 6) ---- +--- version-equality --- // Test equality of different-length versions #test(version(), version(0)) #test(version(0), version(0, 0)) #test(version(1, 2), version(1, 2, 0, 0, 0, 0)) ---- + +--- version-at --- // Test `version.at`. // Non-negative index in bounds @@ -36,12 +36,12 @@ // Error: 2-22 component index out of bounds (index: -3, len: 2) #version(1, 2).at(-3) ---- +--- version-fields --- // Test version fields. #test(version(1, 2, 3).major, 1) #test(version(1, 2, 3).minor, 2) #test(version(1, 2, 3).patch, 3) ---- +--- version-type --- // Test the type of `sys.version` #test(type(sys.version), version) diff --git a/tests/suite/introspection/counter.typ b/tests/suite/introspection/counter.typ new file mode 100644 index 0000000000..8a5315f956 --- /dev/null +++ b/tests/suite/introspection/counter.typ @@ -0,0 +1,78 @@ +// Test counters. + +--- counter-basic-1 --- +// Count with string key. +#let mine = counter("mine!") + +Final: #context mine.final().at(0) \ +#mine.step() +First: #context mine.display() \ +#mine.update(7) +#context mine.display("1 of 1", both: true) \ +#mine.step() +#mine.step() +Second: #context mine.display("I") +#mine.update(n => n * 2) +#mine.step() + +--- counter-basic-2 --- +// Test `counter`. +#let c = counter("heading") +#c.update(2) +#c.update(n => n + 2) +#context test(c.get(), (4,)) +#c.update(n => n - 3) +#context test(c.at(here()), (1,)) + +--- counter-label --- +// Count labels. +#let label = +#let count = context counter(label).display() +#let elem(it) = [#box(it) #label] + +#elem[hey, there!] #count \ +#elem[more here!] #count + +--- counter-heading --- +// Count headings. +#set heading(numbering: "1.a.") +#show heading: set text(10pt) +#counter(heading).step() + += Alpha +In #context counter(heading).display() +== Beta + +#set heading(numbering: none) += Gamma +#heading(numbering: "I.")[Delta] + +At Beta, it was #context { + let it = query(heading).find(it => it.body == [Beta]) + numbering(it.numbering, ..counter(heading).at(it.location())) +} + +--- counter-page --- +#set page(height: 50pt, margin: (bottom: 20pt, rest: 10pt)) +#lorem(12) +#set page(numbering: "(i)") +#lorem(6) +#pagebreak() +#set page(numbering: "1 / 1") +#counter(page).update(1) +#lorem(20) + +--- counter-figure --- +// Count figures. +#figure(numbering: "A", caption: [Four 'A's], kind: image, supplement: "Figure")[_AAAA!_] +#figure(numbering: none, caption: [Four 'B's], kind: image, supplement: "Figure")[_BBBB!_] +#figure(caption: [Four 'C's], kind: image, supplement: "Figure")[_CCCC!_] +#counter(figure.where(kind: image)).update(n => n + 3) +#figure(caption: [Four 'D's], kind: image, supplement: "Figure")[_DDDD!_] + +--- counter-at-no-context --- +// Test `counter.at` outside of context. +// Error: 2-28 can only be used when context is known +// Hint: 2-28 try wrapping this in a `context` expression +// Hint: 2-28 the `context` expression should wrap everything that depends on this function +#counter("key").at(