mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-22 03:32:39 -05:00
commit
7c99ba6eeb
3 files changed
+61
-5
No files matched your search
@@ -91,3 +91,7 @@ sync_github_name = false
|
||||
[people.krinry]
|
||||
display_name = "krinry"
|
||||
sync_github_name = true
|
||||
|
||||
[people.Silvertree2010]
|
||||
display_name = "Netter"
|
||||
sync_github_name = false
|
||||
@@ -2,7 +2,7 @@
|
||||
//! that keeps files in memory (the web editor's browser storage) answers for its own files.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::{Arc, Mutex, PoisonError};
|
||||
|
||||
use effectcraft_engine::{Services, Session};
|
||||
|
||||
@@ -13,14 +13,19 @@ struct Mem(Mutex<HashMap<String, Vec<u8>>>);
|
||||
|
||||
impl Services for Mem {
|
||||
fn read_file(&self, path: &str) -> std::io::Result<Vec<u8>> {
|
||||
self.0.lock().unwrap().get(path).cloned().ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, path.to_string()))
|
||||
self.0
|
||||
.lock()
|
||||
.unwrap_or_else(PoisonError::into_inner)
|
||||
.get(path)
|
||||
.cloned()
|
||||
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, path.to_string()))
|
||||
}
|
||||
fn write_file(&self, path: &str, data: &[u8]) -> std::io::Result<()> {
|
||||
self.0.lock().unwrap().insert(path.into(), data.into());
|
||||
self.0.lock().unwrap_or_else(PoisonError::into_inner).insert(path.into(), data.into());
|
||||
Ok(())
|
||||
}
|
||||
fn exists(&self, path: &str) -> bool {
|
||||
self.0.lock().unwrap().contains_key(path)
|
||||
self.0.lock().unwrap_or_else(PoisonError::into_inner).contains_key(path)
|
||||
}
|
||||
fn is_dir(&self, path: &str) -> bool {
|
||||
path == "/browser/footage"
|
||||
|
||||
@@ -514,12 +514,37 @@ pub fn resolve(family: &str, style: &str) -> Resolved {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if let Some(r) = resolve_alias(family, style) {
|
||||
return r;
|
||||
}
|
||||
// Inter is bundled; face 0 (the first bundled face) is the last resort.
|
||||
let mut r = resolve_in(DEFAULT_FAMILY, style).unwrap_or(Resolved { face: 0, synth_bold: false, synth_italic: false, missing: true, instance: None });
|
||||
r.missing = !family.is_empty() && !family.eq_ignore_ascii_case(DEFAULT_FAMILY);
|
||||
r
|
||||
}
|
||||
|
||||
/// Metric-compatible substitutes for common proprietary families, in order of preference (the
|
||||
/// same pairs fontconfig's `30-metric-aliases.conf` uses). Text keeps its widths and line breaks.
|
||||
const METRIC_ALIASES: &[(&str, &[&str])] = &[
|
||||
("Calibri", &["Carlito"]),
|
||||
("Cambria", &["Caladea"]),
|
||||
("Arial", &["Liberation Sans", "Arimo"]),
|
||||
("Helvetica", &["Liberation Sans", "Arimo"]),
|
||||
("Arial Narrow", &["Liberation Sans Narrow"]),
|
||||
("Times New Roman", &["Liberation Serif", "Tinos"]),
|
||||
("Times", &["Liberation Serif", "Tinos"]),
|
||||
("Courier New", &["Liberation Mono", "Cousine"]),
|
||||
("Courier", &["Liberation Mono", "Cousine"]),
|
||||
];
|
||||
|
||||
/// An installed metric-compatible substitute for a missing family. The result stays `missing`,
|
||||
/// so Find Missing Fonts still lists the requested name.
|
||||
fn resolve_alias(family: &str, style: &str) -> Option<Resolved> {
|
||||
let family = family.trim();
|
||||
let (_, subs) = METRIC_ALIASES.iter().find(|(name, _)| name.eq_ignore_ascii_case(family))?;
|
||||
subs.iter().find_map(|s| resolve_in(s, style)).map(|r| Resolved { missing: true, ..r })
|
||||
}
|
||||
|
||||
fn resolve_in(family: &str, style: &str) -> Option<Resolved> {
|
||||
let faces = all_faces();
|
||||
let fam: Vec<&Arc<Face>> = faces.iter().filter(|f| f.info.family.eq_ignore_ascii_case(family)).collect();
|
||||
@@ -1150,6 +1175,11 @@ mod tests {
|
||||
/// A minimal TrueType font built in code (no font file in the repo): U+0020 and U+E000 map to
|
||||
/// an empty glyph, U+E001 to a triangle.
|
||||
fn blank_glyph_font() -> Vec<u8> {
|
||||
test_font("Blank Glyph Test")
|
||||
}
|
||||
|
||||
/// The blank-glyph test font under another family name.
|
||||
fn test_font(family: &str) -> Vec<u8> {
|
||||
fn u16s(v: &[u16]) -> Vec<u8> {
|
||||
v.iter().flat_map(|x| x.to_be_bytes()).collect()
|
||||
}
|
||||
@@ -1177,7 +1207,7 @@ mod tests {
|
||||
hhea.extend(u16s(&[0, 3]));
|
||||
let hmtx = u16s(&[600, 0, 600, 0, 600, 0]);
|
||||
let maxp = u16s(&[1, 0, 3, 3, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
let strings: Vec<Vec<u8>> = ["Blank Glyph Test", "Regular"].iter().map(|s| s.encode_utf16().flat_map(|u| u.to_be_bytes()).collect()).collect();
|
||||
let strings: Vec<Vec<u8>> = [family, "Regular"].iter().map(|s| s.encode_utf16().flat_map(|u| u.to_be_bytes()).collect()).collect();
|
||||
let mut name = u16s(&[0, 2, 30]);
|
||||
let mut off = 0;
|
||||
for (id, st) in strings.iter().enumerate() {
|
||||
@@ -1221,6 +1251,23 @@ mod tests {
|
||||
assert_eq!(fallback_for('\u{e001}', inter), id, "fallback finds the drawn one");
|
||||
}
|
||||
|
||||
/// A Microsoft family that isn't installed renders in its installed metric-compatible
|
||||
/// substitute instead of Inter, and still reports itself missing (#1677).
|
||||
#[test]
|
||||
fn microsoft_families_resolve_to_metric_compatible_substitutes() {
|
||||
add_font_data(test_font("Carlito"));
|
||||
add_font_data(test_font("Caladea"));
|
||||
let calibri = resolve("Calibri", "Regular");
|
||||
assert_eq!(face(calibri.face).info.family, "Carlito");
|
||||
assert!(calibri.missing, "the original name stays missing for Find Missing Fonts");
|
||||
let cambria = resolve("Cambria", "Bold");
|
||||
assert_eq!(face(cambria.face).info.family, "Caladea");
|
||||
assert!(cambria.missing);
|
||||
let unknown = resolve("No Such Family 1677", "Regular");
|
||||
assert_eq!(face(unknown.face).info.family, DEFAULT_FAMILY);
|
||||
assert!(unknown.missing);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_han_order_follows_the_cjk_locale() {
|
||||
// Chinese first for any Chinese tag, whether or not it names a script.
|
||||
|
||||
Reference in new issue
Block a user