diff --git a/resources/codegen_inputs/cmap.rs b/resources/codegen_inputs/cmap.rs index 72f979eec..6ffd25c54 100644 --- a/resources/codegen_inputs/cmap.rs +++ b/resources/codegen_inputs/cmap.rs @@ -107,21 +107,26 @@ table Cmap4 { #[format = 4] format: u16, /// This is the length in bytes of the subtable. + #[compile(self.compute_length())] length: u16, /// For requirements on use of the language field, see “Use of /// the language field in 'cmap' subtables” in this document. language: u16, /// 2 × segCount. + #[compile(2 * array_len($end_code))] seg_count_x2: u16, /// Maximum power of 2 less than or equal to segCount, times 2 /// ((2**floor(log2(segCount))) * 2, where “**” is an /// exponentiation operator) + #[compile(self.compute_search_range())] search_range: u16, /// Log2 of the maximum power of 2 less than or equal to numTables /// (log2(searchRange/2), which is equal to floor(log2(segCount))) + #[compile(self.compute_entry_selector())] entry_selector: u16, /// segCount times 2, minus searchRange ((segCount * 2) - /// searchRange) + #[compile(self.compute_range_shift())] range_shift: u16, /// End characterCode for each segment, last=0xFFFF. #[count(half($seg_count_x2))] @@ -236,11 +241,13 @@ table Cmap12 { #[compile(0)] reserved: u16, /// Byte length of this subtable (including the header) + #[compile(self.compute_length())] length: u32, /// For requirements on use of the language field, see “Use of /// the language field in 'cmap' subtables” in this document. language: u32, /// Number of groupings which follow + #[compile(array_len($groups))] num_groups: u32, /// Array of SequentialMapGroup records. #[count($num_groups)] diff --git a/write-fonts/generated/generated_cmap.rs b/write-fonts/generated/generated_cmap.rs index b719f3eff..cd933ba38 100644 --- a/write-fonts/generated/generated_cmap.rs +++ b/write-fonts/generated/generated_cmap.rs @@ -158,14 +158,8 @@ impl CmapSubtable { } /// Construct a new `Cmap4` subtable - #[allow(clippy::too_many_arguments)] pub fn format_4( - length: u16, language: u16, - seg_count_x2: u16, - search_range: u16, - entry_selector: u16, - range_shift: u16, end_code: Vec, start_code: Vec, id_delta: Vec, @@ -173,12 +167,7 @@ impl CmapSubtable { glyph_id_array: Vec, ) -> Self { Self::Format4(Cmap4::new( - length, language, - seg_count_x2, - search_range, - entry_selector, - range_shift, end_code, start_code, id_delta, @@ -233,13 +222,8 @@ impl CmapSubtable { } /// Construct a new `Cmap12` subtable - pub fn format_12( - length: u32, - language: u32, - num_groups: u32, - groups: Vec, - ) -> Self { - Self::Format12(Cmap12::new(length, language, num_groups, groups)) + pub fn format_12(language: u32, groups: Vec) -> Self { + Self::Format12(Cmap12::new(language, groups)) } /// Construct a new `Cmap13` subtable @@ -566,23 +550,9 @@ impl FromObjRef for SubHeader { #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Cmap4 { - /// This is the length in bytes of the subtable. - pub length: u16, /// For requirements on use of the language field, see “Use of /// the language field in 'cmap' subtables” in this document. pub language: u16, - /// 2 × segCount. - pub seg_count_x2: u16, - /// Maximum power of 2 less than or equal to segCount, times 2 - /// ((2**floor(log2(segCount))) * 2, where “**” is an - /// exponentiation operator) - pub search_range: u16, - /// Log2 of the maximum power of 2 less than or equal to numTables - /// (log2(searchRange/2), which is equal to floor(log2(segCount))) - pub entry_selector: u16, - /// segCount times 2, minus searchRange ((segCount * 2) - - /// searchRange) - pub range_shift: u16, /// End characterCode for each segment, last=0xFFFF. pub end_code: Vec, /// Start character code for each segment. @@ -597,14 +567,8 @@ pub struct Cmap4 { impl Cmap4 { /// Construct a new `Cmap4` - #[allow(clippy::too_many_arguments)] pub fn new( - length: u16, language: u16, - seg_count_x2: u16, - search_range: u16, - entry_selector: u16, - range_shift: u16, end_code: Vec, start_code: Vec, id_delta: Vec, @@ -612,12 +576,7 @@ impl Cmap4 { glyph_id_array: Vec, ) -> Self { Self { - length, language, - seg_count_x2, - search_range, - entry_selector, - range_shift, end_code: end_code.into_iter().map(Into::into).collect(), start_code: start_code.into_iter().map(Into::into).collect(), id_delta: id_delta.into_iter().map(Into::into).collect(), @@ -631,12 +590,12 @@ impl FontWrite for Cmap4 { #[allow(clippy::unnecessary_cast)] fn write_into(&self, writer: &mut TableWriter) { (4 as u16).write_into(writer); - self.length.write_into(writer); + (self.compute_length() as u16).write_into(writer); self.language.write_into(writer); - self.seg_count_x2.write_into(writer); - self.search_range.write_into(writer); - self.entry_selector.write_into(writer); - self.range_shift.write_into(writer); + (2 * array_len(&self.end_code).unwrap() as u16).write_into(writer); + (self.compute_search_range() as u16).write_into(writer); + (self.compute_entry_selector() as u16).write_into(writer); + (self.compute_range_shift() as u16).write_into(writer); self.end_code.write_into(writer); (0 as u16).write_into(writer); self.start_code.write_into(writer); @@ -657,12 +616,7 @@ impl<'a> FromObjRef> for Cmap4 { fn from_obj_ref(obj: &read_fonts::tables::cmap::Cmap4<'a>, _: FontData) -> Self { let offset_data = obj.offset_data(); Cmap4 { - length: obj.length(), language: obj.language(), - seg_count_x2: obj.seg_count_x2(), - search_range: obj.search_range(), - entry_selector: obj.entry_selector(), - range_shift: obj.range_shift(), end_code: obj.end_code().to_owned_obj(offset_data), start_code: obj.start_code().to_owned_obj(offset_data), id_delta: obj.id_delta().to_owned_obj(offset_data), @@ -985,29 +939,18 @@ impl<'a> FontRead<'a> for Cmap10 { #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Cmap12 { - /// Byte length of this subtable (including the header) - pub length: u32, /// For requirements on use of the language field, see “Use of /// the language field in 'cmap' subtables” in this document. pub language: u32, - /// Number of groupings which follow - pub num_groups: u32, /// Array of SequentialMapGroup records. pub groups: Vec, } impl Cmap12 { /// Construct a new `Cmap12` - pub fn new( - length: u32, - language: u32, - num_groups: u32, - groups: Vec, - ) -> Self { + pub fn new(language: u32, groups: Vec) -> Self { Self { - length, language, - num_groups, groups: groups.into_iter().map(Into::into).collect(), } } @@ -1018,9 +961,9 @@ impl FontWrite for Cmap12 { fn write_into(&self, writer: &mut TableWriter) { (12 as u16).write_into(writer); (0 as u16).write_into(writer); - self.length.write_into(writer); + (self.compute_length() as u32).write_into(writer); self.language.write_into(writer); - self.num_groups.write_into(writer); + (array_len(&self.groups).unwrap() as u32).write_into(writer); self.groups.write_into(writer); } fn table_type(&self) -> TableType { @@ -1045,9 +988,7 @@ impl<'a> FromObjRef> for Cmap12 { fn from_obj_ref(obj: &read_fonts::tables::cmap::Cmap12<'a>, _: FontData) -> Self { let offset_data = obj.offset_data(); Cmap12 { - length: obj.length(), language: obj.language(), - num_groups: obj.num_groups(), groups: obj.groups().to_owned_obj(offset_data), } } diff --git a/write-fonts/src/tables/cmap.rs b/write-fonts/src/tables/cmap.rs index a993efe52..c20585997 100644 --- a/write-fonts/src/tables/cmap.rs +++ b/write-fonts/src/tables/cmap.rs @@ -16,19 +16,6 @@ const WINDOWS_FULL_REPERTOIRE_ENCODING: u16 = 10; const UNICODE_BMP_ENCODING: u16 = 3; const UNICODE_FULL_REPERTOIRE_ENCODING: u16 = 4; -fn size_of_cmap4(seg_count: u16, gid_count: u16) -> u16 { - // https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-4-segment-mapping-to-delta-values - 8 * 2 // 8 uint16's - + 2 * seg_count * 4 // 4 parallel arrays of len seg_count, 2 bytes per entry - + 2 * gid_count // 2 bytes per gid in glyphIdArray -} - -fn size_of_cmap12(num_groups: u32) -> u32 { - // https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-12-segmented-coverage - 2 * 2 + 3 * 4 // 2 uint16's and 3 uint32's - + num_groups * 3 * 4 // 3 unit32's per segment map group -} - impl CmapSubtable { /// Create a new format 4 `CmapSubtable` from a list of `(char, GlyphId)` pairs. /// @@ -97,17 +84,9 @@ impl CmapSubtable { "uneven parallel arrays, very bad. Very very bad." ); - let seg_count: u16 = start_code.len().try_into().unwrap(); - - 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, - computed.search_range, - computed.entry_selector, - computed.range_shift, end_code, start_code, id_deltas, @@ -148,15 +127,12 @@ impl CmapSubtable { } groups.push((start_char_code, last_char_code, start_glyph_id)); - let num_groups: u32 = groups.len().try_into().unwrap(); let seq_map_groups = groups .into_iter() .map(|(start_char, end_char, gid)| SequentialMapGroup::new(start_char, end_char, gid)) .collect::>(); CmapSubtable::format_12( - size_of_cmap12(num_groups), 0, // 'lang' set to zero for all 'cmap' subtables whose platform IDs are other than Macintosh - num_groups, seq_map_groups, ) } @@ -262,6 +238,46 @@ impl Cmap { } } +impl Cmap4 { + fn compute_length(&self) -> u16 { + // https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-4-segment-mapping-to-delta-values + // there are always 8 u16 fields + const FIXED_SIZE: usize = 8 * u16::RAW_BYTE_LEN; + const PER_SEGMENT_LEN: usize = 4 * u16::RAW_BYTE_LEN; + + let segment_len = self.end_code.len() * PER_SEGMENT_LEN; + let gid_len = self.glyph_id_array.len() * u16::RAW_BYTE_LEN; + + (FIXED_SIZE + segment_len + gid_len) + .try_into() + .expect("cmap4 overflow") + } + + fn compute_search_range(&self) -> u16 { + SearchRange::compute(self.end_code.len(), u16::RAW_BYTE_LEN).search_range + } + + fn compute_entry_selector(&self) -> u16 { + SearchRange::compute(self.end_code.len(), u16::RAW_BYTE_LEN).entry_selector + } + + fn compute_range_shift(&self) -> u16 { + SearchRange::compute(self.end_code.len(), u16::RAW_BYTE_LEN).range_shift + } +} + +impl Cmap12 { + fn compute_length(&self) -> u32 { + // https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-12-segmented-coverage + const FIXED_SIZE: usize = 2 * u16::RAW_BYTE_LEN + 3 * u32::RAW_BYTE_LEN; + const PER_SEGMENT_LEN: usize = 3 * u32::RAW_BYTE_LEN; + + (FIXED_SIZE + PER_SEGMENT_LEN * self.groups.len()) + .try_into() + .unwrap() + } +} + #[cfg(test)] mod tests { use font_types::GlyphId;