From 2ead568ebc4cdffb3c4326231ed745725a1c7d55 Mon Sep 17 00:00:00 2001 From: Philip Deuchler Date: Thu, 1 Aug 2024 23:58:11 -0600 Subject: [PATCH 1/7] Bump version to pull in updated `pathfinder_simd` dep (#251) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 74ee625..43e1d8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "font-kit" -version = "0.14.1" +version = "0.14.2" authors = ["Patrick Walton "] description = "A cross-platform font loading library" license = "MIT OR Apache-2.0" From 32571e9e8fd91b5b45a976fd0f6a9dea88d2262b Mon Sep 17 00:00:00 2001 From: Leon Teichroeb Date: Sat, 12 Oct 2024 17:25:27 +0200 Subject: [PATCH 2/7] SubpixelAA FreeType rasterize_glyph fix (#254) * Add failing test for #252 * Change bytes_per_pixel to usize. * Add checks to avoid confusing runtime exceptions. * Fix bitmap_size in FreeType rasterize_glyph for MODE_LCD* * Fix buffer checks in blit_from * Revert "Change bytes_per_pixel to usize." This reverts commit a8231d7f2e9e925bcd29f3fb54988635af0fb2b7. * Fix missing cast due to revert --- src/canvas.rs | 18 +++++++++++++ src/loaders/freetype.rs | 6 ++++- tests/tests.rs | 58 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/src/canvas.rs b/src/canvas.rs index 4c08abc..896ebe1 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -85,6 +85,10 @@ impl Canvas { ) } + /// Blits to a rectangle with origin at `dst_point` and size according to `src_size`. + /// If the target area overlaps the boundaries of the canvas, only the drawable region is blitted. + /// `dst_point` and `src_size` are specified in pixels. `src_stride` is specified in bytes. + /// `src_stride` must be equal or larger than the actual data length. #[allow(dead_code)] pub(crate) fn blit_from( &mut self, @@ -94,6 +98,17 @@ impl Canvas { src_stride: usize, src_format: Format, ) { + assert_eq!( + src_stride * src_size.y() as usize, + src_bytes.len(), + "Number of pixels in src_bytes does not match stride and size." + ); + assert!( + src_stride >= src_size.x() as usize * src_format.bytes_per_pixel() as usize, + "src_stride must be >= than src_size.x()" + ); + + let dst_rect = RectI::new(dst_point, src_size); let dst_rect = dst_rect.intersection(RectI::new(Vector2I::default(), self.size)); let dst_rect = match dst_rect { @@ -166,6 +181,9 @@ impl Canvas { } } + /// Blits to area `rect` using the data given in the buffer `src_bytes`. + /// `src_stride` must be specified in bytes. + /// The dimensions of `rect` must be in pixels. fn blit_from_with( &mut self, rect: RectI, diff --git a/src/loaders/freetype.rs b/src/loaders/freetype.rs index a07bbc8..a2ca581 100644 --- a/src/loaders/freetype.rs +++ b/src/loaders/freetype.rs @@ -838,9 +838,9 @@ impl Font { // that mode. let bitmap = &(*(*self.freetype_face).glyph).bitmap; let bitmap_stride = bitmap.pitch as usize; + // bitmap_width is given in bytes. let bitmap_width = bitmap.width; let bitmap_height = bitmap.rows; - let bitmap_size = Vector2I::new(bitmap_width, bitmap_height); let bitmap_buffer = bitmap.buffer as *const i8 as *const u8; let bitmap_length = bitmap_stride * bitmap_height as usize; if bitmap_buffer.is_null() { @@ -858,9 +858,12 @@ impl Font { // FIXME(pcwalton): This function should return a Result instead. match bitmap.pixel_mode as u32 { FT_PIXEL_MODE_GRAY => { + let bitmap_size = Vector2I::new(bitmap_width, bitmap_height); canvas.blit_from(dst_point, buffer, bitmap_size, bitmap_stride, Format::A8); } FT_PIXEL_MODE_LCD | FT_PIXEL_MODE_LCD_V => { + // Three bytes per pixel for Rgb24 format + let bitmap_size = Vector2I::new(bitmap_width / 3, bitmap_height); canvas.blit_from( dst_point, buffer, @@ -870,6 +873,7 @@ impl Font { ); } FT_PIXEL_MODE_MONO => { + let bitmap_size = Vector2I::new(bitmap_width, bitmap_height); canvas.blit_from_bitmap_1bpp(dst_point, buffer, bitmap_size, bitmap_stride); } _ => panic!("Unexpected FreeType pixel mode!"), diff --git a/tests/tests.rs b/tests/tests.rs index 6f4d83b..25fe61c 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -778,7 +778,7 @@ pub fn rasterize_glyph_with_full_hinting() { RasterizationOptions::Bilevel, ) .unwrap(); - let origin = -raster_rect.origin().to_f32(); + let origin: Vector2F = -raster_rect.origin().to_f32(); let mut canvas = Canvas::new(raster_rect.size(), Format::A8); font.rasterize_glyph( &mut canvas, @@ -808,6 +808,61 @@ pub fn rasterize_glyph_with_full_hinting() { } } +// https://github.com/servo/font-kit/issues/252 +// Panic when targeting Canvas larger than glyph with SubpixelAa option in Freetype. +#[cfg(all( + feature = "source", + any( + not(any(target_os = "macos", target_os = "ios", target_family = "windows")), + feature = "loader-freetype-default" + ) +))] +#[test] +pub fn rasterize_glyph_with_full_hinting_subpixel() { + let font = SystemSource::new() + .select_best_match(&[FamilyName::SansSerif], &Properties::new()) + .unwrap() + .load() + .unwrap(); + let glyph_id = font.glyph_for_char('L').unwrap(); + let size = 32.0; + let raster_rect = font + .raster_bounds( + glyph_id, + size, + Transform2F::default(), + HintingOptions::Full(size), + RasterizationOptions::SubpixelAa, + ) + .unwrap(); + let origin: Vector2F = -raster_rect.origin().to_f32(); + let mut canvas = Canvas::new(raster_rect.size(), Format::Rgb24); + font.rasterize_glyph( + &mut canvas, + glyph_id, + size, + Transform2F::from_translation(origin), + HintingOptions::Full(size), + RasterizationOptions::SubpixelAa, + ) + .unwrap(); + check_L_shape(&canvas); + + // Test with larger canvas + let mut canvas = Canvas::new(Vector2I::new(100, 100), Format::Rgb24); + font.rasterize_glyph( + &mut canvas, + glyph_id, + size, + Transform2F::from_translation(origin), + HintingOptions::Full(size), + RasterizationOptions::SubpixelAa, + ) + .unwrap(); + check_L_shape(&canvas); +} + + #[cfg(all(feature = "source", target_family = "windows"))] #[test] pub fn rasterize_glyph() { @@ -888,6 +943,7 @@ pub fn rasterize_empty_glyph_on_empty_canvas() { .unwrap(); } + #[cfg(feature = "source")] #[test] pub fn font_transform() { From ea5734e84ce4388352dace223b5839443ae98b21 Mon Sep 17 00:00:00 2001 From: Leon Teichroeb Date: Sat, 12 Oct 2024 20:48:03 +0200 Subject: [PATCH 3/7] Add libfontconfig-dev dependency for version bump to Ubuntu 24.04 (#255) * Add libfontconfig-dev dependency for version bump to Ubuntu 24.04 * Fix formatting errors. * Fix formatting --- .github/workflows/ci.yml | 2 +- src/canvas.rs | 1 - tests/tests.rs | 4 +--- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b363d73..19586e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: - name: Install run: | echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | sudo debconf-set-selections; - sudo apt-get install ttf-mscorefonts-installer; + sudo apt-get install ttf-mscorefonts-installer libfontconfig-dev; - name: Build run: cargo build - name: Tests diff --git a/src/canvas.rs b/src/canvas.rs index 896ebe1..12590fe 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -107,7 +107,6 @@ impl Canvas { src_stride >= src_size.x() as usize * src_format.bytes_per_pixel() as usize, "src_stride must be >= than src_size.x()" ); - let dst_rect = RectI::new(dst_point, src_size); let dst_rect = dst_rect.intersection(RectI::new(Vector2I::default(), self.size)); diff --git a/tests/tests.rs b/tests/tests.rs index 25fe61c..50bbef2 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -847,7 +847,7 @@ pub fn rasterize_glyph_with_full_hinting_subpixel() { ) .unwrap(); check_L_shape(&canvas); - + // Test with larger canvas let mut canvas = Canvas::new(Vector2I::new(100, 100), Format::Rgb24); font.rasterize_glyph( @@ -862,7 +862,6 @@ pub fn rasterize_glyph_with_full_hinting_subpixel() { check_L_shape(&canvas); } - #[cfg(all(feature = "source", target_family = "windows"))] #[test] pub fn rasterize_glyph() { @@ -943,7 +942,6 @@ pub fn rasterize_empty_glyph_on_empty_canvas() { .unwrap(); } - #[cfg(feature = "source")] #[test] pub fn font_transform() { From 175ee91280aaa918e471cae793701278e66b5d62 Mon Sep 17 00:00:00 2001 From: NN708 Date: Fri, 15 Nov 2024 01:36:01 +0800 Subject: [PATCH 4/7] Fix Flatpak specific paths (#256) --- src/sources/fs.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sources/fs.rs b/src/sources/fs.rs index a0ed0fc..f32936d 100644 --- a/src/sources/fs.rs +++ b/src/sources/fs.rs @@ -221,12 +221,15 @@ fn default_font_directories() -> Vec { let mut directories = vec![ PathBuf::from("/usr/share/fonts"), PathBuf::from("/usr/local/share/fonts"), - PathBuf::from("/var/run/host/usr/share/fonts"), // Flatpak specific - PathBuf::from("/var/run/host/usr/local/share/fonts"), + + // Flatpak specific + PathBuf::from("/run/host/fonts"), + PathBuf::from("/run/host/local-fonts"), + PathBuf::from("/run/host/user-fonts"), ]; if let Some(path) = dirs::home_dir() { directories.push(path.join(".fonts")); // ~/.fonts is deprecated - directories.push(path.join("local").join("share").join("fonts")); // Flatpak specific + directories.push(path.join(".local").join("share").join("fonts")); } if let Some(mut path) = dirs::data_dir() { path.push("fonts"); From d49041ca57da4e9b412951f96f74cb34e3b6324f Mon Sep 17 00:00:00 2001 From: ColdPaleLight Date: Wed, 4 Dec 2024 11:52:37 +0800 Subject: [PATCH 5/7] Fix CoreText font width to CSS font stretch conversion (#259) Previous mapping was incorrect, using [-1, 1] to [0, 8]. Based on the values ranging from 'kCTFontWidthExtraCompressed' (-0.4) to 'kCTFontWidthUltraExpanded' (0.4), the corrected conversion maps CoreText font width values from [-0.4, 0.4] to [0, 8] --- src/loaders/core_text.rs | 12 ++++++++++-- src/sources/core_text.rs | 8 ++++---- src/sources/fs.rs | 1 - 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/loaders/core_text.rs b/src/loaders/core_text.rs index 8685321..f0315a5 100644 --- a/src/loaders/core_text.rs +++ b/src/loaders/core_text.rs @@ -806,7 +806,7 @@ fn core_text_to_css_font_weight(core_text_weight: f32) -> Weight { fn core_text_width_to_css_stretchiness(core_text_width: f32) -> Stretch { Stretch(piecewise_linear_lookup( - (core_text_width + 1.0) * 4.0, + ((core_text_width + 0.4) * 10.0).clamp(0.0, 8.0), &Stretch::MAPPING, )) } @@ -969,6 +969,14 @@ mod test { super::core_text_width_to_css_stretchiness(-1.0), Stretch(0.5) ); + assert_eq!( + super::core_text_width_to_css_stretchiness(-0.5), + Stretch(0.5) + ); + assert_eq!( + super::core_text_width_to_css_stretchiness(0.5), + Stretch(2.0) + ); assert_eq!( super::core_text_width_to_css_stretchiness(1.0), Stretch(2.0) @@ -976,7 +984,7 @@ mod test { // Linear interpolation assert_eq!( - super::core_text_width_to_css_stretchiness(0.85), + super::core_text_width_to_css_stretchiness(0.34), Stretch(1.7) ); } diff --git a/src/sources/core_text.rs b/src/sources/core_text.rs index e4b6292..2c4a0ed 100644 --- a/src/sources/core_text.rs +++ b/src/sources/core_text.rs @@ -150,7 +150,7 @@ fn css_to_core_text_font_weight(css_weight: Weight) -> f32 { #[allow(dead_code)] fn css_stretchiness_to_core_text_width(css_stretchiness: Stretch) -> f32 { let css_stretchiness = utils::clamp(css_stretchiness.0, 0.5, 2.0); - 0.25 * core_text_loader::piecewise_linear_find_index(css_stretchiness, &Stretch::MAPPING) - 1.0 + 0.1 * core_text_loader::piecewise_linear_find_index(css_stretchiness, &Stretch::MAPPING) - 0.4 } #[derive(Clone)] @@ -286,17 +286,17 @@ mod test { ); assert_eq!( super::css_stretchiness_to_core_text_width(Stretch(0.5)), - -1.0 + -0.4 ); assert_eq!( super::css_stretchiness_to_core_text_width(Stretch(2.0)), - 1.0 + 0.4 ); // Linear interpolation assert_eq!( super::css_stretchiness_to_core_text_width(Stretch(1.7)), - 0.85 + 0.34 ); } } diff --git a/src/sources/fs.rs b/src/sources/fs.rs index f32936d..a2d50c4 100644 --- a/src/sources/fs.rs +++ b/src/sources/fs.rs @@ -221,7 +221,6 @@ fn default_font_directories() -> Vec { let mut directories = vec![ PathBuf::from("/usr/share/fonts"), PathBuf::from("/usr/local/share/fonts"), - // Flatpak specific PathBuf::from("/run/host/fonts"), PathBuf::from("/run/host/local-fonts"), From 504d084e29bce4f60614bc702e91af7f7d9e60ad Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Mon, 17 Mar 2025 11:57:47 +1300 Subject: [PATCH 6/7] Upgrade dirs to 6.0.0 (#261) Signed-off-by: Nico Burns --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 43e1d8c..449df31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ freetype-sys = "0.20" yeslogic-fontconfig-sys = "6.0" [target.'cfg(not(any(target_arch = "wasm32", target_family = "windows", target_os = "android", target_env = "ohos")))'.dependencies] -dirs = "5.0" +dirs = "6.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] walkdir = "2.1" From 30dae22490c4a44ed35c5cff236ce374c1fecf9f Mon Sep 17 00:00:00 2001 From: Andy <8334252+acarl005@users.noreply.github.com> Date: Sat, 29 Mar 2025 16:41:41 -0400 Subject: [PATCH 7/7] Avoid panics when calling into `dwrote` (#260) * Avoid panics when calling into dwrote * bump dwrote version * migrate another method call --- Cargo.toml | 2 +- src/loaders/directwrite.rs | 4 +++- src/sources/directwrite.rs | 21 +++++++++++---------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 449df31..05bec76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ pbr = "1.0" prettytable-rs = "0.10" [target.'cfg(target_family = "windows")'.dependencies] -dwrote = { version = "0.11", default-features = false } +dwrote = { version = "^0.11.3", default-features = false } [target.'cfg(target_family = "windows")'.dependencies.winapi] version = "0.3" diff --git a/src/loaders/directwrite.rs b/src/loaders/directwrite.rs index b39b7d9..4f77fd9 100644 --- a/src/loaders/directwrite.rs +++ b/src/loaders/directwrite.rs @@ -105,7 +105,9 @@ impl Font { font_index -= 1; continue; } - let dwrite_font = family.get_font(family_font_index); + let Ok(dwrite_font) = family.font(family_font_index) else { + continue; + }; let dwrite_font_face = dwrite_font.create_font_face(); return Ok(Font { dwrite_font, diff --git a/src/sources/directwrite.rs b/src/sources/directwrite.rs index 16a8f9f..b295279 100644 --- a/src/sources/directwrite.rs +++ b/src/sources/directwrite.rs @@ -41,7 +41,9 @@ impl DirectWriteSource { for dwrite_family in self.system_font_collection.families_iter() { for font_index in 0..dwrite_family.get_font_count() { - let dwrite_font = dwrite_family.get_font(font_index); + let Ok(dwrite_font) = dwrite_family.font(font_index) else { + continue; + }; handles.push(self.create_handle_from_dwrite_font(dwrite_font)) } } @@ -54,7 +56,7 @@ impl DirectWriteSource { Ok(self .system_font_collection .families_iter() - .map(|dwrite_family| dwrite_family.name()) + .filter_map(|dwrite_family| dwrite_family.family_name().ok()) .collect()) } @@ -63,16 +65,15 @@ impl DirectWriteSource { /// TODO(pcwalton): Case-insensitivity. pub fn select_family_by_name(&self, family_name: &str) -> Result { let mut family = FamilyHandle::new(); - let dwrite_family = match self - .system_font_collection - .get_font_family_by_name(family_name) - { - Some(dwrite_family) => dwrite_family, - None => return Err(SelectionError::NotFound), + let dwrite_family = match self.system_font_collection.font_family_by_name(family_name) { + Ok(Some(dwrite_family)) => dwrite_family, + Err(_) | Ok(None) => return Err(SelectionError::NotFound), }; for font_index in 0..dwrite_family.get_font_count() { - let dwrite_font = dwrite_family.get_font(font_index); - family.push(self.create_handle_from_dwrite_font(dwrite_font)) + let Ok(dwrite_font) = dwrite_family.font(font_index) else { + continue; + }; + family.push(self.create_handle_from_dwrite_font(dwrite_font)); } Ok(family) }