Skip to content

Commit

Permalink
[write] Add common util for computing searchRange
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
cmyr committed Oct 18, 2024
1 parent fa707c0 commit 26bae5d
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 22 deletions.
15 changes: 7 additions & 8 deletions write-fonts/src/font_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,19 +34,16 @@ pub struct BuilderError {
impl TableDirectory {
pub fn from_table_records(table_records: Vec<TableRecord>) -> TableDirectory {
assert!(table_records.len() <= u16::MAX as usize);
const TABLE_RECORD_LEN: usize = 16;

// 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,
)
}
Expand Down
20 changes: 6 additions & 14 deletions write-fonts/src/tables/cmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
26 changes: 26 additions & 0 deletions write-fonts/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
}
}

0 comments on commit 26bae5d

Please sign in to comment.