From 5128be220c9b0cdddf692949d9564ed62144ffd1 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Wed, 27 Sep 2023 16:18:07 -0400 Subject: [PATCH 1/7] [WIP] CBLC parsing --- read-fonts/generated/generated_cblc.rs | 903 +++++++++++++++++++++++++ read-fonts/src/table_provider.rs | 4 + read-fonts/src/tables.rs | 1 + read-fonts/src/tables/cblc.rs | 3 + resources/codegen_inputs/cblc.rs | 114 ++++ resources/codegen_plan.toml | 5 + 6 files changed, 1030 insertions(+) create mode 100644 read-fonts/generated/generated_cblc.rs create mode 100644 read-fonts/src/tables/cblc.rs create mode 100644 resources/codegen_inputs/cblc.rs diff --git a/read-fonts/generated/generated_cblc.rs b/read-fonts/generated/generated_cblc.rs new file mode 100644 index 000000000..af7dd283b --- /dev/null +++ b/read-fonts/generated/generated_cblc.rs @@ -0,0 +1,903 @@ +// THIS FILE IS AUTOGENERATED. +// Any changes to this file will be overwritten. +// For more information about how codegen works, see font-codegen/README.md + +#[allow(unused_imports)] +use crate::codegen_prelude::*; + +/// The [Color Bitmap Location](https://learn.microsoft.com/en-us/typography/opentype/spec/cblc) table +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub struct CblcMarker { + bitmap_sizes_byte_len: usize, +} + +impl CblcMarker { + fn major_version_byte_range(&self) -> Range { + let start = 0; + start..start + u16::RAW_BYTE_LEN + } + fn minor_version_byte_range(&self) -> Range { + let start = self.major_version_byte_range().end; + start..start + u16::RAW_BYTE_LEN + } + fn num_sizes_byte_range(&self) -> Range { + let start = self.minor_version_byte_range().end; + start..start + u32::RAW_BYTE_LEN + } + fn bitmap_sizes_byte_range(&self) -> Range { + let start = self.num_sizes_byte_range().end; + start..start + self.bitmap_sizes_byte_len + } +} + +impl TopLevelTable for Cblc<'_> { + /// `CBLC` + const TAG: Tag = Tag::new(b"CBLC"); +} + +impl<'a> FontRead<'a> for Cblc<'a> { + fn read(data: FontData<'a>) -> Result { + let mut cursor = data.cursor(); + cursor.advance::(); + cursor.advance::(); + let num_sizes: u32 = cursor.read()?; + let bitmap_sizes_byte_len = num_sizes as usize * BitmapSize::RAW_BYTE_LEN; + cursor.advance_by(bitmap_sizes_byte_len); + cursor.finish(CblcMarker { + bitmap_sizes_byte_len, + }) + } +} + +/// The [Color Bitmap Location](https://learn.microsoft.com/en-us/typography/opentype/spec/cblc) table +pub type Cblc<'a> = TableRef<'a, CblcMarker>; + +impl<'a> Cblc<'a> { + /// Major version of the CBLC table, = 3. + pub fn major_version(&self) -> u16 { + let range = self.shape.major_version_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Minor version of CBLC table, = 0. + pub fn minor_version(&self) -> u16 { + let range = self.shape.minor_version_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Number of BitmapSize records. + pub fn num_sizes(&self) -> u32 { + let range = self.shape.num_sizes_byte_range(); + self.data.read_at(range.start).unwrap() + } + + pub fn bitmap_sizes(&self) -> &'a [BitmapSize] { + let range = self.shape.bitmap_sizes_byte_range(); + self.data.read_array(range).unwrap() + } +} + +#[cfg(feature = "traversal")] +impl<'a> SomeTable<'a> for Cblc<'a> { + fn type_name(&self) -> &str { + "Cblc" + } + fn get_field(&self, idx: usize) -> Option> { + match idx { + 0usize => Some(Field::new("major_version", self.major_version())), + 1usize => Some(Field::new("minor_version", self.minor_version())), + 2usize => Some(Field::new("num_sizes", self.num_sizes())), + 3usize => Some(Field::new( + "bitmap_sizes", + traversal::FieldType::array_of_records( + stringify!(BitmapSize), + self.bitmap_sizes(), + self.offset_data(), + ), + )), + _ => None, + } + } +} + +#[cfg(feature = "traversal")] +impl<'a> std::fmt::Debug for Cblc<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (self as &dyn SomeTable<'a>).fmt(f) + } +} + +/// [BitmapSize](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmapsize-record) record. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct BitmapSize { + /// Offset to index subtable from beginning of EBLC/CBLC. + pub index_subtable_array_offset: BigEndian, + /// Number of bytes in corresponding index subtables and array. + pub index_tables_size: BigEndian, + /// There is an index subtable for each range or format change. + pub number_of_index_subtables: BigEndian, + /// Not used; set to 0. + pub color_ref: BigEndian, + /// Line metrics for text rendered horizontally. + pub hori: SbitLineMetrics, + /// Line metrics for text rendered vertically. + pub vert: SbitLineMetrics, + /// Lowest glyph index for this size. + pub start_glyph_index: BigEndian, + /// Highest glyph index for this size. + pub end_glyph_index: BigEndian, + /// Horizontal pixels per em. + pub ppem_x: u8, + /// Vertical pixels per em. + pub ppem_y: u8, + /// The Microsoft rasterizer v.1.7 or greater supports the following + /// bitDepth values, as described below: 1, 2, 4, and 8 (and 32 for CBLC). + pub bit_depth: u8, + /// Vertical or horizontal. + pub flags: BigEndian, +} + +impl BitmapSize { + /// Offset to index subtable from beginning of EBLC/CBLC. + pub fn index_subtable_array_offset(&self) -> u32 { + self.index_subtable_array_offset.get() + } + + /// Number of bytes in corresponding index subtables and array. + pub fn index_tables_size(&self) -> u32 { + self.index_tables_size.get() + } + + /// There is an index subtable for each range or format change. + pub fn number_of_index_subtables(&self) -> u32 { + self.number_of_index_subtables.get() + } + + /// Not used; set to 0. + pub fn color_ref(&self) -> u32 { + self.color_ref.get() + } + + /// Line metrics for text rendered horizontally. + pub fn hori(&self) -> &SbitLineMetrics { + &self.hori + } + + /// Line metrics for text rendered vertically. + pub fn vert(&self) -> &SbitLineMetrics { + &self.vert + } + + /// Lowest glyph index for this size. + pub fn start_glyph_index(&self) -> GlyphId { + self.start_glyph_index.get() + } + + /// Highest glyph index for this size. + pub fn end_glyph_index(&self) -> GlyphId { + self.end_glyph_index.get() + } + + /// Horizontal pixels per em. + pub fn ppem_x(&self) -> u8 { + self.ppem_x + } + + /// Vertical pixels per em. + pub fn ppem_y(&self) -> u8 { + self.ppem_y + } + + /// The Microsoft rasterizer v.1.7 or greater supports the following + /// bitDepth values, as described below: 1, 2, 4, and 8 (and 32 for CBLC). + pub fn bit_depth(&self) -> u8 { + self.bit_depth + } + + /// Vertical or horizontal. + pub fn flags(&self) -> BitmapFlags { + self.flags.get() + } +} + +#[cfg(feature = "traversal")] +impl<'a> SomeRecord<'a> for BitmapSize { + fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { + RecordResolver { + name: "BitmapSize", + get_field: Box::new(move |idx, _data| match idx { + 0usize => Some(Field::new( + "index_subtable_array_offset", + self.index_subtable_array_offset(), + )), + 1usize => Some(Field::new("index_tables_size", self.index_tables_size())), + 2usize => Some(Field::new( + "number_of_index_subtables", + self.number_of_index_subtables(), + )), + 3usize => Some(Field::new("color_ref", self.color_ref())), + 4usize => Some(compile_error!(concat!("another weird type: ", "hori"))), + 5usize => Some(compile_error!(concat!("another weird type: ", "vert"))), + 6usize => Some(Field::new("start_glyph_index", self.start_glyph_index())), + 7usize => Some(Field::new("end_glyph_index", self.end_glyph_index())), + 8usize => Some(Field::new("ppem_x", self.ppem_x())), + 9usize => Some(Field::new("ppem_y", self.ppem_y())), + 10usize => Some(Field::new("bit_depth", self.bit_depth())), + 11usize => Some(Field::new("flags", self.flags())), + _ => None, + }), + data, + } + } +} + +/// [SbitLineMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#sbitlinemetrics-record) record. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +#[repr(packed)] +pub struct SbitLineMetrics { + pub ascender: BigEndian, + pub descender: BigEndian, + pub width_max: u8, + pub caret_slope_numerator: BigEndian, + pub caret_slope_denominator: u8, + pub caret_offset: BigEndian, + pub min_origin_sb: BigEndian, + pub min_advance_sb: BigEndian, + pub max_before_bl: BigEndian, + pub min_after_bl: BigEndian, + pub pad1: BigEndian, + pub pad2: BigEndian, +} + +impl SbitLineMetrics { + pub fn ascender(&self) -> i8 { + self.ascender.get() + } + + pub fn descender(&self) -> i8 { + self.descender.get() + } + + pub fn width_max(&self) -> u8 { + self.width_max + } + + pub fn caret_slope_numerator(&self) -> i8 { + self.caret_slope_numerator.get() + } + + pub fn caret_slope_denominator(&self) -> u8 { + self.caret_slope_denominator + } + + pub fn caret_offset(&self) -> i8 { + self.caret_offset.get() + } + + pub fn min_origin_sb(&self) -> i8 { + self.min_origin_sb.get() + } + + pub fn min_advance_sb(&self) -> i8 { + self.min_advance_sb.get() + } + + pub fn max_before_bl(&self) -> i8 { + self.max_before_bl.get() + } + + pub fn min_after_bl(&self) -> i8 { + self.min_after_bl.get() + } + + pub fn pad1(&self) -> i8 { + self.pad1.get() + } + + pub fn pad2(&self) -> i8 { + self.pad2.get() + } +} + +impl FixedSize for SbitLineMetrics { + const RAW_BYTE_LEN: usize = i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN; +} + +impl sealed::Sealed for SbitLineMetrics {} + +/// SAFETY: see the [`FromBytes`] trait documentation. +unsafe impl FromBytes for SbitLineMetrics { + fn this_trait_should_only_be_implemented_in_generated_code() {} +} + +#[cfg(feature = "traversal")] +impl<'a> SomeRecord<'a> for SbitLineMetrics { + fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { + RecordResolver { + name: "SbitLineMetrics", + get_field: Box::new(move |idx, _data| match idx { + 0usize => Some(Field::new("ascender", self.ascender())), + 1usize => Some(Field::new("descender", self.descender())), + 2usize => Some(Field::new("width_max", self.width_max())), + 3usize => Some(Field::new( + "caret_slope_numerator", + self.caret_slope_numerator(), + )), + 4usize => Some(Field::new( + "caret_slope_denominator", + self.caret_slope_denominator(), + )), + 5usize => Some(Field::new("caret_offset", self.caret_offset())), + 6usize => Some(Field::new("min_origin_sb", self.min_origin_sb())), + 7usize => Some(Field::new("min_advance_sb", self.min_advance_sb())), + 8usize => Some(Field::new("max_before_bl", self.max_before_bl())), + 9usize => Some(Field::new("min_after_bl", self.min_after_bl())), + 10usize => Some(Field::new("pad1", self.pad1())), + 11usize => Some(Field::new("pad2", self.pad2())), + _ => None, + }), + data, + } + } +} + +/// [Bitmap flags](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmap-flags). +#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BitmapFlags { + bits: u8, +} + +impl BitmapFlags { + /// Horizontal + pub const HORIZONTAL_METRICS: Self = Self { bits: 0x01 }; + + /// Vertical + pub const VERTICAL_METRICS: Self = Self { bits: 0x02 }; +} + +impl BitmapFlags { + /// Returns an empty set of flags. + #[inline] + pub const fn empty() -> Self { + Self { bits: 0 } + } + + /// Returns the set containing all flags. + #[inline] + pub const fn all() -> Self { + Self { + bits: Self::HORIZONTAL_METRICS.bits | Self::VERTICAL_METRICS.bits, + } + } + + /// Returns the raw value of the flags currently stored. + #[inline] + pub const fn bits(&self) -> u8 { + self.bits + } + + /// Convert from underlying bit representation, unless that + /// representation contains bits that do not correspond to a flag. + #[inline] + pub const fn from_bits(bits: u8) -> Option { + if (bits & !Self::all().bits()) == 0 { + Some(Self { bits }) + } else { + None + } + } + + /// Convert from underlying bit representation, dropping any bits + /// that do not correspond to flags. + #[inline] + pub const fn from_bits_truncate(bits: u8) -> Self { + Self { + bits: bits & Self::all().bits, + } + } + + /// Returns `true` if no flags are currently stored. + #[inline] + pub const fn is_empty(&self) -> bool { + self.bits() == Self::empty().bits() + } + + /// Returns `true` if there are flags common to both `self` and `other`. + #[inline] + pub const fn intersects(&self, other: Self) -> bool { + !(Self { + bits: self.bits & other.bits, + }) + .is_empty() + } + + /// Returns `true` if all of the flags in `other` are contained within `self`. + #[inline] + pub const fn contains(&self, other: Self) -> bool { + (self.bits & other.bits) == other.bits + } + + /// Inserts the specified flags in-place. + #[inline] + pub fn insert(&mut self, other: Self) { + self.bits |= other.bits; + } + + /// Removes the specified flags in-place. + #[inline] + pub fn remove(&mut self, other: Self) { + self.bits &= !other.bits; + } + + /// Toggles the specified flags in-place. + #[inline] + pub fn toggle(&mut self, other: Self) { + self.bits ^= other.bits; + } + + /// Returns the intersection between the flags in `self` and + /// `other`. + /// + /// Specifically, the returned set contains only the flags which are + /// present in *both* `self` *and* `other`. + /// + /// This is equivalent to using the `&` operator (e.g. + /// [`ops::BitAnd`]), as in `flags & other`. + /// + /// [`ops::BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html + #[inline] + #[must_use] + pub const fn intersection(self, other: Self) -> Self { + Self { + bits: self.bits & other.bits, + } + } + + /// Returns the union of between the flags in `self` and `other`. + /// + /// Specifically, the returned set contains all flags which are + /// present in *either* `self` *or* `other`, including any which are + /// present in both. + /// + /// This is equivalent to using the `|` operator (e.g. + /// [`ops::BitOr`]), as in `flags | other`. + /// + /// [`ops::BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html + #[inline] + #[must_use] + pub const fn union(self, other: Self) -> Self { + Self { + bits: self.bits | other.bits, + } + } + + /// Returns the difference between the flags in `self` and `other`. + /// + /// Specifically, the returned set contains all flags present in + /// `self`, except for the ones present in `other`. + /// + /// It is also conceptually equivalent to the "bit-clear" operation: + /// `flags & !other` (and this syntax is also supported). + /// + /// This is equivalent to using the `-` operator (e.g. + /// [`ops::Sub`]), as in `flags - other`. + /// + /// [`ops::Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html + #[inline] + #[must_use] + pub const fn difference(self, other: Self) -> Self { + Self { + bits: self.bits & !other.bits, + } + } +} + +impl std::ops::BitOr for BitmapFlags { + type Output = Self; + + /// Returns the union of the two sets of flags. + #[inline] + fn bitor(self, other: BitmapFlags) -> Self { + Self { + bits: self.bits | other.bits, + } + } +} + +impl std::ops::BitOrAssign for BitmapFlags { + /// Adds the set of flags. + #[inline] + fn bitor_assign(&mut self, other: Self) { + self.bits |= other.bits; + } +} + +impl std::ops::BitXor for BitmapFlags { + type Output = Self; + + /// Returns the left flags, but with all the right flags toggled. + #[inline] + fn bitxor(self, other: Self) -> Self { + Self { + bits: self.bits ^ other.bits, + } + } +} + +impl std::ops::BitXorAssign for BitmapFlags { + /// Toggles the set of flags. + #[inline] + fn bitxor_assign(&mut self, other: Self) { + self.bits ^= other.bits; + } +} + +impl std::ops::BitAnd for BitmapFlags { + type Output = Self; + + /// Returns the intersection between the two sets of flags. + #[inline] + fn bitand(self, other: Self) -> Self { + Self { + bits: self.bits & other.bits, + } + } +} + +impl std::ops::BitAndAssign for BitmapFlags { + /// Disables all flags disabled in the set. + #[inline] + fn bitand_assign(&mut self, other: Self) { + self.bits &= other.bits; + } +} + +impl std::ops::Sub for BitmapFlags { + type Output = Self; + + /// Returns the set difference of the two sets of flags. + #[inline] + fn sub(self, other: Self) -> Self { + Self { + bits: self.bits & !other.bits, + } + } +} + +impl std::ops::SubAssign for BitmapFlags { + /// Disables all flags enabled in the set. + #[inline] + fn sub_assign(&mut self, other: Self) { + self.bits &= !other.bits; + } +} + +impl std::ops::Not for BitmapFlags { + type Output = Self; + + /// Returns the complement of this set of flags. + #[inline] + fn not(self) -> Self { + Self { bits: !self.bits } & Self::all() + } +} + +impl std::fmt::Debug for BitmapFlags { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let members: &[(&str, Self)] = &[ + ("HORIZONTAL_METRICS", Self::HORIZONTAL_METRICS), + ("VERTICAL_METRICS", Self::VERTICAL_METRICS), + ]; + let mut first = true; + for (name, value) in members { + if self.contains(*value) { + if !first { + f.write_str(" | ")?; + } + first = false; + f.write_str(name)?; + } + } + if first { + f.write_str("(empty)")?; + } + Ok(()) + } +} + +impl std::fmt::Binary for BitmapFlags { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Binary::fmt(&self.bits, f) + } +} + +impl std::fmt::Octal for BitmapFlags { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Octal::fmt(&self.bits, f) + } +} + +impl std::fmt::LowerHex for BitmapFlags { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::LowerHex::fmt(&self.bits, f) + } +} + +impl std::fmt::UpperHex for BitmapFlags { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::UpperHex::fmt(&self.bits, f) + } +} + +impl font_types::Scalar for BitmapFlags { + type Raw = ::Raw; + fn to_raw(self) -> Self::Raw { + self.bits().to_raw() + } + fn from_raw(raw: Self::Raw) -> Self { + let t = ::from_raw(raw); + Self::from_bits_truncate(t) + } +} + +#[cfg(feature = "traversal")] +impl<'a> From for FieldType<'a> { + fn from(src: BitmapFlags) -> FieldType<'a> { + src.bits().into() + } +} + +/// [BigGlyphMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bigglyphmetrics) record. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +#[repr(packed)] +pub struct BigGlyphMetrics { + /// Number of rows of data. + pub height: u8, + /// Number of columns of data. + pub width: u8, + /// Distance in pixels from the horizontal origin to the left edge of the bitmap. + pub hori_bearing_x: BigEndian, + /// Distance in pixels from the horizontal origin to the top edge of the bitmap. + pub hori_bearing_y: BigEndian, + /// Horizontal advance width in pixels. + pub hori_advance: u8, + /// Distance in pixels from the vertical origin to the left edge of the bitmap. + pub vert_bearing_x: BigEndian, + /// Distance in pixels from the vertical origin to the top edge of the bitmap. + pub vert_bearing_y: BigEndian, + /// Vertical advance width in pixels. + pub vert_advance: u8, +} + +impl BigGlyphMetrics { + /// Number of rows of data. + pub fn height(&self) -> u8 { + self.height + } + + /// Number of columns of data. + pub fn width(&self) -> u8 { + self.width + } + + /// Distance in pixels from the horizontal origin to the left edge of the bitmap. + pub fn hori_bearing_x(&self) -> i8 { + self.hori_bearing_x.get() + } + + /// Distance in pixels from the horizontal origin to the top edge of the bitmap. + pub fn hori_bearing_y(&self) -> i8 { + self.hori_bearing_y.get() + } + + /// Horizontal advance width in pixels. + pub fn hori_advance(&self) -> u8 { + self.hori_advance + } + + /// Distance in pixels from the vertical origin to the left edge of the bitmap. + pub fn vert_bearing_x(&self) -> i8 { + self.vert_bearing_x.get() + } + + /// Distance in pixels from the vertical origin to the top edge of the bitmap. + pub fn vert_bearing_y(&self) -> i8 { + self.vert_bearing_y.get() + } + + /// Vertical advance width in pixels. + pub fn vert_advance(&self) -> u8 { + self.vert_advance + } +} + +impl FixedSize for BigGlyphMetrics { + const RAW_BYTE_LEN: usize = u8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN; +} + +impl sealed::Sealed for BigGlyphMetrics {} + +/// SAFETY: see the [`FromBytes`] trait documentation. +unsafe impl FromBytes for BigGlyphMetrics { + fn this_trait_should_only_be_implemented_in_generated_code() {} +} + +#[cfg(feature = "traversal")] +impl<'a> SomeRecord<'a> for BigGlyphMetrics { + fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { + RecordResolver { + name: "BigGlyphMetrics", + get_field: Box::new(move |idx, _data| match idx { + 0usize => Some(Field::new("height", self.height())), + 1usize => Some(Field::new("width", self.width())), + 2usize => Some(Field::new("hori_bearing_x", self.hori_bearing_x())), + 3usize => Some(Field::new("hori_bearing_y", self.hori_bearing_y())), + 4usize => Some(Field::new("hori_advance", self.hori_advance())), + 5usize => Some(Field::new("vert_bearing_x", self.vert_bearing_x())), + 6usize => Some(Field::new("vert_bearing_y", self.vert_bearing_y())), + 7usize => Some(Field::new("vert_advance", self.vert_advance())), + _ => None, + }), + data, + } + } +} + +/// [SmallGlyphMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#smallglyphmetrics) record. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +#[repr(packed)] +pub struct SmallGlyphMetrics { + /// Number of rows of data. + pub height: u8, + /// Number of columns of data. + pub width: u8, + /// Distance in pixels from the horizontal origin to the left edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the top edge of the bitmap (for vertical text). + pub bearing_x: BigEndian, + /// Distance in pixels from the horizontal origin to the top edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the left edge of the bitmap (for vertical text). + pub bearing_y: BigEndian, + /// Horizontal or vertical advance width in pixels. + pub advance: u8, +} + +impl SmallGlyphMetrics { + /// Number of rows of data. + pub fn height(&self) -> u8 { + self.height + } + + /// Number of columns of data. + pub fn width(&self) -> u8 { + self.width + } + + /// Distance in pixels from the horizontal origin to the left edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the top edge of the bitmap (for vertical text). + pub fn bearing_x(&self) -> i8 { + self.bearing_x.get() + } + + /// Distance in pixels from the horizontal origin to the top edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the left edge of the bitmap (for vertical text). + pub fn bearing_y(&self) -> i8 { + self.bearing_y.get() + } + + /// Horizontal or vertical advance width in pixels. + pub fn advance(&self) -> u8 { + self.advance + } +} + +impl FixedSize for SmallGlyphMetrics { + const RAW_BYTE_LEN: usize = u8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN; +} + +impl sealed::Sealed for SmallGlyphMetrics {} + +/// SAFETY: see the [`FromBytes`] trait documentation. +unsafe impl FromBytes for SmallGlyphMetrics { + fn this_trait_should_only_be_implemented_in_generated_code() {} +} + +#[cfg(feature = "traversal")] +impl<'a> SomeRecord<'a> for SmallGlyphMetrics { + fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { + RecordResolver { + name: "SmallGlyphMetrics", + get_field: Box::new(move |idx, _data| match idx { + 0usize => Some(Field::new("height", self.height())), + 1usize => Some(Field::new("width", self.width())), + 2usize => Some(Field::new("bearing_x", self.bearing_x())), + 3usize => Some(Field::new("bearing_y", self.bearing_y())), + 4usize => Some(Field::new("advance", self.advance())), + _ => None, + }), + data, + } + } +} + +/// [IndexSubtableArray](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtablearray) record. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +#[repr(packed)] +pub struct IndexSubtableArray { + /// First glyph ID of this range. + pub first_glyph_index: BigEndian, + /// Last glyph ID of this range (inclusive). + pub last_glyph_index: BigEndian, + /// Add to indexSubTableArrayOffset to get offset from beginning of EBLC. + pub additional_offset_to_index_subtable: BigEndian, +} + +impl IndexSubtableArray { + /// First glyph ID of this range. + pub fn first_glyph_index(&self) -> GlyphId { + self.first_glyph_index.get() + } + + /// Last glyph ID of this range (inclusive). + pub fn last_glyph_index(&self) -> GlyphId { + self.last_glyph_index.get() + } + + /// Add to indexSubTableArrayOffset to get offset from beginning of EBLC. + pub fn additional_offset_to_index_subtable(&self) -> u32 { + self.additional_offset_to_index_subtable.get() + } +} + +impl FixedSize for IndexSubtableArray { + const RAW_BYTE_LEN: usize = GlyphId::RAW_BYTE_LEN + GlyphId::RAW_BYTE_LEN + u32::RAW_BYTE_LEN; +} + +impl sealed::Sealed for IndexSubtableArray {} + +/// SAFETY: see the [`FromBytes`] trait documentation. +unsafe impl FromBytes for IndexSubtableArray { + fn this_trait_should_only_be_implemented_in_generated_code() {} +} + +#[cfg(feature = "traversal")] +impl<'a> SomeRecord<'a> for IndexSubtableArray { + fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { + RecordResolver { + name: "IndexSubtableArray", + get_field: Box::new(move |idx, _data| match idx { + 0usize => Some(Field::new("first_glyph_index", self.first_glyph_index())), + 1usize => Some(Field::new("last_glyph_index", self.last_glyph_index())), + 2usize => Some(Field::new( + "additional_offset_to_index_subtable", + self.additional_offset_to_index_subtable(), + )), + _ => None, + }), + data, + } + } +} diff --git a/read-fonts/src/table_provider.rs b/read-fonts/src/table_provider.rs index c136b4658..4a4919b93 100644 --- a/read-fonts/src/table_provider.rs +++ b/read-fonts/src/table_provider.rs @@ -138,6 +138,10 @@ pub trait TableProvider<'a> { self.expect_table() } + fn cblc(&self) -> Result, ReadError> { + self.expect_table() + } + fn sbix(&self) -> Result, ReadError> { // should we make the user pass this in? let num_glyphs = self.maxp().map(|maxp| maxp.num_glyphs())?; diff --git a/read-fonts/src/tables.rs b/read-fonts/src/tables.rs index ad4f53fa4..7dde4ea74 100644 --- a/read-fonts/src/tables.rs +++ b/read-fonts/src/tables.rs @@ -2,6 +2,7 @@ pub mod avar; pub mod base; +pub mod cblc; pub mod cff; pub mod cff2; pub mod cmap; diff --git a/read-fonts/src/tables/cblc.rs b/read-fonts/src/tables/cblc.rs new file mode 100644 index 000000000..739d49db7 --- /dev/null +++ b/read-fonts/src/tables/cblc.rs @@ -0,0 +1,3 @@ +//! The [CBLC (Color Bitmap Location)](https://docs.microsoft.com/en-us/typography/opentype/spec/cblc) table + +include!("../../generated/generated_cblc.rs"); diff --git a/resources/codegen_inputs/cblc.rs b/resources/codegen_inputs/cblc.rs new file mode 100644 index 000000000..755fa8522 --- /dev/null +++ b/resources/codegen_inputs/cblc.rs @@ -0,0 +1,114 @@ +#![parse_module(read_fonts::tables::cblc)] + +/// The [Color Bitmap Location](https://learn.microsoft.com/en-us/typography/opentype/spec/cblc) table +#[tag = "CBLC"] +table Cblc { + /// Major version of the CBLC table, = 3. + #[compile(3)] + major_version: u16, + /// Minor version of CBLC table, = 0. + #[compile(0)] + minor_version: u16, + /// Number of BitmapSize records. + #[compile(array_len($bitmap_sizes))] + num_sizes: u32, + #[count($num_sizes)] + bitmap_sizes: [BitmapSize], +} + +/// [BitmapSize](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmapsize-record) record. +record BitmapSize { + /// Offset to index subtable from beginning of EBLC/CBLC. + index_subtable_array_offset: u32, + /// Number of bytes in corresponding index subtables and array. + index_tables_size: u32, + /// There is an index subtable for each range or format change. + number_of_index_subtables: u32, + /// Not used; set to 0. + color_ref: u32, + /// Line metrics for text rendered horizontally. + hori: SbitLineMetrics, + /// Line metrics for text rendered vertically. + vert: SbitLineMetrics, + /// Lowest glyph index for this size. + start_glyph_index: GlyphId, + /// Highest glyph index for this size. + end_glyph_index: GlyphId, + /// Horizontal pixels per em. + ppem_x: u8, + /// Vertical pixels per em. + ppem_y: u8, + /// The Microsoft rasterizer v.1.7 or greater supports the following + /// bitDepth values, as described below: 1, 2, 4, and 8 (and 32 for CBLC). + bit_depth: u8, + /// Vertical or horizontal. + flags: BitmapFlags, +} + +/// [SbitLineMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#sbitlinemetrics-record) record. +record SbitLineMetrics { + ascender: i8, + descender: i8, + width_max: u8, + caret_slope_numerator: i8, + caret_slope_denominator: u8, + caret_offset: i8, + min_origin_sb: i8, + min_advance_sb: i8, + max_before_bl: i8, + min_after_bl: i8, + pad1: i8, + pad2: i8, +} + +/// [Bitmap flags](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmap-flags). +flags u8 BitmapFlags { + /// Horizontal + HORIZONTAL_METRICS = 0x01, + /// Vertical + VERTICAL_METRICS = 0x02, +} + +/// [BigGlyphMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bigglyphmetrics) record. +record BigGlyphMetrics { + /// Number of rows of data. + height: u8, + /// Number of columns of data. + width: u8, + /// Distance in pixels from the horizontal origin to the left edge of the bitmap. + hori_bearing_x: i8, + /// Distance in pixels from the horizontal origin to the top edge of the bitmap. + hori_bearing_y: i8, + /// Horizontal advance width in pixels. + hori_advance: u8, + /// Distance in pixels from the vertical origin to the left edge of the bitmap. + vert_bearing_x: i8, + /// Distance in pixels from the vertical origin to the top edge of the bitmap. + vert_bearing_y: i8, + /// Vertical advance width in pixels. + vert_advance: u8, +} + +/// [SmallGlyphMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#smallglyphmetrics) record. +record SmallGlyphMetrics { + /// Number of rows of data. + height: u8, + /// Number of columns of data. + width: u8, + /// Distance in pixels from the horizontal origin to the left edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the top edge of the bitmap (for vertical text). + bearing_x: i8, + /// Distance in pixels from the horizontal origin to the top edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the left edge of the bitmap (for vertical text). + bearing_y: i8, + /// Horizontal or vertical advance width in pixels. + advance: u8, +} + +/// [IndexSubtableArray](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtablearray) record. +record IndexSubtableArray { + /// First glyph ID of this range. + first_glyph_index: GlyphId, + /// Last glyph ID of this range (inclusive). + last_glyph_index: GlyphId, + /// Add to indexSubTableArrayOffset to get offset from beginning of EBLC. + additional_offset_to_index_subtable: u32, +} diff --git a/resources/codegen_plan.toml b/resources/codegen_plan.toml index a93f6d7ed..b558c46d9 100644 --- a/resources/codegen_plan.toml +++ b/resources/codegen_plan.toml @@ -298,6 +298,11 @@ mode = "compile" source = "resources/codegen_inputs/sbix.rs" target = "write-fonts/generated/generated_sbix.rs" +[[generate]] +mode = "parse" +source = "resources/codegen_inputs/cblc.rs" +target = "read-fonts/generated/generated_cblc.rs" + # modules just used for testing [[generate]] mode = "parse" From 7098368f61c1730c978d28b3da64fb2e70db03ec Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Fri, 29 Sep 2023 12:20:27 -0400 Subject: [PATCH 2/7] some codegen hacks for SbitLineMetrics --- font-codegen/src/fields.rs | 10 ++++++++- font-codegen/src/record.rs | 9 ++++++-- read-fonts/generated/generated_cblc.rs | 30 +++++++++++++++++++++++--- read-fonts/src/tables/cblc.rs | 11 ++++++++++ 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/font-codegen/src/fields.rs b/font-codegen/src/fields.rs index b7467a567..bd088f98f 100644 --- a/font-codegen/src/fields.rs +++ b/font-codegen/src/fields.rs @@ -403,7 +403,7 @@ fn traversal_arm_for_field( quote!(Field::new(#name_str, traversal::FieldType::var_array(#typ_str, self.#name()#maybe_unwrap, #data))) } // HACK: who wouldn't want to hard-code ValueRecord handling - FieldType::Struct { typ } if typ == "ValueRecord" => { + FieldType::Struct { typ } if typ == "ValueRecord" || typ == "SbitLineMetrics" => { let offset_data = pass_data .cloned() .unwrap_or_else(|| fld.offset_getter_data_src()); @@ -476,6 +476,14 @@ impl Field { } pub(crate) fn is_zerocopy_compatible(&self) -> bool { + match &self.typ { + FieldType::Struct { typ: name } => { + if name == "SbitLineMetrics" { + return true; + } + } + _ => {} + } matches!( self.typ, FieldType::Scalar { .. } | FieldType::Offset { .. } diff --git a/font-codegen/src/record.rs b/font-codegen/src/record.rs index 273b02122..2c4a8e1d0 100644 --- a/font-codegen/src/record.rs +++ b/font-codegen/src/record.rs @@ -52,10 +52,15 @@ pub(crate) fn generate(item: &Record, all_items: &Items) -> syn::Result std::fmt::Debug for Cblc<'a> { /// [BitmapSize](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmapsize-record) record. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +#[repr(packed)] pub struct BitmapSize { /// Offset to index subtable from beginning of EBLC/CBLC. pub index_subtable_array_offset: BigEndian, @@ -201,6 +203,28 @@ impl BitmapSize { } } +impl FixedSize for BitmapSize { + const RAW_BYTE_LEN: usize = u32::RAW_BYTE_LEN + + u32::RAW_BYTE_LEN + + u32::RAW_BYTE_LEN + + u32::RAW_BYTE_LEN + + SbitLineMetrics::RAW_BYTE_LEN + + SbitLineMetrics::RAW_BYTE_LEN + + GlyphId::RAW_BYTE_LEN + + GlyphId::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + BitmapFlags::RAW_BYTE_LEN; +} + +impl sealed::Sealed for BitmapSize {} + +/// SAFETY: see the [`FromBytes`] trait documentation. +unsafe impl FromBytes for BitmapSize { + fn this_trait_should_only_be_implemented_in_generated_code() {} +} + #[cfg(feature = "traversal")] impl<'a> SomeRecord<'a> for BitmapSize { fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { @@ -217,8 +241,8 @@ impl<'a> SomeRecord<'a> for BitmapSize { self.number_of_index_subtables(), )), 3usize => Some(Field::new("color_ref", self.color_ref())), - 4usize => Some(compile_error!(concat!("another weird type: ", "hori"))), - 5usize => Some(compile_error!(concat!("another weird type: ", "vert"))), + 4usize => Some(Field::new("hori", self.hori().traversal_type(_data))), + 5usize => Some(Field::new("vert", self.vert().traversal_type(_data))), 6usize => Some(Field::new("start_glyph_index", self.start_glyph_index())), 7usize => Some(Field::new("end_glyph_index", self.end_glyph_index())), 8usize => Some(Field::new("ppem_x", self.ppem_x())), @@ -233,7 +257,7 @@ impl<'a> SomeRecord<'a> for BitmapSize { } /// [SbitLineMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#sbitlinemetrics-record) record. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(C)] #[repr(packed)] pub struct SbitLineMetrics { diff --git a/read-fonts/src/tables/cblc.rs b/read-fonts/src/tables/cblc.rs index 739d49db7..956f75723 100644 --- a/read-fonts/src/tables/cblc.rs +++ b/read-fonts/src/tables/cblc.rs @@ -1,3 +1,14 @@ //! The [CBLC (Color Bitmap Location)](https://docs.microsoft.com/en-us/typography/opentype/spec/cblc) table include!("../../generated/generated_cblc.rs"); + +#[cfg(feature = "traversal")] +impl SbitLineMetrics { + pub(crate) fn traversal_type<'a>(&self, data: FontData<'a>) -> FieldType<'a> { + FieldType::Record(self.clone().traverse(data)) + } + + pub(crate) fn get_field<'a>(&self, idx: usize, data: FontData<'a>) -> Option> { + None + } +} From 10c01840c53d5acd6ea99fcbb68061b9a4b62ed1 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Tue, 3 Oct 2023 23:33:19 -0400 Subject: [PATCH 3/7] read location info and the png data formats also reorganize common code and add EBLC/EBDT --- font-codegen/src/fields.rs | 21 +- read-fonts/generated/generated_bitmap.rs | 1507 ++++++++++++++++++++++ read-fonts/generated/generated_cbdt.rs | 74 ++ read-fonts/generated/generated_cblc.rs | 819 +----------- read-fonts/generated/generated_ebdt.rs | 74 ++ read-fonts/generated/generated_eblc.rs | 110 ++ read-fonts/src/table_provider.rs | 12 + read-fonts/src/tables.rs | 4 + read-fonts/src/tables/bitmap.rs | 192 +++ read-fonts/src/tables/cbdt.rs | 11 + read-fonts/src/tables/cblc.rs | 13 +- read-fonts/src/tables/ebdt.rs | 11 + read-fonts/src/tables/eblc.rs | 5 + resources/codegen_inputs/bitmap.rs | 194 +++ resources/codegen_inputs/cbdt.rs | 12 + resources/codegen_inputs/cblc.rs | 100 +- resources/codegen_inputs/ebdt.rs | 12 + resources/codegen_inputs/eblc.rs | 20 + resources/codegen_plan.toml | 20 + 19 files changed, 2273 insertions(+), 938 deletions(-) create mode 100644 read-fonts/generated/generated_bitmap.rs create mode 100644 read-fonts/generated/generated_cbdt.rs create mode 100644 read-fonts/generated/generated_ebdt.rs create mode 100644 read-fonts/generated/generated_eblc.rs create mode 100644 read-fonts/src/tables/bitmap.rs create mode 100644 read-fonts/src/tables/cbdt.rs create mode 100644 read-fonts/src/tables/ebdt.rs create mode 100644 read-fonts/src/tables/eblc.rs create mode 100644 resources/codegen_inputs/bitmap.rs create mode 100644 resources/codegen_inputs/cbdt.rs create mode 100644 resources/codegen_inputs/ebdt.rs create mode 100644 resources/codegen_inputs/eblc.rs diff --git a/font-codegen/src/fields.rs b/font-codegen/src/fields.rs index bd088f98f..6c3c7d82f 100644 --- a/font-codegen/src/fields.rs +++ b/font-codegen/src/fields.rs @@ -476,18 +476,15 @@ impl Field { } pub(crate) fn is_zerocopy_compatible(&self) -> bool { - match &self.typ { - FieldType::Struct { typ: name } => { - if name == "SbitLineMetrics" { - return true; - } - } - _ => {} - } - matches!( - self.typ, - FieldType::Scalar { .. } | FieldType::Offset { .. } - ) + // hack: we want to add `FieldType::Struct` here but don't want to + // catch `ValueRecord` so use this attribute to ignore it. + // Fields that require args for reading can't be read "zerocopy" + // anyway. + self.attrs.read_with_args.is_none() + && matches!( + self.typ, + FieldType::Scalar { .. } | FieldType::Offset { .. } | FieldType::Struct { .. } + ) } pub(crate) fn is_array(&self) -> bool { diff --git a/read-fonts/generated/generated_bitmap.rs b/read-fonts/generated/generated_bitmap.rs new file mode 100644 index 000000000..5a0165064 --- /dev/null +++ b/read-fonts/generated/generated_bitmap.rs @@ -0,0 +1,1507 @@ +// THIS FILE IS AUTOGENERATED. +// Any changes to this file will be overwritten. +// For more information about how codegen works, see font-codegen/README.md + +#[allow(unused_imports)] +use crate::codegen_prelude::*; + +/// [BitmapSize](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmapsize-record) record. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +#[repr(packed)] +pub struct BitmapSize { + /// Offset to index subtable from beginning of EBLC/CBLC. + pub index_subtable_array_offset: BigEndian, + /// Number of bytes in corresponding index subtables and array. + pub index_tables_size: BigEndian, + /// There is an index subtable for each range or format change. + pub number_of_index_subtables: BigEndian, + /// Not used; set to 0. + pub color_ref: BigEndian, + /// Line metrics for text rendered horizontally. + pub hori: SbitLineMetrics, + /// Line metrics for text rendered vertically. + pub vert: SbitLineMetrics, + /// Lowest glyph index for this size. + pub start_glyph_index: BigEndian, + /// Highest glyph index for this size. + pub end_glyph_index: BigEndian, + /// Horizontal pixels per em. + pub ppem_x: u8, + /// Vertical pixels per em. + pub ppem_y: u8, + /// The Microsoft rasterizer v.1.7 or greater supports the following + /// bitDepth values, as described below: 1, 2, 4, and 8 (and 32 for CBLC). + pub bit_depth: u8, + /// Vertical or horizontal. + pub flags: BigEndian, +} + +impl BitmapSize { + /// Offset to index subtable from beginning of EBLC/CBLC. + pub fn index_subtable_array_offset(&self) -> u32 { + self.index_subtable_array_offset.get() + } + + /// Number of bytes in corresponding index subtables and array. + pub fn index_tables_size(&self) -> u32 { + self.index_tables_size.get() + } + + /// There is an index subtable for each range or format change. + pub fn number_of_index_subtables(&self) -> u32 { + self.number_of_index_subtables.get() + } + + /// Not used; set to 0. + pub fn color_ref(&self) -> u32 { + self.color_ref.get() + } + + /// Line metrics for text rendered horizontally. + pub fn hori(&self) -> &SbitLineMetrics { + &self.hori + } + + /// Line metrics for text rendered vertically. + pub fn vert(&self) -> &SbitLineMetrics { + &self.vert + } + + /// Lowest glyph index for this size. + pub fn start_glyph_index(&self) -> GlyphId { + self.start_glyph_index.get() + } + + /// Highest glyph index for this size. + pub fn end_glyph_index(&self) -> GlyphId { + self.end_glyph_index.get() + } + + /// Horizontal pixels per em. + pub fn ppem_x(&self) -> u8 { + self.ppem_x + } + + /// Vertical pixels per em. + pub fn ppem_y(&self) -> u8 { + self.ppem_y + } + + /// The Microsoft rasterizer v.1.7 or greater supports the following + /// bitDepth values, as described below: 1, 2, 4, and 8 (and 32 for CBLC). + pub fn bit_depth(&self) -> u8 { + self.bit_depth + } + + /// Vertical or horizontal. + pub fn flags(&self) -> BitmapFlags { + self.flags.get() + } +} + +impl FixedSize for BitmapSize { + const RAW_BYTE_LEN: usize = u32::RAW_BYTE_LEN + + u32::RAW_BYTE_LEN + + u32::RAW_BYTE_LEN + + u32::RAW_BYTE_LEN + + SbitLineMetrics::RAW_BYTE_LEN + + SbitLineMetrics::RAW_BYTE_LEN + + GlyphId::RAW_BYTE_LEN + + GlyphId::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + BitmapFlags::RAW_BYTE_LEN; +} + +impl sealed::Sealed for BitmapSize {} + +/// SAFETY: see the [`FromBytes`] trait documentation. +unsafe impl FromBytes for BitmapSize { + fn this_trait_should_only_be_implemented_in_generated_code() {} +} + +#[cfg(feature = "traversal")] +impl<'a> SomeRecord<'a> for BitmapSize { + fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { + RecordResolver { + name: "BitmapSize", + get_field: Box::new(move |idx, _data| match idx { + 0usize => Some(Field::new( + "index_subtable_array_offset", + self.index_subtable_array_offset(), + )), + 1usize => Some(Field::new("index_tables_size", self.index_tables_size())), + 2usize => Some(Field::new( + "number_of_index_subtables", + self.number_of_index_subtables(), + )), + 3usize => Some(Field::new("color_ref", self.color_ref())), + 4usize => Some(Field::new("hori", self.hori().traversal_type(_data))), + 5usize => Some(Field::new("vert", self.vert().traversal_type(_data))), + 6usize => Some(Field::new("start_glyph_index", self.start_glyph_index())), + 7usize => Some(Field::new("end_glyph_index", self.end_glyph_index())), + 8usize => Some(Field::new("ppem_x", self.ppem_x())), + 9usize => Some(Field::new("ppem_y", self.ppem_y())), + 10usize => Some(Field::new("bit_depth", self.bit_depth())), + 11usize => Some(Field::new("flags", self.flags())), + _ => None, + }), + data, + } + } +} + +/// [SbitLineMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#sbitlinemetrics-record) record. +#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +#[repr(packed)] +pub struct SbitLineMetrics { + pub ascender: BigEndian, + pub descender: BigEndian, + pub width_max: u8, + pub caret_slope_numerator: BigEndian, + pub caret_slope_denominator: u8, + pub caret_offset: BigEndian, + pub min_origin_sb: BigEndian, + pub min_advance_sb: BigEndian, + pub max_before_bl: BigEndian, + pub min_after_bl: BigEndian, + pub pad1: BigEndian, + pub pad2: BigEndian, +} + +impl SbitLineMetrics { + pub fn ascender(&self) -> i8 { + self.ascender.get() + } + + pub fn descender(&self) -> i8 { + self.descender.get() + } + + pub fn width_max(&self) -> u8 { + self.width_max + } + + pub fn caret_slope_numerator(&self) -> i8 { + self.caret_slope_numerator.get() + } + + pub fn caret_slope_denominator(&self) -> u8 { + self.caret_slope_denominator + } + + pub fn caret_offset(&self) -> i8 { + self.caret_offset.get() + } + + pub fn min_origin_sb(&self) -> i8 { + self.min_origin_sb.get() + } + + pub fn min_advance_sb(&self) -> i8 { + self.min_advance_sb.get() + } + + pub fn max_before_bl(&self) -> i8 { + self.max_before_bl.get() + } + + pub fn min_after_bl(&self) -> i8 { + self.min_after_bl.get() + } + + pub fn pad1(&self) -> i8 { + self.pad1.get() + } + + pub fn pad2(&self) -> i8 { + self.pad2.get() + } +} + +impl FixedSize for SbitLineMetrics { + const RAW_BYTE_LEN: usize = i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN; +} + +impl sealed::Sealed for SbitLineMetrics {} + +/// SAFETY: see the [`FromBytes`] trait documentation. +unsafe impl FromBytes for SbitLineMetrics { + fn this_trait_should_only_be_implemented_in_generated_code() {} +} + +#[cfg(feature = "traversal")] +impl<'a> SomeRecord<'a> for SbitLineMetrics { + fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { + RecordResolver { + name: "SbitLineMetrics", + get_field: Box::new(move |idx, _data| match idx { + 0usize => Some(Field::new("ascender", self.ascender())), + 1usize => Some(Field::new("descender", self.descender())), + 2usize => Some(Field::new("width_max", self.width_max())), + 3usize => Some(Field::new( + "caret_slope_numerator", + self.caret_slope_numerator(), + )), + 4usize => Some(Field::new( + "caret_slope_denominator", + self.caret_slope_denominator(), + )), + 5usize => Some(Field::new("caret_offset", self.caret_offset())), + 6usize => Some(Field::new("min_origin_sb", self.min_origin_sb())), + 7usize => Some(Field::new("min_advance_sb", self.min_advance_sb())), + 8usize => Some(Field::new("max_before_bl", self.max_before_bl())), + 9usize => Some(Field::new("min_after_bl", self.min_after_bl())), + 10usize => Some(Field::new("pad1", self.pad1())), + 11usize => Some(Field::new("pad2", self.pad2())), + _ => None, + }), + data, + } + } +} + +/// [Bitmap flags](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmap-flags). +#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BitmapFlags { + bits: u8, +} + +impl BitmapFlags { + /// Horizontal + pub const HORIZONTAL_METRICS: Self = Self { bits: 0x01 }; + + /// Vertical + pub const VERTICAL_METRICS: Self = Self { bits: 0x02 }; +} + +impl BitmapFlags { + /// Returns an empty set of flags. + #[inline] + pub const fn empty() -> Self { + Self { bits: 0 } + } + + /// Returns the set containing all flags. + #[inline] + pub const fn all() -> Self { + Self { + bits: Self::HORIZONTAL_METRICS.bits | Self::VERTICAL_METRICS.bits, + } + } + + /// Returns the raw value of the flags currently stored. + #[inline] + pub const fn bits(&self) -> u8 { + self.bits + } + + /// Convert from underlying bit representation, unless that + /// representation contains bits that do not correspond to a flag. + #[inline] + pub const fn from_bits(bits: u8) -> Option { + if (bits & !Self::all().bits()) == 0 { + Some(Self { bits }) + } else { + None + } + } + + /// Convert from underlying bit representation, dropping any bits + /// that do not correspond to flags. + #[inline] + pub const fn from_bits_truncate(bits: u8) -> Self { + Self { + bits: bits & Self::all().bits, + } + } + + /// Returns `true` if no flags are currently stored. + #[inline] + pub const fn is_empty(&self) -> bool { + self.bits() == Self::empty().bits() + } + + /// Returns `true` if there are flags common to both `self` and `other`. + #[inline] + pub const fn intersects(&self, other: Self) -> bool { + !(Self { + bits: self.bits & other.bits, + }) + .is_empty() + } + + /// Returns `true` if all of the flags in `other` are contained within `self`. + #[inline] + pub const fn contains(&self, other: Self) -> bool { + (self.bits & other.bits) == other.bits + } + + /// Inserts the specified flags in-place. + #[inline] + pub fn insert(&mut self, other: Self) { + self.bits |= other.bits; + } + + /// Removes the specified flags in-place. + #[inline] + pub fn remove(&mut self, other: Self) { + self.bits &= !other.bits; + } + + /// Toggles the specified flags in-place. + #[inline] + pub fn toggle(&mut self, other: Self) { + self.bits ^= other.bits; + } + + /// Returns the intersection between the flags in `self` and + /// `other`. + /// + /// Specifically, the returned set contains only the flags which are + /// present in *both* `self` *and* `other`. + /// + /// This is equivalent to using the `&` operator (e.g. + /// [`ops::BitAnd`]), as in `flags & other`. + /// + /// [`ops::BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html + #[inline] + #[must_use] + pub const fn intersection(self, other: Self) -> Self { + Self { + bits: self.bits & other.bits, + } + } + + /// Returns the union of between the flags in `self` and `other`. + /// + /// Specifically, the returned set contains all flags which are + /// present in *either* `self` *or* `other`, including any which are + /// present in both. + /// + /// This is equivalent to using the `|` operator (e.g. + /// [`ops::BitOr`]), as in `flags | other`. + /// + /// [`ops::BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html + #[inline] + #[must_use] + pub const fn union(self, other: Self) -> Self { + Self { + bits: self.bits | other.bits, + } + } + + /// Returns the difference between the flags in `self` and `other`. + /// + /// Specifically, the returned set contains all flags present in + /// `self`, except for the ones present in `other`. + /// + /// It is also conceptually equivalent to the "bit-clear" operation: + /// `flags & !other` (and this syntax is also supported). + /// + /// This is equivalent to using the `-` operator (e.g. + /// [`ops::Sub`]), as in `flags - other`. + /// + /// [`ops::Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html + #[inline] + #[must_use] + pub const fn difference(self, other: Self) -> Self { + Self { + bits: self.bits & !other.bits, + } + } +} + +impl std::ops::BitOr for BitmapFlags { + type Output = Self; + + /// Returns the union of the two sets of flags. + #[inline] + fn bitor(self, other: BitmapFlags) -> Self { + Self { + bits: self.bits | other.bits, + } + } +} + +impl std::ops::BitOrAssign for BitmapFlags { + /// Adds the set of flags. + #[inline] + fn bitor_assign(&mut self, other: Self) { + self.bits |= other.bits; + } +} + +impl std::ops::BitXor for BitmapFlags { + type Output = Self; + + /// Returns the left flags, but with all the right flags toggled. + #[inline] + fn bitxor(self, other: Self) -> Self { + Self { + bits: self.bits ^ other.bits, + } + } +} + +impl std::ops::BitXorAssign for BitmapFlags { + /// Toggles the set of flags. + #[inline] + fn bitxor_assign(&mut self, other: Self) { + self.bits ^= other.bits; + } +} + +impl std::ops::BitAnd for BitmapFlags { + type Output = Self; + + /// Returns the intersection between the two sets of flags. + #[inline] + fn bitand(self, other: Self) -> Self { + Self { + bits: self.bits & other.bits, + } + } +} + +impl std::ops::BitAndAssign for BitmapFlags { + /// Disables all flags disabled in the set. + #[inline] + fn bitand_assign(&mut self, other: Self) { + self.bits &= other.bits; + } +} + +impl std::ops::Sub for BitmapFlags { + type Output = Self; + + /// Returns the set difference of the two sets of flags. + #[inline] + fn sub(self, other: Self) -> Self { + Self { + bits: self.bits & !other.bits, + } + } +} + +impl std::ops::SubAssign for BitmapFlags { + /// Disables all flags enabled in the set. + #[inline] + fn sub_assign(&mut self, other: Self) { + self.bits &= !other.bits; + } +} + +impl std::ops::Not for BitmapFlags { + type Output = Self; + + /// Returns the complement of this set of flags. + #[inline] + fn not(self) -> Self { + Self { bits: !self.bits } & Self::all() + } +} + +impl std::fmt::Debug for BitmapFlags { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let members: &[(&str, Self)] = &[ + ("HORIZONTAL_METRICS", Self::HORIZONTAL_METRICS), + ("VERTICAL_METRICS", Self::VERTICAL_METRICS), + ]; + let mut first = true; + for (name, value) in members { + if self.contains(*value) { + if !first { + f.write_str(" | ")?; + } + first = false; + f.write_str(name)?; + } + } + if first { + f.write_str("(empty)")?; + } + Ok(()) + } +} + +impl std::fmt::Binary for BitmapFlags { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Binary::fmt(&self.bits, f) + } +} + +impl std::fmt::Octal for BitmapFlags { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Octal::fmt(&self.bits, f) + } +} + +impl std::fmt::LowerHex for BitmapFlags { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::LowerHex::fmt(&self.bits, f) + } +} + +impl std::fmt::UpperHex for BitmapFlags { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::UpperHex::fmt(&self.bits, f) + } +} + +impl font_types::Scalar for BitmapFlags { + type Raw = ::Raw; + fn to_raw(self) -> Self::Raw { + self.bits().to_raw() + } + fn from_raw(raw: Self::Raw) -> Self { + let t = ::from_raw(raw); + Self::from_bits_truncate(t) + } +} + +#[cfg(feature = "traversal")] +impl<'a> From for FieldType<'a> { + fn from(src: BitmapFlags) -> FieldType<'a> { + src.bits().into() + } +} + +/// [BigGlyphMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bigglyphmetrics) record. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +#[repr(packed)] +pub struct BigGlyphMetrics { + /// Number of rows of data. + pub height: u8, + /// Number of columns of data. + pub width: u8, + /// Distance in pixels from the horizontal origin to the left edge of the bitmap. + pub hori_bearing_x: BigEndian, + /// Distance in pixels from the horizontal origin to the top edge of the bitmap. + pub hori_bearing_y: BigEndian, + /// Horizontal advance width in pixels. + pub hori_advance: u8, + /// Distance in pixels from the vertical origin to the left edge of the bitmap. + pub vert_bearing_x: BigEndian, + /// Distance in pixels from the vertical origin to the top edge of the bitmap. + pub vert_bearing_y: BigEndian, + /// Vertical advance width in pixels. + pub vert_advance: u8, +} + +impl BigGlyphMetrics { + /// Number of rows of data. + pub fn height(&self) -> u8 { + self.height + } + + /// Number of columns of data. + pub fn width(&self) -> u8 { + self.width + } + + /// Distance in pixels from the horizontal origin to the left edge of the bitmap. + pub fn hori_bearing_x(&self) -> i8 { + self.hori_bearing_x.get() + } + + /// Distance in pixels from the horizontal origin to the top edge of the bitmap. + pub fn hori_bearing_y(&self) -> i8 { + self.hori_bearing_y.get() + } + + /// Horizontal advance width in pixels. + pub fn hori_advance(&self) -> u8 { + self.hori_advance + } + + /// Distance in pixels from the vertical origin to the left edge of the bitmap. + pub fn vert_bearing_x(&self) -> i8 { + self.vert_bearing_x.get() + } + + /// Distance in pixels from the vertical origin to the top edge of the bitmap. + pub fn vert_bearing_y(&self) -> i8 { + self.vert_bearing_y.get() + } + + /// Vertical advance width in pixels. + pub fn vert_advance(&self) -> u8 { + self.vert_advance + } +} + +impl FixedSize for BigGlyphMetrics { + const RAW_BYTE_LEN: usize = u8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN; +} + +impl sealed::Sealed for BigGlyphMetrics {} + +/// SAFETY: see the [`FromBytes`] trait documentation. +unsafe impl FromBytes for BigGlyphMetrics { + fn this_trait_should_only_be_implemented_in_generated_code() {} +} + +#[cfg(feature = "traversal")] +impl<'a> SomeRecord<'a> for BigGlyphMetrics { + fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { + RecordResolver { + name: "BigGlyphMetrics", + get_field: Box::new(move |idx, _data| match idx { + 0usize => Some(Field::new("height", self.height())), + 1usize => Some(Field::new("width", self.width())), + 2usize => Some(Field::new("hori_bearing_x", self.hori_bearing_x())), + 3usize => Some(Field::new("hori_bearing_y", self.hori_bearing_y())), + 4usize => Some(Field::new("hori_advance", self.hori_advance())), + 5usize => Some(Field::new("vert_bearing_x", self.vert_bearing_x())), + 6usize => Some(Field::new("vert_bearing_y", self.vert_bearing_y())), + 7usize => Some(Field::new("vert_advance", self.vert_advance())), + _ => None, + }), + data, + } + } +} + +/// [SmallGlyphMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#smallglyphmetrics) record. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +#[repr(packed)] +pub struct SmallGlyphMetrics { + /// Number of rows of data. + pub height: u8, + /// Number of columns of data. + pub width: u8, + /// Distance in pixels from the horizontal origin to the left edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the top edge of the bitmap (for vertical text). + pub bearing_x: BigEndian, + /// Distance in pixels from the horizontal origin to the top edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the left edge of the bitmap (for vertical text). + pub bearing_y: BigEndian, + /// Horizontal or vertical advance width in pixels. + pub advance: u8, +} + +impl SmallGlyphMetrics { + /// Number of rows of data. + pub fn height(&self) -> u8 { + self.height + } + + /// Number of columns of data. + pub fn width(&self) -> u8 { + self.width + } + + /// Distance in pixels from the horizontal origin to the left edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the top edge of the bitmap (for vertical text). + pub fn bearing_x(&self) -> i8 { + self.bearing_x.get() + } + + /// Distance in pixels from the horizontal origin to the top edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the left edge of the bitmap (for vertical text). + pub fn bearing_y(&self) -> i8 { + self.bearing_y.get() + } + + /// Horizontal or vertical advance width in pixels. + pub fn advance(&self) -> u8 { + self.advance + } +} + +impl FixedSize for SmallGlyphMetrics { + const RAW_BYTE_LEN: usize = u8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + i8::RAW_BYTE_LEN + + u8::RAW_BYTE_LEN; +} + +impl sealed::Sealed for SmallGlyphMetrics {} + +/// SAFETY: see the [`FromBytes`] trait documentation. +unsafe impl FromBytes for SmallGlyphMetrics { + fn this_trait_should_only_be_implemented_in_generated_code() {} +} + +#[cfg(feature = "traversal")] +impl<'a> SomeRecord<'a> for SmallGlyphMetrics { + fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { + RecordResolver { + name: "SmallGlyphMetrics", + get_field: Box::new(move |idx, _data| match idx { + 0usize => Some(Field::new("height", self.height())), + 1usize => Some(Field::new("width", self.width())), + 2usize => Some(Field::new("bearing_x", self.bearing_x())), + 3usize => Some(Field::new("bearing_y", self.bearing_y())), + 4usize => Some(Field::new("advance", self.advance())), + _ => None, + }), + data, + } + } +} + +/// [IndexSubtableArray](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtablearray) table. +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub struct IndexSubtableArrayMarker {} + +impl IndexSubtableArrayMarker { + fn first_glyph_index_byte_range(&self) -> Range { + let start = 0; + start..start + GlyphId::RAW_BYTE_LEN + } + fn last_glyph_index_byte_range(&self) -> Range { + let start = self.first_glyph_index_byte_range().end; + start..start + GlyphId::RAW_BYTE_LEN + } + fn additional_offset_to_index_subtable_byte_range(&self) -> Range { + let start = self.last_glyph_index_byte_range().end; + start..start + u32::RAW_BYTE_LEN + } +} + +impl<'a> FontRead<'a> for IndexSubtableArray<'a> { + fn read(data: FontData<'a>) -> Result { + let mut cursor = data.cursor(); + cursor.advance::(); + cursor.advance::(); + cursor.advance::(); + cursor.finish(IndexSubtableArrayMarker {}) + } +} + +/// [IndexSubtableArray](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtablearray) table. +pub type IndexSubtableArray<'a> = TableRef<'a, IndexSubtableArrayMarker>; + +impl<'a> IndexSubtableArray<'a> { + /// First glyph ID of this range. + pub fn first_glyph_index(&self) -> GlyphId { + let range = self.shape.first_glyph_index_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Last glyph ID of this range (inclusive). + pub fn last_glyph_index(&self) -> GlyphId { + let range = self.shape.last_glyph_index_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Add to indexSubTableArrayOffset to get offset from beginning of EBLC. + pub fn additional_offset_to_index_subtable(&self) -> u32 { + let range = self.shape.additional_offset_to_index_subtable_byte_range(); + self.data.read_at(range.start).unwrap() + } +} + +#[cfg(feature = "traversal")] +impl<'a> SomeTable<'a> for IndexSubtableArray<'a> { + fn type_name(&self) -> &str { + "IndexSubtableArray" + } + fn get_field(&self, idx: usize) -> Option> { + match idx { + 0usize => Some(Field::new("first_glyph_index", self.first_glyph_index())), + 1usize => Some(Field::new("last_glyph_index", self.last_glyph_index())), + 2usize => Some(Field::new( + "additional_offset_to_index_subtable", + self.additional_offset_to_index_subtable(), + )), + _ => None, + } + } +} + +#[cfg(feature = "traversal")] +impl<'a> std::fmt::Debug for IndexSubtableArray<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (self as &dyn SomeTable<'a>).fmt(f) + } +} + +/// [IndexSubtables](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtables) format type. +pub enum IndexSubtable<'a> { + Format1(IndexSubtable1<'a>), + Format2(IndexSubtable2<'a>), + Format3(IndexSubtable3<'a>), + Format4(IndexSubtable4<'a>), + Format5(IndexSubtable5<'a>), +} + +impl<'a> FontRead<'a> for IndexSubtable<'a> { + fn read(data: FontData<'a>) -> Result { + let format: u16 = data.read_at(0usize)?; + match format { + IndexSubtable1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)), + IndexSubtable2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)), + IndexSubtable3Marker::FORMAT => Ok(Self::Format3(FontRead::read(data)?)), + IndexSubtable4Marker::FORMAT => Ok(Self::Format4(FontRead::read(data)?)), + IndexSubtable5Marker::FORMAT => Ok(Self::Format5(FontRead::read(data)?)), + other => Err(ReadError::InvalidFormat(other.into())), + } + } +} + +#[cfg(feature = "traversal")] +impl<'a> IndexSubtable<'a> { + fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> { + match self { + Self::Format1(table) => table, + Self::Format2(table) => table, + Self::Format3(table) => table, + Self::Format4(table) => table, + Self::Format5(table) => table, + } + } +} + +#[cfg(feature = "traversal")] +impl<'a> std::fmt::Debug for IndexSubtable<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.dyn_inner().fmt(f) + } +} + +#[cfg(feature = "traversal")] +impl<'a> SomeTable<'a> for IndexSubtable<'a> { + fn type_name(&self) -> &str { + self.dyn_inner().type_name() + } + fn get_field(&self, idx: usize) -> Option> { + self.dyn_inner().get_field(idx) + } +} + +impl Format for IndexSubtable1Marker { + const FORMAT: u16 = 1; +} + +/// [IndexSubTable1](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable1-variable-metrics-glyphs-with-4-byte-offsets): variable-metrics glyphs with 4-byte offsets. +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub struct IndexSubtable1Marker { + sbit_offsets_byte_len: usize, +} + +impl IndexSubtable1Marker { + fn index_format_byte_range(&self) -> Range { + let start = 0; + start..start + u16::RAW_BYTE_LEN + } + fn image_format_byte_range(&self) -> Range { + let start = self.index_format_byte_range().end; + start..start + u16::RAW_BYTE_LEN + } + fn image_data_offset_byte_range(&self) -> Range { + let start = self.image_format_byte_range().end; + start..start + u32::RAW_BYTE_LEN + } + fn sbit_offsets_byte_range(&self) -> Range { + let start = self.image_data_offset_byte_range().end; + start..start + self.sbit_offsets_byte_len + } +} + +impl<'a> FontRead<'a> for IndexSubtable1<'a> { + fn read(data: FontData<'a>) -> Result { + let mut cursor = data.cursor(); + cursor.advance::(); + cursor.advance::(); + cursor.advance::(); + let sbit_offsets_byte_len = cursor.remaining_bytes(); + cursor.advance_by(sbit_offsets_byte_len); + cursor.finish(IndexSubtable1Marker { + sbit_offsets_byte_len, + }) + } +} + +/// [IndexSubTable1](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable1-variable-metrics-glyphs-with-4-byte-offsets): variable-metrics glyphs with 4-byte offsets. +pub type IndexSubtable1<'a> = TableRef<'a, IndexSubtable1Marker>; + +impl<'a> IndexSubtable1<'a> { + /// Format of this IndexSubTable. + pub fn index_format(&self) -> u16 { + let range = self.shape.index_format_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Format of EBDT image data. + pub fn image_format(&self) -> u16 { + let range = self.shape.image_format_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Offset to image data in EBDT table. + pub fn image_data_offset(&self) -> u32 { + let range = self.shape.image_data_offset_byte_range(); + self.data.read_at(range.start).unwrap() + } + + pub fn sbit_offsets(&self) -> &'a [BigEndian] { + let range = self.shape.sbit_offsets_byte_range(); + self.data.read_array(range).unwrap() + } +} + +#[cfg(feature = "traversal")] +impl<'a> SomeTable<'a> for IndexSubtable1<'a> { + fn type_name(&self) -> &str { + "IndexSubtable1" + } + fn get_field(&self, idx: usize) -> Option> { + match idx { + 0usize => Some(Field::new("index_format", self.index_format())), + 1usize => Some(Field::new("image_format", self.image_format())), + 2usize => Some(Field::new("image_data_offset", self.image_data_offset())), + 3usize => Some(Field::new("sbit_offsets", self.sbit_offsets())), + _ => None, + } + } +} + +#[cfg(feature = "traversal")] +impl<'a> std::fmt::Debug for IndexSubtable1<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (self as &dyn SomeTable<'a>).fmt(f) + } +} + +impl Format for IndexSubtable2Marker { + const FORMAT: u16 = 2; +} + +/// [IndexSubTable2](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable2-all-glyphs-have-identical-metrics): all glyphs have identical metrics. +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub struct IndexSubtable2Marker { + big_metrics_byte_len: usize, +} + +impl IndexSubtable2Marker { + fn index_format_byte_range(&self) -> Range { + let start = 0; + start..start + u16::RAW_BYTE_LEN + } + fn image_format_byte_range(&self) -> Range { + let start = self.index_format_byte_range().end; + start..start + u16::RAW_BYTE_LEN + } + fn image_data_offset_byte_range(&self) -> Range { + let start = self.image_format_byte_range().end; + start..start + u32::RAW_BYTE_LEN + } + fn image_size_byte_range(&self) -> Range { + let start = self.image_data_offset_byte_range().end; + start..start + u32::RAW_BYTE_LEN + } + fn big_metrics_byte_range(&self) -> Range { + let start = self.image_size_byte_range().end; + start..start + self.big_metrics_byte_len + } +} + +impl<'a> FontRead<'a> for IndexSubtable2<'a> { + fn read(data: FontData<'a>) -> Result { + let mut cursor = data.cursor(); + cursor.advance::(); + cursor.advance::(); + cursor.advance::(); + cursor.advance::(); + let big_metrics_byte_len = 1_usize * BigGlyphMetrics::RAW_BYTE_LEN; + cursor.advance_by(big_metrics_byte_len); + cursor.finish(IndexSubtable2Marker { + big_metrics_byte_len, + }) + } +} + +/// [IndexSubTable2](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable2-all-glyphs-have-identical-metrics): all glyphs have identical metrics. +pub type IndexSubtable2<'a> = TableRef<'a, IndexSubtable2Marker>; + +impl<'a> IndexSubtable2<'a> { + /// Format of this IndexSubTable. + pub fn index_format(&self) -> u16 { + let range = self.shape.index_format_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Format of EBDT image data. + pub fn image_format(&self) -> u16 { + let range = self.shape.image_format_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Offset to image data in EBDT table. + pub fn image_data_offset(&self) -> u32 { + let range = self.shape.image_data_offset_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// All the glyphs are of the same size. + pub fn image_size(&self) -> u32 { + let range = self.shape.image_size_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// All glyphs have the same metrics; glyph data may be compressed, byte-aligned, or bit-aligned. + pub fn big_metrics(&self) -> &'a [BigGlyphMetrics] { + let range = self.shape.big_metrics_byte_range(); + self.data.read_array(range).unwrap() + } +} + +#[cfg(feature = "traversal")] +impl<'a> SomeTable<'a> for IndexSubtable2<'a> { + fn type_name(&self) -> &str { + "IndexSubtable2" + } + fn get_field(&self, idx: usize) -> Option> { + match idx { + 0usize => Some(Field::new("index_format", self.index_format())), + 1usize => Some(Field::new("image_format", self.image_format())), + 2usize => Some(Field::new("image_data_offset", self.image_data_offset())), + 3usize => Some(Field::new("image_size", self.image_size())), + 4usize => Some(Field::new( + "big_metrics", + traversal::FieldType::array_of_records( + stringify!(BigGlyphMetrics), + self.big_metrics(), + self.offset_data(), + ), + )), + _ => None, + } + } +} + +#[cfg(feature = "traversal")] +impl<'a> std::fmt::Debug for IndexSubtable2<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (self as &dyn SomeTable<'a>).fmt(f) + } +} + +impl Format for IndexSubtable3Marker { + const FORMAT: u16 = 3; +} + +/// [IndexSubTable3](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable3-variable-metrics-glyphs-with-2-byte-offsets): variable-metrics glyphs with 2-byte offsets. +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub struct IndexSubtable3Marker { + sbit_offsets_byte_len: usize, +} + +impl IndexSubtable3Marker { + fn index_format_byte_range(&self) -> Range { + let start = 0; + start..start + u16::RAW_BYTE_LEN + } + fn image_format_byte_range(&self) -> Range { + let start = self.index_format_byte_range().end; + start..start + u16::RAW_BYTE_LEN + } + fn image_data_offset_byte_range(&self) -> Range { + let start = self.image_format_byte_range().end; + start..start + u32::RAW_BYTE_LEN + } + fn sbit_offsets_byte_range(&self) -> Range { + let start = self.image_data_offset_byte_range().end; + start..start + self.sbit_offsets_byte_len + } +} + +impl<'a> FontRead<'a> for IndexSubtable3<'a> { + fn read(data: FontData<'a>) -> Result { + let mut cursor = data.cursor(); + cursor.advance::(); + cursor.advance::(); + cursor.advance::(); + let sbit_offsets_byte_len = cursor.remaining_bytes(); + cursor.advance_by(sbit_offsets_byte_len); + cursor.finish(IndexSubtable3Marker { + sbit_offsets_byte_len, + }) + } +} + +/// [IndexSubTable3](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable3-variable-metrics-glyphs-with-2-byte-offsets): variable-metrics glyphs with 2-byte offsets. +pub type IndexSubtable3<'a> = TableRef<'a, IndexSubtable3Marker>; + +impl<'a> IndexSubtable3<'a> { + /// Format of this IndexSubTable. + pub fn index_format(&self) -> u16 { + let range = self.shape.index_format_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Format of EBDT image data. + pub fn image_format(&self) -> u16 { + let range = self.shape.image_format_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Offset to image data in EBDT table. + pub fn image_data_offset(&self) -> u32 { + let range = self.shape.image_data_offset_byte_range(); + self.data.read_at(range.start).unwrap() + } + + pub fn sbit_offsets(&self) -> &'a [BigEndian] { + let range = self.shape.sbit_offsets_byte_range(); + self.data.read_array(range).unwrap() + } +} + +#[cfg(feature = "traversal")] +impl<'a> SomeTable<'a> for IndexSubtable3<'a> { + fn type_name(&self) -> &str { + "IndexSubtable3" + } + fn get_field(&self, idx: usize) -> Option> { + match idx { + 0usize => Some(Field::new("index_format", self.index_format())), + 1usize => Some(Field::new("image_format", self.image_format())), + 2usize => Some(Field::new("image_data_offset", self.image_data_offset())), + 3usize => Some(Field::new("sbit_offsets", self.sbit_offsets())), + _ => None, + } + } +} + +#[cfg(feature = "traversal")] +impl<'a> std::fmt::Debug for IndexSubtable3<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (self as &dyn SomeTable<'a>).fmt(f) + } +} + +impl Format for IndexSubtable4Marker { + const FORMAT: u16 = 4; +} + +/// [IndexSubTable4](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable3-variable-metrics-glyphs-with-2-byte-offsets): variable-metrics glyphs with sparse glyph codes. +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub struct IndexSubtable4Marker { + glyph_array_byte_len: usize, +} + +impl IndexSubtable4Marker { + fn index_format_byte_range(&self) -> Range { + let start = 0; + start..start + u16::RAW_BYTE_LEN + } + fn image_format_byte_range(&self) -> Range { + let start = self.index_format_byte_range().end; + start..start + u16::RAW_BYTE_LEN + } + fn image_data_offset_byte_range(&self) -> Range { + let start = self.image_format_byte_range().end; + start..start + u32::RAW_BYTE_LEN + } + fn num_glyphs_byte_range(&self) -> Range { + let start = self.image_data_offset_byte_range().end; + start..start + u32::RAW_BYTE_LEN + } + fn glyph_array_byte_range(&self) -> Range { + let start = self.num_glyphs_byte_range().end; + start..start + self.glyph_array_byte_len + } +} + +impl<'a> FontRead<'a> for IndexSubtable4<'a> { + fn read(data: FontData<'a>) -> Result { + let mut cursor = data.cursor(); + cursor.advance::(); + cursor.advance::(); + cursor.advance::(); + let num_glyphs: u32 = cursor.read()?; + let glyph_array_byte_len = + transforms::add(num_glyphs, 1_usize) * GlyphIdOffsetPair::RAW_BYTE_LEN; + cursor.advance_by(glyph_array_byte_len); + cursor.finish(IndexSubtable4Marker { + glyph_array_byte_len, + }) + } +} + +/// [IndexSubTable4](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable3-variable-metrics-glyphs-with-2-byte-offsets): variable-metrics glyphs with sparse glyph codes. +pub type IndexSubtable4<'a> = TableRef<'a, IndexSubtable4Marker>; + +impl<'a> IndexSubtable4<'a> { + /// Format of this IndexSubTable. + pub fn index_format(&self) -> u16 { + let range = self.shape.index_format_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Format of EBDT image data. + pub fn image_format(&self) -> u16 { + let range = self.shape.image_format_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Offset to image data in EBDT table. + pub fn image_data_offset(&self) -> u32 { + let range = self.shape.image_data_offset_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Array length. + pub fn num_glyphs(&self) -> u32 { + let range = self.shape.num_glyphs_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// One per glyph. + pub fn glyph_array(&self) -> &'a [GlyphIdOffsetPair] { + let range = self.shape.glyph_array_byte_range(); + self.data.read_array(range).unwrap() + } +} + +#[cfg(feature = "traversal")] +impl<'a> SomeTable<'a> for IndexSubtable4<'a> { + fn type_name(&self) -> &str { + "IndexSubtable4" + } + fn get_field(&self, idx: usize) -> Option> { + match idx { + 0usize => Some(Field::new("index_format", self.index_format())), + 1usize => Some(Field::new("image_format", self.image_format())), + 2usize => Some(Field::new("image_data_offset", self.image_data_offset())), + 3usize => Some(Field::new("num_glyphs", self.num_glyphs())), + 4usize => Some(Field::new( + "glyph_array", + traversal::FieldType::array_of_records( + stringify!(GlyphIdOffsetPair), + self.glyph_array(), + self.offset_data(), + ), + )), + _ => None, + } + } +} + +#[cfg(feature = "traversal")] +impl<'a> std::fmt::Debug for IndexSubtable4<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (self as &dyn SomeTable<'a>).fmt(f) + } +} + +/// [GlyphIdOffsetPair](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#glyphidoffsetpair-record) record. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +#[repr(packed)] +pub struct GlyphIdOffsetPair { + /// Glyph ID of glyph present. + pub glyph_id: BigEndian, + /// Location in EBDT. + pub sbit_offset: BigEndian, +} + +impl GlyphIdOffsetPair { + /// Glyph ID of glyph present. + pub fn glyph_id(&self) -> GlyphId { + self.glyph_id.get() + } + + /// Location in EBDT. + pub fn sbit_offset(&self) -> u16 { + self.sbit_offset.get() + } +} + +impl FixedSize for GlyphIdOffsetPair { + const RAW_BYTE_LEN: usize = GlyphId::RAW_BYTE_LEN + u16::RAW_BYTE_LEN; +} + +impl sealed::Sealed for GlyphIdOffsetPair {} + +/// SAFETY: see the [`FromBytes`] trait documentation. +unsafe impl FromBytes for GlyphIdOffsetPair { + fn this_trait_should_only_be_implemented_in_generated_code() {} +} + +#[cfg(feature = "traversal")] +impl<'a> SomeRecord<'a> for GlyphIdOffsetPair { + fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { + RecordResolver { + name: "GlyphIdOffsetPair", + get_field: Box::new(move |idx, _data| match idx { + 0usize => Some(Field::new("glyph_id", self.glyph_id())), + 1usize => Some(Field::new("sbit_offset", self.sbit_offset())), + _ => None, + }), + data, + } + } +} + +impl Format for IndexSubtable5Marker { + const FORMAT: u16 = 5; +} + +/// [IndexSubTable5](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable5-constant-metrics-glyphs-with-sparse-glyph-codes): constant-metrics glyphs with sparse glyph codes +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub struct IndexSubtable5Marker { + big_metrics_byte_len: usize, + glyph_array_byte_len: usize, +} + +impl IndexSubtable5Marker { + fn index_format_byte_range(&self) -> Range { + let start = 0; + start..start + u16::RAW_BYTE_LEN + } + fn image_format_byte_range(&self) -> Range { + let start = self.index_format_byte_range().end; + start..start + u16::RAW_BYTE_LEN + } + fn image_data_offset_byte_range(&self) -> Range { + let start = self.image_format_byte_range().end; + start..start + u32::RAW_BYTE_LEN + } + fn image_size_byte_range(&self) -> Range { + let start = self.image_data_offset_byte_range().end; + start..start + u32::RAW_BYTE_LEN + } + fn big_metrics_byte_range(&self) -> Range { + let start = self.image_size_byte_range().end; + start..start + self.big_metrics_byte_len + } + fn num_glyphs_byte_range(&self) -> Range { + let start = self.big_metrics_byte_range().end; + start..start + u32::RAW_BYTE_LEN + } + fn glyph_array_byte_range(&self) -> Range { + let start = self.num_glyphs_byte_range().end; + start..start + self.glyph_array_byte_len + } +} + +impl<'a> FontRead<'a> for IndexSubtable5<'a> { + fn read(data: FontData<'a>) -> Result { + let mut cursor = data.cursor(); + cursor.advance::(); + cursor.advance::(); + cursor.advance::(); + cursor.advance::(); + let big_metrics_byte_len = 1_usize * BigGlyphMetrics::RAW_BYTE_LEN; + cursor.advance_by(big_metrics_byte_len); + let num_glyphs: u32 = cursor.read()?; + let glyph_array_byte_len = num_glyphs as usize * GlyphId::RAW_BYTE_LEN; + cursor.advance_by(glyph_array_byte_len); + cursor.finish(IndexSubtable5Marker { + big_metrics_byte_len, + glyph_array_byte_len, + }) + } +} + +/// [IndexSubTable5](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable5-constant-metrics-glyphs-with-sparse-glyph-codes): constant-metrics glyphs with sparse glyph codes +pub type IndexSubtable5<'a> = TableRef<'a, IndexSubtable5Marker>; + +impl<'a> IndexSubtable5<'a> { + /// Format of this IndexSubTable. + pub fn index_format(&self) -> u16 { + let range = self.shape.index_format_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Format of EBDT image data. + pub fn image_format(&self) -> u16 { + let range = self.shape.image_format_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Offset to image data in EBDT table. + pub fn image_data_offset(&self) -> u32 { + let range = self.shape.image_data_offset_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// All glyphs have the same data size. + pub fn image_size(&self) -> u32 { + let range = self.shape.image_size_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// All glyphs have the same metrics. + pub fn big_metrics(&self) -> &'a [BigGlyphMetrics] { + let range = self.shape.big_metrics_byte_range(); + self.data.read_array(range).unwrap() + } + + /// Array length. + pub fn num_glyphs(&self) -> u32 { + let range = self.shape.num_glyphs_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// One per glyph, sorted by glyhph ID. + pub fn glyph_array(&self) -> &'a [BigEndian] { + let range = self.shape.glyph_array_byte_range(); + self.data.read_array(range).unwrap() + } +} + +#[cfg(feature = "traversal")] +impl<'a> SomeTable<'a> for IndexSubtable5<'a> { + fn type_name(&self) -> &str { + "IndexSubtable5" + } + fn get_field(&self, idx: usize) -> Option> { + match idx { + 0usize => Some(Field::new("index_format", self.index_format())), + 1usize => Some(Field::new("image_format", self.image_format())), + 2usize => Some(Field::new("image_data_offset", self.image_data_offset())), + 3usize => Some(Field::new("image_size", self.image_size())), + 4usize => Some(Field::new( + "big_metrics", + traversal::FieldType::array_of_records( + stringify!(BigGlyphMetrics), + self.big_metrics(), + self.offset_data(), + ), + )), + 5usize => Some(Field::new("num_glyphs", self.num_glyphs())), + 6usize => Some(Field::new("glyph_array", self.glyph_array())), + _ => None, + } + } +} + +#[cfg(feature = "traversal")] +impl<'a> std::fmt::Debug for IndexSubtable5<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (self as &dyn SomeTable<'a>).fmt(f) + } +} diff --git a/read-fonts/generated/generated_cbdt.rs b/read-fonts/generated/generated_cbdt.rs new file mode 100644 index 000000000..ed24ac511 --- /dev/null +++ b/read-fonts/generated/generated_cbdt.rs @@ -0,0 +1,74 @@ +// THIS FILE IS AUTOGENERATED. +// Any changes to this file will be overwritten. +// For more information about how codegen works, see font-codegen/README.md + +#[allow(unused_imports)] +use crate::codegen_prelude::*; + +/// The [Color Bitmap Data](https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt) table +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub struct CbdtMarker {} + +impl CbdtMarker { + fn major_version_byte_range(&self) -> Range { + let start = 0; + start..start + u16::RAW_BYTE_LEN + } + fn minor_version_byte_range(&self) -> Range { + let start = self.major_version_byte_range().end; + start..start + u16::RAW_BYTE_LEN + } +} + +impl TopLevelTable for Cbdt<'_> { + /// `CBDT` + const TAG: Tag = Tag::new(b"CBDT"); +} + +impl<'a> FontRead<'a> for Cbdt<'a> { + fn read(data: FontData<'a>) -> Result { + let mut cursor = data.cursor(); + cursor.advance::(); + cursor.advance::(); + cursor.finish(CbdtMarker {}) + } +} + +/// The [Color Bitmap Data](https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt) table +pub type Cbdt<'a> = TableRef<'a, CbdtMarker>; + +impl<'a> Cbdt<'a> { + /// Major version of the CBDT table, = 3. + pub fn major_version(&self) -> u16 { + let range = self.shape.major_version_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Minor version of CBDT table, = 0. + pub fn minor_version(&self) -> u16 { + let range = self.shape.minor_version_byte_range(); + self.data.read_at(range.start).unwrap() + } +} + +#[cfg(feature = "traversal")] +impl<'a> SomeTable<'a> for Cbdt<'a> { + fn type_name(&self) -> &str { + "Cbdt" + } + fn get_field(&self, idx: usize) -> Option> { + match idx { + 0usize => Some(Field::new("major_version", self.major_version())), + 1usize => Some(Field::new("minor_version", self.minor_version())), + _ => None, + } + } +} + +#[cfg(feature = "traversal")] +impl<'a> std::fmt::Debug for Cbdt<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (self as &dyn SomeTable<'a>).fmt(f) + } +} diff --git a/read-fonts/generated/generated_cblc.rs b/read-fonts/generated/generated_cblc.rs index 693ff4448..2a9f1cb0a 100644 --- a/read-fonts/generated/generated_cblc.rs +++ b/read-fonts/generated/generated_cblc.rs @@ -72,6 +72,7 @@ impl<'a> Cblc<'a> { self.data.read_at(range.start).unwrap() } + /// BitmapSize records array. pub fn bitmap_sizes(&self) -> &'a [BitmapSize] { let range = self.shape.bitmap_sizes_byte_range(); self.data.read_array(range).unwrap() @@ -107,821 +108,3 @@ impl<'a> std::fmt::Debug for Cblc<'a> { (self as &dyn SomeTable<'a>).fmt(f) } } - -/// [BitmapSize](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmapsize-record) record. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] -#[repr(packed)] -pub struct BitmapSize { - /// Offset to index subtable from beginning of EBLC/CBLC. - pub index_subtable_array_offset: BigEndian, - /// Number of bytes in corresponding index subtables and array. - pub index_tables_size: BigEndian, - /// There is an index subtable for each range or format change. - pub number_of_index_subtables: BigEndian, - /// Not used; set to 0. - pub color_ref: BigEndian, - /// Line metrics for text rendered horizontally. - pub hori: SbitLineMetrics, - /// Line metrics for text rendered vertically. - pub vert: SbitLineMetrics, - /// Lowest glyph index for this size. - pub start_glyph_index: BigEndian, - /// Highest glyph index for this size. - pub end_glyph_index: BigEndian, - /// Horizontal pixels per em. - pub ppem_x: u8, - /// Vertical pixels per em. - pub ppem_y: u8, - /// The Microsoft rasterizer v.1.7 or greater supports the following - /// bitDepth values, as described below: 1, 2, 4, and 8 (and 32 for CBLC). - pub bit_depth: u8, - /// Vertical or horizontal. - pub flags: BigEndian, -} - -impl BitmapSize { - /// Offset to index subtable from beginning of EBLC/CBLC. - pub fn index_subtable_array_offset(&self) -> u32 { - self.index_subtable_array_offset.get() - } - - /// Number of bytes in corresponding index subtables and array. - pub fn index_tables_size(&self) -> u32 { - self.index_tables_size.get() - } - - /// There is an index subtable for each range or format change. - pub fn number_of_index_subtables(&self) -> u32 { - self.number_of_index_subtables.get() - } - - /// Not used; set to 0. - pub fn color_ref(&self) -> u32 { - self.color_ref.get() - } - - /// Line metrics for text rendered horizontally. - pub fn hori(&self) -> &SbitLineMetrics { - &self.hori - } - - /// Line metrics for text rendered vertically. - pub fn vert(&self) -> &SbitLineMetrics { - &self.vert - } - - /// Lowest glyph index for this size. - pub fn start_glyph_index(&self) -> GlyphId { - self.start_glyph_index.get() - } - - /// Highest glyph index for this size. - pub fn end_glyph_index(&self) -> GlyphId { - self.end_glyph_index.get() - } - - /// Horizontal pixels per em. - pub fn ppem_x(&self) -> u8 { - self.ppem_x - } - - /// Vertical pixels per em. - pub fn ppem_y(&self) -> u8 { - self.ppem_y - } - - /// The Microsoft rasterizer v.1.7 or greater supports the following - /// bitDepth values, as described below: 1, 2, 4, and 8 (and 32 for CBLC). - pub fn bit_depth(&self) -> u8 { - self.bit_depth - } - - /// Vertical or horizontal. - pub fn flags(&self) -> BitmapFlags { - self.flags.get() - } -} - -impl FixedSize for BitmapSize { - const RAW_BYTE_LEN: usize = u32::RAW_BYTE_LEN - + u32::RAW_BYTE_LEN - + u32::RAW_BYTE_LEN - + u32::RAW_BYTE_LEN - + SbitLineMetrics::RAW_BYTE_LEN - + SbitLineMetrics::RAW_BYTE_LEN - + GlyphId::RAW_BYTE_LEN - + GlyphId::RAW_BYTE_LEN - + u8::RAW_BYTE_LEN - + u8::RAW_BYTE_LEN - + u8::RAW_BYTE_LEN - + BitmapFlags::RAW_BYTE_LEN; -} - -impl sealed::Sealed for BitmapSize {} - -/// SAFETY: see the [`FromBytes`] trait documentation. -unsafe impl FromBytes for BitmapSize { - fn this_trait_should_only_be_implemented_in_generated_code() {} -} - -#[cfg(feature = "traversal")] -impl<'a> SomeRecord<'a> for BitmapSize { - fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { - RecordResolver { - name: "BitmapSize", - get_field: Box::new(move |idx, _data| match idx { - 0usize => Some(Field::new( - "index_subtable_array_offset", - self.index_subtable_array_offset(), - )), - 1usize => Some(Field::new("index_tables_size", self.index_tables_size())), - 2usize => Some(Field::new( - "number_of_index_subtables", - self.number_of_index_subtables(), - )), - 3usize => Some(Field::new("color_ref", self.color_ref())), - 4usize => Some(Field::new("hori", self.hori().traversal_type(_data))), - 5usize => Some(Field::new("vert", self.vert().traversal_type(_data))), - 6usize => Some(Field::new("start_glyph_index", self.start_glyph_index())), - 7usize => Some(Field::new("end_glyph_index", self.end_glyph_index())), - 8usize => Some(Field::new("ppem_x", self.ppem_x())), - 9usize => Some(Field::new("ppem_y", self.ppem_y())), - 10usize => Some(Field::new("bit_depth", self.bit_depth())), - 11usize => Some(Field::new("flags", self.flags())), - _ => None, - }), - data, - } - } -} - -/// [SbitLineMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#sbitlinemetrics-record) record. -#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] -#[repr(packed)] -pub struct SbitLineMetrics { - pub ascender: BigEndian, - pub descender: BigEndian, - pub width_max: u8, - pub caret_slope_numerator: BigEndian, - pub caret_slope_denominator: u8, - pub caret_offset: BigEndian, - pub min_origin_sb: BigEndian, - pub min_advance_sb: BigEndian, - pub max_before_bl: BigEndian, - pub min_after_bl: BigEndian, - pub pad1: BigEndian, - pub pad2: BigEndian, -} - -impl SbitLineMetrics { - pub fn ascender(&self) -> i8 { - self.ascender.get() - } - - pub fn descender(&self) -> i8 { - self.descender.get() - } - - pub fn width_max(&self) -> u8 { - self.width_max - } - - pub fn caret_slope_numerator(&self) -> i8 { - self.caret_slope_numerator.get() - } - - pub fn caret_slope_denominator(&self) -> u8 { - self.caret_slope_denominator - } - - pub fn caret_offset(&self) -> i8 { - self.caret_offset.get() - } - - pub fn min_origin_sb(&self) -> i8 { - self.min_origin_sb.get() - } - - pub fn min_advance_sb(&self) -> i8 { - self.min_advance_sb.get() - } - - pub fn max_before_bl(&self) -> i8 { - self.max_before_bl.get() - } - - pub fn min_after_bl(&self) -> i8 { - self.min_after_bl.get() - } - - pub fn pad1(&self) -> i8 { - self.pad1.get() - } - - pub fn pad2(&self) -> i8 { - self.pad2.get() - } -} - -impl FixedSize for SbitLineMetrics { - const RAW_BYTE_LEN: usize = i8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + u8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + u8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN; -} - -impl sealed::Sealed for SbitLineMetrics {} - -/// SAFETY: see the [`FromBytes`] trait documentation. -unsafe impl FromBytes for SbitLineMetrics { - fn this_trait_should_only_be_implemented_in_generated_code() {} -} - -#[cfg(feature = "traversal")] -impl<'a> SomeRecord<'a> for SbitLineMetrics { - fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { - RecordResolver { - name: "SbitLineMetrics", - get_field: Box::new(move |idx, _data| match idx { - 0usize => Some(Field::new("ascender", self.ascender())), - 1usize => Some(Field::new("descender", self.descender())), - 2usize => Some(Field::new("width_max", self.width_max())), - 3usize => Some(Field::new( - "caret_slope_numerator", - self.caret_slope_numerator(), - )), - 4usize => Some(Field::new( - "caret_slope_denominator", - self.caret_slope_denominator(), - )), - 5usize => Some(Field::new("caret_offset", self.caret_offset())), - 6usize => Some(Field::new("min_origin_sb", self.min_origin_sb())), - 7usize => Some(Field::new("min_advance_sb", self.min_advance_sb())), - 8usize => Some(Field::new("max_before_bl", self.max_before_bl())), - 9usize => Some(Field::new("min_after_bl", self.min_after_bl())), - 10usize => Some(Field::new("pad1", self.pad1())), - 11usize => Some(Field::new("pad2", self.pad2())), - _ => None, - }), - data, - } - } -} - -/// [Bitmap flags](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmap-flags). -#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct BitmapFlags { - bits: u8, -} - -impl BitmapFlags { - /// Horizontal - pub const HORIZONTAL_METRICS: Self = Self { bits: 0x01 }; - - /// Vertical - pub const VERTICAL_METRICS: Self = Self { bits: 0x02 }; -} - -impl BitmapFlags { - /// Returns an empty set of flags. - #[inline] - pub const fn empty() -> Self { - Self { bits: 0 } - } - - /// Returns the set containing all flags. - #[inline] - pub const fn all() -> Self { - Self { - bits: Self::HORIZONTAL_METRICS.bits | Self::VERTICAL_METRICS.bits, - } - } - - /// Returns the raw value of the flags currently stored. - #[inline] - pub const fn bits(&self) -> u8 { - self.bits - } - - /// Convert from underlying bit representation, unless that - /// representation contains bits that do not correspond to a flag. - #[inline] - pub const fn from_bits(bits: u8) -> Option { - if (bits & !Self::all().bits()) == 0 { - Some(Self { bits }) - } else { - None - } - } - - /// Convert from underlying bit representation, dropping any bits - /// that do not correspond to flags. - #[inline] - pub const fn from_bits_truncate(bits: u8) -> Self { - Self { - bits: bits & Self::all().bits, - } - } - - /// Returns `true` if no flags are currently stored. - #[inline] - pub const fn is_empty(&self) -> bool { - self.bits() == Self::empty().bits() - } - - /// Returns `true` if there are flags common to both `self` and `other`. - #[inline] - pub const fn intersects(&self, other: Self) -> bool { - !(Self { - bits: self.bits & other.bits, - }) - .is_empty() - } - - /// Returns `true` if all of the flags in `other` are contained within `self`. - #[inline] - pub const fn contains(&self, other: Self) -> bool { - (self.bits & other.bits) == other.bits - } - - /// Inserts the specified flags in-place. - #[inline] - pub fn insert(&mut self, other: Self) { - self.bits |= other.bits; - } - - /// Removes the specified flags in-place. - #[inline] - pub fn remove(&mut self, other: Self) { - self.bits &= !other.bits; - } - - /// Toggles the specified flags in-place. - #[inline] - pub fn toggle(&mut self, other: Self) { - self.bits ^= other.bits; - } - - /// Returns the intersection between the flags in `self` and - /// `other`. - /// - /// Specifically, the returned set contains only the flags which are - /// present in *both* `self` *and* `other`. - /// - /// This is equivalent to using the `&` operator (e.g. - /// [`ops::BitAnd`]), as in `flags & other`. - /// - /// [`ops::BitAnd`]: https://doc.rust-lang.org/std/ops/trait.BitAnd.html - #[inline] - #[must_use] - pub const fn intersection(self, other: Self) -> Self { - Self { - bits: self.bits & other.bits, - } - } - - /// Returns the union of between the flags in `self` and `other`. - /// - /// Specifically, the returned set contains all flags which are - /// present in *either* `self` *or* `other`, including any which are - /// present in both. - /// - /// This is equivalent to using the `|` operator (e.g. - /// [`ops::BitOr`]), as in `flags | other`. - /// - /// [`ops::BitOr`]: https://doc.rust-lang.org/std/ops/trait.BitOr.html - #[inline] - #[must_use] - pub const fn union(self, other: Self) -> Self { - Self { - bits: self.bits | other.bits, - } - } - - /// Returns the difference between the flags in `self` and `other`. - /// - /// Specifically, the returned set contains all flags present in - /// `self`, except for the ones present in `other`. - /// - /// It is also conceptually equivalent to the "bit-clear" operation: - /// `flags & !other` (and this syntax is also supported). - /// - /// This is equivalent to using the `-` operator (e.g. - /// [`ops::Sub`]), as in `flags - other`. - /// - /// [`ops::Sub`]: https://doc.rust-lang.org/std/ops/trait.Sub.html - #[inline] - #[must_use] - pub const fn difference(self, other: Self) -> Self { - Self { - bits: self.bits & !other.bits, - } - } -} - -impl std::ops::BitOr for BitmapFlags { - type Output = Self; - - /// Returns the union of the two sets of flags. - #[inline] - fn bitor(self, other: BitmapFlags) -> Self { - Self { - bits: self.bits | other.bits, - } - } -} - -impl std::ops::BitOrAssign for BitmapFlags { - /// Adds the set of flags. - #[inline] - fn bitor_assign(&mut self, other: Self) { - self.bits |= other.bits; - } -} - -impl std::ops::BitXor for BitmapFlags { - type Output = Self; - - /// Returns the left flags, but with all the right flags toggled. - #[inline] - fn bitxor(self, other: Self) -> Self { - Self { - bits: self.bits ^ other.bits, - } - } -} - -impl std::ops::BitXorAssign for BitmapFlags { - /// Toggles the set of flags. - #[inline] - fn bitxor_assign(&mut self, other: Self) { - self.bits ^= other.bits; - } -} - -impl std::ops::BitAnd for BitmapFlags { - type Output = Self; - - /// Returns the intersection between the two sets of flags. - #[inline] - fn bitand(self, other: Self) -> Self { - Self { - bits: self.bits & other.bits, - } - } -} - -impl std::ops::BitAndAssign for BitmapFlags { - /// Disables all flags disabled in the set. - #[inline] - fn bitand_assign(&mut self, other: Self) { - self.bits &= other.bits; - } -} - -impl std::ops::Sub for BitmapFlags { - type Output = Self; - - /// Returns the set difference of the two sets of flags. - #[inline] - fn sub(self, other: Self) -> Self { - Self { - bits: self.bits & !other.bits, - } - } -} - -impl std::ops::SubAssign for BitmapFlags { - /// Disables all flags enabled in the set. - #[inline] - fn sub_assign(&mut self, other: Self) { - self.bits &= !other.bits; - } -} - -impl std::ops::Not for BitmapFlags { - type Output = Self; - - /// Returns the complement of this set of flags. - #[inline] - fn not(self) -> Self { - Self { bits: !self.bits } & Self::all() - } -} - -impl std::fmt::Debug for BitmapFlags { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let members: &[(&str, Self)] = &[ - ("HORIZONTAL_METRICS", Self::HORIZONTAL_METRICS), - ("VERTICAL_METRICS", Self::VERTICAL_METRICS), - ]; - let mut first = true; - for (name, value) in members { - if self.contains(*value) { - if !first { - f.write_str(" | ")?; - } - first = false; - f.write_str(name)?; - } - } - if first { - f.write_str("(empty)")?; - } - Ok(()) - } -} - -impl std::fmt::Binary for BitmapFlags { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - std::fmt::Binary::fmt(&self.bits, f) - } -} - -impl std::fmt::Octal for BitmapFlags { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - std::fmt::Octal::fmt(&self.bits, f) - } -} - -impl std::fmt::LowerHex for BitmapFlags { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - std::fmt::LowerHex::fmt(&self.bits, f) - } -} - -impl std::fmt::UpperHex for BitmapFlags { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - std::fmt::UpperHex::fmt(&self.bits, f) - } -} - -impl font_types::Scalar for BitmapFlags { - type Raw = ::Raw; - fn to_raw(self) -> Self::Raw { - self.bits().to_raw() - } - fn from_raw(raw: Self::Raw) -> Self { - let t = ::from_raw(raw); - Self::from_bits_truncate(t) - } -} - -#[cfg(feature = "traversal")] -impl<'a> From for FieldType<'a> { - fn from(src: BitmapFlags) -> FieldType<'a> { - src.bits().into() - } -} - -/// [BigGlyphMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bigglyphmetrics) record. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] -#[repr(packed)] -pub struct BigGlyphMetrics { - /// Number of rows of data. - pub height: u8, - /// Number of columns of data. - pub width: u8, - /// Distance in pixels from the horizontal origin to the left edge of the bitmap. - pub hori_bearing_x: BigEndian, - /// Distance in pixels from the horizontal origin to the top edge of the bitmap. - pub hori_bearing_y: BigEndian, - /// Horizontal advance width in pixels. - pub hori_advance: u8, - /// Distance in pixels from the vertical origin to the left edge of the bitmap. - pub vert_bearing_x: BigEndian, - /// Distance in pixels from the vertical origin to the top edge of the bitmap. - pub vert_bearing_y: BigEndian, - /// Vertical advance width in pixels. - pub vert_advance: u8, -} - -impl BigGlyphMetrics { - /// Number of rows of data. - pub fn height(&self) -> u8 { - self.height - } - - /// Number of columns of data. - pub fn width(&self) -> u8 { - self.width - } - - /// Distance in pixels from the horizontal origin to the left edge of the bitmap. - pub fn hori_bearing_x(&self) -> i8 { - self.hori_bearing_x.get() - } - - /// Distance in pixels from the horizontal origin to the top edge of the bitmap. - pub fn hori_bearing_y(&self) -> i8 { - self.hori_bearing_y.get() - } - - /// Horizontal advance width in pixels. - pub fn hori_advance(&self) -> u8 { - self.hori_advance - } - - /// Distance in pixels from the vertical origin to the left edge of the bitmap. - pub fn vert_bearing_x(&self) -> i8 { - self.vert_bearing_x.get() - } - - /// Distance in pixels from the vertical origin to the top edge of the bitmap. - pub fn vert_bearing_y(&self) -> i8 { - self.vert_bearing_y.get() - } - - /// Vertical advance width in pixels. - pub fn vert_advance(&self) -> u8 { - self.vert_advance - } -} - -impl FixedSize for BigGlyphMetrics { - const RAW_BYTE_LEN: usize = u8::RAW_BYTE_LEN - + u8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + u8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + u8::RAW_BYTE_LEN; -} - -impl sealed::Sealed for BigGlyphMetrics {} - -/// SAFETY: see the [`FromBytes`] trait documentation. -unsafe impl FromBytes for BigGlyphMetrics { - fn this_trait_should_only_be_implemented_in_generated_code() {} -} - -#[cfg(feature = "traversal")] -impl<'a> SomeRecord<'a> for BigGlyphMetrics { - fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { - RecordResolver { - name: "BigGlyphMetrics", - get_field: Box::new(move |idx, _data| match idx { - 0usize => Some(Field::new("height", self.height())), - 1usize => Some(Field::new("width", self.width())), - 2usize => Some(Field::new("hori_bearing_x", self.hori_bearing_x())), - 3usize => Some(Field::new("hori_bearing_y", self.hori_bearing_y())), - 4usize => Some(Field::new("hori_advance", self.hori_advance())), - 5usize => Some(Field::new("vert_bearing_x", self.vert_bearing_x())), - 6usize => Some(Field::new("vert_bearing_y", self.vert_bearing_y())), - 7usize => Some(Field::new("vert_advance", self.vert_advance())), - _ => None, - }), - data, - } - } -} - -/// [SmallGlyphMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#smallglyphmetrics) record. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] -#[repr(packed)] -pub struct SmallGlyphMetrics { - /// Number of rows of data. - pub height: u8, - /// Number of columns of data. - pub width: u8, - /// Distance in pixels from the horizontal origin to the left edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the top edge of the bitmap (for vertical text). - pub bearing_x: BigEndian, - /// Distance in pixels from the horizontal origin to the top edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the left edge of the bitmap (for vertical text). - pub bearing_y: BigEndian, - /// Horizontal or vertical advance width in pixels. - pub advance: u8, -} - -impl SmallGlyphMetrics { - /// Number of rows of data. - pub fn height(&self) -> u8 { - self.height - } - - /// Number of columns of data. - pub fn width(&self) -> u8 { - self.width - } - - /// Distance in pixels from the horizontal origin to the left edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the top edge of the bitmap (for vertical text). - pub fn bearing_x(&self) -> i8 { - self.bearing_x.get() - } - - /// Distance in pixels from the horizontal origin to the top edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the left edge of the bitmap (for vertical text). - pub fn bearing_y(&self) -> i8 { - self.bearing_y.get() - } - - /// Horizontal or vertical advance width in pixels. - pub fn advance(&self) -> u8 { - self.advance - } -} - -impl FixedSize for SmallGlyphMetrics { - const RAW_BYTE_LEN: usize = u8::RAW_BYTE_LEN - + u8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + i8::RAW_BYTE_LEN - + u8::RAW_BYTE_LEN; -} - -impl sealed::Sealed for SmallGlyphMetrics {} - -/// SAFETY: see the [`FromBytes`] trait documentation. -unsafe impl FromBytes for SmallGlyphMetrics { - fn this_trait_should_only_be_implemented_in_generated_code() {} -} - -#[cfg(feature = "traversal")] -impl<'a> SomeRecord<'a> for SmallGlyphMetrics { - fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { - RecordResolver { - name: "SmallGlyphMetrics", - get_field: Box::new(move |idx, _data| match idx { - 0usize => Some(Field::new("height", self.height())), - 1usize => Some(Field::new("width", self.width())), - 2usize => Some(Field::new("bearing_x", self.bearing_x())), - 3usize => Some(Field::new("bearing_y", self.bearing_y())), - 4usize => Some(Field::new("advance", self.advance())), - _ => None, - }), - data, - } - } -} - -/// [IndexSubtableArray](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtablearray) record. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] -#[repr(packed)] -pub struct IndexSubtableArray { - /// First glyph ID of this range. - pub first_glyph_index: BigEndian, - /// Last glyph ID of this range (inclusive). - pub last_glyph_index: BigEndian, - /// Add to indexSubTableArrayOffset to get offset from beginning of EBLC. - pub additional_offset_to_index_subtable: BigEndian, -} - -impl IndexSubtableArray { - /// First glyph ID of this range. - pub fn first_glyph_index(&self) -> GlyphId { - self.first_glyph_index.get() - } - - /// Last glyph ID of this range (inclusive). - pub fn last_glyph_index(&self) -> GlyphId { - self.last_glyph_index.get() - } - - /// Add to indexSubTableArrayOffset to get offset from beginning of EBLC. - pub fn additional_offset_to_index_subtable(&self) -> u32 { - self.additional_offset_to_index_subtable.get() - } -} - -impl FixedSize for IndexSubtableArray { - const RAW_BYTE_LEN: usize = GlyphId::RAW_BYTE_LEN + GlyphId::RAW_BYTE_LEN + u32::RAW_BYTE_LEN; -} - -impl sealed::Sealed for IndexSubtableArray {} - -/// SAFETY: see the [`FromBytes`] trait documentation. -unsafe impl FromBytes for IndexSubtableArray { - fn this_trait_should_only_be_implemented_in_generated_code() {} -} - -#[cfg(feature = "traversal")] -impl<'a> SomeRecord<'a> for IndexSubtableArray { - fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { - RecordResolver { - name: "IndexSubtableArray", - get_field: Box::new(move |idx, _data| match idx { - 0usize => Some(Field::new("first_glyph_index", self.first_glyph_index())), - 1usize => Some(Field::new("last_glyph_index", self.last_glyph_index())), - 2usize => Some(Field::new( - "additional_offset_to_index_subtable", - self.additional_offset_to_index_subtable(), - )), - _ => None, - }), - data, - } - } -} diff --git a/read-fonts/generated/generated_ebdt.rs b/read-fonts/generated/generated_ebdt.rs new file mode 100644 index 000000000..3002c4532 --- /dev/null +++ b/read-fonts/generated/generated_ebdt.rs @@ -0,0 +1,74 @@ +// THIS FILE IS AUTOGENERATED. +// Any changes to this file will be overwritten. +// For more information about how codegen works, see font-codegen/README.md + +#[allow(unused_imports)] +use crate::codegen_prelude::*; + +/// The [Embedded Bitmap Data](https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt) table +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub struct EbdtMarker {} + +impl EbdtMarker { + fn major_version_byte_range(&self) -> Range { + let start = 0; + start..start + u16::RAW_BYTE_LEN + } + fn minor_version_byte_range(&self) -> Range { + let start = self.major_version_byte_range().end; + start..start + u16::RAW_BYTE_LEN + } +} + +impl TopLevelTable for Ebdt<'_> { + /// `EBDT` + const TAG: Tag = Tag::new(b"EBDT"); +} + +impl<'a> FontRead<'a> for Ebdt<'a> { + fn read(data: FontData<'a>) -> Result { + let mut cursor = data.cursor(); + cursor.advance::(); + cursor.advance::(); + cursor.finish(EbdtMarker {}) + } +} + +/// The [Embedded Bitmap Data](https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt) table +pub type Ebdt<'a> = TableRef<'a, EbdtMarker>; + +impl<'a> Ebdt<'a> { + /// Major version of the EBDT table, = 2. + pub fn major_version(&self) -> u16 { + let range = self.shape.major_version_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Minor version of EBDT table, = 0. + pub fn minor_version(&self) -> u16 { + let range = self.shape.minor_version_byte_range(); + self.data.read_at(range.start).unwrap() + } +} + +#[cfg(feature = "traversal")] +impl<'a> SomeTable<'a> for Ebdt<'a> { + fn type_name(&self) -> &str { + "Ebdt" + } + fn get_field(&self, idx: usize) -> Option> { + match idx { + 0usize => Some(Field::new("major_version", self.major_version())), + 1usize => Some(Field::new("minor_version", self.minor_version())), + _ => None, + } + } +} + +#[cfg(feature = "traversal")] +impl<'a> std::fmt::Debug for Ebdt<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (self as &dyn SomeTable<'a>).fmt(f) + } +} diff --git a/read-fonts/generated/generated_eblc.rs b/read-fonts/generated/generated_eblc.rs new file mode 100644 index 000000000..f9ac0c8b3 --- /dev/null +++ b/read-fonts/generated/generated_eblc.rs @@ -0,0 +1,110 @@ +// THIS FILE IS AUTOGENERATED. +// Any changes to this file will be overwritten. +// For more information about how codegen works, see font-codegen/README.md + +#[allow(unused_imports)] +use crate::codegen_prelude::*; + +/// The [Embedded Bitmap Location](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc) table +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub struct EblcMarker { + bitmap_sizes_byte_len: usize, +} + +impl EblcMarker { + fn major_version_byte_range(&self) -> Range { + let start = 0; + start..start + u16::RAW_BYTE_LEN + } + fn minor_version_byte_range(&self) -> Range { + let start = self.major_version_byte_range().end; + start..start + u16::RAW_BYTE_LEN + } + fn num_sizes_byte_range(&self) -> Range { + let start = self.minor_version_byte_range().end; + start..start + u32::RAW_BYTE_LEN + } + fn bitmap_sizes_byte_range(&self) -> Range { + let start = self.num_sizes_byte_range().end; + start..start + self.bitmap_sizes_byte_len + } +} + +impl TopLevelTable for Eblc<'_> { + /// `EBLC` + const TAG: Tag = Tag::new(b"EBLC"); +} + +impl<'a> FontRead<'a> for Eblc<'a> { + fn read(data: FontData<'a>) -> Result { + let mut cursor = data.cursor(); + cursor.advance::(); + cursor.advance::(); + let num_sizes: u32 = cursor.read()?; + let bitmap_sizes_byte_len = num_sizes as usize * BitmapSize::RAW_BYTE_LEN; + cursor.advance_by(bitmap_sizes_byte_len); + cursor.finish(EblcMarker { + bitmap_sizes_byte_len, + }) + } +} + +/// The [Embedded Bitmap Location](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc) table +pub type Eblc<'a> = TableRef<'a, EblcMarker>; + +impl<'a> Eblc<'a> { + /// Major version of the EBLC table, = 2. + pub fn major_version(&self) -> u16 { + let range = self.shape.major_version_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Minor version of EBLC table, = 0. + pub fn minor_version(&self) -> u16 { + let range = self.shape.minor_version_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// Number of BitmapSize records. + pub fn num_sizes(&self) -> u32 { + let range = self.shape.num_sizes_byte_range(); + self.data.read_at(range.start).unwrap() + } + + /// BitmapSize records array. + pub fn bitmap_sizes(&self) -> &'a [BitmapSize] { + let range = self.shape.bitmap_sizes_byte_range(); + self.data.read_array(range).unwrap() + } +} + +#[cfg(feature = "traversal")] +impl<'a> SomeTable<'a> for Eblc<'a> { + fn type_name(&self) -> &str { + "Eblc" + } + fn get_field(&self, idx: usize) -> Option> { + match idx { + 0usize => Some(Field::new("major_version", self.major_version())), + 1usize => Some(Field::new("minor_version", self.minor_version())), + 2usize => Some(Field::new("num_sizes", self.num_sizes())), + 3usize => Some(Field::new( + "bitmap_sizes", + traversal::FieldType::array_of_records( + stringify!(BitmapSize), + self.bitmap_sizes(), + self.offset_data(), + ), + )), + _ => None, + } + } +} + +#[cfg(feature = "traversal")] +impl<'a> std::fmt::Debug for Eblc<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (self as &dyn SomeTable<'a>).fmt(f) + } +} diff --git a/read-fonts/src/table_provider.rs b/read-fonts/src/table_provider.rs index 4a4919b93..2f246875b 100644 --- a/read-fonts/src/table_provider.rs +++ b/read-fonts/src/table_provider.rs @@ -142,6 +142,18 @@ pub trait TableProvider<'a> { self.expect_table() } + fn cbdt(&self) -> Result, ReadError> { + self.expect_table() + } + + fn eblc(&self) -> Result, ReadError> { + self.expect_table() + } + + fn ebdt(&self) -> Result, ReadError> { + self.expect_table() + } + fn sbix(&self) -> Result, ReadError> { // should we make the user pass this in? let num_glyphs = self.maxp().map(|maxp| maxp.num_glyphs())?; diff --git a/read-fonts/src/tables.rs b/read-fonts/src/tables.rs index 7dde4ea74..93e29bb7a 100644 --- a/read-fonts/src/tables.rs +++ b/read-fonts/src/tables.rs @@ -2,12 +2,16 @@ pub mod avar; pub mod base; +pub mod bitmap; +pub mod cbdt; pub mod cblc; pub mod cff; pub mod cff2; pub mod cmap; pub mod colr; pub mod cpal; +pub mod ebdt; +pub mod eblc; pub mod fvar; pub mod gdef; pub mod glyf; diff --git a/read-fonts/src/tables/bitmap.rs b/read-fonts/src/tables/bitmap.rs new file mode 100644 index 000000000..a25e16099 --- /dev/null +++ b/read-fonts/src/tables/bitmap.rs @@ -0,0 +1,192 @@ +//! Common bitmap (EBLC/EBDT/CBLC/CBDT) types. + +include!("../../generated/generated_bitmap.rs"); + +impl BitmapSize { + pub fn subtable<'a>( + &self, + offset_data: FontData<'a>, + index: u32, + ) -> Result, ReadError> { + let base_offset = self.index_subtable_array_offset() as usize; + const SUBTABLE_HEADER_SIZE: usize = 8; + let header_offset = base_offset + index as usize * SUBTABLE_HEADER_SIZE; + let header_data = offset_data + .slice(header_offset..) + .ok_or(ReadError::OutOfBounds)?; + let header = IndexSubtableArray::read(header_data)?; + let subtable_offset = base_offset + header.additional_offset_to_index_subtable() as usize; + let subtable_data = offset_data + .slice(subtable_offset..) + .ok_or(ReadError::OutOfBounds)?; + let subtable = IndexSubtable::read(subtable_data)?; + Ok(BitmapSizeSubtable { + first_glyph_index: header.first_glyph_index(), + last_glyph_index: header.last_glyph_index(), + kind: subtable, + }) + } + + pub fn location( + &self, + offset_data: FontData, + glyph_id: GlyphId, + ) -> Result { + if !(self.start_glyph_index()..=self.end_glyph_index()).contains(&glyph_id) { + return Err(ReadError::OutOfBounds); + } + let mut location = BitmapLocation::default(); + for ix in 0..self.number_of_index_subtables() { + let subtable = self.subtable(offset_data, ix)?; + if !(subtable.first_glyph_index..=subtable.last_glyph_index).contains(&glyph_id) { + continue; + } + // glyph index relative to the first glyph in the subtable + let glyph_ix = + glyph_id.to_u16() as usize - subtable.first_glyph_index.to_u16() as usize; + match &subtable.kind { + IndexSubtable::Format1(st) => { + location.format = st.image_format(); + location.data_offset = st.image_data_offset() as usize + + st.sbit_offsets() + .get(glyph_ix) + .ok_or(ReadError::OutOfBounds)? + .get() as usize; + } + IndexSubtable::Format2(st) => { + location.format = st.image_format(); + let data_size = st.image_size() as usize; + location.data_size = Some(data_size); + location.data_offset = st.image_data_offset() as usize + glyph_ix * data_size; + location.metrics = Some(st.big_metrics()[0].clone()); + } + IndexSubtable::Format3(st) => { + location.format = st.image_format(); + location.data_offset = st.image_data_offset() as usize + + st.sbit_offsets() + .get(glyph_ix) + .ok_or(ReadError::OutOfBounds)? + .get() as usize; + } + IndexSubtable::Format4(st) => { + location.format = st.image_format(); + let array = st.glyph_array(); + let array_ix = match array.binary_search_by(|x| x.glyph_id().cmp(&glyph_id)) { + Ok(ix) => ix, + _ => { + return Err(ReadError::InvalidCollectionIndex(glyph_id.to_u16() as u32)) + } + }; + let offset1 = array[array_ix].sbit_offset() as usize; + let offset2 = array + .get(array_ix + 1) + .ok_or(ReadError::OutOfBounds)? + .sbit_offset() as usize; + location.data_offset = offset1; + location.data_size = Some(offset2 - offset1); + } + IndexSubtable::Format5(st) => { + location.format = st.image_format(); + let array = st.glyph_array(); + if array.binary_search_by(|x| x.get().cmp(&glyph_id)).is_err() { + return Err(ReadError::InvalidCollectionIndex(glyph_id.to_u16() as u32)); + } + let data_size = st.image_size() as usize; + location.data_size = Some(data_size); + location.data_offset = st.image_data_offset() as usize + glyph_ix * data_size; + location.metrics = Some(st.big_metrics()[0].clone()); + } + } + return Ok(location); + } + Err(ReadError::OutOfBounds) + } +} + +pub struct BitmapSizeSubtable<'a> { + pub first_glyph_index: GlyphId, + pub last_glyph_index: GlyphId, + pub kind: IndexSubtable<'a>, +} + +#[derive(Clone, Default)] +pub struct BitmapLocation { + /// Format of EBDT/CBDT image data. + pub format: u16, + /// Offset in EBDT/CBDT table. + pub data_offset: usize, + pub data_size: Option, + pub metrics: Option, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum BitmapFormat { + Mask, + PackedMask, + Color, + Png, +} + +#[derive(Clone)] +pub enum BitmapMetrics { + Small(SmallGlyphMetrics), + Big(BigGlyphMetrics), +} + +#[derive(Clone)] +pub struct BitmapData<'a> { + pub format: BitmapFormat, + pub metrics: Option, + pub data: &'a [u8], +} + +pub(crate) fn bitmap_data<'a>( + offset_data: FontData<'a>, + location: &BitmapLocation, + is_color: bool, +) -> Result, ReadError> { + let mut image_data = offset_data + .slice(location.data_offset..) + .ok_or(ReadError::OutOfBounds)? + .cursor(); + match location.format { + 17 if is_color => { + let metrics = image_data.read_array::(1)?[0].clone(); + let data_len = image_data.read::()? as usize; + let data = image_data.read_array::(data_len)?; + return Ok(BitmapData { + format: BitmapFormat::Png, + metrics: Some(BitmapMetrics::Small(metrics)), + data, + }); + } + 18 if is_color => { + let metrics = image_data.read_array::(1)?[0].clone(); + let data_len = image_data.read::()? as usize; + let data = image_data.read_array::(data_len)?; + return Ok(BitmapData { + format: BitmapFormat::Png, + metrics: Some(BitmapMetrics::Big(metrics)), + data, + }); + } + 19 if is_color => { + let data_len = image_data.read::()? as usize; + let data = image_data.read_array::(data_len)?; + return Ok(BitmapData { + format: BitmapFormat::Png, + metrics: None, + data, + }); + } + _ => {} + } + Err(ReadError::MalformedData("bad image format")) +} + +#[cfg(feature = "traversal")] +impl SbitLineMetrics { + pub(crate) fn traversal_type<'a>(&self, data: FontData<'a>) -> FieldType<'a> { + FieldType::Record(self.clone().traverse(data)) + } +} diff --git a/read-fonts/src/tables/cbdt.rs b/read-fonts/src/tables/cbdt.rs new file mode 100644 index 000000000..c2c723414 --- /dev/null +++ b/read-fonts/src/tables/cbdt.rs @@ -0,0 +1,11 @@ +//! The [CBDT (Color Bitmap Data)](https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt) table + +use super::bitmap::{BitmapData, BitmapLocation}; + +include!("../../generated/generated_cbdt.rs"); + +impl<'a> Cbdt<'a> { + pub fn data(&self, location: &BitmapLocation) -> Result, ReadError> { + super::bitmap::bitmap_data(self.offset_data(), location, true) + } +} diff --git a/read-fonts/src/tables/cblc.rs b/read-fonts/src/tables/cblc.rs index 956f75723..abeba74df 100644 --- a/read-fonts/src/tables/cblc.rs +++ b/read-fonts/src/tables/cblc.rs @@ -1,14 +1,5 @@ //! The [CBLC (Color Bitmap Location)](https://docs.microsoft.com/en-us/typography/opentype/spec/cblc) table -include!("../../generated/generated_cblc.rs"); - -#[cfg(feature = "traversal")] -impl SbitLineMetrics { - pub(crate) fn traversal_type<'a>(&self, data: FontData<'a>) -> FieldType<'a> { - FieldType::Record(self.clone().traverse(data)) - } +use super::bitmap::*; - pub(crate) fn get_field<'a>(&self, idx: usize, data: FontData<'a>) -> Option> { - None - } -} +include!("../../generated/generated_cblc.rs"); diff --git a/read-fonts/src/tables/ebdt.rs b/read-fonts/src/tables/ebdt.rs new file mode 100644 index 000000000..daa6755b4 --- /dev/null +++ b/read-fonts/src/tables/ebdt.rs @@ -0,0 +1,11 @@ +//! The [EBDT (Embedded Bitmap Data)](https://docs.microsoft.com/en-us/typography/opentype/spec/ebdt) table + +use super::bitmap::{BitmapData, BitmapLocation}; + +include!("../../generated/generated_ebdt.rs"); + +impl<'a> Ebdt<'a> { + pub fn data(&self, location: &BitmapLocation) -> Result, ReadError> { + super::bitmap::bitmap_data(self.offset_data(), location, false) + } +} diff --git a/read-fonts/src/tables/eblc.rs b/read-fonts/src/tables/eblc.rs new file mode 100644 index 000000000..b8618e1a7 --- /dev/null +++ b/read-fonts/src/tables/eblc.rs @@ -0,0 +1,5 @@ +//! The [EBLC (Embedded Bitmap Location)](https://docs.microsoft.com/en-us/typography/opentype/spec/eblc) table + +use super::bitmap::*; + +include!("../../generated/generated_eblc.rs"); diff --git a/resources/codegen_inputs/bitmap.rs b/resources/codegen_inputs/bitmap.rs new file mode 100644 index 000000000..f9d6dea04 --- /dev/null +++ b/resources/codegen_inputs/bitmap.rs @@ -0,0 +1,194 @@ +#![parse_module(read_fonts::tables::bitmap)] + +/// [BitmapSize](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmapsize-record) record. +record BitmapSize { + /// Offset to index subtable from beginning of EBLC/CBLC. + index_subtable_array_offset: u32, + /// Number of bytes in corresponding index subtables and array. + index_tables_size: u32, + /// There is an index subtable for each range or format change. + number_of_index_subtables: u32, + /// Not used; set to 0. + color_ref: u32, + /// Line metrics for text rendered horizontally. + hori: SbitLineMetrics, + /// Line metrics for text rendered vertically. + vert: SbitLineMetrics, + /// Lowest glyph index for this size. + start_glyph_index: GlyphId, + /// Highest glyph index for this size. + end_glyph_index: GlyphId, + /// Horizontal pixels per em. + ppem_x: u8, + /// Vertical pixels per em. + ppem_y: u8, + /// The Microsoft rasterizer v.1.7 or greater supports the following + /// bitDepth values, as described below: 1, 2, 4, and 8 (and 32 for CBLC). + bit_depth: u8, + /// Vertical or horizontal. + flags: BitmapFlags, +} + +/// [SbitLineMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#sbitlinemetrics-record) record. +record SbitLineMetrics { + ascender: i8, + descender: i8, + width_max: u8, + caret_slope_numerator: i8, + caret_slope_denominator: u8, + caret_offset: i8, + min_origin_sb: i8, + min_advance_sb: i8, + max_before_bl: i8, + min_after_bl: i8, + pad1: i8, + pad2: i8, +} + +/// [Bitmap flags](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmap-flags). +flags u8 BitmapFlags { + /// Horizontal + HORIZONTAL_METRICS = 0x01, + /// Vertical + VERTICAL_METRICS = 0x02, +} + +/// [BigGlyphMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bigglyphmetrics) record. +record BigGlyphMetrics { + /// Number of rows of data. + height: u8, + /// Number of columns of data. + width: u8, + /// Distance in pixels from the horizontal origin to the left edge of the bitmap. + hori_bearing_x: i8, + /// Distance in pixels from the horizontal origin to the top edge of the bitmap. + hori_bearing_y: i8, + /// Horizontal advance width in pixels. + hori_advance: u8, + /// Distance in pixels from the vertical origin to the left edge of the bitmap. + vert_bearing_x: i8, + /// Distance in pixels from the vertical origin to the top edge of the bitmap. + vert_bearing_y: i8, + /// Vertical advance width in pixels. + vert_advance: u8, +} + +/// [SmallGlyphMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#smallglyphmetrics) record. +record SmallGlyphMetrics { + /// Number of rows of data. + height: u8, + /// Number of columns of data. + width: u8, + /// Distance in pixels from the horizontal origin to the left edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the top edge of the bitmap (for vertical text). + bearing_x: i8, + /// Distance in pixels from the horizontal origin to the top edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the left edge of the bitmap (for vertical text). + bearing_y: i8, + /// Horizontal or vertical advance width in pixels. + advance: u8, +} + +/// [IndexSubtableArray](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtablearray) table. +table IndexSubtableArray { + /// First glyph ID of this range. + first_glyph_index: GlyphId, + /// Last glyph ID of this range (inclusive). + last_glyph_index: GlyphId, + /// Add to indexSubTableArrayOffset to get offset from beginning of EBLC. + additional_offset_to_index_subtable: u32, +} + +/// [IndexSubtables](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtables) format type. +format u16 IndexSubtable { + Format1(IndexSubtable1), + Format2(IndexSubtable2), + Format3(IndexSubtable3), + Format4(IndexSubtable4), + Format5(IndexSubtable5), +} + +/// [IndexSubTable1](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable1-variable-metrics-glyphs-with-4-byte-offsets): variable-metrics glyphs with 4-byte offsets. +table IndexSubtable1 { + /// Format of this IndexSubTable. + #[format = 1] + index_format: u16, + /// Format of EBDT image data. + image_format: u16, + /// Offset to image data in EBDT table. + image_data_offset: u32, + #[count(..)] + sbit_offsets: [u32], +} + +/// [IndexSubTable2](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable2-all-glyphs-have-identical-metrics): all glyphs have identical metrics. +table IndexSubtable2 { + /// Format of this IndexSubTable. + #[format = 2] + index_format: u16, + /// Format of EBDT image data. + image_format: u16, + /// Offset to image data in EBDT table. + image_data_offset: u32, + /// All the glyphs are of the same size. + image_size: u32, + /// All glyphs have the same metrics; glyph data may be compressed, byte-aligned, or bit-aligned. + #[count(1)] + big_metrics: [BigGlyphMetrics], +} + +/// [IndexSubTable3](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable3-variable-metrics-glyphs-with-2-byte-offsets): variable-metrics glyphs with 2-byte offsets. +table IndexSubtable3 { + /// Format of this IndexSubTable. + #[format = 3] + index_format: u16, + /// Format of EBDT image data. + image_format: u16, + /// Offset to image data in EBDT table. + image_data_offset: u32, + #[count(..)] + sbit_offsets: [u16], +} + +/// [IndexSubTable4](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable3-variable-metrics-glyphs-with-2-byte-offsets): variable-metrics glyphs with sparse glyph codes. +table IndexSubtable4 { + /// Format of this IndexSubTable. + #[format = 4] + index_format: u16, + /// Format of EBDT image data. + image_format: u16, + /// Offset to image data in EBDT table. + image_data_offset: u32, + /// Array length. + num_glyphs: u32, + /// One per glyph. + #[count(add($num_glyphs, 1))] + glyph_array: [GlyphIdOffsetPair], +} + +/// [GlyphIdOffsetPair](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#glyphidoffsetpair-record) record. +record GlyphIdOffsetPair { + /// Glyph ID of glyph present. + glyph_id: GlyphId, + /// Location in EBDT. + sbit_offset: u16, +} + +/// [IndexSubTable5](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable5-constant-metrics-glyphs-with-sparse-glyph-codes): constant-metrics glyphs with sparse glyph codes +table IndexSubtable5 { + /// Format of this IndexSubTable. + #[format = 5] + index_format: u16, + /// Format of EBDT image data. + image_format: u16, + /// Offset to image data in EBDT table. + image_data_offset: u32, + /// All glyphs have the same data size. + image_size: u32, + /// All glyphs have the same metrics. + #[count(1)] + big_metrics: [BigGlyphMetrics], + /// Array length. + num_glyphs: u32, + /// One per glyph, sorted by glyhph ID. + #[count($num_glyphs)] + glyph_array: [GlyphId], +} diff --git a/resources/codegen_inputs/cbdt.rs b/resources/codegen_inputs/cbdt.rs new file mode 100644 index 000000000..92752e001 --- /dev/null +++ b/resources/codegen_inputs/cbdt.rs @@ -0,0 +1,12 @@ +#![parse_module(read_fonts::tables::cbdt)] + +/// The [Color Bitmap Data](https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt) table +#[tag = "CBDT"] +table Cbdt { + /// Major version of the CBDT table, = 3. + #[compile(3)] + major_version: u16, + /// Minor version of CBDT table, = 0. + #[compile(0)] + minor_version: u16, +} diff --git a/resources/codegen_inputs/cblc.rs b/resources/codegen_inputs/cblc.rs index 755fa8522..b952512d0 100644 --- a/resources/codegen_inputs/cblc.rs +++ b/resources/codegen_inputs/cblc.rs @@ -1,5 +1,7 @@ #![parse_module(read_fonts::tables::cblc)] +extern record BitmapSize; + /// The [Color Bitmap Location](https://learn.microsoft.com/en-us/typography/opentype/spec/cblc) table #[tag = "CBLC"] table Cblc { @@ -12,103 +14,7 @@ table Cblc { /// Number of BitmapSize records. #[compile(array_len($bitmap_sizes))] num_sizes: u32, + /// BitmapSize records array. #[count($num_sizes)] bitmap_sizes: [BitmapSize], } - -/// [BitmapSize](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmapsize-record) record. -record BitmapSize { - /// Offset to index subtable from beginning of EBLC/CBLC. - index_subtable_array_offset: u32, - /// Number of bytes in corresponding index subtables and array. - index_tables_size: u32, - /// There is an index subtable for each range or format change. - number_of_index_subtables: u32, - /// Not used; set to 0. - color_ref: u32, - /// Line metrics for text rendered horizontally. - hori: SbitLineMetrics, - /// Line metrics for text rendered vertically. - vert: SbitLineMetrics, - /// Lowest glyph index for this size. - start_glyph_index: GlyphId, - /// Highest glyph index for this size. - end_glyph_index: GlyphId, - /// Horizontal pixels per em. - ppem_x: u8, - /// Vertical pixels per em. - ppem_y: u8, - /// The Microsoft rasterizer v.1.7 or greater supports the following - /// bitDepth values, as described below: 1, 2, 4, and 8 (and 32 for CBLC). - bit_depth: u8, - /// Vertical or horizontal. - flags: BitmapFlags, -} - -/// [SbitLineMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#sbitlinemetrics-record) record. -record SbitLineMetrics { - ascender: i8, - descender: i8, - width_max: u8, - caret_slope_numerator: i8, - caret_slope_denominator: u8, - caret_offset: i8, - min_origin_sb: i8, - min_advance_sb: i8, - max_before_bl: i8, - min_after_bl: i8, - pad1: i8, - pad2: i8, -} - -/// [Bitmap flags](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bitmap-flags). -flags u8 BitmapFlags { - /// Horizontal - HORIZONTAL_METRICS = 0x01, - /// Vertical - VERTICAL_METRICS = 0x02, -} - -/// [BigGlyphMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#bigglyphmetrics) record. -record BigGlyphMetrics { - /// Number of rows of data. - height: u8, - /// Number of columns of data. - width: u8, - /// Distance in pixels from the horizontal origin to the left edge of the bitmap. - hori_bearing_x: i8, - /// Distance in pixels from the horizontal origin to the top edge of the bitmap. - hori_bearing_y: i8, - /// Horizontal advance width in pixels. - hori_advance: u8, - /// Distance in pixels from the vertical origin to the left edge of the bitmap. - vert_bearing_x: i8, - /// Distance in pixels from the vertical origin to the top edge of the bitmap. - vert_bearing_y: i8, - /// Vertical advance width in pixels. - vert_advance: u8, -} - -/// [SmallGlyphMetrics](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#smallglyphmetrics) record. -record SmallGlyphMetrics { - /// Number of rows of data. - height: u8, - /// Number of columns of data. - width: u8, - /// Distance in pixels from the horizontal origin to the left edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the top edge of the bitmap (for vertical text). - bearing_x: i8, - /// Distance in pixels from the horizontal origin to the top edge of the bitmap (for horizontal text); or distance in pixels from the vertical origin to the left edge of the bitmap (for vertical text). - bearing_y: i8, - /// Horizontal or vertical advance width in pixels. - advance: u8, -} - -/// [IndexSubtableArray](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtablearray) record. -record IndexSubtableArray { - /// First glyph ID of this range. - first_glyph_index: GlyphId, - /// Last glyph ID of this range (inclusive). - last_glyph_index: GlyphId, - /// Add to indexSubTableArrayOffset to get offset from beginning of EBLC. - additional_offset_to_index_subtable: u32, -} diff --git a/resources/codegen_inputs/ebdt.rs b/resources/codegen_inputs/ebdt.rs new file mode 100644 index 000000000..4b35bf6ed --- /dev/null +++ b/resources/codegen_inputs/ebdt.rs @@ -0,0 +1,12 @@ +#![parse_module(read_fonts::tables::ebdt)] + +/// The [Embedded Bitmap Data](https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt) table +#[tag = "EBDT"] +table Ebdt { + /// Major version of the EBDT table, = 2. + #[compile(2)] + major_version: u16, + /// Minor version of EBDT table, = 0. + #[compile(0)] + minor_version: u16, +} \ No newline at end of file diff --git a/resources/codegen_inputs/eblc.rs b/resources/codegen_inputs/eblc.rs new file mode 100644 index 000000000..2b71d8000 --- /dev/null +++ b/resources/codegen_inputs/eblc.rs @@ -0,0 +1,20 @@ +#![parse_module(read_fonts::tables::eblc)] + +extern record BitmapSize; + +/// The [Embedded Bitmap Location](https://learn.microsoft.com/en-us/typography/opentype/spec/eblc) table +#[tag = "EBLC"] +table Eblc { + /// Major version of the EBLC table, = 2. + #[compile(2)] + major_version: u16, + /// Minor version of EBLC table, = 0. + #[compile(0)] + minor_version: u16, + /// Number of BitmapSize records. + #[compile(array_len($bitmap_sizes))] + num_sizes: u32, + /// BitmapSize records array. + #[count($num_sizes)] + bitmap_sizes: [BitmapSize], +} diff --git a/resources/codegen_plan.toml b/resources/codegen_plan.toml index b558c46d9..52789ad4f 100644 --- a/resources/codegen_plan.toml +++ b/resources/codegen_plan.toml @@ -298,11 +298,31 @@ mode = "compile" source = "resources/codegen_inputs/sbix.rs" target = "write-fonts/generated/generated_sbix.rs" +[[generate]] +mode = "parse" +source = "resources/codegen_inputs/bitmap.rs" +target = "read-fonts/generated/generated_bitmap.rs" + +[[generate]] +mode = "parse" +source = "resources/codegen_inputs/eblc.rs" +target = "read-fonts/generated/generated_eblc.rs" + +[[generate]] +mode = "parse" +source = "resources/codegen_inputs/ebdt.rs" +target = "read-fonts/generated/generated_ebdt.rs" + [[generate]] mode = "parse" source = "resources/codegen_inputs/cblc.rs" target = "read-fonts/generated/generated_cblc.rs" +[[generate]] +mode = "parse" +source = "resources/codegen_inputs/cbdt.rs" +target = "read-fonts/generated/generated_cbdt.rs" + # modules just used for testing [[generate]] mode = "parse" From 181a2b2a85c9d41681e02cdc567ad01364e5dd0c Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Sat, 7 Oct 2023 14:14:58 -0400 Subject: [PATCH 4/7] add EBDT formats --- read-fonts/generated/generated_bitmap.rs | 57 ++++++++ read-fonts/src/tables/bitmap.rs | 170 +++++++++++++++++++---- resources/codegen_inputs/bitmap.rs | 10 ++ 3 files changed, 210 insertions(+), 27 deletions(-) diff --git a/read-fonts/generated/generated_bitmap.rs b/read-fonts/generated/generated_bitmap.rs index 5a0165064..281fe5ef9 100644 --- a/read-fonts/generated/generated_bitmap.rs +++ b/read-fonts/generated/generated_bitmap.rs @@ -1505,3 +1505,60 @@ impl<'a> std::fmt::Debug for IndexSubtable5<'a> { (self as &dyn SomeTable<'a>).fmt(f) } } + +/// [EbdtComponent](https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#ebdtcomponent-record) record. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(C)] +#[repr(packed)] +pub struct BdtComponent { + /// Component glyph ID. + pub glyph_id: BigEndian, + /// Position of component left. + pub x_offset: BigEndian, + /// Position of component top. + pub y_offset: BigEndian, +} + +impl BdtComponent { + /// Component glyph ID. + pub fn glyph_id(&self) -> GlyphId { + self.glyph_id.get() + } + + /// Position of component left. + pub fn x_offset(&self) -> i8 { + self.x_offset.get() + } + + /// Position of component top. + pub fn y_offset(&self) -> i8 { + self.y_offset.get() + } +} + +impl FixedSize for BdtComponent { + const RAW_BYTE_LEN: usize = GlyphId::RAW_BYTE_LEN + i8::RAW_BYTE_LEN + i8::RAW_BYTE_LEN; +} + +impl sealed::Sealed for BdtComponent {} + +/// SAFETY: see the [`FromBytes`] trait documentation. +unsafe impl FromBytes for BdtComponent { + fn this_trait_should_only_be_implemented_in_generated_code() {} +} + +#[cfg(feature = "traversal")] +impl<'a> SomeRecord<'a> for BdtComponent { + fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> { + RecordResolver { + name: "BdtComponent", + get_field: Box::new(move |idx, _data| match idx { + 0usize => Some(Field::new("glyph_id", self.glyph_id())), + 1usize => Some(Field::new("x_offset", self.x_offset())), + 2usize => Some(Field::new("y_offset", self.y_offset())), + _ => None, + }), + data, + } + } +} diff --git a/read-fonts/src/tables/bitmap.rs b/read-fonts/src/tables/bitmap.rs index a25e16099..e5badc4f9 100644 --- a/read-fonts/src/tables/bitmap.rs +++ b/read-fonts/src/tables/bitmap.rs @@ -35,7 +35,10 @@ impl BitmapSize { if !(self.start_glyph_index()..=self.end_glyph_index()).contains(&glyph_id) { return Err(ReadError::OutOfBounds); } - let mut location = BitmapLocation::default(); + let mut location = BitmapLocation { + bit_depth: self.bit_depth, + ..BitmapLocation::default() + }; for ix in 0..self.number_of_index_subtables() { let subtable = self.subtable(offset_data, ix)?; if !(subtable.first_glyph_index..=subtable.last_glyph_index).contains(&glyph_id) { @@ -116,14 +119,14 @@ pub struct BitmapLocation { /// Offset in EBDT/CBDT table. pub data_offset: usize, pub data_size: Option, + pub bit_depth: u8, pub metrics: Option, } #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum BitmapFormat { - Mask, - PackedMask, - Color, +pub enum BitmapDataFormat { + BitAligned, + ByteAligned, Png, } @@ -135,9 +138,14 @@ pub enum BitmapMetrics { #[derive(Clone)] pub struct BitmapData<'a> { - pub format: BitmapFormat, - pub metrics: Option, - pub data: &'a [u8], + pub metrics: BitmapMetrics, + pub content: BitmapContent<'a>, +} + +#[derive(Clone)] +pub enum BitmapContent<'a> { + Data(BitmapDataFormat, &'a [u8]), + Composite(&'a [BdtComponent]), } pub(crate) fn bitmap_data<'a>( @@ -150,38 +158,146 @@ pub(crate) fn bitmap_data<'a>( .ok_or(ReadError::OutOfBounds)? .cursor(); match location.format { + // Small metrics, byte-aligned data + // + 1 => { + let metrics = read_small_metrics(&mut image_data)?; + // The data for each row is padded to a byte boundary + let pitch = (metrics.width as usize * location.bit_depth as usize + 7) / 8; + let height = metrics.height as usize; + let data = image_data.read_array::(pitch * height)?; + Ok(BitmapData { + metrics: BitmapMetrics::Small(metrics), + content: BitmapContent::Data(BitmapDataFormat::ByteAligned, data), + }) + } + // Small metrics, bit-aligned data + // + 2 => { + let metrics = read_small_metrics(&mut image_data)?; + let width = metrics.width as usize * location.bit_depth as usize; + let height = metrics.height as usize; + // The data is tightly packed + let data = image_data.read_array::((width * height + 7) / 8)?; + Ok(BitmapData { + metrics: BitmapMetrics::Small(metrics), + content: BitmapContent::Data(BitmapDataFormat::BitAligned, data), + }) + } + // Format 3 is obsolete + // + // Format 4 is not supported + // + // --- + // Metrics in EBLC/CBLC, bit-aligned image data only + // + 5 => { + let metrics = location.metrics.clone().ok_or(ReadError::MalformedData( + "expected metrics from location table", + ))?; + let width = metrics.width as usize * location.bit_depth as usize; + let height = metrics.height as usize; + // The data is tightly packed + let data = image_data.read_array::((width * height + 7) / 8)?; + Ok(BitmapData { + metrics: BitmapMetrics::Big(metrics), + content: BitmapContent::Data(BitmapDataFormat::BitAligned, data), + }) + } + // Big metrics, byte-aligned data + // + 6 => { + let metrics = read_big_metrics(&mut image_data)?; + // The data for each row is padded to a byte boundary + let pitch = (metrics.width as usize * location.bit_depth as usize + 7) / 8; + let height = metrics.height as usize; + let data = image_data.read_array::(pitch * height)?; + Ok(BitmapData { + metrics: BitmapMetrics::Big(metrics), + content: BitmapContent::Data(BitmapDataFormat::ByteAligned, data), + }) + } + // Big metrics, bit-aligned data + // + 7 => { + let metrics = read_big_metrics(&mut image_data)?; + let width = metrics.width as usize * location.bit_depth as usize; + let height = metrics.height as usize; + // The data is tightly packed + let data = image_data.read_array::((width * height + 7) / 8)?; + Ok(BitmapData { + metrics: BitmapMetrics::Big(metrics), + content: BitmapContent::Data(BitmapDataFormat::BitAligned, data), + }) + } + // Small metrics, component data + // + 8 => { + let metrics = read_small_metrics(&mut image_data)?; + let _pad = image_data.read::()?; + let count = image_data.read::()? as usize; + let components = image_data.read_array::(count)?; + Ok(BitmapData { + metrics: BitmapMetrics::Small(metrics), + content: BitmapContent::Composite(components), + }) + } + // Big metrics, component data + // + 9 => { + let metrics = read_big_metrics(&mut image_data)?; + let count = image_data.read::()? as usize; + let components = image_data.read_array::(count)?; + Ok(BitmapData { + metrics: BitmapMetrics::Big(metrics), + content: BitmapContent::Composite(components), + }) + } + // Small metrics, PNG image data + // 17 if is_color => { - let metrics = image_data.read_array::(1)?[0].clone(); + let metrics = read_small_metrics(&mut image_data)?; let data_len = image_data.read::()? as usize; let data = image_data.read_array::(data_len)?; - return Ok(BitmapData { - format: BitmapFormat::Png, - metrics: Some(BitmapMetrics::Small(metrics)), - data, - }); + Ok(BitmapData { + metrics: BitmapMetrics::Small(metrics), + content: BitmapContent::Data(BitmapDataFormat::Png, data), + }) } + // Big metrics, PNG image data + // 18 if is_color => { - let metrics = image_data.read_array::(1)?[0].clone(); + let metrics = read_big_metrics(&mut image_data)?; let data_len = image_data.read::()? as usize; let data = image_data.read_array::(data_len)?; - return Ok(BitmapData { - format: BitmapFormat::Png, - metrics: Some(BitmapMetrics::Big(metrics)), - data, - }); + Ok(BitmapData { + metrics: BitmapMetrics::Big(metrics), + content: BitmapContent::Data(BitmapDataFormat::Png, data), + }) } + // Metrics in CBLC table, PNG image data + // 19 if is_color => { + let metrics = location.metrics.clone().ok_or(ReadError::MalformedData( + "expected metrics from location table", + ))?; let data_len = image_data.read::()? as usize; let data = image_data.read_array::(data_len)?; - return Ok(BitmapData { - format: BitmapFormat::Png, - metrics: None, - data, - }); + Ok(BitmapData { + metrics: BitmapMetrics::Big(metrics), + content: BitmapContent::Data(BitmapDataFormat::Png, data), + }) } - _ => {} + _ => Err(ReadError::MalformedData("unexpected bitmap data format")), } - Err(ReadError::MalformedData("bad image format")) +} + +fn read_small_metrics(cursor: &mut Cursor) -> Result { + Ok(cursor.read_array::(1)?[0].clone()) +} + +fn read_big_metrics(cursor: &mut Cursor) -> Result { + Ok(cursor.read_array::(1)?[0].clone()) } #[cfg(feature = "traversal")] diff --git a/resources/codegen_inputs/bitmap.rs b/resources/codegen_inputs/bitmap.rs index f9d6dea04..83f0e4f1c 100644 --- a/resources/codegen_inputs/bitmap.rs +++ b/resources/codegen_inputs/bitmap.rs @@ -192,3 +192,13 @@ table IndexSubtable5 { #[count($num_glyphs)] glyph_array: [GlyphId], } + +/// [EbdtComponent](https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#ebdtcomponent-record) record. +record BdtComponent { + /// Component glyph ID. + glyph_id: GlyphId, + /// Position of component left. + x_offset: i8, + /// Position of component top. + y_offset: i8, +} From b4d8653a91d0bc69278d303fdd6199131dbc075c Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Sat, 7 Oct 2023 14:28:02 -0400 Subject: [PATCH 5/7] appease clippy --- font-codegen/src/fields.rs | 12 +++++++++--- read-fonts/generated/generated_bitmap.rs | 4 ++-- read-fonts/src/tables/bitmap.rs | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/font-codegen/src/fields.rs b/font-codegen/src/fields.rs index b90087bb0..5bda0d3f0 100644 --- a/font-codegen/src/fields.rs +++ b/font-codegen/src/fields.rs @@ -7,8 +7,8 @@ use quote::{quote, ToTokens}; use syn::spanned::Spanned; use super::parsing::{ - logged_syn_error, Attr, Count, CustomCompile, Field, FieldReadArgs, FieldType, FieldValidation, - Fields, NeededWhen, OffsetTarget, Phase, Record, ReferencedFields, + logged_syn_error, Attr, Count, CountArg, CustomCompile, Field, FieldReadArgs, FieldType, + FieldValidation, Fields, NeededWhen, OffsetTarget, Phase, Record, ReferencedFields, }; impl Fields { @@ -1017,7 +1017,13 @@ impl Field { } _ => unreachable!("count not valid here"), }; - quote!( #count_expr * #size_expr ) + match other { + Count::SingleArg(CountArg::Literal(lit)) if lit.base10_digits() == "1" => { + // Prevent identity-op clippy error with `1 * size` + size_expr + } + _ => quote!( #count_expr * #size_expr ), + } } None => quote!(compile_error!("missing count attribute?")), }; diff --git a/read-fonts/generated/generated_bitmap.rs b/read-fonts/generated/generated_bitmap.rs index 281fe5ef9..e9144dfcd 100644 --- a/read-fonts/generated/generated_bitmap.rs +++ b/read-fonts/generated/generated_bitmap.rs @@ -1030,7 +1030,7 @@ impl<'a> FontRead<'a> for IndexSubtable2<'a> { cursor.advance::(); cursor.advance::(); cursor.advance::(); - let big_metrics_byte_len = 1_usize * BigGlyphMetrics::RAW_BYTE_LEN; + let big_metrics_byte_len = BigGlyphMetrics::RAW_BYTE_LEN; cursor.advance_by(big_metrics_byte_len); cursor.finish(IndexSubtable2Marker { big_metrics_byte_len, @@ -1414,7 +1414,7 @@ impl<'a> FontRead<'a> for IndexSubtable5<'a> { cursor.advance::(); cursor.advance::(); cursor.advance::(); - let big_metrics_byte_len = 1_usize * BigGlyphMetrics::RAW_BYTE_LEN; + let big_metrics_byte_len = BigGlyphMetrics::RAW_BYTE_LEN; cursor.advance_by(big_metrics_byte_len); let num_glyphs: u32 = cursor.read()?; let glyph_array_byte_len = num_glyphs as usize * GlyphId::RAW_BYTE_LEN; diff --git a/read-fonts/src/tables/bitmap.rs b/read-fonts/src/tables/bitmap.rs index e5badc4f9..fd2643708 100644 --- a/read-fonts/src/tables/bitmap.rs +++ b/read-fonts/src/tables/bitmap.rs @@ -303,6 +303,6 @@ fn read_big_metrics(cursor: &mut Cursor) -> Result { #[cfg(feature = "traversal")] impl SbitLineMetrics { pub(crate) fn traversal_type<'a>(&self, data: FontData<'a>) -> FieldType<'a> { - FieldType::Record(self.clone().traverse(data)) + FieldType::Record(self.traverse(data)) } } From d4c450564b91ae6393e492bed84006c7f6c7b2d6 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Mon, 9 Oct 2023 19:54:07 -0400 Subject: [PATCH 6/7] add a test font and some bitmap loading tests this covers: * EBLC/CBLC formats 1, 2 and 3 * EBDT/CBDT formats 2, 5, and 17 --- font-test-data/src/lib.rs | 2 + .../test_data/ttf/embedded_bitmaps.ttf | Bin 0 -> 1352 bytes .../test_data/ttx/embedded_bitmaps.ttx | 313 ++++++++++++++++++ read-fonts/src/tables/bitmap.rs | 65 ++-- read-fonts/src/tables/cbdt.rs | 113 +++++++ read-fonts/src/tables/ebdt.rs | 136 ++++++++ 6 files changed, 603 insertions(+), 26 deletions(-) create mode 100644 font-test-data/test_data/ttf/embedded_bitmaps.ttf create mode 100644 font-test-data/test_data/ttx/embedded_bitmaps.ttx diff --git a/font-test-data/src/lib.rs b/font-test-data/src/lib.rs index cff1e07cf..dbc3ccc69 100644 --- a/font-test-data/src/lib.rs +++ b/font-test-data/src/lib.rs @@ -37,6 +37,8 @@ pub static CANTARELL_VF_TRIMMED_GLYPHS: &str = pub static CHARSTRING_PATH_OPS: &[u8] = include_bytes!("../test_data/ttf/charstring_path_ops.ttf"); +pub static EMBEDDED_BITMAPS: &[u8] = include_bytes!("../test_data/ttf/embedded_bitmaps.ttf"); + pub mod post { #[rustfmt::skip] diff --git a/font-test-data/test_data/ttf/embedded_bitmaps.ttf b/font-test-data/test_data/ttf/embedded_bitmaps.ttf new file mode 100644 index 0000000000000000000000000000000000000000..eeba70af3d68d4a91f2f7f5619b31faa9b604f97 GIT binary patch literal 1352 zcmYjR4NwzT5Pr#pga`yv#3EurWCTiEv4BcPfrJDD0tvrP3=n93On{h}9|c8>0urbr ztwU{9W&pJ`T1v+vCD<0M3dU;GR$Q-U_@BY>g_fNQ0aNK+!2MDk9zmb}#4)i`aer6v8M(4hQPx*mnqndNQFB3(jx?zz{N;kjw;3UnNN1=(fOn0myFgJWeVg=*Z{{Md+`!zqkQZ z3lh`08#RepSy={yL8Ve@wOYMipRZIF78WX%O0`;DP*9*$D0EtFW=4iaqk$Sw@^W(( z3WZ!Qmr5kC)6>#KLLrE$DJj5n#p0Ztob2pukw}yx5Nr_&!9!|uW+o5^hcbS0GHeJP z1bmxyWDIWgo<21>4fz>k?0CvT&B;qOcqoqJMjU^H=XT-W)I)%4@P#+=m?t&3u?rXA zL-K)UBSPeYcn&MQh&0|;dFWA$YsIj~#b0+Aki&@1WA^Hs0^(YtWw*j)Nh98a za72P!t6sOGu_+=@JHu!jBj=yj`R_FJtUhpTw9fkG3LQ1#-#aCS*0`3o4_#`_t2^yC zGNn;s?O?O^Z?`r}x$cJrYW1`|qx7fEljrwsT-xRN{@g`c@v2khWhILyE8Gv4ES{Vx zvy5&fosO+Kp}%&7A#J}Yx!)jdmoc@pg!Z*yUd-)~#pRu6jh4{G6iOydj?R{5s1P6*_xp z;H~^D#Z39Yl7!1okD4mq-kHJJdpFv2ZN0s0>c<;Nt?PzlQSH%Tdq3MQs?{*W))VDl z2aQN+XUojK0naJ|io)BUIn^yos4Y6e_1iYy-^1GEJ#%HcexjTAqIp%^<8G^G{+h<& z4IeeEbaNe=cHMIEk~pM4WAX3DvEFy)EML)keb|?_@+Y_5ExUY8+BVU1-()p^n>K?ecK@)v+%xvmVx$3NJW*7fIq& zC)OA2yZpwtW5h5(@!Vui3p;l|!HM>o?_#tL(CZaPvQeV@9P9raQ4mF?@*@!lS!d{f zCZMD75w?fuac7J~rA)Lua;6|Eyqt3yQc|9|rf`rI48i|-XMlhdw1)QrB9X#Ma1fQo4hat?}9qQpZ5S-y#33S|> XxHaVGb@Kc)@&vw!Nk^{q*=h72Q8w0h literal 0 HcmV?d00001 diff --git a/font-test-data/test_data/ttx/embedded_bitmaps.ttx b/font-test-data/test_data/ttx/embedded_bitmaps.ttx new file mode 100644 index 000000000..9cd11bea7 --- /dev/null +++ b/font-test-data/test_data/ttx/embedded_bitmaps.ttx @@ -0,0 +1,313 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + eeaeea + + + + + + + + + + + + f0f0f0f0 + + + + + + + + + + + + abababab + + + + + + + aabbccdd 00112233 ffee1234 424242aa + 88990011 + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + 89504e47 0d0a1a0a 0000000d 49484452 + 00000088 00000080 08030000 00e737d1 + 0d000000 8a504c54 4547704c 5c5c5c75 + 75756d6d 6d727272 7474746a 69696c6c + 6c696969 6f6f6f6b 6b6b6968 68737272 + 56555570 70706d6d 6d6b6b6b 61606068 + 68686666 66646363 64636354 53535b59 + 59616060 52515154 5353605f 5f5e5e5e + 5d5d5d5b 5b5b5150 505a5959 51505052 + 51515756 56515050 51505052 51515453 + 534f4e4e 4f4e4e51 50504f4e 4e4f4e4e + 4f4e4eb5 c8e4e900 00002e74 524e5300 + 208040eb ff511070 a3c38fff 30ffffff + 80ffffea ff60bfff af9fffff ffff70ff + 10cfff40 ef8fff80 bfff50ff df66dbac + 80000002 6b494441 547801ed d8878eea + 400c85e1 13c2d27b 872dd909 1d26efff + 78b76107 4b9adb22 e2a0953f b5e8df36 + 5e4c8579 06c61863 8c31c618 13d57e89 + 21c5b718 414f547f b96988d8 a0566f42 + 4deb85b4 45ec708c a1a6db23 3511eb1c + 63a8e9f4 4803b966 8f414f8f 45c8c5dc + 3a50d31f 30116b94 7a6da869 0c881cbe + cdb10635 b5e1cda0 2bf76648 34777548 + 1a220e07 149b5033 1cddc8e1 e311a943 + 4d73c444 6c71eb40 4d1cfa9b 5d8e63a8 + 194f485b ee0dc706 d4b42744 0e3f6111 + d474a624 46ae3f65 d033654d e41adc34 + 77754a66 726f3876 a1a63527 72f80eb5 + e9026aba 73227775 c631869a e59cc817 + 23730635 fdd57c75 b346aeb5 620b94a8 + 31beebce 56643e5f 72dcac72 f30db5b8 + 8473bc0a 6f6ff7eb 557ec157 f2ba8147 + 1bbf1532 fec20779 2fa48483 7c14f285 + 0f927c16 f2f883b8 e56701a9 4389a274 + cb960e64 b7cf63e2 a064b765 1172074a + 9f29a07e 903de441 c8016a92 23598ab8 + e4b8809a cd919c44 dc73dc41 cd39307c + 7464d073 617de476 dc52a8e9 5f988827 + 6e1ba8b9 7a22875f 733c41cd c913397c + ca517357 3d4944f4 cc41cd3e 30fc8edb + 1e6a5c68 f884db19 4ac4f099 88872a77 + 550e7fd6 3f88cb3c eb83f5bd ce4db338 + df653eb7 3f337f97 715b9470 0e5fc8a2 + 84ad28e4 6407b183 a81f24f1 85247834 + b7f1059c 1d4a9478 9645206e edd9c641 + 4b1af8c7 f79feec5 48f6ac2f 46ecc5c8 + 156a36a1 e13d8ba0 260d0cdf f70c5ac2 + c35f2bd9 5516da9b 03d42cfe b8ab09d4 + 1c42c367 4ff26ed3 79862779 804fa126 + fae3aeae 2bfe6464 53e503fc bae24f46 + d6cff100 1fbea3f6 b9ed2b7e 80df693e + c0b373e0 6f467b8a 0be871c9 e9a7c441 + e89f7e59 c018638c 31c61863 4cf5be03 + d8291f21 ceb2e953 00000000 49454e44 + ae426082 + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/read-fonts/src/tables/bitmap.rs b/read-fonts/src/tables/bitmap.rs index fd2643708..aea1d7c2b 100644 --- a/read-fonts/src/tables/bitmap.rs +++ b/read-fonts/src/tables/bitmap.rs @@ -3,30 +3,13 @@ include!("../../generated/generated_bitmap.rs"); impl BitmapSize { - pub fn subtable<'a>( - &self, - offset_data: FontData<'a>, - index: u32, - ) -> Result, ReadError> { - let base_offset = self.index_subtable_array_offset() as usize; - const SUBTABLE_HEADER_SIZE: usize = 8; - let header_offset = base_offset + index as usize * SUBTABLE_HEADER_SIZE; - let header_data = offset_data - .slice(header_offset..) - .ok_or(ReadError::OutOfBounds)?; - let header = IndexSubtableArray::read(header_data)?; - let subtable_offset = base_offset + header.additional_offset_to_index_subtable() as usize; - let subtable_data = offset_data - .slice(subtable_offset..) - .ok_or(ReadError::OutOfBounds)?; - let subtable = IndexSubtable::read(subtable_data)?; - Ok(BitmapSizeSubtable { - first_glyph_index: header.first_glyph_index(), - last_glyph_index: header.last_glyph_index(), - kind: subtable, - }) - } - + /// Returns the bitmap location information for the given glyph. + /// + /// The `offset_data` parameter is provided by the `offset_data()` method + /// of the parent `Eblc` or `Cblc` table. + /// + /// The resulting [`BitmapLocation`] value is used by the `data()` method + /// in the associated `Ebdt` or `Cbdt` table to extract the bitmap data. pub fn location( &self, offset_data: FontData, @@ -104,9 +87,33 @@ impl BitmapSize { } Err(ReadError::OutOfBounds) } + + fn subtable<'a>( + &self, + offset_data: FontData<'a>, + index: u32, + ) -> Result, ReadError> { + let base_offset = self.index_subtable_array_offset() as usize; + const SUBTABLE_HEADER_SIZE: usize = 8; + let header_offset = base_offset + index as usize * SUBTABLE_HEADER_SIZE; + let header_data = offset_data + .slice(header_offset..) + .ok_or(ReadError::OutOfBounds)?; + let header = IndexSubtableArray::read(header_data)?; + let subtable_offset = base_offset + header.additional_offset_to_index_subtable() as usize; + let subtable_data = offset_data + .slice(subtable_offset..) + .ok_or(ReadError::OutOfBounds)?; + let subtable = IndexSubtable::read(subtable_data)?; + Ok(BitmapSizeSubtable { + first_glyph_index: header.first_glyph_index(), + last_glyph_index: header.last_glyph_index(), + kind: subtable, + }) + } } -pub struct BitmapSizeSubtable<'a> { +struct BitmapSizeSubtable<'a> { pub first_glyph_index: GlyphId, pub last_glyph_index: GlyphId, pub kind: IndexSubtable<'a>, @@ -116,16 +123,22 @@ pub struct BitmapSizeSubtable<'a> { pub struct BitmapLocation { /// Format of EBDT/CBDT image data. pub format: u16, - /// Offset in EBDT/CBDT table. + /// Offset in bytes from the start of the EBDT/CBDT table. pub data_offset: usize, + /// Size of the image data in bytes, if present in the EBLC/CBLC table. pub data_size: Option, + /// Bit depth from the associated size. Required for computing image data + /// size when unspecified. pub bit_depth: u8, + /// Full metrics, if present in the EBLC/CBLC table. pub metrics: Option, } #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum BitmapDataFormat { + /// The full bitmap is tightly packed according to the bit depth. BitAligned, + /// Each row of the data is aligned to a byte boundary. ByteAligned, Png, } diff --git a/read-fonts/src/tables/cbdt.rs b/read-fonts/src/tables/cbdt.rs index c2c723414..37fb6b792 100644 --- a/read-fonts/src/tables/cbdt.rs +++ b/read-fonts/src/tables/cbdt.rs @@ -9,3 +9,116 @@ impl<'a> Cbdt<'a> { super::bitmap::bitmap_data(self.offset_data(), location, true) } } + +#[cfg(test)] +mod tests { + use super::super::bitmap::{BitmapDataFormat, SmallGlyphMetrics}; + use crate::{types::GlyphId, FontRef, TableProvider}; + + #[test] + fn read_cblc_1_cbdt_17() { + let font = FontRef::new(font_test_data::EMBEDDED_BITMAPS).unwrap(); + let cblc = font.cblc().unwrap(); + let cbdt = font.cbdt().unwrap(); + let size = &cblc.bitmap_sizes()[0]; + // Metrics for size at index 0 + assert_eq!(size.hori.ascender(), 101); + assert_eq!(size.hori.descender(), -27); + assert_eq!(size.hori.width_max(), 136); + assert_eq!(size.vert.ascender(), 101); + assert_eq!(size.vert.descender(), -27); + assert_eq!(size.vert.width_max(), 136); + assert_eq!(size.start_glyph_index(), GlyphId::new(4)); + assert_eq!(size.end_glyph_index(), GlyphId::new(4)); + assert_eq!(size.ppem_x(), 109); + assert_eq!(size.ppem_y(), 109); + assert_eq!(size.bit_depth(), 32); + let expected: &[(GlyphId, &[u8], SmallGlyphMetrics)] = &[( + GlyphId::new(4), + &raw_image_data( + r#"89504e47 0d0a1a0a 0000000d 49484452 + 00000088 00000080 08030000 00e737d1 + 0d000000 8a504c54 4547704c 5c5c5c75 + 75756d6d 6d727272 7474746a 69696c6c + 6c696969 6f6f6f6b 6b6b6968 68737272 + 56555570 70706d6d 6d6b6b6b 61606068 + 68686666 66646363 64636354 53535b59 + 59616060 52515154 5353605f 5f5e5e5e + 5d5d5d5b 5b5b5150 505a5959 51505052 + 51515756 56515050 51505052 51515453 + 534f4e4e 4f4e4e51 50504f4e 4e4f4e4e + 4f4e4eb5 c8e4e900 00002e74 524e5300 + 208040eb ff511070 a3c38fff 30ffffff + 80ffffea ff60bfff af9fffff ffff70ff + 10cfff40 ef8fff80 bfff50ff df66dbac + 80000002 6b494441 547801ed d8878eea + 400c85e1 13c2d27b 872dd909 1d26efff + 78b76107 4b9adb22 e2a0953f b5e8df36 + 5e4c8579 06c61863 8c31c618 13d57e89 + 21c5b718 414f547f b96988d8 a0566f42 + 4deb85b4 45ec708c a1a6db23 3511eb1c + 63a8e9f4 4803b966 8f414f8f 45c8c5dc + 3a50d31f 30116b94 7a6da869 0c881cbe + cdb10635 b5e1cda0 2bf76648 34777548 + 1a220e07 149b5033 1cddc8e1 e311a943 + 4d73c444 6c71eb40 4d1cfa9b 5d8e63a8 + 194f485b ee0dc706 d4b42744 0e3f6111 + d474a624 46ae3f65 d033654d e41adc34 + 77754a66 726f3876 a1a63527 72f80eb5 + e9026aba 73227775 c631869a e59cc817 + 23730635 fdd57c75 b346aeb5 620b94a8 + 31beebce 56643e5f 72dcac72 f30db5b8 + 8473bc0a 6f6ff7eb 557ec157 f2ba8147 + 1bbf1532 fec20779 2fa48483 7c14f285 + 0f927c16 f2f883b8 e56701a9 4389a274 + cb960e64 b7cf63e2 a064b765 1172074a + 9f29a07e 903de441 c8016a92 23598ab8 + e4b8809a cd919c44 dc73dc41 cd39307c + 7464d073 617de476 dc52a8e9 5f988827 + 6e1ba8b9 7a22875f 733c41cd c913397c + ca517357 3d4944f4 cc41cd3e 30fc8edb + 1e6a5c68 f884db19 4ac4f099 88872a77 + 550e7fd6 3f88cb3c eb83f5bd ce4db338 + df653eb7 3f337f97 715b9470 0e5fc8a2 + 84ad28e4 6407b183 a81f24f1 85247834 + b7f1059c 1d4a9478 9645206e edd9c641 + 4b1af8c7 f79feec5 48f6ac2f 46ecc5c8 + 156a36a1 e13d8ba0 260d0cdf f70c5ac2 + c35f2bd9 5516da9b 03d42cfe b8ab09d4 + 1c42c367 4ff26ed3 79862779 804fa126 + fae3aeae 2bfe6464 53e503fc bae24f46 + d6cff100 1fbea3f6 b9ed2b7e 80df693e + c0b373e0 6f467b8a 0be871c9 e9a7c441 + e89f7e59 c018638c 31c61863 4cf5be03 + d8291f21 ceb2e953 00000000 49454e44 + ae426082"#, + ), + SmallGlyphMetrics { + height: 128, + width: 136, + bearing_x: 0.into(), + bearing_y: 101.into(), + advance: 136, + }, + )]; + for (gid, data, metrics) in expected { + let location = size.location(cblc.offset_data(), *gid).unwrap(); + // all glyphs have data format == 17 + assert_eq!(location.format, 17); + let bitmap_data = cbdt.data(&location).unwrap(); + let (img_fmt, img_data) = bitmap_data.content.extract_data(); + // all glyphs are PNG + assert_eq!(img_fmt, BitmapDataFormat::Png); + assert_eq!(img_data, *data); + assert_eq!(bitmap_data.extract_small_metrics(), metrics); + } + } + + fn raw_image_data(raw: &str) -> Vec { + raw.replace([' ', '\n', '\r'], "") + .as_bytes() + .chunks(2) + .map(|str_hex| u8::from_str_radix(std::str::from_utf8(str_hex).unwrap(), 16).unwrap()) + .collect() + } +} diff --git a/read-fonts/src/tables/ebdt.rs b/read-fonts/src/tables/ebdt.rs index daa6755b4..a6b133167 100644 --- a/read-fonts/src/tables/ebdt.rs +++ b/read-fonts/src/tables/ebdt.rs @@ -9,3 +9,139 @@ impl<'a> Ebdt<'a> { super::bitmap::bitmap_data(self.offset_data(), location, false) } } + +#[cfg(test)] +mod tests { + use super::super::bitmap::{ + BigGlyphMetrics, BitmapContent, BitmapData, BitmapDataFormat, BitmapMetrics, + SmallGlyphMetrics, + }; + use crate::{types::GlyphId, FontRef, TableProvider}; + + impl<'a> BitmapContent<'a> { + pub(crate) fn extract_data(&self) -> (BitmapDataFormat, &'a [u8]) { + match self { + BitmapContent::Data(fmt, data) => (*fmt, *data), + _ => panic!("expected data content"), + } + } + } + + impl<'a> BitmapData<'a> { + pub(crate) fn extract_small_metrics(&self) -> &SmallGlyphMetrics { + match &self.metrics { + BitmapMetrics::Small(small) => small, + _ => panic!("expected small glyph metrics"), + } + } + } + + #[test] + fn read_eblc_3_ebdt_2() { + let font = FontRef::new(font_test_data::EMBEDDED_BITMAPS).unwrap(); + let eblc = font.eblc().unwrap(); + let ebdt = font.ebdt().unwrap(); + let size = &eblc.bitmap_sizes()[0]; + // Metrics for size at index 0 + assert_eq!(size.hori.ascender(), 6); + assert_eq!(size.hori.descender(), 2); + assert_eq!(size.hori.width_max(), 4); + assert_eq!(size.hori.max_before_bl(), 6); + assert_eq!(size.hori.min_after_bl(), -2); + assert_eq!(size.vert.ascender(), 6); + assert_eq!(size.vert.descender(), 2); + assert_eq!(size.start_glyph_index(), GlyphId::new(1)); + assert_eq!(size.end_glyph_index(), GlyphId::new(2)); + assert_eq!(size.ppem_x(), 7); + assert_eq!(size.ppem_y(), 7); + assert_eq!(size.bit_depth(), 1); + // Bit aligned formats in this strike: + let expected: &[(GlyphId, &[u8], SmallGlyphMetrics)] = &[ + ( + GlyphId::new(1), + &[0xee, 0xae, 0xea], + SmallGlyphMetrics { + height: 8, + width: 3, + bearing_x: 1.into(), + bearing_y: 6.into(), + advance: 4, + }, + ), + ( + GlyphId::new(2), + &[0xf0, 0xf0, 0xf0, 0xf0], + SmallGlyphMetrics { + height: 8, + width: 4, + bearing_x: 0.into(), + bearing_y: 6.into(), + advance: 4, + }, + ), + ]; + for (gid, data, metrics) in expected { + let location = size.location(eblc.offset_data(), *gid).unwrap(); + // all glyphs have data format == 2 + assert_eq!(location.format, 2); + let bitmap_data = ebdt.data(&location).unwrap(); + let (img_fmt, img_data) = bitmap_data.content.extract_data(); + // all glyphs are bit aligned + assert_eq!(img_fmt, BitmapDataFormat::BitAligned); + assert_eq!(img_data, *data); + assert_eq!(bitmap_data.extract_small_metrics(), metrics); + } + } + + #[test] + fn read_eblc_2_ebdt_5() { + let font = FontRef::new(font_test_data::EMBEDDED_BITMAPS).unwrap(); + let eblc = font.eblc().unwrap(); + let ebdt = font.ebdt().unwrap(); + let size = &eblc.bitmap_sizes()[1]; + // Metrics for size at index 1 + assert_eq!(size.hori.ascender(), 12); + assert_eq!(size.hori.descender(), 5); + assert_eq!(size.hori.width_max(), 9); + assert_eq!(size.hori.max_before_bl(), 12); + assert_eq!(size.hori.min_after_bl(), -5); + assert_eq!(size.vert.ascender(), 12); + assert_eq!(size.vert.descender(), 5); + assert_eq!(size.start_glyph_index(), GlyphId::new(3)); + assert_eq!(size.end_glyph_index(), GlyphId::new(3)); + assert_eq!(size.ppem_x(), 15); + assert_eq!(size.ppem_y(), 15); + assert_eq!(size.bit_depth(), 1); + let expected: &[(GlyphId, &[u8])] = &[( + GlyphId::new(3), + &[ + 0xaa, 0xbb, 0xcc, 0xdd, 0x00, 0x11, 0x22, 0x33, 0xff, 0xee, 0x12, 0x34, 0x42, 0x42, + 0x42, 0xaa, 0x88, 0x99, 0x00, 0x11, + ], + )]; + for (gid, data) in expected { + let location = size.location(eblc.offset_data(), *gid).unwrap(); + // Metrics are in EBLC, so the same for all glyphs + assert_eq!( + &location.metrics, + &Some(BigGlyphMetrics { + height: 17, + width: 9, + hori_bearing_x: 0.into(), + hori_bearing_y: 12.into(), + hori_advance: 9, + vert_bearing_x: (-4).into(), + vert_bearing_y: (-9).into(), + vert_advance: 0, + }) + ); + // all glyphs have data format == 5 + assert_eq!(location.format, 5); + let bitmap_data = ebdt.data(&location).unwrap(); + let (img_fmt, img_data) = bitmap_data.content.extract_data(); + // all glyphs are bit aligned + assert_eq!(img_fmt, BitmapDataFormat::BitAligned); + assert_eq!(img_data, *data); + } + } +} From 05d977f60b2d4bb59e9b1f2bce3b50b6787fdeec Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Mon, 9 Oct 2023 23:42:01 -0400 Subject: [PATCH 7/7] review feedback * collapse test metrics asserts to single expression * reduce CBDT test case size * remove useless comments * add links to issue for improving codegen to relevant places --- font-codegen/src/fields.rs | 4 +- font-codegen/src/record.rs | 2 + .../test_data/ttf/embedded_bitmaps.ttf | Bin 1352 -> 476 bytes .../test_data/ttx/embedded_bitmaps.ttx | 93 +++--------------- read-fonts/src/tables/cbdt.rs | 93 +++--------------- read-fonts/src/tables/ebdt.rs | 56 +++++------ 6 files changed, 60 insertions(+), 188 deletions(-) diff --git a/font-codegen/src/fields.rs b/font-codegen/src/fields.rs index 5bda0d3f0..ccce0039d 100644 --- a/font-codegen/src/fields.rs +++ b/font-codegen/src/fields.rs @@ -401,7 +401,8 @@ fn traversal_arm_for_field( let data = fld.offset_getter_data_src(); quote!(Field::new(#name_str, traversal::FieldType::var_array(#typ_str, self.#name()#maybe_unwrap, #data))) } - // HACK: who wouldn't want to hard-code ValueRecord handling + // See if there are better ways to handle these hardcoded types + // FieldType::Struct { typ } if typ == "ValueRecord" || typ == "SbitLineMetrics" => { let offset_data = pass_data .cloned() @@ -479,6 +480,7 @@ impl Field { // catch `ValueRecord` so use this attribute to ignore it. // Fields that require args for reading can't be read "zerocopy" // anyway. + // self.attrs.read_with_args.is_none() && matches!( self.typ, diff --git a/font-codegen/src/record.rs b/font-codegen/src/record.rs index 2c4a8e1d0..ddf5763ea 100644 --- a/font-codegen/src/record.rs +++ b/font-codegen/src/record.rs @@ -55,6 +55,8 @@ pub(crate) fn generate(item: &Record, all_items: &Items) -> syn::Result if item.name == "SbitLineMetrics" { use quote::TokenStreamExt; let mut copy_trait = quote!(Copy,); diff --git a/font-test-data/test_data/ttf/embedded_bitmaps.ttf b/font-test-data/test_data/ttf/embedded_bitmaps.ttf index eeba70af3d68d4a91f2f7f5619b31faa9b604f97..9a06b939f871462e85b134ae270392a54d2a4398 100644 GIT binary patch delta 146 zcmX@Xb%!~GfsuiMfsMg|fr&xE*~uj&NipgyP-G1di#j{`I9s|HbOPB&fH=SvD9;^d zuED^-C<5ebx&q}ho!MJ}d(HY?9&dVjmHCdM>kcmNXas$f=0G7QVZvX%Q delta 1030 zcmcb^e1a>4fsuiMfsMg|fr&xE*~ukD<*|J*P-G1Q19PLZlaI5di}6I(1)adj{7 ziHV6REiKK>%`GY_Dk&++%FN8k$;r&j%+Jry&d$!v$S5u<3JVP_C@27`0g5ChBxGb{ zq@|^$Bqsw=NN{j;WF$~9C@>JnPKb}MkBf_ojg5_tjt&e6h>DB^l0doeurMG4!Un4I z_wxfHAQ!>_l3Pzac?oouUP+K&FoQya!|VTn0tJf?_y0He|Nno(|NpQ4C+z>fem;=% zzu>>X`Tq{@`~NrW{~z%Ge%kFd4GauS*`6+rAr*{o>uj4beJFhBQgyrTO-@<0 z_x~%lC$f9bx~=qR!BqRLFYcSg`LtHD9g|4zF+3(Ae6_Aq@#uC5NB@xeotYgs7KG(H z`Mz%5;`*kbXW_Ej%BF&^Ws+CC{Nlm9Gp*mzzu)!5(K}WFm*ouvv!_($uE^x+klA;3 zBb({ghi4aPe^0CTFexwfkW%7f7nvPkEOYn7!^eUvoqdasxa1VRcJP(?H9NL1d4;6E zNAx@1<7`*9sJrmlCkkFES*GH)&OY^majNeVsXIU;ywZyDEy@-yGgUA8!MF7#Q`W9x zrSj5ahHbN+&N(5jT+C+r_i9b)X18@)lenj>Fx>b0Tv&=-e9@iyHASC!x9(^u-ourj z|NV7n-NEoryBgi4_lp|+JH%e8zoezPM&wf~|D+nRPd}P>JWXd@>D;-fFS*6q-UtDkFc=n{QWzDI;;&59}moH}=&)OOM>ANkL6_fF!<+h~P zkqdG{C7t^zOB)Bdvo-JXqMT+hqm1d>eYC8 z?tkZrI&?T*`(~)vt=Y_1bpGvF&3Q$}>2SLLr@YIRZR(W`{tMN9Jzlp?`(H{*@KfeL zyB_(wT|57gL4M!jZ#&;=*EQVFv^%i5_(8r~br<)G!jmtTA8~v!zb^6sB*`TEeBH-< jLsMSy+@_bHJk8+g>gTe~DWM6-oOr~KiMe*N2;&I=ITOCu diff --git a/font-test-data/test_data/ttx/embedded_bitmaps.ttx b/font-test-data/test_data/ttx/embedded_bitmaps.ttx index 9cd11bea7..06a4b2696 100644 --- a/font-test-data/test_data/ttx/embedded_bitmaps.ttx +++ b/font-test-data/test_data/ttx/embedded_bitmaps.ttx @@ -24,10 +24,10 @@ - - - - + + + + @@ -38,7 +38,7 @@
- + @@ -50,7 +50,7 @@ eeaeea - + @@ -61,22 +61,10 @@ f0f0f0f0 - - - - - - - - - - - abababab - - + aabbccdd 00112233 ffee1234 424242aa 88990011 @@ -128,8 +116,8 @@ - - + + @@ -184,7 +172,7 @@ - + @@ -192,7 +180,7 @@
- + @@ -201,62 +189,7 @@ - 89504e47 0d0a1a0a 0000000d 49484452 - 00000088 00000080 08030000 00e737d1 - 0d000000 8a504c54 4547704c 5c5c5c75 - 75756d6d 6d727272 7474746a 69696c6c - 6c696969 6f6f6f6b 6b6b6968 68737272 - 56555570 70706d6d 6d6b6b6b 61606068 - 68686666 66646363 64636354 53535b59 - 59616060 52515154 5353605f 5f5e5e5e - 5d5d5d5b 5b5b5150 505a5959 51505052 - 51515756 56515050 51505052 51515453 - 534f4e4e 4f4e4e51 50504f4e 4e4f4e4e - 4f4e4eb5 c8e4e900 00002e74 524e5300 - 208040eb ff511070 a3c38fff 30ffffff - 80ffffea ff60bfff af9fffff ffff70ff - 10cfff40 ef8fff80 bfff50ff df66dbac - 80000002 6b494441 547801ed d8878eea - 400c85e1 13c2d27b 872dd909 1d26efff - 78b76107 4b9adb22 e2a0953f b5e8df36 - 5e4c8579 06c61863 8c31c618 13d57e89 - 21c5b718 414f547f b96988d8 a0566f42 - 4deb85b4 45ec708c a1a6db23 3511eb1c - 63a8e9f4 4803b966 8f414f8f 45c8c5dc - 3a50d31f 30116b94 7a6da869 0c881cbe - cdb10635 b5e1cda0 2bf76648 34777548 - 1a220e07 149b5033 1cddc8e1 e311a943 - 4d73c444 6c71eb40 4d1cfa9b 5d8e63a8 - 194f485b ee0dc706 d4b42744 0e3f6111 - d474a624 46ae3f65 d033654d e41adc34 - 77754a66 726f3876 a1a63527 72f80eb5 - e9026aba 73227775 c631869a e59cc817 - 23730635 fdd57c75 b346aeb5 620b94a8 - 31beebce 56643e5f 72dcac72 f30db5b8 - 8473bc0a 6f6ff7eb 557ec157 f2ba8147 - 1bbf1532 fec20779 2fa48483 7c14f285 - 0f927c16 f2f883b8 e56701a9 4389a274 - cb960e64 b7cf63e2 a064b765 1172074a - 9f29a07e 903de441 c8016a92 23598ab8 - e4b8809a cd919c44 dc73dc41 cd39307c - 7464d073 617de476 dc52a8e9 5f988827 - 6e1ba8b9 7a22875f 733c41cd c913397c - ca517357 3d4944f4 cc41cd3e 30fc8edb - 1e6a5c68 f884db19 4ac4f099 88872a77 - 550e7fd6 3f88cb3c eb83f5bd ce4db338 - df653eb7 3f337f97 715b9470 0e5fc8a2 - 84ad28e4 6407b183 a81f24f1 85247834 - b7f1059c 1d4a9478 9645206e edd9c641 - 4b1af8c7 f79feec5 48f6ac2f 46ecc5c8 - 156a36a1 e13d8ba0 260d0cdf f70c5ac2 - c35f2bd9 5516da9b 03d42cfe b8ab09d4 - 1c42c367 4ff26ed3 79862779 804fa126 - fae3aeae 2bfe6464 53e503fc bae24f46 - d6cff100 1fbea3f6 b9ed2b7e 80df693e - c0b373e0 6f467b8a 0be871c9 e9a7c441 - e89f7e59 c018638c 31c61863 4cf5be03 - d8291f21 ceb2e953 00000000 49454e44 - ae426082 + 89504e47 0d0a1a0a @@ -305,7 +238,7 @@ - + diff --git a/read-fonts/src/tables/cbdt.rs b/read-fonts/src/tables/cbdt.rs index 37fb6b792..81b7d13e9 100644 --- a/read-fonts/src/tables/cbdt.rs +++ b/read-fonts/src/tables/cbdt.rs @@ -22,77 +22,22 @@ mod tests { let cbdt = font.cbdt().unwrap(); let size = &cblc.bitmap_sizes()[0]; // Metrics for size at index 0 - assert_eq!(size.hori.ascender(), 101); - assert_eq!(size.hori.descender(), -27); - assert_eq!(size.hori.width_max(), 136); - assert_eq!(size.vert.ascender(), 101); - assert_eq!(size.vert.descender(), -27); - assert_eq!(size.vert.width_max(), 136); - assert_eq!(size.start_glyph_index(), GlyphId::new(4)); - assert_eq!(size.end_glyph_index(), GlyphId::new(4)); - assert_eq!(size.ppem_x(), 109); - assert_eq!(size.ppem_y(), 109); - assert_eq!(size.bit_depth(), 32); + assert!( + size.hori.ascender() == 101 + && size.hori.descender() == -27 + && size.hori.width_max() == 136 + && size.vert.ascender() == 101 + && size.vert.descender() == -27 + && size.vert.width_max() == 136 + && size.start_glyph_index() == GlyphId::new(4) + && size.end_glyph_index() == GlyphId::new(4) + && size.ppem_x() == 109 + && size.ppem_y() == 109 + && size.bit_depth() == 32 + ); let expected: &[(GlyphId, &[u8], SmallGlyphMetrics)] = &[( GlyphId::new(4), - &raw_image_data( - r#"89504e47 0d0a1a0a 0000000d 49484452 - 00000088 00000080 08030000 00e737d1 - 0d000000 8a504c54 4547704c 5c5c5c75 - 75756d6d 6d727272 7474746a 69696c6c - 6c696969 6f6f6f6b 6b6b6968 68737272 - 56555570 70706d6d 6d6b6b6b 61606068 - 68686666 66646363 64636354 53535b59 - 59616060 52515154 5353605f 5f5e5e5e - 5d5d5d5b 5b5b5150 505a5959 51505052 - 51515756 56515050 51505052 51515453 - 534f4e4e 4f4e4e51 50504f4e 4e4f4e4e - 4f4e4eb5 c8e4e900 00002e74 524e5300 - 208040eb ff511070 a3c38fff 30ffffff - 80ffffea ff60bfff af9fffff ffff70ff - 10cfff40 ef8fff80 bfff50ff df66dbac - 80000002 6b494441 547801ed d8878eea - 400c85e1 13c2d27b 872dd909 1d26efff - 78b76107 4b9adb22 e2a0953f b5e8df36 - 5e4c8579 06c61863 8c31c618 13d57e89 - 21c5b718 414f547f b96988d8 a0566f42 - 4deb85b4 45ec708c a1a6db23 3511eb1c - 63a8e9f4 4803b966 8f414f8f 45c8c5dc - 3a50d31f 30116b94 7a6da869 0c881cbe - cdb10635 b5e1cda0 2bf76648 34777548 - 1a220e07 149b5033 1cddc8e1 e311a943 - 4d73c444 6c71eb40 4d1cfa9b 5d8e63a8 - 194f485b ee0dc706 d4b42744 0e3f6111 - d474a624 46ae3f65 d033654d e41adc34 - 77754a66 726f3876 a1a63527 72f80eb5 - e9026aba 73227775 c631869a e59cc817 - 23730635 fdd57c75 b346aeb5 620b94a8 - 31beebce 56643e5f 72dcac72 f30db5b8 - 8473bc0a 6f6ff7eb 557ec157 f2ba8147 - 1bbf1532 fec20779 2fa48483 7c14f285 - 0f927c16 f2f883b8 e56701a9 4389a274 - cb960e64 b7cf63e2 a064b765 1172074a - 9f29a07e 903de441 c8016a92 23598ab8 - e4b8809a cd919c44 dc73dc41 cd39307c - 7464d073 617de476 dc52a8e9 5f988827 - 6e1ba8b9 7a22875f 733c41cd c913397c - ca517357 3d4944f4 cc41cd3e 30fc8edb - 1e6a5c68 f884db19 4ac4f099 88872a77 - 550e7fd6 3f88cb3c eb83f5bd ce4db338 - df653eb7 3f337f97 715b9470 0e5fc8a2 - 84ad28e4 6407b183 a81f24f1 85247834 - b7f1059c 1d4a9478 9645206e edd9c641 - 4b1af8c7 f79feec5 48f6ac2f 46ecc5c8 - 156a36a1 e13d8ba0 260d0cdf f70c5ac2 - c35f2bd9 5516da9b 03d42cfe b8ab09d4 - 1c42c367 4ff26ed3 79862779 804fa126 - fae3aeae 2bfe6464 53e503fc bae24f46 - d6cff100 1fbea3f6 b9ed2b7e 80df693e - c0b373e0 6f467b8a 0be871c9 e9a7c441 - e89f7e59 c018638c 31c61863 4cf5be03 - d8291f21 ceb2e953 00000000 49454e44 - ae426082"#, - ), + &[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], SmallGlyphMetrics { height: 128, width: 136, @@ -103,22 +48,12 @@ mod tests { )]; for (gid, data, metrics) in expected { let location = size.location(cblc.offset_data(), *gid).unwrap(); - // all glyphs have data format == 17 assert_eq!(location.format, 17); let bitmap_data = cbdt.data(&location).unwrap(); let (img_fmt, img_data) = bitmap_data.content.extract_data(); - // all glyphs are PNG assert_eq!(img_fmt, BitmapDataFormat::Png); assert_eq!(img_data, *data); assert_eq!(bitmap_data.extract_small_metrics(), metrics); } } - - fn raw_image_data(raw: &str) -> Vec { - raw.replace([' ', '\n', '\r'], "") - .as_bytes() - .chunks(2) - .map(|str_hex| u8::from_str_radix(std::str::from_utf8(str_hex).unwrap(), 16).unwrap()) - .collect() - } } diff --git a/read-fonts/src/tables/ebdt.rs b/read-fonts/src/tables/ebdt.rs index a6b133167..b0f643b9c 100644 --- a/read-fonts/src/tables/ebdt.rs +++ b/read-fonts/src/tables/ebdt.rs @@ -43,18 +43,20 @@ mod tests { let ebdt = font.ebdt().unwrap(); let size = &eblc.bitmap_sizes()[0]; // Metrics for size at index 0 - assert_eq!(size.hori.ascender(), 6); - assert_eq!(size.hori.descender(), 2); - assert_eq!(size.hori.width_max(), 4); - assert_eq!(size.hori.max_before_bl(), 6); - assert_eq!(size.hori.min_after_bl(), -2); - assert_eq!(size.vert.ascender(), 6); - assert_eq!(size.vert.descender(), 2); - assert_eq!(size.start_glyph_index(), GlyphId::new(1)); - assert_eq!(size.end_glyph_index(), GlyphId::new(2)); - assert_eq!(size.ppem_x(), 7); - assert_eq!(size.ppem_y(), 7); - assert_eq!(size.bit_depth(), 1); + assert!( + size.hori.ascender() == 6 + && size.hori.descender() == 2 + && size.hori.width_max() == 4 + && size.hori.max_before_bl() == 6 + && size.hori.min_after_bl() == -2 + && size.vert.ascender() == 6 + && size.vert.descender() == 2 + && size.start_glyph_index() == GlyphId::new(1) + && size.end_glyph_index() == GlyphId::new(2) + && size.ppem_x() == 7 + && size.ppem_y() == 7 + && size.bit_depth() == 1 + ); // Bit aligned formats in this strike: let expected: &[(GlyphId, &[u8], SmallGlyphMetrics)] = &[ ( @@ -82,11 +84,9 @@ mod tests { ]; for (gid, data, metrics) in expected { let location = size.location(eblc.offset_data(), *gid).unwrap(); - // all glyphs have data format == 2 assert_eq!(location.format, 2); let bitmap_data = ebdt.data(&location).unwrap(); let (img_fmt, img_data) = bitmap_data.content.extract_data(); - // all glyphs are bit aligned assert_eq!(img_fmt, BitmapDataFormat::BitAligned); assert_eq!(img_data, *data); assert_eq!(bitmap_data.extract_small_metrics(), metrics); @@ -100,18 +100,20 @@ mod tests { let ebdt = font.ebdt().unwrap(); let size = &eblc.bitmap_sizes()[1]; // Metrics for size at index 1 - assert_eq!(size.hori.ascender(), 12); - assert_eq!(size.hori.descender(), 5); - assert_eq!(size.hori.width_max(), 9); - assert_eq!(size.hori.max_before_bl(), 12); - assert_eq!(size.hori.min_after_bl(), -5); - assert_eq!(size.vert.ascender(), 12); - assert_eq!(size.vert.descender(), 5); - assert_eq!(size.start_glyph_index(), GlyphId::new(3)); - assert_eq!(size.end_glyph_index(), GlyphId::new(3)); - assert_eq!(size.ppem_x(), 15); - assert_eq!(size.ppem_y(), 15); - assert_eq!(size.bit_depth(), 1); + assert!( + size.hori.ascender() == 12 + && size.hori.descender() == 5 + && size.hori.width_max() == 9 + && size.hori.max_before_bl() == 12 + && size.hori.min_after_bl() == -5 + && size.vert.ascender() == 12 + && size.vert.descender() == 5 + && size.start_glyph_index() == GlyphId::new(3) + && size.end_glyph_index() == GlyphId::new(3) + && size.ppem_x() == 15 + && size.ppem_y() == 15 + && size.bit_depth() == 1 + ); let expected: &[(GlyphId, &[u8])] = &[( GlyphId::new(3), &[ @@ -135,11 +137,9 @@ mod tests { vert_advance: 0, }) ); - // all glyphs have data format == 5 assert_eq!(location.format, 5); let bitmap_data = ebdt.data(&location).unwrap(); let (img_fmt, img_data) = bitmap_data.content.extract_data(); - // all glyphs are bit aligned assert_eq!(img_fmt, BitmapDataFormat::BitAligned); assert_eq!(img_data, *data); }