ttf_parser/tables/
svg.rs

1//! An [SVG Table](https://docs.microsoft.com/en-us/typography/opentype/spec/svg) implementation.
2
3use crate::parser::{FromData, LazyArray16, NumFrom, Offset, Offset32, Stream};
4use crate::GlyphId;
5
6/// An [SVG documents](
7/// https://docs.microsoft.com/en-us/typography/opentype/spec/svg#svg-document-list).
8#[derive(Clone, Copy, Debug)]
9pub struct SvgDocument<'a> {
10    /// The SVG document data.
11    ///
12    /// Can be stored as a string or as a gzip compressed data, aka SVGZ.
13    pub data: &'a [u8],
14    /// The first glyph ID for the range covered by this record.
15    pub start_glyph_id: GlyphId,
16    /// The last glyph ID, *inclusive*, for the range covered by this record.
17    pub end_glyph_id: GlyphId,
18}
19
20impl SvgDocument<'_> {
21    /// Returns the glyphs range.
22    pub fn glyphs_range(&self) -> core::ops::RangeInclusive<GlyphId> {
23        self.start_glyph_id..=self.end_glyph_id
24    }
25}
26
27#[derive(Clone, Copy)]
28struct SvgDocumentRecord {
29    start_glyph_id: GlyphId,
30    end_glyph_id: GlyphId,
31    svg_doc_offset: Option<Offset32>,
32    svg_doc_length: u32,
33}
34
35impl SvgDocumentRecord {
36    fn glyphs_range(&self) -> core::ops::RangeInclusive<GlyphId> {
37        self.start_glyph_id..=self.end_glyph_id
38    }
39}
40
41impl FromData for SvgDocumentRecord {
42    const SIZE: usize = 12;
43
44    #[inline]
45    fn parse(data: &[u8]) -> Option<Self> {
46        let mut s = Stream::new(data);
47        Some(SvgDocumentRecord {
48            start_glyph_id: s.read::<GlyphId>()?,
49            end_glyph_id: s.read::<GlyphId>()?,
50            svg_doc_offset: s.read::<Option<Offset32>>()?,
51            svg_doc_length: s.read::<u32>()?,
52        })
53    }
54}
55
56/// A list of [SVG documents](
57/// https://docs.microsoft.com/en-us/typography/opentype/spec/svg#svg-document-list).
58#[derive(Clone, Copy)]
59pub struct SvgDocumentsList<'a> {
60    data: &'a [u8],
61    records: LazyArray16<'a, SvgDocumentRecord>,
62}
63
64impl<'a> SvgDocumentsList<'a> {
65    /// Returns SVG document data at index.
66    ///
67    /// `index` is not a GlyphId. You should use [`find()`](SvgDocumentsList::find) instead.
68    #[inline]
69    pub fn get(&self, index: u16) -> Option<SvgDocument<'a>> {
70        let record = self.records.get(index)?;
71        let offset = record.svg_doc_offset?.to_usize();
72        self.data
73            .get(offset..offset + usize::num_from(record.svg_doc_length))
74            .map(|data| SvgDocument {
75                data,
76                start_glyph_id: record.start_glyph_id,
77                end_glyph_id: record.end_glyph_id,
78            })
79    }
80
81    /// Returns a SVG document data by glyph ID.
82    #[inline]
83    pub fn find(&self, glyph_id: GlyphId) -> Option<SvgDocument<'a>> {
84        let index = self
85            .records
86            .into_iter()
87            .position(|v| v.glyphs_range().contains(&glyph_id))?;
88        self.get(index as u16)
89    }
90
91    /// Returns the number of SVG documents in the list.
92    pub fn len(&self) -> u16 {
93        self.records.len()
94    }
95
96    /// Checks if the list is empty.
97    pub fn is_empty(&self) -> bool {
98        self.records.is_empty()
99    }
100}
101
102impl core::fmt::Debug for SvgDocumentsList<'_> {
103    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
104        write!(f, "SvgDocumentsList {{ ... }}")
105    }
106}
107
108impl<'a> IntoIterator for SvgDocumentsList<'a> {
109    type Item = SvgDocument<'a>;
110    type IntoIter = SvgDocumentsListIter<'a>;
111
112    #[inline]
113    fn into_iter(self) -> Self::IntoIter {
114        SvgDocumentsListIter {
115            list: self,
116            index: 0,
117        }
118    }
119}
120
121/// An iterator over [`SvgDocumentsList`] values.
122#[derive(Clone, Copy)]
123#[allow(missing_debug_implementations)]
124pub struct SvgDocumentsListIter<'a> {
125    list: SvgDocumentsList<'a>,
126    index: u16,
127}
128
129impl<'a> Iterator for SvgDocumentsListIter<'a> {
130    type Item = SvgDocument<'a>;
131
132    #[inline]
133    fn next(&mut self) -> Option<Self::Item> {
134        if self.index < self.list.len() {
135            self.index += 1;
136            self.list.get(self.index - 1)
137        } else {
138            None
139        }
140    }
141
142    #[inline]
143    fn count(self) -> usize {
144        usize::from(self.list.len().saturating_sub(self.index))
145    }
146}
147
148/// An [SVG Table](https://docs.microsoft.com/en-us/typography/opentype/spec/svg).
149#[derive(Clone, Copy, Debug)]
150pub struct Table<'a> {
151    /// A list of SVG documents.
152    pub documents: SvgDocumentsList<'a>,
153}
154
155impl<'a> Table<'a> {
156    /// Parses a table from raw data.
157    pub fn parse(data: &'a [u8]) -> Option<Self> {
158        let mut s = Stream::new(data);
159        s.skip::<u16>(); // version
160        let doc_list_offset = s.read::<Option<Offset32>>()??;
161
162        let mut s = Stream::new_at(data, doc_list_offset.to_usize())?;
163        let count = s.read::<u16>()?;
164        let records = s.read_array16::<SvgDocumentRecord>(count)?;
165
166        Some(Table {
167            documents: SvgDocumentsList {
168                data: &data[doc_list_offset.0 as usize..],
169                records,
170            },
171        })
172    }
173}