From b16ba381a6053a78aefcdfcb20cd00bdddaaabbd Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Mon, 2 Nov 2026 22:11:42 -0500 Subject: [PATCH] font-catalog: count distinct legacy pairs against the pair budget Collect the legacy (family, style) pairs into a sorted set and stop at MAX_LEGACY_PAIRS distinct pairs, so the same pair repeated across languages cannot use up the budget. Output order is unchanged (sorted, deduplicated), and the test now expects exactly the full budget. --- crates/font-catalog/src/scan.rs | 28 ++++++++++++++++------------ crates/font-catalog/src/tests.rs | 2 +- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/crates/font-catalog/src/scan.rs b/crates/font-catalog/src/scan.rs index 0b6cca19..e0909247 100644 --- a/crates/font-catalog/src/scan.rs +++ b/crates/font-catalog/src/scan.rs @@ -175,8 +175,8 @@ fn name(font: &skrifa::FontRef<'_>, ids: &[StringId]) -> Option { const MAX_NAME_RECORDS: usize = 512; /// Distinct strings, with their language, kept per name ID. const MAX_NAMES: usize = 128; -/// Legacy family and style pairs kept per face. The pairs are a product of two name lists, so -/// they get a budget of their own. +/// Distinct legacy family and style pairs kept per face. The pairs are a product of two name +/// lists, so they get a budget of their own. const MAX_LEGACY_PAIRS: usize = 256; /// Every distinct string of `id`, with its language, in the order the font lists them (at most @@ -227,18 +227,22 @@ pub(crate) fn font_faces(font: &skrifa::FontRef<'_>, index: u32, path: Option<&P // typographic style: every weight of `Hiragino Sans` is `Regular` in its own legacy family. let style_names: Vec = if typo_style.is_empty() { legacy_style.iter().map(|(_, s)| s.clone()).collect() } else { typo_style }; let mut styles: Vec = std::iter::once(style.as_str()).chain(style_names.iter().map(String::as_str)).map(norm).collect(); - let mut legacy: Vec<(String, String)> = if typo_family.is_empty() { - vec![] - } else { - legacy_family + // Sorted and distinct; the budget counts distinct pairs, so repeats across languages do not + // crowd out real ones. + let mut legacy_pairs: std::collections::BTreeSet<(String, String)> = std::collections::BTreeSet::new(); + if !typo_family.is_empty() { + let pairs = legacy_family .iter() .flat_map(|(lf, f)| legacy_style.iter().filter(move |(ls, _)| ls == lf).map(move |(_, s)| (norm(f), norm(s)))) - .filter(|(f, s)| !f.is_empty() && !s.is_empty()) - .take(MAX_LEGACY_PAIRS) - .collect() - }; - legacy.sort(); - legacy.dedup(); + .filter(|(f, s)| !f.is_empty() && !s.is_empty()); + for pair in pairs { + legacy_pairs.insert(pair); + if legacy_pairs.len() == MAX_LEGACY_PAIRS { + break; + } + } + } + let legacy: Vec<(String, String)> = legacy_pairs.into_iter().collect(); let mut postscript: Vec = all_names(font, StringId::POSTSCRIPT_NAME).iter().map(|n| norm(n)).collect(); let mut full_names: Vec = all_names(font, StringId::FULL_NAME).iter().map(|n| norm(n)).collect(); for v in [&mut families, &mut styles, &mut full_names, &mut postscript] { diff --git a/crates/font-catalog/src/tests.rs b/crates/font-catalog/src/tests.rs index 47ea9d7f..7573ba13 100644 --- a/crates/font-catalog/src/tests.rs +++ b/crates/font-catalog/src/tests.rs @@ -537,5 +537,5 @@ fn repeated_name_records_are_bounded_before_pairing() { let face = faces.first().expect("a face"); assert_eq!(face.info.family, "Typo Family"); assert!(!face.keys.legacy.is_empty()); - assert!(face.keys.legacy.len() <= 256, "{} legacy pairs", face.keys.legacy.len()); + assert_eq!(face.keys.legacy.len(), 256, "the pair budget counts distinct pairs"); }