summaryrefslogtreecommitdiffstats
path: root/font
diff options
context:
space:
mode:
authorJoe Wilm <joe@jwilm.com>2016-12-30 00:34:57 -0500
committerJoe Wilm <joe@jwilm.com>2016-12-30 00:34:57 -0500
commit44c6171bc0ce461697ef568a0b1134f02cb4c9aa (patch)
tree7bd8bde7b617eec573e1895b5f2b464f8ef6dd83 /font
parent72ff775b234644113487f5af8fc358d07bdc3178 (diff)
Refactor FontConfig wrappers
There's now a proper wrapper in place for working with the FontConfig library. This should help significantly with error handling with font loading; at least, the FontConfig code shouldn't panic. The FreeType rasterizer still needs to be updated to handle missing fonts, and a more sensible default font should be specified.
Diffstat (limited to 'font')
-rw-r--r--font/Cargo.lock25
-rw-r--r--font/src/ft/list_fonts.rs485
2 files changed, 396 insertions, 114 deletions
diff --git a/font/Cargo.lock b/font/Cargo.lock
index 58622341..5dde9e48 100644
--- a/font/Cargo.lock
+++ b/font/Cargo.lock
@@ -191,3 +191,28 @@ name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[metadata]
+"checksum bitflags 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "72cd7314bd4ee024071241147222c706e80385a1605ac7d4cd2fcc339da2ae46"
+"checksum core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "20a6d0448d3a99d977ae4a2aa5a98d886a923e863e81ad9ff814645b6feb3bbd"
+"checksum core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "05eed248dc504a5391c63794fe4fb64f46f071280afaa1b73308f3c0ce4574c5"
+"checksum core-graphics 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0c56c6022ba22aedbaa7d231be545778becbe1c7aceda4c82ba2f2084dd4c723"
+"checksum core-text 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94d4f3fab9e0242a648728764ac50e322b61eeb28c2d26d483721fe392cb2878"
+"checksum euclid 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c7b555729225fcc2aabc1ac951f9346967b35c901f4f03a480c31b6a45824109"
+"checksum expat-sys 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccf6f838594c1571f176f0afdbeb9cfa9f83b478f269d3f0390939b1df4323e"
+"checksum freetype-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e8c93a141b156862ab58d0206fa44a9b20d899c86c3e6260017ab748029aa42"
+"checksum freetype-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eccfb6d96cac99921f0c2142a91765f6c219868a2c45bdfe7d65a08775f18127"
+"checksum gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "3da3a2cbaeb01363c8e3704fd9fd0eb2ceb17c6f27abd4c1ef040fb57d20dc79"
+"checksum heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "abb306abb8d398e053cfb1b3e7b72c2f580be048b85745c52652954f8ad1439c"
+"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
+"checksum libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c96061f0c8a2dc27482e394d82e23073569de41d73cd736672ccd3e5c7471bfd"
+"checksum libz-sys 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c9795a8a0498b3abab873f8f063816fcc2e002388e89df87da065628dd5a8ed2"
+"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
+"checksum make-cmd 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3"
+"checksum num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "51eab148f171aefad295f8cece636fc488b9b392ef544da31ea4b8ef6b9e9c39"
+"checksum pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8cee804ecc7eaf201a4a207241472cc870e825206f6c031e3ee2a72fa425f2fa"
+"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b"
+"checksum serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b76133a8a02f1c6ebd3fb9a2ecaab3d54302565a51320e80931adba571aadb1b"
+"checksum servo-fontconfig 0.2.0 (git+https://github.com/jwilm/rust-fontconfig)" = "<none>"
+"checksum servo-fontconfig-sys 2.11.3 (git+https://github.com/jwilm/libfontconfig)" = "<none>"
+"checksum winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3969e500d618a5e974917ddefd0ba152e4bcaae5eb5d9b8c1fbc008e9e28c24e"
+"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
diff --git a/font/src/ft/list_fonts.rs b/font/src/ft/list_fonts.rs
index 44794501..86ee9708 100644
--- a/font/src/ft/list_fonts.rs
+++ b/font/src/ft/list_fonts.rs
@@ -13,66 +13,360 @@
// limitations under the License.
//
use std::collections::HashMap;
-use std::ffi::{CStr, CString};
use std::fmt;
use std::path::PathBuf;
-use std::ptr;
-use std::str::from_utf8;
-use libc::{c_char, c_int};
+mod fc {
+ use std::ptr;
+ use std::ffi::{CStr, CString};
+ use std::str;
+ use std::ops::{Deref, DerefMut};
+
+ use libc::{c_char, c_int};
+ use fontconfig::fontconfig as ffi;
+
+ use self::ffi::{FcConfigGetCurrent, FcConfigGetFonts};
+ use self::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString};
+ use self::ffi::{FcPatternGetInteger};
+ use self::ffi::{FcObjectSetCreate, FcObjectSetAdd};
+ use self::ffi::{FcResultMatch, FcFontSetList};
+ use self::ffi::{FcChar8, FcConfig, FcPattern, FcFontSet, FcObjectSet};
+ use self::ffi::{FcFontSetDestroy, FcPatternDestroy, FcObjectSetDestroy, FcConfigDestroy};
+
+ /// FcConfig - Font Configuration
+ pub struct Config(*mut FcConfig);
+
+ /// FcFontSet
+ pub struct FontSet(*mut FcFontSet);
+
+ /// FcFontSet reference
+ pub struct FontSetRef(*mut FcFontSet);
+
+ /// Iterator over a font set
+ pub struct FontSetIter<'a> {
+ font_set: &'a FontSetRef,
+ num_fonts: usize,
+ current: usize,
+ }
-use fontconfig::fontconfig::{FcConfigGetCurrent, FcConfigGetFonts, FcSetSystem};
-use fontconfig::fontconfig::{FcPatternGetString, FcPatternCreate, FcPatternAddString};
-use fontconfig::fontconfig::{FcPatternGetInteger};
-use fontconfig::fontconfig::{FcObjectSetCreate, FcObjectSetAdd};
-use fontconfig::fontconfig::{FcResultMatch, FcFontSetList};
-use fontconfig::fontconfig::{FcChar8};
-use fontconfig::fontconfig::{FcFontSetDestroy, FcPatternDestroy, FcObjectSetDestroy};
+ macro_rules! ref_type {
+ ($($owned:ty => $refty:ty),*) => {
+ $(
+ impl Deref for $owned {
+ type Target = $refty;
+ fn deref(&self) -> &Self::Target {
+ unsafe {
+ &*(self.0 as *mut _)
+ }
+ }
+ }
+
+ impl DerefMut for $owned {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ unsafe {
+ &mut *(self.0 as *mut _)
+ }
+ }
+ }
+ )*
+ }
+ }
-unsafe fn fc_char8_to_string(fc_str: *mut FcChar8) -> String {
- from_utf8(CStr::from_ptr(fc_str as *const c_char).to_bytes()).unwrap().to_owned()
-}
+ /// FcPattern
+ pub struct Pattern(*mut FcPattern);
-fn list_families() -> Vec<String> {
- let mut families = Vec::new();
- unsafe {
- // https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcconfiggetcurrent.html
- let config = FcConfigGetCurrent(); // *mut FcConfig
-
- // https://www.freedesktop.org/software/fontconfig/fontconfig-devel/fcconfiggetfonts.html
- let font_set = FcConfigGetFonts(config, FcSetSystem); // *mut FcFontSet
-
- let nfont = (*font_set).nfont as isize;
- for i in 0..nfont {
- let font = (*font_set).fonts.offset(i); // *mut FcPattern
- let id = 0 as c_int;
- let mut family: *mut FcChar8 = ptr::null_mut();
- let mut format: *mut FcChar8 = ptr::null_mut();
-
- let result = FcPatternGetString(*font,
- b"fontformat\0".as_ptr() as *mut c_char,
- id,
- &mut format);
-
- if result != FcResultMatch {
- continue;
+ /// FcObjectSet
+ pub struct ObjectSet(*mut FcObjectSet);
+
+ /// FcObjectSet reference
+ pub struct ObjectSetRef(*mut FcObjectSet);
+
+ ref_type! {
+ ObjectSet => ObjectSetRef,
+ Pattern => PatternRef,
+ FontSet => FontSetRef
+ }
+
+ impl Drop for ObjectSet {
+ fn drop(&mut self) {
+ unsafe {
+ FcObjectSetDestroy(self.0);
}
+ }
+ }
+
+ impl ObjectSet {
+ pub fn new() -> ObjectSet {
+ ObjectSet(unsafe {
+ FcObjectSetCreate()
+ })
+ }
+ }
- let format = fc_char8_to_string(format);
- if format != "TrueType" && format != "CFF" {
- continue
+ impl ObjectSetRef {
+ fn add(&mut self, property: &[u8]) {
+ unsafe {
+ FcObjectSetAdd(self.0, property.as_ptr() as *mut c_char);
}
+ }
+
+ #[inline]
+ pub fn add_file(&mut self) {
+ self.add(b"file\0");
+ }
+
+ #[inline]
+ pub fn add_index(&mut self) {
+ self.add(b"index\0");
+ }
- let mut id = 0;
- while FcPatternGetString(
- *font,
- b"family\0".as_ptr() as *mut c_char,
- id, &mut family
- ) == FcResultMatch {
- let safe_family = fc_char8_to_string(family);
- id += 1;
- families.push(safe_family);
+ #[inline]
+ pub fn add_style(&mut self) {
+ self.add(b"style\0");
+ }
+ }
+
+ macro_rules! pattern_add_string {
+ ($name:ident => $object:expr) => {
+ #[inline]
+ pub fn $name(&mut self, value: &str) -> bool {
+ unsafe {
+ self.add_string($object, value)
+ }
+ }
+ }
+ }
+
+ impl Pattern {
+ pub fn new() -> Pattern {
+ Pattern(unsafe { FcPatternCreate() })
+ }
+ }
+
+ impl Drop for Pattern {
+ fn drop(&mut self) {
+ unsafe {
+ FcPatternDestroy(self.0);
+ }
+ }
+ }
+
+ /// FcPattern reference
+ pub struct PatternRef(*mut FcPattern);
+
+ /// Available font sets
+ pub enum SetName {
+ System = 0,
+ Application = 1,
+ }
+
+ pub unsafe fn char8_to_string(fc_str: *mut FcChar8) -> String {
+ str::from_utf8(CStr::from_ptr(fc_str as *const c_char).to_bytes()).unwrap().to_owned()
+ }
+
+ macro_rules! pattern_get_string {
+ ($($method:ident() => $property:expr),+) => {
+ $(
+ pub fn $method(&self, id: isize) -> Option<String> {
+ unsafe {
+ let mut format: *mut FcChar8 = ptr::null_mut();
+
+ let result = FcPatternGetString(
+ self.0,
+ $property.as_ptr() as *mut c_char,
+ id as c_int,
+ &mut format
+ );
+
+ if result == FcResultMatch {
+ Some(char8_to_string(format))
+ } else {
+ None
+ }
+ }
+ }
+ )+
+ };
+ }
+
+ macro_rules! pattern_get_integer {
+ ($($method:ident() => $property:expr),+) => {
+ $(
+ pub fn $method(&self, id: isize) -> Option<isize> {
+ let mut index = 0 as c_int;
+ unsafe {
+ let result = FcPatternGetInteger(
+ self.0,
+ $property.as_ptr() as *mut c_char,
+ id as c_int,
+ &mut index
+ );
+
+ if result == FcResultMatch {
+ Some(index as isize)
+ } else {
+ None
+ }
+ }
+ }
+ )+
+ };
+ }
+
+ impl PatternRef {
+ /// Add a string value to the pattern
+ ///
+ /// If the returned value is `true`, the value is added at the end of
+ /// any existing list, otherwise it is inserted at the beginning.
+ ///
+ /// # Unsafety
+ ///
+ /// `object` is not checked to be a valid null-terminated string
+ unsafe fn add_string(&mut self, object: &[u8], value: &str) -> bool {
+ let value = CString::new(&value[..]).unwrap();
+ let value = value.as_ptr();
+
+ FcPatternAddString(
+ self.0,
+ object.as_ptr() as *mut c_char,
+ value as *mut FcChar8
+ ) == 1
+ }
+
+ pattern_add_string! {
+ add_family => b"family\0"
+ }
+
+ pattern_get_string! {
+ fontformat() => b"fontformat\0",
+ family() => b"family\0",
+ file() => b"file\0",
+ style() => b"style\0"
+ }
+
+ pattern_get_integer! {
+ index() => b"index\0"
+ }
+ }
+
+ impl<'a> IntoIterator for &'a FontSet {
+ type Item = &'a PatternRef;
+ type IntoIter = FontSetIter<'a>;
+ fn into_iter(self) -> FontSetIter<'a> {
+ let num_fonts = unsafe {
+ (*self.0).nfont as isize
+ };
+
+ FontSetIter {
+ font_set: unsafe { &*(self.0 as *mut _) },
+ num_fonts: num_fonts as _,
+ current: 0,
+ }
+ }
+ }
+
+ impl<'a> IntoIterator for &'a FontSetRef {
+ type Item = &'a PatternRef;
+ type IntoIter = FontSetIter<'a>;
+ fn into_iter(self) -> FontSetIter<'a> {
+ let num_fonts = unsafe {
+ (*self.0).nfont as isize
+ };
+
+ FontSetIter {
+ font_set: self,
+ num_fonts: num_fonts as _,
+ current: 0,
+ }
+ }
+ }
+
+ impl<'a> Iterator for FontSetIter<'a> {
+ type Item = &'a PatternRef;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.current == self.num_fonts {
+ None
+ } else {
+ let pattern = unsafe {
+ let ptr = *(*self.font_set.0).fonts.offset(self.current as isize);
+ &*(ptr as *mut _)
+ };
+
+ self.current += 1;
+ Some(pattern)
+ }
+ }
+ }
+
+ impl FontSet {
+ pub fn list(
+ config: &Config,
+ source: &mut FontSetRef,
+ pattern: &PatternRef,
+ objects: &ObjectSetRef
+ ) -> FontSet {
+ let raw = unsafe {
+ FcFontSetList(
+ config.0,
+ &mut source.0,
+ 1 /* nsets */,
+ pattern.0,
+ objects.0
+ )
+ };
+ FontSet(raw)
+ }
+ }
+
+ impl Config {
+ /// Get the current configuration
+ pub fn get_current() -> Config {
+ Config(unsafe { FcConfigGetCurrent() })
+ }
+
+ /// Returns one of the two sets of fonts from the configuration as
+ /// specified by `set`.
+ pub fn get_fonts<'a>(&'a self, set: SetName) -> &'a FontSetRef {
+ unsafe {
+ let ptr = FcConfigGetFonts(self.0, set as u32);
+ &*(ptr as *mut _)
+ }
+ }
+ }
+
+ impl Drop for FontSet {
+ fn drop(&mut self) {
+ unsafe {
+ FcFontSetDestroy(self.0)
+ }
+ }
+ }
+
+ impl Drop for Config {
+ fn drop(&mut self) {
+ unsafe {
+ if self.0 != FcConfigGetCurrent() {
+ FcConfigDestroy(self.0)
+ }
+ }
+ }
+ }
+}
+
+fn list_families() -> Vec<String> {
+ let mut families = Vec::new();
+
+ let config = fc::Config::get_current();
+ let font_set = config.get_fonts(fc::SetName::System);
+ for font in font_set {
+ if let Some(format) = font.fontformat(0) {
+ if format == "TrueType" || format == "CFF" {
+ let id = 0;
+ while let Some(family) = font.family(id) {
+ families.push(family);
+ }
}
}
}
@@ -125,71 +419,33 @@ impl Family {
}
}
-static FILE: &'static [u8] = b"file\0";
-static FAMILY: &'static [u8] = b"family\0";
-static INDEX: &'static [u8] = b"index\0";
-static STYLE: &'static [u8] = b"style\0";
-
+#[allow(mutable_transmutes)]
pub fn get_family_info(family: String) -> Family {
-
let mut members = Vec::new();
-
- unsafe {
- let config = FcConfigGetCurrent(); // *mut FcConfig
- let mut font_set = FcConfigGetFonts(config, FcSetSystem); // *mut FcFontSet
-
- let pattern = FcPatternCreate();
- let family_name = CString::new(&family[..]).unwrap();
- let family_name = family_name.as_ptr();
-
- // Add family name to pattern. Use this for searching.
- FcPatternAddString(
- pattern,
- FAMILY.as_ptr() as *mut c_char,
- family_name as *mut FcChar8
- );
-
- // Request filename, style, and index for each variant in family
- let object_set = FcObjectSetCreate(); // *mut FcObjectSet
- FcObjectSetAdd(object_set, FILE.as_ptr() as *mut c_char);
- FcObjectSetAdd(object_set, INDEX.as_ptr() as *mut c_char);
- FcObjectSetAdd(object_set, STYLE.as_ptr() as *mut c_char);
-
- let variants = FcFontSetList(
- config,
- &mut font_set,
- 1 /* nsets */,
- pattern, object_set
- );
-
- let num_variant = (*variants).nfont as isize;
-
- for i in 0..num_variant {
- let font = (*variants).fonts.offset(i);
- let mut file: *mut FcChar8 = ptr::null_mut();
- assert_eq!(FcPatternGetString(*font, FILE.as_ptr() as *mut c_char, 0, &mut file),
- FcResultMatch);
- let file = fc_char8_to_string(file);
-
- let mut style: *mut FcChar8 = ptr::null_mut();
- assert_eq!(FcPatternGetString(*font, STYLE.as_ptr() as *mut c_char, 0, &mut style),
- FcResultMatch);
- let style = fc_char8_to_string(style);
-
- let mut index = 0 as c_int;
- assert_eq!(FcPatternGetInteger(*font, INDEX.as_ptr() as *mut c_char, 0, &mut index),
- FcResultMatch);
-
- members.push(Variant {
- style: style,
- file: PathBuf::from(file),
- index: index as isize,
- });
+ let config = fc::Config::get_current();
+ let font_set = config.get_fonts(fc::SetName::System);
+
+ let mut pattern = fc::Pattern::new();
+ pattern.add_family(&family);
+
+ let mut objects = fc::ObjectSet::new();
+ objects.add_file();
+ objects.add_index();
+ objects.add_style();
+
+ let variants = fc::FontSet::list(&config, unsafe { ::std::mem::transmute(font_set) }, &pattern, &objects);
+ for variant in &variants {
+ if let Some(file) = variant.file(0) {
+ if let Some(style) = variant.style(0) {
+ if let Some(index) = variant.index(0) {
+ members.push(Variant {
+ style: style,
+ file: PathBuf::from(file),
+ index: index as isize,
+ });
+ }
+ }
}
-
- FcFontSetDestroy(variants);
- FcPatternDestroy(pattern);
- FcObjectSetDestroy(object_set);
}
Family {
@@ -199,9 +455,10 @@ pub fn get_family_info(family: String) -> Family {
}
pub fn get_font_families() -> HashMap<String, Family> {
- list_families().into_iter()
- .map(|family| (family.clone(), get_family_info(family)))
- .collect()
+ list_families()
+ .into_iter()
+ .map(|family| (family.clone(), get_family_info(family)))
+ .collect()
}
#[cfg(test)]