use super::families;
use fontdb::{FaceInfo, Source};
pub use fontdb::{Stretch, Style, Weight};
use log::{debug, info, trace, warn};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::collections::hash_map::{Entry, HashMap};
use std::fmt;
use std::path::Path;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum AddMode {
Prepend,
Append,
Replace,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State {
New(bool),
Ready,
}
fn to_uppercase<'a>(c: Cow<'a, str>) -> Cow<'a, str> {
match c {
Cow::Borrowed(b) if !b.chars().any(|c| c.is_lowercase()) => Cow::Borrowed(b),
c => Cow::Owned(c.to_uppercase()),
}
}
pub struct Database {
state: State,
db: fontdb::Database,
families_upper: HashMap<String, Vec<usize>>,
aliases: HashMap<Cow<'static, str>, Vec<Cow<'static, str>>>,
}
impl Database {
pub(crate) fn new() -> Self {
let mut aliases = HashMap::new();
aliases.insert(
"SERIF".into(),
families::DEFAULT_SERIF
.iter()
.map(|s| to_uppercase((*s).into()))
.collect(),
);
aliases.insert(
"SANS-SERIF".into(),
families::DEFAULT_SANS_SERIF
.iter()
.map(|s| to_uppercase((*s).into()))
.collect(),
);
aliases.insert(
"MONOSPACE".into(),
families::DEFAULT_MONOSPACE
.iter()
.map(|s| to_uppercase((*s).into()))
.collect(),
);
aliases.insert(
"CURSIVE".into(),
families::DEFAULT_CURSIVE
.iter()
.map(|s| to_uppercase((*s).into()))
.collect(),
);
aliases.insert(
"FANTASY".into(),
families::DEFAULT_FANTASY
.iter()
.map(|s| to_uppercase((*s).into()))
.collect(),
);
Database {
state: State::New(true),
db: fontdb::Database::new(),
families_upper: HashMap::new(),
aliases,
}
}
pub fn db(&self) -> &fontdb::Database {
&self.db
}
pub fn families_upper(&self) -> impl Iterator<Item = &str> {
self.families_upper.keys().map(|s| s.as_str())
}
pub fn alias_keys(&self) -> impl Iterator<Item = &str> {
self.aliases.keys().map(|k| k.as_ref())
}
pub fn aliases_of(&self, family: &str) -> Option<impl Iterator<Item = &str>> {
self.aliases
.get(family)
.map(|result| result.iter().map(|s| s.as_ref()))
}
pub fn font_family_from_alias(&self, family: &str) -> Option<String> {
let families_upper = &self.families_upper;
let db = &self.db;
self.aliases
.get(family)
.and_then(|list| list.iter().next())
.map(|name| {
let index = families_upper.get(name.as_ref()).unwrap()[0];
db.faces()[index].family.clone()
})
}
pub fn add_aliases<I>(&mut self, family: Cow<'static, str>, aliases: I, mode: AddMode)
where
I: Iterator<Item = Cow<'static, str>>,
{
if self.state == State::Ready {
warn!("unable to add aliases after font DB init");
return;
}
let aliases = aliases.map(to_uppercase);
match self.aliases.entry(to_uppercase(family)) {
Entry::Occupied(mut entry) => {
let existing = entry.get_mut();
match mode {
AddMode::Prepend => {
existing.splice(0..0, aliases);
}
AddMode::Append => {
existing.extend(aliases);
}
AddMode::Replace => {
existing.clear();
existing.extend(aliases);
}
}
}
Entry::Vacant(entry) => {
entry.insert(aliases.collect());
}
}
}
pub fn set_load_system_fonts(&mut self, load: bool) {
if let State::New(l) = &mut self.state {
*l = load;
}
}
pub fn load_font_data(&mut self, data: Vec<u8>) {
if self.state == State::Ready {
warn!("unable to load fonts after font DB init");
return;
}
self.db.load_font_data(data);
}
pub fn load_font_file<P: AsRef<Path>>(&mut self, path: P) -> Result<(), std::io::Error> {
if self.state == State::Ready {
warn!("unable to load fonts after font DB init");
return Ok(());
}
self.db.load_font_file(path)
}
pub fn load_fonts_dir<P: AsRef<Path>>(&mut self, dir: P) {
if self.state == State::Ready {
warn!("unable to load fonts after font DB init");
return;
}
self.db.load_fonts_dir(dir);
}
pub(crate) fn init(&mut self) {
if let State::New(load) = self.state {
if load {
self.db.load_system_fonts();
}
info!("Found {} fonts", self.db.len());
let families_upper = &mut self.families_upper;
for (i, face) in self.db.faces().iter().enumerate() {
trace!("Discovered: {}", DisplayFaceInfo(face));
families_upper
.entry(face.family.to_uppercase())
.or_default()
.push(i);
}
for aliases in self.aliases.values_mut() {
aliases.retain(|name| families_upper.contains_key(name.as_ref()));
let mut i = 0;
while i < aliases.len() {
if aliases[0..i].contains(&aliases[i]) {
aliases.remove(i);
} else {
i += 1;
}
}
}
if let Some(name) = self.font_family_from_alias("SERIF") {
info!("Default serif font: {}", name);
self.db.set_serif_family(name);
}
if let Some(name) = self.font_family_from_alias("SANS-SERIF") {
info!("Default sans-serif font: {}", name);
self.db.set_sans_serif_family(name);
}
if let Some(name) = self.font_family_from_alias("MONOSPACE") {
info!("Default monospace font: {}", name);
self.db.set_monospace_family(name);
}
if let Some(name) = self.font_family_from_alias("CURSIVE") {
info!("Default cursive font: {}", name);
self.db.set_cursive_family(name);
}
if let Some(name) = self.font_family_from_alias("FANTASY") {
info!("Default fantasy font: {}", name);
self.db.set_fantasy_family(name);
}
self.state = State::Ready;
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FontSelector<'a> {
families: Vec<Cow<'a, str>>,
#[cfg_attr(feature = "serde", serde(default, with = "remote::Weight"))]
weight: Weight,
#[cfg_attr(feature = "serde", serde(default, with = "remote::Stretch"))]
stretch: Stretch,
#[cfg_attr(feature = "serde", serde(default, with = "remote::Style"))]
style: Style,
}
impl<'a> FontSelector<'a> {
#[inline]
pub fn new() -> Self {
FontSelector::default()
}
#[inline]
pub fn assign(&mut self, rhs: &Self) {
self.families.clear();
self.families.extend_from_slice(&rhs.families);
self.weight = rhs.weight;
self.stretch = rhs.stretch;
self.style = rhs.style;
}
#[inline]
pub fn set_families(&mut self, mut names: Vec<Cow<'a, str>>) {
for x in &mut names {
let mut y = Default::default();
std::mem::swap(x, &mut y);
*x = to_uppercase(y);
}
self.families = names;
}
#[inline]
pub fn set_style(&mut self, style: Style) {
self.style = style;
}
#[inline]
pub fn set_weight(&mut self, weight: Weight) {
self.weight = weight;
}
#[inline]
pub fn set_stretch(&mut self, stretch: Stretch) {
self.stretch = stretch;
}
pub(crate) fn select<'b, F>(
&'b self,
db: &'b Database,
mut add_face: F,
) -> Result<(), Box<dyn std::error::Error>>
where
F: FnMut(&'b Source, u32) -> Result<(), Box<dyn std::error::Error>>,
{
debug!("select(): {:?}", self);
let mut families: Vec<Cow<'b, str>> = self.families.clone();
let sans_serif = Cow::<'static, str>::from("SANS-SERIF");
if !families.contains(&sans_serif) {
families.push(sans_serif);
}
let mut i = 0;
while i < families.len() {
if let Some(aliases) = db.aliases.get(&families[i]) {
let mut j = i + 1;
for alias in aliases {
if !families.contains(alias) {
families.insert(j, alias.clone());
j += 1;
}
}
}
i += 1;
}
let mut candidates = Vec::new();
for family in families {
if let Some(indices) = db.families_upper.get(family.as_ref()) {
for index in indices {
let candidate = &db.db.faces()[*index];
trace!("candidate: {}", DisplayFaceInfo(candidate));
candidates.push(candidate);
}
}
if !candidates.is_empty() {
if let Some(index) = self.find_best_match(&candidates) {
let candidate = candidates[index];
add_face(&candidate.source, candidate.index)?;
}
candidates.clear();
}
}
Ok(())
}
#[inline(never)]
fn find_best_match(&self, candidates: &[&FaceInfo]) -> Option<usize> {
debug_assert!(!candidates.is_empty());
let mut matching_set: Vec<usize> = (0..candidates.len()).collect();
let matches = matching_set
.iter()
.any(|&index| candidates[index].stretch == self.stretch);
let matching_stretch = if matches {
self.stretch
} else if self.stretch <= Stretch::Normal {
let stretch = matching_set
.iter()
.filter(|&&index| candidates[index].stretch < self.stretch)
.min_by_key(|&&index| {
self.stretch.to_number() - candidates[index].stretch.to_number()
});
match stretch {
Some(&matching_index) => candidates[matching_index].stretch,
None => {
let matching_index = *matching_set.iter().min_by_key(|&&index| {
candidates[index].stretch.to_number() - self.stretch.to_number()
})?;
candidates[matching_index].stretch
}
}
} else {
let stretch = matching_set
.iter()
.filter(|&&index| candidates[index].stretch > self.stretch)
.min_by_key(|&&index| {
candidates[index].stretch.to_number() - self.stretch.to_number()
});
match stretch {
Some(&matching_index) => candidates[matching_index].stretch,
None => {
let matching_index = *matching_set.iter().min_by_key(|&&index| {
self.stretch.to_number() - candidates[index].stretch.to_number()
})?;
candidates[matching_index].stretch
}
}
};
matching_set.retain(|&index| candidates[index].stretch == matching_stretch);
let style_preference = match self.style {
Style::Italic => [Style::Italic, Style::Oblique, Style::Normal],
Style::Oblique => [Style::Oblique, Style::Italic, Style::Normal],
Style::Normal => [Style::Normal, Style::Oblique, Style::Italic],
};
let matching_style = *style_preference.iter().find(|&query_style| {
matching_set
.iter()
.any(|&index| candidates[index].style == *query_style)
})?;
matching_set.retain(|&index| candidates[index].style == matching_style);
let weight = self.weight.0;
let matches = (400..450).contains(&weight)
&& matching_set
.iter()
.any(|&index| candidates[index].weight.0 == 500);
let matching_weight = if matches {
Weight::MEDIUM
} else if (450..=500).contains(&weight)
&& matching_set
.iter()
.any(|&index| candidates[index].weight.0 == 400)
{
Weight::NORMAL
} else if weight <= 500 {
let idx = matching_set
.iter()
.filter(|&&index| candidates[index].weight.0 <= weight)
.min_by_key(|&&index| weight - candidates[index].weight.0);
match idx {
Some(&matching_index) => candidates[matching_index].weight,
None => {
let matching_index = *matching_set
.iter()
.min_by_key(|&&index| candidates[index].weight.0 - weight)?;
candidates[matching_index].weight
}
}
} else {
let idx = matching_set
.iter()
.filter(|&&index| candidates[index].weight.0 >= weight)
.min_by_key(|&&index| candidates[index].weight.0 - weight);
match idx {
Some(&matching_index) => candidates[matching_index].weight,
None => {
let matching_index = *matching_set
.iter()
.min_by_key(|&&index| weight - candidates[index].weight.0)?;
candidates[matching_index].weight
}
}
};
matching_set.retain(|&index| candidates[index].weight == matching_weight);
matching_set.into_iter().next()
}
}
struct DisplayFaceInfo<'a>(&'a FaceInfo);
impl<'a> fmt::Display for DisplayFaceInfo<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let path = match &self.0.source {
Source::Binary(_) => None,
Source::File(path) => Some(path.display()),
Source::SharedFile(path, _) => Some(path.display()),
};
write!(
f,
"family=\"{}\", source={:?},{}",
self.0.family, path, self.0.index
)
}
}
#[cfg(feature = "serde")]
mod remote {
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
#[serde(remote = "fontdb::Weight")]
pub struct Weight(pub u16);
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash, Serialize, Deserialize)]
#[serde(remote = "fontdb::Stretch")]
pub enum Stretch {
UltraCondensed,
ExtraCondensed,
Condensed,
SemiCondensed,
Normal,
SemiExpanded,
Expanded,
ExtraExpanded,
UltraExpanded,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
#[serde(remote = "fontdb::Style")]
pub enum Style {
Normal,
Italic,
Oblique,
}
}