blob: 19d72b47d9c04e96ad1edc9d2f6e852cfb08fca3 [file] [log] [blame]
use std::ffi::{CStr, CString};
use std::fmt;
use std::mem;
use std::path::PathBuf;
use std::ptr::{self, NonNull};
use std::str;
use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
use libc::{c_char, c_double, c_int};
use super::ffi::FcMatrix;
use super::ffi::FcResultMatch;
use super::ffi::{FcBool, FcFontRenderPrepare, FcPatternGetBool, FcPatternGetDouble};
use super::ffi::{FcChar8, FcConfigSubstitute, FcDefaultSubstitute, FcPattern, FcPatternHash};
use super::ffi::{
FcPatternAddCharSet, FcPatternDestroy, FcPatternDuplicate, FcPatternGetCharSet,
FcPatternGetMatrix,
};
use super::ffi::{FcPatternAddDouble, FcPatternAddString, FcPatternCreate, FcPatternGetString};
use super::ffi::{FcPatternAddInteger, FcPatternGetInteger, FcPatternPrint};
use super::{CharSetRef, ConfigRef, HintStyle, LcdFilter, MatchKind, Rgba, Slant, Weight, Width};
pub struct StringPropertyIter<'a> {
pattern: &'a PatternRef,
object: &'a [u8],
index: usize,
}
impl<'a> StringPropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> StringPropertyIter<'b> {
StringPropertyIter { pattern, object, index: 0 }
}
fn get_value(&self, index: usize) -> Option<&'a str> {
let mut value: *mut FcChar8 = ptr::null_mut();
let result = unsafe {
FcPatternGetString(
self.pattern.as_ptr(),
self.object.as_ptr() as *mut c_char,
index as c_int,
&mut value,
)
};
if result == FcResultMatch {
// Transmute here is to extend lifetime of the str to that of the iterator.
//
// Potential unsafety? What happens if the pattern is modified while this ptr is
// borrowed out?
Some(unsafe {
mem::transmute(CStr::from_ptr(value as *const c_char).to_str().unwrap())
})
} else {
None
}
}
}
/// Iterator over integer properties.
pub struct BooleanPropertyIter<'a> {
pattern: &'a PatternRef,
object: &'a [u8],
index: usize,
}
impl<'a> BooleanPropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> BooleanPropertyIter<'b> {
BooleanPropertyIter { pattern, object, index: 0 }
}
fn get_value(&self, index: usize) -> Option<bool> {
let mut value: FcBool = 0;
let result = unsafe {
FcPatternGetBool(
self.pattern.as_ptr(),
self.object.as_ptr() as *mut c_char,
index as c_int,
&mut value,
)
};
if result == FcResultMatch {
Some(value != 0)
} else {
None
}
}
}
/// Iterator over integer properties.
pub struct IntPropertyIter<'a> {
pattern: &'a PatternRef,
object: &'a [u8],
index: usize,
}
impl<'a> IntPropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> IntPropertyIter<'b> {
IntPropertyIter { pattern, object, index: 0 }
}
fn get_value(&self, index: usize) -> Option<isize> {
let mut value = 0 as c_int;
let result = unsafe {
FcPatternGetInteger(
self.pattern.as_ptr(),
self.object.as_ptr() as *mut c_char,
index as c_int,
&mut value,
)
};
if result == FcResultMatch {
Some(value as isize)
} else {
None
}
}
}
pub struct RgbaPropertyIter<'a> {
inner: IntPropertyIter<'a>,
}
impl<'a> RgbaPropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> RgbaPropertyIter<'b> {
RgbaPropertyIter { inner: IntPropertyIter::new(pattern, object) }
}
#[inline]
fn inner<'b>(&'b mut self) -> &'b mut IntPropertyIter<'a> {
&mut self.inner
}
fn get_value(&self, index: usize) -> Option<Rgba> {
self.inner.get_value(index).map(Rgba::from)
}
}
pub struct HintStylePropertyIter<'a> {
inner: IntPropertyIter<'a>,
}
impl<'a> HintStylePropertyIter<'a> {
fn new(pattern: &PatternRef) -> HintStylePropertyIter {
HintStylePropertyIter { inner: IntPropertyIter::new(pattern, b"hintstyle\0") }
}
#[inline]
fn inner<'b>(&'b mut self) -> &'b mut IntPropertyIter<'a> {
&mut self.inner
}
fn get_value(&self, index: usize) -> Option<HintStyle> {
self.inner.get_value(index).and_then(|hint_style| {
Some(match hint_style {
0 => HintStyle::None,
1 => HintStyle::Slight,
2 => HintStyle::Medium,
3 => HintStyle::Full,
_ => return None,
})
})
}
}
pub struct LcdFilterPropertyIter<'a> {
inner: IntPropertyIter<'a>,
}
impl<'a> LcdFilterPropertyIter<'a> {
fn new(pattern: &PatternRef) -> LcdFilterPropertyIter {
LcdFilterPropertyIter { inner: IntPropertyIter::new(pattern, b"lcdfilter\0") }
}
#[inline]
fn inner<'b>(&'b mut self) -> &'b mut IntPropertyIter<'a> {
&mut self.inner
}
fn get_value(&self, index: usize) -> Option<LcdFilter> {
self.inner.get_value(index).and_then(|hint_style| {
Some(match hint_style {
0 => LcdFilter::None,
1 => LcdFilter::Default,
2 => LcdFilter::Light,
3 => LcdFilter::Legacy,
_ => return None,
})
})
}
}
/// Iterator over integer properties.
pub struct DoublePropertyIter<'a> {
pattern: &'a PatternRef,
object: &'a [u8],
index: usize,
}
impl<'a> DoublePropertyIter<'a> {
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> DoublePropertyIter<'b> {
DoublePropertyIter { pattern, object, index: 0 }
}
fn get_value(&self, index: usize) -> Option<f64> {
let mut value = f64::from(0);
let result = unsafe {
FcPatternGetDouble(
self.pattern.as_ptr(),
self.object.as_ptr() as *mut c_char,
index as c_int,
&mut value,
)
};
if result == FcResultMatch {
Some(value as f64)
} else {
None
}
}
}
/// Implement debug for a property iterator.
macro_rules! impl_property_iter_debug {
($iter:ty => $item:ty) => {
impl<'a> fmt::Debug for $iter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[")?;
for i in 0.. {
match self.get_value(i) {
Some(val) => {
if i > 0 {
write!(f, ", {}", val)?;
} else {
write!(f, "{}", val)?;
}
},
_ => break,
}
}
write!(f, "]")
}
}
};
}
/// Implement Iterator and Debug for a property iterator.
macro_rules! impl_property_iter {
($($iter:ty => $item:ty),*) => {
$(
impl<'a> Iterator for $iter {
type Item = $item;
fn next(&mut self) -> Option<Self::Item> {
let res = self.get_value(self.index);
self.index += 1;
res
}
#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.index += n;
self.next()
}
}
impl_property_iter_debug!($iter => $item);
)*
}
}
/// Implement Iterator and Debug for a property iterator which internally relies
/// on another property iterator.
macro_rules! impl_derived_property_iter {
($($iter:ty => $item:ty),*) => {
$(
impl<'a> Iterator for $iter {
type Item = $item;
fn next(&mut self) -> Option<Self::Item> {
let index = { self.inner().index };
let res = self.get_value(index);
self.inner().index += 1;
res
}
#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.inner().index += n;
self.next()
}
}
impl_property_iter_debug!($iter => $item);
)*
}
}
// Basic Iterators.
impl_property_iter! {
StringPropertyIter<'a> => &'a str,
IntPropertyIter<'a> => isize,
DoublePropertyIter<'a> => f64,
BooleanPropertyIter<'a> => bool
}
// Derived Iterators.
impl_derived_property_iter! {
RgbaPropertyIter<'a> => Rgba,
HintStylePropertyIter<'a> => HintStyle,
LcdFilterPropertyIter<'a> => LcdFilter
}
foreign_type! {
pub unsafe type Pattern {
type CType = FcPattern;
fn drop = FcPatternDestroy;
fn clone = FcPatternDuplicate;
}
}
macro_rules! string_accessor {
($([$getter:ident, $setter:ident] => $object_name:expr),*) => {
$(
#[inline]
pub fn $setter(&mut self, value: &str) -> bool {
unsafe {
self.add_string($object_name, value)
}
}
#[inline]
pub fn $getter(&self) -> StringPropertyIter {
unsafe {
self.get_string($object_name)
}
}
)*
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct PatternHash(pub u32);
#[derive(Hash, Eq, PartialEq, Debug)]
pub struct FTFaceLocation {
pub path: PathBuf,
pub index: isize,
}
impl FTFaceLocation {
pub fn new(path: PathBuf, index: isize) -> Self {
Self { path, index }
}
}
impl Pattern {
pub fn new() -> Self {
Self::default()
}
}
impl Default for Pattern {
fn default() -> Self {
Pattern(unsafe { NonNull::new(FcPatternCreate()).unwrap() })
}
}
macro_rules! pattern_get_integer {
($($method:ident() => $property:expr),+) => {
$(
pub fn $method(&self) -> IntPropertyIter {
unsafe {
self.get_integer($property)
}
}
)+
};
}
macro_rules! boolean_getter {
($($method:ident() => $property:expr),*) => {
$(
pub fn $method(&self) -> BooleanPropertyIter {
unsafe {
self.get_boolean($property)
}
}
)*
}
}
macro_rules! double_getter {
($($method:ident() => $property:expr),*) => {
$(
pub fn $method(&self) -> DoublePropertyIter {
unsafe {
self.get_double($property)
}
}
)*
}
}
impl PatternRef {
boolean_getter! {
antialias() => b"antialias\0",
hinting() => b"hinting\0",
verticallayout() => b"verticallayout\0",
autohint() => b"autohint\0",
globaladvance() => b"globaladvance\0",
scalable() => b"scalable\0",
symbol() => b"symbol\0",
color() => b"color\0",
minspace() => b"minspace\0",
embolden() => b"embolden\0",
embeddedbitmap() => b"embeddedbitmap\0",
decorative() => b"decorative\0"
}
double_getter! {
size() => b"size\0",
aspect() => b"aspect\0",
pixelsize() => b"pixelsize\0",
pixelsizefixupfactor() => b"pixelsizefixupfactor\0",
scale() => b"scale\0",
dpi() => b"dpi\0"
}
string_accessor! {
[family, add_family] => b"family\0",
[familylang, add_familylang] => b"familylang\0",
[style, add_style] => b"style\0",
[stylelang, add_stylelang] => b"stylelang\0",
[fullname, add_fullname] => b"fullname\0",
[fullnamelang, add_fullnamelang] => b"fullnamelang\0",
[foundry, add_foundry] => b"foundry\0",
[capability, add_capability] => b"capability\0",
[fontformat, add_fontformat] => b"fontformat\0",
[fontfeatures, add_fontfeatures] => b"fontfeatures\0",
[namelang, add_namelang] => b"namelang\0",
[postscriptname, add_postscriptname] => b"postscriptname\0"
}
pattern_get_integer! {
index() => b"index\0"
}
/// Prints the pattern to stdout.
///
/// FontConfig doesn't expose a way to iterate over all members of a pattern;
/// instead, we just defer to FcPatternPrint. Otherwise, this could have been
/// a `fmt::Debug` impl.
pub fn print(&self) {
unsafe { FcPatternPrint(self.as_ptr()) }
}
/// 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 add_double(&self, object: &[u8], value: f64) -> bool {
FcPatternAddDouble(self.as_ptr(), object.as_ptr() as *mut c_char, value as c_double) == 1
}
unsafe fn get_string<'a>(&'a self, object: &'a [u8]) -> StringPropertyIter<'a> {
StringPropertyIter::new(self, object)
}
unsafe fn get_integer<'a>(&'a self, object: &'a [u8]) -> IntPropertyIter<'a> {
IntPropertyIter::new(self, object)
}
unsafe fn get_double<'a>(&'a self, object: &'a [u8]) -> DoublePropertyIter<'a> {
DoublePropertyIter::new(self, object)
}
unsafe fn get_boolean<'a>(&'a self, object: &'a [u8]) -> BooleanPropertyIter<'a> {
BooleanPropertyIter::new(self, object)
}
pub fn hintstyle(&self) -> HintStylePropertyIter {
HintStylePropertyIter::new(self)
}
pub fn lcdfilter(&self) -> LcdFilterPropertyIter {
LcdFilterPropertyIter::new(self)
}
pub fn set_slant(&mut self, slant: Slant) -> bool {
unsafe { self.add_integer(b"slant\0", slant as isize) }
}
pub fn add_pixelsize(&mut self, size: f64) -> bool {
unsafe { self.add_double(b"pixelsize\0", size) }
}
pub fn set_weight(&mut self, weight: Weight) -> bool {
unsafe { self.add_integer(b"weight\0", weight as isize) }
}
pub fn set_width(&mut self, width: Width) -> bool {
unsafe { self.add_integer(b"width\0", width.to_isize()) }
}
pub fn get_width(&self) -> Option<Width> {
unsafe { self.get_integer(b"width\0").next().map(Width::from) }
}
pub fn rgba(&self) -> RgbaPropertyIter {
RgbaPropertyIter::new(self, b"rgba\0")
}
pub fn set_rgba(&self, rgba: &Rgba) -> bool {
unsafe { self.add_integer(b"rgba\0", rgba.to_isize()) }
}
pub fn render_prepare(&self, config: &ConfigRef, request: &PatternRef) -> Pattern {
unsafe {
let ptr = FcFontRenderPrepare(config.as_ptr(), self.as_ptr(), request.as_ptr());
Pattern::from_ptr(ptr)
}
}
pub fn hash(&self) -> PatternHash {
unsafe { PatternHash(FcPatternHash(self.as_ptr())) }
}
/// 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
}
}
/// Get charset from the pattern.
pub fn get_charset(&self) -> Option<&CharSetRef> {
unsafe {
let mut charset = ptr::null_mut();
let result = FcPatternGetCharSet(
self.as_ptr(),
b"charset\0".as_ptr() as *mut c_char,
0,
&mut charset,
);
if result == FcResultMatch {
Some(&*(charset as *const CharSetRef))
} else {
None
}
}
}
/// Get matrix from the pattern.
pub fn get_matrix(&self) -> Option<FcMatrix> {
unsafe {
let mut matrix = ptr::null_mut();
let result = FcPatternGetMatrix(
self.as_ptr(),
b"matrix\0".as_ptr() as *mut c_char,
0,
&mut matrix,
);
if result == FcResultMatch {
Some(*matrix)
} else {
None
}
}
}
pub fn file(&self, index: usize) -> Option<PathBuf> {
unsafe { self.get_string(b"file\0").nth(index) }.map(From::from)
}
pub fn ft_face_location(&self, index: usize) -> Option<FTFaceLocation> {
match (self.file(index), self.index().next()) {
(Some(path), Some(index)) => Some(FTFaceLocation::new(path, index)),
_ => None,
}
}
pub fn config_substitute(&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());
}
}
}