Extend and improve FcPattern bindings
The fontconfig `FcPattern` type is wrapped as `fc::Pattern` and `fc::Pattern` ref. All methods for accessing data on the pattern now return an `Iterator`. This API turns out to be much more ergonomic than providing an integer index. We also override the default `nth` implementation of `Iterator` on these accessors to allow random (incremental only) access. For instance, accessing `family` attributes from a pattern: let families = pattern.family(); let second = pattern.nth(1); Or printing available styles for style in pattern.style() { println!("style={}", style); }
This commit is contained in:
parent
65065e06d1
commit
6c74c51cef
|
@ -13,6 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||||
|
|
||||||
|
@ -27,19 +28,19 @@ 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_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::{FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABOLD, FC_WEIGHT_BLACK, FC_WEIGHT_EXTRABLACK};
|
||||||
|
|
||||||
mod config;
|
pub mod config;
|
||||||
pub use self::config::{Config, ConfigRef};
|
pub use self::config::{Config, ConfigRef};
|
||||||
|
|
||||||
mod font_set;
|
pub mod font_set;
|
||||||
pub use self::font_set::{FontSet, FontSetRef};
|
pub use self::font_set::{FontSet, FontSetRef};
|
||||||
|
|
||||||
mod object_set;
|
pub mod object_set;
|
||||||
pub use self::object_set::{ObjectSet, ObjectSetRef};
|
pub use self::object_set::{ObjectSet, ObjectSetRef};
|
||||||
|
|
||||||
mod char_set;
|
pub mod char_set;
|
||||||
pub use self::char_set::{CharSet, CharSetRef};
|
pub use self::char_set::{CharSet, CharSetRef};
|
||||||
|
|
||||||
mod pattern;
|
pub mod pattern;
|
||||||
pub use self::pattern::{Pattern, PatternRef};
|
pub use self::pattern::{Pattern, PatternRef};
|
||||||
|
|
||||||
/// Find the font closest matching the provided pattern.
|
/// Find the font closest matching the provided pattern.
|
||||||
|
@ -162,6 +163,143 @@ pub enum Weight {
|
||||||
Extrablack = FC_WEIGHT_EXTRABLACK as isize,
|
Extrablack = FC_WEIGHT_EXTRABLACK as isize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum Width {
|
||||||
|
Ultracondensed,
|
||||||
|
Extracondensed,
|
||||||
|
Condensed,
|
||||||
|
Semicondensed,
|
||||||
|
Normal,
|
||||||
|
Semiexpanded,
|
||||||
|
Expanded,
|
||||||
|
Extraexpanded,
|
||||||
|
Ultraexpanded,
|
||||||
|
Other(i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Width {
|
||||||
|
fn to_isize(&self) -> isize {
|
||||||
|
use self::Width::*;
|
||||||
|
match *self {
|
||||||
|
Ultracondensed => 50,
|
||||||
|
Extracondensed => 63,
|
||||||
|
Condensed => 75,
|
||||||
|
Semicondensed => 87,
|
||||||
|
Normal => 100,
|
||||||
|
Semiexpanded => 113,
|
||||||
|
Expanded => 125,
|
||||||
|
Extraexpanded => 150,
|
||||||
|
Ultraexpanded => 200,
|
||||||
|
Other(value) => value as isize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<isize> for Width {
|
||||||
|
fn from(value: isize) -> Self {
|
||||||
|
match value {
|
||||||
|
50 => Width::Ultracondensed,
|
||||||
|
63 => Width::Extracondensed,
|
||||||
|
75 => Width::Condensed,
|
||||||
|
87 => Width::Semicondensed,
|
||||||
|
100 => Width::Normal,
|
||||||
|
113 => Width::Semiexpanded,
|
||||||
|
125 => Width::Expanded,
|
||||||
|
150 => Width::Extraexpanded,
|
||||||
|
200 => Width::Ultraexpanded,
|
||||||
|
_ => Width::Other(value as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subpixel geometry
|
||||||
|
pub enum Rgba {
|
||||||
|
Unknown,
|
||||||
|
Rgb,
|
||||||
|
Bgr,
|
||||||
|
Vrgb,
|
||||||
|
Vbgr,
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rgba {
|
||||||
|
fn to_isize(&self) -> isize {
|
||||||
|
match *self {
|
||||||
|
Rgba::Unknown => 0,
|
||||||
|
Rgba::Rgb => 1,
|
||||||
|
Rgba::Bgr => 2,
|
||||||
|
Rgba::Vrgb => 3,
|
||||||
|
Rgba::Vbgr => 4,
|
||||||
|
Rgba::None => 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Rgba {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use self::Rgba::*;
|
||||||
|
f.write_str(match *self {
|
||||||
|
Unknown => "unknown",
|
||||||
|
Rgb => "rgb",
|
||||||
|
Bgr => "bgr",
|
||||||
|
Vrgb => "vrgb",
|
||||||
|
Vbgr => "vbgr",
|
||||||
|
None => "none",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<isize> for Rgba {
|
||||||
|
fn from(val: isize) -> Rgba {
|
||||||
|
match val {
|
||||||
|
1 => Rgba::Rgb,
|
||||||
|
2 => Rgba::Bgr,
|
||||||
|
3 => Rgba::Vrgb,
|
||||||
|
4 => Rgba::Vbgr,
|
||||||
|
5 => Rgba::None,
|
||||||
|
_ => Rgba::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hinting Style
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum HintStyle {
|
||||||
|
None,
|
||||||
|
Slight,
|
||||||
|
Medium,
|
||||||
|
Full
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for HintStyle {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.write_str(match *self {
|
||||||
|
HintStyle::None => "none",
|
||||||
|
HintStyle::Slight => "slight",
|
||||||
|
HintStyle::Medium => "medium",
|
||||||
|
HintStyle::Full => "full",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lcd filter, used to reduce color fringing with subpixel rendering
|
||||||
|
pub enum LcdFilter {
|
||||||
|
None,
|
||||||
|
Default,
|
||||||
|
Light,
|
||||||
|
Legacy
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for LcdFilter {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.write_str(match *self {
|
||||||
|
LcdFilter::None => "none",
|
||||||
|
LcdFilter::Default => "default",
|
||||||
|
LcdFilter::Light => "light",
|
||||||
|
LcdFilter::Legacy => "legacy",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -176,15 +314,17 @@ mod tests {
|
||||||
let config = Config::get_current();
|
let config = Config::get_current();
|
||||||
let font = super::font_match(config, &mut pattern).expect("match font monospace");
|
let font = super::font_match(config, &mut pattern).expect("match font monospace");
|
||||||
|
|
||||||
print!("family={:?}", font.family(0));
|
print!("index={:?}; ", font.index());
|
||||||
for i in 0.. {
|
print!("family={:?}; ", font.family());
|
||||||
if let Some(style) = font.style(i) {
|
print!("style={:?}; ", font.style());
|
||||||
print!(", style={:?}, ", style);
|
print!("antialias={:?}; ", font.antialias());
|
||||||
} else {
|
print!("autohint={:?}; ", font.autohint());
|
||||||
break;
|
print!("hinting={:?}; ", font.hinting());
|
||||||
}
|
print!("rgba={:?}; ", font.rgba());
|
||||||
}
|
print!("embeddedbitmap={:?}; ", font.embeddedbitmap());
|
||||||
info!("");
|
print!("lcdfilter={:?}; ", font.lcdfilter());
|
||||||
|
print!("hintstyle={:?}", font.hintstyle());
|
||||||
|
println!("");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -198,14 +338,12 @@ mod tests {
|
||||||
.expect("sort font monospace");
|
.expect("sort font monospace");
|
||||||
|
|
||||||
for font in fonts.into_iter().take(10) {
|
for font in fonts.into_iter().take(10) {
|
||||||
print!("family={:?}", font.family(0));
|
let font = font.render_prepare(&config, &pattern);
|
||||||
for i in 0.. {
|
print!("index={:?}; ", font.index());
|
||||||
if let Some(style) = font.style(i) {
|
print!("family={:?}; ", font.family());
|
||||||
print!(", style={:?}", style);
|
print!("style={:?}; ", font.style());
|
||||||
} else {
|
print!("rgba={:?}", font.rgba());
|
||||||
break;
|
print!("rgba={:?}", font.rgba());
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("");
|
println!("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,14 +360,11 @@ mod tests {
|
||||||
let fonts = super::font_sort(config, &mut pattern).expect("font_sort");
|
let fonts = super::font_sort(config, &mut pattern).expect("font_sort");
|
||||||
|
|
||||||
for font in fonts.into_iter().take(10) {
|
for font in fonts.into_iter().take(10) {
|
||||||
print!("family={:?}", font.family(0));
|
let font = font.render_prepare(&config, &pattern);
|
||||||
for i in 0.. {
|
print!("index={:?}; ", font.index());
|
||||||
if let Some(style) = font.style(i) {
|
print!("family={:?}; ", font.family());
|
||||||
print!(", style={:?}", style);
|
print!("style={:?}; ", font.style());
|
||||||
} else {
|
print!("rgba={:?}", font.rgba());
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("");
|
println!("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,20 +12,308 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::fmt;
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
use libc::{c_char, c_int};
|
use libc::{c_char, c_int};
|
||||||
use foreign_types::{ForeignTypeRef};
|
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||||
|
|
||||||
use super::ffi::FcResultMatch;
|
use super::ffi::FcResultMatch;
|
||||||
use super::ffi::{FcPatternDestroy, FcPatternAddCharSet};
|
use super::ffi::{FcPatternDestroy, FcPatternAddCharSet};
|
||||||
use super::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString};
|
use super::ffi::{FcPatternGetString, FcPatternCreate, FcPatternAddString};
|
||||||
use super::ffi::{FcPatternGetInteger, FcPatternAddInteger};
|
use super::ffi::{FcPatternGetInteger, FcPatternAddInteger};
|
||||||
use super::ffi::{FcChar8, FcPattern, FcDefaultSubstitute, FcConfigSubstitute};
|
use super::ffi::{FcChar8, FcPattern, FcDefaultSubstitute, FcConfigSubstitute};
|
||||||
|
use super::ffi::{FcFontRenderPrepare, FcPatternGetBool, FcBool};
|
||||||
|
|
||||||
use super::{MatchKind, ConfigRef, CharSetRef, Weight, Slant};
|
use super::{MatchKind, ConfigRef, CharSetRef, Weight, Slant, Width, Rgba, HintStyle, LcdFilter};
|
||||||
|
|
||||||
|
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: pattern,
|
||||||
|
object: 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 interger 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: pattern,
|
||||||
|
object: 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 interger 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: pattern,
|
||||||
|
object: 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<'b>(pattern: &'b PatternRef) -> HintStylePropertyIter<'b> {
|
||||||
|
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<'b>(pattern: &'b PatternRef) -> LcdFilterPropertyIter<'b> {
|
||||||
|
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
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
BooleanPropertyIter<'a> => bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derived Iterators
|
||||||
|
impl_derived_property_iter! {
|
||||||
|
RgbaPropertyIter<'a> => Rgba,
|
||||||
|
HintStylePropertyIter<'a> => HintStyle,
|
||||||
|
LcdFilterPropertyIter<'a> => LcdFilter
|
||||||
|
}
|
||||||
|
|
||||||
foreign_type! {
|
foreign_type! {
|
||||||
type CType = FcPattern;
|
type CType = FcPattern;
|
||||||
|
@ -34,13 +322,20 @@ foreign_type! {
|
||||||
pub struct PatternRef;
|
pub struct PatternRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! pattern_add_string {
|
macro_rules! pattern_string_accessors {
|
||||||
($($name:ident => $object:expr),*) => {
|
($([$getter:ident, $setter:ident] => $object_name:expr),*) => {
|
||||||
$(
|
$(
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn $name(&mut self, value: &str) -> bool {
|
pub fn $setter(&mut self, value: &str) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.add_string($object, value)
|
self.add_string($object_name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn $getter(&self) -> StringPropertyIter {
|
||||||
|
unsafe {
|
||||||
|
self.get_string($object_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
@ -66,18 +361,6 @@ impl Pattern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
macro_rules! pattern_add_integer {
|
||||||
($($method:ident() => $property:expr),+) => {
|
($($method:ident() => $property:expr),+) => {
|
||||||
$(
|
$(
|
||||||
|
@ -98,29 +381,25 @@ macro_rules! pattern_add_integer {
|
||||||
macro_rules! pattern_get_integer {
|
macro_rules! pattern_get_integer {
|
||||||
($($method:ident() => $property:expr),+) => {
|
($($method:ident() => $property:expr),+) => {
|
||||||
$(
|
$(
|
||||||
pub fn $method(&self, id: isize) -> Option<isize> {
|
pub fn $method(&self) -> IntPropertyIter {
|
||||||
let mut index = 0 as c_int;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let result = FcPatternGetInteger(
|
self.get_integer($property)
|
||||||
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 {
|
macro_rules! boolean_getter {
|
||||||
str::from_utf8(CStr::from_ptr(fc_str as *const c_char).to_bytes()).unwrap().to_owned()
|
($($method:ident() => $property:expr),*) => {
|
||||||
|
$(
|
||||||
|
pub fn $method(&self) -> BooleanPropertyIter {
|
||||||
|
unsafe {
|
||||||
|
self.get_boolean($property)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PatternRef {
|
impl PatternRef {
|
||||||
|
@ -151,26 +430,54 @@ impl PatternRef {
|
||||||
) == 1
|
) == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_string(&self, object: &[u8], index: isize) -> Option<String> {
|
unsafe fn get_string<'a>(&'a self, object: &'a [u8]) -> StringPropertyIter<'a> {
|
||||||
let mut format: *mut FcChar8 = ptr::null_mut();
|
StringPropertyIter::new(self, object)
|
||||||
|
|
||||||
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! {
|
unsafe fn get_integer<'a>(&'a self, object: &'a [u8]) -> IntPropertyIter<'a> {
|
||||||
add_family => b"family\0",
|
IntPropertyIter::new(self, object)
|
||||||
add_style => b"style\0"
|
}
|
||||||
|
|
||||||
|
unsafe fn get_boolean<'a>(&'a self, object: &'a [u8]) -> BooleanPropertyIter<'a> {
|
||||||
|
BooleanPropertyIter::new(self, object)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hintstyle<'a>(&'a self) -> HintStylePropertyIter<'a> {
|
||||||
|
HintStylePropertyIter::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lcdfilter<'a>(&'a self) -> LcdFilterPropertyIter<'a> {
|
||||||
|
LcdFilterPropertyIter::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern_string_accessors! {
|
||||||
|
[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"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_slant(&mut self, slant: Slant) -> bool {
|
pub fn set_slant(&mut self, slant: Slant) -> bool {
|
||||||
|
@ -185,6 +492,36 @@ impl PatternRef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
.nth(0)
|
||||||
|
.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(), request.as_ptr(), self.as_ptr());
|
||||||
|
Pattern::from_ptr(ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Add charset to the pattern
|
/// Add charset to the pattern
|
||||||
///
|
///
|
||||||
|
@ -202,18 +539,12 @@ impl PatternRef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file(&self, index: isize) -> Option<PathBuf> {
|
pub fn file(&self, index: usize) -> Option<PathBuf> {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.get_string(b"file\0", index)
|
self.get_string(b"file\0").nth(index)
|
||||||
}.map(From::from)
|
}.map(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern_get_string! {
|
|
||||||
fontformat() => b"fontformat\0",
|
|
||||||
family() => b"family\0",
|
|
||||||
style() => b"style\0"
|
|
||||||
}
|
|
||||||
|
|
||||||
pattern_get_integer! {
|
pattern_get_integer! {
|
||||||
index() => b"index\0"
|
index() => b"index\0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ use std::cmp::min;
|
||||||
use freetype::{self, Library, Face};
|
use freetype::{self, Library, Face};
|
||||||
|
|
||||||
|
|
||||||
mod fc;
|
pub mod fc;
|
||||||
|
|
||||||
use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey, Weight, Slant, Style};
|
use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey, Weight, Slant, Style};
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ impl FreeTypeRasterizer {
|
||||||
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
|
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
|
||||||
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
|
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
|
||||||
|
|
||||||
if let (Some(path), Some(index)) = (font.file(0), font.index(0)) {
|
if let (Some(path), Some(index)) = (font.file(0), font.index().nth(0)) {
|
||||||
return Ok(self.library.new_face(path, index)?);
|
return Ok(self.library.new_face(path, index)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ impl FreeTypeRasterizer {
|
||||||
|
|
||||||
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
|
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
|
||||||
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
|
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
|
||||||
if let (Some(path), Some(index)) = (font.file(0), font.index(0)) {
|
if let (Some(path), Some(index)) = (font.file(0), font.index().nth(0)) {
|
||||||
println!("got font path={:?}", path);
|
println!("got font path={:?}", path);
|
||||||
Ok(self.library.new_face(path, index)?)
|
Ok(self.library.new_face(path, index)?)
|
||||||
}
|
}
|
||||||
|
@ -293,7 +293,7 @@ impl FreeTypeRasterizer {
|
||||||
let config = fc::Config::get_current();
|
let config = fc::Config::get_current();
|
||||||
match fc::font_match(config, &mut pattern) {
|
match fc::font_match(config, &mut pattern) {
|
||||||
Some(font) => {
|
Some(font) => {
|
||||||
if let (Some(path), Some(index)) = (font.file(0), font.index(0)) {
|
if let (Some(path), Some(index)) = (font.file(0), font.index().nth(0)) {
|
||||||
match self.keys.get(&path) {
|
match self.keys.get(&path) {
|
||||||
// We've previously loaded this font, so don't
|
// We've previously loaded this font, so don't
|
||||||
// load it again.
|
// load it again.
|
||||||
|
|
|
@ -48,7 +48,7 @@ use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||||
|
|
||||||
// If target isn't macos, reexport everything from ft
|
// If target isn't macos, reexport everything from ft
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
mod ft;
|
pub mod ft;
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
pub use ft::{FreeTypeRasterizer as Rasterizer, Error};
|
pub use ft::{FreeTypeRasterizer as Rasterizer, Error};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue