read-fonts 0.44.0

Reading OpenType font files.
Documentation
//! The [SVG](https://learn.microsoft.com/en-us/typography/opentype/spec/svg) table

use core::cmp::Ordering;

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

impl<'a> Svg<'a> {
    /// Get the raw data of the SVG document. Is not guaranteed to be valid and might be compressed.
    pub fn glyph_data(&self, glyph_id: GlyphId) -> Option<&'a [u8]> {
        let document_list = self.svg_document_list().ok()?;
        let svg_document = document_list
            .document_records()
            .binary_search_by(|r| {
                if r.start_glyph_id.get() > glyph_id {
                    Ordering::Greater
                } else if r.end_glyph_id.get() < glyph_id {
                    Ordering::Less
                } else {
                    Ordering::Equal
                }
            })
            .ok()
            .and_then(|index| document_list.document_records().get(index))
            .and_then(|r| {
                let all_data = document_list.data.as_bytes();
                let start = r.svg_doc_offset();
                let end = start.checked_add(r.svg_doc_length())?;
                all_data.get(start as usize..end as usize)
            });
        svg_document
    }
}

#[cfg(test)]
mod tests {
    use font_test_data::bebuffer::BeBuffer;

    use super::*;

    #[test]
    fn read_dummy_svg_file() {
        let data: [u16; 32] = [
            0, // Version
            0, 10, // SVGDocumentListOffset
            0, 0, // Reserved
            // SVGDocumentList
            3, // numEntries
            // documentRecords
            // Record 1
            1, // startGlyphID
            3, // endGlyphID
            0, 38, // svgDocOffset
            0, 10, // svgDocLength
            // Record 2
            6, // startGlyphID
            7, // endGlyphID
            0, 48, // svgDocOffset
            0, 6, // svgDocLength
            // Record 3
            9, // startGlyphID
            9, // endGlyphID
            0, 38, // svgDocOffset
            0, 10,
            // svgDocLength
            // SVG Documents. Not actual valid SVGs, but just dummy data.
            1, 0, 0, 0, 1, // Document 1
            2, 0, 0, // Document 2
        ];

        let mut buf = BeBuffer::new();
        buf = buf.extend(data);

        let table = Svg::read(buf.data().into()).unwrap();

        let first_document = &[0, 1, 0, 0, 0, 0, 0, 0, 0, 1][..];
        let second_document = &[0, 2, 0, 0, 0, 0][..];

        assert_eq!(table.glyph_data(GlyphId::new(0)), None);
        assert_eq!(table.glyph_data(GlyphId::new(1)), Some(first_document));
        assert_eq!(table.glyph_data(GlyphId::new(2)), Some(first_document));
        assert_eq!(table.glyph_data(GlyphId::new(3)), Some(first_document));
        assert_eq!(table.glyph_data(GlyphId::new(4)), None);
        assert_eq!(table.glyph_data(GlyphId::new(5)), None);
        assert_eq!(table.glyph_data(GlyphId::new(6)), Some(second_document));
        assert_eq!(table.glyph_data(GlyphId::new(7)), Some(second_document));
        assert_eq!(table.glyph_data(GlyphId::new(8)), None);
        assert_eq!(table.glyph_data(GlyphId::new(9)), Some(first_document));
        assert_eq!(table.glyph_data(GlyphId::new(10)), None);
    }

    #[test]
    fn test_svg_glyph_data_overflow_guard() {
        let data: [u16; 12] = [
            0, // Version
            0, 10, // SVGDocumentListOffset
            0, 0, // Reserved
            // SVGDocumentList
            1, // numEntries
            // documentRecords
            // Record 1
            1, // startGlyphID
            3, // endGlyphID
            0xFFFF, 0xFFFF, // svgDocOffset
            0, 10, // svgDocLength
        ];
        let mut buf = BeBuffer::new();
        buf = buf.extend(data);
        let table = Svg::read(buf.data().into()).unwrap();
        // Just don't panic with overflow
        let _ = table.glyph_data(GlyphId::new(1));
    }
}