blob: 7389614f8bfe67908576384ee24865239bb0631c [file] [log] [blame]
// 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::fmt;
use std::ptr;
use foreign_types::{ForeignType, ForeignTypeRef};
use fontconfig::fontconfig as ffi;
use ffi::FcResultNoMatch;
use ffi::{FcFontList, FcFontMatch, FcFontSort};
use ffi::{FcMatchFont, FcMatchPattern, FcMatchScan};
use ffi::{FcSetApplication, FcSetSystem};
use ffi::{FC_SLANT_ITALIC, FC_SLANT_OBLIQUE, FC_SLANT_ROMAN};
use ffi::{FC_WEIGHT_BLACK, FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABLACK, FC_WEIGHT_EXTRABOLD};
use ffi::{FC_WEIGHT_BOOK, FC_WEIGHT_MEDIUM, FC_WEIGHT_REGULAR, FC_WEIGHT_SEMIBOLD};
use ffi::{FC_WEIGHT_EXTRALIGHT, FC_WEIGHT_LIGHT, FC_WEIGHT_THIN};
pub mod config;
pub use config::{Config, ConfigRef};
pub mod font_set;
pub use font_set::{FontSet, FontSetRef};
pub mod object_set;
pub use object_set::{ObjectSet, ObjectSetRef};
pub mod char_set;
pub use char_set::{CharSet, CharSetRef};
pub mod pattern;
pub use pattern::{Pattern, PatternRef};
/// Find the font closest matching the provided pattern.
///
/// The returned pattern is the result of Pattern::render_prepare.
pub fn font_match(config: &ConfigRef, pattern: &mut PatternRef) -> Option<Pattern> {
pattern.config_substitute(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
pub fn font_sort(config: &ConfigRef, pattern: &mut PatternRef) -> Option<FontSet> {
pattern.config_substitute(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(),
1, // Trim font list
&mut charsets,
&mut result,
);
if ptr.is_null() {
None
} else {
Some(FontSet::from_ptr(ptr))
}
}
}
/// List fonts matching pattern
pub fn font_list(
config: &ConfigRef,
pattern: &mut PatternRef,
objects: &ObjectSetRef,
) -> Option<FontSet> {
pattern.config_substitute(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,
}
#[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)]
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!("index={:?}; ", font.index());
print!("family={:?}; ", font.family());
print!("style={:?}; ", font.style());
print!("antialias={:?}; ", font.antialias());
print!("autohint={:?}; ", font.autohint());
print!("hinting={:?}; ", font.hinting());
print!("rgba={:?}; ", font.rgba());
print!("embeddedbitmap={:?}; ", font.embeddedbitmap());
print!("lcdfilter={:?}; ", font.lcdfilter());
print!("hintstyle={:?}", font.hintstyle());
println!();
}
#[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) {
let font = pattern.render_prepare(&config, &font);
print!("index={:?}; ", font.index());
print!("family={:?}; ", font.family());
print!("style={:?}; ", font.style());
print!("rgba={:?}", font.rgba());
print!("rgba={:?}", font.rgba());
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) {
let font = pattern.render_prepare(&config, &font);
print!("index={:?}; ", font.index());
print!("family={:?}; ", font.family());
print!("style={:?}; ", font.style());
print!("rgba={:?}", font.rgba());
println!();
}
}
}