Coverage Report

Created: 2026-06-16 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ttf-parser/src/tables/svg.rs
Line
Count
Source
1
//! An [SVG Table](https://docs.microsoft.com/en-us/typography/opentype/spec/svg) implementation.
2
3
use crate::parser::{FromData, LazyArray16, NumFrom, Offset, Offset32, Stream};
4
use 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)]
9
pub 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
20
impl SvgDocument<'_> {
21
    /// Returns the glyphs range.
22
392
    pub fn glyphs_range(&self) -> core::ops::RangeInclusive<GlyphId> {
23
392
        self.start_glyph_id..=self.end_glyph_id
24
392
    }
25
}
26
27
#[derive(Clone, Copy)]
28
struct SvgDocumentRecord {
29
    start_glyph_id: GlyphId,
30
    end_glyph_id: GlyphId,
31
    svg_doc_offset: Option<Offset32>,
32
    svg_doc_length: u32,
33
}
34
35
impl SvgDocumentRecord {
36
813
    fn glyphs_range(&self) -> core::ops::RangeInclusive<GlyphId> {
37
813
        self.start_glyph_id..=self.end_glyph_id
38
813
    }
39
}
40
41
impl FromData for SvgDocumentRecord {
42
    const SIZE: usize = 12;
43
44
    #[inline]
45
145k
    fn parse(data: &[u8]) -> Option<Self> {
46
145k
        let mut s = Stream::new(data);
47
        Some(SvgDocumentRecord {
48
145k
            start_glyph_id: s.read::<GlyphId>()?,
49
145k
            end_glyph_id: s.read::<GlyphId>()?,
50
145k
            svg_doc_offset: s.read::<Option<Offset32>>()?,
51
145k
            svg_doc_length: s.read::<u32>()?,
52
        })
53
145k
    }
<ttf_parser::tables::svg::SvgDocumentRecord as ttf_parser::parser::FromData>::parse
Line
Count
Source
45
144k
    fn parse(data: &[u8]) -> Option<Self> {
46
144k
        let mut s = Stream::new(data);
47
        Some(SvgDocumentRecord {
48
144k
            start_glyph_id: s.read::<GlyphId>()?,
49
144k
            end_glyph_id: s.read::<GlyphId>()?,
50
144k
            svg_doc_offset: s.read::<Option<Offset32>>()?,
51
144k
            svg_doc_length: s.read::<u32>()?,
52
        })
53
144k
    }
<ttf_parser::tables::svg::SvgDocumentRecord as ttf_parser::parser::FromData>::parse
Line
Count
Source
45
902
    fn parse(data: &[u8]) -> Option<Self> {
46
902
        let mut s = Stream::new(data);
47
        Some(SvgDocumentRecord {
48
902
            start_glyph_id: s.read::<GlyphId>()?,
49
902
            end_glyph_id: s.read::<GlyphId>()?,
50
902
            svg_doc_offset: s.read::<Option<Offset32>>()?,
51
902
            svg_doc_length: s.read::<u32>()?,
52
        })
53
902
    }
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)]
59
pub struct SvgDocumentsList<'a> {
60
    data: &'a [u8],
61
    records: LazyArray16<'a, SvgDocumentRecord>,
62
}
63
64
impl<'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
144k
    pub fn get(&self, index: u16) -> Option<SvgDocument<'a>> {
70
144k
        let record = self.records.get(index)?;
71
144k
        let offset = record.svg_doc_offset?.to_usize();
72
124k
        self.data
73
124k
            .get(offset..offset + usize::num_from(record.svg_doc_length))
74
124k
            .map(|data| SvgDocument {
75
393
                data,
76
393
                start_glyph_id: record.start_glyph_id,
77
393
                end_glyph_id: record.end_glyph_id,
78
393
            })
<ttf_parser::tables::svg::SvgDocumentsList>::get::{closure#0}
Line
Count
Source
75
392
                data,
76
392
                start_glyph_id: record.start_glyph_id,
77
392
                end_glyph_id: record.end_glyph_id,
78
392
            })
<ttf_parser::tables::svg::SvgDocumentsList>::get::{closure#0}
Line
Count
Source
75
1
                data,
76
1
                start_glyph_id: record.start_glyph_id,
77
1
                end_glyph_id: record.end_glyph_id,
78
1
            })
79
144k
    }
<ttf_parser::tables::svg::SvgDocumentsList>::get
Line
Count
Source
69
144k
    pub fn get(&self, index: u16) -> Option<SvgDocument<'a>> {
70
144k
        let record = self.records.get(index)?;
71
144k
        let offset = record.svg_doc_offset?.to_usize();
72
124k
        self.data
73
124k
            .get(offset..offset + usize::num_from(record.svg_doc_length))
74
124k
            .map(|data| SvgDocument {
75
                data,
76
                start_glyph_id: record.start_glyph_id,
77
                end_glyph_id: record.end_glyph_id,
78
            })
79
144k
    }
<ttf_parser::tables::svg::SvgDocumentsList>::get
Line
Count
Source
69
89
    pub fn get(&self, index: u16) -> Option<SvgDocument<'a>> {
70
89
        let record = self.records.get(index)?;
71
89
        let offset = record.svg_doc_offset?.to_usize();
72
82
        self.data
73
82
            .get(offset..offset + usize::num_from(record.svg_doc_length))
74
82
            .map(|data| SvgDocument {
75
                data,
76
                start_glyph_id: record.start_glyph_id,
77
                end_glyph_id: record.end_glyph_id,
78
            })
79
89
    }
80
81
    /// Returns a SVG document data by glyph ID.
82
    #[inline]
83
98
    pub fn find(&self, glyph_id: GlyphId) -> Option<SvgDocument<'a>> {
84
98
        let index = self
85
98
            .records
86
98
            .into_iter()
87
813
            .position(|v| v.glyphs_range().contains(&glyph_id))?;
88
89
        self.get(index as u16)
89
98
    }
90
91
    /// Returns the number of SVG documents in the list.
92
4
    pub fn len(&self) -> u16 {
93
4
        self.records.len()
94
4
    }
95
96
    /// Checks if the list is empty.
97
0
    pub fn is_empty(&self) -> bool {
98
0
        self.records.is_empty()
99
0
    }
100
}
101
102
impl core::fmt::Debug for SvgDocumentsList<'_> {
103
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
104
0
        write!(f, "SvgDocumentsList {{ ... }}")
105
0
    }
106
}
107
108
impl<'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)]
124
pub struct SvgDocumentsListIter<'a> {
125
    list: SvgDocumentsList<'a>,
126
    index: u16,
127
}
128
129
impl<'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)]
150
pub struct Table<'a> {
151
    /// A list of SVG documents.
152
    pub documents: SvgDocumentsList<'a>,
153
}
154
155
impl<'a> Table<'a> {
156
    /// Parses a table from raw data.
157
363
    pub fn parse(data: &'a [u8]) -> Option<Self> {
158
363
        let mut s = Stream::new(data);
159
363
        s.skip::<u16>(); // version
160
363
        let doc_list_offset = s.read::<Option<Offset32>>()??;
161
162
352
        let mut s = Stream::new_at(data, doc_list_offset.to_usize())?;
163
198
        let count = s.read::<u16>()?;
164
191
        let records = s.read_array16::<SvgDocumentRecord>(count)?;
165
166
120
        Some(Table {
167
120
            documents: SvgDocumentsList {
168
120
                data: &data[doc_list_offset.0 as usize..],
169
120
                records,
170
120
            },
171
120
        })
172
363
    }
173
}