Skip to content

Conversation

@Kumasan00
Copy link

@Kumasan00 Kumasan00 commented Dec 17, 2025

Body:

This PR adds support for parsing Instance Records from the fvar table, as proposed in #208 .

Changes:

  • Added Instance struct to represent a single instance record.
  • Added Instances struct and InstancesIter for iterating over records.
  • Updated fvar::Table to include a public instances field.
  • Implemented logic to detect the presence of the optional postScriptNameID field based on record_len and axisCount, strictly following the OpenType specification.

Example: Users can now iterate over named instances like this:

if let Some(fvar) = face.tables().fvar {
    for instance in fvar.instances.iter() {
        let name_id = instance.subfamily_name_id;
        let coords = instance.coordinates;
        // ...
    }
}

Related Issue: Closes #208 Relates to #129

@Kumasan00
Copy link
Author

Verification: I verified the implementation by comparing the parsing results against the read-fonts crate using STIXTwoText[wght].ttf and NotoSans-VariableFont_wdth,wght.ttf. The outputs match perfectly.

Click to see the comparison output

STIXTwoText[wght].ttf :

ttf-parser output:

Instance 0: name_id=257, flags=0, coordinates=[Fixed(400.0)], postscript_name_id=None
Instance 1: name_id=258, flags=0, coordinates=[Fixed(500.0)], postscript_name_id=None
Instance 2: name_id=259, flags=0, coordinates=[Fixed(600.0)], postscript_name_id=None
Instance 3: name_id=260, flags=0, coordinates=[Fixed(700.0)], postscript_name_id=None
ttf-parser iterator output:
Instance via iterator: Instance { subfamily_name_id: 257, flags: 0, coordinates: [Fixed(400.0)], post_script_name_id: None }
Instance via iterator: Instance { subfamily_name_id: 258, flags: 0, coordinates: [Fixed(500.0)], post_script_name_id: None }
Instance via iterator: Instance { subfamily_name_id: 259, flags: 0, coordinates: [Fixed(600.0)], post_script_name_id: None }
Instance via iterator: Instance { subfamily_name_id: 260, flags: 0, coordinates: [Fixed(700.0)], post_script_name_id: None }
read-fonts output:
ReadFonts Instance 0: Ok(InstanceRecord { subfamily_name_id: NameId 257, flags: 0, coordinates: [400.0], post_script_name_id: None })
ReadFonts Instance 1: Ok(InstanceRecord { subfamily_name_id: NameId 258, flags: 0, coordinates: [500.0], post_script_name_id: None })
ReadFonts Instance 2: Ok(InstanceRecord { subfamily_name_id: NameId 259, flags: 0, coordinates: [600.0], post_script_name_id: None })
ReadFonts Instance 3: Ok(InstanceRecord { subfamily_name_id: NameId 260, flags: 0, coordinates: [700.0], post_script_name_id: None })

NotoSans-VariableFont_wdth,wght.ttf:

ttf-parser output:
Instance 0: name_id=297, flags=0, coordinates=[Fixed(100.0), Fixed(100.0)], postscript_name_id=Some(298)
Instance 1: name_id=299, flags=0, coordinates=[Fixed(200.0), Fixed(100.0)], postscript_name_id=Some(300)
Instance 2: name_id=301, flags=0, coordinates=[Fixed(300.0), Fixed(100.0)], postscript_name_id=Some(302)
Instance 3: name_id=303, flags=0, coordinates=[Fixed(400.0), Fixed(100.0)], postscript_name_id=Some(304)
Instance 4: name_id=305, flags=0, coordinates=[Fixed(500.0), Fixed(100.0)], postscript_name_id=Some(306)
Instance 5: name_id=307, flags=0, coordinates=[Fixed(600.0), Fixed(100.0)], postscript_name_id=Some(308)
Instance 6: name_id=309, flags=0, coordinates=[Fixed(700.0), Fixed(100.0)], postscript_name_id=Some(310)
Instance 7: name_id=311, flags=0, coordinates=[Fixed(800.0), Fixed(100.0)], postscript_name_id=Some(312)
Instance 8: name_id=313, flags=0, coordinates=[Fixed(900.0), Fixed(100.0)], postscript_name_id=Some(314)
ttf-parser iterator output:
Instance via iterator: Instance { subfamily_name_id: 297, flags: 0, coordinates: [Fixed(100.0), Fixed(100.0)], post_script_name_id: Some(298) }
Instance via iterator: Instance { subfamily_name_id: 299, flags: 0, coordinates: [Fixed(200.0), Fixed(100.0)], post_script_name_id: Some(300) }
Instance via iterator: Instance { subfamily_name_id: 301, flags: 0, coordinates: [Fixed(300.0), Fixed(100.0)], post_script_name_id: Some(302) }
Instance via iterator: Instance { subfamily_name_id: 303, flags: 0, coordinates: [Fixed(400.0), Fixed(100.0)], post_script_name_id: Some(304) }
Instance via iterator: Instance { subfamily_name_id: 305, flags: 0, coordinates: [Fixed(500.0), Fixed(100.0)], post_script_name_id: Some(306) }
Instance via iterator: Instance { subfamily_name_id: 307, flags: 0, coordinates: [Fixed(600.0), Fixed(100.0)], post_script_name_id: Some(308) }
Instance via iterator: Instance { subfamily_name_id: 309, flags: 0, coordinates: [Fixed(700.0), Fixed(100.0)], post_script_name_id: Some(310) }
Instance via iterator: Instance { subfamily_name_id: 311, flags: 0, coordinates: [Fixed(800.0), Fixed(100.0)], post_script_name_id: Some(312) }
Instance via iterator: Instance { subfamily_name_id: 313, flags: 0, coordinates: [Fixed(900.0), Fixed(100.0)], post_script_name_id: Some(314) }
read-fonts output:
ReadFonts Instance 0: Ok(InstanceRecord { subfamily_name_id: NameId 297, flags: 0, coordinates: [100.0, 100.0], post_script_name_id: Some(NameId 298) })
ReadFonts Instance 1: Ok(InstanceRecord { subfamily_name_id: NameId 299, flags: 0, coordinates: [200.0, 100.0], post_script_name_id: Some(NameId 300) })
ReadFonts Instance 2: Ok(InstanceRecord { subfamily_name_id: NameId 301, flags: 0, coordinates: [300.0, 100.0], post_script_name_id: Some(NameId 302) })
ReadFonts Instance 3: Ok(InstanceRecord { subfamily_name_id: NameId 303, flags: 0, coordinates: [400.0, 100.0], post_script_name_id: Some(NameId 304) })
ReadFonts Instance 4: Ok(InstanceRecord { subfamily_name_id: NameId 305, flags: 0, coordinates: [500.0, 100.0], post_script_name_id: Some(NameId 306) })
ReadFonts Instance 5: Ok(InstanceRecord { subfamily_name_id: NameId 307, flags: 0, coordinates: [600.0, 100.0], post_script_name_id: Some(NameId 308) })
ReadFonts Instance 6: Ok(InstanceRecord { subfamily_name_id: NameId 309, flags: 0, coordinates: [700.0, 100.0], post_script_name_id: Some(NameId 310) })
ReadFonts Instance 7: Ok(InstanceRecord { subfamily_name_id: NameId 311, flags: 0, coordinates: [800.0, 100.0], post_script_name_id: Some(NameId 312) })
ReadFonts Instance 8: Ok(InstanceRecord { subfamily_name_id: NameId 313, flags: 0, coordinates: [900.0, 100.0], post_script_name_id: Some(NameId 314) })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for InstanceRecord in fvar table

1 participant