Skip to main content

read_fonts/tables/
svg.rs

1//! The [SVG](https://learn.microsoft.com/en-us/typography/opentype/spec/svg) table
2
3use core::cmp::Ordering;
4
5include!("../../generated/generated_svg.rs");
6
7impl<'a> Svg<'a> {
8    /// Get the raw data of the SVG document. Is not guaranteed to be valid and might be compressed.
9    pub fn glyph_data(&self, glyph_id: GlyphId) -> Result<Option<&'a [u8]>, ReadError> {
10        let document_list = self.svg_document_list()?;
11        let svg_document = document_list
12            .document_records()
13            .binary_search_by(|r| {
14                if r.start_glyph_id.get() > glyph_id {
15                    Ordering::Greater
16                } else if r.end_glyph_id.get() < glyph_id {
17                    Ordering::Less
18                } else {
19                    Ordering::Equal
20                }
21            })
22            .ok()
23            .and_then(|index| document_list.document_records().get(index))
24            .and_then(|r| {
25                let all_data = document_list.data.as_bytes();
26                let start = r.svg_doc_offset();
27                let end = start.checked_add(r.svg_doc_length())?;
28                all_data.get(start as usize..end as usize)
29            });
30        Ok(svg_document)
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use font_test_data::bebuffer::BeBuffer;
37
38    use super::*;
39
40    #[test]
41    fn read_dummy_svg_file() {
42        let data: [u16; 32] = [
43            0, // Version
44            0, 10, // SVGDocumentListOffset
45            0, 0, // Reserved
46            // SVGDocumentList
47            3, // numEntries
48            // documentRecords
49            // Record 1
50            1, // startGlyphID
51            3, // endGlyphID
52            0, 38, // svgDocOffset
53            0, 10, // svgDocLength
54            // Record 2
55            6, // startGlyphID
56            7, // endGlyphID
57            0, 48, // svgDocOffset
58            0, 6, // svgDocLength
59            // Record 3
60            9, // startGlyphID
61            9, // endGlyphID
62            0, 38, // svgDocOffset
63            0, 10,
64            // svgDocLength
65            // SVG Documents. Not actual valid SVGs, but just dummy data.
66            1, 0, 0, 0, 1, // Document 1
67            2, 0, 0, // Document 2
68        ];
69
70        let mut buf = BeBuffer::new();
71        buf = buf.extend(data);
72
73        let table = Svg::read(buf.data().into()).unwrap();
74
75        let first_document = &[0, 1, 0, 0, 0, 0, 0, 0, 0, 1][..];
76        let second_document = &[0, 2, 0, 0, 0, 0][..];
77
78        assert_eq!(table.glyph_data(GlyphId::new(0)).unwrap(), None);
79        assert_eq!(
80            table.glyph_data(GlyphId::new(1)).unwrap(),
81            Some(first_document)
82        );
83        assert_eq!(
84            table.glyph_data(GlyphId::new(2)).unwrap(),
85            Some(first_document)
86        );
87        assert_eq!(
88            table.glyph_data(GlyphId::new(3)).unwrap(),
89            Some(first_document)
90        );
91        assert_eq!(table.glyph_data(GlyphId::new(4)).unwrap(), None);
92        assert_eq!(table.glyph_data(GlyphId::new(5)).unwrap(), None);
93        assert_eq!(
94            table.glyph_data(GlyphId::new(6)).unwrap(),
95            Some(second_document)
96        );
97        assert_eq!(
98            table.glyph_data(GlyphId::new(7)).unwrap(),
99            Some(second_document)
100        );
101        assert_eq!(table.glyph_data(GlyphId::new(8)).unwrap(), None);
102        assert_eq!(
103            table.glyph_data(GlyphId::new(9)).unwrap(),
104            Some(first_document)
105        );
106        assert_eq!(table.glyph_data(GlyphId::new(10)).unwrap(), None);
107    }
108
109    #[test]
110    fn test_svg_glyph_data_overflow_guard() {
111        let data: [u16; 12] = [
112            0, // Version
113            0, 10, // SVGDocumentListOffset
114            0, 0, // Reserved
115            // SVGDocumentList
116            1, // numEntries
117            // documentRecords
118            // Record 1
119            1, // startGlyphID
120            3, // endGlyphID
121            0xFFFF, 0xFFFF, // svgDocOffset
122            0, 10, // svgDocLength
123        ];
124        let mut buf = BeBuffer::new();
125        buf = buf.extend(data);
126        let table = Svg::read(buf.data().into()).unwrap();
127        // Just don't panic with overflow
128        let _ = table.glyph_data(GlyphId::new(1));
129    }
130}