use crate::{Charmap, CharmapIndex};
use super::super::{Collection, SourceCache};
use alloc::vec::Vec;
use super::{
super::{Attributes, Blob, FallbackKey, FamilyId, FamilyInfo, GenericFamily, Synthesis},
Inner,
};
#[derive(Clone, Default)]
pub(super) struct QueryState {
families: Vec<CachedFamily>,
fallback_families: Vec<CachedFamily>,
}
impl QueryState {
fn clear(&mut self) {
self.families.clear();
self.fallback_families.clear();
}
}
pub struct Query<'a> {
collection: &'a mut Inner,
state: &'a mut QueryState,
source_cache: &'a mut SourceCache,
attributes: Attributes,
fallbacks: Option<FallbackKey>,
}
impl<'a> Query<'a> {
pub(super) fn new(collection: &'a mut Collection, source_cache: &'a mut SourceCache) -> Self {
collection.query_state.clear();
Self {
collection: &mut collection.inner,
state: &mut collection.query_state,
source_cache,
attributes: Attributes::default(),
fallbacks: None,
}
}
pub fn set_families<'f, I>(&mut self, families: I)
where
I: IntoIterator,
I::Item: Into<QueryFamily<'f>>,
{
self.state.families.clear();
for family in families {
let family = family.into();
match family {
QueryFamily::Named(name) => {
if let Some(id) = self.collection.family_id(name) {
self.state.families.push(CachedFamily::new(id));
}
}
QueryFamily::Id(id) => {
self.state.families.push(CachedFamily::new(id));
}
QueryFamily::Generic(generic) => {
for id in self.collection.generic_families(generic) {
self.state.families.push(CachedFamily::new(id));
}
}
}
}
}
pub fn set_attributes(&mut self, attributes: Attributes) {
if self.attributes != attributes {
for family in &mut self.state.families {
family.clear_fonts();
}
for family in &mut self.state.fallback_families {
family.clear_fonts();
}
self.attributes = attributes;
}
}
pub fn set_fallbacks(&mut self, key: impl Into<FallbackKey>) {
let key = key.into();
if self.fallbacks != Some(key) {
self.state.fallback_families.clear();
self.state.fallback_families.extend(
self.collection
.fallback_families(key)
.map(CachedFamily::new),
);
self.fallbacks = Some(key);
}
}
pub fn matches_with(&mut self, mut f: impl FnMut(&QueryFont) -> QueryStatus) {
for family in self
.state
.families
.iter_mut()
.chain(self.state.fallback_families.iter_mut())
{
match &mut family.family {
Entry::Error => continue,
Entry::Ok(..) => {}
status @ Entry::Vacant => {
if let Some(info) = self.collection.family(family.id) {
*status = Entry::Ok(info);
} else {
*status = Entry::Error;
continue;
}
}
}
let Entry::Ok(family_info) = &family.family else {
continue;
};
let mut best_index = None;
if let Some(font) = load_font(
family_info,
self.attributes,
&mut family.best,
false,
self.source_cache,
) {
best_index = Some(font.family.1);
if f(font) == QueryStatus::Stop {
return;
}
}
if best_index == Some(family_info.default_font_index()) {
continue;
}
if let Some(font) = load_font(
family_info,
self.attributes,
&mut family.default,
true,
self.source_cache,
) {
if f(font) == QueryStatus::Stop {
return;
}
}
}
}
}
impl Drop for Query<'_> {
fn drop(&mut self) {
self.state.clear();
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum QueryStatus {
Continue,
Stop,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum QueryFamily<'a> {
Named(&'a str),
Id(FamilyId),
Generic(GenericFamily),
}
impl<'a> From<&'a str> for QueryFamily<'a> {
fn from(value: &'a str) -> Self {
Self::Named(value)
}
}
impl From<FamilyId> for QueryFamily<'static> {
fn from(value: FamilyId) -> Self {
Self::Id(value)
}
}
impl From<GenericFamily> for QueryFamily<'static> {
fn from(value: GenericFamily) -> Self {
Self::Generic(value)
}
}
#[derive(Clone, Debug)]
pub struct QueryFont {
pub family: (FamilyId, usize),
pub blob: Blob<u8>,
pub index: u32,
pub synthesis: Synthesis,
pub charmap_index: CharmapIndex,
}
impl QueryFont {
pub fn charmap(&self) -> Option<Charmap<'_>> {
self.charmap_index.charmap(self.blob.as_ref())
}
}
fn load_font<'a>(
family: &FamilyInfo,
attributes: Attributes,
font: &'a mut Entry<QueryFont>,
is_default: bool,
source_cache: &mut SourceCache,
) -> Option<&'a QueryFont> {
match font {
Entry::Error => None,
Entry::Ok(font) => Some(font),
status @ Entry::Vacant => {
*status = Entry::Error;
let family_index = if is_default {
family.default_font_index()
} else {
family.match_index(attributes.width, attributes.style, attributes.weight, true)?
};
let font_info = family.fonts().get(family_index)?;
let blob = font_info.load(Some(source_cache))?;
let blob_index = font_info.index();
let synthesis =
font_info.synthesis(attributes.width, attributes.style, attributes.weight);
*status = Entry::Ok(QueryFont {
family: (family.id(), family_index),
blob: blob.clone(),
index: blob_index,
synthesis,
charmap_index: font_info.charmap_index(),
});
if let Entry::Ok(font) = status {
Some(font)
} else {
None
}
}
}
}
#[derive(Clone)]
struct CachedFamily {
id: FamilyId,
family: Entry<FamilyInfo>,
best: Entry<QueryFont>,
default: Entry<QueryFont>,
}
impl CachedFamily {
fn new(id: FamilyId) -> Self {
Self {
id,
family: Entry::Vacant,
best: Entry::Vacant,
default: Entry::Vacant,
}
}
fn clear_fonts(&mut self) {
self.best = Entry::Vacant;
self.default = Entry::Vacant;
}
}
#[derive(Clone)]
enum Entry<T> {
Ok(T),
Vacant,
Error,
}