fontconfig: Branch 'main' - 2 commits

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Fri Jun 13 17:04:46 UTC 2025


 fc-fontations/attributes.rs           |  331 ++++++++++++++++++++++++++++------
 fc-fontations/bitmap.rs               |    8 
 fc-fontations/capabilities.rs         |   10 -
 fc-fontations/foundries.rs            |   17 +
 fc-fontations/mod.rs                  |   32 ---
 fc-fontations/name_records.rs         |    4 
 fc-fontations/names.rs                |   68 ++++--
 fc-fontations/pattern_bindings/mod.rs |   14 +
 8 files changed, 364 insertions(+), 120 deletions(-)

New commits:
commit 41f2ae7df48d192b938ab78217bece1fab09dd1b
Merge: bccd175 031eeae
Author: Akira TAGOH <akira at tagoh.org>
Date:   Fri Jun 13 17:04:44 2025 +0000

    Merge branch 'landUbuntuMatch' into 'main'
    
    [Fontations] Assorted fixes to match FreeType indexing
    
    See merge request fontconfig/fontconfig!428

commit 031eeae68e3e631ab526651c3d5019819e476d8c
Author: Dominik Röttsches <drott at chromium.org>
Date:   Tue Jun 10 14:17:12 2025 +0300

    [Fontations] Assorted fixes to match FreeType indexing
    
    With these changes, only 48 fonts out of 4801 fonts that include all
    extracted fonts from Ubuntu fonts-.* packages have minor
    differences. The remaining differences are mostly explained by #480 and
    a set of several fonts (~3) which have a broken OS/2 table for which
    Skrifa fails to retrieve foundry information.
    
    * Fix language order, trim string ends.
    * Attributes from style string
    * Foundry retrieval fixes
    * Lowercase language
    * Clippy fixes
    * No bitmap strikes for EBDT
    * PostScript name assembly from subfamily, using family or prefix
    * PostScript name sanitizing,
    * OS/2 min/max size
    * Correct sequence joining when graphite support is present

diff --git a/fc-fontations/attributes.rs b/fc-fontations/attributes.rs
index 092ccfe..5fd6d7a 100644
--- a/fc-fontations/attributes.rs
+++ b/fc-fontations/attributes.rs
@@ -24,19 +24,24 @@
 
 use fc_fontations_bindgen::{
     fcint::{
-        FC_INDEX_OBJECT, FC_NAMED_INSTANCE_OBJECT, FC_SIZE_OBJECT, FC_SLANT_OBJECT,
-        FC_SPACING_OBJECT, FC_VARIABLE_OBJECT, FC_WEIGHT_OBJECT, FC_WIDTH_OBJECT,
+        FC_DECORATIVE_OBJECT, FC_INDEX_OBJECT, FC_NAMED_INSTANCE_OBJECT, FC_SIZE_OBJECT,
+        FC_SLANT_OBJECT, FC_SPACING_OBJECT, FC_STYLE_OBJECT, FC_VARIABLE_OBJECT, FC_WEIGHT_OBJECT,
+        FC_WIDTH_OBJECT,
     },
     FcWeightFromOpenTypeDouble, FC_DUAL, FC_MONO, FC_SLANT_ITALIC, FC_SLANT_OBLIQUE,
-    FC_SLANT_ROMAN, FC_WEIGHT_BLACK, FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABOLD, FC_WEIGHT_EXTRALIGHT,
-    FC_WEIGHT_LIGHT, FC_WEIGHT_MEDIUM, FC_WEIGHT_NORMAL, FC_WEIGHT_SEMIBOLD, FC_WEIGHT_THIN,
-    FC_WIDTH_CONDENSED, FC_WIDTH_EXPANDED, FC_WIDTH_EXTRACONDENSED, FC_WIDTH_EXTRAEXPANDED,
-    FC_WIDTH_NORMAL, FC_WIDTH_SEMICONDENSED, FC_WIDTH_SEMIEXPANDED, FC_WIDTH_ULTRACONDENSED,
-    FC_WIDTH_ULTRAEXPANDED,
+    FC_SLANT_ROMAN, FC_WEIGHT_BLACK, FC_WEIGHT_BOLD, FC_WEIGHT_BOOK, FC_WEIGHT_DEMIBOLD,
+    FC_WEIGHT_DEMILIGHT, FC_WEIGHT_EXTRABLACK, FC_WEIGHT_EXTRABOLD, FC_WEIGHT_EXTRALIGHT,
+    FC_WEIGHT_HEAVY, FC_WEIGHT_LIGHT, FC_WEIGHT_MEDIUM, FC_WEIGHT_NORMAL, FC_WEIGHT_REGULAR,
+    FC_WEIGHT_SEMIBOLD, FC_WEIGHT_SEMILIGHT, FC_WEIGHT_THIN, FC_WEIGHT_ULTRABLACK,
+    FC_WEIGHT_ULTRABOLD, FC_WEIGHT_ULTRALIGHT, FC_WIDTH_CONDENSED, FC_WIDTH_EXPANDED,
+    FC_WIDTH_EXTRACONDENSED, FC_WIDTH_EXTRAEXPANDED, FC_WIDTH_NORMAL, FC_WIDTH_SEMICONDENSED,
+    FC_WIDTH_SEMIEXPANDED, FC_WIDTH_ULTRACONDENSED, FC_WIDTH_ULTRAEXPANDED,
 };
 
 use crate::{
-    pattern_bindings::{fc_wrapper::FcRangeWrapper, FcPatternBuilder, PatternElement},
+    pattern_bindings::{
+        fc_wrapper::FcRangeWrapper, FcPatternBuilder, PatternElement, PatternValue,
+    },
     InstanceMode,
 };
 use read_fonts::TableProvider;
@@ -47,6 +52,7 @@ use skrifa::{
     prelude::{LocationRef, Size},
     AxisCollection, FontRef, MetadataProvider, NamedInstance, Tag,
 };
+use std::ffi::CString;
 
 fn fc_weight(skrifa_weight: Weight) -> f64 {
     (match skrifa_weight {
@@ -118,6 +124,15 @@ fn fc_width_from_os2(font_ref: &FontRef) -> Option<f64> {
     Some(converted as f64)
 }
 
+fn fc_size_from_os2(font_ref: &FontRef) -> Option<(f64, f64)> {
+    font_ref.os2().ok().and_then(|os2| {
+        Some((
+            os2.us_lower_optical_point_size()? as f64 / 20.0,
+            os2.us_upper_optical_point_size()? as f64 / 20.0,
+        ))
+    })
+}
+
 struct AttributesToPattern<'a> {
     weight_from_os2: Option<f64>,
     width_from_os2: Option<f64>,
@@ -153,30 +168,30 @@ impl<'a> AttributesToPattern<'a> {
         Some(axis_coords.find(|item| item.0 == tag)?.1 as f64)
     }
 
-    fn static_weight(&self) -> PatternElement {
-        self.weight_from_os2.map_or_else(
-            || {
-                PatternElement::new(
-                    FC_WEIGHT_OBJECT as i32,
-                    fc_weight(self.attributes.weight).into(),
-                )
-            },
-            |os2_weight| PatternElement::new(FC_WEIGHT_OBJECT as i32, os2_weight.into()),
+    fn flags_weight(&self) -> PatternElement {
+        PatternElement::new(
+            FC_WEIGHT_OBJECT as i32,
+            fc_weight(self.attributes.weight).into(),
         )
     }
 
-    fn static_width(&self) -> PatternElement {
-        self.width_from_os2.map_or_else(
-            || {
-                PatternElement::new(
-                    FC_WIDTH_OBJECT as i32,
-                    fc_width(self.attributes.stretch).into(),
-                )
-            },
-            |os2_width| PatternElement::new(FC_WIDTH_OBJECT as i32, os2_width.into()),
+    fn os2_weight(&self) -> Option<PatternElement> {
+        self.weight_from_os2
+            .map(|weight| PatternElement::new(FC_WEIGHT_OBJECT as i32, weight.into()))
+    }
+
+    fn flags_width(&self) -> PatternElement {
+        PatternElement::new(
+            FC_WIDTH_OBJECT as i32,
+            fc_width(self.attributes.stretch).into(),
         )
     }
 
+    fn os2_width(&self) -> Option<PatternElement> {
+        self.width_from_os2
+            .map(|width| PatternElement::new(FC_WIDTH_OBJECT as i32, width.into()))
+    }
+
     fn static_slant(&self) -> PatternElement {
         PatternElement::new(
             FC_SLANT_OBJECT as i32,
@@ -227,7 +242,7 @@ impl<'a> AttributesToPattern<'a> {
         ))
     }
 
-    fn default_size(&self) -> Option<PatternElement> {
+    fn default_axis_size(&self) -> Option<PatternElement> {
         self.axes.get_by_tag(Tag::new(b"opsz")).map(|opsz_axis| {
             PatternElement::new(
                 FC_SIZE_OBJECT as i32,
@@ -236,6 +251,17 @@ impl<'a> AttributesToPattern<'a> {
         })
     }
 
+    fn os2_size(&self) -> Option<PatternElement> {
+        fc_size_from_os2(&self.font_ref).and_then(|(lower, higher)| {
+            if lower != higher {
+                let range = FcRangeWrapper::new(lower, higher)?;
+                Some(PatternElement::new(FC_SIZE_OBJECT as i32, range.into()))
+            } else {
+                Some(PatternElement::new(FC_SIZE_OBJECT as i32, lower.into()))
+            }
+        })
+    }
+
     fn variable_weight(&self) -> Option<PatternElement> {
         let weight_axis = self.axes.get_by_tag(Tag::new(b"wght"))?;
         unsafe {
@@ -334,18 +360,164 @@ impl<'a> AttributesToPattern<'a> {
             .font_ref
             .charmap()
             .mappings()
-            .map(|(_codepoint, gid)| {
+            .filter_map(|(_codepoint, gid)| {
                 glyph_metrics
                     .advance_width(gid)
                     .and_then(|adv| if adv > 0.0 { Some(adv) } else { None })
-            })
-            .flatten();
+            });
 
         Self::spacing_from_advances(advances)
             .map(|spacing| PatternElement::new(FC_SPACING_OBJECT as i32, spacing.into()))
     }
 }
 
+#[derive(Default)]
+struct AttributesFromStyleString {
+    weight: Option<PatternElement>,
+    width: Option<PatternElement>,
+    slant: Option<PatternElement>,
+    decorative: Option<PatternElement>,
+}
+
+fn contains_weight(style_name: &CString) -> Option<PatternElement> {
+    const WEIGHT_MAP: [(&str, f64); 23] = [
+        ("thin", FC_WEIGHT_THIN as f64),
+        ("extralight", FC_WEIGHT_EXTRALIGHT as f64),
+        ("ultralight", FC_WEIGHT_ULTRALIGHT as f64),
+        ("demilight", FC_WEIGHT_DEMILIGHT as f64),
+        ("semilight", FC_WEIGHT_SEMILIGHT as f64),
+        ("light", FC_WEIGHT_LIGHT as f64),
+        ("book", FC_WEIGHT_BOOK as f64),
+        ("regular", FC_WEIGHT_REGULAR as f64),
+        ("normal", FC_WEIGHT_NORMAL as f64),
+        ("medium", FC_WEIGHT_MEDIUM as f64),
+        ("demibold", FC_WEIGHT_DEMIBOLD as f64),
+        ("demi", FC_WEIGHT_DEMIBOLD as f64),
+        ("semibold", FC_WEIGHT_SEMIBOLD as f64),
+        ("extrabold", FC_WEIGHT_EXTRABOLD as f64),
+        ("superbold", FC_WEIGHT_EXTRABOLD as f64),
+        ("ultrabold", FC_WEIGHT_ULTRABOLD as f64),
+        ("bold", FC_WEIGHT_BOLD as f64),
+        ("ultrablack", FC_WEIGHT_ULTRABLACK as f64),
+        ("superblack", FC_WEIGHT_EXTRABLACK as f64),
+        ("extrablack", FC_WEIGHT_EXTRABLACK as f64),
+        ("ultra", FC_WEIGHT_ULTRABOLD as f64),
+        ("black", FC_WEIGHT_BLACK as f64),
+        ("heavy", FC_WEIGHT_HEAVY as f64),
+    ];
+
+    for weight_mapping in WEIGHT_MAP {
+        if style_name
+            .to_string_lossy()
+            .to_lowercase()
+            .contains(weight_mapping.0)
+        {
+            return Some(PatternElement::new(
+                FC_WEIGHT_OBJECT as i32,
+                weight_mapping.1.into(),
+            ));
+        }
+    }
+    None
+}
+
+fn contains_slant(style_name: &CString) -> Option<PatternElement> {
+    const SLANT_MAP: [(&str, i32); 3] = [
+        ("italic", FC_SLANT_ITALIC as i32),
+        ("kursiv", FC_SLANT_ITALIC as i32),
+        ("oblique", FC_SLANT_OBLIQUE as i32),
+    ];
+
+    for mapping in SLANT_MAP {
+        if style_name
+            .to_string_lossy()
+            .to_lowercase()
+            .contains(mapping.0)
+        {
+            return Some(PatternElement::new(
+                FC_SLANT_OBJECT as i32,
+                mapping.1.into(),
+            ));
+        }
+    }
+    None
+}
+
+fn contains_width(style_name: &CString) -> Option<PatternElement> {
+    const WIDTH_MAP: [(&str, f64); 10] = [
+        ("ultracondensed", FC_WIDTH_ULTRACONDENSED as f64),
+        ("extracondensed", FC_WIDTH_EXTRACONDENSED as f64),
+        ("semicondensed", FC_WIDTH_SEMICONDENSED as f64),
+        ("condensed", FC_WIDTH_CONDENSED as f64),
+        ("normal", FC_WIDTH_NORMAL as f64),
+        ("semiexpanded", FC_WIDTH_SEMIEXPANDED as f64),
+        ("extraexpanded", FC_WIDTH_EXTRAEXPANDED as f64),
+        ("ultraexpanded", FC_WIDTH_ULTRAEXPANDED as f64),
+        ("expanded", FC_WIDTH_EXPANDED as f64),
+        ("extended", FC_WIDTH_EXPANDED as f64),
+    ];
+    for mapping in WIDTH_MAP {
+        if style_name
+            .to_string_lossy()
+            .to_lowercase()
+            .contains(mapping.0)
+        {
+            return Some(PatternElement::new(
+                FC_WIDTH_OBJECT as i32,
+                mapping.1.into(),
+            ));
+        }
+    }
+    None
+}
+
+fn contains_decorative(style_name: &CString) -> Option<PatternElement> {
+    let had_decorative = style_name
+        .to_string_lossy()
+        .to_lowercase()
+        .contains("decorative");
+
+    Some(PatternElement::new(
+        FC_DECORATIVE_OBJECT as i32,
+        had_decorative.into(),
+    ))
+}
+
+impl AttributesFromStyleString {
+    fn new(pattern: &FcPatternBuilder) -> Self {
+        let style_string = pattern
+            .into_iter()
+            .find(|element| element.object_id == FC_STYLE_OBJECT as i32)
+            .and_then(|element| match &element.value {
+                PatternValue::String(style) => Some(style),
+                _ => None,
+            });
+
+        if let Some(style) = style_string {
+            Self {
+                weight: contains_weight(style),
+                width: contains_width(style),
+                slant: contains_slant(style),
+                decorative: contains_decorative(style),
+            }
+        } else {
+            Self {
+                weight: None,
+                width: None,
+                slant: None,
+                decorative: Some(PatternElement::new(
+                    FC_DECORATIVE_OBJECT as i32,
+                    false.into(),
+                )),
+            }
+        }
+    }
+}
+
+/// Appends style pattern elements such as weight, width, slant, decorative to the pattern.
+/// Requires a textual style element to be already added to the pattern, so it's good
+/// to run this after names have been added. This is because this method performs certain
+/// string matches on the font name to determine style attributes.
 pub fn append_style_elements(
     font: &FontRef,
     instance_mode: InstanceMode,
@@ -356,17 +528,37 @@ pub fn append_style_elements(
     // but falls back to flags if those are not found. So far, I haven't identified test fonts
     // for which the WWS code path would trigger.
 
-    let attributes_converter = AttributesToPattern::new(font, &instance_mode);
+    let attributes_text = AttributesFromStyleString::new(pattern);
 
-    if let Some(spacing) = attributes_converter.spacing() {
+    let skrifa_attributes = AttributesToPattern::new(font, &instance_mode);
+
+    if let Some(spacing) = skrifa_attributes.spacing() {
         pattern.append_element(spacing);
     }
 
     match instance_mode {
         InstanceMode::Default => {
-            pattern.append_element(attributes_converter.static_weight());
-            pattern.append_element(attributes_converter.static_width());
-            pattern.append_element(attributes_converter.static_slant());
+            let pattern_weight = skrifa_attributes
+                .os2_weight()
+                .or(attributes_text.weight)
+                .unwrap_or(skrifa_attributes.flags_weight());
+            pattern.append_element(pattern_weight);
+
+            let width = skrifa_attributes
+                .os2_width()
+                .or(attributes_text.width)
+                .unwrap_or(skrifa_attributes.flags_width());
+            pattern.append_element(width);
+
+            pattern.append_element(
+                attributes_text
+                    .slant
+                    .unwrap_or(skrifa_attributes.static_slant()),
+            );
+
+            if let Some(element) = attributes_text.decorative {
+                pattern.append_element(element)
+            }
 
             pattern.append_element(PatternElement::new(FC_VARIABLE_OBJECT as i32, false.into()));
             pattern.append_element(PatternElement::new(
@@ -374,7 +566,10 @@ pub fn append_style_elements(
                 ttc_index.unwrap_or_default().into(),
             ));
 
-            if let Some(size) = attributes_converter.default_size() {
+            if let Some(size) = skrifa_attributes
+                .default_axis_size()
+                .or(skrifa_attributes.os2_size())
+            {
                 pattern.append_element(size);
             }
 
@@ -384,21 +579,28 @@ pub fn append_style_elements(
             ));
         }
         InstanceMode::Variable => {
-            if let Some(weight_to_add) = attributes_converter
+            let weight = skrifa_attributes
                 .variable_weight()
-                .or(Some(attributes_converter.static_weight()))
-            {
-                pattern.append_element(weight_to_add);
-            }
-            if let Some(width_to_add) = attributes_converter
+                .or(skrifa_attributes.os2_weight())
+                .or(attributes_text.weight)
+                .unwrap_or(skrifa_attributes.flags_weight());
+            pattern.append_element(weight);
+
+            let width = skrifa_attributes
                 .variable_width()
-                .or(Some(attributes_converter.static_width()))
-            {
-                pattern.append_element(width_to_add);
+                .or(skrifa_attributes.os2_width())
+                .or(attributes_text.width)
+                .unwrap_or(skrifa_attributes.flags_width());
+            pattern.append_element(width);
+
+            if let Some(element) = attributes_text.decorative {
+                pattern.append_element(element)
             }
-            if let Some(size) = attributes_converter.variable_opsz() {
+
+            if let Some(size) = skrifa_attributes.variable_opsz() {
                 pattern.append_element(size);
             }
+
             pattern.append_element(PatternElement::new(FC_VARIABLE_OBJECT as i32, true.into()));
 
             // TODO: Check if this should have a zero ttc index if not part of a collection.
@@ -410,29 +612,44 @@ pub fn append_style_elements(
                 FC_NAMED_INSTANCE_OBJECT as i32,
                 false.into(),
             ));
-            pattern.append_element(attributes_converter.static_slant());
+            pattern.append_element(skrifa_attributes.static_slant());
         }
         InstanceMode::Named(index) => {
-            if let Some(weight) = attributes_converter.instance_weight() {
-                pattern.append_element(weight);
-            }
-            if let Some(width) = attributes_converter.instance_width() {
-                pattern.append_element(width);
+            let weight = skrifa_attributes
+                .instance_weight()
+                .or(attributes_text.weight)
+                .unwrap_or(skrifa_attributes.flags_weight());
+            pattern.append_element(weight);
+
+            let width = skrifa_attributes
+                .instance_width()
+                .or(attributes_text.width)
+                .unwrap_or(skrifa_attributes.flags_width());
+            pattern.append_element(width);
+
+            pattern.append_element(
+                skrifa_attributes
+                    .instance_slant()
+                    .or(attributes_text.slant)
+                    .unwrap_or(skrifa_attributes.static_slant()),
+            );
+
+            if let Some(element) = attributes_text.decorative {
+                pattern.append_element(element)
             }
+
             pattern.append_element(PatternElement::new(FC_VARIABLE_OBJECT as i32, false.into()));
             pattern.append_element(PatternElement::new(
                 FC_INDEX_OBJECT as i32,
                 (ttc_index.unwrap_or_default() + ((index + 1) << 16)).into(),
             ));
-            if let Some(size_element) = attributes_converter
+            if let Some(size_element) = skrifa_attributes
                 .instance_size()
-                .or(attributes_converter.default_size())
+                .or(skrifa_attributes.default_axis_size())
             {
                 pattern.append_element(size_element);
             };
-            if let Some(slant_element) = attributes_converter.instance_slant() {
-                pattern.append_element(slant_element);
-            }
+
             pattern.append_element(PatternElement::new(
                 FC_NAMED_INSTANCE_OBJECT as i32,
                 true.into(),
diff --git a/fc-fontations/bitmap.rs b/fc-fontations/bitmap.rs
index 149c07f..14cc03c 100644
--- a/fc-fontations/bitmap.rs
+++ b/fc-fontations/bitmap.rs
@@ -22,7 +22,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-use skrifa::FontRef;
+use skrifa::{bitmap::BitmapFormat, FontRef};
 
 use skrifa::bitmap::BitmapStrikes;
 
@@ -32,7 +32,11 @@ use fc_fontations_bindgen::fcint::{FC_ANTIALIAS_OBJECT, FC_PIXEL_SIZE_OBJECT};
 pub fn add_pixel_size(pattern: &mut FcPatternBuilder, font: &FontRef) {
     let strikes = BitmapStrikes::new(font);
 
-    let has_strikes = strikes.len() > 0;
+    if let Some(BitmapFormat::Ebdt) = strikes.format() {
+        return;
+    }
+
+    let has_strikes = !strikes.is_empty();
 
     for strike in strikes.iter() {
         pattern.append_element(PatternElement::new(
diff --git a/fc-fontations/capabilities.rs b/fc-fontations/capabilities.rs
index a19345d..232857b 100644
--- a/fc-fontations/capabilities.rs
+++ b/fc-fontations/capabilities.rs
@@ -29,20 +29,20 @@ use skrifa::FontRef;
 use std::ffi::CString;
 
 // Mimicking issue in FreeType indexer inserting two delimiting spaces.
-const SILF_CAPABILITIES_PREFIX: &str = "ttable:Silf ";
+const SILF_CAPABILITIES_PREFIX: &str = "ttable:Silf";
 const SILF_TAG: Tag = Tag::new(b"Silf");
 
 fn capabilities_string<T: IntoIterator<Item = Tag>>(tags: T, has_silf: bool) -> Option<CString> {
     let mut deduplicated_tags: Vec<Tag> = tags.into_iter().collect::<Vec<_>>();
     deduplicated_tags.sort();
     deduplicated_tags.dedup();
-    let mut capabilities = deduplicated_tags
+    let mut capabilities_set = deduplicated_tags
         .into_iter()
         .map(|tag| format!("otlayout:{}", tag))
-        .collect::<Vec<_>>()
-        .join(" ");
+        .collect::<Vec<_>>();
+    has_silf.then(|| capabilities_set.insert(0, SILF_CAPABILITIES_PREFIX.to_string()));
 
-    has_silf.then(|| capabilities.insert_str(0, SILF_CAPABILITIES_PREFIX));
+    let capabilities = capabilities_set.join(" ");
     if capabilities.is_empty() {
         return None;
     }
diff --git a/fc-fontations/foundries.rs b/fc-fontations/foundries.rs
index 536331a..ba10356 100644
--- a/fc-fontations/foundries.rs
+++ b/fc-fontations/foundries.rs
@@ -27,7 +27,7 @@ use skrifa::{
     string::{LocalizedStrings, StringId},
     FontRef, MetadataProvider,
 };
-use std::ffi::CString;
+use std::ffi::{CStr, CString};
 
 fn foundry_name_to_taglike(foundry: &str) -> Option<&'static str> {
     match foundry {
@@ -60,7 +60,20 @@ fn map_foundry_from_name_entry(localized_strings: &mut LocalizedStrings) -> Opti
 
 pub fn make_foundry(font: &FontRef) -> Option<CString> {
     if let Ok(os2) = font.os2() {
-        return CString::new(os2.ach_vend_id().to_be_bytes()).ok();
+        let vend_bytes = os2.ach_vend_id().to_be_bytes();
+        let foundry = if vend_bytes.contains(&0) {
+            CStr::from_bytes_until_nul(&vend_bytes)
+                .ok()
+                .map(|cstr| cstr.to_owned())
+        } else {
+            CString::new(vend_bytes).ok()
+        };
+
+        if let Some(foundry) = foundry {
+            if !foundry.is_empty() {
+                return Some(foundry);
+            }
+        }
     }
 
     map_foundry_from_name_entry(&mut font.localized_strings(StringId::TRADEMARK)).or_else(|| {
diff --git a/fc-fontations/mod.rs b/fc-fontations/mod.rs
index 88d23e7..8ed2ce3 100644
--- a/fc-fontations/mod.rs
+++ b/fc-fontations/mod.rs
@@ -43,9 +43,9 @@ use names::add_names;
 use fc_fontations_bindgen::{
     fcint::{
         FcFreeTypeLangSet, FC_CAPABILITY_OBJECT, FC_CHARSET_OBJECT, FC_COLOR_OBJECT,
-        FC_DECORATIVE_OBJECT, FC_FILE_OBJECT, FC_FONTFORMAT_OBJECT, FC_FONTVERSION_OBJECT,
-        FC_FONT_HAS_HINT_OBJECT, FC_FONT_WRAPPER_OBJECT, FC_FOUNDRY_OBJECT, FC_LANG_OBJECT,
-        FC_ORDER_OBJECT, FC_OUTLINE_OBJECT, FC_SCALABLE_OBJECT, FC_SYMBOL_OBJECT,
+        FC_FILE_OBJECT, FC_FONTFORMAT_OBJECT, FC_FONTVERSION_OBJECT, FC_FONT_HAS_HINT_OBJECT,
+        FC_FONT_WRAPPER_OBJECT, FC_FOUNDRY_OBJECT, FC_LANG_OBJECT, FC_ORDER_OBJECT,
+        FC_OUTLINE_OBJECT, FC_SCALABLE_OBJECT, FC_SYMBOL_OBJECT,
     },
     FcFontSet, FcFontSetAdd, FcPattern,
 };
@@ -288,33 +288,15 @@ fn build_patterns_for_font(
         .flat_map(move |instance_mode| {
             let mut instance_pattern = pattern.clone();
 
+            // Family, full name, postscript name, etc.
+            // Includes adding style name to the pattern, which is then used by append_style_elements.
+            add_names(font, instance_mode, &mut instance_pattern);
+
             // Style names: fcfreetype adds TT_NAME_ID_WWS_SUBFAMILY, TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY,
             // TT_NAME_ID_FONT_SUBFAMILY as FC_STYLE_OBJECT, FC_STYLE_OBJECT_LANG unless a named instance
             // is added,then the instance's name id is used as FC_STYLE_OBJECT.
-
             append_style_elements(font, instance_mode, ttc_index, &mut instance_pattern);
 
-            // For variable fonts:
-            // Names (mainly postscript name and style), weight, width and opsz (font-size?) are affected.
-            // * Add the variable font itself, with ranges for weight, width, opsz.
-            // * Add an entry for each named instance
-            //   * With instance name turning into FC_STYLE_OBJECT.
-            //   * Fixed width, wgth, opsz
-            // * Add the default instance with fixed values.
-            let mut had_decoratve = false;
-            // Family and full name.
-            add_names(
-                font,
-                instance_mode,
-                &mut instance_pattern,
-                &mut had_decoratve,
-            );
-
-            instance_pattern.append_element(PatternElement::new(
-                FC_DECORATIVE_OBJECT as i32,
-                had_decoratve.into(),
-            ));
-
             instance_pattern
                 .create_fc_pattern()
                 .map(|wrapper| wrapper.into_raw() as *mut FcPattern)
diff --git a/fc-fontations/name_records.rs b/fc-fontations/name_records.rs
index 08cce97..631ccdc 100644
--- a/fc-fontations/name_records.rs
+++ b/fc-fontations/name_records.rs
@@ -91,7 +91,7 @@ impl<'a> FcSortedNameRecords<'a> {
             }
             (MS_ENGLISH_US, _) | (MAC_ENGLISH, _) => std::cmp::Ordering::Greater,
             (_, MS_ENGLISH_US) | (_, MAC_ENGLISH) => std::cmp::Ordering::Less,
-            _ => a.cmp(&b),
+            _ => a.cmp(&b).reverse(),
         }
     }
 
@@ -125,7 +125,7 @@ impl<'a> FcSortedNameRecords<'a> {
     }
 }
 
-impl<'a> Iterator for FcSortedNameRecords<'a> {
+impl Iterator for FcSortedNameRecords<'_> {
     type Item = NameRecord;
 
     fn next(&mut self) -> Option<Self::Item> {
diff --git a/fc-fontations/names.rs b/fc-fontations/names.rs
index ae7fded..0344db5 100644
--- a/fc-fontations/names.rs
+++ b/fc-fontations/names.rs
@@ -41,7 +41,9 @@ fn object_ids_for_name_id(string_id: StringId) -> Option<(i32, i32)> {
         StringId::FAMILY_NAME | StringId::WWS_FAMILY_NAME | StringId::TYPOGRAPHIC_FAMILY_NAME => {
             Some((FC_FAMILY_OBJECT as i32, FC_FAMILYLANG_OBJECT as i32))
         }
-        StringId::FULL_NAME => Some((FC_FULLNAME_OBJECT as i32, FC_FULLNAMELANG_OBJECT as i32)),
+        StringId::FULL_NAME | StringId::COMPATIBLE_FULL_NAME => {
+            Some((FC_FULLNAME_OBJECT as i32, FC_FULLNAMELANG_OBJECT as i32))
+        }
         StringId::POSTSCRIPT_NAME => {
             Some((FC_POSTSCRIPT_NAME_OBJECT as i32, FC_INVALID_OBJECT as i32))
         }
@@ -69,13 +71,37 @@ fn mangle_postscript_name_for_named_instance(
     let instance_ps_name_id = font
         .named_instances()
         .get(named_instance_id as usize)?
-        .postscript_name_id()?;
-    let ps_name = font
-        .localized_strings(instance_ps_name_id)
-        .english_or_first()?
-        .clone()
-        .to_string();
-    CString::new(ps_name).ok()
+        .postscript_name_id();
+
+    if let Some(ps_name_id) = instance_ps_name_id {
+        let ps_name = font
+            .localized_strings(ps_name_id)
+            .english_or_first()?
+            .clone()
+            .to_string();
+        CString::new(ps_name).ok()
+    } else {
+        let instance_subfamily_name_id = font
+            .named_instances()
+            .get(named_instance_id as usize)?
+            .subfamily_name_id();
+        let prefix = font
+            .localized_strings(StringId::VARIATIONS_POSTSCRIPT_NAME_PREFIX)
+            .english_or_first()
+            .or(font
+                .localized_strings(StringId::FAMILY_NAME)
+                .english_or_first())?
+            .to_string()
+            + "-";
+        let subfam = font
+            .localized_strings(instance_subfamily_name_id)
+            .english_or_first()?
+            .to_string();
+
+        let assembled = prefix + &subfam;
+        let assembled = assembled.replace(" ", "");
+        CString::new(assembled).ok()
+    }
 }
 
 fn mangle_subfamily_name_for_named_instance(
@@ -112,19 +138,7 @@ fn mangle_full_name_for_named_instance(font: &FontRef, named_instance_id: i32) -
     CString::new(full_name + &subfam).ok()
 }
 
-fn determine_decorative(object_id: i32, name: &Option<CString>) -> bool {
-    object_id == FC_STYLE_OBJECT as i32
-        && name
-            .as_ref()
-            .is_some_and(|name| name.to_string_lossy().to_lowercase().contains("decorative"))
-}
-
-pub fn add_names(
-    font: &FontRef,
-    instance_mode: InstanceMode,
-    pattern: &mut FcPatternBuilder,
-    had_decorative: &mut bool,
-) {
+pub fn add_names(font: &FontRef, instance_mode: InstanceMode, pattern: &mut FcPatternBuilder) {
     let mut already_encountered_names: HashSet<(i32, String)> = HashSet::new();
     let name_table = font.name();
     if name_table.is_err() {
@@ -140,13 +154,19 @@ pub fn add_names(
             let name = if localized.to_string().is_empty() {
                 None
             } else {
-                CString::new(localized.to_string()).ok()
+                let mut name_trimmed = localized.to_string().trim().to_owned();
+                // PostScript name sanitization.
+                if object_ids.0 == FC_POSTSCRIPT_NAME_OBJECT as i32 {
+                    name_trimmed = name_trimmed.replace(" ", "");
+                }
+                CString::new(name_trimmed).ok()
             };
             let language = localized.language().or(Some("und")).and_then(|lang| {
+                let lang = lang.to_lowercase();
                 let lang = if lang.starts_with("zh") {
                     lang
                 } else {
-                    lang.split('-').next().unwrap_or(lang)
+                    lang.split('-').next().unwrap_or(&lang).to_string()
                 };
                 CString::new(lang).ok()
             });
@@ -173,8 +193,6 @@ pub fn add_names(
                 _ => name,
             };
 
-            *had_decorative = determine_decorative(object_ids.0, &name);
-
             if let (Some(name), Some(language)) = (name, language) {
                 let normalized_name = normalize_name(&name);
                 if already_encountered_names.contains(&(object_ids.0, normalized_name.clone())) {
diff --git a/fc-fontations/pattern_bindings/mod.rs b/fc-fontations/pattern_bindings/mod.rs
index 990d5e1..826f34b 100644
--- a/fc-fontations/pattern_bindings/mod.rs
+++ b/fc-fontations/pattern_bindings/mod.rs
@@ -93,8 +93,8 @@ impl From<FcLangSetWrapper> for PatternValue {
 
 #[derive(Debug, Clone)]
 pub struct PatternElement {
-    object_id: i32,
-    value: PatternValue,
+    pub object_id: i32,
+    pub value: PatternValue,
 }
 
 impl PatternElement {
@@ -191,6 +191,16 @@ impl FcPatternBuilder {
     }
 }
 
+/// Mainly needed for finding the style PatternElement in attributes.rs.
+impl<'a> IntoIterator for &'a FcPatternBuilder {
+    type Item = &'a PatternElement;
+    type IntoIter = std::slice::Iter<'a, PatternElement>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.elements.iter()
+    }
+}
+
 #[cfg(test)]
 mod test {
     use std::ffi::CString;


More information about the Fontconfig mailing list