Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support custom glyph indices in GlyphKey #35

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
51 changes: 32 additions & 19 deletions src/darwin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ pub mod byte_order;
use byte_order::kCGBitmapByteOrder32Host;

use super::{
BitmapBuffer, Error, FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style,
Weight,
BitmapBuffer, Error, FontDesc, FontKey, GlyphId, GlyphKey, Metrics, RasterizedGlyph, Size,
Slant, Style, Weight,
};

/// According to the documentation, the index of 0 must be a missing glyph character:
Expand Down Expand Up @@ -89,19 +89,24 @@ impl Descriptor {
// Investigate if we can actually use the .-prefixed
// fallbacks somehow.
if let Ok(apple_symbols) = new_from_name("Apple Symbols", size) {
fallbacks.push(Font {
let mut font = Font {
cg_font: apple_symbols.copy_to_CGFont(),
ct_font: apple_symbols,
fallbacks: Vec::new(),
})
placeholder_glyph_index: 0,
};
font.placeholder_glyph_index = font.glyph_index(' ');
fallbacks.push(font);
};

fallbacks
} else {
Vec::new()
};

Font { ct_font, cg_font, fallbacks }
let mut font = Font { ct_font, cg_font, fallbacks, placeholder_glyph_index: 0 };
font.placeholder_glyph_index = font.glyph_index(' ');
font
}
}

Expand Down Expand Up @@ -153,13 +158,27 @@ impl crate::Rasterize for Rasterizer {
// Find a font where the given character is present.
let (font, glyph_index) = iter::once(font)
.chain(font.fallbacks.iter())
.find_map(|font| match font.glyph_index(glyph.character) {
MISSING_GLYPH_INDEX => None,
glyph_index => Some((font, glyph_index)),
.find_map(|font| {
if let Some(c) = glyph.id.as_char() {
match font.glyph_index(c) {
MISSING_GLYPH_INDEX => None,
glyph_index => Some((font, glyph_index)),
}
} else {
let index = glyph.id.value();
if index == 0 {
match font.placeholder_glyph_index {
MISSING_GLYPH_INDEX => None,
glyph_index => Some((font, glyph_index)),
}
} else {
Some((font, index))
}
}
})
.unwrap_or((font, MISSING_GLYPH_INDEX));

let glyph = font.get_glyph(glyph.character, glyph_index, self.use_thin_strokes);
let glyph = font.get_glyph(glyph.id, glyph_index, self.use_thin_strokes);

if glyph_index == MISSING_GLYPH_INDEX {
Err(Error::MissingGlyph(glyph))
Expand Down Expand Up @@ -315,6 +334,7 @@ pub struct Font {
ct_font: CTFont,
cg_font: CGFont,
fallbacks: Vec<Font>,
placeholder_glyph_index: u32,
}

unsafe impl Send for Font {}
Expand Down Expand Up @@ -375,7 +395,7 @@ impl Font {

pub fn get_glyph(
&self,
character: char,
id: GlyphId,
glyph_index: u32,
use_thin_strokes: bool,
) -> RasterizedGlyph {
Expand All @@ -391,14 +411,7 @@ impl Font {
let rasterized_height = (rasterized_descent + rasterized_ascent) as u32;

if rasterized_width == 0 || rasterized_height == 0 {
return RasterizedGlyph {
character: ' ',
width: 0,
height: 0,
top: 0,
left: 0,
buffer: BitmapBuffer::Rgb(Vec::new()),
};
return RasterizedGlyph::default();
}

let mut cg_context = CGContext::create_bitmap_context(
Expand Down Expand Up @@ -457,7 +470,7 @@ impl Font {
};

RasterizedGlyph {
character,
id,
left: rasterized_left,
top: (bounds.size.height + bounds.origin.y).ceil() as i32,
width: rasterized_width as i32,
Expand Down
43 changes: 30 additions & 13 deletions src/directwrite/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use winapi::um::dwrite;
use winapi::um::winnls::GetUserDefaultLocaleName;

use super::{
BitmapBuffer, Error, FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style,
Weight,
BitmapBuffer, Error, FontDesc, FontKey, GlyphId, GlyphKey, Metrics, RasterizedGlyph, Size,
Slant, Style, Weight,
};

/// DirectWrite uses 0 for missing glyph symbols.
Expand All @@ -30,6 +30,7 @@ struct Font {
weight: FontWeight,
style: FontStyle,
stretch: FontStretch,
placeholder_glyph_index: u16,
}

pub struct DirectWriteRasterizer {
Expand All @@ -45,7 +46,7 @@ impl DirectWriteRasterizer {
&self,
face: &FontFace,
size: Size,
character: char,
id: GlyphId,
glyph_index: u16,
) -> Result<RasterizedGlyph, Error> {
let em_size = em_size(size);
Expand Down Expand Up @@ -85,7 +86,7 @@ impl DirectWriteRasterizer {
);

Ok(RasterizedGlyph {
character,
id,
width: (bounds.right - bounds.left) as i32,
height: (bounds.bottom - bounds.top) as i32,
top: -bounds.top,
Expand Down Expand Up @@ -232,17 +233,28 @@ impl crate::Rasterize for DirectWriteRasterizer {

let loaded_fallback_font;
let mut font = loaded_font;
let mut glyph_index = self.get_glyph_index(&loaded_font.face, glyph.character);
if glyph_index == MISSING_GLYPH_INDEX {
if let Some(fallback_font) = self.get_fallback_font(&loaded_font, glyph.character) {
loaded_fallback_font = Font::from(fallback_font);
glyph_index = self.get_glyph_index(&loaded_fallback_font.face, glyph.character);
font = &loaded_fallback_font;

let glyph_index = if let Some(character) = glyph.id.as_char() {
let mut glyph_index = self.get_glyph_index(&loaded_font.face, character);
if glyph_index == MISSING_GLYPH_INDEX {
if let Some(fallback_font) = self.get_fallback_font(&loaded_font, character) {
loaded_fallback_font = Font::from(fallback_font);
glyph_index = self.get_glyph_index(&loaded_fallback_font.face, character);
font = &loaded_fallback_font;
}
}
}
glyph_index
} else {
let index = glyph.id.value();
if index == 0 {
loaded_font.placeholder_glyph_index
} else {
index as u16
}
};

let rasterized_glyph =
self.rasterize_glyph(&font.face, glyph.size, glyph.character, glyph_index)?;
self.rasterize_glyph(&font.face, glyph.size, glyph.id, glyph_index)?;

if glyph_index == MISSING_GLYPH_INDEX {
Err(Error::MissingGlyph(rasterized_glyph))
Expand All @@ -262,12 +274,17 @@ fn em_size(size: Size) -> f32 {

impl From<dwrote::Font> for Font {
fn from(font: dwrote::Font) -> Font {
let face = font.create_font_face();
let placeholder_glyph_index =
face.get_glyph_indices(&[' ' as u32]).first().copied().unwrap_or(MISSING_GLYPH_INDEX);

Font {
face: font.create_font_face(),
face,
family_name: font.family_name(),
weight: font.weight(),
style: font.style(),
stretch: font.stretch(),
placeholder_glyph_index,
}
}
}
Expand Down
54 changes: 39 additions & 15 deletions src/ft/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ struct FallbackList {
coverage: CharSet,
}

struct FaceLoadingProperties {
pub struct FaceLoadingProperties {
load_flags: LoadFlag,
render_mode: freetype::RenderMode,
lcd_filter: c_uint,
Expand All @@ -64,6 +64,7 @@ struct FaceLoadingProperties {
pixelsize_fixup_factor: Option<f64>,
ft_face: Rc<FtFace>,
rgba: Rgba,
placeholder_glyph_index: u32,
}

impl fmt::Debug for FaceLoadingProperties {
Expand All @@ -86,7 +87,7 @@ impl fmt::Debug for FaceLoadingProperties {

/// Rasterizes glyphs for a single font face.
pub struct FreeTypeRasterizer {
loader: FreeTypeLoader,
pub loader: FreeTypeLoader,
fallback_lists: HashMap<FontKey, FallbackList>,
device_pixel_ratio: f32,

Expand Down Expand Up @@ -297,22 +298,30 @@ impl FreeTypeRasterizer {
}

fn face_for_glyph(&mut self, glyph_key: GlyphKey) -> FontKey {
if let Some(face) = self.loader.faces.get(&glyph_key.font_key) {
let index = face.ft_face.get_char_index(glyph_key.character as usize);
if let Some(c) = glyph_key.id.as_char() {
if let Some(face) = self.loader.faces.get(&glyph_key.font_key) {
let index = face.ft_face.get_char_index(c as usize);

if index != 0 {
return glyph_key.font_key;
if index != 0 {
return glyph_key.font_key;
}
}
}

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 c = if let Some(c) = glyph.id.as_char() {
c
} else {
return Ok(glyph.font_key);
};

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.character) {
if !fallback_list.coverage.has_char(c) {
return Ok(glyph.font_key);
}

Expand All @@ -321,16 +330,15 @@ impl FreeTypeRasterizer {
let font_pattern = &fallback_font.pattern;
match self.loader.faces.get(&font_key) {
Some(face) => {
let index = face.ft_face.get_char_index(glyph.character as usize);
let index = face.ft_face.get_char_index(c as usize);

// We found something in a current face, so let's use it.
if index != 0 {
return Ok(font_key);
}
},
None => {
if !font_pattern.get_charset().map_or(false, |cs| cs.has_char(glyph.character))
{
if !font_pattern.get_charset().map_or(false, |cs| cs.has_char(c)) {
continue;
}

Expand All @@ -350,7 +358,18 @@ impl FreeTypeRasterizer {
// Render a normal character if it's not a cursor.
let font_key = self.face_for_glyph(glyph_key);
let face = &self.loader.faces[&font_key];
let index = face.ft_face.get_char_index(glyph_key.character as usize) as u32;

let index = if let Some(c) = glyph_key.id.as_char() {
face.ft_face.get_char_index(c as usize) as u32
} else {
let val = glyph_key.id.value();
if val == 0 {
face.placeholder_glyph_index
} else {
val
}
};

let pixelsize = face
.non_scalable
.unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.);
Expand Down Expand Up @@ -399,7 +418,7 @@ impl FreeTypeRasterizer {
Self::normalize_buffer(&glyph.bitmap(), &face.rgba)?;

let mut rasterized_glyph = RasterizedGlyph {
character: glyph_key.character,
id: glyph_key.id,
top: glyph.bitmap_top(),
left: glyph.bitmap_left(),
width: pixel_width,
Expand Down Expand Up @@ -612,10 +631,10 @@ impl From<freetype::Error> for Error {

unsafe impl Send for FreeTypeRasterizer {}

struct FreeTypeLoader {
pub struct FreeTypeLoader {
library: Library,
faces: HashMap<FontKey, FaceLoadingProperties>,
ft_faces: HashMap<FtFaceLocation, Rc<FtFace>>,
pub faces: HashMap<FontKey, FaceLoadingProperties>,
pub ft_faces: HashMap<FtFaceLocation, Rc<FtFace>>,
}

impl FreeTypeLoader {
Expand Down Expand Up @@ -663,6 +682,10 @@ impl FreeTypeLoader {
None => self.load_ft_face(ft_face_location)?,
};

// This will be different for each font so we can't use a constant but we don't want to
// look it up every time so we cache it on font load.
let placeholder_glyph_index = ft_face.get_char_index(' ' as usize);

let non_scalable = if pattern.scalable().next().unwrap_or(true) {
None
} else {
Expand Down Expand Up @@ -696,6 +719,7 @@ impl FreeTypeLoader {
pixelsize_fixup_factor,
ft_face,
rgba,
placeholder_glyph_index,
};

debug!("Loaded Face {:?}", face);
Expand Down
Loading