fontconfig: Branch 'main' - 2 commits
GitLab Mirror
gitlab-mirror at kemper.freedesktop.org
Wed May 7 09:26:37 UTC 2025
fc-fontations/attributes.rs | 176 ++++++++++++++++++++++++++++++++++
fc-fontations/foundries.rs | 6 -
fc-fontations/mod.rs | 19 +++
fc-fontations/pattern_bindings/mod.rs | 12 +-
test/test_fontations_ft_query.py | 3
5 files changed, 209 insertions(+), 7 deletions(-)
New commits:
commit 752eb3d1c8423de634c3f61cbdd3ae13c7fdab78
Merge: 2fdcfb4 0ddd01e
Author: Akira TAGOH <akira at tagoh.org>
Date: Wed May 7 09:26:34 2025 +0000
Merge branch 'attributes' into 'main'
[Fontations] Add attributes weight, width, slant to Pattern
See merge request fontconfig/fontconfig!396
commit 0ddd01e33e18f9d597aa011e755981d5af9d35f4
Author: Dominik Röttsches <drott at chromium.org>
Date: Mon Apr 28 12:37:53 2025 +0300
[Fontations] Add attributes weight, width, slant to Pattern
* Parse style attributes through Fontations Attributes
and OS/2 table information.
* Add an instance mode enum as preparation for handling
variable fonts and named instances.
* Enable comparison testing for "size", "weight", "width", "slant".
diff --git a/fc-fontations/attributes.rs b/fc-fontations/attributes.rs
new file mode 100644
index 0000000..e0ee28c
--- /dev/null
+++ b/fc-fontations/attributes.rs
@@ -0,0 +1,176 @@
+extern crate fc_fontations_bindgen;
+
+use fc_fontations_bindgen::{
+ fcint::{
+ FC_INDEX_OBJECT, FC_NAMED_INSTANCE_OBJECT, FC_SLANT_OBJECT, FC_VARIABLE_OBJECT,
+ FC_WEIGHT_OBJECT, FC_WIDTH_OBJECT,
+ },
+ FcWeightFromOpenTypeDouble, 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,
+};
+
+use crate::{
+ pattern_bindings::{FcPatternBuilder, PatternElement},
+ InstanceMode,
+};
+use read_fonts::TableProvider;
+use skrifa::{
+ attribute::{Attributes, Stretch, Style, Weight},
+ FontRef,
+};
+
+fn fc_weight(skrifa_weight: Weight) -> f64 {
+ (match skrifa_weight {
+ Weight::THIN => FC_WEIGHT_THIN,
+ Weight::EXTRA_LIGHT => FC_WEIGHT_EXTRALIGHT,
+ Weight::LIGHT => FC_WEIGHT_LIGHT,
+ Weight::NORMAL => FC_WEIGHT_NORMAL,
+ Weight::MEDIUM => FC_WEIGHT_MEDIUM,
+ Weight::SEMI_BOLD => FC_WEIGHT_SEMIBOLD,
+ Weight::BOLD => FC_WEIGHT_BOLD,
+ Weight::EXTRA_BOLD => FC_WEIGHT_EXTRABOLD,
+ Weight::BLACK => FC_WEIGHT_BLACK,
+ // See fcfreetype.c: When weight is not available, set to medium.
+ // This would mean a font did not have a parseable OS/2 table or
+ // a weight value could not be retrieved from it.
+ _ => FC_WEIGHT_MEDIUM,
+ }) as f64
+}
+
+fn fc_slant(skrifa_style: Style) -> u32 {
+ match skrifa_style {
+ Style::Italic => FC_SLANT_ITALIC,
+ Style::Oblique(_) => FC_SLANT_OBLIQUE,
+ _ => FC_SLANT_ROMAN,
+ }
+}
+
+fn fc_width(skrifa_stretch: Stretch) -> f64 {
+ (match skrifa_stretch {
+ Stretch::ULTRA_CONDENSED => FC_WIDTH_ULTRACONDENSED,
+ Stretch::EXTRA_CONDENSED => FC_WIDTH_EXTRACONDENSED,
+ Stretch::CONDENSED => FC_WIDTH_CONDENSED,
+ Stretch::SEMI_CONDENSED => FC_WIDTH_SEMICONDENSED,
+ Stretch::NORMAL => FC_WIDTH_NORMAL,
+ Stretch::SEMI_EXPANDED => FC_WIDTH_SEMIEXPANDED,
+ Stretch::EXPANDED => FC_WIDTH_EXPANDED,
+ Stretch::EXTRA_EXPANDED => FC_WIDTH_EXTRAEXPANDED,
+ Stretch::ULTRA_EXPANDED => FC_WIDTH_ULTRAEXPANDED,
+ _ => FC_WIDTH_NORMAL,
+ } as f64)
+}
+
+fn fc_weight_from_os2(font_ref: &FontRef) -> Option<f64> {
+ let us_weight = font_ref.os2().ok()?.us_weight_class() as f64;
+ unsafe {
+ let result = FcWeightFromOpenTypeDouble(us_weight);
+ if result == -1.0 {
+ None
+ } else {
+ Some(result)
+ }
+ }
+}
+
+fn fc_width_from_os2(font_ref: &FontRef) -> Option<f64> {
+ let us_width = font_ref.os2().ok()?.us_width_class();
+ let converted = match us_width {
+ 1 => FC_WIDTH_ULTRACONDENSED,
+ 2 => FC_WIDTH_EXTRACONDENSED,
+ 3 => FC_WIDTH_CONDENSED,
+ 4 => FC_WIDTH_SEMICONDENSED,
+ 5 => FC_WIDTH_NORMAL,
+ 6 => FC_WIDTH_SEMIEXPANDED,
+ 7 => FC_WIDTH_EXPANDED,
+ 8 => FC_WIDTH_EXTRAEXPANDED,
+ 9 => FC_WIDTH_ULTRAEXPANDED,
+ _ => FC_WIDTH_NORMAL,
+ };
+ Some(converted as f64)
+}
+
+struct AttributesToPattern {
+ weight_from_os2: Option<f64>,
+ width_from_os2: Option<f64>,
+ attributes: Attributes,
+}
+
+impl AttributesToPattern {
+ fn new(font: &FontRef) -> Self {
+ Self {
+ weight_from_os2: fc_weight_from_os2(font),
+ width_from_os2: fc_width_from_os2(font),
+ attributes: Attributes::new(font),
+ }
+ }
+
+ 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 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 static_slant(&self) -> PatternElement {
+ PatternElement::new(
+ FC_SLANT_OBJECT as i32,
+ (fc_slant(self.attributes.style) as i32).into(),
+ )
+ }
+}
+
+pub fn append_style_elements(
+ font: &FontRef,
+ instance_mode: InstanceMode,
+ ttc_index: Option<i32>,
+ pattern: &mut FcPatternBuilder,
+) {
+ // TODO: fcfreetype.c seems to prefer parsing information from the WWS name table entry,
+ // 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);
+
+ 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());
+
+ 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().into(),
+ ));
+
+ pattern.append_element(PatternElement::new(
+ FC_NAMED_INSTANCE_OBJECT as i32,
+ false.into(),
+ ));
+ }
+ _ => {
+ // TODO: Variable and named instances not implemented yet.
+ unreachable!()
+ }
+ }
+}
diff --git a/fc-fontations/foundries.rs b/fc-fontations/foundries.rs
index ad60647..536331a 100644
--- a/fc-fontations/foundries.rs
+++ b/fc-fontations/foundries.rs
@@ -63,7 +63,7 @@ pub fn make_foundry(font: &FontRef) -> Option<CString> {
return CString::new(os2.ach_vend_id().to_be_bytes()).ok();
}
- map_foundry_from_name_entry(&mut font.localized_strings(StringId::TRADEMARK)).or_else(
- || map_foundry_from_name_entry(&mut font.localized_strings(StringId::MANUFACTURER)),
- )
+ map_foundry_from_name_entry(&mut font.localized_strings(StringId::TRADEMARK)).or_else(|| {
+ map_foundry_from_name_entry(&mut font.localized_strings(StringId::MANUFACTURER))
+ })
}
diff --git a/fc-fontations/mod.rs b/fc-fontations/mod.rs
index 26754ff..1c4780d 100644
--- a/fc-fontations/mod.rs
+++ b/fc-fontations/mod.rs
@@ -22,10 +22,12 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
+mod attributes;
mod foundries;
mod names;
mod pattern_bindings;
+use attributes::append_style_elements;
use foundries::make_foundry;
use names::add_names;
@@ -80,6 +82,18 @@ pub unsafe extern "C" fn add_patterns_to_fontset(
1
}
+/// Used for controlling FontConfig's behavior per font instance.
+///
+/// We add one pattern for the default instance, one for each named instance,
+/// and one for using the font as a variable font, with ranges of values where applicable.
+#[derive(Copy, Clone)]
+#[allow(dead_code)]
+enum InstanceMode {
+ Default,
+ Named(i32),
+ Variable,
+}
+
fn fonts_and_indices(
file_ref: Option<FileRef>,
) -> impl Iterator<Item = (FontRef<'_>, Option<i32>)> {
@@ -136,7 +150,7 @@ fn has_hint(font_ref: &FontRef) -> bool {
fn build_patterns_for_font(
font: &FontRef,
_: *const libc::c_char,
- _: Option<i32>,
+ ttc_index: Option<i32>,
) -> Vec<*mut FcPattern> {
let mut pattern = FcPatternBuilder::new();
@@ -200,6 +214,9 @@ fn build_patterns_for_font(
version.into(),
));
+ // TODO: Handle variable instance and named instances.
+ append_style_elements(font, InstanceMode::Default, ttc_index, &mut pattern);
+
pattern
.create_fc_pattern()
.map(|p| p.into_raw() as *mut FcPattern)
diff --git a/fc-fontations/pattern_bindings/mod.rs b/fc-fontations/pattern_bindings/mod.rs
index 29359e9..24f7158 100644
--- a/fc-fontations/pattern_bindings/mod.rs
+++ b/fc-fontations/pattern_bindings/mod.rs
@@ -55,15 +55,21 @@ impl From<CString> for PatternValue {
}
}
+impl From<i32> for PatternValue {
+ fn from(item: i32) -> Self {
+ PatternValue::Integer(item)
+ }
+}
+
impl From<bool> for PatternValue {
fn from(item: bool) -> Self {
PatternValue::Boolean(item)
}
}
-impl From<i32> for PatternValue {
- fn from(item: i32) -> Self {
- PatternValue::Integer(item)
+impl From<f64> for PatternValue {
+ fn from(item: f64) -> Self {
+ PatternValue::Double(item)
}
}
diff --git a/test/test_fontations_ft_query.py b/test/test_fontations_ft_query.py
index c08b16e..9a00e3e 100644
--- a/test/test_fontations_ft_query.py
+++ b/test/test_fontations_ft_query.py
@@ -62,6 +62,9 @@ def test_fontations_freetype_fcquery_equal(font_file):
"fonthashint",
"foundry",
"version",
+ "weight",
+ "width",
+ "slant",
]
format_string = ":".join(
"%{" + entity + "}" for entity in supported_format_entitites
More information about the Fontconfig
mailing list