Organize fontconfig wrappers

Each Fc type is split into a separate file. This organization will help
as features are added to the bindings.
This commit is contained in:
Joe Wilm 2017-06-27 10:57:26 -07:00 committed by Joe Wilm
parent 044b546267
commit 65065e06d1
8 changed files with 725 additions and 588 deletions

View File

@ -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
}
}
}

47
font/src/ft/fc/config.rs Normal file
View File

@ -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)
}
}
}

110
font/src/ft/fc/font_set.rs Normal file
View File

@ -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<Self::Item> {
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)
}
}
}

236
font/src/ft/fc/mod.rs Normal file
View File

@ -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> {
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<FontSet> {
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<FontSet> {
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!("");
}
}
}

View File

@ -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");
}
}

233
font/src/ft/fc/pattern.rs Normal file
View File

@ -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<String> {
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<isize> {
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<String> {
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<PathBuf> {
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());
}
}
}

View File

@ -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> {
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<FontSet> {
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<FontSet> {
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<String> {
unsafe {
self.get_string($property, id)
}
}
)+
};
}
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.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<String> {
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<PathBuf> {
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<Self::Item> {
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!("");
}
}
}

View File

@ -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.