#![cfg(feature = "font_loading")]
use azul_css::{AzString, U8Vec};
use rust_fontconfig::{FcFontCache, FontSource};
pub mod loading;
#[cfg(target_os = "windows")]
const KNOWN_SYSTEM_SERIF_FONTS: &[&str] = &["Times New Roman"];
#[cfg(target_os = "linux")]
const KNOWN_SYSTEM_SERIF_FONTS: &[&str] = &[
"Times",
"Times New Roman",
"DejaVu Serif",
"Free Serif",
"Noto Serif",
"Bitstream Vera Serif",
"Roman",
"Regular",
];
#[cfg(target_os = "macos")]
const KNOWN_SYSTEM_SERIF_FONTS: &[&str] = &["Times", "New York", "Palatino"];
#[cfg(target_os = "windows")]
const KNOWN_SYSTEM_MONOSPACE_FONTS: &[&str] = &[
"Segoe UI Mono",
"Courier New",
"Cascadia Code",
"Cascadia Mono",
];
#[cfg(target_os = "linux")]
const KNOWN_SYSTEM_MONOSPACE_FONTS: &[&str] = &[
"Source Code Pro",
"Cantarell",
"DejaVu Sans Mono",
"Roboto Mono",
"Ubuntu Monospace",
"Droid Sans Mono",
];
#[cfg(target_os = "macos")]
const KNOWN_SYSTEM_MONOSPACE_FONTS: &[&str] = &[
"SF Mono",
"Menlo",
"Monaco",
"Oxygen Mono",
"Source Code Pro",
"Fira Mono",
];
#[cfg(target_os = "windows")]
const KNOWN_SYSTEM_SANS_SERIF_FONTS: &[&str] = &[
"Segoe UI", "Tahoma", "Microsoft Sans Serif",
"MS Sans Serif",
"Helv",
];
#[cfg(target_os = "linux")]
const KNOWN_SYSTEM_SANS_SERIF_FONTS: &[&str] = &[
"Ubuntu",
"Arial",
"DejaVu Sans",
"Noto Sans",
"Liberation Sans",
];
#[cfg(target_os = "macos")]
const KNOWN_SYSTEM_SANS_SERIF_FONTS: &[&str] = &[
"San Francisco", "Helvetica Neue", "Lucida Grande", ];
#[cfg(target_family = "wasm")]
const KNOWN_SYSTEM_MONOSPACE_FONTS: &[&str] = &[];
#[cfg(target_family = "wasm")]
const KNOWN_SYSTEM_SANS_SERIF_FONTS: &[&str] = &[];
#[cfg(target_family = "wasm")]
const KNOWN_SYSTEM_SERIF_FONTS: &[&str] = &[];
pub fn load_system_font(id: &str, fc_cache: &FcFontCache) -> Option<(U8Vec, i32)> {
use rust_fontconfig::{FcFontPath, FcPattern, PatternMatch};
let mut patterns = Vec::new();
match id {
"monospace" => {
#[cfg(target_os = "linux")]
{
if let Some(gsettings_pref) = linux_get_gsettings_font("monospace-font-name") {
patterns.push(FcPattern {
name: Some(gsettings_pref),
..FcPattern::default()
});
}
if let Some(fontconfig_pref) = linux_get_fc_match_font("monospace") {
patterns.push(FcPattern {
name: Some(fontconfig_pref),
..FcPattern::default()
});
}
}
for monospace_font_name in KNOWN_SYSTEM_MONOSPACE_FONTS.iter() {
patterns.push(FcPattern {
name: Some(monospace_font_name.to_string()),
..FcPattern::default()
});
}
patterns.push(FcPattern {
monospace: PatternMatch::True,
..FcPattern::default()
});
}
"fantasy" | "oblique" => {
#[cfg(target_os = "linux")]
{
if let Some(fontconfig_pref) = linux_get_fc_match_font("sans-serif") {
patterns.push(FcPattern {
name: Some(fontconfig_pref),
oblique: PatternMatch::True,
..FcPattern::default()
});
}
}
for serif_font in KNOWN_SYSTEM_SERIF_FONTS.iter() {
patterns.push(FcPattern {
name: Some(serif_font.to_string()),
oblique: PatternMatch::True,
..FcPattern::default()
});
}
patterns.push(FcPattern {
oblique: PatternMatch::True,
..FcPattern::default()
});
}
"italic" => {
#[cfg(target_os = "linux")]
{
if let Some(fontconfig_pref) = linux_get_fc_match_font("italic") {
patterns.push(FcPattern {
name: Some(fontconfig_pref),
italic: PatternMatch::True,
..FcPattern::default()
});
}
}
for serif_font in KNOWN_SYSTEM_SERIF_FONTS.iter() {
patterns.push(FcPattern {
name: Some(serif_font.to_string()),
italic: PatternMatch::True,
..FcPattern::default()
});
}
patterns.push(FcPattern {
italic: PatternMatch::True,
..FcPattern::default()
});
}
"sans-serif" => {
#[cfg(target_os = "linux")]
{
if let Some(gsettings_pref) = linux_get_gsettings_font("font-name") {
patterns.push(FcPattern {
name: Some(gsettings_pref),
..FcPattern::default()
});
}
if let Some(fontconfig_pref) = linux_get_fc_match_font("sans-serif") {
patterns.push(FcPattern {
name: Some(fontconfig_pref),
..FcPattern::default()
});
}
}
for sans_serif_font in KNOWN_SYSTEM_SANS_SERIF_FONTS.iter() {
patterns.push(FcPattern {
name: Some(sans_serif_font.to_string()),
..FcPattern::default()
});
}
}
"serif" => {
#[cfg(target_os = "linux")]
{
if let Some(fontconfig_pref) = linux_get_fc_match_font("serif") {
patterns.push(FcPattern {
name: Some(fontconfig_pref),
..FcPattern::default()
});
}
}
for serif_font in KNOWN_SYSTEM_SERIF_FONTS.iter() {
patterns.push(FcPattern {
name: Some(serif_font.to_string()),
..FcPattern::default()
});
}
}
other => {
patterns.push(FcPattern {
name: Some(other.clone().into()),
..FcPattern::default()
});
patterns.push(FcPattern {
family: Some(other.clone().into()),
..FcPattern::default()
});
}
}
patterns.push(FcPattern::default());
for pattern in patterns {
let font_source = fc_cache
.query(&pattern, &mut Vec::new())
.and_then(|s| fc_cache.get_font_by_id(&s.id));
let font_source = match font_source {
Some(s) => s,
None => continue,
};
match font_source {
FontSource::Memory(m) => {
return Some((m.bytes.clone().into(), m.font_index as i32));
}
FontSource::Disk(d) => {
use std::{fs, path::Path};
if let Ok(bytes) = fs::read(Path::new(&d.path)) {
return Some((bytes.into(), d.font_index as i32));
}
}
}
}
None
}
#[cfg(all(target_os = "linux", feature = "std"))]
fn linux_get_gsettings_font(font_name: &'static str) -> Option<String> {
std::process::Command::new("gsettings")
.arg("get")
.arg("org.gnome.desktop.interface")
.arg(font_name)
.output()
.ok()
.map(|output| output.stdout)
.and_then(|stdout_bytes| String::from_utf8(stdout_bytes).ok())
.map(|stdout_string| stdout_string.lines().collect::<String>())
.map(|s| parse_gsettings_font(&s).to_string())
}
fn parse_gsettings_font(input: &str) -> &str {
use std::char;
let input = input.trim();
let input = input.trim_matches('\'');
let input = input.trim_end_matches(char::is_numeric);
let input = input.trim();
input
}
#[cfg(all(target_os = "linux", feature = "std"))]
fn linux_get_fc_match_font(font_name: &'static str) -> Option<String> {
std::process::Command::new("fc-match")
.arg(font_name)
.output()
.ok()
.map(|output| output.stdout)
.and_then(|stdout_bytes| String::from_utf8(stdout_bytes).ok())
.map(|stdout_string| stdout_string.lines().collect::<String>())
.and_then(|s| Some(parse_fc_match_font(&s)?.to_string()))
}
fn parse_fc_match_font(input: &str) -> Option<&str> {
let input = input.trim();
let mut split_iterator = input.split(":");
split_iterator.next()?;
let fonts_str = split_iterator.next()?; let fonts_str = fonts_str.trim();
let mut font_iterator = input.split("\" \"");
let first_font = font_iterator.next()?;
let first_font = first_font.trim();
let first_font = first_font.trim_start_matches('"');
let first_font = first_font.trim_end_matches('"');
let first_font = first_font.trim();
Some(first_font)
}