diff --git a/Cargo.lock b/Cargo.lock index 1c26817..3783949 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -417,9 +417,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "calloop" @@ -449,9 +449,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.19" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "jobserver", "libc", @@ -2523,9 +2523,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.36.1" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a05e2e8efddfa51a84ca47cec303fac86c8541b686d37cac5efc0e094417bc" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" dependencies = [ "memchr", ] @@ -3168,9 +3168,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ "indexmap", "toml_datetime", @@ -3272,9 +3272,9 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] @@ -3287,15 +3287,15 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "url" @@ -3678,9 +3678,9 @@ dependencies = [ [[package]] name = "wgpu-profiler" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb9c0c8c123e8772c9b44a1ca04a9549929654bb3f86a6f4b4ed2d5559f6027d" +checksum = "06b2cee91fdc885ff0d3d714c59810cc72c6d84b81b0eaa48bab8ff2ad54fb5b" dependencies = [ "parking_lot", "thiserror", diff --git a/README.md b/README.md index 4dba237..85f4db9 100644 --- a/README.md +++ b/README.md @@ -22,3 +22,42 @@ effort: 4. Where possible, features of the renderer should be regression tested to ensure that changes do not change the quality of the rendering unless explicitly intended. + +## Dependencies + +The tests require the nerd fonts to be installed. Run the +following: + +### Windows +```powershell +scoop bucket add extras +scoop bucket add nerd-fonts +scoop install Monaspace-NF FiraCode-NF ProFont-NF CascadiaCode-NF Noto-NF +``` + +### Mac +```bash +brew install font-monaspace font-fira-code-nerd-font font-profont-nerd-font font-caskaydia-cove-nerd-font font-monaspace-nerd-font font-noto-nerd-font +``` +### Linux +```bash +FONT_DIR="${HOME}/.local/share/fonts" +mkdir -p "$FONT_DIR" + +for font in ${{ env.FONTS }}; do + ZIP_FILE="${font}${EXTENSION}" + if [[ "$font" == "Monaspace" ]]; then + DOWNLOAD_URL="https://github.com/githubnext/monaspace/releases/download/v1.101/monaspace-v1.101.zip" + else + DOWNLOAD_URL="https://github.com/ryanoasis/nerd-fonts/releases/download/${VERSION}/${ZIP_FILE}" + fi + echo "Downloading and installing '$font'..." + wget --quiet "$DOWNLOAD_URL" -O "$ZIP_FILE" + unzip -oq "$ZIP_FILE" -d "$FONT_DIR" + rm "$ZIP_FILE" + echo "'$font' installed successfully." +done + +# Refresh font cache +fc-cache -fv +``` diff --git a/crates/scene_viewer/src/main.rs b/crates/scene_viewer/src/main.rs index 3092931..a6e898b 100644 --- a/crates/scene_viewer/src/main.rs +++ b/crates/scene_viewer/src/main.rs @@ -2,7 +2,7 @@ use std::{fs::File, io::Read, path::Path, sync::Arc}; use futures::executor::block_on; -use glamour::{Point2, Size2}; +use glamour::{Point2, Rect, Size2}; use notify::{recommended_watcher, RecursiveMode, Watcher}; use palette::Srgba; use parking_lot::RwLock; @@ -57,8 +57,10 @@ impl ApplicationHandler for App { scene.add_layer(Default::default()); scene.add_quad( Quad::new( - Point2::new(self.mouse_pos.x as f32, self.mouse_pos.y as f32), - Size2::new(100., 100.), + Rect::new( + Point2::new(self.mouse_pos.x as f32, self.mouse_pos.y as f32), + Size2::new(100., 100.), + ), Srgba::new(0.5, 0.5, 1., 0.5), ) .with_edge_blur(5.0), diff --git a/scene.json b/scene.json index cd37d14..54eee6f 100644 --- a/scene.json +++ b/scene.json @@ -5589,13 +5589,15 @@ { "Quads": [ { - "top_left": { - "x": 150.0, - "y": 100.0 - }, - "size": { - "width": 100.0, - "height": 100.0 + "region": { + "origin": { + "x": 150.0, + "y": 100.0 + }, + "size": { + "width": 100.0, + "height": 100.0 + } }, "color": { "red": 1.0, @@ -10563,13 +10565,15 @@ { "Quads": [ { - "top_left": { - "x": 500.0, - "y": 10.0 - }, - "size": { - "width": 50.0, - "height": 50.0 + "region": { + "origin": { + "x": 500.0, + "y": 10.0 + }, + "size": { + "width": 50.0, + "height": 50.0 + } }, "color": { "red": 1.0, @@ -10581,13 +10585,15 @@ "edge_blur": 0.0 }, { - "top_left": { - "x": 510.0, - "y": 20.0 - }, - "size": { - "width": 50.0, - "height": 50.0 + "region": { + "origin": { + "x": 510.0, + "y": 20.0 + }, + "size": { + "width": 50.0, + "height": 50.0 + } }, "color": { "red": 1.0, @@ -10599,13 +10605,15 @@ "edge_blur": 0.0 }, { - "top_left": { - "x": 520.0, - "y": 30.0 - }, - "size": { - "width": 50.0, - "height": 50.0 + "region": { + "origin": { + "x": 520.0, + "y": 30.0 + }, + "size": { + "width": 50.0, + "height": 50.0 + } }, "color": { "red": 0.0, @@ -10617,13 +10625,15 @@ "edge_blur": 0.0 }, { - "top_left": { - "x": 530.0, - "y": 40.0 - }, - "size": { - "width": 50.0, - "height": 50.0 + "region": { + "origin": { + "x": 530.0, + "y": 40.0 + }, + "size": { + "width": 50.0, + "height": 50.0 + } }, "color": { "red": 0.0, @@ -10635,13 +10645,15 @@ "edge_blur": 0.0 }, { - "top_left": { - "x": 540.0, - "y": 50.0 - }, - "size": { - "width": 50.0, - "height": 50.0 + "region": { + "origin": { + "x": 540.0, + "y": 50.0 + }, + "size": { + "width": 50.0, + "height": 50.0 + } }, "color": { "red": 0.0, @@ -10663,4 +10675,4 @@ }, "textures": {} } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 744298b..fc063a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ mod drawable; mod drawable_pipeline; mod drawable_reference; mod offscreen_renderer; +pub mod prelude; mod renderer; mod scene; mod shader; @@ -15,6 +16,7 @@ mod test; pub use glamour; pub use palette; pub use parley; +pub use winit; pub use offscreen_renderer::OffscreenRenderer; pub use renderer::Renderer; diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..28c371f --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,6 @@ +pub use crate::offscreen_renderer::OffscreenRenderer; +pub use crate::renderer::Renderer; +pub use crate::scene::*; +pub use crate::shader::*; +pub use crate::shaper::Shaper; +pub use crate::winit_renderer::WinitRenderer; diff --git a/src/scene.rs b/src/scene.rs index 21a086b..f4dc826 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -121,13 +121,13 @@ impl Scene { self } - pub fn add_text_layout(&mut self, layout: Layout, top_left: Point2) { + pub fn add_text_layout(&mut self, layout: &Layout, top_left: Point2) { self.update_layer(|resources, layer| { layer.add_text_layout(resources, layout, top_left); }); } - pub fn with_text_layout(mut self, layout: Layout, top_left: Point2) -> Self { + pub fn with_text_layout(mut self, layout: &Layout, top_left: Point2) -> Self { self.add_text_layout(layout, top_left); self } diff --git a/src/scene/layer.rs b/src/scene/layer.rs index e993eba..8a75802 100644 --- a/src/scene/layer.rs +++ b/src/scene/layer.rs @@ -63,8 +63,7 @@ impl Layer { pub fn add_clear(&mut self, color: Srgba) { self.add_quad(Quad::new( - point2!(0.0, 0.0), - size2!(f32::MAX / 2., f32::MAX / 2.), + Rect::new(point2!(0.0, 0.0), size2!(f32::MAX / 2., f32::MAX / 2.)), color, )); } @@ -163,7 +162,7 @@ impl Layer { pub fn add_text_layout( &mut self, resources: &mut Resources, - layout: Layout, + layout: &Layout, position: Point2, ) { for line in layout.lines() { @@ -208,7 +207,7 @@ impl Layer { pub fn with_text_layout( mut self, resources: &mut Resources, - layout: Layout, + layout: &Layout, position: Point2, ) -> Self { self.add_text_layout(resources, layout, position); diff --git a/src/scene/path.rs b/src/scene/path.rs index 34a2520..827b987 100644 --- a/src/scene/path.rs +++ b/src/scene/path.rs @@ -52,7 +52,12 @@ impl Path { } } + #[deprecated(note = "Use new_line_instead")] pub fn new_open_stroke(width: f32, color: Srgba, start: Point2) -> Self { + Self::new_line(width, color, start) + } + + pub fn new_line(width: f32, color: Srgba, start: Point2) -> Self { Self { fill: None, stroke: Some((width, color)), diff --git a/src/scene/quad.rs b/src/scene/quad.rs index 25051ec..a28d712 100644 --- a/src/scene/quad.rs +++ b/src/scene/quad.rs @@ -1,13 +1,12 @@ use crate::default_drawables::InstancedQuad; use glam::Vec4; -use glamour::{AsRaw, Point2, Size2}; +use glamour::{AsRaw, Rect}; use palette::Srgba; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Quad { - pub top_left: Point2, - pub size: Size2, + pub region: Rect, pub color: Srgba, #[serde(default)] pub corner_radius: f32, @@ -16,10 +15,9 @@ pub struct Quad { } impl Quad { - pub fn new(top_left: Point2, size: Size2, color: Srgba) -> Self { + pub fn new(region: Rect, color: Srgba) -> Self { Self { - top_left, - size, + region, color, corner_radius: 0.0, edge_blur: 0.0, @@ -38,8 +36,8 @@ impl Quad { pub fn to_instanced(&self) -> InstancedQuad { InstancedQuad { - top_left: *self.top_left.as_raw(), - size: *self.size.as_raw(), + top_left: *self.region.origin.as_raw(), + size: *self.region.size.as_raw(), color: Vec4::from_array(self.color.into_linear().into()), corner_radius: self.corner_radius, edge_blur: self.edge_blur, diff --git a/src/test.rs b/src/test.rs index ac30358..e0da156 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,3 +1,5 @@ +mod font_styles; + use std::{env::temp_dir, fs::create_dir_all, path::PathBuf, thread}; use glamour::{point2, size2, vec2, Rect}; @@ -5,23 +7,15 @@ use image::ImageReader; use lazy_static::lazy_static; use palette::Srgba; use parley::{ - style::{ - FontFamily, FontSettings, FontStack, FontStretch, FontStyle, FontWeight, StyleProperty, - }, - swash, - swash::{tag_from_bytes, Attributes, Setting, Stretch, Style, Tag, Weight}, + style::{FontFamily, FontSettings, FontStack, FontWeight, StyleProperty}, + swash::Setting, }; use rust_embed::RustEmbed; use crate::{ - offscreen_renderer::OffscreenRenderer, scene::Scene, scene::Synthesis, Layer, Path, Quad, - Shaper, Sprite, Texture, + offscreen_renderer::OffscreenRenderer, scene::Scene, Layer, Path, Quad, Shaper, Sprite, Texture, }; -const WGHT: Tag = tag_from_bytes(b"wght"); -const WDTH: Tag = tag_from_bytes(b"wdth"); -const SLNT: Tag = tag_from_bytes(b"slnt"); - #[derive(RustEmbed)] #[folder = "test_data/assets"] struct Assets; @@ -95,8 +89,7 @@ fn simple_quad() { Scene::new() .with_clear(Srgba::new(1., 0., 0.5, 1.)) .with_quad(Quad::new( - point2!(10., 10.), - size2!(50., 50.), + Rect::new(point2!(10., 10.), size2!(50., 50.)), Srgba::new(0., 0., 1., 1.), )), ); @@ -117,7 +110,7 @@ fn simple_text() { builder.push_default(&StyleProperty::FontSize(font_size)); }); - scene.add_text_layout(layout, point2!(0., y)); + scene.add_text_layout(&layout, point2!(0., y)); } assert_eq!(scene.resources.fonts.len(), 1); @@ -157,12 +150,12 @@ fn simple_blur() { shaper.push_default(StyleProperty::FontStack(FontStack::Source("monospace"))); shaper.push_default(StyleProperty::Brush(Srgba::new(0., 0., 0., 1.))); + let layout = shaper.layout_with("TestTestTestTestTestTestTestTest", |builder| { + builder.push_default(&StyleProperty::FontSize(15.)); + }); for i in 0..20 { let bottom = 15. * i as f32; - let layout = shaper.layout_with("TestTestTestTestTestTestTestTest", |builder| { - builder.push_default(&StyleProperty::FontSize(15.)); - }); - scene.add_text_layout(layout, point2!(0., bottom)); + scene.add_text_layout(&layout, point2!(0., bottom)); } for x in 0..3 { @@ -187,8 +180,10 @@ fn simple_blurred_quad() { for y in 0..5 { scene.add_quad( Quad::new( - point2!(15., 15.) + vec2!(x as f32 * 60., y as f32 * 60.), - size2!(50., 50.), + Rect::new( + point2!(15., 15.) + vec2!(x as f32 * 60., y as f32 * 60.), + size2!(50., 50.), + ), Srgba::new(x as f32 / 5., y as f32 / 5., 1., 1.), ) .with_corner_radius(x as f32 * 2.) @@ -213,8 +208,10 @@ fn overlapping_quads() { for (i, color) in colors.into_iter().enumerate() { scene.add_quad(Quad::new( - point2!(10., 10.) + vec2!(i as f32 * 10., i as f32 * 10.), - size2!(50., 50.), + Rect::new( + point2!(10., 10.) + vec2!(i as f32 * 10., i as f32 * 10.), + size2!(50., 50.), + ), color, )); } @@ -243,7 +240,7 @@ fn swash_modern_ligatures() { .collect::>>(), ))); }); - scene.add_text_layout(layout, point2!(5., 5.)); + scene.add_text_layout(&layout, point2!(5., 5.)); assert_no_regressions(200, 30, scene); } @@ -260,11 +257,10 @@ fn text_layout_bounds() { }); scene.add_quad(Quad::new( - point2!(10., 10.), - size2!(layout.width(), layout.height()), + Rect::new(point2!(10., 10.), size2!(layout.width(), layout.height())), Srgba::new(0., 1., 0., 0.5), )); - scene.add_text_layout(layout, point2!(10., 10.)); + scene.add_text_layout(&layout, point2!(10., 10.)); assert_no_regressions(325, 35, scene); } @@ -289,441 +285,7 @@ fn parley_line_breaking_and_font_fallback() { let layout_width = layout.width(); let layout_height = layout.height(); - scene.add_text_layout(layout, point2!(padding, padding)); - - assert_no_regressions( - (layout_width + padding * 2.) as u32, - (layout_height + padding * 2.) as u32, - scene, - ); -} - -#[test] -fn font_styles() { - let mut scene = Scene::new(); - let mut shaper = Shaper::new(); - - let padding = 10.; - #[derive(Debug, PartialEq, Eq)] - struct Expected { - fullname: String, - attributes: Attributes, - synthesis: Synthesis, - } - - let lines = vec![ - ( - "FiraCode Normal", - vec![StyleProperty::FontStack(FontStack::Source( - "FiraCode Nerd Font", - ))], - Expected { - fullname: "FiraCode Nerd Font Reg".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "FiraCode (Native) Bold", - vec![ - StyleProperty::FontStack(FontStack::Source("FiraCode Nerd Font")), - StyleProperty::FontWeight(FontWeight::BOLD), - ], - Expected { - fullname: "FiraCode Nerd Font Bold".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::BOLD, Style::Normal), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "FiraCode (Faux) Italic", - vec![ - StyleProperty::FontStack(FontStack::Source("FiraCode Nerd Font")), - StyleProperty::FontStyle(FontStyle::Italic), - ], - Expected { - fullname: "FiraCode Nerd Font Reg".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 14.0.into(), - }, - }, - ), - ( - "FiraCode Oblique 5 degrees", - vec![ - StyleProperty::FontStack(FontStack::Source("FiraCode Nerd Font")), - StyleProperty::FontStyle(FontStyle::Oblique(Some(5.0))), - ], - Expected { - fullname: "FiraCode Nerd Font Reg".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 5.0.into(), - }, - }, - ), - ( - "FiraCode Synthetic Stretch Wide (Buggy)", - vec![ - StyleProperty::FontStack(FontStack::Source("FiraCode Nerd Font")), - StyleProperty::FontStretch(FontStretch::EXPANDED), - ], - Expected { - // FIXME: Fontique does not support synthetic stretch - fullname: "FiraCode Nerd Font Reg".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "ProFontWindows Nerd Font", - vec![StyleProperty::FontStack(FontStack::Source( - "ProFontWindows Nerd Font", - ))], - Expected { - fullname: "ProFontWindows Nerd Font".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "ProFontWindows Nerd Font (Faux) Bold", - vec![ - StyleProperty::FontStack(FontStack::Source("ProFontWindows Nerd Font")), - StyleProperty::FontWeight(FontWeight::BOLD), - ], - Expected { - fullname: "ProFontWindows Nerd Font".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![], - embolden: true, - skew: 0.0.into(), - }, - }, - ), - ( - "CaskaydiaCove Nerd Font Italic", - vec![ - StyleProperty::FontStack(FontStack::Source("CaskaydiaCove Nerd Font")), - StyleProperty::FontStyle(FontStyle::Italic), - ], - Expected { - fullname: "CaskaydiaCove NF Italic".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Italic), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "Monaspace Xenon Var", - vec![StyleProperty::FontStack(FontStack::Source( - "Monaspace Xenon Var", - ))], - Expected { - fullname: "Monaspace Xenon Var Regular".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "Monaspace Xenon Var (Variadic) Bold", - vec![ - StyleProperty::FontStack(FontStack::Source("Monaspace Xenon Var")), - StyleProperty::FontWeight(FontWeight::BOLD), - ], - Expected { - fullname: "Monaspace Xenon Var Regular".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![crate::Setting { - tag: WGHT, - value: swash::Weight::BOLD.0.into(), - }], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "Monaspace Xenon Var (Variadic) Italic (Buggy)", - vec![ - StyleProperty::FontStack(FontStack::Source("Monaspace Xenon Var")), - StyleProperty::FontStyle(FontStyle::Italic), - ], - Expected { - fullname: "Monaspace Xenon Var Regular".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![crate::Setting { - tag: SLNT, - // FIXME: This should be -11 - // See: https://github.com/linebender/parley/issues/94 - value: (14.0).into(), - }], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "Monaspace Xenon Var Oblique -10 degrees", - vec![ - StyleProperty::FontStack(FontStack::Source("Monaspace Xenon Var")), - StyleProperty::FontStyle(FontStyle::Oblique(Some(-10.0))), - ], - Expected { - fullname: "Monaspace Xenon Var Regular".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![crate::Setting { - tag: SLNT, - value: (-10.0).into(), - }], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "Monaspace Xenon Var Oblique 5 degreees = no italic", - vec![ - StyleProperty::FontStack(FontStack::Source("Monaspace Xenon Var")), - StyleProperty::FontStyle(FontStyle::Oblique(Some(-5.0))), - ], - Expected { - fullname: "Monaspace Xenon Var Regular".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![crate::Setting { - tag: SLNT, - value: (-5.0).into(), - }], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "Monaspace Xenon Var Stretch=113 Weight = 637, Oblique -8 degrees", - vec![ - StyleProperty::FontStack(FontStack::Source("Monaspace Xenon Var")), - StyleProperty::FontWeight(FontWeight::new(637.0)), - StyleProperty::FontStyle(FontStyle::Oblique(Some(-8.0))), - StyleProperty::FontStretch(FontStretch::from_percentage(113.0)), - ], - Expected { - fullname: "Monaspace Xenon Var Regular".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![ - crate::Setting { - tag: WDTH, - value: 113.0.into(), - }, - crate::Setting { - tag: WGHT, - value: 637.0.into(), - }, - crate::Setting { - tag: SLNT, - value: (-8.0).into(), - }, - ], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "Noto Serif Normal", - vec![StyleProperty::FontStack(FontStack::Source( - "NotoSerif Nerd Font", - ))], - Expected { - fullname: "NotoSerif NF Reg".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "Noto Serif Bold", - vec![ - StyleProperty::FontStack(FontStack::Source("NotoSerif Nerd Font")), - StyleProperty::FontWeight(FontWeight::BOLD), - ], - Expected { - fullname: "NotoSerif NF Bold".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::BOLD, Style::Normal), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "Noto Serif Condensed", - vec![ - StyleProperty::FontStack(FontStack::Source("NotoSerif Nerd Font")), - StyleProperty::FontStretch(FontStretch::CONDENSED), - ], - Expected { - fullname: "NotoSerif NF Cond Reg".into(), - attributes: Attributes::new(Stretch::CONDENSED, Weight::NORMAL, Style::Normal), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "Noto Serif Bold Condensed", - vec![ - StyleProperty::FontStack(FontStack::Source("NotoSerif Nerd Font")), - StyleProperty::FontWeight(FontWeight::BOLD), - StyleProperty::FontStretch(FontStretch::CONDENSED), - ], - Expected { - fullname: "NotoSerif NF Cond Bold".into(), - attributes: Attributes::new(Stretch::CONDENSED, Weight::BOLD, Style::Normal), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "Noto Serif Italic", - vec![ - StyleProperty::FontStack(FontStack::Source("NotoSerif Nerd Font")), - StyleProperty::FontStyle(FontStyle::Italic), - ], - Expected { - fullname: "NotoSerif NF Italic".into(), - attributes: Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Italic), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ( - "Noto Serif Bold Condensed Italic", - vec![ - StyleProperty::FontStack(FontStack::Source("NotoSerif Nerd Font")), - StyleProperty::FontWeight(FontWeight::BOLD), - StyleProperty::FontStretch(FontStretch::CONDENSED), - StyleProperty::FontStyle(FontStyle::Italic), - ], - Expected { - fullname: "NotoSerif NF Cond Bold Italic".into(), - attributes: Attributes::new(Stretch::CONDENSED, Weight::BOLD, Style::Italic), - synthesis: Synthesis { - vars: vec![], - embolden: false, - skew: 0.0.into(), - }, - }, - ), - ]; - let layout = shaper.layout_within_with( - &lines - .iter() - .map(|line| line.0) - .collect::>() - .join("\n"), - 800., - |builder| { - builder.push_default(&StyleProperty::Brush(Srgba::new(0., 0., 0., 1.))); - builder.push_default(&StyleProperty::FontSize(16.)); - let mut start = 0; - for line in &lines { - let line_len = line.0.len(); - let range = start..start + line_len; - for prop in &line.1 { - builder.push(prop, range.clone()); - } - start += line_len + 1; - } - }, - ); - - let layout_width = layout.width(); - let layout_height = layout.height(); - scene.add_text_layout(layout, point2!(padding, padding)); - - let current_layer = scene.layer(); - assert_eq!( - current_layer - .contents - .primitives - .last() - .unwrap() - .as_glyph_run_vec() - .unwrap() - .len(), - lines.len() - ); - for (index, line) in lines.iter().enumerate() { - let glyph_runs = current_layer - .contents - .primitives - .last() - .unwrap() - .as_glyph_run_vec() - .unwrap(); - let glyph_run = &glyph_runs[index]; - let font = scene.resources.fonts.get(&glyph_run.font_id).unwrap(); - let font_ref = font.as_swash_font_ref(glyph_run.font_index).unwrap(); - let fullname = font_ref - .localized_strings() - .find_by_id(swash::StringId::Full, None) - .map_or("".into(), |str| str.chars().collect::()); - let attributes = font_ref.attributes(); - let synthesis = glyph_run.synthesis.clone(); - let actual = Expected { - fullname, - attributes, - synthesis, - }; - assert_eq!(line.2, actual, "line number {index}"); - } + scene.add_text_layout(&layout, point2!(padding, padding)); assert_no_regressions( (layout_width + padding * 2.) as u32, @@ -746,8 +308,10 @@ fn complex_mask_clips_properly() { for (i, color) in colors.into_iter().enumerate() { scene.add_quad(Quad::new( - point2!(10., 10.) + vec2!(i as f32 * 20., i as f32 * 20.), - size2!(100., 100.), + Rect::new( + point2!(10., 10.) + vec2!(i as f32 * 20., i as f32 * 20.), + size2!(100., 100.), + ), color, )); } @@ -757,12 +321,12 @@ fn complex_mask_clips_properly() { shaper.push_default(StyleProperty::FontStack(FontStack::Source("monospace"))); shaper.push_default(StyleProperty::Brush(Srgba::new(0., 0., 0., 1.))); + let layout = shaper.layout_with("TestTestTestTestTestTestTestTest", |builder| { + builder.push_default(&StyleProperty::FontSize(20.)); + }); for i in 0..20 { let bottom = 20. * i as f32; - let layout = shaper.layout_with("TestTestTestTestTestTestTestTest", |builder| { - builder.push_default(&StyleProperty::FontSize(20.)); - }); - mask_layer.add_text_layout(&mut scene.resources, layout, point2!(0., bottom)); + mask_layer.add_text_layout(&mut scene.resources, &layout, point2!(0., bottom)); } scene.set_mask(mask_layer); @@ -801,7 +365,7 @@ fn open_paths() { for i in 0..10 { scene.add_path( - Path::new_open_stroke(5., Srgba::new(0., 0., 0., 1.), point2!(20., 20.)) + Path::new_line(5., Srgba::new(0., 0., 0., 1.), point2!(20., 20.)) .with_quadratic_bezier_to(point2!(20. + i as f32 * 30., 100.), point2!(20., 180.)), ); } @@ -816,12 +380,12 @@ fn simple_mask() { shaper.push_default(StyleProperty::FontStack(FontStack::Source("monospace"))); shaper.push_default(StyleProperty::Brush(Srgba::new(0., 0., 0., 1.))); + let layout = shaper.layout_with("TestTestTestTestTestTestTestTest", |builder| { + builder.push_default(&StyleProperty::FontSize(15.)); + }); for i in 0..20 { let bottom = 15. * i as f32; - let layout = shaper.layout_with("TestTestTestTestTestTestTestTest", |builder| { - builder.push_default(&StyleProperty::FontSize(15.)); - }); - scene.add_text_layout(layout, point2!(0., bottom)); + scene.add_text_layout(&layout, point2!(0., bottom)); } // Triangle path to mask diff --git a/src/test/font_styles.rs b/src/test/font_styles.rs new file mode 100644 index 0000000..0c18312 --- /dev/null +++ b/src/test/font_styles.rs @@ -0,0 +1,445 @@ +use glamour::point2; +use palette::Srgba; +use parley::{ + style::{FontStack, FontStretch, FontStyle, FontWeight, StyleProperty}, + swash, + swash::{tag_from_bytes, Attributes, Stretch, Style, Tag, Weight}, +}; + +use super::assert_no_regressions; +use crate::{scene::Scene, scene::Synthesis, Shaper}; + +const WGHT: Tag = tag_from_bytes(b"wght"); +const WDTH: Tag = tag_from_bytes(b"wdth"); +const SLNT: Tag = tag_from_bytes(b"slnt"); + +fn assert_attributes( + style_properties: Vec>, + text: &str, + expected_full_name: &'static str, + expected_attributes: Attributes, + expected_synthesis: Synthesis, +) { + let mut scene = Scene::new(); + let mut shaper = Shaper::new(); + let padding = 10.; + + let layout = shaper.layout_within_with(text, 800., |builder| { + builder.push_default(&StyleProperty::Brush(Srgba::new(0., 0., 0., 1.))); + builder.push_default(&StyleProperty::FontSize(16.)); + for prop in style_properties { + builder.push(&prop, 0..text.len()); + } + }); + + let layout_width = layout.width(); + let layout_height = layout.height(); + scene.add_text_layout(&layout, point2!(padding, padding)); + + let current_layer = scene.layer(); + + let glyph_run = ¤t_layer + .contents + .primitives + .last() + .unwrap() + .as_glyph_run_vec() + .unwrap()[0]; + let font = scene.resources.fonts.get(&glyph_run.font_id).unwrap(); + let font_ref = font.as_swash_font_ref(glyph_run.font_index).unwrap(); + let fullname = font_ref + .localized_strings() + .find_by_id(swash::StringId::Full, None) + .map_or("".into(), |str| str.chars().collect::()); + let attributes = font_ref.attributes(); + let synthesis = glyph_run.synthesis.clone(); + + assert_eq!(fullname, expected_full_name); + assert_eq!(attributes, expected_attributes); + assert_eq!(synthesis, expected_synthesis); + + assert_no_regressions( + (layout_width + padding * 2.) as u32, + (layout_height + padding * 2.) as u32, + scene, + ); +} + +#[test] +fn firacode_normal() { + assert_attributes( + vec![StyleProperty::FontStack(FontStack::Source( + "FiraCode Nerd Font", + ))], + "FiraCode Normal", + "FiraCode Nerd Font Reg".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +fn firacode_native_bold() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("FiraCode Nerd Font")), + StyleProperty::FontWeight(FontWeight::BOLD), + ], + "FiraCode (Native) Bold", + "FiraCode Nerd Font Bold".into(), + Attributes::new(Stretch::NORMAL, Weight::BOLD, Style::Normal), + Synthesis { + vars: vec![], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +fn firacode_faux_italic() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("FiraCode Nerd Font")), + StyleProperty::FontStyle(FontStyle::Italic), + ], + "FiraCode (Faux) Italic", + "FiraCode Nerd Font Reg".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![], + embolden: false, + skew: 14.0.into(), + }, + ); +} + +#[test] +fn firacode_oblique_5_degrees() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("FiraCode Nerd Font")), + StyleProperty::FontStyle(FontStyle::Oblique(Some(5.0))), + ], + "FiraCode Oblique 5 degrees", + "FiraCode Nerd Font Reg".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![], + embolden: false, + skew: 5.0.into(), + }, + ); +} + +#[test] +fn firacode_synthetic_stretch_wide() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("FiraCode Nerd Font")), + StyleProperty::FontStretch(FontStretch::EXPANDED), + ], + "FiraCode Synthetic Stretch Wide", + "FiraCode Nerd Font Reg".into(), + // FIXME: Fontique does not support synthetic stretch + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +fn profontwindows_nerd_font_faux_bold() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("ProFontWindows Nerd Font")), + StyleProperty::FontWeight(FontWeight::BOLD), + ], + "ProFontWindows Nerd Font (Faux) Bold", + "ProFontWindows Nerd Font".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![], + embolden: true, + skew: 0.0.into(), + }, + ); +} + +#[test] +#[cfg_attr(target_os = "windows", ignore = "Font not found on windows")] +fn caskaydiacove_nerd_font_italic() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("CaskaydiaCove Nerd Font")), + StyleProperty::FontStyle(FontStyle::Italic), + ], + "CaskaydiaCove Nerd Font Italic", + "CaskaydiaCove NF Italic".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Italic), + Synthesis { + vars: vec![], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +fn monaspace_xenon_var() { + assert_attributes( + vec![StyleProperty::FontStack(FontStack::Source( + "Monaspace Xenon Var", + ))], + "Monaspace Xenon Var", + "Monaspace Xenon Var Regular".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +fn monaspace_xenon_var_variadic_bold() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("Monaspace Xenon Var")), + StyleProperty::FontWeight(FontWeight::BOLD), + ], + "Monaspace Xenon Var (Variadic) Bold", + "Monaspace Xenon Var Regular".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![crate::Setting { + tag: WGHT, + value: swash::Weight::BOLD.0.into(), + }], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +fn monaspace_xenon_var_variadic_italic_buggy() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("Monaspace Xenon Var")), + StyleProperty::FontStyle(FontStyle::Italic), + ], + "Monaspace Xenon Var (Variadic) Italic", + "Monaspace Xenon Var Regular".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![crate::Setting { + tag: SLNT, + // FIXME: This should be -11 + // See: https://github.com/linebender/parley/issues/94 + value: (14.0).into(), + }], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +fn monaspace_xenon_var_oblique_minus_10_degrees() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("Monaspace Xenon Var")), + StyleProperty::FontStyle(FontStyle::Oblique(Some(-10.0))), + ], + "Monaspace Xenon Var Oblique -10 degrees", + "Monaspace Xenon Var Regular".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![crate::Setting { + tag: SLNT, + value: (-10.0).into(), + }], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +fn monaspace_xenon_var_oblique_5_degrees_no_italic() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("Monaspace Xenon Var")), + StyleProperty::FontStyle(FontStyle::Oblique(Some(-5.0))), + ], + "Monaspace Xenon Var Oblique 5 degreees = no italic", + "Monaspace Xenon Var Regular".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![crate::Setting { + tag: SLNT, + value: (-5.0).into(), + }], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +fn monaspace_xenon_var_stretch_113_weight_637_oblique_minus_8_degrees() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("Monaspace Xenon Var")), + StyleProperty::FontWeight(FontWeight::new(637.0)), + StyleProperty::FontStyle(FontStyle::Oblique(Some(-8.0))), + StyleProperty::FontStretch(FontStretch::from_percentage(113.0)), + ], + "Monaspace Xenon Var Stretch=113 Weight = 637, Oblique -8 degrees", + "Monaspace Xenon Var Regular".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![ + crate::Setting { + tag: WDTH, + value: 113.0.into(), + }, + crate::Setting { + tag: WGHT, + value: 637.0.into(), + }, + crate::Setting { + tag: SLNT, + value: (-8.0).into(), + }, + ], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +#[cfg_attr(target_os = "windows", ignore = "Font not found on windows")] +fn notoserif_normal() { + assert_attributes( + vec![StyleProperty::FontStack(FontStack::Source( + "NotoSerif Nerd Font", + ))], + "Noto Serif Normal", + "NotoSerif NF Reg".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +#[cfg_attr(target_os = "windows", ignore = "Font not found on windows")] +fn notoserif_bold() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("NotoSerif Nerd Font")), + StyleProperty::FontWeight(FontWeight::BOLD), + ], + "Noto Serif Bold", + "NotoSerif NF Bold".into(), + Attributes::new(Stretch::NORMAL, Weight::BOLD, Style::Normal), + Synthesis { + vars: vec![], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +#[cfg_attr(target_os = "windows", ignore = "Font not found on windows")] +fn notoserif_condensed() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("NotoSerif Nerd Font")), + StyleProperty::FontStretch(FontStretch::CONDENSED), + ], + "Noto Serif Condensed", + "NotoSerif NF Cond Reg".into(), + Attributes::new(Stretch::CONDENSED, Weight::NORMAL, Style::Normal), + Synthesis { + vars: vec![], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +#[cfg_attr(target_os = "windows", ignore = "Font not found on windows")] +fn notoserif_bold_condensed() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("NotoSerif Nerd Font")), + StyleProperty::FontWeight(FontWeight::BOLD), + StyleProperty::FontStretch(FontStretch::CONDENSED), + ], + "Noto Serif Bold Condensed", + "NotoSerif NF Cond Bold".into(), + Attributes::new(Stretch::CONDENSED, Weight::BOLD, Style::Normal), + Synthesis { + vars: vec![], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +#[cfg_attr(target_os = "windows", ignore = "Font not found on windows")] +fn notoserif_italic() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("NotoSerif Nerd Font")), + StyleProperty::FontStyle(FontStyle::Italic), + ], + "Noto Serif Italic", + "NotoSerif NF Italic".into(), + Attributes::new(Stretch::NORMAL, Weight::NORMAL, Style::Italic), + Synthesis { + vars: vec![], + embolden: false, + skew: 0.0.into(), + }, + ); +} + +#[test] +#[cfg_attr(target_os = "windows", ignore = "Font not found on windows")] +fn notoserif_bold_condensed_italic() { + assert_attributes( + vec![ + StyleProperty::FontStack(FontStack::Source("NotoSerif Nerd Font")), + StyleProperty::FontWeight(FontWeight::BOLD), + StyleProperty::FontStretch(FontStretch::CONDENSED), + StyleProperty::FontStyle(FontStyle::Italic), + ], + "Noto Serif Bold Condensed Italic", + "NotoSerif NF Cond Bold Italic".into(), + Attributes::new(Stretch::CONDENSED, Weight::BOLD, Style::Italic), + Synthesis { + vars: vec![], + embolden: false, + skew: 0.0.into(), + }, + ); +} diff --git a/test_data/Kaylee Simmons/firacode_faux_italic.png b/test_data/Kaylee Simmons/firacode_faux_italic.png new file mode 100644 index 0000000..bb9b997 Binary files /dev/null and b/test_data/Kaylee Simmons/firacode_faux_italic.png differ diff --git a/test_data/Kaylee Simmons/firacode_native_bold.png b/test_data/Kaylee Simmons/firacode_native_bold.png new file mode 100644 index 0000000..21e62c3 Binary files /dev/null and b/test_data/Kaylee Simmons/firacode_native_bold.png differ diff --git a/test_data/Kaylee Simmons/firacode_normal.png b/test_data/Kaylee Simmons/firacode_normal.png new file mode 100644 index 0000000..f88cd0e Binary files /dev/null and b/test_data/Kaylee Simmons/firacode_normal.png differ diff --git a/test_data/Kaylee Simmons/firacode_oblique_5_degrees.png b/test_data/Kaylee Simmons/firacode_oblique_5_degrees.png new file mode 100644 index 0000000..202228d Binary files /dev/null and b/test_data/Kaylee Simmons/firacode_oblique_5_degrees.png differ diff --git a/test_data/Kaylee Simmons/firacode_synthetic_stretch_wide.png b/test_data/Kaylee Simmons/firacode_synthetic_stretch_wide.png new file mode 100644 index 0000000..d5cd0e5 Binary files /dev/null and b/test_data/Kaylee Simmons/firacode_synthetic_stretch_wide.png differ diff --git a/test_data/Kaylee Simmons/monaspace_xenon_var.png b/test_data/Kaylee Simmons/monaspace_xenon_var.png new file mode 100644 index 0000000..5cd04a4 Binary files /dev/null and b/test_data/Kaylee Simmons/monaspace_xenon_var.png differ diff --git a/test_data/Kaylee Simmons/monaspace_xenon_var_oblique_5_degrees_no_italic.png b/test_data/Kaylee Simmons/monaspace_xenon_var_oblique_5_degrees_no_italic.png new file mode 100644 index 0000000..731ee03 Binary files /dev/null and b/test_data/Kaylee Simmons/monaspace_xenon_var_oblique_5_degrees_no_italic.png differ diff --git a/test_data/Kaylee Simmons/monaspace_xenon_var_oblique_minus_10_degrees.png b/test_data/Kaylee Simmons/monaspace_xenon_var_oblique_minus_10_degrees.png new file mode 100644 index 0000000..dbab42c Binary files /dev/null and b/test_data/Kaylee Simmons/monaspace_xenon_var_oblique_minus_10_degrees.png differ diff --git a/test_data/Kaylee Simmons/monaspace_xenon_var_stretch_113_weight_637_oblique_minus_8_degrees.png b/test_data/Kaylee Simmons/monaspace_xenon_var_stretch_113_weight_637_oblique_minus_8_degrees.png new file mode 100644 index 0000000..3f30ac7 Binary files /dev/null and b/test_data/Kaylee Simmons/monaspace_xenon_var_stretch_113_weight_637_oblique_minus_8_degrees.png differ diff --git a/test_data/Kaylee Simmons/monaspace_xenon_var_variadic_bold.png b/test_data/Kaylee Simmons/monaspace_xenon_var_variadic_bold.png new file mode 100644 index 0000000..ee82142 Binary files /dev/null and b/test_data/Kaylee Simmons/monaspace_xenon_var_variadic_bold.png differ diff --git a/test_data/Kaylee Simmons/monaspace_xenon_var_variadic_italic_buggy.png b/test_data/Kaylee Simmons/monaspace_xenon_var_variadic_italic_buggy.png new file mode 100644 index 0000000..46eca02 Binary files /dev/null and b/test_data/Kaylee Simmons/monaspace_xenon_var_variadic_italic_buggy.png differ diff --git a/test_data/Kaylee Simmons/parley_line_breaking_and_font_fallback.png b/test_data/Kaylee Simmons/parley_line_breaking_and_font_fallback.png index c7b06fa..d4dddad 100644 Binary files a/test_data/Kaylee Simmons/parley_line_breaking_and_font_fallback.png and b/test_data/Kaylee Simmons/parley_line_breaking_and_font_fallback.png differ diff --git a/test_data/Kaylee Simmons/profontwindows_nerd_font_faux_bold.png b/test_data/Kaylee Simmons/profontwindows_nerd_font_faux_bold.png new file mode 100644 index 0000000..69619df Binary files /dev/null and b/test_data/Kaylee Simmons/profontwindows_nerd_font_faux_bold.png differ