Search code examples
rustfontssave

How do I modify a font and save it with Rust 'write-fonts'?


I would like to open a font file and modify eg the usWeightClass and save it in a different location – using Rust: https://github.com/googlefonts/fontations

I am already able to load a file, read data from the font, but I am not able to modify and save it.

I expect, that it should work somehow with WriteFont (but I don't know how): https://docs.rs/write-fonts/0.5.0/write_fonts/trait.FontWrite.html#

Any help would be much appreciated. Thanks a lot in advance, Olli

Cargo.toml

[dependencies]
write-fonts = "0.5.0"
read-fonts = "0.3.0"
font-types = "0.1.8"

main.rs

use read_fonts::{FontRef, TableProvider};

fn main() {
    pub static FONT_DATA: &[u8] = include_bytes!("/path/to/a/font/somefont.ttf");
    let font = FontRef::new(FONT_DATA).unwrap();

    let mut os2 = font.os2().expect("missing OS/2 table");
    println!("os2.version: {}", os2.version());
    println!("os2.us_weight_class: {}", os2.us_weight_class());

    let mut name = font.name().expect("missing name table");

    for item in name.name_record() {
        println!("name_id: {:?}", item.name_id);
        println!("language_id: {:?}", item.language_id);
        let data = item.string(name.string_data()).unwrap();
        println!("String entry: {:?}", data.chars().collect::<String>());
    };
}

Solution

  • I haven't worked with this library or with fonts in general yet, but after a little digging in the documentation, this seems to work:

    use read_fonts::{FontRead, FontRef, TableProvider, TopLevelTable};
    use write_fonts::{dump_table, tables::os2::Os2, FontBuilder};
    
    fn main() {
        {
            let font_data = std::fs::read("Roboto-Regular.ttf").unwrap();
            let font = FontRef::new(&font_data).unwrap();
    
            let os2 = font.os2().expect("missing OS/2 table");
            println!("os2.us_weight_class: {}", os2.us_weight_class());
    
            // Create a new font builder
            let mut builder = FontBuilder::default();
    
            // Iterate over tables and add them to the builder
            for table in font.table_directory.table_records() {
                let tag = table.tag();
    
                println!("    Adding table {tag} ...");
    
                let font_data = font
                    .table_data(tag)
                    .expect(&format!("Table {tag} not found!"));
    
                let mut raw_data = font_data.as_ref().to_owned();
    
                // Modify the OS2 tag
                if tag == Os2::TAG {
                    let mut os2 = Os2::read(font_data).unwrap();
                    os2.us_weight_class = 420;
                    raw_data = dump_table(&os2).unwrap();
                }
    
                builder.add_table(tag, raw_data);
            }
    
            // Build the font
            let data = builder.build();
            std::fs::write("Roboto-Regular-modified.ttf", data).unwrap();
        }
    
        {
            // Load the font again and check if it got modified
            let font_data = std::fs::read("Roboto-Regular-modified.ttf").unwrap();
            let font = FontRef::new(&font_data).unwrap();
    
            let os2 = font.os2().expect("missing OS/2 table");
            println!("os2.us_weight_class: {}", os2.us_weight_class());
        }
    }
    
    os2.us_weight_class: 400
        Adding table GDEF ...
        Adding table GPOS ...
        Adding table GSUB ...
        Adding table OS/2 ...
        Adding table cmap ...
        Adding table cvt  ...
        Adding table fpgm ...
        Adding table gasp ...
        Adding table glyf ...
        Adding table hdmx ...
        Adding table head ...
        Adding table hhea ...
        Adding table hmtx ...
        Adding table loca ...
        Adding table maxp ...
        Adding table name ...
        Adding table post ...
        Adding table prep ...
    os2.us_weight_class: 420