Support bitmap fonts
To allow FontConfig to work with Bitmap font, we shall pass the size we are interested in, and account for the size returned in the font matching process. This is, because we cannot scale those fonts. FontConfig will return the closest match, and we take its returned pixel size back when we are rendering the glyphs. There's an oddity when call set_char_size in TrueType - we need to behave as if the DPI is 72. It is due to the following macro: #define FT_REQUEST_HEIGHT( req ) \ ( (req)->vertResolution \ ? ( (req)->height * (FT_Pos)(req)->vertResolution + 36 ) / 72 \ : (req)->height ) Further work can allow for integer scaling of the largest bitmap font variant. Tested with Terminus PCF-type font under Linux. This addresses issue #582 .
This commit is contained in:
parent
619e877819
commit
b03ec0df37
|
@ -18,15 +18,15 @@ use std::path::PathBuf;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use libc::{c_char, c_int};
|
use libc::{c_char, c_int, c_double};
|
||||||
use foreign_types::{ForeignType, 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, FcPatternAddDouble};
|
||||||
use super::ffi::{FcPatternGetInteger, FcPatternAddInteger, FcPatternPrint};
|
use super::ffi::{FcPatternGetInteger, FcPatternAddInteger, FcPatternPrint};
|
||||||
use super::ffi::{FcChar8, FcPattern, FcDefaultSubstitute, FcConfigSubstitute};
|
use super::ffi::{FcChar8, FcPattern, FcDefaultSubstitute, FcConfigSubstitute};
|
||||||
use super::ffi::{FcFontRenderPrepare, FcPatternGetBool, FcBool};
|
use super::ffi::{FcFontRenderPrepare, FcPatternGetBool, FcBool, FcPatternGetDouble};
|
||||||
|
|
||||||
use super::{MatchKind, ConfigRef, CharSetRef, Weight, Slant, Width, Rgba, HintStyle, LcdFilter};
|
use super::{MatchKind, ConfigRef, CharSetRef, Weight, Slant, Width, Rgba, HintStyle, LcdFilter};
|
||||||
|
|
||||||
|
@ -115,7 +115,6 @@ pub struct IntPropertyIter<'a> {
|
||||||
index: usize
|
index: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a> IntPropertyIter<'a> {
|
impl<'a> IntPropertyIter<'a> {
|
||||||
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> IntPropertyIter<'b> {
|
fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> IntPropertyIter<'b> {
|
||||||
IntPropertyIter {
|
IntPropertyIter {
|
||||||
|
@ -227,6 +226,42 @@ impl<'a> LcdFilterPropertyIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterator over interger 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: pattern,
|
||||||
|
object: object,
|
||||||
|
index: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_value(&self, index: usize) -> Option<f64> {
|
||||||
|
let mut value = 0 as c_double;
|
||||||
|
|
||||||
|
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
|
/// Implement debug for a property iterator
|
||||||
macro_rules! impl_property_iter_debug {
|
macro_rules! impl_property_iter_debug {
|
||||||
($iter:ty => $item:ty) => {
|
($iter:ty => $item:ty) => {
|
||||||
|
@ -305,6 +340,7 @@ macro_rules! impl_derived_property_iter {
|
||||||
impl_property_iter! {
|
impl_property_iter! {
|
||||||
StringPropertyIter<'a> => &'a str,
|
StringPropertyIter<'a> => &'a str,
|
||||||
IntPropertyIter<'a> => isize,
|
IntPropertyIter<'a> => isize,
|
||||||
|
DoublePropertyIter<'a> => f64,
|
||||||
BooleanPropertyIter<'a> => bool
|
BooleanPropertyIter<'a> => bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,7 +358,7 @@ foreign_type! {
|
||||||
pub struct PatternRef;
|
pub struct PatternRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! pattern_string_accessors {
|
macro_rules! string_accessor {
|
||||||
($([$getter:ident, $setter:ident] => $object_name:expr),*) => {
|
($([$getter:ident, $setter:ident] => $object_name:expr),*) => {
|
||||||
$(
|
$(
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -372,6 +408,18 @@ macro_rules! boolean_getter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! double_getter {
|
||||||
|
($($method:ident() => $property:expr),*) => {
|
||||||
|
$(
|
||||||
|
pub fn $method(&self) -> DoublePropertyIter {
|
||||||
|
unsafe {
|
||||||
|
self.get_double($property)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PatternRef {
|
impl PatternRef {
|
||||||
// Prints the pattern to stdout
|
// Prints the pattern to stdout
|
||||||
//
|
//
|
||||||
|
@ -411,6 +459,14 @@ impl PatternRef {
|
||||||
) == 1
|
) == 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> {
|
unsafe fn get_string<'a>(&'a self, object: &'a [u8]) -> StringPropertyIter<'a> {
|
||||||
StringPropertyIter::new(self, object)
|
StringPropertyIter::new(self, object)
|
||||||
}
|
}
|
||||||
|
@ -419,6 +475,10 @@ impl PatternRef {
|
||||||
IntPropertyIter::new(self, object)
|
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> {
|
unsafe fn get_boolean<'a>(&'a self, object: &'a [u8]) -> BooleanPropertyIter<'a> {
|
||||||
BooleanPropertyIter::new(self, object)
|
BooleanPropertyIter::new(self, object)
|
||||||
}
|
}
|
||||||
|
@ -446,7 +506,15 @@ impl PatternRef {
|
||||||
decorative() => b"decorative\0"
|
decorative() => b"decorative\0"
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern_string_accessors! {
|
double_getter! {
|
||||||
|
size() => b"size\0",
|
||||||
|
aspect() => b"aspect\0",
|
||||||
|
pixelsize() => b"pixelsize\0",
|
||||||
|
scale() => b"scale\0",
|
||||||
|
dpi() => b"dpi\0"
|
||||||
|
}
|
||||||
|
|
||||||
|
string_accessor! {
|
||||||
[family, add_family] => b"family\0",
|
[family, add_family] => b"family\0",
|
||||||
[familylang, add_familylang] => b"familylang\0",
|
[familylang, add_familylang] => b"familylang\0",
|
||||||
[style, add_style] => b"style\0",
|
[style, add_style] => b"style\0",
|
||||||
|
@ -467,6 +535,12 @@ impl PatternRef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
pub fn set_weight(&mut self, weight: Weight) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.add_integer(b"weight\0", weight as isize)
|
self.add_integer(b"weight\0", weight as isize)
|
||||||
|
|
|
@ -26,12 +26,17 @@ 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};
|
||||||
|
|
||||||
|
struct FixedSize {
|
||||||
|
pixelsize: f64,
|
||||||
|
}
|
||||||
|
|
||||||
struct Face {
|
struct Face {
|
||||||
ft_face: freetype::Face<'static>,
|
ft_face: freetype::Face<'static>,
|
||||||
key: FontKey,
|
key: FontKey,
|
||||||
load_flags: freetype::face::LoadFlag,
|
load_flags: freetype::face::LoadFlag,
|
||||||
render_mode: freetype::RenderMode,
|
render_mode: freetype::RenderMode,
|
||||||
lcd_filter: c_uint,
|
lcd_filter: c_uint,
|
||||||
|
non_scalable: Option<FixedSize>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Face {
|
impl fmt::Debug for Face {
|
||||||
|
@ -103,8 +108,8 @@ impl ::Rasterize for FreeTypeRasterizer {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> {
|
fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> {
|
||||||
let face = self.get_face(desc)?;
|
let face = self.get_face(desc, size)?;
|
||||||
let key = face.key;
|
let key = face.key;
|
||||||
self.faces.insert(key, face);
|
self.faces.insert(key, face);
|
||||||
Ok(key)
|
Ok(key)
|
||||||
|
@ -145,15 +150,19 @@ impl IntoFontconfigType for Weight {
|
||||||
|
|
||||||
impl FreeTypeRasterizer {
|
impl FreeTypeRasterizer {
|
||||||
/// Load a font face accoring to `FontDesc`
|
/// Load a font face accoring to `FontDesc`
|
||||||
fn get_face(&mut self, desc: &FontDesc) -> Result<Face, Error> {
|
fn get_face(&mut self, desc: &FontDesc, size: Size) -> Result<Face, Error> {
|
||||||
|
// Adjust for DPI
|
||||||
|
let scale = self.dpi_x as f32 / 72.;
|
||||||
|
let size = Size::new(size.as_f32_pts() * scale);
|
||||||
|
|
||||||
match desc.style {
|
match desc.style {
|
||||||
Style::Description { slant, weight } => {
|
Style::Description { slant, weight } => {
|
||||||
// Match nearest font
|
// Match nearest font
|
||||||
self.get_matching_face(&desc, slant, weight)
|
self.get_matching_face(&desc, slant, weight, size)
|
||||||
}
|
}
|
||||||
Style::Specific(ref style) => {
|
Style::Specific(ref style) => {
|
||||||
// If a name was specified, try and load specifically that font.
|
// If a name was specified, try and load specifically that font.
|
||||||
self.get_specific_face(&desc, &style)
|
self.get_specific_face(&desc, &style, size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,12 +171,14 @@ impl FreeTypeRasterizer {
|
||||||
&mut self,
|
&mut self,
|
||||||
desc: &FontDesc,
|
desc: &FontDesc,
|
||||||
slant: Slant,
|
slant: Slant,
|
||||||
weight: Weight
|
weight: Weight,
|
||||||
|
size: Size,
|
||||||
) -> Result<Face, Error> {
|
) -> Result<Face, Error> {
|
||||||
let mut pattern = fc::Pattern::new();
|
let mut pattern = fc::Pattern::new();
|
||||||
pattern.add_family(&desc.name);
|
pattern.add_family(&desc.name);
|
||||||
pattern.set_weight(weight.into_fontconfig_type());
|
pattern.set_weight(weight.into_fontconfig_type());
|
||||||
pattern.set_slant(slant.into_fontconfig_type());
|
pattern.set_slant(slant.into_fontconfig_type());
|
||||||
|
pattern.add_pixelsize(size.as_f32_pts() as _);
|
||||||
|
|
||||||
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()))?;
|
||||||
|
@ -183,11 +194,13 @@ impl FreeTypeRasterizer {
|
||||||
fn get_specific_face(
|
fn get_specific_face(
|
||||||
&mut self,
|
&mut self,
|
||||||
desc: &FontDesc,
|
desc: &FontDesc,
|
||||||
style: &str
|
style: &str,
|
||||||
|
size: Size,
|
||||||
) -> Result<Face, Error> {
|
) -> Result<Face, Error> {
|
||||||
let mut pattern = fc::Pattern::new();
|
let mut pattern = fc::Pattern::new();
|
||||||
pattern.add_family(&desc.name);
|
pattern.add_family(&desc.name);
|
||||||
pattern.add_style(style);
|
pattern.add_style(style);
|
||||||
|
pattern.add_pixelsize(size.as_f32_pts() as _);
|
||||||
|
|
||||||
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()))?;
|
||||||
|
@ -203,16 +216,29 @@ impl FreeTypeRasterizer {
|
||||||
if let (Some(path), Some(index)) = (pattern.file(0), pattern.index().nth(0)) {
|
if let (Some(path), Some(index)) = (pattern.file(0), pattern.index().nth(0)) {
|
||||||
trace!("got font path={:?}", path);
|
trace!("got font path={:?}", path);
|
||||||
let ft_face = self.library.new_face(path, index)?;
|
let ft_face = self.library.new_face(path, index)?;
|
||||||
|
|
||||||
|
// Get available pixel sizes if font isn't scalable.
|
||||||
|
let non_scalable = if !pattern.scalable().next().unwrap_or(true) {
|
||||||
|
let mut pixelsize = pattern.pixelsize();
|
||||||
|
debug!("pixelsizes: {:?}", pixelsize);
|
||||||
|
|
||||||
|
Some(FixedSize {
|
||||||
|
pixelsize: pixelsize.next().expect("has 1+ pixelsize"),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let face = Face {
|
let face = Face {
|
||||||
ft_face: ft_face,
|
ft_face: ft_face,
|
||||||
key: FontKey::next(),
|
key: FontKey::next(),
|
||||||
load_flags: Self::ft_load_flags(pattern),
|
load_flags: Self::ft_load_flags(pattern),
|
||||||
render_mode: Self::ft_render_mode(pattern),
|
render_mode: Self::ft_render_mode(pattern),
|
||||||
lcd_filter: Self::ft_lcd_filter(pattern),
|
lcd_filter: Self::ft_lcd_filter(pattern),
|
||||||
|
non_scalable: non_scalable,
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Loaded Face {:?}", face);
|
debug!("Loaded Face {:?}", face);
|
||||||
|
|
||||||
Ok(Some(face))
|
Ok(Some(face))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -220,14 +246,11 @@ impl FreeTypeRasterizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn face_for_glyph(&mut self, glyph_key: &GlyphKey, have_recursed: bool) -> Result<FontKey, Error> {
|
fn face_for_glyph(&mut self, glyph_key: &GlyphKey, have_recursed: bool) -> Result<FontKey, Error> {
|
||||||
let size = glyph_key.size.as_f32_pts() * self.dpr;
|
|
||||||
let c = glyph_key.c;
|
let c = glyph_key.c;
|
||||||
|
|
||||||
let use_initial_face = if self.faces.contains_key(&glyph_key.font_key) {
|
let use_initial_face = if self.faces.contains_key(&glyph_key.font_key) {
|
||||||
// Get face and unwrap since we just checked for presence.
|
// Get face and unwrap since we just checked for presence.
|
||||||
let face = self.faces.get(&glyph_key.font_key).unwrap();
|
let face = self.faces.get(&glyph_key.font_key).unwrap();
|
||||||
|
|
||||||
face.ft_face.set_char_size(to_freetype_26_6(size), 0, self.dpi_x, self.dpi_y)?;
|
|
||||||
let index = face.ft_face.get_char_index(c as usize);
|
let index = face.ft_face.get_char_index(c as usize);
|
||||||
|
|
||||||
if index != 0 || have_recursed {
|
if index != 0 || have_recursed {
|
||||||
|
@ -253,6 +276,12 @@ impl FreeTypeRasterizer {
|
||||||
let face = self.faces.get(&font_key).unwrap();
|
let face = self.faces.get(&font_key).unwrap();
|
||||||
let index = face.ft_face.get_char_index(glyph_key.c as usize);
|
let index = face.ft_face.get_char_index(glyph_key.c as usize);
|
||||||
|
|
||||||
|
let size = face.non_scalable.as_ref()
|
||||||
|
.map(|v| v.pixelsize as f32)
|
||||||
|
.unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.dpi_x as f32 / 72.);
|
||||||
|
|
||||||
|
face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let ft_lib = self.library.raw();
|
let ft_lib = self.library.raw();
|
||||||
freetype::ffi::FT_Library_SetLcdFilter(ft_lib, face.lcd_filter);
|
freetype::ffi::FT_Library_SetLcdFilter(ft_lib, face.lcd_filter);
|
||||||
|
|
Loading…
Reference in New Issue