chromium/third_party/rust/chromium_crates_io/vendor/read-fonts-0.20.0/src/tables/bitmap.rs

//! Common bitmap (EBLC/EBDT/CBLC/CBDT) types.

include!("../../generated/generated_bitmap.rs");

impl BitmapSize {
    /// 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,
        glyph_id: GlyphId,
    ) -> Result<BitmapLocation, ReadError> {
        if !(self.start_glyph_index()..=self.end_glyph_index()).contains(&glyph_id) {
            return Err(ReadError::OutOfBounds);
        }
        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) {
                continue;
            }
            // glyph index relative to the first glyph in the subtable
            let glyph_ix =
                glyph_id.to_u32() as usize - subtable.first_glyph_index.to_u32() as usize;
            match &subtable.kind {
                IndexSubtable::Format1(st) => {
                    location.format = st.image_format();
                    let start = st.image_data_offset() as usize
                        + st.sbit_offsets()
                            .get(glyph_ix)
                            .ok_or(ReadError::OutOfBounds)?
                            .get() as usize;
                    let end = st.image_data_offset() as usize
                        + st.sbit_offsets()
                            .get(glyph_ix + 1)
                            .ok_or(ReadError::OutOfBounds)?
                            .get() as usize;
                    location.data_offset = start;
                    if end < start {
                        return Err(ReadError::OutOfBounds);
                    }
                    location.data_size = end - start;
                }
                IndexSubtable::Format2(st) => {
                    location.format = st.image_format();
                    let data_size = st.image_size() as usize;
                    location.data_size = data_size;
                    location.data_offset = st.image_data_offset() as usize + glyph_ix * data_size;
                    location.metrics = Some(st.big_metrics()[0]);
                }
                IndexSubtable::Format3(st) => {
                    location.format = st.image_format();
                    let start = st.image_data_offset() as usize
                        + st.sbit_offsets()
                            .get(glyph_ix)
                            .ok_or(ReadError::OutOfBounds)?
                            .get() as usize;
                    let end = st.image_data_offset() as usize
                        + st.sbit_offsets()
                            .get(glyph_ix + 1)
                            .ok_or(ReadError::OutOfBounds)?
                            .get() as usize;
                    location.data_offset = start;
                    if end < start {
                        return Err(ReadError::OutOfBounds);
                    }
                    location.data_size = end - start;
                }
                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().to_u32().cmp(&glyph_id.to_u32()))
                    {
                        Ok(ix) => ix,
                        _ => return Err(ReadError::InvalidCollectionIndex(glyph_id.to_u32())),
                    };
                    let start = array[array_ix].sbit_offset() as usize;
                    let end = array
                        .get(array_ix + 1)
                        .ok_or(ReadError::OutOfBounds)?
                        .sbit_offset() as usize;
                    location.data_offset = start;
                    if end < start {
                        return Err(ReadError::OutOfBounds);
                    }
                    location.data_size = end - start;
                }
                IndexSubtable::Format5(st) => {
                    location.format = st.image_format();
                    let array = st.glyph_array();
                    let array_ix = match array
                        .binary_search_by(|gid| gid.get().to_u32().cmp(&glyph_id.to_u32()))
                    {
                        Ok(ix) => ix,
                        _ => return Err(ReadError::InvalidCollectionIndex(glyph_id.to_u32())),
                    };
                    let data_size = st.image_size() as usize;
                    location.data_size = data_size;
                    location.data_offset = st.image_data_offset() as usize + array_ix * data_size;
                    location.metrics = Some(st.big_metrics()[0]);
                }
            }
            return Ok(location);
        }
        Err(ReadError::OutOfBounds)
    }

    fn subtable<'a>(
        &self,
        offset_data: FontData<'a>,
        index: u32,
    ) -> Result<BitmapSizeSubtable<'a>, 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,
        })
    }
}

struct BitmapSizeSubtable<'a> {
    pub first_glyph_index: GlyphId16,
    pub last_glyph_index: GlyphId16,
    pub kind: IndexSubtable<'a>,
}

#[derive(Clone, Default)]
pub struct BitmapLocation {
    /// Format of EBDT/CBDT image data.
    pub format: u16,
    /// Offset in bytes from the start of the EBDT/CBDT table.
    pub data_offset: usize,
    /// Size of the image data in bytes.
    pub data_size: usize,
    /// 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<BigGlyphMetrics>,
}

impl BitmapLocation {
    /// Returns true if the location references an empty bitmap glyph such as
    /// a space.
    pub fn is_empty(&self) -> bool {
        self.data_size == 0
    }
}

#[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,
}

#[derive(Clone)]
pub enum BitmapMetrics {
    Small(SmallGlyphMetrics),
    Big(BigGlyphMetrics),
}

#[derive(Clone)]
pub struct BitmapData<'a> {
    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>(
    offset_data: FontData<'a>,
    location: &BitmapLocation,
    is_color: bool,
) -> Result<BitmapData<'a>, ReadError> {
    let mut image_data = offset_data
        .slice(location.data_offset..location.data_offset + location.data_size)
        .ok_or(ReadError::OutOfBounds)?
        .cursor();
    match location.format {
        // Small metrics, byte-aligned data
        // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-1-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::<u8>(pitch * height)?;
            Ok(BitmapData {
                metrics: BitmapMetrics::Small(metrics),
                content: BitmapContent::Data(BitmapDataFormat::ByteAligned, data),
            })
        }
        // Small metrics, bit-aligned data
        // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-2-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::<u8>((width * height + 7) / 8)?;
            Ok(BitmapData {
                metrics: BitmapMetrics::Small(metrics),
                content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
            })
        }
        // Format 3 is obsolete
        // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-3-obsolete>
        // Format 4 is not supported
        // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-4-not-supported-metrics-in-eblc-compressed-data>
        // ---
        // Metrics in EBLC/CBLC, bit-aligned image data only
        // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-5-metrics-in-eblc-bit-aligned-image-data-only>
        5 => {
            let metrics = location.metrics.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::<u8>((width * height + 7) / 8)?;
            Ok(BitmapData {
                metrics: BitmapMetrics::Big(metrics),
                content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
            })
        }
        // Big metrics, byte-aligned data
        // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-6-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::<u8>(pitch * height)?;
            Ok(BitmapData {
                metrics: BitmapMetrics::Big(metrics),
                content: BitmapContent::Data(BitmapDataFormat::ByteAligned, data),
            })
        }
        // Big metrics, bit-aligned data
        // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format7-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::<u8>((width * height + 7) / 8)?;
            Ok(BitmapData {
                metrics: BitmapMetrics::Big(metrics),
                content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
            })
        }
        // Small metrics, component data
        // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-8-small-metrics-component-data>
        8 => {
            let metrics = read_small_metrics(&mut image_data)?;
            let _pad = image_data.read::<u8>()?;
            let count = image_data.read::<u16>()? as usize;
            let components = image_data.read_array::<BdtComponent>(count)?;
            Ok(BitmapData {
                metrics: BitmapMetrics::Small(metrics),
                content: BitmapContent::Composite(components),
            })
        }
        // Big metrics, component data
        // <https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#format-9-big-metrics-component-data>
        9 => {
            let metrics = read_big_metrics(&mut image_data)?;
            let count = image_data.read::<u16>()? as usize;
            let components = image_data.read_array::<BdtComponent>(count)?;
            Ok(BitmapData {
                metrics: BitmapMetrics::Big(metrics),
                content: BitmapContent::Composite(components),
            })
        }
        // Small metrics, PNG image data
        // <https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt#format-17-small-metrics-png-image-data>
        17 if is_color => {
            let metrics = read_small_metrics(&mut image_data)?;
            let data_len = image_data.read::<u32>()? as usize;
            let data = image_data.read_array::<u8>(data_len)?;
            Ok(BitmapData {
                metrics: BitmapMetrics::Small(metrics),
                content: BitmapContent::Data(BitmapDataFormat::Png, data),
            })
        }
        // Big metrics, PNG image data
        // <https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt#format-18-big-metrics-png-image-data>
        18 if is_color => {
            let metrics = read_big_metrics(&mut image_data)?;
            let data_len = image_data.read::<u32>()? as usize;
            let data = image_data.read_array::<u8>(data_len)?;
            Ok(BitmapData {
                metrics: BitmapMetrics::Big(metrics),
                content: BitmapContent::Data(BitmapDataFormat::Png, data),
            })
        }
        // Metrics in CBLC table, PNG image data
        // <https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt#format-19-metrics-in-cblc-table-png-image-data>
        19 if is_color => {
            let metrics = location.metrics.ok_or(ReadError::MalformedData(
                "expected metrics from location table",
            ))?;
            let data_len = image_data.read::<u32>()? as usize;
            let data = image_data.read_array::<u8>(data_len)?;
            Ok(BitmapData {
                metrics: BitmapMetrics::Big(metrics),
                content: BitmapContent::Data(BitmapDataFormat::Png, data),
            })
        }
        _ => Err(ReadError::MalformedData("unexpected bitmap data format")),
    }
}

fn read_small_metrics(cursor: &mut Cursor) -> Result<SmallGlyphMetrics, ReadError> {
    Ok(cursor.read_array::<SmallGlyphMetrics>(1)?[0])
}

fn read_big_metrics(cursor: &mut Cursor) -> Result<BigGlyphMetrics, ReadError> {
    Ok(cursor.read_array::<BigGlyphMetrics>(1)?[0])
}

#[cfg(feature = "traversal")]
impl SbitLineMetrics {
    pub(crate) fn traversal_type<'a>(&self, data: FontData<'a>) -> FieldType<'a> {
        FieldType::Record(self.traverse(data))
    }
}