summaryrefslogtreecommitdiffstats
path: root/font/src
diff options
context:
space:
mode:
authorKirill Chibisov <contact@kchibisov.com>2020-02-03 00:17:04 +0300
committerGitHub <noreply@github.com>2020-02-03 00:17:04 +0300
commit2672770683244bdfb55273a11328b874e2d2af04 (patch)
tree5f732808108ce0487f9b07ca0a864978b2f3a34c /font/src
parent15cc07c069b09f109ed18fb94e02e9650be7fa33 (diff)
Fix non-scalable font resize issue
Fixes resize regression from 15cc07c069b09f109ed18fb94e02e9650be7fa33.
Diffstat (limited to 'font/src')
-rw-r--r--font/src/ft/fc/pattern.rs2
-rw-r--r--font/src/ft/mod.rs255
2 files changed, 125 insertions, 132 deletions
diff --git a/font/src/ft/fc/pattern.rs b/font/src/ft/fc/pattern.rs
index af7640a9..d7e824ed 100644
--- a/font/src/ft/fc/pattern.rs
+++ b/font/src/ft/fc/pattern.rs
@@ -354,7 +354,7 @@ macro_rules! string_accessor {
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct PatternHash(u32);
+pub struct PatternHash(pub u32);
impl Pattern {
pub fn new() -> Self {
diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs
index 32e3ff82..35d3d28b 100644
--- a/font/src/ft/mod.rs
+++ b/font/src/ft/mod.rs
@@ -38,13 +38,22 @@ struct FixedSize {
struct FallbackFont {
pattern: Pattern,
- hash: PatternHash,
+ id: FontID,
}
impl FallbackFont {
- fn new(pattern: Pattern) -> FallbackFont {
- let hash = pattern.hash();
- Self { pattern, hash }
+ fn new(pattern: Pattern, id: FontID) -> FallbackFont {
+ Self { pattern, id }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
+struct FontID(u32);
+
+impl FontID {
+ fn new(lhs: PatternHash, rhs: PatternHash) -> Self {
+ // XOR two hashes to get a font ID
+ Self(lhs.0.rotate_left(1) ^ rhs.0)
}
}
@@ -88,7 +97,7 @@ impl fmt::Debug for Face {
pub struct FreeTypeRasterizer {
faces: HashMap<FontKey, Face>,
library: Library,
- keys: HashMap<PatternHash, FontKey>,
+ keys: HashMap<FontID, FontKey>,
fallback_lists: HashMap<FontKey, FallbackList>,
device_pixel_ratio: f32,
pixel_size: f64,
@@ -213,75 +222,49 @@ impl FreeTypeRasterizer {
let size = Size::new(size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.);
self.pixel_size = f64::from(size.as_f32_pts());
+ let config = fc::Config::get_current();
+ let mut pattern = Pattern::new();
+ pattern.add_family(&desc.name);
+ pattern.add_pixelsize(self.pixel_size);
+ let hash = pattern.hash();
+
+ // Add style to a pattern
match desc.style {
Style::Description { slant, weight } => {
// Match nearest font
- self.get_matching_face(&desc, slant, weight)
+ pattern.set_weight(weight.into_fontconfig_type());
+ pattern.set_slant(slant.into_fontconfig_type());
},
Style::Specific(ref style) => {
- // If a name was specified, try and load specifically that font.
- self.get_specific_face(&desc, &style)
+ // If a name was specified, try and load specifically that font
+ pattern.add_style(style);
},
}
- }
-
- fn full_metrics(&self, key: FontKey) -> Result<FullMetrics, Error> {
- let face = self.faces.get(&key).ok_or(Error::FontNotLoaded)?;
- let size_metrics = face.ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?;
-
- let width = match face.ft_face.load_char('0' as usize, face.load_flags) {
- Ok(_) => face.ft_face.glyph().metrics().horiAdvance / 64,
- Err(_) => size_metrics.max_advance / 64,
- } as f64;
-
- Ok(FullMetrics { size_metrics, cell_width: width })
- }
-
- fn get_matching_face(
- &mut self,
- desc: &FontDesc,
- slant: Slant,
- weight: Weight,
- ) -> Result<FontKey, Error> {
- let mut pattern = Pattern::new();
- pattern.add_family(&desc.name);
- pattern.set_weight(weight.into_fontconfig_type());
- pattern.set_slant(slant.into_fontconfig_type());
- pattern.add_pixelsize(self.pixel_size);
-
- self.query_font(pattern, desc)
- }
-
- fn get_specific_face(&mut self, desc: &FontDesc, style: &str) -> Result<FontKey, Error> {
- let mut pattern = Pattern::new();
- pattern.add_family(&desc.name);
- pattern.add_style(style);
- pattern.add_pixelsize(self.pixel_size);
-
- self.query_font(pattern, desc)
- }
-
- fn query_font(&mut self, pattern: Pattern, desc: &FontDesc) -> Result<FontKey, Error> {
- let config = fc::Config::get_current();
- let fonts = fc::font_sort(&config, &mut pattern.clone())
+ // Get font list using pattern. First font is the primary one while the rest are fallbacks
+ let matched_fonts = fc::font_sort(&config, &mut pattern.clone())
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
+ let mut matched_fonts = matched_fonts.into_iter();
- let mut font_iter = fonts.into_iter();
+ let primary_font =
+ matched_fonts.next().ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
- let base_font = font_iter.next().ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
- let base_font = pattern.render_prepare(config, base_font);
+ // We should render patterns to get values like `pixelsizefixupfactor`
+ let primary_font = pattern.render_prepare(config, primary_font);
+
+ // Hash pattern together with request pattern to include requested font size in the hash
+ let primary_font_id = FontID::new(hash, primary_font.hash());
// Reload already loaded faces and drop their fallback faces
- let font_key = if let Some(font_key) = self.keys.remove(&base_font.hash()) {
+ let font_key = if let Some(font_key) = self.keys.remove(&primary_font_id) {
let fallback_list = self.fallback_lists.remove(&font_key).unwrap_or_default();
for fallback_font in &fallback_list.list {
- if let Some(ff_key) = self.keys.get(&fallback_font.hash) {
+ if let Some(ff_key) = self.keys.get(&fallback_font.id) {
// Skip primary fonts, since these are all reloaded later
if !self.fallback_lists.contains_key(&ff_key) {
self.faces.remove(ff_key);
- self.keys.remove(&fallback_font.hash);
+ self.keys.remove(&fallback_font.id);
}
}
}
@@ -293,19 +276,24 @@ impl FreeTypeRasterizer {
};
// Reuse the font_key, since changing it can break library users
- let font_key = self.face_from_pattern(&base_font, font_key).and_then(|pattern| {
- pattern.map(Ok).unwrap_or_else(|| Err(Error::MissingFont(desc.to_owned())))
- })?;
+ let font_key = self
+ .face_from_pattern(&primary_font, primary_font_id, font_key)
+ .and_then(|pattern| pattern.ok_or_else(|| Error::MissingFont(desc.to_owned())))?;
// Coverage for fallback fonts
let coverage = CharSet::new();
let empty_charset = CharSet::new();
- // Load fallback list
- let list: Vec<FallbackFont> = font_iter
- .map(|font| {
- let charset = font.get_charset().unwrap_or(&empty_charset);
+
+ // Build fallback list
+ let list: Vec<FallbackFont> = matched_fonts
+ .map(|fallback_font| {
+ let charset = fallback_font.get_charset().unwrap_or(&empty_charset);
+ let fallback_font = primary_font.render_prepare(config, fallback_font);
+ let fallback_font_id = FontID::new(hash, fallback_font.hash());
+
let _ = coverage.merge(&charset);
- FallbackFont::new(font.to_owned())
+
+ FallbackFont::new(fallback_font, fallback_font_id)
})
.collect();
@@ -314,14 +302,27 @@ impl FreeTypeRasterizer {
Ok(font_key)
}
+ fn full_metrics(&self, key: FontKey) -> Result<FullMetrics, Error> {
+ let face = self.faces.get(&key).ok_or(Error::FontNotLoaded)?;
+
+ let size_metrics = face.ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?;
+
+ let width = match face.ft_face.load_char('0' as usize, face.load_flags) {
+ Ok(_) => face.ft_face.glyph().metrics().horiAdvance / 64,
+ Err(_) => size_metrics.max_advance / 64,
+ } as f64;
+
+ Ok(FullMetrics { size_metrics, cell_width: width })
+ }
+
fn face_from_pattern(
&mut self,
pattern: &PatternRef,
+ font_id: FontID,
key: Option<FontKey>,
) -> Result<Option<FontKey>, Error> {
if let (Some(path), Some(index)) = (pattern.file(0), pattern.index().next()) {
- let font_hash = pattern.hash();
- if let Some(key) = self.keys.get(&font_hash) {
+ if let Some(key) = self.keys.get(&font_id) {
return Ok(Some(*key));
}
@@ -366,37 +367,78 @@ impl FreeTypeRasterizer {
let key = face.key;
self.faces.insert(key, face);
- self.keys.insert(font_hash, key);
+ self.keys.insert(font_id, key);
Ok(Some(key))
} else {
Ok(None)
}
}
- fn face_for_glyph(
- &mut self,
- glyph_key: GlyphKey,
- have_recursed: bool,
- ) -> Result<FontKey, Error> {
- let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) {
+ fn face_for_glyph(&mut self, glyph_key: GlyphKey) -> Result<FontKey, Error> {
+ if let Some(face) = self.faces.get(&glyph_key.font_key) {
let index = face.ft_face.get_char_index(glyph_key.c as usize);
- index != 0 || have_recursed
- } else {
- false
- };
+ if index != 0 {
+ return Ok(glyph_key.font_key);
+ }
+ }
- if use_initial_face {
- Ok(glyph_key.font_key)
- } else {
- let key = self.load_face_with_glyph(glyph_key).unwrap_or(glyph_key.font_key);
- Ok(key)
+ Ok(self.load_face_with_glyph(glyph_key).unwrap_or(glyph_key.font_key))
+ }
+
+ fn load_face_with_glyph(&mut self, glyph: GlyphKey) -> Result<FontKey, Error> {
+ let fallback_list = self.fallback_lists.get(&glyph.font_key).unwrap();
+
+ // Check whether glyph is presented in any fallback font
+ if !fallback_list.coverage.has_char(glyph.c) {
+ return Ok(glyph.font_key);
}
+
+ for fallback_font in &fallback_list.list {
+ let font_id = fallback_font.id;
+ let font_pattern = &fallback_font.pattern;
+ match self.keys.get(&font_id) {
+ Some(&key) => {
+ let face = match self.faces.get(&key) {
+ Some(face) => face,
+ None => continue,
+ };
+
+ let index = face.ft_face.get_char_index(glyph.c as usize);
+
+ // We found something in a current face, so let's use it
+ if index != 0 {
+ return Ok(key);
+ }
+ },
+ None => {
+ if font_pattern.get_charset().map(|cs| cs.has_char(glyph.c)) != Some(true) {
+ continue;
+ }
+
+ // Recreate a pattern
+ let mut pattern = Pattern::new();
+ pattern.add_pixelsize(self.pixel_size as f64);
+ pattern.add_style(font_pattern.style().next().unwrap_or("Regular"));
+ pattern.add_family(font_pattern.family().next().unwrap_or("monospace"));
+
+ // Render pattern, otherwise most of its properties wont work
+ let config = fc::Config::get_current();
+ let pattern = pattern.render_prepare(config, font_pattern);
+
+ let key = self.face_from_pattern(&pattern, font_id, None)?.unwrap();
+ return Ok(key);
+ },
+ }
+ }
+
+ // You can hit this return, if you're failing to get charset from a pattern
+ Ok(glyph.font_key)
}
fn get_rendered_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> {
// Render a normal character if it's not a cursor
- let font_key = self.face_for_glyph(glyph_key, false)?;
+ let font_key = self.face_for_glyph(glyph_key)?;
let face = &self.faces[&font_key];
let index = face.ft_face.get_char_index(glyph_key.c as usize);
@@ -608,55 +650,6 @@ impl FreeTypeRasterizer {
mode => panic!("unhandled pixel mode: {:?}", mode),
}
}
-
- fn load_face_with_glyph(&mut self, glyph: GlyphKey) -> Result<FontKey, Error> {
- let fallback_list = self.fallback_lists.get(&glyph.font_key).unwrap();
-
- // Check whether glyph is presented in any fallback font
- if !fallback_list.coverage.has_char(glyph.c) {
- return Ok(glyph.font_key);
- }
-
- for fallback_font in &fallback_list.list {
- let font_pattern = &fallback_font.pattern;
- match self.keys.get(&fallback_font.hash) {
- Some(&key) => {
- let face = match self.faces.get(&key) {
- Some(face) => face,
- None => continue,
- };
-
- let index = face.ft_face.get_char_index(glyph.c as usize);
-
- // We found something in a current face, so let's use it
- if index != 0 {
- return Ok(key);
- }
- },
- None => {
- if font_pattern.get_charset().map(|cs| cs.has_char(glyph.c)) != Some(true) {
- continue;
- }
-
- // Recreate a pattern
- let mut pattern = Pattern::new();
- pattern.add_pixelsize(self.pixel_size as f64);
- pattern.add_style(font_pattern.style().next().unwrap_or("Regular"));
- pattern.add_family(font_pattern.family().next().unwrap_or("monospace"));
-
- // Render pattern, otherwise most of its properties wont work
- let config = fc::Config::get_current();
- let pattern = pattern.render_prepare(config, font_pattern);
-
- let key = self.face_from_pattern(&pattern, None)?.unwrap();
- return Ok(key);
- },
- }
- }
-
- // You can hit this return, if you're failing to get charset from a pattern
- Ok(glyph.font_key)
- }
}
/// Downscale a bitmap by a fixed factor.