Remove showbits-common

This commit is contained in:
Joscha 2025-03-01 23:24:32 +01:00
parent 6e6dfb2b66
commit a55109c677
17 changed files with 18 additions and 1789 deletions

252
Cargo.lock generated
View file

@ -333,20 +333,6 @@ name = "bytemuck"
version = "1.21.0" version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@ -560,29 +546,6 @@ dependencies = [
"libm", "libm",
] ]
[[package]]
name = "cosmic-text"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2"
dependencies = [
"bitflags 2.8.0",
"fontdb 0.16.2",
"log",
"rangemap",
"rayon",
"rustc-hash",
"rustybuzz 0.14.1",
"self_cell",
"swash",
"sys-locale",
"ttf-parser 0.21.1",
"unicode-bidi",
"unicode-linebreak",
"unicode-script",
"unicode-segmentation",
]
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.17" version = "0.2.17"
@ -904,15 +867,6 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
[[package]]
name = "font-types"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3971f9a5ca983419cdc386941ba3b9e1feba01a0ab888adf78739feb2798492"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "fontconfig-parser" name = "fontconfig-parser"
version = "0.5.7" version = "0.5.7"
@ -922,20 +876,6 @@ dependencies = [
"roxmltree", "roxmltree",
] ]
[[package]]
name = "fontdb"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3"
dependencies = [
"fontconfig-parser",
"log",
"memmap2",
"slotmap",
"tinyvec",
"ttf-parser 0.20.0",
]
[[package]] [[package]]
name = "fontdb" name = "fontdb"
version = "0.21.0" version = "0.21.0"
@ -947,7 +887,7 @@ dependencies = [
"memmap2", "memmap2",
"slotmap", "slotmap",
"tinyvec", "tinyvec",
"ttf-parser 0.24.1", "ttf-parser",
] ]
[[package]] [[package]]
@ -1056,12 +996,6 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "grid"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36119f3a540b086b4e436bb2b588cf98a68863470e0e880f4d0842f112a3183a"
[[package]] [[package]]
name = "half" name = "half"
version = "2.4.1" version = "2.4.1"
@ -1650,16 +1584,6 @@ dependencies = [
"imgref", "imgref",
] ]
[[package]]
name = "mark"
version = "0.0.0"
source = "git+https://github.com/Garmelon/mark.git?rev=2a862a69d69abc64ddd7eefd1e1ff3d05ce3b6e4#2a862a69d69abc64ddd7eefd1e1ff3d05ce3b6e4"
dependencies = [
"image",
"palette",
"rand 0.9.0",
]
[[package]] [[package]]
name = "matchit" name = "matchit"
version = "0.8.4" version = "0.8.4"
@ -2055,7 +1979,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d15afa937836bf3d876f5a04ce28810c06045857bf46c3d0d31073b8aada5494" checksum = "d15afa937836bf3d876f5a04ce28810c06045857bf46c3d0d31073b8aada5494"
dependencies = [ dependencies = [
"ttf-parser 0.24.1", "ttf-parser",
] ]
[[package]] [[package]]
@ -2279,12 +2203,6 @@ dependencies = [
"zerocopy 0.8.20", "zerocopy 0.8.20",
] ]
[[package]]
name = "rangemap"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684"
[[package]] [[package]]
name = "rav1e" name = "rav1e"
version = "0.7.1" version = "0.7.1"
@ -2355,16 +2273,6 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "read-fonts"
version = "0.22.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69aacb76b5c29acfb7f90155d39759a29496aebb49395830e928a9703d2eec2f"
dependencies = [
"bytemuck",
"font-types",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.9" version = "0.5.9"
@ -2496,12 +2404,6 @@ version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.44" version = "0.38.44"
@ -2521,23 +2423,6 @@ version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
[[package]]
name = "rustybuzz"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c"
dependencies = [
"bitflags 2.8.0",
"bytemuck",
"libm",
"smallvec",
"ttf-parser 0.21.1",
"unicode-bidi-mirroring 0.2.0",
"unicode-ccc 0.2.0",
"unicode-properties",
"unicode-script",
]
[[package]] [[package]]
name = "rustybuzz" name = "rustybuzz"
version = "0.18.0" version = "0.18.0"
@ -2549,9 +2434,9 @@ dependencies = [
"core_maths", "core_maths",
"log", "log",
"smallvec", "smallvec",
"ttf-parser 0.24.1", "ttf-parser",
"unicode-bidi-mirroring 0.3.0", "unicode-bidi-mirroring",
"unicode-ccc 0.3.0", "unicode-ccc",
"unicode-properties", "unicode-properties",
"unicode-script", "unicode-script",
] ]
@ -2609,12 +2494,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "self_cell"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.218" version = "1.0.218"
@ -2712,23 +2591,6 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
name = "showbits-assets" name = "showbits-assets"
version = "0.0.0" version = "0.0.0"
[[package]]
name = "showbits-common"
version = "0.0.0"
dependencies = [
"anyhow",
"cosmic-text",
"image",
"mark",
"palette",
"paste",
"showbits-assets",
"taffy",
"typst",
"typst-assets",
"typst-render",
]
[[package]] [[package]]
name = "showbits-thermal-printer" name = "showbits-thermal-printer"
version = "0.0.0" version = "0.0.0"
@ -2803,16 +2665,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]]
name = "skrifa"
version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1c44ad1f6c5bdd4eefed8326711b7dbda9ea45dfd36068c427d332aa382cbe"
dependencies = [
"bytemuck",
"read-fonts",
]
[[package]] [[package]]
name = "slotmap" name = "slotmap"
version = "1.0.7" version = "1.0.7"
@ -2920,17 +2772,6 @@ dependencies = [
"siphasher", "siphasher",
] ]
[[package]]
name = "swash"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbd59f3f359ddd2c95af4758c18270eddd9c730dde98598023cdabff472c2ca2"
dependencies = [
"skrifa",
"yazi",
"zeno",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.98" version = "2.0.98"
@ -2981,15 +2822,6 @@ dependencies = [
"yaml-rust", "yaml-rust",
] ]
[[package]]
name = "sys-locale"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "system-deps" name = "system-deps"
version = "6.2.2" version = "6.2.2"
@ -3003,18 +2835,6 @@ dependencies = [
"version-compare", "version-compare",
] ]
[[package]]
name = "taffy"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50e3102cd96acfdb0b3f037cb06e9b40d3dca1d7c6e4742147441daf6f34e8a6"
dependencies = [
"arrayvec",
"grid",
"serde",
"slotmap",
]
[[package]] [[package]]
name = "tar" name = "tar"
version = "0.4.44" version = "0.4.44"
@ -3297,18 +3117,6 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "ttf-parser"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4"
[[package]]
name = "ttf-parser"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
[[package]] [[package]]
name = "ttf-parser" name = "ttf-parser"
version = "0.24.1" version = "0.24.1"
@ -3412,7 +3220,7 @@ dependencies = [
"ecow", "ecow",
"env_proxy", "env_proxy",
"flate2", "flate2",
"fontdb 0.21.0", "fontdb",
"native-tls", "native-tls",
"once_cell", "once_cell",
"openssl", "openssl",
@ -3443,9 +3251,9 @@ dependencies = [
"icu_provider_blob", "icu_provider_blob",
"icu_segmenter", "icu_segmenter",
"kurbo", "kurbo",
"rustybuzz 0.18.0", "rustybuzz",
"smallvec", "smallvec",
"ttf-parser 0.24.1", "ttf-parser",
"typst-assets", "typst-assets",
"typst-library", "typst-library",
"typst-macros", "typst-macros",
@ -3474,7 +3282,7 @@ dependencies = [
"csv", "csv",
"ecow", "ecow",
"flate2", "flate2",
"fontdb 0.21.0", "fontdb",
"hayagriva", "hayagriva",
"icu_properties", "icu_properties",
"icu_provider", "icu_provider",
@ -3494,7 +3302,7 @@ dependencies = [
"regex-syntax", "regex-syntax",
"roxmltree", "roxmltree",
"rust_decimal", "rust_decimal",
"rustybuzz 0.18.0", "rustybuzz",
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
@ -3503,7 +3311,7 @@ dependencies = [
"syntect", "syntect",
"time", "time",
"toml", "toml",
"ttf-parser 0.24.1", "ttf-parser",
"two-face", "two-face",
"typed-arena", "typed-arena",
"typst-assets", "typst-assets",
@ -3561,7 +3369,7 @@ dependencies = [
"pixglyph", "pixglyph",
"resvg", "resvg",
"tiny-skia", "tiny-skia",
"ttf-parser 0.24.1", "ttf-parser",
"typst-library", "typst-library",
"typst-macros", "typst-macros",
"typst-timing", "typst-timing",
@ -3578,7 +3386,7 @@ dependencies = [
"ecow", "ecow",
"flate2", "flate2",
"image", "image",
"ttf-parser 0.24.1", "ttf-parser",
"typst-library", "typst-library",
"typst-macros", "typst-macros",
"typst-timing", "typst-timing",
@ -3661,24 +3469,12 @@ version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
[[package]]
name = "unicode-bidi-mirroring"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86"
[[package]] [[package]]
name = "unicode-bidi-mirroring" name = "unicode-bidi-mirroring"
version = "0.3.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64af057ad7466495ca113126be61838d8af947f41d93a949980b2389a118082f" checksum = "64af057ad7466495ca113126be61838d8af947f41d93a949980b2389a118082f"
[[package]]
name = "unicode-ccc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656"
[[package]] [[package]]
name = "unicode-ccc" name = "unicode-ccc"
version = "0.3.0" version = "0.3.0"
@ -3691,12 +3487,6 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
[[package]]
name = "unicode-linebreak"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
[[package]] [[package]]
name = "unicode-math-class" name = "unicode-math-class"
version = "0.1.0" version = "0.1.0"
@ -3785,13 +3575,13 @@ dependencies = [
"base64", "base64",
"data-url", "data-url",
"flate2", "flate2",
"fontdb 0.21.0", "fontdb",
"imagesize", "imagesize",
"kurbo", "kurbo",
"log", "log",
"pico-args", "pico-args",
"roxmltree", "roxmltree",
"rustybuzz 0.18.0", "rustybuzz",
"simplecss", "simplecss",
"siphasher", "siphasher",
"strict-num", "strict-num",
@ -4146,12 +3936,6 @@ dependencies = [
"linked-hash-map", "linked-hash-map",
] ]
[[package]]
name = "yazi"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1"
[[package]] [[package]]
name = "yoke" name = "yoke"
version = "0.7.5" version = "0.7.5"
@ -4176,12 +3960,6 @@ dependencies = [
"synstructure", "synstructure",
] ]
[[package]]
name = "zeno"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.7.35" version = "0.7.35"

View file

@ -1,11 +1,6 @@
[workspace] [workspace]
resolver = "3" resolver = "3"
members = [ members = ["showbits-assets", "showbits-thermal-printer", "showbits-typst"]
"showbits-assets",
"showbits-common",
"showbits-thermal-printer",
"showbits-typst",
]
[workspace.package] [workspace.package]
version = "0.0.0" version = "0.0.0"
@ -15,18 +10,15 @@ edition = "2024"
anyhow = "1.0.96" anyhow = "1.0.96"
axum = "0.8.1" axum = "0.8.1"
clap = { version = "4.5.30", features = ["derive", "deprecated"] } clap = { version = "4.5.30", features = ["derive", "deprecated"] }
cosmic-text = "0.12.1"
escpos = "0.15.0" escpos = "0.15.0"
image = { version = "0.25.5", default-features = false } image = "0.25.5"
jiff = "0.2.1" jiff = "0.2.1"
mime_guess = "2.0.5" mime_guess = "2.0.5"
palette = "0.7.6" palette = "0.7.6"
paste = "1.0.15"
rand = "0.9.0" rand = "0.9.0"
rust-embed = "8.5.0" rust-embed = "8.5.0"
serde = { version = "1.0.218", features = ["derive"] } serde = { version = "1.0.218", features = ["derive"] }
showbits-assets.path = "./showbits-assets" showbits-assets.path = "./showbits-assets"
showbits-common.path = "./showbits-common"
showbits-typst.path = "./showbits-typst" showbits-typst.path = "./showbits-typst"
tokio = "1.43.0" tokio = "1.43.0"
typst = "0.13.0" typst = "0.13.0"
@ -34,15 +26,6 @@ typst-assets = { version = "0.13.0", features = ["fonts"] }
typst-kit = "0.13.0" typst-kit = "0.13.0"
typst-render = "0.13.0" typst-render = "0.13.0"
[workspace.dependencies.taffy]
version = "0.7.6"
default-features = false
features = ["std", "taffy_tree", "flexbox", "grid", "block_layout"]
[workspace.dependencies.mark]
git = "https://github.com/Garmelon/mark.git"
rev = "2a862a69d69abc64ddd7eefd1e1ff3d05ce3b6e4"
[workspace.lints] [workspace.lints]
rust.unsafe_code = { level = "forbid", priority = 1 } rust.unsafe_code = { level = "forbid", priority = 1 }
# Lint groups # Lint groups

View file

@ -1,20 +0,0 @@
[package]
name = "showbits-common"
version = { workspace = true }
edition = { workspace = true }
[dependencies]
anyhow = { workspace = true }
cosmic-text = { workspace = true }
image = { workspace = true }
mark = { workspace = true }
palette = { workspace = true }
paste = { workspace = true }
showbits-assets = { workspace = true }
taffy = { workspace = true }
typst = { workspace = true }
typst-assets = { workspace = true }
typst-render = { workspace = true }
[lints]
workspace = true

View file

@ -1,25 +0,0 @@
use palette::Srgba;
pub const TRANSPARENT: Srgba = Srgba::new(0.0, 0.0, 0.0, 0.0);
pub const BLACK: Srgba = Srgba::new(0.0, 0.0, 0.0, 1.0);
pub const WHITE: Srgba = Srgba::new(1.0, 1.0, 1.0, 1.0);
pub fn from_image_color(color: image::Rgba<u8>) -> Srgba {
let [r, g, b, a] = color.0;
Srgba::new(r, g, b, a).into_format()
}
pub fn to_image_color(color: Srgba) -> image::Rgba<u8> {
let color = color.into_format::<u8, u8>();
image::Rgba([color.red, color.green, color.blue, color.alpha])
}
pub fn from_text_color(color: cosmic_text::Color) -> Srgba {
let [r, g, b, a] = color.as_rgba();
Srgba::new(r, g, b, a).into_format()
}
pub fn to_text_color(color: Srgba) -> cosmic_text::Color {
let color = color.into_format::<u8, u8>();
cosmic_text::Color::rgba(color.red, color.green, color.blue, color.alpha)
}

View file

@ -1,10 +0,0 @@
pub use crate::{node::*, rect::*, tree::*, vec2::*, view::*, widget::*};
pub mod color;
mod node;
mod rect;
mod tree;
mod vec2;
mod view;
mod widget;
pub mod widgets;

View file

@ -1,221 +0,0 @@
use paste::paste;
use taffy::{
AlignContent, AlignItems, AlignSelf, Dimension, Display, FlexDirection, FlexWrap, GridAutoFlow,
GridPlacement, JustifyContent, LengthPercentage, LengthPercentageAuto, Line, NodeId,
NonRepeatedTrackSizingFunction, Overflow, Point, Position, Rect, Size, Style, TaffyResult,
TrackSizingFunction,
};
use crate::{BoxedWidget, Tree, Widget};
pub struct Node<C> {
layout: Style,
children: Vec<NodeId>,
widget: Option<BoxedWidget<C>>,
}
impl<C> Node<C> {
pub fn empty() -> Self {
Self {
layout: Style::default(),
children: vec![],
widget: None,
}
}
pub fn and_child(mut self, node: NodeId) -> Self {
self.children.push(node);
self
}
pub fn with_widget<W: Widget<C> + 'static>(mut self, widget: W) -> Self {
self.widget = Some(Box::new(widget));
self
}
pub fn register(self, tree: &mut Tree<C>) -> TaffyResult<NodeId> {
let tree = tree.taffy_tree();
let id = tree.new_with_children(self.layout, &self.children)?;
tree.set_node_context(id, self.widget)?;
Ok(id)
}
}
// Layout helper functions
macro_rules! layout_setter {
( $name:ident : $type:ty ) => {
paste! {
pub fn [<with_ $name>](mut self, $name: $type) -> Self {
self.layout.$name = $name;
self
}
}
};
}
macro_rules! layout_setter_point {
( $name:ident : Point<$type:ty> ) => {
paste! {
pub fn [<with_ $name>](mut self, $name: Point<$type>) -> Self {
self.layout.$name = $name;
self
}
pub fn [<with_ $name _x>](mut self, x: $type) -> Self {
self.layout.$name.x = x;
self
}
pub fn [<with_ $name _y>](mut self, y: $type) -> Self {
self.layout.$name.y = y;
self
}
pub fn [<with_ $name _all>](mut self, all: $type) -> Self {
self.layout.$name.x = all;
self.layout.$name.y = all;
self
}
}
};
}
macro_rules! layout_setter_size {
( $name:ident : Size<$type:ty> ) => {
paste! {
pub fn [<with_ $name>](mut self, $name: Size<$type>) -> Self {
self.layout.$name = $name;
self
}
pub fn [<with_ $name _width>](mut self, width: $type) -> Self {
self.layout.$name.width = width;
self
}
pub fn [<with_ $name _height>](mut self, height: $type) -> Self {
self.layout.$name.height = height;
self
}
pub fn [<with_ $name _all>](mut self, all: $type) -> Self {
self.layout.$name.width = all;
self.layout.$name.height = all;
self
}
}
};
}
macro_rules! layout_setter_line {
( $name:ident : Line<$type:ty> ) => {
paste! {
pub fn [<with_ $name>](mut self, $name: Line<$type>) -> Self {
self.layout.$name = $name;
self
}
pub fn [<with_ $name _start>](mut self, start: $type) -> Self {
self.layout.$name.start = start;
self
}
pub fn [<with_ $name _end>](mut self, end: $type) -> Self {
self.layout.$name.end = end;
self
}
pub fn [<with_ $name _all>](mut self, all: $type) -> Self {
self.layout.$name.start = all;
self.layout.$name.end = all;
self
}
}
};
}
macro_rules! layout_setter_rect {
( $name:ident : Rect<$type:ty> ) => {
paste! {
pub fn [<with_ $name>](mut self, $name: Rect<$type>) -> Self {
self.layout.$name = $name;
self
}
pub fn [<with_ $name _left>](mut self, left: $type) -> Self {
self.layout.$name.left = left;
self
}
pub fn [<with_ $name _right>](mut self, right: $type) -> Self {
self.layout.$name.right = right;
self
}
pub fn [<with_ $name _top>](mut self, top: $type) -> Self {
self.layout.$name.top = top;
self
}
pub fn [<with_ $name _bottom>](mut self, bottom: $type) -> Self {
self.layout.$name.bottom = bottom;
self
}
pub fn [<with_ $name _horiz>](mut self, horizontal: $type) -> Self {
self.layout.$name.left = horizontal;
self.layout.$name.right = horizontal;
self
}
pub fn [<with_ $name _vert>](mut self, vertical: $type) -> Self {
self.layout.$name.top = vertical;
self.layout.$name.bottom = vertical;
self
}
pub fn [<with_ $name _all>](mut self, all: $type) -> Self {
self.layout.$name.left = all;
self.layout.$name.right = all;
self.layout.$name.top = all;
self.layout.$name.bottom = all;
self
}
}
};
}
impl<C> Node<C> {
layout_setter!(display: Display);
layout_setter_point!(overflow: Point<Overflow>);
layout_setter!(scrollbar_width: f32);
layout_setter!(position: Position);
layout_setter_rect!(inset: Rect<LengthPercentageAuto>);
layout_setter_size!(size: Size<Dimension>);
layout_setter_size!(min_size: Size<Dimension>);
layout_setter_size!(max_size: Size<Dimension>);
layout_setter!(aspect_ratio: Option<f32>);
layout_setter_rect!(margin: Rect<LengthPercentageAuto>);
layout_setter_rect!(padding: Rect<LengthPercentage>);
layout_setter_rect!(border: Rect<LengthPercentage>);
layout_setter!(align_items: Option<AlignItems>);
layout_setter!(align_self: Option<AlignSelf>);
layout_setter!(justify_items: Option<AlignItems>);
layout_setter!(justify_self: Option<AlignSelf>);
layout_setter!(align_content: Option<AlignContent>);
layout_setter!(justify_content: Option<JustifyContent>);
layout_setter_size!(gap: Size<LengthPercentage>);
layout_setter!(flex_direction: FlexDirection);
layout_setter!(flex_wrap: FlexWrap);
layout_setter!(flex_basis: Dimension);
layout_setter!(flex_grow: f32);
layout_setter!(flex_shrink: f32);
layout_setter!(grid_template_rows: Vec<TrackSizingFunction>);
layout_setter!(grid_template_columns: Vec<TrackSizingFunction>);
layout_setter!(grid_auto_rows: Vec<NonRepeatedTrackSizingFunction>);
layout_setter!(grid_auto_columns: Vec<NonRepeatedTrackSizingFunction>);
layout_setter!(grid_auto_flow: GridAutoFlow);
layout_setter_line!(grid_row: Line<GridPlacement>);
layout_setter_line!(grid_column: Line<GridPlacement>);
}

View file

@ -1,143 +0,0 @@
use std::ops::{Add, Sub};
use crate::Vec2;
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Rect {
north: i32,
south: i32,
west: i32,
east: i32,
}
impl Rect {
/// A zero-sized rectangle located at the origin.
pub const ZERO: Self = Self::new(0, 0, -1, -1);
/// Whenever a rectangle is constructed, this function must be used. This
/// ensures invariants are always checked.
const fn new(north: i32, south: i32, west: i32, east: i32) -> Self {
let result = Self {
north,
south,
west,
east,
};
let size = result.size();
assert!(size.x >= 0);
assert!(size.y >= 0);
result
}
/// Construct a rectangle that is a bounding box around two points.
///
/// It is not possible to construct a rectangle with a width or height of 0
/// through this method. Use one of the other constructor functions instead.
pub fn from_points(a: Vec2, b: Vec2) -> Self {
Self::new(a.y.min(b.y), a.y.max(b.y), a.x.min(b.x), a.x.max(b.x))
}
/// Construct a rectangle from its north-west and south-east corners.
pub const fn from_nw_se(nw: Vec2, se: Vec2) -> Self {
Self::new(nw.y, se.y, nw.x, se.x)
}
/// Construct a rectangle from its north-east and south-west corners.
pub const fn from_ne_sw(ne: Vec2, sw: Vec2) -> Self {
Self::new(ne.y, sw.y, sw.x, ne.x)
}
/// Construct a rectangle from its north-west corner and size.
pub fn from_nw(nw: Vec2, size: Vec2) -> Self {
let se = nw + (size - 1);
Self::from_nw_se(nw, se)
}
/// Construct a rectangle from its north-east corner and size.
pub fn from_ne(ne: Vec2, size: Vec2) -> Self {
let sw = ne + (size - 1).neg_x();
Self::from_ne_sw(ne, sw)
}
/// Construct a rectangle from its south-west corner and size.
pub fn from_sw(sw: Vec2, size: Vec2) -> Self {
let ne = sw + (size - 1).neg_y();
Self::from_ne_sw(ne, sw)
}
/// Construct a rectangle from its south-east corner and size.
pub fn from_se(se: Vec2, size: Vec2) -> Self {
let nw = se - (size - 1);
Self::from_nw_se(nw, se)
}
pub const fn north(self) -> i32 {
self.north
}
pub const fn south(self) -> i32 {
self.south
}
pub const fn west(self) -> i32 {
self.west
}
pub const fn east(self) -> i32 {
self.east
}
pub const fn corner_nw(self) -> Vec2 {
Vec2::new(self.west, self.north)
}
pub const fn corner_ne(self) -> Vec2 {
Vec2::new(self.east, self.north)
}
pub const fn corner_sw(self) -> Vec2 {
Vec2::new(self.west, self.south)
}
pub const fn corner_se(self) -> Vec2 {
Vec2::new(self.east, self.south)
}
pub const fn size(self) -> Vec2 {
Vec2::new(self.east - self.west + 1, self.south - self.north + 1)
}
/// An iterator of all points contained within the rectangle.
pub fn points(self) -> impl Iterator<Item = Vec2> {
(self.north..=self.south)
.flat_map(move |y| (self.west..=self.east).map(move |x| Vec2::new(x, y)))
}
}
impl Add<Vec2> for Rect {
type Output = Self;
fn add(self, rhs: Vec2) -> Self::Output {
Self::new(
self.north + rhs.y,
self.south + rhs.y,
self.west + rhs.x,
self.east + rhs.x,
)
}
}
impl Sub<Vec2> for Rect {
type Output = Self;
fn sub(self, rhs: Vec2) -> Self::Output {
Self::new(
self.north - rhs.y,
self.south - rhs.y,
self.west - rhs.x,
self.east - rhs.x,
)
}
}

View file

@ -1,110 +0,0 @@
use image::RgbaImage;
use palette::Srgba;
use taffy::{AvailableSpace, NodeId, Point, Size, TaffyResult, TaffyTree};
use crate::{BoxedWidget, Rect, Vec2, View, color};
fn point_to_vec2(point: Point<f32>) -> Vec2 {
Vec2::new(point.x as i32, point.y as i32)
}
fn size_to_vec2(size: Size<f32>) -> Vec2 {
Vec2::new(size.width as i32, size.height as i32)
}
pub struct Tree<C> {
tree: TaffyTree<BoxedWidget<C>>,
background: Srgba,
}
impl<C> Tree<C> {
pub fn new(background: Srgba) -> Self {
Self {
tree: TaffyTree::new(),
background,
}
}
pub(crate) fn taffy_tree(&mut self) -> &mut TaffyTree<BoxedWidget<C>> {
&mut self.tree
}
fn layout(
&mut self,
ctx: &mut C,
root: NodeId,
available: Size<AvailableSpace>,
) -> TaffyResult<()> {
self.tree.enable_rounding(); // Just to make sure
self.tree.compute_layout_with_measure(
root,
available,
|known, available, _node, context, _style| {
if let Some(widget) = context {
widget.size(ctx, known, available)
} else {
Size::ZERO
}
},
)
}
fn render_to_view(
&mut self,
ctx: &mut C,
node: NodeId,
view: &mut View<'_>,
) -> anyhow::Result<()> {
let layout = *self.tree.layout(node)?;
let area = Rect::from_nw(point_to_vec2(layout.location), size_to_vec2(layout.size));
let mut view = view.dup().zoom(area);
// First pass
if let Some(widget) = self.tree.get_node_context_mut(node) {
widget.draw_below(ctx, &mut view, &layout)?;
}
// Render children
let mut children = vec![];
for child in self.tree.children(node)? {
let order = self.tree.layout(child)?.order;
children.push((order, child));
}
children.sort_unstable_by_key(|(order, _)| *order);
for (_, child) in children {
self.render_to_view(ctx, child, &mut view)?;
}
// Second pass
if let Some(widget) = self.tree.get_node_context_mut(node) {
widget.draw_above(ctx, &mut view, &layout)?;
}
Ok(())
}
pub fn render(
&mut self,
ctx: &mut C,
root: NodeId,
available: Size<AvailableSpace>,
) -> anyhow::Result<RgbaImage> {
self.layout(ctx, root, available)?;
let layout = self.tree.layout(root)?;
assert_eq!(layout.location.x, 0.0);
assert_eq!(layout.location.y, 0.0);
// TODO Check how taffy treats the border?
let (width, height) = size_to_vec2(layout.size).to_u32();
let mut image =
RgbaImage::from_pixel(width, height, color::to_image_color(self.background));
self.render_to_view(ctx, root, &mut View::new(&mut image))?;
Ok(image)
}
pub fn print_debug(&mut self, root: NodeId) {
self.tree.print_tree(root)
}
}

View file

@ -1,187 +0,0 @@
use std::{
fmt,
ops::{Add, Mul, Neg, Sub},
};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Vec2 {
pub x: i32,
pub y: i32,
}
impl Vec2 {
pub const ZERO: Self = Self::new(0, 0);
pub const NORTH: Self = Self::new(0, -1);
pub const SOUTH: Self = Self::new(0, 1);
pub const WEST: Self = Self::new(-1, 0);
pub const EAST: Self = Self::new(1, 0);
pub const fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
pub fn from_u32_checked(x: u32, y: u32) -> Option<Self> {
let x: i32 = x.try_into().ok()?;
let y: i32 = y.try_into().ok()?;
Some(Self::new(x, y))
}
pub fn from_u32(x: u32, y: u32) -> Self {
let x: i32 = x.try_into().expect("x too large");
let y: i32 = y.try_into().expect("y too large");
Self::new(x, y)
}
pub fn to_u32_checked(self) -> Option<(u32, u32)> {
let x: u32 = self.x.try_into().ok()?;
let y: u32 = self.y.try_into().ok()?;
Some((x, y))
}
pub fn to_u32(self) -> (u32, u32) {
let x: u32 = self.x.try_into().expect("x too small");
let y: u32 = self.y.try_into().expect("y too small");
(x, y)
}
pub fn with_x(self, x: i32) -> Self {
Self { x, ..self }
}
pub fn with_y(self, y: i32) -> Self {
Self { y, ..self }
}
/// The vector pointing from `self` to `other`.
///
/// ```
/// # use showbits_common::Vec2;
/// let a = Vec2::new(1, 3);
/// let b = Vec2::new(3, 7);
/// assert_eq!(a.to(b), b - a);
/// ```
pub fn to(self, other: Self) -> Self {
other - self
}
/// Negate the `x` component of the vector.
///
/// ```
/// # use showbits_common::Vec2;
/// let v = Vec2::new(3, 4);
/// assert_eq!(v.neg_x(), v * Vec2::new(-1, 1));
/// ```
pub fn neg_x(self) -> Self {
Self { x: -self.x, ..self }
}
/// Negate the `y` component of the vector.
///
/// ```
/// # use showbits_common::Vec2;
/// let v = Vec2::new(3, 4);
/// assert_eq!(v.neg_y(), v * Vec2::new(1, -1));
/// ```
pub fn neg_y(self) -> Self {
Self { y: -self.y, ..self }
}
}
impl fmt::Debug for Vec2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Vec2").field(&self.x).field(&self.y).finish()
}
}
impl Neg for Vec2 {
type Output = Self;
fn neg(self) -> Self::Output {
Self {
x: -self.x,
y: -self.y,
}
}
}
impl Add for Vec2 {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl Add<i32> for Vec2 {
type Output = Self;
fn add(self, rhs: i32) -> Self::Output {
self + Self::new(rhs, rhs)
}
}
impl Sub for Vec2 {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl Sub<i32> for Vec2 {
type Output = Self;
fn sub(self, rhs: i32) -> Self::Output {
self - Self::new(rhs, rhs)
}
}
impl Mul for Vec2 {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self {
x: self.x * rhs.x,
y: self.y * rhs.y,
}
}
}
impl Mul<i32> for Vec2 {
type Output = Self;
fn mul(self, rhs: i32) -> Self::Output {
self * Self::new(rhs, rhs)
}
}
#[cfg(test)]
mod tests {
use crate::Vec2;
#[test]
fn arithmetic() {
let a = Vec2::new(1, 3);
let b = Vec2::new(3, 7);
assert_eq!(-a, Vec2::new(-1, -3));
assert_eq!(a.neg_x(), Vec2::new(-1, 3));
assert_eq!(a.neg_y(), Vec2::new(1, -3));
assert_eq!(a + b, Vec2::new(4, 10));
assert_eq!(a + 2, Vec2::new(3, 5));
assert_eq!(a - b, Vec2::new(-2, -4));
assert_eq!(a - 2, Vec2::new(-1, 1));
assert_eq!(a - b, b.to(a));
assert_eq!(a * b, Vec2::new(3, 21));
assert_eq!(a * 2, Vec2::new(2, 6));
}
}

View file

@ -1,89 +0,0 @@
use image::RgbaImage;
use palette::{Srgba, blend::Compose};
use crate::{Rect, Vec2, color};
pub struct View<'a> {
area: Rect,
buffer: &'a mut RgbaImage,
}
impl<'a> View<'a> {
pub fn new(buffer: &'a mut RgbaImage) -> Self {
let size = Vec2::from_u32(buffer.width(), buffer.height());
let area = Rect::from_nw(Vec2::ZERO, size);
Self { area, buffer }
}
pub fn dup(&mut self) -> View<'_> {
View {
area: self.area,
buffer: self.buffer,
}
}
pub fn zoom(mut self, area: Rect) -> Self {
self.area = area + self.area.corner_nw();
self
}
pub fn size(&self) -> Vec2 {
self.area.size()
}
pub fn area(&self) -> Rect {
Rect::from_nw(Vec2::ZERO, self.size())
}
fn pos_to_buffer_pos(&self, pos: Vec2) -> Vec2 {
pos + self.area.corner_nw()
}
pub fn get(&self, pos: Vec2) -> Option<Srgba> {
let (x, y) = self.pos_to_buffer_pos(pos).to_u32_checked()?;
let pixel = self.buffer.get_pixel_checked(x, y)?;
Some(color::from_image_color(*pixel))
}
pub fn set(&mut self, pos: Vec2, color: Srgba) {
let Some((x, y)) = self.pos_to_buffer_pos(pos).to_u32_checked() else {
return;
};
let Some(pixel) = self.buffer.get_pixel_mut_checked(x, y) else {
return;
};
let below = color::from_image_color(*pixel);
*pixel = color::to_image_color(color.atop(below));
}
pub fn replace(&mut self, pos: Vec2, color: Srgba) {
let Some((x, y)) = self.pos_to_buffer_pos(pos).to_u32_checked() else {
return;
};
let Some(pixel) = self.buffer.get_pixel_mut_checked(x, y) else {
return;
};
*pixel = color::to_image_color(color);
}
// More complicated drawing primitives
pub fn rect(&mut self, area: Rect, color: Srgba) {
for pos in area.points() {
self.set(pos, color);
}
}
pub fn image(&mut self, image: &RgbaImage) {
for y in 0..image.height() {
for x in 0..image.width() {
let color = color::from_image_color(*image.get_pixel(x, y));
self.set(Vec2::from_u32(x, y), color);
}
}
}
}

View file

@ -1,53 +0,0 @@
use taffy::{AvailableSpace, Layout, Size};
use crate::{Node, View};
pub trait Widget<C> {
/// Used for the measure function in
/// [`taffy::TaffyTree::compute_layout_with_measure`].
#[allow(unused_variables)]
fn size(
&mut self,
ctx: &mut C,
known: Size<Option<f32>>,
available: Size<AvailableSpace>,
) -> Size<f32> {
Size::ZERO
}
/// Called before all children are drawn.
///
/// Prefer this over [`Self::draw_above`] when implementing a leaf widget.
#[allow(unused_variables)]
fn draw_below(
&mut self,
ctx: &mut C,
view: &mut View<'_>,
layout: &Layout,
) -> anyhow::Result<()> {
Ok(())
}
/// Called after all children are drawn.
#[allow(unused_variables)]
fn draw_above(
&mut self,
ctx: &mut C,
view: &mut View<'_>,
layout: &Layout,
) -> anyhow::Result<()> {
Ok(())
}
}
pub type BoxedWidget<C> = Box<dyn Widget<C>>;
pub trait WidgetExt<C> {
fn node(self) -> Node<C>;
}
impl<C, W: Widget<C> + 'static> WidgetExt<C> for W {
fn node(self) -> Node<C> {
Node::empty().with_widget(self)
}
}

View file

@ -1,9 +0,0 @@
pub use block::*;
pub use image::*;
pub use text::*;
pub use typst::*;
mod block;
mod image;
mod text;
mod typst;

View file

@ -1,77 +0,0 @@
use palette::Srgba;
use taffy::Layout;
use crate::{Rect, Vec2, View, Widget, color};
pub struct Block {
border: Srgba,
background: Srgba,
}
impl Block {
pub fn new() -> Self {
Self {
border: color::TRANSPARENT,
background: color::TRANSPARENT,
}
}
pub fn with_border(mut self, color: Srgba) -> Self {
self.border = color;
self
}
pub fn with_background(mut self, color: Srgba) -> Self {
self.background = color;
self
}
}
impl Default for Block {
fn default() -> Self {
Self::new()
}
}
impl<C> Widget<C> for Block {
fn draw_below(
&mut self,
_ctx: &mut C,
view: &mut View<'_>,
layout: &Layout,
) -> anyhow::Result<()> {
let area = view.area();
// Background
view.rect(area, self.background);
// And now... the border!
//
// It is important not to draw pixels twice in case the border color is
// transparent. That's why the logic below is a bit more complex.
let left = layout.border.left as i32;
let right = layout.border.right as i32;
let top = layout.border.top as i32;
let bottom = layout.border.bottom as i32;
if top > 0 {
let border = Rect::from_nw(area.corner_nw(), area.size().with_y(top));
view.rect(border, self.border);
}
if bottom > 0 {
let border = Rect::from_sw(area.corner_sw(), area.size().with_y(bottom));
view.rect(border, self.border);
}
if left > 0 {
let nw = area.corner_nw() + Vec2::new(0, top);
let size = Vec2::new(left, area.size().y - top - bottom);
view.rect(Rect::from_nw(nw, size), self.border);
}
if right > 0 {
let ne = area.corner_ne() + Vec2::new(0, top);
let size = Vec2::new(right, area.size().y - top - bottom);
view.rect(Rect::from_ne(ne, size), self.border);
}
Ok(())
}
}

View file

@ -1,161 +0,0 @@
use image::{
RgbaImage,
imageops::{self, FilterType},
};
use mark::dither::{AlgoFloydSteinberg, AlgoStucki, Algorithm, DiffEuclid, Palette};
use palette::{IntoColor, LinSrgb, Srgba};
use taffy::prelude::{AvailableSpace, Layout, Size};
use crate::Widget;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum DitherAlgorithm {
FloydSteinberg,
Stucki,
}
impl DitherAlgorithm {
fn dither(self, image: RgbaImage, palette: &Palette<LinSrgb>) -> RgbaImage {
match self {
Self::FloydSteinberg => {
<AlgoFloydSteinberg as Algorithm<LinSrgb, DiffEuclid>>::run(image, palette)
}
Self::Stucki => <AlgoStucki as Algorithm<LinSrgb, DiffEuclid>>::run(image, palette),
}
}
}
pub struct Image {
image: RgbaImage,
shrink: bool,
grow: bool,
scale: u32,
filter: FilterType,
dither_palette: Option<Palette<LinSrgb>>,
dither_algorithm: DitherAlgorithm,
}
impl Image {
pub fn new(image: RgbaImage) -> Self {
Self {
image,
shrink: true,
grow: false,
filter: FilterType::CatmullRom,
scale: 1,
dither_palette: None,
dither_algorithm: DitherAlgorithm::FloydSteinberg,
}
}
pub fn with_shrink(mut self, shrink: bool) -> Self {
self.shrink = shrink;
self
}
pub fn with_grow(mut self, grow: bool) -> Self {
self.grow = grow;
self
}
pub fn with_scale(mut self, scale: u32) -> Self {
self.scale = scale.max(1);
self
}
pub fn with_filter(mut self, filter: FilterType) -> Self {
self.filter = filter;
self
}
pub fn with_dither_palette(mut self, palette: &[Srgba]) -> Self {
let palette = palette
.iter()
.map(|c| c.color.into_color())
.collect::<Vec<LinSrgb>>();
self.dither_palette = Some(Palette::new(palette));
self
}
pub fn with_dither_algorithm(mut self, algorithm: DitherAlgorithm) -> Self {
self.dither_algorithm = algorithm;
self
}
}
impl<C> Widget<C> for Image {
fn size(
&mut self,
_ctx: &mut C,
known: Size<Option<f32>>,
available: Size<AvailableSpace>,
) -> Size<f32> {
if self.image.width() == 0 || self.image.height() == 0 {
// We don't want to divide by zero later on
return Size {
width: 0.0,
height: 0.0,
};
}
let size = Size {
width: (self.image.width() * self.scale) as f32,
height: (self.image.height() * self.scale) as f32,
};
let max_width = known.width.or(match available.width {
AvailableSpace::Definite(width) => Some(width),
AvailableSpace::MinContent => Some(0.0),
AvailableSpace::MaxContent => None,
});
let max_height = known.height.or(match available.height {
AvailableSpace::Definite(height) => Some(height),
AvailableSpace::MinContent => Some(0.0),
AvailableSpace::MaxContent => None,
});
let scale_factor = match (max_width, max_height) {
(None, None) => 1.0,
(None, Some(height)) => height / size.height,
(Some(width), None) => width / size.width,
(Some(width), Some(height)) => (width / size.width).min(height / size.height),
};
if (scale_factor < 1.0 && self.shrink) || (scale_factor > 1.0 && self.grow) {
Size {
width: size.width * scale_factor,
height: size.height * scale_factor,
}
} else {
size
}
}
fn draw_below(
&mut self,
_ctx: &mut C,
view: &mut crate::View<'_>,
_layout: &Layout,
) -> anyhow::Result<()> {
let (width, height) = view.size().to_u32();
let iwidth = width / self.scale;
let iheight = height / self.scale;
let image = imageops::resize(&self.image, iwidth, iheight, self.filter);
let image = if let Some(palette) = &self.dither_palette {
self.dither_algorithm.dither(image, palette)
} else {
image
};
let image = imageops::resize(&image, width, height, FilterType::Nearest);
view.image(&image);
Ok(())
}
}

View file

@ -1,210 +0,0 @@
use cosmic_text::{Attrs, AttrsOwned, Buffer, Family, FontSystem, Metrics, Shaping, SwashCache};
use palette::Srgba;
use showbits_assets::{UNIFONT, UNIFONT_JP, UNIFONT_NAME, UNIFONT_SIZE, UNIFONT_UPPER};
use taffy::{
Layout,
prelude::{AvailableSpace, Size},
};
use crate::{Rect, Vec2, View, Widget, color};
// https://github.com/DioxusLabs/taffy/blob/main/examples/cosmic_text.rs
pub struct FontStuff {
font_system: FontSystem,
swash_cache: SwashCache,
}
impl FontStuff {
pub fn new() -> Self {
let mut font_system = FontSystem::new();
let db = font_system.db_mut();
db.load_font_data(UNIFONT.to_vec());
db.load_font_data(UNIFONT_JP.to_vec());
db.load_font_data(UNIFONT_UPPER.to_vec());
db.set_monospace_family(UNIFONT_NAME);
Self {
font_system,
swash_cache: SwashCache::new(),
}
}
}
impl Default for FontStuff {
fn default() -> Self {
Self::new()
}
}
pub trait HasFontStuff {
fn font_stuff(&mut self) -> &mut FontStuff;
}
pub struct Text {
metrics: Metrics,
default_attrs: AttrsOwned,
chunks: Vec<(AttrsOwned, String)>,
shaping: Shaping,
color: Srgba,
}
impl Text {
pub const fn default_metrics() -> Metrics {
Metrics::new(UNIFONT_SIZE, UNIFONT_SIZE)
}
pub fn default_attrs<'a>() -> Attrs<'a> {
Attrs::new().family(Family::Monospace)
}
pub fn new() -> Self {
Self {
metrics: Self::default_metrics(),
default_attrs: AttrsOwned::new(Self::default_attrs()),
chunks: vec![],
shaping: Shaping::Advanced,
color: color::BLACK,
}
}
pub fn with_metrics(mut self, metrics: Metrics) -> Self {
self.metrics = metrics;
self
}
pub fn with_font_size(mut self, size: f32) -> Self {
self.metrics.font_size = size;
self
}
pub fn with_line_height(mut self, height: f32) -> Self {
self.metrics.line_height = height;
self
}
pub fn with_default_attrs(mut self, attrs: Attrs<'_>) -> Self {
self.default_attrs = AttrsOwned::new(attrs);
self
}
pub fn with_shaping(mut self, shaping: Shaping) -> Self {
self.shaping = shaping;
self
}
pub fn with_color(mut self, color: Srgba) -> Self {
self.color = color;
self
}
pub fn and_plain<S: ToString>(mut self, text: S) -> Self {
let chunk = (self.default_attrs.clone(), text.to_string());
self.chunks.push(chunk);
self
}
pub fn and_rich<S: ToString>(mut self, attrs: Attrs<'_>, text: S) -> Self {
let chunk = (AttrsOwned::new(attrs), text.to_string());
self.chunks.push(chunk);
self
}
pub fn and_chunks<I>(mut self, chunks: I) -> Self
where
I: IntoIterator<Item = (AttrsOwned, String)>,
{
self.chunks.extend(chunks);
self
}
pub fn widget<C: HasFontStuff>(self, font_stuff: &mut FontStuff) -> impl Widget<C> + use<C> {
let fs = &mut font_stuff.font_system;
let mut buffer = Buffer::new_empty(self.metrics);
buffer.set_size(fs, None, None);
let spans = self
.chunks
.iter()
.map(|(attrs, text)| (text as &str, attrs.as_attrs()));
buffer.set_rich_text(fs, spans, self.default_attrs.as_attrs(), self.shaping);
TextWidget {
buffer,
color: self.color,
}
}
}
impl Default for Text {
fn default() -> Self {
Self::new()
}
}
struct TextWidget {
buffer: Buffer,
color: Srgba,
}
impl<C: HasFontStuff> Widget<C> for TextWidget {
fn size(
&mut self,
ctx: &mut C,
known: Size<Option<f32>>,
available: Size<AvailableSpace>,
) -> Size<f32> {
let width = known.width.unwrap_or(match available.width {
AvailableSpace::Definite(width) => width,
AvailableSpace::MinContent => 0.0,
AvailableSpace::MaxContent => f32::INFINITY,
});
let fs = &mut ctx.font_stuff().font_system;
self.buffer.set_size(fs, Some(width), None);
self.buffer.shape_until_scroll(fs, false);
let runs = self.buffer.layout_runs().collect::<Vec<_>>();
let height = runs.len() as f32 * self.buffer.metrics().line_height;
let width = runs
.into_iter()
.map(|run| run.line_w)
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or(0.0);
// If we don't round up here, the layout rounding may round down our
// size slightly. This may lead to more line breaks, moving some words
// below our visible area.
let width = width.ceil();
let height = height.ceil();
Size { width, height }
}
fn draw_below(
&mut self,
ctx: &mut C,
view: &mut View<'_>,
_layout: &Layout,
) -> anyhow::Result<()> {
let size = view.size();
let FontStuff {
font_system: fs,
swash_cache: sc,
} = ctx.font_stuff();
self.buffer
.set_size(fs, Some(size.x as f32), Some(size.y as f32));
self.buffer.shape_until_scroll(fs, true);
let color = color::to_text_color(self.color);
self.buffer.draw(fs, sc, color, |x, y, w, h, color| {
let color = color::from_text_color(color);
let area = Rect::from_nw(Vec2::new(x, y), Vec2::from_u32(w, h));
view.rect(area, color);
});
Ok(())
}
}

View file

@ -1,217 +0,0 @@
use std::{fs, path::PathBuf, sync::OnceLock};
use anyhow::anyhow;
use image::RgbaImage;
use taffy::{
Layout,
prelude::{AvailableSpace, Size},
};
use typst::{
Library, World,
diag::{FileError, FileResult},
foundations::{Bytes, Datetime},
layout::Abs,
syntax::{FileId, Source},
text::{Font, FontBook},
utils::LazyHash,
visualize::Color,
};
use crate::{View, Widget};
// The logic for detecting and loading fonts was ripped straight from:
// https://github.com/typst/typst/blob/69dcc89d84176838c293b2d59747cd65e28843ad/crates/typst-cli/src/fonts.rs
// https://github.com/typst/typst/blob/69dcc89d84176838c293b2d59747cd65e28843ad/crates/typst-cli/src/world.rs#L193-L195
struct FontSlot {
path: PathBuf,
index: u32,
font: OnceLock<Option<Font>>,
}
impl FontSlot {
pub fn get(&self) -> Option<Font> {
self.font
.get_or_init(|| {
let data = fs::read(&self.path).ok()?;
Font::new(Bytes::new(data), self.index)
})
.clone()
}
}
struct FontLoader {
book: FontBook,
fonts: Vec<FontSlot>,
}
impl FontLoader {
fn new() -> Self {
Self {
book: FontBook::new(),
fonts: vec![],
}
}
fn load_embedded_fonts(&mut self) {
// https://github.com/typst/typst/blob/be12762d942e978ddf2e0ac5c34125264ab483b7/crates/typst-cli/src/fonts.rs#L107-L121
for font_file in typst_assets::fonts() {
let font_data = Bytes::new(font_file);
for (i, font) in Font::iter(font_data).enumerate() {
self.book.push(font.info().clone());
self.fonts.push(FontSlot {
path: PathBuf::new(),
index: i as u32,
font: OnceLock::from(Some(font)),
});
}
}
}
}
struct DummyWorld {
library: LazyHash<Library>,
book: LazyHash<FontBook>,
main: Source,
fonts: Vec<FontSlot>,
}
impl DummyWorld {
fn new(main: String) -> Self {
let mut loader = FontLoader::new();
loader.load_embedded_fonts();
Self {
library: LazyHash::new(Library::builder().build()),
book: LazyHash::new(loader.book),
main: Source::detached(main),
fonts: loader.fonts,
}
}
}
impl World for DummyWorld {
fn library(&self) -> &LazyHash<Library> {
&self.library
}
fn book(&self) -> &LazyHash<FontBook> {
&self.book
}
fn main(&self) -> FileId {
self.main.id()
}
fn source(&self, id: FileId) -> FileResult<Source> {
if id == self.main.id() {
return Ok(self.main.clone());
}
Err(FileError::AccessDenied)
}
fn file(&self, _id: FileId) -> FileResult<Bytes> {
Err(FileError::AccessDenied)
}
fn font(&self, index: usize) -> Option<Font> {
self.fonts[index].get()
}
fn today(&self, _offset: Option<i64>) -> Option<Datetime> {
None
}
}
const SCALE: f32 = 3.0;
pub struct Typst {
code: String,
}
impl Typst {
pub fn new(code: String) -> Self {
Self { code }
}
fn render(&self, width: Option<f32>, height: Option<f32>) -> Result<RgbaImage, Vec<String>> {
let width = match width {
Some(width) => format!("{}pt", width / SCALE),
None => "auto".to_string(),
};
let height = match height {
Some(height) => format!("{}pt", height / SCALE),
None => "auto".to_string(),
};
let mut source = String::new();
source.push_str(&format!("#set page(width: {width}, height: {height})\n"));
source.push_str("#set page(margin: (left: 0mm, right: 0mm, top: 1mm, bottom: 2mm))\n");
source.push_str(&self.code);
let world = DummyWorld::new(source);
let document = typst::compile(&world).output.map_err(|errs| {
errs.into_iter()
.map(|sd| sd.message.to_string())
.collect::<Vec<_>>()
})?;
let pixmap = typst_render::render_merged(&document, SCALE, Abs::zero(), Some(Color::WHITE));
let buffer = RgbaImage::from_raw(pixmap.width(), pixmap.height(), pixmap.take()).unwrap();
Ok(buffer)
}
}
impl<C> Widget<C> for Typst {
fn size(
&mut self,
_ctx: &mut C,
known: Size<Option<f32>>,
available: Size<AvailableSpace>,
) -> Size<f32> {
let width = known.width.or(match available.width {
AvailableSpace::Definite(width) => Some(width),
AvailableSpace::MinContent => None, // auto
AvailableSpace::MaxContent => Some(4096.0),
});
let height = known.height.or(match available.height {
AvailableSpace::Definite(width) => Some(width),
AvailableSpace::MinContent | AvailableSpace::MaxContent => None, // auto
});
let Ok(buffer) = self.render(width, height) else {
return Size {
width: width.unwrap_or(0.0),
height: height.unwrap_or(0.0),
};
};
// Round up so we definitely have enough space.
// let width = (buffer.width() as f32 / SCALE).ceil() * SCALE + 1.0;
// let height = (buffer.height() as f32 / SCALE).ceil() * SCALE + 1.0;
let width = (buffer.width() as f32).ceil() + 1.0;
let height = (buffer.height() as f32).ceil() + 1.0;
Size { width, height }
}
fn draw_below(
&mut self,
_ctx: &mut C,
view: &mut View<'_>,
_layout: &Layout,
) -> anyhow::Result<()> {
let size = view.size();
let buffer = self
.render(Some(size.x as f32), Some(size.y as f32))
.map_err(|errs| anyhow!("{}", errs.join("\n")))?;
view.image(&buffer);
Ok(())
}
}

View file

@ -8,7 +8,7 @@ anyhow = { workspace = true }
axum = { workspace = true, features = ["multipart"] } axum = { workspace = true, features = ["multipart"] }
clap = { workspace = true } clap = { workspace = true }
escpos = { workspace = true } escpos = { workspace = true }
image = { workspace = true, default-features = true } image = { workspace = true }
jiff = { workspace = true } jiff = { workspace = true }
mime_guess = { workspace = true } mime_guess = { workspace = true }
palette = { workspace = true } palette = { workspace = true }