diff --git a/Cargo.lock b/Cargo.lock index 97703229ad..58c2cb44c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -928,9 +928,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "hayagriva" @@ -941,7 +941,7 @@ dependencies = [ "biblatex", "ciborium", "citationberg", - "indexmap 2.7.1", + "indexmap 2.12.1", "paste", "roman-numerals-rs", "serde", @@ -1020,7 +1020,7 @@ dependencies = [ "log", "rustc-hash", "smallvec", - "zune-jpeg", + "zune-jpeg 0.4.14", ] [[package]] @@ -1282,8 +1282,8 @@ dependencies = [ "image-webp", "num-traits", "png", - "zune-core", - "zune-jpeg", + "zune-core 0.4.12", + "zune-jpeg 0.4.14", ] [[package]] @@ -1320,14 +1320,15 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.16.1", "rayon", "serde", + "serde_core", ] [[package]] @@ -1437,9 +1438,9 @@ dependencies = [ [[package]] name = "krilla" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199be5f63da6e19b71051fd5276258a8e55449ac48e2e7492c68238f38ca9f3b" +checksum = "a0ddfec86fec13d068075e14f22a7e217c281f3ed69ddcb427bf3f5d504fd674" dependencies = [ "base64", "bumpalo", @@ -1450,6 +1451,7 @@ dependencies = [ "hayro-write", "image-webp", "imagesize 0.14.0", + "indexmap 2.12.1", "once_cell", "pdf-writer", "png", @@ -1463,14 +1465,14 @@ dependencies = [ "tiny-skia-path", "xmp-writer", "yoke 0.8.0", - "zune-jpeg", + "zune-jpeg 0.5.5", ] [[package]] name = "krilla-svg" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3eec075c9507dfdbfb4b9bc3b2aeac074ed422b61bcfd93517616d6b3d19c3" +checksum = "f485e1a850201a01dcd8d73e7cf09f2cd4c4cc85c2cd296359094d49336d8ef7" dependencies = [ "flate2", "fontdb", @@ -1857,7 +1859,7 @@ dependencies = [ "bitvec", "crossbeam-channel", "filetime", - "indexmap 2.7.1", + "indexmap 2.12.1", "libdeflater", "log", "rayon", @@ -2014,7 +2016,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ "base64", - "indexmap 2.7.1", + "indexmap 2.12.1", "quick-xml 0.32.0", "serde", "time", @@ -2280,7 +2282,7 @@ dependencies = [ "svgtypes", "tiny-skia", "usvg", - "zune-jpeg", + "zune-jpeg 0.4.14", ] [[package]] @@ -2435,18 +2437,28 @@ checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -2492,7 +2504,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.12.1", "itoa", "ryu", "serde", @@ -2906,7 +2918,7 @@ version = "0.22.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.12.1", "serde", "serde_spanned", "toml_datetime", @@ -2941,7 +2953,7 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "typst" -version = "0.14.0" +version = "0.14.1" dependencies = [ "comemo", "ecow", @@ -2959,13 +2971,13 @@ dependencies = [ [[package]] name = "typst-assets" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712f5e2c4134bcf22f292086aa515caff00a21d54c8b30c77c4fc1227442a89a" +checksum = "98b250f6c1ddd0f435db2283c7eb36a9b72bee34d6397625bf7f817c64ad7c62" [[package]] name = "typst-cli" -version = "0.14.0" +version = "0.14.1" dependencies = [ "chrono", "clap", @@ -3011,12 +3023,12 @@ dependencies = [ [[package]] name = "typst-dev-assets" -version = "0.14.0" -source = "git+https://github.com/typst/typst-dev-assets?tag=v0.14.0#0b12d75772199e07294749ffe078ea030b857746" +version = "0.14.1" +source = "git+https://github.com/typst/typst-dev-assets?tag=v0.14.1#03addcfb64f3f95209464a521e6aa49645bd271a" [[package]] name = "typst-docs" -version = "0.14.0" +version = "0.14.1" dependencies = [ "clap", "codex", @@ -3043,11 +3055,11 @@ dependencies = [ [[package]] name = "typst-eval" -version = "0.14.0" +version = "0.14.1" dependencies = [ "comemo", "ecow", - "indexmap 2.7.1", + "indexmap 2.12.1", "rustc-hash", "stacker", "toml", @@ -3061,7 +3073,7 @@ dependencies = [ [[package]] name = "typst-fuzz" -version = "0.14.0" +version = "0.14.1" dependencies = [ "comemo", "libfuzzer-sys", @@ -3073,7 +3085,7 @@ dependencies = [ [[package]] name = "typst-html" -version = "0.14.0" +version = "0.14.1" dependencies = [ "bumpalo", "comemo", @@ -3092,7 +3104,7 @@ dependencies = [ [[package]] name = "typst-ide" -version = "0.14.0" +version = "0.14.1" dependencies = [ "comemo", "ecow", @@ -3109,7 +3121,7 @@ dependencies = [ [[package]] name = "typst-kit" -version = "0.14.0" +version = "0.14.1" dependencies = [ "dirs", "ecow", @@ -3133,7 +3145,7 @@ dependencies = [ [[package]] name = "typst-layout" -version = "0.14.0" +version = "0.14.1" dependencies = [ "az", "bumpalo", @@ -3167,7 +3179,7 @@ dependencies = [ [[package]] name = "typst-library" -version = "0.14.0" +version = "0.14.1" dependencies = [ "az", "bitflags 2.9.1", @@ -3187,7 +3199,7 @@ dependencies = [ "icu_provider", "icu_provider_blob", "image", - "indexmap 2.7.1", + "indexmap 2.12.1", "kamadak-exif", "kurbo 0.12.0", "lipsum", @@ -3232,7 +3244,7 @@ dependencies = [ [[package]] name = "typst-macros" -version = "0.14.0" +version = "0.14.1" dependencies = [ "heck", "proc-macro2", @@ -3242,14 +3254,14 @@ dependencies = [ [[package]] name = "typst-pdf" -version = "0.14.0" +version = "0.14.1" dependencies = [ "az", "bytemuck", "comemo", "ecow", "image", - "indexmap 2.7.1", + "indexmap 2.12.1", "infer", "krilla", "krilla-svg", @@ -3266,7 +3278,7 @@ dependencies = [ [[package]] name = "typst-realize" -version = "0.14.0" +version = "0.14.1" dependencies = [ "arrayvec", "bumpalo", @@ -3282,7 +3294,7 @@ dependencies = [ [[package]] name = "typst-render" -version = "0.14.0" +version = "0.14.1" dependencies = [ "bytemuck", "comemo", @@ -3300,7 +3312,7 @@ dependencies = [ [[package]] name = "typst-svg" -version = "0.14.0" +version = "0.14.1" dependencies = [ "base64", "comemo", @@ -3322,7 +3334,7 @@ dependencies = [ [[package]] name = "typst-syntax" -version = "0.14.0" +version = "0.14.1" dependencies = [ "ecow", "rustc-hash", @@ -3339,7 +3351,7 @@ dependencies = [ [[package]] name = "typst-tests" -version = "0.14.0" +version = "0.14.1" dependencies = [ "bitflags 2.9.1", "clap", @@ -3366,7 +3378,7 @@ dependencies = [ [[package]] name = "typst-timing" -version = "0.14.0" +version = "0.14.1" dependencies = [ "parking_lot", "serde", @@ -3376,7 +3388,7 @@ dependencies = [ [[package]] name = "typst-utils" -version = "0.14.0" +version = "0.14.1" dependencies = [ "once_cell", "portable-atomic", @@ -3688,9 +3700,9 @@ dependencies = [ [[package]] name = "wasmi" -version = "0.51.1" +version = "0.51.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4f6b71d5cb04a4615b9a8a2e522ba284c491ad847afd9e905d89be15e3efc0" +checksum = "118030b2d125bc893e0cc5b1ce156eb41461f4373308cb5f2e3c698533b5e547" dependencies = [ "spin", "wasmi_collections", @@ -3701,24 +3713,24 @@ dependencies = [ [[package]] name = "wasmi_collections" -version = "0.51.1" +version = "0.51.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a4a11fa090c4d742e5a77dbbc8efbbe1aa151db7335ca6850232e6cafbb1023" +checksum = "653fa20efc966818934524dceb54b1b81e6f845bbcc2e155d6e9fc32becf667e" [[package]] name = "wasmi_core" -version = "0.51.1" +version = "0.51.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3e422fc1f4df78c9ded6ed48c4ca6d1f55f4609f04c99962fc07532e4db61d" +checksum = "65701f60308c7e46cca85f273ad17e80c60998cc63d3fa168ac393f62b123038" dependencies = [ "libm", ] [[package]] name = "wasmi_ir" -version = "0.51.1" +version = "0.51.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13fe9f9f1747ec81644e764c4dc798f063f5d54a495f0a3b4a375bce9af65399" +checksum = "a6539f63bf2a6838f27876e2877f0b00a088e84f91e92445620cc6b977fde032" dependencies = [ "wasmi_core", ] @@ -3873,7 +3885,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "886614b5ce857341226aa091f3c285e450683894acaaa7887f366c361efef79d" dependencies = [ "font-types", - "indexmap 2.7.1", + "indexmap 2.12.1", "kurbo 0.12.0", "log", "read-fonts", @@ -4100,7 +4112,7 @@ dependencies = [ "arbitrary", "crc32fast", "flate2", - "indexmap 2.7.1", + "indexmap 2.12.1", "memchr", "zopfli", ] @@ -4131,11 +4143,26 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" +[[package]] +name = "zune-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773" + [[package]] name = "zune-jpeg" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" dependencies = [ - "zune-core", + "zune-core 0.4.12", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6fb7703e32e9a07fb3f757360338b3a567a5054f21b5f52a666752e333d58e" +dependencies = [ + "zune-core 0.5.0", ] diff --git a/Cargo.toml b/Cargo.toml index 946fcb148a..91712f246b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ default-members = ["crates/typst-cli"] resolver = "2" [workspace.package] -version = "0.14.0" -rust-version = "1.88" # also change in ci.yml +version = "0.14.1" +rust-version = "1.89" # also change in ci.yml authors = ["The Typst Project Developers"] edition = "2024" homepage = "https://typst.app" @@ -16,24 +16,24 @@ keywords = ["typst"] readme = "README.md" [workspace.dependencies] -typst = { path = "crates/typst", version = "0.14.0" } -typst-cli = { path = "crates/typst-cli", version = "0.14.0" } -typst-eval = { path = "crates/typst-eval", version = "0.14.0" } -typst-html = { path = "crates/typst-html", version = "0.14.0" } -typst-ide = { path = "crates/typst-ide", version = "0.14.0" } -typst-kit = { path = "crates/typst-kit", version = "0.14.0" } -typst-layout = { path = "crates/typst-layout", version = "0.14.0" } -typst-library = { path = "crates/typst-library", version = "0.14.0" } -typst-macros = { path = "crates/typst-macros", version = "0.14.0" } -typst-pdf = { path = "crates/typst-pdf", version = "0.14.0" } -typst-realize = { path = "crates/typst-realize", version = "0.14.0" } -typst-render = { path = "crates/typst-render", version = "0.14.0" } -typst-svg = { path = "crates/typst-svg", version = "0.14.0" } -typst-syntax = { path = "crates/typst-syntax", version = "0.14.0" } -typst-timing = { path = "crates/typst-timing", version = "0.14.0" } -typst-utils = { path = "crates/typst-utils", version = "0.14.0" } -typst-assets = "0.14.0" -typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", tag = "v0.14.0" } +typst = { path = "crates/typst", version = "0.14.1" } +typst-cli = { path = "crates/typst-cli", version = "0.14.1" } +typst-eval = { path = "crates/typst-eval", version = "0.14.1" } +typst-html = { path = "crates/typst-html", version = "0.14.1" } +typst-ide = { path = "crates/typst-ide", version = "0.14.1" } +typst-kit = { path = "crates/typst-kit", version = "0.14.1" } +typst-layout = { path = "crates/typst-layout", version = "0.14.1" } +typst-library = { path = "crates/typst-library", version = "0.14.1" } +typst-macros = { path = "crates/typst-macros", version = "0.14.1" } +typst-pdf = { path = "crates/typst-pdf", version = "0.14.1" } +typst-realize = { path = "crates/typst-realize", version = "0.14.1" } +typst-render = { path = "crates/typst-render", version = "0.14.1" } +typst-svg = { path = "crates/typst-svg", version = "0.14.1" } +typst-syntax = { path = "crates/typst-syntax", version = "0.14.1" } +typst-timing = { path = "crates/typst-timing", version = "0.14.1" } +typst-utils = { path = "crates/typst-utils", version = "0.14.1" } +typst-assets = "0.14.1" +typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", tag = "v0.14.1" } arrayvec = "0.7.4" az = "1.2" base64 = "0.22" @@ -77,8 +77,8 @@ image = { version = "0.25.5", default-features = false, features = ["png", "jpeg indexmap = { version = "2", features = ["serde"] } infer = { version = "0.19.0", default-features = false } kamadak-exif = "0.6" -krilla = { version = "0.5.0", default-features = false, features = ["raster-images", "comemo", "rayon", "pdf"] } -krilla-svg = "0.2" +krilla = { version = "0.6.0", default-features = false, features = ["raster-images", "comemo", "rayon", "pdf"] } +krilla-svg = "0.3.0" kurbo = "0.12" libfuzzer-sys = "0.4" lipsum = "0.9" @@ -141,7 +141,7 @@ ureq = { version = "2", default-features = false, features = ["native-tls", "gzi usvg = { version = "0.45", default-features = false, features = ["text"] } utf8_iter = "1.0.4" walkdir = "2" -wasmi = { version = "0.51.0", default-features = false, features = ["simd"] } +wasmi = { version = "0.51.2", default-features = false, features = ["simd"] } web-sys = "0.3" xmlparser = "0.13.5" xmlwriter = "0.1.0" diff --git a/crates/typst-cli/src/deps.rs b/crates/typst-cli/src/deps.rs index 921400a88d..bdd97f8c48 100644 --- a/crates/typst-cli/src/deps.rs +++ b/crates/typst-cli/src/deps.rs @@ -64,7 +64,7 @@ fn write_deps_make( dest: &Output, outputs: &[Output], ) -> io::Result<()> { - let mut dest = dest.open()?; + let mut buffer = Vec::new(); for (i, output) in outputs.iter().enumerate() { let path = match output { Output::Path(path) => path.as_os_str(), @@ -82,10 +82,14 @@ fn write_deps_make( // processed. let Some(string) = path.to_str() else { continue }; if i != 0 { - dest.write_all(b" ")?; + buffer.write_all(b" ")?; } - dest.write_all(munge(string).as_bytes())?; + buffer.write_all(munge(string).as_bytes())?; } + + // Only create the deps file in case of valid output paths. + let mut dest = dest.open()?; + dest.write_all(&buffer)?; dest.write_all(b":")?; for dep in relative_dependencies(world)? { diff --git a/crates/typst-html/src/rules.rs b/crates/typst-html/src/rules.rs index 8bca088d0d..06dd0584b1 100644 --- a/crates/typst-html/src/rules.rs +++ b/crates/typst-html/src/rules.rs @@ -8,7 +8,7 @@ use typst_library::foundations::{ Content, Context, NativeElement, NativeRuleMap, ShowFn, Smart, StyleChain, Target, }; use typst_library::introspection::Counter; -use typst_library::layout::resolve::{Cell, CellGrid, Entry}; +use typst_library::layout::resolve::{Cell, CellGrid, Entry, Header}; use typst_library::layout::{ BlockBody, BlockElem, BoxElem, HElem, OuterVAlignment, Sizing, }; @@ -563,10 +563,25 @@ fn show_cellgrid(grid: &CellGrid, styles: StyleChain) -> Content { // TODO(subfooters): similarly to headers, take consecutive footers from // the end for 'tfoot'. let footer = grid.footer.as_ref().map(|ft| { - let rows = rows.drain(ft.start..); + // Convert from gutter to non-gutter coordinates. Use ceil as it might + // include the previous gutter row + // (cf. typst-library/layout/grid/resolve.rs). + let footer_start = if grid.has_gutter { ft.start.div_ceil(2) } else { ft.start }; + let rows = rows.drain(footer_start..); elem(tag::tfoot, Content::sequence(rows.map(|row| tr(tag::td, row)))) }); + // Header range converting from gutter (doubled) to non-gutter coordinates. + let header_range = |hd: &Header| { + if grid.has_gutter { + // Use ceil as it might be `2 * row_amount - 1` if the header is at + // the end (cf. typst-library/layout/grid/resolve.rs). + hd.range.start / 2..hd.range.end.div_ceil(2) + } else { + hd.range.clone() + } + }; + // Store all consecutive headers at the start in 'thead'. All remaining // headers are just 'th' rows across the table body. let mut consecutive_header_end = 0; @@ -574,15 +589,16 @@ fn show_cellgrid(grid: &CellGrid, styles: StyleChain) -> Content { .headers .iter() .take_while(|hd| { - let is_consecutive = hd.range.start == consecutive_header_end; - consecutive_header_end = hd.range.end; + let range = header_range(hd); + let is_consecutive = range.start == consecutive_header_end; + consecutive_header_end = range.end; is_consecutive }) .count(); let (y_offset, header) = if first_mid_table_header > 0 { let removed_header_rows = - grid.headers.get(first_mid_table_header - 1).unwrap().range.end; + header_range(grid.headers.get(first_mid_table_header - 1).unwrap()).end; let rows = rows.drain(..removed_header_rows); ( @@ -601,10 +617,11 @@ fn show_cellgrid(grid: &CellGrid, styles: StyleChain) -> Content { let mut body = Content::sequence(rows.into_iter().enumerate().map(|(relative_y, row)| { let y = relative_y + y_offset; - if let Some(current_header) = - grid.headers.get(next_header).filter(|h| h.range.contains(&y)) + if let Some(current_header_range) = + grid.headers.get(next_header).map(|h| header_range(h)) + && current_header_range.contains(&y) { - if y + 1 == current_header.range.end { + if y + 1 == current_header_range.end { next_header += 1; } diff --git a/crates/typst-layout/src/inline/line.rs b/crates/typst-layout/src/inline/line.rs index 646522002f..f4d93517f1 100644 --- a/crates/typst-layout/src/inline/line.rs +++ b/crates/typst-layout/src/inline/line.rs @@ -222,9 +222,9 @@ fn collect_items<'a>( // Add fallback text to expand the line height, if necessary. if !items.iter().any(|item| matches!(item, Item::Text(_))) - && let Some(fallback) = fallback + && let Some((idx, fallback)) = fallback { - items.push(fallback, LogicalIndex::FALLBACK_TEXT); + items.push(fallback, idx); } } @@ -287,7 +287,7 @@ fn collect_range<'a>( range: Range, trim: &Trim, items: &mut Items<'a>, - fallback: &mut Option>, + fallback: &mut Option<(LogicalIndex, ItemEntry<'a>)>, ) { for (i, (subrange, item)) in p.slice(range.clone()) { let idx = LogicalIndex::from_item_index(i); @@ -310,7 +310,7 @@ fn collect_range<'a>( // When there is no text, still keep this as a fallback item, which // we can use to force a non-zero line-height when the line doesn't // contain any other text. - *fallback = Some(ItemEntry::from(Item::Text(shaped.empty()))); + *fallback = Some((idx, ItemEntry::from(Item::Text(shaped.empty())))); continue; } @@ -782,7 +782,6 @@ pub struct LogicalIndex(usize); impl LogicalIndex { const START_HYPHEN: Self = Self(0); - const FALLBACK_TEXT: Self = Self(usize::MAX - 1); const END_HYPHEN: Self = Self(usize::MAX); /// Create a logical index from the index of an item in the [`p.items`](Preparation::items). diff --git a/crates/typst-layout/src/inline/shaping.rs b/crates/typst-layout/src/inline/shaping.rs index 847f2c0ca5..7346c54d85 100644 --- a/crates/typst-layout/src/inline/shaping.rs +++ b/crates/typst-layout/src/inline/shaping.rs @@ -1100,17 +1100,17 @@ fn determine_shift( // "subs"/"sups" to the feature list if supported by the font. // In case of a problem, we just early exit let gsub = font.rusty().tables().gsub?; - let subtable_index = - gsub.features.find(settings.kind.feature())?.lookup_indices.get(0)?; - let coverage = gsub - .lookups - .get(subtable_index)? - .subtables - .get::(0)? - .coverage(); + let lookups = gsub.features.find(settings.kind.feature())?.lookup_indices; text.chars() .all(|c| { - font.rusty().glyph_index(c).is_some_and(|i| coverage.contains(i)) + let Some(i) = font.rusty().glyph_index(c) else { return false }; + lookups + .into_iter() + .flat_map(|i| gsub.lookups.get(i)) + .flat_map(|lookup| { + lookup.subtables.into_iter::() + }) + .any(|subtable| subtable.coverage().contains(i)) }) .then(|| { // If we can use the OpenType feature, we can keep the text diff --git a/crates/typst-layout/src/math/fragment.rs b/crates/typst-layout/src/math/fragment.rs index 1b1072d2a8..43be9ca82a 100644 --- a/crates/typst-layout/src/math/fragment.rs +++ b/crates/typst-layout/src/math/fragment.rs @@ -839,7 +839,16 @@ fn assemble( } match axis { - Axis::X => base.size.x = full, + Axis::X => { + base.size.x = full; + let (ascent, descent) = glyphs + .iter() + .filter_map(|glyph| ascent_descent(&base.item.font, GlyphId(glyph.id))) + .reduce(|(ma, md), (a, d)| (ma.max(a), md.max(d))) + .unwrap_or((Em::zero(), Em::zero())); + base.baseline = Some(ascent.at(base.item.size)); + base.size.y = (ascent + descent).at(base.item.size); + } Axis::Y => { base.baseline = None; base.size.y = full; diff --git a/crates/typst-layout/src/math/mat.rs b/crates/typst-layout/src/math/mat.rs index 743c8b1791..92e7316288 100644 --- a/crates/typst-layout/src/math/mat.rs +++ b/crates/typst-layout/src/math/mat.rs @@ -7,6 +7,7 @@ use typst_library::math::{Augment, AugmentOffsets, CasesElem, MatElem, VecElem}; use typst_library::text::TextElem; use typst_library::visualize::{FillRule, FixedStroke, Geometry, LineCap, Shape}; use typst_syntax::Span; +use unicode_math_class::MathClass; use super::{ AlignmentResult, DELIM_SHORT_FALL, FrameFragment, GlyphFragment, LeftRightAlternator, @@ -348,6 +349,7 @@ fn layout_delimiters( ctx.layout_into_fragment(&SymbolElem::packed(left_c).spanned(span), styles)?; left.stretch_vertical(ctx, target, short_fall); left.center_on_axis(); + left.set_class(MathClass::Opening); ctx.push(left); } @@ -358,6 +360,7 @@ fn layout_delimiters( ctx.layout_into_fragment(&SymbolElem::packed(right_c).spanned(span), styles)?; right.stretch_vertical(ctx, target, short_fall); right.center_on_axis(); + right.set_class(MathClass::Closing); ctx.push(right); } diff --git a/crates/typst-layout/src/math/text.rs b/crates/typst-layout/src/math/text.rs index f15936d6b4..66a718c4ca 100644 --- a/crates/typst-layout/src/math/text.rs +++ b/crates/typst-layout/src/math/text.rs @@ -97,8 +97,6 @@ fn layout_inline_text( .chars() .flat_map(|c| to_style(c, MathStyle::select(c, variant, bold, italic))) .collect(); - - let spaced = styled_text.graphemes(true).nth(1).is_some(); let elem = TextElem::packed(styled_text).spanned(span); // There isn't a natural width for a paragraph in a math environment; @@ -118,7 +116,7 @@ fn layout_inline_text( Ok(FrameFragment::new(styles, frame) .with_class(MathClass::Alphabetic) .with_text_like(true) - .with_spaced(spaced)) + .with_spaced(true)) } } diff --git a/crates/typst-library/src/diag.rs b/crates/typst-library/src/diag.rs index 11d09a1b18..fe9026f348 100644 --- a/crates/typst-library/src/diag.rs +++ b/crates/typst-library/src/diag.rs @@ -1,5 +1,6 @@ //! Diagnostics. +use std::backtrace::{Backtrace, BacktraceStatus}; use std::fmt::{self, Display, Formatter, Write as _}; use std::io; use std::path::{Path, PathBuf}; @@ -868,3 +869,55 @@ pub fn format_xml_like_error(format: &str, error: roxmltree::Error) -> LoadError LoadError { pos: pos.into(), message } } + +/// Asserts a condition, generating an internal compiler error with the provided +/// message on failure. +#[track_caller] +pub fn assert_internal(cond: bool, msg: &str) -> HintedStrResult<()> { + if !cond { Err(internal_error(msg)) } else { Ok(()) } +} + +/// Generates an internal compiler error with the provided message. +#[track_caller] +pub fn panic_internal(msg: &str) -> HintedStrResult<()> { + Err(internal_error(msg)) +} + +/// Adds a method analogous to [`Option::expect`] that raises an internal +/// compiler error instead of panicking. +pub trait ExpectInternal { + /// Extracts the value, producing an internal error if `self` is `None`. + fn expect_internal(self, msg: &str) -> HintedStrResult; +} + +impl ExpectInternal for Option { + #[track_caller] + fn expect_internal(self, msg: &str) -> HintedStrResult { + match self { + Some(val) => Ok(val), + None => Err(internal_error(msg)), + } + } +} + +/// The shared internal implementation of [`assert_internal`] and +/// [`expect_internal`]. +#[track_caller] +fn internal_error(msg: &str) -> HintedString { + let loc = std::panic::Location::caller(); + let mut error = error!( + "internal error: {msg} (occurred at {loc})"; + hint: "please report this as a bug" + ); + + if cfg!(debug_assertions) { + let backtrace = Backtrace::capture(); + if backtrace.status() == BacktraceStatus::Captured { + error.hint(eco_format!("compiler backtrace:\n{backtrace}")); + } else { + error.hint("set `RUST_BACKTRACE` to `1` or `full` to capture a backtrace"); + } + } + + error +} diff --git a/crates/typst-library/src/foundations/array.rs b/crates/typst-library/src/foundations/array.rs index 16105f96c9..0c2e2f5326 100644 --- a/crates/typst-library/src/foundations/array.rs +++ b/crates/typst-library/src/foundations/array.rs @@ -720,6 +720,7 @@ impl Array { last: Option, /// What to return if the array is empty. #[named] + #[default] default: Option, ) -> StrResult { let len = self.0.len(); @@ -890,6 +891,10 @@ impl Array { #[named] by: Option, ) -> SourceResult { + // We use `glidesort` instead of the standard library sorting algorithm + // to prevent panics in case the comparison function does not define a + // valid order (see https://github.com/typst/typst/pull/5627 and + // https://github.com/typst/typst/issues/6285). match by { Some(by) => { let mut are_in_order = |mut x, mut y| { @@ -910,10 +915,7 @@ impl Array { } } }; - // If a comparison function is provided, we use `glidesort` - // instead of the standard library sorting algorithm to prevent - // panics in case the comparison function does not define a - // valid order (see https://github.com/typst/typst/pull/5627). + let mut result = Ok(()); let mut vec = self.0.into_iter().enumerate().collect::>(); glidesort::sort_by(&mut vec, |(i, x), (j, y)| { @@ -966,12 +968,10 @@ impl Array { Some(f) => f.call(engine, context, [x]), None => Ok(x), }; - // If no comparison function is provided, we know the order is - // valid, so we can use the standard library sort and prevent an - // extra allocation. + let mut result = Ok(()); let mut vec = self.0; - vec.make_mut().sort_by(|a, b| { + glidesort::sort_by(vec.make_mut(), |a, b| { match (key_of(a.clone()), key_of(b.clone())) { (Ok(a), Ok(b)) => ops::compare(&a, &b).unwrap_or_else(|err| { if result.is_ok() { diff --git a/crates/typst-library/src/foundations/calc.rs b/crates/typst-library/src/foundations/calc.rs index e2c7eccee8..e7a5955eff 100644 --- a/crates/typst-library/src/foundations/calc.rs +++ b/crates/typst-library/src/foundations/calc.rs @@ -559,6 +559,9 @@ fn binom_impl(n: u64, k: u64) -> Option { /// 2つの整数値の最大公約数。 /// +/// This will error if the result of integer division would be larger than the +/// maximum 64-bit signed integer. +/// /// ```example /// #calc.gcd(7, 42) /// ``` @@ -568,15 +571,15 @@ pub fn gcd( a: i64, /// 2つ目の整数値。 b: i64, -) -> i64 { +) -> StrResult { let (mut a, mut b) = (a, b); while b != 0 { let temp = b; - b = a % b; + b = a.checked_rem(b).ok_or_else(too_large)?; a = temp; } - a.abs() + Ok(a.abs()) } /// 2つの整数値の最小公倍数。 @@ -595,7 +598,7 @@ pub fn lcm( return Ok(a.abs()); } - Ok(a.checked_div(gcd(a, b)) + Ok(a.checked_div(gcd(a, b)?) .and_then(|gcd| gcd.checked_mul(b)) .map(|v| v.abs()) .ok_or_else(too_large)?) @@ -892,7 +895,9 @@ pub fn rem( dividend .apply2( divisor.v, - |a, b| Some(DecNum::Int(a % b)), + // `checked_rem` can only overflow on `i64::MIN % -1` which is + // mathematically zero. + |a, b| Some(DecNum::Int(a.checked_rem(b).unwrap_or(0))), |a, b| Some(DecNum::Float(a % b)), |a, b| a.checked_rem(b).map(DecNum::Decimal), ) @@ -906,6 +911,9 @@ pub fn rem( /// /// この計算の結果は、商を被除数が除数の`{n}`倍以上になる整数`{n}`へ丸めた値です。 /// +/// This can error if the resulting number is larger than the maximum value or +/// smaller than the minimum value for its type. +/// /// ```example /// #calc.div-euclid(7, 3) \ /// #calc.div-euclid(7, -3) \ @@ -929,7 +937,7 @@ pub fn div_euclid( dividend .apply2( divisor.v, - |a, b| Some(DecNum::Int(a.div_euclid(b))), + |a, b| a.checked_div_euclid(b).map(DecNum::Int), |a, b| Some(DecNum::Float(a.div_euclid(b))), |a, b| a.checked_div_euclid(b).map(DecNum::Decimal), ) @@ -969,7 +977,9 @@ pub fn rem_euclid( dividend .apply2( divisor.v, - |a, b| Some(DecNum::Int(a.rem_euclid(b))), + // `checked_rem_euclid` can only overflow on `i64::MIN % -1` which + // is mathematically zero. + |a, b| Some(DecNum::Int(a.checked_rem_euclid(b).unwrap_or(0))), |a, b| Some(DecNum::Float(a.rem_euclid(b))), |a, b| a.checked_rem_euclid(b).map(DecNum::Decimal), ) @@ -981,7 +991,9 @@ pub fn rem_euclid( /// 2つの数値の商(切り捨て除算)を計算します。 /// -/// この関数は常に[整数値]($int)を返し、結果となる[`float`]や[`decimal`]が64ビット符号付き整数の最大値より大きい、または最小値より小さい場合はエラーとなることに注意してください。 +/// Note that this function will always return an [integer]($int), and will +/// error if the resulting number is larger than the maximum 64-bit signed +/// integer or smaller than the minimum for that type. /// /// ```example /// $ "quo"(a, b) &= floor(a/b) \ @@ -1003,7 +1015,7 @@ pub fn quo( let divided = dividend .apply2( divisor.v, - |a, b| Some(DecNum::Int(a / b)), + |a, b| a.checked_div(b).map(DecNum::Int), |a, b| Some(DecNum::Float(a / b)), |a, b| a.checked_div(b).map(DecNum::Decimal), ) diff --git a/crates/typst-library/src/foundations/datetime.rs b/crates/typst-library/src/foundations/datetime.rs index 5a02ce95cf..a6181b248c 100644 --- a/crates/typst-library/src/foundations/datetime.rs +++ b/crates/typst-library/src/foundations/datetime.rs @@ -276,6 +276,12 @@ impl Datetime { /// 現在の日付を取得。 /// + /// In the CLI, this can be overridden with the `--creation-timestamp` + /// argument or by setting the + /// [`SOURCE_DATE_EPOCH`](https://reproducible-builds.org/specs/source-date-epoch/) + /// environment variable. In both cases, the value should be given as a UNIX + /// timestamp. + /// /// ```example /// Today's date is /// #datetime.today().display(). diff --git a/crates/typst-library/src/foundations/selector.rs b/crates/typst-library/src/foundations/selector.rs index 4fa9238968..abc5898e09 100644 --- a/crates/typst-library/src/foundations/selector.rs +++ b/crates/typst-library/src/foundations/selector.rs @@ -38,7 +38,7 @@ pub use crate::__select_where as select_where; /// A filter for selecting elements within the document. /// /// To construct a selector you can: -/// - use an element [function] +/// - use an [element function]($function/#element-functions) /// - filter for an element function with [specific fields]($function.where) /// - use a [string]($str) or [regular expression]($regex) /// - use a [`{