mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-31 15:47:26 +00:00

Bigger change is cause by resvg/usvg which now takes the font database in a Arc, so we must store it in a Arc ourself to cheaply lend it to usvg
222 lines
7.5 KiB
Rust
222 lines
7.5 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
|
|
|
use std::cell::RefCell;
|
|
use std::sync::Arc;
|
|
|
|
pub use fontdb;
|
|
|
|
#[derive(derive_more::Deref)]
|
|
pub struct FontDatabase {
|
|
// This is in a Arc because usvg takes the database in a Arc
|
|
#[deref]
|
|
db: Arc<fontdb::Database>,
|
|
#[cfg(not(any(
|
|
target_family = "windows",
|
|
target_os = "macos",
|
|
target_os = "ios",
|
|
target_arch = "wasm32",
|
|
target_os = "android",
|
|
)))]
|
|
pub fontconfig_fallback_families: Vec<String>,
|
|
// Default font families to use instead of SansSerif when SLINT_DEFAULT_FONT env var is set.
|
|
pub default_font_family_ids: Vec<fontdb::ID>,
|
|
// Same as default_font_families but reduced to unique family names
|
|
default_font_family_names: Vec<String>,
|
|
}
|
|
|
|
impl FontDatabase {
|
|
pub fn query_with_family(
|
|
&self,
|
|
query: fontdb::Query<'_>,
|
|
family: Option<&'_ str>,
|
|
) -> Option<fontdb::ID> {
|
|
let mut query = query;
|
|
if let Some(specified_family) = family {
|
|
let single_family = [fontdb::Family::Name(specified_family)];
|
|
query.families = &single_family;
|
|
self.db.query(&query)
|
|
} else if self.default_font_family_ids.is_empty() {
|
|
query.families = &[fontdb::Family::SansSerif];
|
|
self.db.query(&query)
|
|
} else {
|
|
let family_storage = self
|
|
.default_font_family_names
|
|
.iter()
|
|
.map(|name| fontdb::Family::Name(name))
|
|
.collect::<Vec<_>>();
|
|
query.families = &family_storage;
|
|
self.db.query(&query)
|
|
}
|
|
}
|
|
|
|
pub fn make_mut(&mut self) -> &mut fontdb::Database {
|
|
Arc::make_mut(&mut self.db)
|
|
}
|
|
}
|
|
|
|
thread_local! {
|
|
pub static FONT_DB: RefCell<FontDatabase> = RefCell::new(init_fontdb())
|
|
}
|
|
|
|
#[cfg(not(any(
|
|
target_family = "windows",
|
|
target_os = "macos",
|
|
target_os = "ios",
|
|
target_arch = "wasm32",
|
|
target_os = "android",
|
|
)))]
|
|
mod fontconfig;
|
|
|
|
fn init_fontdb() -> FontDatabase {
|
|
let mut font_db = fontdb::Database::new();
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
let (default_font_family_ids, default_font_family_names) =
|
|
std::env::var_os("SLINT_DEFAULT_FONT")
|
|
.and_then(|maybe_font_path| {
|
|
let path = std::path::Path::new(&maybe_font_path);
|
|
match if path.extension().is_some() {
|
|
font_db.load_font_file(path)
|
|
} else {
|
|
font_db.load_fonts_dir(path);
|
|
Ok(())
|
|
} {
|
|
Ok(_) => {
|
|
let mut family_ids = Vec::new();
|
|
let mut family_names = Vec::new();
|
|
|
|
for face_info in font_db.faces() {
|
|
family_ids.push(face_info.id);
|
|
|
|
let family_name = &face_info.families[0].0;
|
|
if let Err(insert_pos) = family_names.binary_search(family_name) {
|
|
family_names.insert(insert_pos, family_name.clone());
|
|
}
|
|
}
|
|
|
|
Some((family_ids, family_names))
|
|
}
|
|
Err(err) => {
|
|
eprintln!(
|
|
"Could not load the font set via `SLINT_DEFAULT_FONT`: {}: {}",
|
|
path.display(),
|
|
err,
|
|
);
|
|
None
|
|
}
|
|
}
|
|
})
|
|
.unwrap_or_default();
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
let (default_font_family_ids, default_font_family_names) =
|
|
(Default::default(), Default::default());
|
|
|
|
#[cfg(not(any(
|
|
target_family = "windows",
|
|
target_os = "macos",
|
|
target_os = "ios",
|
|
target_arch = "wasm32",
|
|
target_os = "android",
|
|
)))]
|
|
let mut fontconfig_fallback_families = Vec::new();
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
{
|
|
let data = include_bytes!("sharedfontdb/DejaVuSans.ttf");
|
|
font_db.load_font_data(data.to_vec());
|
|
font_db.set_sans_serif_family("DejaVu Sans");
|
|
}
|
|
#[cfg(target_os = "android")]
|
|
{
|
|
font_db.load_fonts_dir("/system/fonts");
|
|
font_db.set_sans_serif_family("Roboto");
|
|
}
|
|
#[cfg(not(any(target_arch = "wasm32", target_os = "android")))]
|
|
{
|
|
font_db.load_system_fonts();
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(not(any(
|
|
target_family = "windows",
|
|
target_os = "macos",
|
|
target_os = "ios",
|
|
target_arch = "wasm32",
|
|
target_os = "android",
|
|
)))] {
|
|
match fontconfig::find_families("sans-serif") {
|
|
Ok(mut fallback_families) => {
|
|
if !fallback_families.is_empty() {
|
|
let default_sans_serif_family = fallback_families.remove(0);
|
|
font_db.set_sans_serif_family(default_sans_serif_family);
|
|
}
|
|
fontconfig_fallback_families = fallback_families;
|
|
}
|
|
Err(e) => {
|
|
eprintln!("Error opening libfontconfig.so.1: {}", e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if font_db
|
|
.query(&fontdb::Query { families: &[fontdb::Family::SansSerif], ..Default::default() })
|
|
.is_none()
|
|
{
|
|
panic!(
|
|
"Unable to determine default font. Failed to locate font for family {}",
|
|
font_db.family_name(&fontdb::Family::SansSerif)
|
|
)
|
|
}
|
|
}
|
|
|
|
FontDatabase {
|
|
db: Arc::new(font_db),
|
|
#[cfg(not(any(
|
|
target_family = "windows",
|
|
target_os = "macos",
|
|
target_os = "ios",
|
|
target_arch = "wasm32",
|
|
target_os = "android",
|
|
)))]
|
|
fontconfig_fallback_families,
|
|
default_font_family_ids,
|
|
default_font_family_names,
|
|
}
|
|
}
|
|
|
|
/// This function can be used to register a custom TrueType font with Slint,
|
|
/// for use with the `font-family` property. The provided slice must be a valid TrueType
|
|
/// font.
|
|
pub fn register_font_from_memory(data: &'static [u8]) -> Result<(), Box<dyn std::error::Error>> {
|
|
FONT_DB.with_borrow_mut(|db| {
|
|
db.make_mut().load_font_source(fontdb::Source::Binary(std::sync::Arc::new(data)))
|
|
});
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
pub fn register_font_from_path(path: &std::path::Path) -> Result<(), Box<dyn std::error::Error>> {
|
|
let requested_path = path.canonicalize().unwrap_or_else(|_| path.to_owned());
|
|
FONT_DB.with_borrow_mut(|db| {
|
|
for face_info in db.faces() {
|
|
match &face_info.source {
|
|
fontdb::Source::Binary(_) => {}
|
|
fontdb::Source::File(loaded_path) | fontdb::Source::SharedFile(loaded_path, ..) => {
|
|
if *loaded_path == requested_path {
|
|
return Ok(());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
db.make_mut().load_font_file(requested_path).map_err(|e| e.into())
|
|
})
|
|
}
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub fn register_font_from_path(_path: &std::path::Path) -> Result<(), Box<dyn std::error::Error>> {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::Other,
|
|
"Registering fonts from paths is not supported in WASM builds",
|
|
)
|
|
.into());
|
|
}
|