linebender / norad

Rust crate for working with Unified Font Object files
Apache License 2.0
43 stars 12 forks source link

Designspaces: handle localization #323

Open madig opened 1 year ago

madig commented 1 year ago

The spec describes e.g. https://fonttools.readthedocs.io/en/latest/designspaceLib/xml.html#stylename-familyname-stylemapstylename-stylemapfamilyname-elements-localised-names-for-instances to assign localized names. Norad may want to handle that.

cmyr commented 1 year ago

Yes we will definitely want to handle this, and I can't really imagine doing it without custom serde impls. I wish that the spec were a bit clearer on expectations here; in particular, is it possible to have only localized names, or should there only be localized names if there is also a default name?

My first thought is to replace the use of Option<String> for this type with an Option<LocalizedString> that is some custom type that wraps some sort of collection..

madig commented 1 year ago

My understanding last time is that the normal attribute for e.g. family name is lang "en", unless there's an explicit "en" entry in localized family name, but I haven't thought about it too hard. designspaceLib uses a dict[str, str] for these localized attributes.

anthrotype commented 1 month ago

also related to DS localisation, axes can have labelname element with localized strings for the UI axis labels.

E.g. see https://fonttools.readthedocs.io/en/latest/designspaceLib/xml.html#example-of-all-axis-elements-together

<axes elidedfallbackname="Regular">
    <axis default="1" maximum="1000" minimum="0" name="weight" tag="wght">
        <labelname xml:lang="fa-IR">قطر</labelname>
        <labelname xml:lang="en">Wéíght</labelname>
        <labels>
            <label userminimum="200" uservalue="200" usermaximum="250" name="Extra Light">
                <labelname xml:lang="de">Extraleicht</labelname>
                <labelname xml:lang="fr">Extra léger</labelname>
            </label>
            <label userminimum="350" uservalue="400" usermaximum="450" name="Regular" elidable="true" />
        </labels>
    </axis>
    <axis default="100" maximum="200" minimum="50" name="width" tag="wdth">
        <map input="50.0" output="10.0" />
        <map input="100.0" output="66.0" />
        <map input="200.0" output="990.0" />
    </axis>
</axes>

Right now norad can only parse the map sub-elements of axis element; the labelname elements appear as direct children of the same axis parent element, alongside map (plus the other labels used for stat axis values).

Today I wanted to try add support for parsing these labelname elements in norad (to tackle https://github.com/googlefonts/fontc/issues/1020) but failed miserably. I naively did the following:

diff --git a/src/designspace.rs b/src/designspace.rs
index 24be8d3..5cb739f 100644
--- a/src/designspace.rs
+++ b/src/designspace.rs
@@ -67,12 +67,23 @@ pub struct Axis {
     /// Mapping between user space coordinates and design space coordinates.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub map: Option<Vec<AxisMapping>>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub label_names: Option<Vec<LabelName>>,
 }

 fn is_false(value: &bool) -> bool {
     !(*value)
 }

+#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
+#[serde(rename = "labelname")]
+pub struct LabelName {
+    #[serde(rename = "@xml:lang")]
+    pub language: String,
+    #[serde(rename = "$text")]
+    pub string: String,
+}
+
 /// Maps one input value (user space coord) to one output value (design space coord).
 #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
 #[serde(rename = "map")]

But they don't seem to get parsed, but the map elements do...

How would one convince quick_xml and serde to parse the labelname elements at the same time as their sibling map elements? Do we need to define an enum (somethign that's either a labelname or a map) maybe? I haven't tried that, it'd look quite ugly.

If anyone else wants to give this one a crack, it'd be much appreciated.