use allsorts::{ binary::read::ReadScope, font::{Font, MatchingPresentation}, font_data::FontData as AllsortsFontData, subset::subset, tag, }; use epaint::{ text::{FontData, FontDefinitions, FontTweak}, FontFamily, }; use std::{ collections::BTreeMap, env, fs::File, io::{BufWriter, Write}, path::Path, sync::Arc, }; include!(concat!( env!("CARGO_MANIFEST_DIR"), "/src/unicode_helper.rs" )); fn font_stripper(from: &str, out: &str, unicodes: Vec) -> Result, String> { let font_path = format!("{}/assets/{}", env!("CARGO_MANIFEST_DIR"), from); let font_data = std::fs::read(&font_path).map_err(|e| e.to_string())?; let scope = ReadScope::new(&font_data); let font_file = scope .read::() .map_err(|e| format!("Failed to read font data: {}", e))?; let provider = font_file .table_provider(0) .map_err(|e| format!("Failed to get table provider: {}", e))?; let mut font = Font::new(provider).map_err(|e| format!("Failed to create font: {:?}", e))?; let mut glyph_ids = Vec::new(); for &ch in &unicodes { let mut glyph_ids_curr = font .map_glyphs( &ch.to_string(), tag::LATN, MatchingPresentation::NotRequired, ) .into_iter() .map(|glyph| glyph.glyph_index) .collect(); glyph_ids.append(&mut glyph_ids_curr); } // Include .notdef glyph glyph_ids.push(0); glyph_ids.sort(); glyph_ids.dedup(); let subset_data = subset(&font.font_table_provider, &glyph_ids) .map_err(|e| format!("Failed to subset font: {}", e))?; let new_path = [&env::var("OUT_DIR").unwrap(), out].concat(); std::fs::write(&new_path, &subset_data) .map_err(|e| format!("Failed to write subset font: {}", e))?; Ok(subset_data) } fn main() { // rebuild if contents of `assets` folder changed println!("cargo:rerun-if-changed=assets/*"); let mut main_chars: Vec = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzsu0123456789?.,!(){}[]-_=+-/<>'\\ :^*`@#$%&|~;" .iter() .map(|c| *c as char) .collect(); main_chars.append(&mut vec!['π', '"']); { let filtered_chars: Vec = main_chars .iter() .filter(|c| !c.is_alphanumeric()) .cloned() .collect(); let chars_array = format!( "const VALID_EXTRA_CHARS: [char; {}] = {};", filtered_chars.len(), to_chars_array(filtered_chars), ); let path = Path::new(&env::var("OUT_DIR").unwrap()).join("valid_chars.rs"); let mut file = BufWriter::new(File::create(path).expect("Could not save compressed_data")); write!(&mut file, "{}", chars_array).expect("unable to write chars_array"); } let fonts = FontDefinitions { font_data: BTreeMap::from([ ( "Ubuntu-Light".to_owned(), Arc::new(FontData::from_owned( font_stripper( "Ubuntu-Light.ttf", "ubuntu-light.ttf", [main_chars, vec!['∫']].concat(), ) .unwrap(), )), ), ( "NotoEmoji-Regular".to_owned(), Arc::new(FontData::from_owned( font_stripper( "NotoEmoji-Regular.ttf", "noto-emoji.ttf", vec!['🌞', '🌙', '✖'], ) .unwrap(), )), ), ( "emoji-icon-font".to_owned(), Arc::new( FontData::from_owned( font_stripper("emoji-icon-font.ttf", "emoji-icon.ttf", vec!['⚙']).unwrap(), ) .tweak(FontTweak { scale: 0.8, y_offset_factor: 0.07, y_offset: -0.0333, }), ), ), ]), families: BTreeMap::from([ ( FontFamily::Monospace, vec![ "Ubuntu-Light".to_owned(), "NotoEmoji-Regular".to_owned(), "emoji-icon-font".to_owned(), ], ), ( FontFamily::Proportional, vec![ "Ubuntu-Light".to_owned(), "NotoEmoji-Regular".to_owned(), "emoji-icon-font".to_owned(), ], ), ]), }; let data = bincode::serialize(&fonts).unwrap(); let zstd_levels = zstd::compression_level_range(); let data_compressed = zstd::encode_all(data.as_slice(), *zstd_levels.end()).expect("Could not compress data"); let path = Path::new(&env::var("OUT_DIR").unwrap()).join("compressed_data"); let mut file = BufWriter::new(File::create(path).expect("Could not save compressed_data")); file.write_all(data_compressed.as_slice()) .expect("Failed to save compressed data"); }