allsorts_subset_browser/
woff.rs1use flate2::bufread::ZlibDecoder;
4
5use crate::binary::read::{ReadArray, ReadBinary, ReadBuf, ReadCtxt, ReadFrom, ReadScope};
6use crate::binary::U32Be;
7use crate::error::ParseError;
8use crate::tables::{FontTableProvider, SfntVersion};
9
10use std::borrow::Cow;
11use std::convert::TryFrom;
12use std::io::Read;
13
14pub const MAGIC: u32 = 0x774F4646;
16
17#[derive(Clone)]
18pub struct WoffFont<'a> {
19 pub scope: ReadScope<'a>,
20 pub woff_header: WoffHeader,
21 pub table_directory: ReadArray<'a, TableDirectoryEntry>,
22}
23
24#[derive(Clone, Debug)]
25pub struct WoffHeader {
26 pub flavor: u32,
27 pub length: u32,
28 pub num_tables: u16,
29 pub total_sfnt_size: u32,
30 pub _major_version: u16,
31 pub _minor_version: u16,
32 pub meta_offset: u32,
33 pub meta_length: u32,
34 pub meta_orig_length: u32,
35 pub priv_offset: u32,
36 pub priv_length: u32,
37}
38
39#[derive(Debug, Clone)]
40pub struct TableDirectoryEntry {
41 pub tag: u32,
42 pub offset: u32,
43 pub comp_length: u32,
44 pub orig_length: u32,
45 pub orig_checksum: u32,
46}
47
48impl<'a> WoffFont<'a> {
49 pub fn flavor(&self) -> u32 {
51 self.woff_header.flavor
52 }
53
54 pub fn extended_metadata(&self) -> Result<Option<String>, ParseError> {
56 let offset = usize::try_from(self.woff_header.meta_offset)?;
57 let length = usize::try_from(self.woff_header.meta_length)?;
58 if offset == 0 || length == 0 {
59 return Ok(None);
60 }
61
62 let compressed_metadata = self.scope.offset_length(offset, length)?;
63 let mut z = ZlibDecoder::new(compressed_metadata.data());
64 let mut metadata = String::new();
65 z.read_to_string(&mut metadata)
66 .map_err(|_err| ParseError::CompressionError)?;
67
68 Ok(Some(metadata))
69 }
70
71 pub fn find_table_directory_entry(&self, tag: u32) -> Option<TableDirectoryEntry> {
73 self.table_directory
74 .iter()
75 .find(|table_entry| table_entry.tag == tag)
76 }
77}
78
79impl<'b> ReadBinary for WoffFont<'b> {
80 type HostType<'a> = WoffFont<'a>;
81
82 fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
83 let scope = ctxt.scope();
84 let woff_header = ctxt.read::<WoffHeader>()?;
85 let table_directory =
86 ctxt.read_array::<TableDirectoryEntry>(usize::from(woff_header.num_tables))?;
87 Ok(WoffFont {
88 scope,
89 woff_header,
90 table_directory,
91 })
92 }
93}
94
95impl<'a> FontTableProvider for WoffFont<'a> {
96 fn table_data(&self, tag: u32) -> Result<Option<Cow<'_, [u8]>>, ParseError> {
97 self.find_table_directory_entry(tag)
98 .map(|table_entry| {
99 table_entry
100 .read_table(&self.scope)
101 .map(|table| table.into_data())
102 })
103 .transpose()
104 }
105
106 fn has_table(&self, tag: u32) -> bool {
107 self.find_table_directory_entry(tag).is_some()
108 }
109
110 fn table_tags(&self) -> Option<Vec<u32>> {
111 Some(self.table_directory.iter().map(|entry| entry.tag).collect())
112 }
113}
114
115impl<'a> SfntVersion for WoffFont<'a> {
116 fn sfnt_version(&self) -> u32 {
117 self.flavor()
118 }
119}
120
121impl ReadBinary for WoffHeader {
122 type HostType<'a> = Self;
123
124 fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
125 let signature = ctxt.read_u32be()?;
126 match signature {
127 MAGIC => {
128 let flavor = ctxt.read_u32be()?;
129 let length = ctxt.read_u32be()?;
130 let num_tables = ctxt.read_u16be()?;
131 let reserved = ctxt.read_u16be()?;
132 ctxt.check(reserved == 0)?;
135 let total_sfnt_size = ctxt.read_u32be()?;
136 let _major_version = ctxt.read_u16be()?;
141 let _minor_version = ctxt.read_u16be()?;
142 let meta_offset = ctxt.read_u32be()?;
143 let meta_length = ctxt.read_u32be()?;
144 let meta_orig_length = ctxt.read_u32be()?;
145 let priv_offset = ctxt.read_u32be()?;
146 let priv_length = ctxt.read_u32be()?;
147
148 Ok(WoffHeader {
149 flavor,
150 length,
151 num_tables,
152 total_sfnt_size,
153 _major_version,
154 _minor_version,
155 meta_offset,
156 meta_length,
157 meta_orig_length,
158 priv_offset,
159 priv_length,
160 })
161 }
162 _ => Err(ParseError::BadVersion),
163 }
164 }
165}
166
167impl ReadFrom for TableDirectoryEntry {
168 type ReadType = ((U32Be, U32Be, U32Be), (U32Be, U32Be));
169 fn read_from(
170 ((tag, offset, comp_length), (orig_length, orig_checksum)): ((u32, u32, u32), (u32, u32)),
171 ) -> Self {
172 TableDirectoryEntry {
173 tag,
174 offset,
175 comp_length,
176 orig_length,
177 orig_checksum,
178 }
179 }
180}
181
182impl TableDirectoryEntry {
183 fn is_compressed(&self) -> bool {
184 self.comp_length != self.orig_length
185 }
186
187 pub fn read_table<'a>(&self, scope: &ReadScope<'a>) -> Result<ReadBuf<'a>, ParseError> {
189 let offset = usize::try_from(self.offset)?;
190 let length = usize::try_from(self.comp_length)?;
191 let table_data = scope.offset_length(offset, length)?;
192
193 if self.is_compressed() {
194 let mut z = ZlibDecoder::new(table_data.data());
195 let mut uncompressed = Vec::new();
196 z.read_to_end(&mut uncompressed)
197 .map_err(|_err| ParseError::CompressionError)?;
198
199 Ok(ReadBuf::from(uncompressed))
200 } else {
201 Ok(ReadBuf::from(table_data.data()))
202 }
203 }
204}