Compare commits

..
Author SHA1 Message Date
Dylan M. Taylor 11fec161cc Fonts: ignore variation tags the font has no axis for 2026-11-03 20:32:00 +00:00
2 changed files with 12 additions and 58 deletions
+5 -55
View File
@@ -421,30 +421,23 @@ fn add_to_dictionary(_s: &mut Session, p: &Value) -> Result<Value> {
/// Replace whole-word `from` with `to` in `text`; how many were replaced.
fn replace_word(text: &mut String, from: &str, to: &str, all: bool) -> usize {
let is_word = |c: char| c.is_alphanumeric() || c == '\'' || c == '’';
// The result is built in one pass; the text is not edited in place for every match.
let mut out = String::new();
let (mut n, mut at, mut copied) = (0, 0, 0);
let mut n = 0;
let mut at = 0;
while let Some(i) = text.get(at..).and_then(|t| t.find(from)).map(|i| i + at) {
let end = i + from.len();
// The character before the match as the edited text would have it.
let before = text.get(copied..i).and_then(|t| t.chars().next_back()).or_else(|| out.chars().next_back()).is_some_and(is_word);
let before = text.get(..i).and_then(|t| t.chars().next_back()).is_some_and(is_word);
let after = text.get(end..).and_then(|t| t.chars().next()).is_some_and(is_word);
if before || after {
at = end;
continue;
}
out.push_str(text.get(copied..i).unwrap_or_default());
out.push_str(to);
text.replace_range(i..end, to);
n += 1;
(at, copied) = (end, end);
at = i + to.len();
if !all {
break;
}
}
if n > 0 {
out.push_str(text.get(copied..).unwrap_or_default());
*text = out;
}
n
}
@@ -771,49 +764,6 @@ fn end_compare(s: &mut Session, _: &Value) -> Result<Value> {
#[cfg(test)]
mod tests {
/// The replacement as it ran before: edited in place for every match.
fn replace_word_in_place(text: &mut String, from: &str, to: &str, all: bool) -> usize {
let is_word = |c: char| c.is_alphanumeric() || c == '\'' || c == '’';
let mut n = 0;
let mut at = 0;
while let Some(i) = text.get(at..).and_then(|t| t.find(from)).map(|i| i + at) {
let end = i + from.len();
let before = text.get(..i).and_then(|t| t.chars().next_back()).is_some_and(is_word);
let after = text.get(end..).and_then(|t| t.chars().next()).is_some_and(is_word);
if before || after {
at = end;
continue;
}
text.replace_range(i..end, to);
n += 1;
at = i + to.len();
if !all {
break;
}
}
n
}
#[test]
fn replace_word_matches_the_in_place_version() {
let cases = [
("a a b a", "a", "x", true),
("aa a. a", "a", "bb", true),
("a-a -a-", "-", "ab", true),
("foo. foo.", "foo.", "", true),
("it's it", "it", "so", true),
("x.x. x.", "x.", "y", true),
("a a a", "a", "b", false),
("none", "z", "q", true),
("é é", "é", "e", true),
];
for (text, from, to, all) in cases {
let (mut a, mut b) = (text.to_string(), text.to_string());
let (na, nb) = (replace_word(&mut a, from, to, all), replace_word_in_place(&mut b, from, to, all));
assert_eq!((na, a), (nb, b), "{text:?} {from:?} -> {to:?}");
}
}
use super::*;
use serde_json::json;
+7 -3
View File
@@ -1215,9 +1215,8 @@ impl FontDb {
fn instance(&self, family: &str, style: &str) -> Option<Arc<FontFace>> {
let open = style.find('{')?;
let base = self.find(family, style[..open].trim())?;
if base.skrifa().is_none_or(|f| f.axes().is_empty()) {
return Some(base);
}
let Some(font) = base.skrifa().filter(|f| !f.axes().is_empty()) else { return Some(base) };
let axis_tags: Vec<[u8; 4]> = font.axes().iter().map(|a| a.tag().to_be_bytes()).collect();
let mut coords = base.coords.clone();
for kv in style[open + 1..].trim_end_matches('}').split(',') {
let (k, v) = kv.split_once(':')?;
@@ -1227,6 +1226,11 @@ impl FontDb {
return None;
}
let tag = [k[0], k[1], k[2], k[3]];
// A tag the font has no axis for changes nothing; keeping it would grow the list with
// every distinct tag in the style text.
if !axis_tags.contains(&tag) {
continue;
}
match coords.iter_mut().find(|(t, _)| *t == tag) {
Some(c) => c.1 = v,
None => coords.push((tag, v)),