use crate::export_work::ExportWork;
use crate::text_layout::{InkRectangleState, TextLayout};
use crate::text_layout_builder::TextLayoutBuilder;
use crate::{channel, FontError, STANDARD_DPI};
#[cfg(feature = "embed_fonts")]
use crate::embedded_fonts;
#[cfg(not(feature = "rayon"))]
use crate::export_work::CurrentThread;
#[cfg(feature = "rayon")]
use crate::export_work::Rayon;
use cosmic_text as ct;
use event_listener::Event;
use ct::fontdb::{Family, Query, ID as FontId};
use ct::{Attrs, AttrsOwned, BufferLine, FontSystem};
use piet::{Error, FontFamily, TextStorage};
use std::cell::{Cell, RefCell, RefMut};
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use std::sync::Arc;
#[derive(Clone)]
pub struct Text(Rc<Inner>);
impl fmt::Debug for Text {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct Borrowed;
impl fmt::Debug for Borrowed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("<borrowed>")
}
}
let mut ds = f.debug_struct("Text");
let font_db = self.0.font_db.try_borrow();
let _ = match &font_db {
Ok(font_db) => ds.field("font_db", font_db),
Err(_) => ds.field("font_db", &Borrowed),
};
ds.field("dpi", &self.0.dpi.get()).finish()
}
}
struct Inner {
font_db: RefCell<DelayedFontSystem>,
font_db_free: Event,
buffer: Cell<Vec<BufferLine>>,
dpi: Cell<f64>,
ink: RefCell<InkRectangleState>,
}
impl Inner {
fn borrow_font_system(&self) -> Option<FontSystemGuard<'_>> {
self.font_db
.try_borrow_mut()
.map(|font_db| FontSystemGuard {
font_db,
font_db_free: &self.font_db_free,
})
.ok()
}
}
pub(crate) struct FontSystemGuard<'a> {
font_db: RefMut<'a, DelayedFontSystem>,
font_db_free: &'a Event,
}
impl Deref for FontSystemGuard<'_> {
type Target = DelayedFontSystem;
fn deref(&self) -> &Self::Target {
&self.font_db
}
}
impl DerefMut for FontSystemGuard<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.font_db
}
}
impl Drop for FontSystemGuard<'_> {
fn drop(&mut self) {
self.font_db_free.notify(1);
}
}
#[allow(clippy::large_enum_variant)] pub(crate) enum DelayedFontSystem {
Real(FontSystemAndDefaults),
Waiting(channel::Receiver<FontSystemAndDefaults>),
}
impl fmt::Debug for DelayedFontSystem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Real(fs) => f
.debug_struct("FontSystem")
.field("db", fs.system.db())
.field("locale", &fs.system.locale())
.field("default_fonts", &fs.default_fonts)
.finish_non_exhaustive(),
Self::Waiting(_) => f.write_str("<waiting for availability>"),
}
}
}
impl DelayedFontSystem {
pub(crate) fn get(&mut self) -> Option<&mut FontSystemAndDefaults> {
loop {
match self {
Self::Real(system) => return Some(system),
Self::Waiting(channel) => {
*self = Self::Real(channel.try_recv()?);
}
}
}
}
pub(crate) async fn wait(&mut self) -> &mut FontSystemAndDefaults {
loop {
match self {
Self::Real(font_system) => return font_system,
Self::Waiting(recv) => *self = Self::Real(recv.recv().await),
}
}
}
pub(crate) fn wait_blocking(&mut self) -> &mut FontSystemAndDefaults {
loop {
match self {
Self::Real(font_system) => return font_system,
Self::Waiting(recv) => *self = Self::Real(recv.recv_blocking()),
}
}
}
}
pub(crate) struct FontSystemAndDefaults {
pub(crate) system: FontSystem,
pub(crate) default_fonts: Vec<FontId>,
}
impl FontSystemAndDefaults {
pub(crate) fn fix_attrs(&mut self, attrs: Attrs<'_>) -> AttrsOwned {
let mut owned = AttrsOwned::new(attrs);
let original = attrs;
if !self.system.get_font_matches(attrs).is_empty() {
return owned;
}
for _ in 0..2 {
for &default_font in &self.default_fonts {
if let Some(font) = self.system.db().face(default_font) {
for (name, _) in font.families.clone() {
owned.family_owned = ct::FamilyOwned::Name(name);
if !self.system.get_font_matches(owned.as_attrs()).is_empty() {
return owned;
}
}
}
}
owned.style = ct::Style::Normal;
owned.weight = ct::Weight::NORMAL;
}
warn!("no fonts match attributes: {:?}", original);
AttrsOwned::new(original)
}
}
impl Text {
pub(crate) fn borrow_font_system(&self) -> Option<FontSystemGuard<'_>> {
self.0.borrow_font_system()
}
pub(crate) fn borrow_ink(&self) -> RefMut<'_, InkRectangleState> {
self.0.ink.borrow_mut()
}
pub(crate) fn take_buffer(&self) -> Vec<BufferLine> {
self.0.buffer.replace(Vec::new())
}
pub(crate) fn set_buffer(&self, buffer: Vec<BufferLine>) {
self.0.buffer.replace(buffer);
}
pub fn new() -> Self {
#[cfg(all(feature = "rayon", not(target_arch = "wasm32")))]
{
Self::with_thread(Rayon)
}
#[cfg(not(all(feature = "rayon", not(target_arch = "wasm32"))))]
{
Self::with_thread(CurrentThread)
}
}
pub fn with_thread(thread: impl ExportWork) -> Self {
let (send, recv) = channel::channel();
thread.run(move || {
#[allow(unused_mut)]
let mut fs = FontSystem::new();
let mut defaults = vec![];
#[cfg(feature = "embed_fonts")]
{
match embedded_fonts::load_embedded_font_data(&mut fs) {
Ok(mut ids) => defaults.append(&mut ids),
Err(_err) => {
error!("failed to load embedded font data: {}", _err);
}
}
}
{
let mut add_defaults = |family: Family<'_>| {
if let Some(font) = fs.db().query(&Query {
families: &[family],
..Default::default()
}) {
defaults.insert(0, font);
} else {
warn!("failed to find default font for family {:?}", family);
}
};
add_defaults(Family::SansSerif);
add_defaults(Family::Serif);
add_defaults(Family::Monospace);
}
send.send(FontSystemAndDefaults {
system: fs,
default_fonts: defaults,
});
});
Self::with_delayed_font_system(DelayedFontSystem::Waiting(recv))
}
pub fn from_font_system(font_system: FontSystem) -> Self {
let defaults = {
let load_default_family = |family: Family<'_>| {
font_system.db().query(&Query {
families: &[family],
..Default::default()
})
};
let mut defaults = vec![];
defaults.extend(load_default_family(Family::SansSerif));
defaults.extend(load_default_family(Family::Serif));
defaults.extend(load_default_family(Family::Monospace));
defaults
};
Self::with_delayed_font_system(DelayedFontSystem::Real(FontSystemAndDefaults {
system: font_system,
default_fonts: defaults,
}))
}
fn with_delayed_font_system(font_db: DelayedFontSystem) -> Self {
Self(Rc::new(Inner {
font_db: RefCell::new(font_db),
font_db_free: Event::new(),
buffer: Cell::new(Vec::new()),
dpi: Cell::new(STANDARD_DPI),
ink: RefCell::new(InkRectangleState::new()),
}))
}
pub fn dpi(&self) -> f64 {
self.0.dpi.get()
}
pub fn set_dpi(&self, dpi: f64) -> f64 {
self.0.dpi.replace(dpi)
}
pub fn is_loaded(&self) -> bool {
self.0
.borrow_font_system()
.map_or(false, |mut font_db| font_db.get().is_some())
}
pub async fn wait_for_load(&self) {
loop {
if let Ok(mut guard) = self.0.font_db.try_borrow_mut() {
guard.wait().await;
return;
}
let listener = self.0.font_db_free.listen();
if let Ok(mut guard) = self.0.font_db.try_borrow_mut() {
guard.wait().await;
return;
}
listener.await;
}
}
pub fn wait_for_load_blocking(&self) {
loop {
if let Ok(mut guard) = self.0.font_db.try_borrow_mut() {
guard.wait_blocking();
return;
}
let listener = self.0.font_db_free.listen();
if let Ok(mut guard) = self.0.font_db.try_borrow_mut() {
guard.wait_blocking();
return;
}
listener.wait();
}
}
pub fn with_font_system_mut<R>(&self, f: impl FnOnce(&mut FontSystem) -> R) -> Option<R> {
let mut font_db = self.0.borrow_font_system()?;
font_db.get().map(|fs| f(&mut fs.system))
}
}
impl Default for Text {
fn default() -> Self {
Self::new()
}
}
impl piet::Text for Text {
type TextLayout = TextLayout;
type TextLayoutBuilder = TextLayoutBuilder;
fn font_family(&mut self, family_name: &str) -> Option<FontFamily> {
let mut db_guard = self.0.borrow_font_system()?;
let db = db_guard.get()?;
for (name, piet_name) in [
(Family::Serif, FontFamily::SERIF),
(Family::SansSerif, FontFamily::SANS_SERIF),
(Family::Monospace, FontFamily::MONOSPACE),
] {
let name = db.system.db().family_name(&name);
if name == family_name {
return Some(piet_name);
}
}
let family = Family::Name(family_name);
let name = db.system.db().family_name(&family);
let font = db
.system
.db()
.faces()
.flat_map(|face| &face.families)
.find(|(face, _)| *face == name)
.map(|(face, _)| FontFamily::new_unchecked(face.clone()));
font
}
fn load_font(&mut self, data: &[u8]) -> Result<FontFamily, Error> {
let span = warn_span!("load_font", data_len = data.len());
let _enter = span.enter();
let mut db_guard = self
.0
.borrow_font_system()
.ok_or_else(|| Error::BackendError(FontError::AlreadyBorrowed.into()))?;
let db = db_guard
.get()
.ok_or_else(|| Error::BackendError(FontError::NotLoaded.into()))?;
let id = {
let ids = db
.system
.db_mut()
.load_font_source(ct::fontdb::Source::Binary(Arc::new(data.to_vec())));
match ids.len() {
0 => {
error!("font collection contained no fonts");
return Err(Error::FontLoadingFailed);
}
1 => ids[0],
_len => {
warn!("received font collection of length {_len}, only selecting first font");
ids[0]
}
}
};
let font = db
.system
.db()
.face(id)
.ok_or_else(|| Error::FontLoadingFailed)?;
Ok(FontFamily::new_unchecked(font.families[0].0.as_str()))
}
fn new_text_layout(&mut self, text: impl TextStorage) -> Self::TextLayoutBuilder {
TextLayoutBuilder::new(self.clone(), text)
}
}