summaryrefslogtreecommitdiffstats
path: root/font
diff options
context:
space:
mode:
authorJoe Wilm <joe@jwilm.com>2016-12-31 11:17:02 -0800
committerJoe Wilm <joe@jwilm.com>2016-12-31 20:47:02 -0800
commit2738969f292c5202800f61d4f073d82aef436836 (patch)
tree55b4a75b2c39284e1fbd1b52f172d0752e820a6a /font
parenta00970c9a83d0a9033e70650beb2f4a924cfe9cc (diff)
Propagate font rasterizer errors
This allows consumers of the font crate to handle errors instead of the library panicking.
Diffstat (limited to 'font')
-rw-r--r--font/src/darwin/mod.rs140
-rw-r--r--font/src/ft/mod.rs133
-rw-r--r--font/src/lib.rs26
3 files changed, 196 insertions, 103 deletions
diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs
index c445996d..99f080ab 100644
--- a/font/src/darwin/mod.rs
+++ b/font/src/darwin/mod.rs
@@ -54,8 +54,6 @@ use self::byte_order::extract_rgb;
use super::Size;
-static FONT_LOAD_ERROR: &'static str = "font specified by FontKey has been loaded";
-
/// Font descriptor
///
/// The descriptor provides data about a font and supports creating a font.
@@ -79,71 +77,107 @@ pub struct Rasterizer {
device_pixel_ratio: f32,
}
-impl Rasterizer {
- pub fn new(_dpi_x: f32, _dpi_y: f32, device_pixel_ratio: f32) -> Rasterizer {
+/// Errors occurring when using the core text rasterizer
+#[derive(Debug)]
+pub enum Error {
+ /// Tried to rasterize a glyph but it was not available
+ MissingGlyph(char),
+
+ /// Couldn't find font matching description
+ MissingFont(FontDesc),
+
+ /// Requested an operation with a FontKey that isn't known to the rasterizer
+ FontNotLoaded,
+}
+
+impl ::std::error::Error for Error {
+ fn description(&self) -> &str {
+ match *self {
+ Error::MissingGlyph(ref _c) => "couldn't find the requested glyph",
+ Error::MissingFont(ref _desc) => "couldn't find the requested font",
+ Error::FontNotLoaded => "tried to operate on font that hasn't been loaded",
+ }
+ }
+}
+
+impl ::std::fmt::Display for Error {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ match *self {
+ Error::MissingGlyph(ref c) => {
+ write!(f, "Glyph not found for char {:?}", c)
+ },
+ Error::MissingFont(ref desc) => {
+ write!(f, "Couldn't find a font with {}", desc)
+ },
+ Error::FontNotLoaded => {
+ f.write_str("Tried to use a font that hasn't been loaded")
+ }
+ }
+ }
+}
+
+impl ::Rasterize for Rasterizer {
+ type Err = Error;
+
+ fn new(_dpi_x: f32, _dpi_y: f32, device_pixel_ratio: f32) -> Result<Rasterizer, Error> {
println!("device_pixel_ratio: {}", device_pixel_ratio);
- Rasterizer {
+ Ok(Rasterizer {
fonts: HashMap::new(),
keys: HashMap::new(),
device_pixel_ratio: device_pixel_ratio,
- }
+ })
}
/// Get metrics for font specified by FontKey
- ///
- /// # Panics
- ///
- /// If FontKey was not generated by `load_font`, this method will panic.
- pub fn metrics(&self, key: FontKey, _size: Size) -> Metrics {
+ fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> {
// NOTE size is not needed here since the font loaded already contains
// it. It's part of the API due to platform differences.
- let font = self.fonts.get(&key).expect(FONT_LOAD_ERROR);
- font.metrics()
+ let font = self.fonts
+ .get(&key)
+ .ok_or(Error::FontNotLoaded)?;
+
+ Ok(font.metrics())
}
- pub fn load_font(&mut self, desc: &FontDesc, size: Size) -> Option<FontKey> {
+ fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> {
self.keys
.get(&(desc.to_owned(), size))
- .map(|k| *k)
- .or_else(|| {
- self.get_font(desc, size)
- .map(|font| {
- let key = FontKey::next();
+ .map(|k| Ok(*k))
+ .unwrap_or_else(|| {
+ let font = self.get_font(desc, size)?;
+ let key = FontKey::next();
- self.fonts.insert(key, font);
- self.keys.insert((desc.clone(), size), key);
+ self.fonts.insert(key, font);
+ self.keys.insert((desc.clone(), size), key);
- key
- })
+ Ok(key)
})
}
- fn get_font(&mut self, desc: &FontDesc, size: Size) -> Option<Font> {
+ /// Get rasterized glyph for given glyph key
+ fn get_glyph(&mut self, glyph: &GlyphKey) -> Result<RasterizedGlyph, Error> {
+ let scaled_size = self.device_pixel_ratio * glyph.size.as_f32_pts();
+
+ self.fonts
+ .get(&glyph.font_key)
+ .ok_or(Error::FontNotLoaded)?
+ .get_glyph(glyph.c, scaled_size as _)
+ }
+}
+
+impl Rasterizer {
+ fn get_font(&mut self, desc: &FontDesc, size: Size) -> Result<Font, Error> {
let descriptors = descriptors_for_family(&desc.name[..]);
for descriptor in descriptors {
if descriptor.style_name == desc.style {
// Found the font we want
let scaled_size = size.as_f32_pts() as f64 * self.device_pixel_ratio as f64;
let font = descriptor.to_font(scaled_size);
- return Some(font);
+ return Ok(font);
}
}
- None
- }
-
- /// Get rasterized glyph for given glyph key
- ///
- /// # Panics
- ///
- /// Panics if the FontKey specified in GlyphKey was not generated from `load_font`
- pub fn get_glyph(&mut self, glyph: &GlyphKey) -> RasterizedGlyph {
- let scaled_size = self.device_pixel_ratio * glyph.size.as_f32_pts();
-
- self.fonts
- .get(&glyph.font_key)
- .expect(FONT_LOAD_ERROR)
- .get_glyph(glyph.c, scaled_size as _)
+ Err(Error::MissingFont(desc.to_owned()))
}
}
@@ -269,21 +303,9 @@ impl Font {
)
}
- pub fn get_glyph(&self, character: char, _size: f64) -> RasterizedGlyph {
- let glyph_index = match self.glyph_index(character) {
- Some(i) => i,
- None => {
- // TODO refactor this
- return RasterizedGlyph {
- c: ' ',
- width: 0,
- height: 0,
- top: 0,
- left: 0,
- buf: Vec::new()
- };
- }
- };
+ pub fn get_glyph(&self, character: char, _size: f64) -> Result<RasterizedGlyph, Error> {
+ let glyph_index = self.glyph_index(character)
+ .ok_or(Error::MissingGlyph(character))?;
let bounds = self.bounding_rect_for_glyph(Default::default(), glyph_index);
@@ -295,14 +317,14 @@ impl Font {
let rasterized_height = (rasterized_descent + rasterized_ascent) as u32;
if rasterized_width == 0 || rasterized_height == 0 {
- return RasterizedGlyph {
+ return Ok(RasterizedGlyph {
c: ' ',
width: 0,
height: 0,
top: 0,
left: 0,
buf: Vec::new()
- };
+ });
}
let mut cg_context = CGContext::create_bitmap_context(
@@ -354,14 +376,14 @@ impl Font {
let buf = extract_rgb(rasterized_pixels);
- RasterizedGlyph {
+ Ok(RasterizedGlyph {
c: character,
left: rasterized_left,
top: (bounds.size.height + bounds.origin.y).ceil() as i32,
width: rasterized_width as i32,
height: rasterized_height as i32,
buf: buf,
- }
+ })
}
fn glyph_index(&self, character: char) -> Option<u32> {
diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs
index c9abd693..8e653816 100644
--- a/font/src/ft/mod.rs
+++ b/font/src/ft/mod.rs
@@ -15,9 +15,7 @@
//! Rasterization powered by FreeType and FontConfig
use std::collections::HashMap;
-use freetype::Library;
-use freetype::Face;
-use freetype;
+use freetype::{self, Library, Face};
mod list_fonts;
@@ -25,7 +23,7 @@ use self::list_fonts::{Family, get_font_families};
use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey};
/// Rasterizes glyphs for a single font face.
-pub struct Rasterizer {
+pub struct FreeTypeRasterizer {
faces: HashMap<FontKey, Face<'static>>,
library: Library,
system_fonts: HashMap<String, Family>,
@@ -40,16 +38,13 @@ fn to_freetype_26_6(f: f32) -> isize {
((1i32 << 6) as f32 * f) as isize
}
-// #[inline]
-// fn freetype_26_6_to_float(val: i64) -> f64 {
-// val as f64 / (1i64 << 6) as f64
-// }
+impl ::Rasterize for FreeTypeRasterizer {
+ type Err = Error;
-impl Rasterizer {
- pub fn new(dpi_x: f32, dpi_y: f32, device_pixel_ratio: f32) -> Rasterizer {
- let library = Library::init().unwrap();
+ fn new(dpi_x: f32, dpi_y: f32, device_pixel_ratio: f32) -> Result<FreeTypeRasterizer, Error> {
+ let library = Library::init()?;
- Rasterizer {
+ Ok(FreeTypeRasterizer {
system_fonts: get_font_families(),
faces: HashMap::new(),
keys: HashMap::new(),
@@ -57,11 +52,13 @@ impl Rasterizer {
dpi_x: dpi_x as u32,
dpi_y: dpi_y as u32,
dpr: device_pixel_ratio,
- }
+ })
}
- pub fn metrics(&self, key: FontKey, size: Size) -> Metrics {
- let face = self.faces.get(&key).unwrap();
+ fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> {
+ let face = self.faces
+ .get(&key)
+ .ok_or(Error::FontNotLoaded)?;
let scale_size = self.dpr as f64 * size.as_f32_pts() as f64;
@@ -72,48 +69,36 @@ impl Rasterizer {
let w_scale = w * scale_size / em_size;
let h_scale = h * scale_size / em_size;
- Metrics {
+ Ok(Metrics {
average_advance: w_scale,
line_height: h_scale,
- }
+ })
}
- pub fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Option<FontKey> {
+ fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> {
self.keys
.get(&desc.to_owned())
- .map(|k| *k)
- .or_else(|| {
- self.get_face(desc)
- .map(|face| {
- let key = FontKey::next();
- self.faces.insert(key, face);
- key
- })
- })
- }
-
- fn get_face(&mut self, desc: &FontDesc) -> Option<Face<'static>> {
- self.system_fonts
- .get(&desc.name[..])
- .and_then(|font| font.variants().get(&desc.style[..]))
- .map(|variant| {
- self.library.new_face(variant.path(), variant.index())
- .expect("TODO handle new_face error")
+ .map(|k| Ok(*k))
+ .unwrap_or_else(|| {
+ let face = self.get_face(desc)?;
+ let key = FontKey::next();
+ self.faces.insert(key, face);
+ Ok(key)
})
}
- pub fn get_glyph(&mut self, glyph_key: &GlyphKey) -> RasterizedGlyph {
+ fn get_glyph(&mut self, glyph_key: &GlyphKey) -> Result<RasterizedGlyph, Error> {
let face = self.faces
.get(&glyph_key.font_key)
- .expect("TODO handle get_face error");
+ .ok_or(Error::FontNotLoaded)?;
let size = glyph_key.size.as_f32_pts() * self.dpr;
let c = glyph_key.c;
- face.set_char_size(to_freetype_26_6(size), 0, self.dpi_x, self.dpi_y).unwrap();
- face.load_char(c as usize, freetype::face::TARGET_LIGHT).unwrap();
+ face.set_char_size(to_freetype_26_6(size), 0, self.dpi_x, self.dpi_y)?;
+ face.load_char(c as usize, freetype::face::TARGET_LIGHT)?;
let glyph = face.glyph();
- glyph.render_glyph(freetype::render_mode::RenderMode::Lcd).unwrap();
+ glyph.render_glyph(freetype::render_mode::RenderMode::Lcd)?;
unsafe {
let ft_lib = self.library.raw();
@@ -134,18 +119,80 @@ impl Rasterizer {
packed.extend_from_slice(&buf[start..stop]);
}
- RasterizedGlyph {
+ Ok(RasterizedGlyph {
c: c,
top: glyph.bitmap_top(),
left: glyph.bitmap_left(),
width: glyph.bitmap().width() / 3,
height: glyph.bitmap().rows(),
buf: packed,
+ })
+ }
+}
+
+impl FreeTypeRasterizer {
+ fn get_face(&mut self, desc: &FontDesc) -> Result<Face<'static>, Error> {
+ self.system_fonts
+ .get(&desc.name[..])
+ .and_then(|font| font.variants().get(&desc.style[..]))
+ .ok_or_else(|| Error::MissingFont(desc.to_owned()))
+ .and_then(|variant| Ok(self.library.new_face(variant.path(), variant.index())?))
+ }
+}
+
+/// Errors occurring when using the freetype rasterizer
+#[derive(Debug)]
+pub enum Error {
+ /// Error occurred within the FreeType library
+ FreeType(freetype::Error),
+
+ /// Couldn't find font matching description
+ MissingFont(FontDesc),
+
+ /// Requested an operation with a FontKey that isn't known to the rasterizer
+ FontNotLoaded,
+}
+
+impl ::std::error::Error for Error {
+ fn cause(&self) -> Option<&::std::error::Error> {
+ match *self {
+ Error::FreeType(ref err) => Some(err),
+ _ => None,
+ }
+ }
+
+ fn description(&self) -> &str {
+ match *self {
+ Error::FreeType(ref err) => err.description(),
+ Error::MissingFont(ref _desc) => "couldn't find the requested font",
+ Error::FontNotLoaded => "tried to operate on font that hasn't been loaded",
+ }
+ }
+}
+
+impl ::std::fmt::Display for Error {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ match *self {
+ Error::FreeType(ref err) => {
+ err.fmt(f)
+ },
+ Error::MissingFont(ref desc) => {
+ write!(f, "Couldn't find a font with {}", desc)
+ },
+ Error::FontNotLoaded => {
+ f.write_str("Tried to use a font that hasn't been loaded")
+ }
}
}
}
-unsafe impl Send for Rasterizer {}
+impl From<freetype::Error> for Error {
+ fn from(val: freetype::Error) -> Error {
+ Error::FreeType(val)
+ }
+}
+
+unsafe impl Send for FreeTypeRasterizer {}
#[cfg(test)]
mod tests {
diff --git a/font/src/lib.rs b/font/src/lib.rs
index 7ec5984c..afe2b94f 100644
--- a/font/src/lib.rs
+++ b/font/src/lib.rs
@@ -46,7 +46,7 @@ use std::sync::atomic::{AtomicU32, ATOMIC_U32_INIT, Ordering};
#[cfg(not(target_os = "macos"))]
mod ft;
#[cfg(not(target_os = "macos"))]
-pub use ft::*;
+pub use ft::{FreeTypeRasterizer as Rasterizer, Error};
// If target is macos, reexport everything from darwin
#[cfg(target_os = "macos")]
@@ -71,6 +71,12 @@ impl FontDesc {
}
}
+impl fmt::Display for FontDesc {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "name '{}' and style '{}'", self.name, self.style)
+ }
+}
+
/// Identifier for a Font for use in maps/etc
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct FontKey {
@@ -156,3 +162,21 @@ pub struct Metrics {
pub average_advance: f64,
pub line_height: f64,
}
+
+pub trait Rasterize {
+ /// Errors occurring in Rasterize methods
+ type Err: ::std::error::Error + Send + Sync + 'static;
+
+ /// Create a new Rasterize
+ fn new(dpi_x: f32, dpi_y: f32, device_pixel_ratio: f32) -> Result<Self, Self::Err>
+ where Self: Sized;
+
+ /// Get `Metrics` for the given `FontKey` and `Size`
+ fn metrics(&self, FontKey, Size) -> Result<Metrics, Self::Err>;
+
+ /// Load the font described by `FontDesc` and `Size`
+ fn load_font(&mut self, &FontDesc, Size) -> Result<FontKey, Self::Err>;
+
+ /// Rasterize the glyph described by `GlyphKey`.
+ fn get_glyph(&mut self, &GlyphKey) -> Result<RasterizedGlyph, Self::Err>;
+}