Annotate fonts with ruby (pinyin/romaji) and produce modified TTF/WOFF2 outputs.

fix: ttc output

lem.my 0b1bf2cb 6042aab7

verified
+38 -53
+36 -46
src/lib.rs
··· 1 1 pub mod renderer; 2 2 3 - use anyhow::{Context, Result, anyhow}; 3 + use anyhow::{Context, Result}; 4 4 use fontcull_font_types::NameId; 5 5 use fontcull_klippa::{Plan, SubsetFlags, subset_font}; 6 6 use fontcull_read_fonts::{ ··· 11 11 }; 12 12 use fontcull_skrifa::{MetadataProvider, outline::OutlinePen}; 13 13 use fontcull_write_fonts::{ 14 - FontBuilder, dump_table, 15 - from_obj::{FromObjRef, ToOwnedObj}, 14 + FontBuilder, 15 + from_obj::ToOwnedObj, 16 16 tables::{ 17 17 glyf::{Glyf, GlyfLocaBuilder, Glyph, SimpleGlyph}, 18 18 head::Head, 19 19 loca::Loca, 20 - name::Name, 21 20 }, 22 21 }; 23 22 use indicatif::ProgressStyle; 24 23 use kurbo::BezPath; 25 24 use rayon::iter::{ParallelBridge, ParallelIterator}; 26 25 use rustc_hash::FxHashMap; 27 - use tracing::info_span; 26 + use tracing::{info, info_span}; 28 27 use tracing_indicatif::span_ext::IndicatifSpanExt; 29 28 use woofwoof; 30 29 31 30 use crate::renderer::RubyRenderer; 32 31 33 - pub fn process_font_file(file: FileRef, renderer: &Box<dyn RubyRenderer>) -> Result<Vec<u8>> { 32 + pub fn process_font_file( 33 + file: FileRef, 34 + renderer: &Box<dyn RubyRenderer>, 35 + subset: bool, 36 + ) -> Result<Vec<u8>> { 34 37 match file { 35 - FileRef::Font(font) => process_single_font(font, &renderer), 36 - FileRef::Collection(collection) => { 37 - let head = collection 38 - .iter() 39 - .next() 40 - .context("No fonts in collection")??; 38 + FileRef::Font(font) => { 39 + let data = process_font_ref(font, &renderer)?; 41 40 42 - let family_name = get_family_name(&head).unwrap(); 41 + if subset { 42 + info!("Subsetting font..."); 43 43 44 + subset_by_renderers(&data, &renderer) 45 + } else { 46 + Ok(data) 47 + } 48 + } 49 + FileRef::Collection(collection) => { 44 50 let collection_span = info_span!("process_fonts_in_collection"); 45 51 collection_span.pb_set_style( 46 52 &ProgressStyle::with_template("{msg} [{wide_bar:.green/cyan}] {pos}/{len}") ··· 59 65 60 66 let font = font.context("Failed to read font")?; 61 67 62 - let data = process_single_font(font, &renderer)?; 68 + let mut data = process_font_ref(font, &renderer)?; 69 + 70 + if subset { 71 + info!("Subsetting font..."); 72 + data = subset_by_renderers(&data, &renderer)?; 73 + } 74 + 63 75 let data = Box::leak(data.into_boxed_slice()); 64 76 65 77 FontRef::new(data).context("Failed to create font ref") ··· 68 80 69 81 drop(process_span_enter); 70 82 71 - build_ttc(&fonts, &family_name) 83 + build_ttc(&fonts) 72 84 } 73 85 } 74 86 } 75 87 76 - fn process_single_font(font: FontRef, renderer: &Box<dyn RubyRenderer>) -> Result<Vec<u8>> { 88 + pub fn process_font_ref(font: FontRef, renderer: &Box<dyn RubyRenderer>) -> Result<Vec<u8>> { 77 89 let font_file_data = font.table_directory.offset_data(); 78 90 let charmap = font.charmap(); 79 91 let hmtx = font.hmtx()?; ··· 214 226 } 215 227 216 228 pub fn subset_by_renderers(font_data: &[u8], renderer: &Box<dyn RubyRenderer>) -> Result<Vec<u8>> { 217 - let file = FileRef::new(font_data).context("Failed to parse font for subsetting")?; 218 - let font = file 219 - .fonts() 220 - .next() 221 - .context("No font found for subsetting")? 222 - .context("Read error")?; 229 + let font = FontRef::new(font_data).context("Failed to parse font for subsetting")?; 223 230 224 231 // Build unicodes set based on provided character sets 225 232 let mut unicodes = IntSet::<u32>::empty(); ··· 295 302 } 296 303 } 297 304 298 - pub fn build_ttc(fonts: &[FontRef], family_name: &str) -> Result<Vec<u8>> { 305 + pub fn build_ttc(fonts: &[FontRef]) -> Result<Vec<u8>> { 299 306 let mut out = Vec::new(); 300 307 301 308 // TTC header ··· 331 338 332 339 for record in records { 333 340 let tag = record.tag(); 334 - let mut data = font 341 + let table_data = font 335 342 .table_data(tag) 336 343 .context("Table missing")? 337 344 .as_ref() 338 345 .to_vec(); 339 346 340 - // Rewrite name table 341 - if tag == Name::TAG { 342 - let name_table = font.name()?; 343 - let mut new_name = Name::from_obj_ref(&name_table, font.data()); 344 - 345 - for rec in new_name.name_record.iter_mut() { 346 - match rec.name_id { 347 - NameId::FAMILY_NAME => { 348 - rec.string = family_name.to_string().into(); 349 - } 350 - _ => {} 351 - } 352 - } 353 - 354 - data = dump_table(&new_name)?; 355 - } 356 - 357 347 // Only share tables that are usually safe and heavy 358 348 let can_share = matches!(tag, Glyf::TAG | Cff::TAG | Loca::TAG); 359 349 360 350 let rel_offset = if can_share { 361 - if let Some(&off) = table_cache.get(&(tag, data.clone())) { 351 + if let Some(&off) = table_cache.get(&(tag, table_data.clone())) { 362 352 off 363 353 } else { 364 354 while table_data_block.len() % 4 != 0 { ··· 366 356 } 367 357 368 358 let off = table_data_block.len() as u32; 369 - table_cache.insert((tag, data.clone()), off); 370 - table_data_block.extend(&data); 359 + table_cache.insert((tag, table_data.clone()), off); 360 + table_data_block.extend(&table_data); 371 361 372 362 off 373 363 } ··· 377 367 } 378 368 379 369 let off = table_data_block.len() as u32; 380 - table_data_block.extend(&data); 370 + table_data_block.extend(&table_data); 381 371 382 372 off 383 373 }; ··· 385 375 out.extend_from_slice(&tag.to_be_bytes()); 386 376 out.extend_from_slice(&record.checksum().to_be_bytes()); 387 377 out.extend_from_slice(&rel_offset.to_be_bytes()); 388 - out.extend_from_slice(&(data.len() as u32).to_be_bytes()); 378 + out.extend_from_slice(&(table_data.len() as u32).to_be_bytes()); 389 379 } 390 380 } 391 381
+2 -7
src/main.rs
··· 22 22 #[facet(args::named, args::short = 'o')] 23 23 out: PathBuf, 24 24 25 - /// Optional font file to use for ruby characters 25 + /// Separate font file to use for ruby characters 26 26 #[facet(args::named)] 27 27 font: Option<PathBuf>, 28 28 ··· 245 245 } 246 246 }; 247 247 248 - let mut new_font_data = rubify::process_font_file(base_file, &renderer)?; 249 - 250 - if cli.subset { 251 - info!("Subsetting font..."); 252 - new_font_data = rubify::subset_by_renderers(&new_font_data, &renderer)?; 253 - } 248 + let mut new_font_data = rubify::process_font_file(base_file, &renderer, cli.subset)?; 254 249 255 250 // let extension = out_path 256 251 // .extension()