///|
fn skia_typeface_from_font_files(
font : @core.FontSpec,
) -> @skia_native.Typeface? {
for family in font.families.families {
for family_name in skia_font_family_candidates(family) {
match skia_typeface_from_font_file_family(family_name, font) {
Some(typeface) => return Some(typeface)
None => ()
}
}
}
None
}
///|
/// Session-scoped memoization for typefaces loaded from font files. A
/// font-file load stats candidate directories and decodes the file (woff2)
/// from disk; before this cache every shape call for a fresh text — one
/// per keystroke on the edited block — repeated that work for the same
/// family/weight/style. Typefaces are immutable and size-independent (the
/// caller derives its sized `Font` from them), so entries never go stale,
/// and misses are cached too so absent files stop re-statting directories.
priv struct SkiaFontFileTypefaceEntry {
typeface : @skia_native.Typeface?
}
///|
let skia_font_file_typeface_cache : Map[String, SkiaFontFileTypefaceEntry] = Map([],
)
///|
fn skia_typeface_from_font_file_family(
family_name : String,
font : @core.FontSpec,
) -> @skia_native.Typeface? {
if family_name != "Open Sans" {
return None
}
let key = family_name +
"/" +
font.weight.to_string() +
"/" +
(font.style is Italic).to_string()
match skia_font_file_typeface_cache.get(key) {
Some(entry) => entry.typeface
None => {
let typeface = skia_open_sans_typeface_from_file(font)
if skia_font_file_typeface_cache.length() >= 32 {
skia_font_file_typeface_cache.clear()
}
skia_font_file_typeface_cache.set(key, { typeface, })
typeface
}
}
}
///|
fn skia_open_sans_typeface_from_file(
font : @core.FontSpec,
) -> @skia_native.Typeface? {
let file_names = skia_open_sans_font_file_names(font)
let directories = skia_open_sans_font_directories()
for directory in directories {
for file_name in file_names {
let path = skia_join_font_path(directory, file_name)
if @fs.path_exists(path) {
match
@skia_native.Typeface::from_file(@utf8.encode(path[:], bom=false)) {
Some(typeface) => return Some(typeface)
None => ()
}
}
}
}
None
}
///|
fn skia_open_sans_font_file_names(font : @core.FontSpec) -> Array[String] {
let file_names : Array[String] = []
let bold = font.weight >= 600
let italic = font.style == Italic
if bold && italic {
skia_push_unique_string(
file_names, "open-sans-v17-latin-ext_latin-700italic.woff2",
)
}
if italic {
skia_push_unique_string(
file_names, "open-sans-v17-latin-ext_latin-italic.woff2",
)
}
if bold {
skia_push_unique_string(
file_names, "open-sans-v17-latin-ext_latin-700.woff2",
)
}
skia_push_unique_string(
file_names, "open-sans-v17-latin-ext_latin-regular.woff2",
)
file_names
}
///|
fn skia_open_sans_font_directories() -> Array[String] {
let directories : Array[String] = []
match @env.get_env_var("MOUI_SKIA_OPEN_SANS_DIR") {
Some(directory) => skia_push_font_directory(directories, directory)
None => ()
}
match @env.get_env_var("MOUI_SKIA_FONT_DIRS") {
Some(value) => skia_push_font_directories_from_env(directories, value)
None => ()
}
match @env.current_dir() {
Some(directory) =>
skia_push_font_directory(
directories,
skia_join_font_path(directory, "assets/fonts/open-sans"),
)
None => ()
}
match @env.get_env_var("HOME") {
Some(home) =>
skia_push_font_directory(
directories,
skia_join_font_path(
home, "Library/Application Support/abnerworks.Typora/themes/github",
),
)
None => ()
}
skia_push_font_directory(
directories, "/Applications/Typora.app/Contents/Resources/TypeMark/style/themes/github",
)
directories
}
///|
fn skia_push_font_directories_from_env(
directories : Array[String],
value : String,
) -> Unit {
let separator = if value.contains(";") { ";" } else { ":" }
for entry in value.split(separator) {
skia_push_font_directory(directories, entry.to_owned())
}
}
///|
fn skia_push_font_directory(
directories : Array[String],
directory : String,
) -> Unit {
if directory != "" && !directories.contains(directory) {
directories.push(directory)
}
}
///|
fn skia_push_unique_string(values : Array[String], value : String) -> Unit {
if !values.contains(value) {
values.push(value)
}
}
///|
fn skia_join_font_path(directory : String, file_name : String) -> String {
if directory == "" {
file_name
} else if directory.has_suffix("/") || directory.has_suffix("\\") {
directory + file_name
} else {
directory + "/" + file_name
}
}