1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! 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));
}
}