From 7ae701d57b10c07dda1a83a6d6b252c0af8785cb Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Thu, 14 May 2020 04:25:11 +0300 Subject: [PATCH] Add support for Fontconfig embolden and matrix options Fixes #1754. --- CHANGELOG.md | 1 + Cargo.lock | 20 ++++++++--------- font/Cargo.toml | 2 +- font/src/ft/fc/pattern.rs | 28 +++++++++++++++++++++-- font/src/ft/mod.rs | 47 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 84 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7aa6525..633efb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `CopySelection` action which copies into selection buffer on Linux/BSD - Option `cursor.thickness` to set terminal cursor thickness - Font fallback on Windows +- Support for Fontconfig embolden and matrix options ### Changed diff --git a/Cargo.lock b/Cargo.lock index 1e97707..97de10d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,7 +575,7 @@ dependencies = [ "dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.20.11 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "freetype-rs 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", + "freetype-rs 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "servo-fontconfig 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -621,17 +621,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "freetype-rs" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "freetype-sys 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "freetype-sys 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "freetype-sys" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1638,16 +1638,16 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", - "servo-fontconfig-sys 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "servo-fontconfig-sys 5.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "servo-fontconfig-sys" -version = "5.0.1" +version = "5.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "expat-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "freetype-sys 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "freetype-sys 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2298,8 +2298,8 @@ dependencies = [ "checksum foreign-types-macros 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "63f713f8b2aa9e24fec85b0e290c56caee12e3b6ae0aeeda238a75b28251afd6" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum foreign-types-shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7684cf33bb7f28497939e8c7cf17e3e4e3b8d9a0080ffa4f8ae2f515442ee855" -"checksum freetype-rs 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)" = "340519227cdc8f41c27b3840a02e32dac6102dc314aefe6189cfa355233c445f" -"checksum freetype-sys 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d48ac0ce366dd47a115ec8e598d7c51b4a974fc52ded5e53a56b31f55f34f3ea" +"checksum freetype-rs 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e687cf953d7052be94742baf3fc63b1ea21ada7d9b45f7b1918221accebf1ae" +"checksum freetype-sys 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7530337a8e76db5271f1b5f81dd5340b0adaea235cb089cefa1928bd0cb9ee71" "checksum fsevent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" "checksum fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" @@ -2417,7 +2417,7 @@ dependencies = [ "checksum serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)" = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" "checksum serde_yaml 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)" = "16c7a592a1ec97c9c1c68d75b6e537dcbf60c7618e038e7841e00af1d9ccf0c4" "checksum servo-fontconfig 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0b47fef69c52fb55838c756949c60595f0b855daa4e82fc52ad99ff3e03e2c70" -"checksum servo-fontconfig-sys 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1c5e8d42c2957f6f433d90ab61277ed0226b3f49fffb19aa5493213c42e5d6df" +"checksum servo-fontconfig-sys 5.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "facb23c6a801c935c3bddfdd7dc4e823af853babc5b0c90ffa3419ebef5d92c7" "checksum shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" "checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" "checksum signal-hook 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff2db2112d6c761e12522c65f7768548bd6e8cd23d2a9dae162520626629bd6" diff --git a/font/Cargo.toml b/font/Cargo.toml index 866b14c..d847a2f 100644 --- a/font/Cargo.toml +++ b/font/Cargo.toml @@ -14,7 +14,7 @@ log = "0.4" [target.'cfg(not(any(target_os = "macos", windows)))'.dependencies] servo-fontconfig = "0.5.0" -freetype-rs = "0.24" +freetype-rs = "0.25" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.6" diff --git a/font/src/ft/fc/pattern.rs b/font/src/ft/fc/pattern.rs index f40cc9c..da459b6 100644 --- a/font/src/ft/fc/pattern.rs +++ b/font/src/ft/fc/pattern.rs @@ -21,10 +21,14 @@ use std::str; use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; use libc::{c_char, c_double, c_int}; +use super::ffi::FcMatrix; use super::ffi::FcResultMatch; use super::ffi::{FcBool, FcFontRenderPrepare, FcPatternGetBool, FcPatternGetDouble}; use super::ffi::{FcChar8, FcConfigSubstitute, FcDefaultSubstitute, FcPattern, FcPatternHash}; -use super::ffi::{FcPatternAddCharSet, FcPatternDestroy, FcPatternDuplicate, FcPatternGetCharSet}; +use super::ffi::{ + FcPatternAddCharSet, FcPatternDestroy, FcPatternDuplicate, FcPatternGetCharSet, + FcPatternGetMatrix, +}; use super::ffi::{FcPatternAddDouble, FcPatternAddString, FcPatternCreate, FcPatternGetString}; use super::ffi::{FcPatternAddInteger, FcPatternGetInteger, FcPatternPrint}; @@ -572,9 +576,10 @@ impl PatternRef { } } + /// Get charset from the pattern. pub fn get_charset(&self) -> Option<&CharSetRef> { unsafe { - let mut charset: *mut _ = ptr::null_mut(); + let mut charset = ptr::null_mut(); let result = FcPatternGetCharSet( self.as_ptr(), @@ -591,6 +596,25 @@ impl PatternRef { } } + /// Get matrix from the pattern. + pub fn get_matrix(&self) -> Option { + unsafe { + let mut matrix = ptr::null_mut(); + let result = FcPatternGetMatrix( + self.as_ptr(), + b"matrix\0".as_ptr() as *mut c_char, + 0, + &mut matrix, + ); + + if result == FcResultMatch { + Some(*matrix) + } else { + None + } + } + } + pub fn file(&self, index: usize) -> Option { unsafe { self.get_string(b"file\0").nth(index) }.map(From::from) } diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index ef1afb5..33cd226 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -19,7 +19,7 @@ use std::fmt::{self, Display, Formatter}; use std::rc::Rc; use freetype::tt_os2::TrueTypeOS2Table; -use freetype::{self, Library}; +use freetype::{self, Library, Matrix}; use freetype::{freetype_sys, Face as FTFace}; use libc::c_uint; use log::{debug, trace}; @@ -63,6 +63,8 @@ struct FaceLoadingProperties { lcd_filter: c_uint, non_scalable: Option, colored: bool, + embolden: bool, + matrix: Option, pixelsize_fixup_factor: Option, ft_face: Rc, } @@ -99,6 +101,11 @@ fn to_freetype_26_6(f: f32) -> isize { ((1i32 << 6) as f32 * f) as isize } +#[inline] +fn to_fixedpoint_16_6(f: f64) -> i64 { + (f * 65536.0) as i64 +} + impl Rasterize for FreeTypeRasterizer { type Err = Error; @@ -333,6 +340,18 @@ impl FreeTypeRasterizer { Some(pattern.pixelsize().next().expect("has 1+ pixelsize") as f32) }; + let embolden = pattern.embolden().next().unwrap_or(false); + + let matrix = pattern.get_matrix().map(|matrix| { + // Convert Fontconfig matrix to FreeType matrix. + let xx = to_fixedpoint_16_6(matrix.xx); + let xy = to_fixedpoint_16_6(matrix.xy); + let yx = to_fixedpoint_16_6(matrix.yx); + let yy = to_fixedpoint_16_6(matrix.yy); + + Matrix { xx, xy, yx, yy } + }); + let pixelsize_fixup_factor = pattern.pixelsizefixupfactor().next(); let face = FaceLoadingProperties { @@ -341,6 +360,8 @@ impl FreeTypeRasterizer { lcd_filter: Self::ft_lcd_filter(pattern), non_scalable, colored: ft_face.has_color(), + embolden, + matrix, pixelsize_fixup_factor, ft_face, }; @@ -425,6 +446,30 @@ impl FreeTypeRasterizer { face.ft_face.load_glyph(index as u32, face.load_flags)?; let glyph = face.ft_face.glyph(); + + // Generate synthetic bold. + if face.embolden { + unsafe { + freetype_sys::FT_GlyphSlot_Embolden(glyph.raw() + as *const freetype_sys::FT_GlyphSlotRec + as *mut freetype_sys::FT_GlyphSlotRec); + } + } + + // Transform glyphs with the matrix from Fontconfig. Primarily used to generate italics. + if let Some(matrix) = face.matrix.as_ref() { + let glyph = face.ft_face.raw().glyph; + + unsafe { + // Check that the glyph is a vectorial outline, not a bitmap. + if (*glyph).format == freetype_sys::FT_GLYPH_FORMAT_OUTLINE { + let outline = &(*glyph).outline; + + freetype_sys::FT_Outline_Transform(outline, matrix); + } + } + } + glyph.render_glyph(face.render_mode)?; let (pixel_height, pixel_width, buf) = Self::normalize_buffer(&glyph.bitmap())?;