Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,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
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "font-kit"
version = "0.14.1"
version = "0.14.2"
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
description = "A cross-platform font loading library"
license = "MIT OR Apache-2.0"
Expand Down Expand Up @@ -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"
Expand All @@ -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"
17 changes: 17 additions & 0 deletions src/canvas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -94,6 +98,16 @@ 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 {
Expand Down Expand Up @@ -166,6 +180,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<B: Blit>(
&mut self,
rect: RectI,
Expand Down
12 changes: 10 additions & 2 deletions src/loaders/core_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,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,
))
}
Expand Down Expand Up @@ -957,14 +957,22 @@ 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)
);

// 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)
);
}
Expand Down
4 changes: 3 additions & 1 deletion src/loaders/directwrite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,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,
Expand Down
6 changes: 5 additions & 1 deletion src/loaders/freetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -839,9 +839,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() {
Expand All @@ -859,9 +859,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,
Expand All @@ -871,6 +874,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!"),
Expand Down
8 changes: 4 additions & 4 deletions src/sources/core_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,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
}

fn create_handles_from_core_text_collection(
Expand Down Expand Up @@ -234,17 +234,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
);
}
}
21 changes: 11 additions & 10 deletions src/sources/directwrite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
Expand All @@ -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())
}

Expand All @@ -63,16 +65,15 @@ impl DirectWriteSource {
/// TODO(pcwalton): Case-insensitivity.
pub fn select_family_by_name(&self, family_name: &str) -> Result<FamilyHandle, SelectionError> {
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)
}
Expand Down
8 changes: 5 additions & 3 deletions src/sources/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,14 @@ fn default_font_directories() -> Vec<PathBuf> {
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");
Expand Down
56 changes: 55 additions & 1 deletion tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,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,
Expand Down Expand Up @@ -796,6 +796,60 @@ 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() {
Expand Down