rs_woff/
lib.rs

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; // how many bytes have been written yet
96
97    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}