use super::{FontStyle, FontWeight, FontWidth};
use fontique::{
Attributes, Collection, FamilyId, GenericFamily, QueryFamily, QueryFont, QueryStatus, Script,
SourceCache,
};
use log::debug;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::{BuildHasher, Hash};
pub struct Resolver {
collection: Collection,
cache: SourceCache,
families: HashMap<FamilySelector, FamilySet>,
}
impl Resolver {
pub(crate) fn new() -> Self {
Resolver {
collection: Collection::new(Default::default()),
cache: SourceCache::new(Default::default()),
families: HashMap::new(),
}
}
pub fn font_family(&mut self, id: FamilyId) -> Option<&str> {
self.collection.family_name(id)
}
pub fn font_family_from_generic(&mut self, generic: GenericFamily) -> Option<&str> {
let id = self.collection.generic_families(generic).next()?;
self.collection.family_name(id)
}
pub fn select_families<I, F>(&mut self, families: I) -> FamilySelector
where
I: IntoIterator<Item = F>,
F: Into<FamilyName>,
{
let set = FamilySet(families.into_iter().map(|f| f.into()).collect());
let hash = self.families.hasher().hash_one(&set);
let sel = FamilySelector(hash | (1 << 63));
match self.families.entry(sel) {
Entry::Vacant(entry) => {
entry.insert(set);
}
Entry::Occupied(entry) => {
log::warn!(
"Resolver::select_families: hash collision for family selector {set:?} and {:?}",
entry.get()
);
}
}
sel
}
pub fn resolve_families(&self, selector: &FamilySelector) -> Vec<FamilyName> {
if let Some(gf) = selector.as_generic() {
vec![FamilyName::Generic(gf)]
} else if let Some(set) = self.families.get(selector) {
set.0.clone()
} else {
vec![]
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum FamilyName {
Named(String),
#[cfg_attr(feature = "serde", serde(with = "remote::GenericFamily"))]
Generic(GenericFamily),
}
impl From<GenericFamily> for FamilyName {
fn from(gf: GenericFamily) -> Self {
FamilyName::Generic(gf)
}
}
impl<'a> From<&'a FamilyName> for QueryFamily<'a> {
fn from(family: &'a FamilyName) -> Self {
match family {
FamilyName::Named(name) => QueryFamily::Named(name),
FamilyName::Generic(gf) => QueryFamily::Generic(*gf),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct FamilySet(Vec<FamilyName>);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct FamilySelector(u64);
impl FamilySelector {
pub const SERIF: FamilySelector = FamilySelector(0);
pub const SANS_SERIF: FamilySelector = FamilySelector(1);
pub const MONOSPACE: FamilySelector = FamilySelector(2);
pub const CURSIVE: FamilySelector = FamilySelector(3);
pub const SYSTEM_UI: FamilySelector = FamilySelector(5);
pub const EMOJI: FamilySelector = FamilySelector(10);
pub const MATH: FamilySelector = FamilySelector(11);
pub const FANG_SONG: FamilySelector = FamilySelector(12);
pub fn as_generic(self) -> Option<GenericFamily> {
match self.0 {
0 => Some(GenericFamily::Serif),
1 => Some(GenericFamily::SansSerif),
2 => Some(GenericFamily::Monospace),
3 => Some(GenericFamily::Cursive),
5 => Some(GenericFamily::SystemUi),
10 => Some(GenericFamily::Emoji),
11 => Some(GenericFamily::Math),
12 => Some(GenericFamily::FangSong),
_ => None,
}
}
pub fn generic_name(self) -> Option<&'static str> {
Some(match self.as_generic()? {
GenericFamily::Serif => "serif",
GenericFamily::SansSerif => "sans-serif",
GenericFamily::Monospace => "monospace",
GenericFamily::Cursive => "cursive",
GenericFamily::SystemUi => "system-ui",
GenericFamily::Emoji => "emoji",
GenericFamily::Math => "math",
GenericFamily::FangSong => "fangsong",
_ => return None,
})
}
pub fn parse_generic(name: &str) -> Option<Self> {
Some(match name.trim() {
"serif" => Self::SERIF,
"sans-serif" => Self::SANS_SERIF,
"monospace" => Self::MONOSPACE,
"cursive" => Self::CURSIVE,
"system-ui" => Self::SYSTEM_UI,
"emoji" => Self::EMOJI,
"math" => Self::MATH,
"fangsong" => Self::FANG_SONG,
_ => return None,
})
}
}
impl Default for FamilySelector {
fn default() -> Self {
FamilySelector::SYSTEM_UI
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
pub struct FontSelector {
pub family: FamilySelector,
pub weight: FontWeight,
pub width: FontWidth,
pub style: FontStyle,
}
impl FontSelector {
pub const EMOJI: Self = FontSelector {
family: FamilySelector::EMOJI,
weight: FontWeight::NORMAL,
width: FontWidth::NORMAL,
style: FontStyle::Normal,
};
#[inline]
pub fn new() -> Self {
FontSelector::default()
}
pub(crate) fn select<F>(&self, resolver: &mut Resolver, script: Script, add_face: F)
where
F: FnMut(&QueryFont) -> QueryStatus,
{
let mut query = resolver.collection.query(&mut resolver.cache);
if let Some(gf) = self.family.as_generic() {
debug!(
"select: Script::{:?}, GenericFamily::{:?}, {:?}, {:?}, {:?}",
&script, gf, &self.weight, &self.width, &self.style
);
query.set_families([gf]);
} else if let Some(set) = resolver.families.get(&self.family) {
debug!(
"select: Script::{:?}, {:?}, {:?}, {:?}, {:?}",
&script, set, &self.weight, &self.width, &self.style
);
query.set_families(set.0.iter());
}
query.set_attributes(Attributes {
width: self.width.into(),
style: self.style.into(),
weight: self.weight.into(),
});
query.set_fallbacks(script);
query.matches_with(add_face);
}
pub fn format_css(&self) -> Option<String> {
let family = self.family.generic_name()?;
let mut s = String::new();
if self.style != FontStyle::Normal {
s.push_str(&format!("{} ", self.style));
}
if self.weight != FontWeight::NORMAL {
s.push_str(&format!("{} ", self.weight));
}
if self.width != FontWidth::NORMAL {
s.push_str(&format!("{} ", self.width));
}
s.push_str(family);
Some(s)
}
pub fn parse_css(s: &str) -> Option<Self> {
let mut weight = FontWeight::NORMAL;
let mut width = FontWidth::NORMAL;
let mut style = FontStyle::Normal;
let mut last_is_oblique = false;
for part in s.split_ascii_whitespace() {
if last_is_oblique {
if part.ends_with("deg") {
style = FontStyle::parse(&format!("oblique {part}"))
.expect("failed to parse oblique angle");
}
last_is_oblique = false;
} else if let Some(v) = FontStyle::parse(part) {
style = v;
if style == FontStyle::Oblique(None) {
last_is_oblique = true;
}
} else if let Some(v) = FontWeight::parse(part) {
weight = v;
} else if let Some(v) = FontWidth::parse(part) {
width = v;
} else {
let family = FamilySelector::parse_generic(part)?;
return Some(FontSelector {
family,
weight,
width,
style,
});
}
}
None
}
}
impl From<FamilySelector> for FontSelector {
#[inline]
fn from(family: FamilySelector) -> Self {
FontSelector {
family,
..Default::default()
}
}
}
#[cfg(feature = "serde")]
mod remote {
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
#[repr(u8)]
#[serde(remote = "fontique::GenericFamily")]
pub enum GenericFamily {
Serif = 0,
SansSerif = 1,
Monospace = 2,
Cursive = 3,
Fantasy = 4,
SystemUi = 5,
UiSerif = 6,
UiSansSerif = 7,
UiMonospace = 8,
UiRounded = 9,
Emoji = 10,
Math = 11,
FangSong = 12,
}
}
#[cfg(feature = "serde")]
mod serde_impls {
use super::*;
use serde::{de, ser};
use std::fmt;
impl ser::Serialize for FamilySelector {
fn serialize<S: ser::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
if let Some(name) = self.generic_name() {
ser.serialize_str(name)
} else {
Err(ser::Error::custom(
"unable to serialize non-generic family selectors",
))
}
}
}
impl<'de> de::Deserialize<'de> for FamilySelector {
fn deserialize<D: de::Deserializer<'de>>(de: D) -> Result<FamilySelector, D::Error> {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = FamilySelector;
fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "a generic family name")
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<FamilySelector, E> {
FamilySelector::parse_generic(s)
.ok_or_else(|| de::Error::invalid_value(de::Unexpected::Str(s), &self))
}
}
de.deserialize_str(Visitor)
}
}
impl ser::Serialize for FontSelector {
fn serialize<S: ser::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
if let Some(s) = self.format_css() {
ser.serialize_str(&s)
} else {
Err(ser::Error::custom(
"unable to serialize non-generic family selectors",
))
}
}
}
impl<'de> de::Deserialize<'de> for FontSelector {
fn deserialize<D: de::Deserializer<'de>>(de: D) -> Result<FontSelector, D::Error> {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = FontSelector;
fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "a CSS-style font selector")
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<FontSelector, E> {
FontSelector::parse_css(s)
.ok_or_else(|| de::Error::invalid_value(de::Unexpected::Str(s), &self))
}
}
de.deserialize_str(Visitor)
}
}
}