From dfea7257d8a89b8e0a3d916278182960796a4f49 Mon Sep 17 00:00:00 2001 From: Colin Rofls Date: Fri, 18 Oct 2024 19:45:32 -0400 Subject: [PATCH] [write] Add common util for computing searchRange This is shared in a few places, so it makes sense to have a single implementation. (It is used in the kern table, which is coming up) --- write-fonts/src/font_builder.rs | 15 ++++++--------- write-fonts/src/tables/cmap.rs | 20 ++++++-------------- write-fonts/src/util.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/write-fonts/src/font_builder.rs b/write-fonts/src/font_builder.rs index 0c56c6322..c3459e06c 100644 --- a/write-fonts/src/font_builder.rs +++ b/write-fonts/src/font_builder.rs @@ -6,6 +6,8 @@ use std::{borrow::Cow, fmt::Display}; use read_fonts::{FontRef, TableProvider}; use types::{Tag, TT_SFNT_VERSION}; +use crate::util::SearchRange; + include!("../generated/generated_font.rs"); const TABLE_RECORD_LEN: usize = 16; @@ -32,19 +34,14 @@ pub struct BuilderError { impl TableDirectory { pub fn from_table_records(table_records: Vec) -> TableDirectory { assert!(table_records.len() <= u16::MAX as usize); - // See https://learn.microsoft.com/en-us/typography/opentype/spec/otff#table-directory - // Computation works at the largest allowable num tables so don't stress the as u16's - let entry_selector = (table_records.len() as f64).log2().floor() as u16; - let search_range = (2.0_f64.powi(entry_selector as i32) * 16.0) as u16; - // The result doesn't really make sense with 0 tables but ... let's at least not fail - let range_shift = (table_records.len() * 16).saturating_sub(search_range as usize) as u16; + let computed = SearchRange::compute(table_records.len(), TABLE_RECORD_LEN); TableDirectory::new( TT_SFNT_VERSION, - search_range, - entry_selector, - range_shift, + computed.search_range, + computed.entry_selector, + computed.range_shift, table_records, ) } diff --git a/write-fonts/src/tables/cmap.rs b/write-fonts/src/tables/cmap.rs index b64928700..a993efe52 100644 --- a/write-fonts/src/tables/cmap.rs +++ b/write-fonts/src/tables/cmap.rs @@ -6,6 +6,8 @@ include!("../../generated/generated_cmap.rs"); use std::collections::HashMap; +use crate::util::SearchRange; + // https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#windows-platform-platform-id--3 const WINDOWS_BMP_ENCODING: u16 = 1; const WINDOWS_FULL_REPERTOIRE_ENCODING: u16 = 10; @@ -96,26 +98,16 @@ impl CmapSubtable { ); let seg_count: u16 = start_code.len().try_into().unwrap(); - // Spec: Log2 of the maximum power of 2 less than or equal to segCount (log2(searchRange/2), - // which is equal to floor(log2(segCount))) - let entry_selector = (seg_count as f32).log2().floor(); - - // Spec: Maximum power of 2 less than or equal to segCount, times 2 - // ((2**floor(log2(segCount))) * 2, where “**” is an exponentiation operator) - let search_range = 2u16.pow(entry_selector as u32).checked_mul(2).unwrap(); - - // if 2^entry_selector*2 is a u16 then so is entry_selector - let entry_selector = entry_selector as u16; - let range_shift = seg_count * 2 - search_range; + let computed = SearchRange::compute(seg_count as _, u16::RAW_BYTE_LEN); let id_range_offsets = vec![0; id_deltas.len()]; Some(CmapSubtable::format_4( size_of_cmap4(seg_count, 0), 0, // 'lang' set to zero for all 'cmap' subtables whose platform IDs are other than Macintosh seg_count * 2, - search_range, - entry_selector, - range_shift, + computed.search_range, + computed.entry_selector, + computed.range_shift, end_code, start_code, id_deltas, diff --git a/write-fonts/src/util.rs b/write-fonts/src/util.rs index 8b7ceba18..d5b76c41d 100644 --- a/write-fonts/src/util.rs +++ b/write-fonts/src/util.rs @@ -100,3 +100,29 @@ impl Default for FloatComparator { pub fn isclose(a: f64, b: f64) -> bool { FloatComparator::default().isclose(a, b) } + +/// Search range values used in various tables +#[derive(Clone, Copy, Debug)] +pub struct SearchRange { + pub search_range: u16, + pub entry_selector: u16, + pub range_shift: u16, +} + +impl SearchRange { + //https://github.com/fonttools/fonttools/blob/729b3d2960ef/Lib/fontTools/ttLib/ttFont.py#L1147 + /// calculate searchRange, entrySelector, and rangeShift + /// + /// these values are used in various tables. + pub fn compute(n_items: usize, item_size: usize) -> Self { + let entry_selector = (n_items as f64).log2().floor() as usize; + let search_range = (2.0_f64.powi(entry_selector as i32) * item_size as f64) as usize; + // The result doesn't really make sense with 0 tables but ... let's at least not fail + let range_shift = (n_items * item_size).saturating_sub(search_range); + SearchRange { + search_range: search_range.try_into().unwrap(), + entry_selector: entry_selector.try_into().unwrap(), + range_shift: range_shift.try_into().unwrap(), + } + } +}