1use std::{io};
2use std::io::{Read, Seek, SeekFrom, Write};
3
4#[allow(dead_code)]
5struct Header {
6 signature: u32,
7 flavour: u32,
8 length: u32,
9 num_tables: u16,
10 reserved: u16,
11 total_sfnt_size: u32,
12 major_version: u16,
13 minor_version: u16,
14 meta_offset: u32,
15 meta_length: u32,
16 meta_orig_length: u32,
17 priv_offset: u32,
18 priv_length: u32,
19}
20
21struct TableDirectoryEntry {
22 tag: u32,
23 offset: u32,
24 comp_length: u32,
25 orig_length: u32,
26 orig_checksum: u32,
27 otf_offset: u32,
28}
29
30fn read_u32_be<R>(input: &mut R) -> io::Result<u32> where R: Read {
31 let mut bf = [0; 4];
32 input.read_exact(&mut bf)?;
33
34 Ok(u32::from_be_bytes(bf))
35}
36
37fn read_u16_be<R>(input: &mut R) -> io::Result<u16> where R: Read {
38 let mut bf = [0; 2];
39 input.read_exact(&mut bf)?;
40
41 Ok(u16::from_be_bytes(bf))
42}
43
44fn write_u32_be<W>(output: &mut W, num: u32) -> io::Result<()> where W: Write {
45 assert_eq!(output.write(&num.to_be_bytes())?, 4);
46 Ok(())
47}
48
49fn write_u16_be<W>(output: &mut W, num: u16) -> io::Result<()> where W: Write {
50 assert_eq!(output.write(&num.to_be_bytes())?, 2);
51 Ok(())
52}
53
54pub fn woff2otf<I, O>(mut input: &mut I, output: &mut O) -> io::Result<()>
55 where I: Read + Seek, O: Write {
56 let header = Header {
57 signature: read_u32_be(input)?,
58 flavour: read_u32_be(input)?,
59 length: read_u32_be(input)?,
60 num_tables: read_u16_be(input)?,
61 reserved: read_u16_be(input)?,
62 total_sfnt_size: read_u32_be(input)?,
63 major_version: read_u16_be(input)?,
64 minor_version: read_u16_be(input)?,
65 meta_offset: read_u32_be(input)?,
66 meta_length: read_u32_be(input)?,
67 meta_orig_length: read_u32_be(input)?,
68 priv_offset: read_u32_be(input)?,
69 priv_length: read_u32_be(input)?,
70 };
71
72 write_u32_be(output, header.flavour)?;
73 write_u16_be(output, header.num_tables)?;
74
75 let (entry_selector, search_range) = (0..16)
76 .map(
77 |n| {
78 (n, u16::pow(2, n))
79 }
80 )
81 .filter(
82 |x| x.1 <= header.num_tables
83 )
84 .map(
85 |x| (x.0, x.1 * 16)
86 )
87 .last()
88 .unwrap();
89
90 write_u16_be(output, search_range)?;
91 write_u16_be(output, entry_selector as u16)?;
92 let range_shift = header.num_tables * 16 - search_range;
93 write_u16_be(output, range_shift)?;
94
95 let mut offset = 4 + 2 + 2 + 2 + 2; let mut table_directory_entries = Vec::new();
98 for _ in 0..header.num_tables {
99 table_directory_entries.push(
100 TableDirectoryEntry {
101 tag: read_u32_be(input)?,
102 offset: read_u32_be(input)?,
103 comp_length: read_u32_be(input)?,
104 orig_length: read_u32_be(input)?,
105 orig_checksum: read_u32_be(input)?,
106 otf_offset: 0,
107 }
108 );
109
110 offset += 16;
111 };
112
113 for table_directory_entry in &mut table_directory_entries {
114 table_directory_entry.otf_offset = offset;
115 offset += table_directory_entry.orig_length;
116
117 write_u32_be(output, table_directory_entry.tag)?;
118 write_u32_be(output, table_directory_entry.orig_checksum)?;
119 write_u32_be(output, table_directory_entry.otf_offset)?;
120 write_u32_be(output, table_directory_entry.orig_length)?;
121
122 if offset % 4 != 0 {
123 offset += 4 - (offset % 4);
124 }
125 }
126
127 for table_directory_entry in table_directory_entries {
128 input.seek(SeekFrom::Start(table_directory_entry.offset as u64))?;
129
130 if table_directory_entry.comp_length != table_directory_entry.orig_length {
131 let mut rd = flate2::read::ZlibDecoder::new(&mut input)
132 .take(table_directory_entry.orig_length as u64);
133 io::copy(&mut rd, output)?;
134 } else {
135 let mut rd = input
136 .take(table_directory_entry.orig_length as u64);
137 io::copy(&mut rd, output)?;
138 };
139
140 input.seek(
141 SeekFrom::Start(
142 (table_directory_entry.otf_offset + table_directory_entry.comp_length) as u64
143 )
144 )?;
145
146 let end_offset = table_directory_entry.otf_offset + table_directory_entry.orig_length;
147 if end_offset % 4 != 0 {
148 output.write_all(
149 vec![0_u8; (4 - (end_offset % 4)) as usize].as_slice()
150 )?;
151 }
152 }
153
154 Ok(())
155}
156
157
158#[test]
159fn test() {
160 let input = include_bytes!("../test_assets/OpenSans-Regular.woff");
161 let output = Vec::new();
162 let mut cursor_i = io::Cursor::new(input);
163 let mut cursor_o = io::Cursor::new(output);
164
165 woff2otf(&mut cursor_i, &mut cursor_o).unwrap();
166
167 let expected = include_bytes!("../test_assets/OpenSans.otf");
168 assert_eq!(expected, cursor_o.into_inner().as_slice());
169}