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