From 65065e06d19216ed5de9b1db952db76a5457492e Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Tue, 27 Jun 2017 10:57:26 -0700 Subject: [PATCH] Organize fontconfig wrappers Each Fc type is split into a separate file. This organization will help as features are added to the bindings. --- font/src/ft/fc/char_set.rs | 42 +++ font/src/ft/fc/config.rs | 47 +++ font/src/ft/fc/font_set.rs | 110 +++++++ font/src/ft/fc/mod.rs | 236 ++++++++++++++ font/src/ft/fc/object_set.rs | 56 ++++ font/src/ft/fc/pattern.rs | 233 ++++++++++++++ font/src/ft/list_fonts.rs | 586 ----------------------------------- font/src/ft/mod.rs | 3 +- 8 files changed, 725 insertions(+), 588 deletions(-) create mode 100644 font/src/ft/fc/char_set.rs create mode 100644 font/src/ft/fc/config.rs create mode 100644 font/src/ft/fc/font_set.rs create mode 100644 font/src/ft/fc/mod.rs create mode 100644 font/src/ft/fc/object_set.rs create mode 100644 font/src/ft/fc/pattern.rs delete mode 100644 font/src/ft/list_fonts.rs diff --git a/font/src/ft/fc/char_set.rs b/font/src/ft/fc/char_set.rs new file mode 100644 index 0000000..e6fe027 --- /dev/null +++ b/font/src/ft/fc/char_set.rs @@ -0,0 +1,42 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use foreign_types::{ForeignTypeRef}; + +use super::ffi::{FcCharSet, FcCharSetDestroy, FcCharSetAddChar}; +use super::ffi::{FcCharSetCreate}; + +foreign_type! { + type CType = FcCharSet; + fn drop = FcCharSetDestroy; + pub struct CharSet; + pub struct CharSetRef; +} + +impl CharSet { + pub fn new() -> CharSet { + CharSet(unsafe { FcCharSetCreate() }) + } +} + +impl CharSetRef { + pub fn add(&mut self, glyph: char) -> bool { + unsafe { + FcCharSetAddChar( + self.as_ptr(), + glyph as _ + ) == 1 + } + } +} + diff --git a/font/src/ft/fc/config.rs b/font/src/ft/fc/config.rs new file mode 100644 index 0000000..92993fe --- /dev/null +++ b/font/src/ft/fc/config.rs @@ -0,0 +1,47 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use foreign_types::{ForeignTypeRef}; + +use super::{SetName, FontSetRef}; +use super::ffi::{FcConfigGetCurrent, FcConfigGetFonts, FcConfig, FcConfigDestroy}; + +foreign_type! { + type CType = FcConfig; + fn drop = FcConfigDestroy; + /// Wraps an FcConfig instance (owned) + pub struct Config; + /// Wraps an FcConfig reference (borrowed) + pub struct ConfigRef; +} + + +impl Config { + /// Get the current configuration + pub fn get_current() -> &'static ConfigRef { + unsafe { + ConfigRef::from_ptr(FcConfigGetCurrent()) + } + } +} + +impl ConfigRef { + /// 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.as_ptr(), set as u32); + FontSetRef::from_ptr(ptr) + } + } +} diff --git a/font/src/ft/fc/font_set.rs b/font/src/ft/fc/font_set.rs new file mode 100644 index 0000000..a0c4eee --- /dev/null +++ b/font/src/ft/fc/font_set.rs @@ -0,0 +1,110 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use std::ops::Deref; + +use foreign_types::{ForeignType, ForeignTypeRef}; + +use super::{ConfigRef, PatternRef, ObjectSetRef}; + +use super::ffi::{FcFontSetList, FcFontSetDestroy, FcFontSet}; + +foreign_type! { + type CType = FcFontSet; + fn drop = FcFontSetDestroy; + /// Wraps an FcFontSet instance (owned) + pub struct FontSet; + /// Wraps an FcFontSet reference (borrowed) + pub struct FontSetRef; +} + +impl FontSet { + pub fn list( + config: &ConfigRef, + source: &mut FontSetRef, + pattern: &PatternRef, + objects: &ObjectSetRef + ) -> FontSet { + let raw = unsafe { + FcFontSetList( + config.as_ptr(), + &mut source.as_ptr(), + 1 /* nsets */, + pattern.as_ptr(), + objects.as_ptr(), + ) + }; + FontSet(raw) + } +} + +/// Iterator over a font set +pub struct Iter<'a> { + font_set: &'a FontSetRef, + num_fonts: usize, + current: usize, +} + +impl<'a> IntoIterator for &'a FontSet { + type Item = &'a PatternRef; + type IntoIter = Iter<'a>; + fn into_iter(self) -> Iter<'a> { + let num_fonts = unsafe { + (*self.as_ptr()).nfont as isize + }; + + info!("num fonts = {}", num_fonts); + + Iter { + font_set: self.deref(), + num_fonts: num_fonts as _, + current: 0, + } + } +} + +impl<'a> IntoIterator for &'a FontSetRef { + type Item = &'a PatternRef; + type IntoIter = Iter<'a>; + fn into_iter(self) -> Iter<'a> { + let num_fonts = unsafe { + (*self.as_ptr()).nfont as isize + }; + + info!("num fonts = {}", num_fonts); + + Iter { + font_set: self, + num_fonts: num_fonts as _, + current: 0, + } + } +} + +impl<'a> Iterator for Iter<'a> { + type Item = &'a PatternRef; + + fn next(&mut self) -> Option { + if self.current == self.num_fonts { + None + } else { + let pattern = unsafe { + let ptr = *(*self.font_set.as_ptr()).fonts.offset(self.current as isize); + PatternRef::from_ptr(ptr) + }; + + self.current += 1; + Some(pattern) + } + } +} diff --git a/font/src/ft/fc/mod.rs b/font/src/ft/fc/mod.rs new file mode 100644 index 0000000..d0a55fa --- /dev/null +++ b/font/src/ft/fc/mod.rs @@ -0,0 +1,236 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +use std::ptr; + +use foreign_types::{ForeignType, ForeignTypeRef}; + +use fontconfig::fontconfig as ffi; + +use self::ffi::{FcSetSystem, FcSetApplication}; +use self::ffi::FcResultNoMatch; +use self::ffi::{FcFontMatch, FcFontList, FcFontSort}; +use self::ffi::{FcMatchFont, FcMatchPattern, FcMatchScan}; +use self::ffi::{FC_SLANT_OBLIQUE, FC_SLANT_ITALIC, FC_SLANT_ROMAN}; +use self::ffi::{FC_WEIGHT_THIN, FC_WEIGHT_EXTRALIGHT, FC_WEIGHT_LIGHT}; +use self::ffi::{FC_WEIGHT_BOOK, FC_WEIGHT_REGULAR, FC_WEIGHT_MEDIUM, FC_WEIGHT_SEMIBOLD}; +use self::ffi::{FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABOLD, FC_WEIGHT_BLACK, FC_WEIGHT_EXTRABLACK}; + +mod config; +pub use self::config::{Config, ConfigRef}; + +mod font_set; +pub use self::font_set::{FontSet, FontSetRef}; + +mod object_set; +pub use self::object_set::{ObjectSet, ObjectSetRef}; + +mod char_set; +pub use self::char_set::{CharSet, CharSetRef}; + +mod pattern; +pub use self::pattern::{Pattern, PatternRef}; + +/// Find the font closest matching the provided pattern. +pub fn font_match( + config: &ConfigRef, + pattern: &mut PatternRef, +) -> Option { + pattern.config_subsitute(config, MatchKind::Pattern); + pattern.default_substitute(); + + unsafe { + // What is this result actually used for? Seems redundant with + // return type. + let mut result = FcResultNoMatch; + let ptr = FcFontMatch( + config.as_ptr(), + pattern.as_ptr(), + &mut result, + ); + + if ptr.is_null() { + None + } else { + Some(Pattern::from_ptr(ptr)) + } + } +} + +/// list fonts by closeness to the pattern +#[allow(dead_code)] +pub fn font_sort( + config: &ConfigRef, + pattern: &mut PatternRef, +) -> Option { + pattern.config_subsitute(config, MatchKind::Pattern); + pattern.default_substitute(); + + unsafe { + // What is this result actually used for? Seems redundant with + // return type. + let mut result = FcResultNoMatch; + + let mut charsets: *mut _ = ptr::null_mut(); + + let ptr = FcFontSort( + config.as_ptr(), + pattern.as_ptr(), + 0, // false + &mut charsets, + &mut result, + ); + + if ptr.is_null() { + None + } else { + Some(FontSet::from_ptr(ptr)) + } + } +} + +/// List fonts matching pattern +#[allow(dead_code)] +pub fn font_list( + config: &ConfigRef, + pattern: &mut PatternRef, + objects: &ObjectSetRef, +) -> Option { + pattern.config_subsitute(config, MatchKind::Pattern); + pattern.default_substitute(); + + unsafe { + let ptr = FcFontList( + config.as_ptr(), + pattern.as_ptr(), + objects.as_ptr(), + ); + + if ptr.is_null() { + None + } else { + Some(FontSet::from_ptr(ptr)) + } + } +} + +/// Available font sets +#[derive(Debug, Copy, Clone)] +pub enum SetName { + System = FcSetSystem as isize, + Application = FcSetApplication as isize, +} + +/// When matching, how to match +#[derive(Debug, Copy, Clone)] +pub enum MatchKind { + Font = FcMatchFont as isize, + Pattern = FcMatchPattern as isize, + Scan = FcMatchScan as isize, +} + +#[derive(Debug, Copy, Clone)] +pub enum Slant { + Italic = FC_SLANT_ITALIC as isize, + Oblique = FC_SLANT_OBLIQUE as isize, + Roman = FC_SLANT_ROMAN as isize, +} + +#[derive(Debug, Copy, Clone)] +pub enum Weight { + Thin = FC_WEIGHT_THIN as isize, + Extralight = FC_WEIGHT_EXTRALIGHT as isize, + Light = FC_WEIGHT_LIGHT as isize, + Book = FC_WEIGHT_BOOK as isize, + Regular = FC_WEIGHT_REGULAR as isize, + Medium = FC_WEIGHT_MEDIUM as isize, + Semibold = FC_WEIGHT_SEMIBOLD as isize, + Bold = FC_WEIGHT_BOLD as isize, + Extrabold = FC_WEIGHT_EXTRABOLD as isize, + Black = FC_WEIGHT_BLACK as isize, + Extrablack = FC_WEIGHT_EXTRABLACK as isize, +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn font_match() { + let mut pattern = Pattern::new(); + pattern.add_family("monospace"); + pattern.add_style("regular"); + + let config = Config::get_current(); + let font = super::font_match(config, &mut pattern).expect("match font monospace"); + + print!("family={:?}", font.family(0)); + for i in 0.. { + if let Some(style) = font.style(i) { + print!(", style={:?}, ", style); + } else { + break; + } + } + info!(""); + } + + #[test] + fn font_sort() { + let mut pattern = Pattern::new(); + pattern.add_family("monospace"); + pattern.set_slant(Slant::Italic); + + let config = Config::get_current(); + let fonts = super::font_sort(config, &mut pattern) + .expect("sort font monospace"); + + for font in fonts.into_iter().take(10) { + print!("family={:?}", font.family(0)); + for i in 0.. { + if let Some(style) = font.style(i) { + print!(", style={:?}", style); + } else { + break; + } + } + println!(""); + } + } + + #[test] + fn font_sort_with_glyph() { + let mut charset = CharSet::new(); + charset.add('💖'); + let mut pattern = Pattern::new(); + pattern.add_charset(&charset); + drop(charset); + + let config = Config::get_current(); + let fonts = super::font_sort(config, &mut pattern).expect("font_sort"); + + for font in fonts.into_iter().take(10) { + print!("family={:?}", font.family(0)); + for i in 0.. { + if let Some(style) = font.style(i) { + print!(", style={:?}", style); + } else { + break; + } + } + println!(""); + } + } +} diff --git a/font/src/ft/fc/object_set.rs b/font/src/ft/fc/object_set.rs new file mode 100644 index 0000000..42e03f6 --- /dev/null +++ b/font/src/ft/fc/object_set.rs @@ -0,0 +1,56 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use libc::c_char; + +use foreign_types::ForeignTypeRef; +use super::ffi::{FcObjectSetCreate, FcObjectSetAdd, FcObjectSet, FcObjectSetDestroy}; + +foreign_type! { + type CType = FcObjectSet; + fn drop = FcObjectSetDestroy; + pub struct ObjectSet; + pub struct ObjectSetRef; +} + +impl ObjectSet { + #[allow(dead_code)] + pub fn new() -> ObjectSet { + ObjectSet(unsafe { + FcObjectSetCreate() + }) + } +} + +impl ObjectSetRef { + fn add(&mut self, property: &[u8]) { + unsafe { + FcObjectSetAdd(self.as_ptr(), 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"); + } + + #[inline] + pub fn add_style(&mut self) { + self.add(b"style\0"); + } +} diff --git a/font/src/ft/fc/pattern.rs b/font/src/ft/fc/pattern.rs new file mode 100644 index 0000000..a7cd9ec --- /dev/null +++ b/font/src/ft/fc/pattern.rs @@ -0,0 +1,233 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use std::ptr; +use std::ffi::{CStr, CString}; +use std::path::PathBuf; +use std::str; + +use libc::{c_char, c_int}; +use foreign_types::{ForeignTypeRef}; + +use super::ffi::FcResultMatch; +use super::ffi::{FcPatternDestroy, FcPatternAddCharSet}; +use super::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString}; +use super::ffi::{FcPatternGetInteger, FcPatternAddInteger}; +use super::ffi::{FcChar8, FcPattern, FcDefaultSubstitute, FcConfigSubstitute}; + +use super::{MatchKind, ConfigRef, CharSetRef, Weight, Slant}; + +foreign_type! { + type CType = FcPattern; + fn drop = FcPatternDestroy; + pub struct Pattern; + pub struct PatternRef; +} + +macro_rules! pattern_add_string { + ($($name:ident => $object:expr),*) => { + $( + #[inline] + pub fn $name(&mut self, value: &str) -> bool { + unsafe { + self.add_string($object, value) + } + } + )* + } +} + +macro_rules! pattern_add_int { + ($($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() }) + } +} + +macro_rules! pattern_get_string { + ($($method:ident() => $property:expr),+) => { + $( + pub fn $method(&self, id: isize) -> Option { + unsafe { + self.get_string($property, id) + } + } + )+ + }; +} + +macro_rules! pattern_add_integer { + ($($method:ident() => $property:expr),+) => { + $( + pub fn $method(&self, int: isize) -> bool { + unsafe { + FcPatternAddInteger( + self.as_ptr(), + $property.as_ptr() as *mut c_char, + int as c_int, + &mut index + ) == 1 + } + } + )+ + }; +} + +macro_rules! pattern_get_integer { + ($($method:ident() => $property:expr),+) => { + $( + pub fn $method(&self, id: isize) -> Option { + let mut index = 0 as c_int; + unsafe { + let result = FcPatternGetInteger( + self.as_ptr(), + $property.as_ptr() as *mut c_char, + id as c_int, + &mut index + ); + + if result == FcResultMatch { + Some(index as isize) + } else { + None + } + } + } + )+ + }; +} + +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() +} + +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.as_ptr(), + object.as_ptr() as *mut c_char, + value as *mut FcChar8 + ) == 1 + } + + unsafe fn add_integer(&self, object: &[u8], int: isize) -> bool { + FcPatternAddInteger( + self.as_ptr(), + object.as_ptr() as *mut c_char, + int as c_int + ) == 1 + } + + unsafe fn get_string(&self, object: &[u8], index: isize) -> Option { + let mut format: *mut FcChar8 = ptr::null_mut(); + + let result = FcPatternGetString( + self.as_ptr(), + object.as_ptr() as *mut c_char, + index as c_int, + &mut format + ); + + if result == FcResultMatch { + Some(char8_to_string(format)) + } else { + None + } + } + + pattern_add_string! { + add_family => b"family\0", + add_style => b"style\0" + } + + pub fn set_slant(&mut self, slant: Slant) -> bool { + unsafe { + self.add_integer(b"slant\0", slant as isize) + } + } + + pub fn set_weight(&mut self, weight: Weight) -> bool { + unsafe { + self.add_integer(b"weight\0", weight as isize) + } + } + + + /// Add charset to the pattern + /// + /// The referenced charset is copied by fontconfig internally using + /// FcValueSave so that no references to application provided memory are + /// retained. That is, the CharSet can be safely dropped immediately + /// after being added to the pattern. + pub fn add_charset(&self, charset: &CharSetRef) -> bool { + unsafe { + FcPatternAddCharSet( + self.as_ptr(), + b"charset\0".as_ptr() as *mut c_char, + charset.as_ptr() + ) == 1 + } + } + + pub fn file(&self, index: isize) -> Option { + unsafe { + self.get_string(b"file\0", index) + }.map(From::from) + } + + pattern_get_string! { + fontformat() => b"fontformat\0", + family() => b"family\0", + style() => b"style\0" + } + + pattern_get_integer! { + index() => b"index\0" + } + + pub fn config_subsitute(&mut self, config: &ConfigRef, kind: MatchKind) { + unsafe { + FcConfigSubstitute(config.as_ptr(), self.as_ptr(), kind as u32); + } + } + + pub fn default_substitute(&mut self) { + unsafe { + FcDefaultSubstitute(self.as_ptr()); + } + } +} + diff --git a/font/src/ft/list_fonts.rs b/font/src/ft/list_fonts.rs deleted file mode 100644 index 18cc500..0000000 --- a/font/src/ft/list_fonts.rs +++ /dev/null @@ -1,586 +0,0 @@ -// Copyright 2016 Joe Wilm, The Alacritty Project Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -pub mod fc { - use std::ptr; - use std::ffi::{CStr, CString}; - use std::str; - use std::ops::Deref; - use std::path::PathBuf; - - use foreign_types::{ForeignType, ForeignTypeRef}; - - use libc::{c_char, c_int}; - use fontconfig::fontconfig as ffi; - - use self::ffi::{FcConfigGetCurrent, FcConfigGetFonts, FcSetSystem, FcSetApplication}; - use self::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString}; - use self::ffi::{FcPatternGetInteger, FcPatternAddInteger}; - use self::ffi::{FcObjectSetCreate, FcObjectSetAdd}; - use self::ffi::{FcResultMatch, FcResultNoMatch, FcFontSetList}; - use self::ffi::{FcChar8, FcConfig, FcPattern, FcFontSet, FcObjectSet}; - use self::ffi::{FcFontSetDestroy, FcPatternDestroy, FcObjectSetDestroy, FcConfigDestroy}; - use self::ffi::{FcFontMatch, FcFontList, FcFontSort, FcConfigSubstitute, FcDefaultSubstitute}; - use self::ffi::{FcMatchFont, FcMatchPattern, FcMatchScan, FC_SLANT_ITALIC, FC_SLANT_ROMAN}; - use self::ffi::{FC_SLANT_OBLIQUE}; - use self::ffi::{FC_WEIGHT_THIN, FC_WEIGHT_EXTRALIGHT, FC_WEIGHT_LIGHT}; - use self::ffi::{FC_WEIGHT_BOOK, FC_WEIGHT_REGULAR, FC_WEIGHT_MEDIUM, FC_WEIGHT_SEMIBOLD}; - use self::ffi::{FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABOLD, FC_WEIGHT_BLACK, FC_WEIGHT_EXTRABLACK}; - use self::ffi::{FcCharSet, FcCharSetDestroy, FcPatternAddCharSet, FcCharSetAddChar}; - use self::ffi::{FcCharSetCreate}; - - /// Iterator over a font set - pub struct FontSetIter<'a> { - font_set: &'a FontSetRef, - num_fonts: usize, - current: usize, - } - - foreign_type! { - type CType = FcPattern; - fn drop = FcPatternDestroy; - pub struct Pattern; - pub struct PatternRef; - } - - foreign_type! { - type CType = FcConfig; - fn drop = FcConfigDestroy; - pub struct Config; - pub struct ConfigRef; - } - - foreign_type! { - type CType = FcObjectSet; - fn drop = FcObjectSetDestroy; - pub struct ObjectSet; - pub struct ObjectSetRef; - } - - foreign_type! { - type CType = FcFontSet; - fn drop = FcFontSetDestroy; - pub struct FontSet; - pub struct FontSetRef; - } - - foreign_type! { - type CType = FcCharSet; - fn drop = FcCharSetDestroy; - pub struct CharSet; - pub struct CharSetRef; - } - - impl ObjectSet { - #[allow(dead_code)] - pub fn new() -> ObjectSet { - ObjectSet(unsafe { - FcObjectSetCreate() - }) - } - } - - /// Find the font closest matching the provided pattern. - pub fn font_match( - config: &ConfigRef, - pattern: &mut PatternRef, - ) -> Option { - pattern.config_subsitute(config, MatchKind::Pattern); - pattern.default_substitute(); - - unsafe { - // What is this result actually used for? Seems redundant with - // return type. - let mut result = FcResultNoMatch; - let ptr = FcFontMatch( - config.as_ptr(), - pattern.as_ptr(), - &mut result, - ); - - if ptr.is_null() { - None - } else { - Some(Pattern::from_ptr(ptr)) - } - } - } - - /// list fonts by closeness to the pattern - #[allow(dead_code)] - pub fn font_sort( - config: &ConfigRef, - pattern: &mut PatternRef, - ) -> Option { - pattern.config_subsitute(config, MatchKind::Pattern); - pattern.default_substitute(); - - unsafe { - // What is this result actually used for? Seems redundant with - // return type. - let mut result = FcResultNoMatch; - - let mut charsets: *mut FcCharSet = ptr::null_mut(); - - let ptr = FcFontSort( - config.as_ptr(), - pattern.as_ptr(), - 0, // false - &mut charsets, - &mut result, - ); - - if ptr.is_null() { - None - } else { - Some(FontSet::from_ptr(ptr)) - } - } - } - - /// List fonts matching pattern - #[allow(dead_code)] - pub fn font_list( - config: &ConfigRef, - pattern: &mut PatternRef, - objects: &ObjectSetRef, - ) -> Option { - pattern.config_subsitute(config, MatchKind::Pattern); - pattern.default_substitute(); - - unsafe { - let ptr = FcFontList( - config.as_ptr(), - pattern.as_ptr(), - objects.as_ptr(), - ); - - if ptr.is_null() { - None - } else { - Some(FontSet::from_ptr(ptr)) - } - } - } - - impl ObjectSetRef { - fn add(&mut self, property: &[u8]) { - unsafe { - FcObjectSetAdd(self.as_ptr(), 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"); - } - - #[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() }) - } - } - - /// Available font sets - #[derive(Debug, Copy, Clone)] - pub enum SetName { - System = FcSetSystem as isize, - Application = FcSetApplication as isize, - } - - /// When matching, how to match - #[derive(Debug, Copy, Clone)] - pub enum MatchKind { - Font = FcMatchFont as isize, - Pattern = FcMatchPattern as isize, - Scan = FcMatchScan as isize, - } - - 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 { - unsafe { - self.get_string($property, id) - } - } - )+ - }; - } - - macro_rules! pattern_get_integer { - ($($method:ident() => $property:expr),+) => { - $( - pub fn $method(&self, id: isize) -> Option { - let mut index = 0 as c_int; - unsafe { - let result = FcPatternGetInteger( - self.as_ptr(), - $property.as_ptr() as *mut c_char, - id as c_int, - &mut index - ); - - if result == FcResultMatch { - Some(index as isize) - } else { - None - } - } - } - )+ - }; - } - - #[derive(Debug, Copy, Clone)] - pub enum Slant { - Italic = FC_SLANT_ITALIC as isize, - Oblique = FC_SLANT_OBLIQUE as isize, - Roman = FC_SLANT_ROMAN as isize, - } - - #[derive(Debug, Copy, Clone)] - pub enum Weight { - Thin = FC_WEIGHT_THIN as isize, - Extralight = FC_WEIGHT_EXTRALIGHT as isize, - Light = FC_WEIGHT_LIGHT as isize, - Book = FC_WEIGHT_BOOK as isize, - Regular = FC_WEIGHT_REGULAR as isize, - Medium = FC_WEIGHT_MEDIUM as isize, - Semibold = FC_WEIGHT_SEMIBOLD as isize, - Bold = FC_WEIGHT_BOLD as isize, - Extrabold = FC_WEIGHT_EXTRABOLD as isize, - Black = FC_WEIGHT_BLACK as isize, - Extrablack = FC_WEIGHT_EXTRABLACK as isize, - } - - impl CharSet { - pub fn new() -> CharSet { - CharSet(unsafe { FcCharSetCreate() }) - } - } - - impl CharSetRef { - pub fn add(&mut self, glyph: char) -> bool { - unsafe { - FcCharSetAddChar( - self.as_ptr(), - glyph as _ - ) == 1 - } - } - } - - 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.as_ptr(), - object.as_ptr() as *mut c_char, - value as *mut FcChar8 - ) == 1 - } - - unsafe fn add_integer(&self, object: &[u8], int: isize) -> bool { - FcPatternAddInteger( - self.as_ptr(), - object.as_ptr() as *mut c_char, - int as c_int - ) == 1 - } - - unsafe fn get_string(&self, object: &[u8], index: isize) -> Option { - let mut format: *mut FcChar8 = ptr::null_mut(); - - let result = FcPatternGetString( - self.as_ptr(), - object.as_ptr() as *mut c_char, - index as c_int, - &mut format - ); - - if result == FcResultMatch { - Some(char8_to_string(format)) - } else { - None - } - } - - pattern_add_string! { - add_family => b"family\0", - add_style => b"style\0" - } - - pub fn set_slant(&mut self, slant: Slant) -> bool { - unsafe { - self.add_integer(b"slant\0", slant as isize) - } - } - - pub fn set_weight(&mut self, weight: Weight) -> bool { - unsafe { - self.add_integer(b"weight\0", weight as isize) - } - } - - - /// Add charset to the pattern - /// - /// The referenced charset is copied by fontconfig internally using - /// FcValueSave so that no references to application provided memory are - /// retained. That is, the CharSet can be safely dropped immediately - /// after being added to the pattern. - pub fn add_charset(&self, charset: &CharSetRef) -> bool { - unsafe { - FcPatternAddCharSet( - self.as_ptr(), - b"charset\0".as_ptr() as *mut c_char, - charset.as_ptr() - ) == 1 - } - } - - pub fn file(&self, index: isize) -> Option { - unsafe { - self.get_string(b"file\0", index) - }.map(From::from) - } - - pattern_get_string! { - fontformat() => b"fontformat\0", - family() => b"family\0", - style() => b"style\0" - } - - pattern_get_integer! { - index() => b"index\0" - } - - pub fn config_subsitute(&mut self, config: &ConfigRef, kind: MatchKind) { - unsafe { - FcConfigSubstitute(config.as_ptr(), self.as_ptr(), kind as u32); - } - } - - pub fn default_substitute(&mut self) { - unsafe { - FcDefaultSubstitute(self.as_ptr()); - } - } - } - - 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.as_ptr()).nfont as isize - }; - - info!("num fonts = {}", num_fonts); - - FontSetIter { - font_set: self.deref(), - 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.as_ptr()).nfont as isize - }; - - info!("num fonts = {}", num_fonts); - - 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 { - if self.current == self.num_fonts { - None - } else { - let pattern = unsafe { - let ptr = *(*self.font_set.as_ptr()).fonts.offset(self.current as isize); - PatternRef::from_ptr(ptr) - }; - - self.current += 1; - Some(pattern) - } - } - } - - impl FontSet { - pub fn list( - config: &ConfigRef, - source: &mut FontSetRef, - pattern: &PatternRef, - objects: &ObjectSetRef - ) -> FontSet { - let raw = unsafe { - FcFontSetList( - config.as_ptr(), - &mut source.as_ptr(), - 1 /* nsets */, - pattern.as_ptr(), - objects.as_ptr(), - ) - }; - FontSet(raw) - } - } - - impl Config { - /// Get the current configuration - pub fn get_current() -> &'static ConfigRef { - unsafe { - ConfigRef::from_ptr(FcConfigGetCurrent()) - } - } - } - - impl ConfigRef { - /// 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.as_ptr(), set as u32); - FontSetRef::from_ptr(ptr) - } - } - } -} - -#[cfg(test)] -mod tests { - use super::fc; - - #[test] - fn font_match() { - let mut pattern = fc::Pattern::new(); - pattern.add_family("monospace"); - pattern.add_style("regular"); - - let config = fc::Config::get_current(); - let font = fc::font_match(config, &mut pattern).expect("match font monospace"); - - print!("family={:?}", font.family(0)); - for i in 0.. { - if let Some(style) = font.style(i) { - print!(", style={:?}, ", style); - } else { - break; - } - } - info!(""); - } - - #[test] - fn font_sort() { - let mut pattern = fc::Pattern::new(); - pattern.add_family("monospace"); - pattern.set_slant(fc::Slant::Italic); - - let config = fc::Config::get_current(); - let fonts = fc::font_sort(config, &mut pattern) - .expect("sort font monospace"); - - for font in fonts.into_iter().take(10) { - print!("family={:?}", font.family(0)); - for i in 0.. { - if let Some(style) = font.style(i) { - print!(", style={:?}", style); - } else { - break; - } - } - println!(""); - } - } - - #[test] - fn font_sort_with_glyph() { - let mut charset = fc::CharSet::new(); - charset.add('💖'); - let mut pattern = fc::Pattern::new(); - pattern.add_charset(&charset); - drop(charset); - - let config = fc::Config::get_current(); - let fonts = fc::font_sort(config, &mut pattern).expect("font_sort"); - - for font in fonts.into_iter().take(10) { - print!("family={:?}", font.family(0)); - for i in 0.. { - if let Some(style) = font.style(i) { - print!(", style={:?}", style); - } else { - break; - } - } - println!(""); - } - } -} diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index c0fb5e0..89ca604 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -19,9 +19,8 @@ use std::cmp::min; use freetype::{self, Library, Face}; -mod list_fonts; +mod fc; -use self::list_fonts::fc; use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey, Weight, Slant, Style}; /// Rasterizes glyphs for a single font face.