Coverage Report

Created: 2026-10-17 06:31

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::GlyphId;
4
use crate::parser::{FromData, LazyArray16, NumFrom, Offset, Offset32, Stream};
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
137
    pub fn glyphs_range(&self) -> core::ops::RangeInclusive<GlyphId> {
23
137
        self.start_glyph_id..=self.end_glyph_id
24
137
    }
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
443
    fn glyphs_range(&self) -> core::ops::RangeInclusive<GlyphId> {
37
443
        self.start_glyph_id..=self.end_glyph_id
38
443
    }
39
}
40
41
impl FromData for SvgDocumentRecord {
42
    const SIZE: usize = 12;
43
44
    #[inline]
45
148k
    fn parse(data: &[u8]) -> Option<Self> {
46
148k
        let mut s = Stream::new(data);
47
        Some(SvgDocumentRecord {
48
148k
            start_glyph_id: s.read::<GlyphId>()?,
49
148k
            end_glyph_id: s.read::<GlyphId>()?,
50
148k
            svg_doc_offset: s.read::<Option<Offset32>>()?,
51
148k
            svg_doc_length: s.read::<u32>()?,
52
        })
53
148k
    }
<ttf_parser::tables::svg::SvgDocumentRecord as ttf_parser::parser::FromData>::parse
Line
Count
Source
45
148k
    fn parse(data: &[u8]) -> Option<Self> {
46
148k
        let mut s = Stream::new(data);
47
        Some(SvgDocumentRecord {
48
148k
            start_glyph_id: s.read::<GlyphId>()?,
49
148k
            end_glyph_id: s.read::<GlyphId>()?,
50
148k
            svg_doc_offset: s.read::<Option<Offset32>>()?,
51
148k
            svg_doc_length: s.read::<u32>()?,
52
        })
53
148k
    }
<ttf_parser::tables::svg::SvgDocumentRecord as ttf_parser::parser::FromData>::parse
Line
Count
Source
45
517
    fn parse(data: &[u8]) -> Option<Self> {
46
517
        let mut s = Stream::new(data);
47
        Some(SvgDocumentRecord {
48
517
            start_glyph_id: s.read::<GlyphId>()?,
49
517
            end_glyph_id: s.read::<GlyphId>()?,
50
517
            svg_doc_offset: s.read::<Option<Offset32>>()?,
51
517
            svg_doc_length: s.read::<u32>()?,
52
        })
53
517
    }
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
148k
    pub fn get(&self, index: u16) -> Option<SvgDocument<'a>> {
70
148k
        let record = self.records.get(index)?;
71
148k
        let offset = record.svg_doc_offset?.to_usize();
72
145k
        self.data
73
145k
            .get(offset..offset + usize::num_from(record.svg_doc_length))
74
145k
            .map(|data| SvgDocument {
75
138
                data,
76
138
                start_glyph_id: record.start_glyph_id,
77
138
                end_glyph_id: record.end_glyph_id,
78
138
            })
<ttf_parser::tables::svg::SvgDocumentsList>::get::{closure#0}
Line
Count
Source
75
137
                data,
76
137
                start_glyph_id: record.start_glyph_id,
77
137
                end_glyph_id: record.end_glyph_id,
78
137
            })
<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
148k
    }
<ttf_parser::tables::svg::SvgDocumentsList>::get
Line
Count
Source
69
148k
    pub fn get(&self, index: u16) -> Option<SvgDocument<'a>> {
70
148k
        let record = self.records.get(index)?;
71
148k
        let offset = record.svg_doc_offset?.to_usize();
72
145k
        self.data
73
145k
            .get(offset..offset + usize::num_from(record.svg_doc_length))
74
145k
            .map(|data| SvgDocument {
75
                data,
76
                start_glyph_id: record.start_glyph_id,
77
                end_glyph_id: record.end_glyph_id,
78
            })
79
148k
    }
<ttf_parser::tables::svg::SvgDocumentsList>::get
Line
Count
Source
69
74
    pub fn get(&self, index: u16) -> Option<SvgDocument<'a>> {
70
74
        let record = self.records.get(index)?;
71
74
        let offset = record.svg_doc_offset?.to_usize();
72
73
        self.data
73
73
            .get(offset..offset + usize::num_from(record.svg_doc_length))
74
73
            .map(|data| SvgDocument {
75
                data,
76
                start_glyph_id: record.start_glyph_id,
77
                end_glyph_id: record.end_glyph_id,
78
            })
79
74
    }
80
81
    /// Returns a SVG document data by glyph ID.
82
    #[inline]
83
86
    pub fn find(&self, glyph_id: GlyphId) -> Option<SvgDocument<'a>> {
84
86
        let index = self
85
86
            .records
86
86
            .into_iter()
87
443
            .position(|v| v.glyphs_range().contains(&glyph_id))?;
88
74
        self.get(index as u16)
89
86
    }
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
524
    pub fn parse(data: &'a [u8]) -> Option<Self> {
158
524
        let mut s = Stream::new(data);
159
524
        s.skip::<u16>(); // version
160
524
        let doc_list_offset = s.read::<Option<Offset32>>()??;
161
162
492
        let mut s = Stream::new_at(data, doc_list_offset.to_usize())?;
163
234
        let count = s.read::<u16>()?;
164
222
        let records = s.read_array16::<SvgDocumentRecord>(count)?;
165
166
112
        Some(Table {
167
112
            documents: SvgDocumentsList {
168
112
                data: &data[doc_list_offset.0 as usize..],
169
112
                records,
170
112
            },
171
112
        })
172
524
    }
173
}