Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[write] Add common util for computing searchRange #1191

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 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,14 @@ pub struct BuilderError {
impl TableDirectory {
pub fn from_table_records(table_records: Vec<TableRecord>) -> 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,
)
}
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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest enumerating them non-exclusively, e.g.

These values are used in at least the following tables:

* [OpenType font table directory](https://learn.microsoft.com/en-us/typography/opentype/spec/otff#table-directory)
* [cmap subtable format 4](https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-4-segment-mapping-to-delta-values)
...others...
```

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(),
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and based on you comment, one for 0?