/src/serenity/Userland/Libraries/LibGfx/Font/WOFF2/Font.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2022, Luke Wilde <lukew@serenityos.org> |
3 | | * Copyright (c) 2023, Andreas Kling <kling@serenityos.org> |
4 | | * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org> |
5 | | * |
6 | | * SPDX-License-Identifier: BSD-2-Clause |
7 | | */ |
8 | | |
9 | | #include <AK/BitStream.h> |
10 | | #include <AK/MemoryStream.h> |
11 | | #include <LibCompress/Brotli.h> |
12 | | #include <LibCore/Resource.h> |
13 | | #include <LibGfx/Font/Font.h> |
14 | | #include <LibGfx/Font/OpenType/Font.h> |
15 | | #include <LibGfx/Font/WOFF2/Font.h> |
16 | | |
17 | | // The following is an implementation of the WOFF2 specification. |
18 | | // https://www.w3.org/TR/WOFF2/ |
19 | | |
20 | | namespace WOFF2 { |
21 | | |
22 | | // https://www.w3.org/TR/WOFF2/#woff20Header |
23 | | struct [[gnu::packed]] Header { |
24 | | BigEndian<u32> signature; // 0x774F4632 'wOF2' |
25 | | BigEndian<u32> flavor; // The "sfnt version" of the input font. |
26 | | BigEndian<u32> length; // Total size of the WOFF file. |
27 | | BigEndian<u16> num_tables; // Number of entries in directory of font tables. |
28 | | BigEndian<u16> reserved; // Reserved; set to 0. |
29 | | BigEndian<u32> total_sfnt_size; // Total size needed for the uncompressed font data, including the sfnt header, |
30 | | // directory, and font tables (including padding). |
31 | | BigEndian<u32> total_compressed_size; // Total length of the compressed data block. |
32 | | BigEndian<u16> major_version; // Major version of the WOFF file. |
33 | | BigEndian<u16> minor_version; // Minor version of the WOFF file. |
34 | | BigEndian<u32> meta_offset; // Offset to metadata block, from beginning of WOFF file. |
35 | | BigEndian<u32> meta_length; // Length of compressed metadata block. |
36 | | BigEndian<u32> meta_orig_length; // Uncompressed size of metadata block. |
37 | | BigEndian<u32> priv_offset; // Offset to private data block, from beginning of WOFF file. |
38 | | BigEndian<u32> priv_length; // Length of private data block. |
39 | | }; |
40 | | static_assert(AssertSize<Header, 48>()); |
41 | | |
42 | | } |
43 | | |
44 | | template<> |
45 | | class AK::Traits<WOFF2::Header> : public DefaultTraits<WOFF2::Header> { |
46 | | public: |
47 | 0 | static constexpr bool is_trivially_serializable() { return true; } |
48 | | }; |
49 | | |
50 | | namespace WOFF2 { |
51 | | |
52 | | static constexpr u32 WOFF2_SIGNATURE = 0x774F4632; |
53 | | static constexpr u32 TTCF_SIGNAURE = 0x74746366; |
54 | | static constexpr size_t SFNT_HEADER_SIZE = 12; |
55 | | static constexpr size_t SFNT_TABLE_SIZE = 16; |
56 | | |
57 | | [[maybe_unused]] static ErrorOr<u16> read_255_u_short(FixedMemoryStream& stream) |
58 | 284k | { |
59 | 284k | constexpr u8 one_more_byte_code_1 = 255; |
60 | 284k | constexpr u8 one_more_byte_code_2 = 254; |
61 | 284k | constexpr u8 word_code = 253; |
62 | 284k | constexpr u8 lowest_u_code = 253; |
63 | 284k | constexpr u16 lowest_u_code_multiplied_by_2 = lowest_u_code * 2; |
64 | | |
65 | 284k | auto code = TRY(stream.read_value<u8>()); |
66 | | |
67 | 284k | if (code == word_code) { |
68 | 806 | return TRY(stream.read_value<BigEndian<u16>>()); |
69 | 806 | } |
70 | | |
71 | 283k | if (code == one_more_byte_code_1) { |
72 | 602 | u16 final_value = TRY(stream.read_value<u8>()); |
73 | 0 | final_value += lowest_u_code; |
74 | 599 | return final_value; |
75 | 602 | } |
76 | | |
77 | 282k | if (code == one_more_byte_code_2) { |
78 | 498 | u16 final_value = TRY(stream.read_value<u8>()); |
79 | 0 | final_value += lowest_u_code_multiplied_by_2; |
80 | 494 | return final_value; |
81 | 498 | } |
82 | 282k | return code; |
83 | 282k | } |
84 | | |
85 | | static ErrorOr<u32> read_uint_base_128(SeekableStream& stream) |
86 | 73.6k | { |
87 | 73.6k | u32 accumulator = 0; |
88 | | |
89 | 82.2k | for (u8 i = 0; i < 5; ++i) { |
90 | 82.2k | u8 const next_byte = TRY(stream.read_value<u8>()); |
91 | | |
92 | 82.0k | if (i == 0 && next_byte == 0x80) |
93 | 2 | return Error::from_string_literal("UIntBase128 type contains a leading zero"); |
94 | | |
95 | 82.0k | if (accumulator & 0xfe000000) |
96 | 13 | return Error::from_string_literal("UIntBase128 type exceeds the length of a u32"); |
97 | | |
98 | 82.0k | accumulator = (accumulator << 7) | (next_byte & 0x7F); |
99 | | |
100 | 82.0k | if ((next_byte & 0x80) == 0) |
101 | 73.4k | return accumulator; |
102 | 82.0k | } |
103 | | |
104 | 20 | return Error::from_string_literal("UIntBase128 type is larger than 5 bytes"); |
105 | 73.6k | } |
106 | | |
107 | | static i16 be_i16(u8 const* ptr) |
108 | 20.6k | { |
109 | 20.6k | return (((i16)ptr[0]) << 8) | ((i16)ptr[1]); |
110 | 20.6k | } |
111 | | |
112 | | static u16 pow_2_less_than_or_equal(u16 x) |
113 | 3.68k | { |
114 | 3.68k | VERIFY(x > 0); |
115 | 3.68k | VERIFY(x < 32769); |
116 | 3.68k | return 1 << (sizeof(u16) * 8 - count_leading_zeroes_safe<u16>(x - 1)); |
117 | 3.68k | } |
118 | | |
119 | | enum class TransformationVersion { |
120 | | Version0, |
121 | | Version1, |
122 | | Version2, |
123 | | Version3, |
124 | | }; |
125 | | |
126 | | struct TableDirectoryEntry { |
127 | | TransformationVersion transformation_version { TransformationVersion::Version0 }; |
128 | | OpenType::Tag tag; |
129 | | u32 original_length { 0 }; |
130 | | Optional<u32> transform_length; |
131 | | |
132 | | bool has_transformation() const |
133 | 22.2k | { |
134 | 22.2k | return transform_length.has_value(); |
135 | 22.2k | } |
136 | | }; |
137 | | |
138 | | // NOTE: Any tags less than 4 characters long are padded with spaces at the end. |
139 | | static constexpr Array<OpenType::Tag, 63> known_tag_names = { |
140 | | OpenType::Tag("cmap"), |
141 | | OpenType::Tag("head"), |
142 | | OpenType::Tag("hhea"), |
143 | | OpenType::Tag("hmtx"), |
144 | | OpenType::Tag("maxp"), |
145 | | OpenType::Tag("name"), |
146 | | OpenType::Tag("OS/2"), |
147 | | OpenType::Tag("post"), |
148 | | OpenType::Tag("cvt "), |
149 | | OpenType::Tag("fpgm"), |
150 | | OpenType::Tag("glyf"), |
151 | | OpenType::Tag("loca"), |
152 | | OpenType::Tag("prep"), |
153 | | OpenType::Tag("CFF "), |
154 | | OpenType::Tag("VORG"), |
155 | | OpenType::Tag("EBDT"), |
156 | | OpenType::Tag("EBLC"), |
157 | | OpenType::Tag("gasp"), |
158 | | OpenType::Tag("hdmx"), |
159 | | OpenType::Tag("kern"), |
160 | | OpenType::Tag("LTSH"), |
161 | | OpenType::Tag("PCLT"), |
162 | | OpenType::Tag("VDMX"), |
163 | | OpenType::Tag("vhea"), |
164 | | OpenType::Tag("vmtx"), |
165 | | OpenType::Tag("BASE"), |
166 | | OpenType::Tag("GDEF"), |
167 | | OpenType::Tag("GPOS"), |
168 | | OpenType::Tag("GSUB"), |
169 | | OpenType::Tag("EBSC"), |
170 | | OpenType::Tag("JSTF"), |
171 | | OpenType::Tag("MATH"), |
172 | | OpenType::Tag("CBDT"), |
173 | | OpenType::Tag("CBLC"), |
174 | | OpenType::Tag("COLR"), |
175 | | OpenType::Tag("CPAL"), |
176 | | OpenType::Tag("SVG "), |
177 | | OpenType::Tag("sbix"), |
178 | | OpenType::Tag("acnt"), |
179 | | OpenType::Tag("avar"), |
180 | | OpenType::Tag("bdat"), |
181 | | OpenType::Tag("bloc"), |
182 | | OpenType::Tag("bsln"), |
183 | | OpenType::Tag("cvar"), |
184 | | OpenType::Tag("fdsc"), |
185 | | OpenType::Tag("feat"), |
186 | | OpenType::Tag("fmtx"), |
187 | | OpenType::Tag("fvar"), |
188 | | OpenType::Tag("gvar"), |
189 | | OpenType::Tag("hsty"), |
190 | | OpenType::Tag("just"), |
191 | | OpenType::Tag("lcar"), |
192 | | OpenType::Tag("mort"), |
193 | | OpenType::Tag("morx"), |
194 | | OpenType::Tag("opbd"), |
195 | | OpenType::Tag("prop"), |
196 | | OpenType::Tag("trak"), |
197 | | OpenType::Tag("Zapf"), |
198 | | OpenType::Tag("Silf"), |
199 | | OpenType::Tag("Glat"), |
200 | | OpenType::Tag("Gloc"), |
201 | | OpenType::Tag("Feat"), |
202 | | OpenType::Tag("Sill"), |
203 | | }; |
204 | | |
205 | | struct CoordinateTripletEncoding { |
206 | | u8 byte_count { 0 }; |
207 | | u8 x_bits { 0 }; |
208 | | u8 y_bits { 0 }; |
209 | | Optional<u16> delta_x; |
210 | | Optional<u16> delta_y; |
211 | | Optional<bool> positive_x; |
212 | | Optional<bool> positive_y; |
213 | | }; |
214 | | |
215 | | // https://www.w3.org/TR/WOFF2/#triplet_decoding |
216 | | // 5.2. Decoding of variable-length X and Y coordinates |
217 | | static CoordinateTripletEncoding const coordinate_triplet_encodings[128] = { |
218 | | { 2, 0, 8, {}, 0, {}, false }, // 0 |
219 | | { 2, 0, 8, {}, 0, {}, true }, // 1 |
220 | | { 2, 0, 8, {}, 256, {}, false }, // 2 |
221 | | { 2, 0, 8, {}, 256, {}, true }, // 3 |
222 | | { 2, 0, 8, {}, 512, {}, false }, // 4 |
223 | | { 2, 0, 8, {}, 512, {}, true }, // 5 |
224 | | { 2, 0, 8, {}, 768, {}, false }, // 6 |
225 | | { 2, 0, 8, {}, 768, {}, true }, // 7 |
226 | | { 2, 0, 8, {}, 1024, {}, false }, // 8 |
227 | | { 2, 0, 8, {}, 1024, {}, true }, // 9 |
228 | | { 2, 8, 0, 0, {}, false, {} }, // 10 |
229 | | { 2, 8, 0, 0, {}, true, {} }, // 11 |
230 | | { 2, 8, 0, 256, {}, false, {} }, // 12 |
231 | | { 2, 8, 0, 256, {}, true, {} }, // 13 |
232 | | { 2, 8, 0, 512, {}, false, {} }, // 14 |
233 | | { 2, 8, 0, 512, {}, true, {} }, // 15 |
234 | | { 2, 8, 0, 768, {}, false, {} }, // 16 |
235 | | { 2, 8, 0, 768, {}, true, {} }, // 17 |
236 | | { 2, 8, 0, 1024, {}, false, {} }, // 18 |
237 | | { 2, 8, 0, 1024, {}, true, {} }, // 19 |
238 | | { 2, 4, 4, 1, 1, false, false }, // 20 |
239 | | { 2, 4, 4, 1, 1, true, false }, // 21 |
240 | | { 2, 4, 4, 1, 1, false, true }, // 22 |
241 | | { 2, 4, 4, 1, 1, true, true }, // 23 |
242 | | { 2, 4, 4, 1, 17, false, false }, // 24 |
243 | | { 2, 4, 4, 1, 17, true, false }, // 25 |
244 | | { 2, 4, 4, 1, 17, false, true }, // 26 |
245 | | { 2, 4, 4, 1, 17, true, true }, // 27 |
246 | | { 2, 4, 4, 1, 33, false, false }, // 28 |
247 | | { 2, 4, 4, 1, 33, true, false }, // 29 |
248 | | { 2, 4, 4, 1, 33, false, true }, // 30 |
249 | | { 2, 4, 4, 1, 33, true, true }, // 31 |
250 | | { 2, 4, 4, 1, 49, false, false }, // 32 |
251 | | { 2, 4, 4, 1, 49, true, false }, // 33 |
252 | | { 2, 4, 4, 1, 49, false, true }, // 34 |
253 | | { 2, 4, 4, 1, 49, true, true }, // 35 |
254 | | { 2, 4, 4, 17, 1, false, false }, // 36 |
255 | | { 2, 4, 4, 17, 1, true, false }, // 37 |
256 | | { 2, 4, 4, 17, 1, false, true }, // 38 |
257 | | { 2, 4, 4, 17, 1, true, true }, // 39 |
258 | | { 2, 4, 4, 17, 17, false, false }, // 40 |
259 | | { 2, 4, 4, 17, 17, true, false }, // 41 |
260 | | { 2, 4, 4, 17, 17, false, true }, // 42 |
261 | | { 2, 4, 4, 17, 17, true, true }, // 43 |
262 | | { 2, 4, 4, 17, 33, false, false }, // 44 |
263 | | { 2, 4, 4, 17, 33, true, false }, // 45 |
264 | | { 2, 4, 4, 17, 33, false, true }, // 46 |
265 | | { 2, 4, 4, 17, 33, true, true }, // 47 |
266 | | { 2, 4, 4, 17, 49, false, false }, // 48 |
267 | | { 2, 4, 4, 17, 49, true, false }, // 49 |
268 | | { 2, 4, 4, 17, 49, false, true }, // 50 |
269 | | { 2, 4, 4, 17, 49, true, true }, // 51 |
270 | | { 2, 4, 4, 33, 1, false, false }, // 52 |
271 | | { 2, 4, 4, 33, 1, true, false }, // 53 |
272 | | { 2, 4, 4, 33, 1, false, true }, // 54 |
273 | | { 2, 4, 4, 33, 1, true, true }, // 55 |
274 | | { 2, 4, 4, 33, 17, false, false }, // 56 |
275 | | { 2, 4, 4, 33, 17, true, false }, // 57 |
276 | | { 2, 4, 4, 33, 17, false, true }, // 58 |
277 | | { 2, 4, 4, 33, 17, true, true }, // 59 |
278 | | { 2, 4, 4, 33, 33, false, false }, // 60 |
279 | | { 2, 4, 4, 33, 33, true, false }, // 61 |
280 | | { 2, 4, 4, 33, 33, false, true }, // 62 |
281 | | { 2, 4, 4, 33, 33, true, true }, // 63 |
282 | | { 2, 4, 4, 33, 49, false, false }, // 64 |
283 | | { 2, 4, 4, 33, 49, true, false }, // 65 |
284 | | { 2, 4, 4, 33, 49, false, true }, // 66 |
285 | | { 2, 4, 4, 33, 49, true, true }, // 67 |
286 | | { 2, 4, 4, 49, 1, false, false }, // 68 |
287 | | { 2, 4, 4, 49, 1, true, false }, // 69 |
288 | | { 2, 4, 4, 49, 1, false, true }, // 70 |
289 | | { 2, 4, 4, 49, 1, true, true }, // 71 |
290 | | { 2, 4, 4, 49, 17, false, false }, // 72 |
291 | | { 2, 4, 4, 49, 17, true, false }, // 73 |
292 | | { 2, 4, 4, 49, 17, false, true }, // 74 |
293 | | { 2, 4, 4, 49, 17, true, true }, // 75 |
294 | | { 2, 4, 4, 49, 33, false, false }, // 76 |
295 | | { 2, 4, 4, 49, 33, true, false }, // 77 |
296 | | { 2, 4, 4, 49, 33, false, true }, // 78 |
297 | | { 2, 4, 4, 49, 33, true, true }, // 79 |
298 | | { 2, 4, 4, 49, 49, false, false }, // 80 |
299 | | { 2, 4, 4, 49, 49, true, false }, // 81 |
300 | | { 2, 4, 4, 49, 49, false, true }, // 82 |
301 | | { 2, 4, 4, 49, 49, true, true }, // 83 |
302 | | { 3, 8, 8, 1, 1, false, false }, // 84 |
303 | | { 3, 8, 8, 1, 1, true, false }, // 85 |
304 | | { 3, 8, 8, 1, 1, false, true }, // 86 |
305 | | { 3, 8, 8, 1, 1, true, true }, // 87 |
306 | | { 3, 8, 8, 1, 257, false, false }, // 88 |
307 | | { 3, 8, 8, 1, 257, true, false }, // 89 |
308 | | { 3, 8, 8, 1, 257, false, true }, // 90 |
309 | | { 3, 8, 8, 1, 257, true, true }, // 91 |
310 | | { 3, 8, 8, 1, 513, false, false }, // 92 |
311 | | { 3, 8, 8, 1, 513, true, false }, // 93 |
312 | | { 3, 8, 8, 1, 513, false, true }, // 94 |
313 | | { 3, 8, 8, 1, 513, true, true }, // 95 |
314 | | { 3, 8, 8, 257, 1, false, false }, // 96 |
315 | | { 3, 8, 8, 257, 1, true, false }, // 97 |
316 | | { 3, 8, 8, 257, 1, false, true }, // 98 |
317 | | { 3, 8, 8, 257, 1, true, true }, // 99 |
318 | | { 3, 8, 8, 257, 257, false, false }, // 100 |
319 | | { 3, 8, 8, 257, 257, true, false }, // 101 |
320 | | { 3, 8, 8, 257, 257, false, true }, // 102 |
321 | | { 3, 8, 8, 257, 257, true, true }, // 103 |
322 | | { 3, 8, 8, 257, 513, false, false }, // 104 |
323 | | { 3, 8, 8, 257, 513, true, false }, // 105 |
324 | | { 3, 8, 8, 257, 513, false, true }, // 106 |
325 | | { 3, 8, 8, 257, 513, true, true }, // 107 |
326 | | { 3, 8, 8, 513, 1, false, false }, // 108 |
327 | | { 3, 8, 8, 513, 1, true, false }, // 109 |
328 | | { 3, 8, 8, 513, 1, false, true }, // 110 |
329 | | { 3, 8, 8, 513, 1, true, true }, // 111 |
330 | | { 3, 8, 8, 513, 257, false, false }, // 112 |
331 | | { 3, 8, 8, 513, 257, true, false }, // 113 |
332 | | { 3, 8, 8, 513, 257, false, true }, // 114 |
333 | | { 3, 8, 8, 513, 257, true, true }, // 115 |
334 | | { 3, 8, 8, 513, 513, false, false }, // 116 |
335 | | { 3, 8, 8, 513, 513, true, false }, // 117 |
336 | | { 3, 8, 8, 513, 513, false, true }, // 118 |
337 | | { 3, 8, 8, 513, 513, true, true }, // 119 |
338 | | { 4, 12, 12, 0, 0, false, false }, // 120 |
339 | | { 4, 12, 12, 0, 0, true, false }, // 121 |
340 | | { 4, 12, 12, 0, 0, false, true }, // 122 |
341 | | { 4, 12, 12, 0, 0, true, true }, // 123 |
342 | | { 5, 16, 16, 0, 0, false, false }, // 124 |
343 | | { 5, 16, 16, 0, 0, true, false }, // 125 |
344 | | { 5, 16, 16, 0, 0, false, true }, // 126 |
345 | | { 5, 16, 16, 0, 0, true, true }, // 127 |
346 | | }; |
347 | | |
348 | | struct FontPoint { |
349 | | i16 x { 0 }; |
350 | | i16 y { 0 }; |
351 | | bool on_curve { false }; |
352 | | }; |
353 | | |
354 | | static ErrorOr<Vector<FontPoint>> retrieve_points_of_simple_glyph(FixedMemoryStream& flags_stream, FixedMemoryStream& glyph_stream, u16 number_of_points) |
355 | 67.9k | { |
356 | 67.9k | Vector<FontPoint> points; |
357 | 67.9k | TRY(points.try_ensure_capacity(number_of_points)); |
358 | | |
359 | 0 | i16 x = 0; |
360 | 67.9k | i16 y = 0; |
361 | | |
362 | 3.78M | for (u32 point = 0; point < number_of_points; ++point) { |
363 | 3.72M | u8 flags = TRY(flags_stream.read_value<u8>()); |
364 | 0 | bool on_curve = (flags & 0x80) == 0; |
365 | 3.72M | u8 coordinate_triplet_index = flags & 0x7F; |
366 | | |
367 | 3.72M | auto const& coordinate_triplet_encoding = coordinate_triplet_encodings[coordinate_triplet_index]; |
368 | | |
369 | | // The byte_count in the array accounts for the flags, but we already read them in from a different stream. |
370 | 3.72M | u8 const byte_count_not_including_flags = coordinate_triplet_encoding.byte_count - 1; |
371 | | |
372 | 3.72M | u8 point_coordinates_buffer[4]; |
373 | 3.72M | Bytes point_coordinates { point_coordinates_buffer, byte_count_not_including_flags }; |
374 | 3.72M | TRY(glyph_stream.read_until_filled(point_coordinates)); |
375 | | |
376 | 0 | int delta_x = 0; |
377 | 3.72M | int delta_y = 0; |
378 | | |
379 | 3.72M | switch (coordinate_triplet_encoding.x_bits) { |
380 | 2.11M | case 0: |
381 | 2.11M | break; |
382 | 588k | case 4: |
383 | 588k | delta_x = static_cast<i16>(point_coordinates[0] >> 4); |
384 | 588k | break; |
385 | 970k | case 8: |
386 | 970k | delta_x = static_cast<i16>(point_coordinates[0]); |
387 | 970k | break; |
388 | 37.4k | case 12: |
389 | 37.4k | delta_x = (static_cast<i16>(point_coordinates[0]) << 4) | (static_cast<i16>(point_coordinates[1]) >> 4); |
390 | 37.4k | break; |
391 | 10.3k | case 16: |
392 | 10.3k | delta_x = be_i16(point_coordinates.data()); |
393 | 10.3k | break; |
394 | 0 | default: |
395 | 0 | VERIFY_NOT_REACHED(); |
396 | 3.72M | } |
397 | | |
398 | 3.72M | switch (coordinate_triplet_encoding.y_bits) { |
399 | 415k | case 0: |
400 | 415k | break; |
401 | 588k | case 4: |
402 | 588k | delta_y = static_cast<i16>(point_coordinates[0] & 0x0f); |
403 | 588k | break; |
404 | 2.66M | case 8: |
405 | 2.66M | delta_y = byte_count_not_including_flags == 2 ? static_cast<i16>(point_coordinates[1]) : static_cast<i16>(point_coordinates[0]); |
406 | 2.66M | break; |
407 | 37.4k | case 12: |
408 | 37.4k | delta_y = (static_cast<i16>(point_coordinates[1] & 0x0f) << 8) | static_cast<i16>(point_coordinates[2]); |
409 | 37.4k | break; |
410 | 10.3k | case 16: |
411 | 10.3k | delta_y = be_i16(point_coordinates.offset(2)); |
412 | 10.3k | break; |
413 | 0 | default: |
414 | 0 | VERIFY_NOT_REACHED(); |
415 | 3.72M | } |
416 | | |
417 | 3.72M | if (coordinate_triplet_encoding.delta_x.has_value()) { |
418 | 1.60M | if (Checked<i16>::addition_would_overflow(delta_x, coordinate_triplet_encoding.delta_x.value())) |
419 | 0 | return Error::from_string_literal("EOVERFLOW 3"); |
420 | | |
421 | 1.60M | delta_x += coordinate_triplet_encoding.delta_x.value(); |
422 | 1.60M | } |
423 | | |
424 | 3.72M | if (coordinate_triplet_encoding.delta_y.has_value()) { |
425 | 3.30M | if (Checked<i16>::addition_would_overflow(delta_y, coordinate_triplet_encoding.delta_y.value())) |
426 | 0 | return Error::from_string_literal("EOVERFLOW 4"); |
427 | | |
428 | 3.30M | delta_y += coordinate_triplet_encoding.delta_y.value(); |
429 | 3.30M | } |
430 | | |
431 | 3.72M | if (coordinate_triplet_encoding.positive_x.has_value() && !coordinate_triplet_encoding.positive_x.value()) |
432 | 808k | delta_x = -delta_x; |
433 | | |
434 | 3.72M | if (coordinate_triplet_encoding.positive_y.has_value() && !coordinate_triplet_encoding.positive_y.value()) |
435 | 2.43M | delta_y = -delta_y; |
436 | | |
437 | 3.72M | if (Checked<i16>::addition_would_overflow(x, delta_x)) |
438 | 36 | return Error::from_string_literal("EOVERFLOW 5"); |
439 | | |
440 | 3.72M | if (Checked<i16>::addition_would_overflow(y, delta_y)) |
441 | 32 | return Error::from_string_literal("EOVERFLOW 6"); |
442 | | |
443 | 3.72M | x += delta_x; |
444 | 3.72M | y += delta_y; |
445 | | |
446 | 3.72M | points.unchecked_append(FontPoint { .x = x, .y = y, .on_curve = on_curve }); |
447 | 3.72M | } |
448 | | |
449 | 67.8k | return points; |
450 | 67.9k | } |
451 | | |
452 | | // https://www.w3.org/TR/WOFF2/#glyf_table_format |
453 | | struct [[gnu::packed]] TransformedGlyfTable { |
454 | | BigEndian<u16> reserved; // = 0x0000 |
455 | | BigEndian<u16> option_flags; // Bit 0: if set, indicates the presence of the overlapSimpleBitmap[] bit array. |
456 | | // Bits 1-15: Reserved. |
457 | | BigEndian<u16> num_glyphs; // Number of glyphs |
458 | | BigEndian<u16> index_format; // Offset format for loca table, should be consistent with indexToLocFormat of the |
459 | | // original head table (see [OFF] specification) |
460 | | BigEndian<u32> n_contour_stream_size; // Size of nContour stream in bytes |
461 | | BigEndian<u32> n_points_stream_size; // Size of nPoints stream in bytes |
462 | | BigEndian<u32> flag_stream_size; // Size of flag stream in bytes |
463 | | BigEndian<u32> glyph_stream_size; // Size of glyph stream in bytes (a stream of variable-length encoded values, see |
464 | | // description below) |
465 | | BigEndian<u32> composite_stream_size; // Size of composite stream in bytes (a stream of variable-length encoded values, |
466 | | // see description below) |
467 | | BigEndian<u32> bbox_stream_size; // Size of bbox data in bytes representing combined length of bboxBitmap |
468 | | // (a packed bit array) and bboxStream (a stream of Int16 values) |
469 | | BigEndian<u32> instruction_stream_size; // Size of instruction stream (a stream of UInt8 values) |
470 | | |
471 | | // Other fields are variable-length, and so are not represented in this struct: |
472 | | // Int16 nContourStream[] Stream of Int16 values representing number of contours for each glyph record |
473 | | // 255UInt16 nPointsStream[] Stream of values representing number of outline points for each contour in glyph records |
474 | | // UInt8 flagStream[] Stream of UInt8 values representing flag values for each outline point. |
475 | | // Vary glyphStream[] Stream of bytes representing point coordinate values using variable length encoding |
476 | | // format (defined in subclause 5.2) |
477 | | // Vary compositeStream[] Stream of bytes representing component flag values and associated composite glyph data |
478 | | // UInt8 bboxBitmap[] Bitmap (a numGlyphs-long bit array) indicating explicit bounding boxes |
479 | | // Int16 bboxStream[] Stream of Int16 values representing glyph bounding box data |
480 | | // UInt8 instructionStream[] Stream of UInt8 values representing a set of instructions for each corresponding glyph |
481 | | // UInt8 overlapSimpleBitmap[] A numGlyphs-long bit array that provides values for the overlap flag [bit 6] for each |
482 | | // simple glyph. (Flag values for composite glyphs are already encoded as part of the |
483 | | // compositeStream[]). |
484 | | }; |
485 | | static_assert(AssertSize<TransformedGlyfTable, 36>()); |
486 | | |
487 | | } |
488 | | |
489 | | template<> |
490 | | class AK::Traits<WOFF2::TransformedGlyfTable> : public DefaultTraits<WOFF2::TransformedGlyfTable> { |
491 | | public: |
492 | 0 | static constexpr bool is_trivially_serializable() { return true; } |
493 | | }; |
494 | | |
495 | | namespace WOFF2 { |
496 | | |
497 | | enum class LocaElementSize { |
498 | | TwoBytes, |
499 | | FourBytes, |
500 | | }; |
501 | | |
502 | | struct GlyfAndLocaTableBuffers { |
503 | | ByteBuffer glyf_table; |
504 | | ByteBuffer loca_table; |
505 | | }; |
506 | | |
507 | | enum SimpleGlyphFlags : u8 { |
508 | | OnCurve = 0x01, |
509 | | XShortVector = 0x02, |
510 | | YShortVector = 0x04, |
511 | | RepeatFlag = 0x08, |
512 | | XIsSameOrPositiveXShortVector = 0x10, |
513 | | YIsSameOrPositiveYShortVector = 0x20, |
514 | | }; |
515 | | |
516 | | static ErrorOr<GlyfAndLocaTableBuffers> create_glyf_and_loca_tables_from_transformed_glyf_table(FixedMemoryStream& table_stream) |
517 | 995 | { |
518 | 995 | auto header = TRY(table_stream.read_value<TransformedGlyfTable>()); |
519 | | |
520 | 993 | auto loca_element_size = header.index_format == 0 ? LocaElementSize::TwoBytes : LocaElementSize::FourBytes; |
521 | | |
522 | 993 | size_t table_size = TRY(table_stream.size()); |
523 | 0 | u64 total_size_of_streams = header.n_contour_stream_size; |
524 | 993 | total_size_of_streams += header.n_points_stream_size; |
525 | 993 | total_size_of_streams += header.flag_stream_size; |
526 | 993 | total_size_of_streams += header.glyph_stream_size; |
527 | 993 | total_size_of_streams += header.composite_stream_size; |
528 | 993 | total_size_of_streams += header.bbox_stream_size; |
529 | 993 | total_size_of_streams += header.instruction_stream_size; |
530 | | |
531 | 993 | if (table_size < total_size_of_streams) |
532 | 40 | return Error::from_string_literal("Not enough data to read in streams of transformed glyf table"); |
533 | | |
534 | 953 | auto number_of_contours_stream = FixedMemoryStream(TRY(table_stream.read_in_place<u8>(header.n_contour_stream_size))); |
535 | 952 | auto number_of_points_stream = FixedMemoryStream(TRY(table_stream.read_in_place<u8>(header.n_points_stream_size))); |
536 | 951 | auto flag_stream = FixedMemoryStream(TRY(table_stream.read_in_place<u8>(header.flag_stream_size))); |
537 | 950 | auto glyph_stream = FixedMemoryStream(TRY(table_stream.read_in_place<u8>(header.glyph_stream_size))); |
538 | 949 | auto composite_stream = FixedMemoryStream(TRY(table_stream.read_in_place<u8>(header.composite_stream_size))); |
539 | | |
540 | 0 | size_t bounding_box_bitmap_length = ((header.num_glyphs + 31) >> 5) << 2; |
541 | 948 | auto bounding_box_bitmap_memory_stream = FixedMemoryStream(TRY(table_stream.read_in_place<u8>(bounding_box_bitmap_length))); |
542 | 0 | auto bounding_box_bitmap_bit_stream = BigEndianInputBitStream { MaybeOwned<Stream>(bounding_box_bitmap_memory_stream) }; |
543 | | |
544 | 945 | if (header.bbox_stream_size < bounding_box_bitmap_length) |
545 | 13 | return Error::from_string_literal("Not enough data to read bounding box stream of transformed glyf table"); |
546 | 932 | auto bounding_box_stream = FixedMemoryStream(TRY(table_stream.read_in_place<u8>(header.bbox_stream_size - bounding_box_bitmap_length))); |
547 | | |
548 | 930 | auto instruction_stream = FixedMemoryStream(TRY(table_stream.read_in_place<u8>(header.instruction_stream_size))); |
549 | | |
550 | 0 | ByteBuffer reconstructed_glyf_table; |
551 | 927 | Vector<u32> loca_indexes; |
552 | | |
553 | 233k | auto append_u16 = [&](BigEndian<u16> value) -> ErrorOr<void> { |
554 | 233k | return reconstructed_glyf_table.try_append(&value, sizeof(value)); |
555 | 233k | }; |
556 | | |
557 | 984k | auto append_i16 = [&](BigEndian<i16> value) -> ErrorOr<void> { |
558 | 984k | return reconstructed_glyf_table.try_append(&value, sizeof(value)); |
559 | 984k | }; |
560 | | |
561 | 5.47k | auto append_bytes = [&](ReadonlyBytes bytes) -> ErrorOr<void> { |
562 | 5.47k | return reconstructed_glyf_table.try_append(bytes); |
563 | 5.47k | }; |
564 | | |
565 | 91.3k | for (size_t glyph_index = 0; glyph_index < header.num_glyphs; ++glyph_index) { |
566 | 90.6k | size_t starting_glyf_table_size = reconstructed_glyf_table.size(); |
567 | | |
568 | 90.6k | bool has_bounding_box = TRY(bounding_box_bitmap_bit_stream.read_bit()); |
569 | | |
570 | 90.6k | auto number_of_contours = TRY(number_of_contours_stream.read_value<BigEndian<i16>>()); |
571 | | |
572 | 90.6k | if (number_of_contours == 0) { |
573 | | // Empty glyph |
574 | | |
575 | | // Reconstruction of an empty glyph (when nContour = 0) is a simple step |
576 | | // that involves incrementing the glyph record count and creating a new entry in the loca table |
577 | | // where loca[n] = loca[n-1]. |
578 | | |
579 | | // If the bboxBitmap flag indicates that the bounding box values are explicitly encoded in the bboxStream |
580 | | // the decoder MUST reject WOFF2 file as invalid. |
581 | 20.8k | if (has_bounding_box) |
582 | 6 | return Error::from_string_literal("Empty glyphs cannot have an explicit bounding box"); |
583 | 69.8k | } else if (number_of_contours < 0) { |
584 | | // Decoding of Composite Glyphs |
585 | | |
586 | 1.81k | [[maybe_unused]] i16 bounding_box_x_min = 0; |
587 | 1.81k | [[maybe_unused]] i16 bounding_box_y_min = 0; |
588 | 1.81k | [[maybe_unused]] i16 bounding_box_x_max = 0; |
589 | 1.81k | [[maybe_unused]] i16 bounding_box_y_max = 0; |
590 | | |
591 | 1.81k | if (has_bounding_box) { |
592 | 456 | bounding_box_x_min = TRY(bounding_box_stream.read_value<BigEndian<i16>>()); |
593 | 451 | bounding_box_y_min = TRY(bounding_box_stream.read_value<BigEndian<i16>>()); |
594 | 449 | bounding_box_x_max = TRY(bounding_box_stream.read_value<BigEndian<i16>>()); |
595 | 448 | bounding_box_y_max = TRY(bounding_box_stream.read_value<BigEndian<i16>>()); |
596 | 446 | } |
597 | | |
598 | 3.60k | TRY(append_i16(number_of_contours)); |
599 | 1.80k | TRY(append_i16(bounding_box_x_min)); |
600 | 1.80k | TRY(append_i16(bounding_box_y_min)); |
601 | 1.80k | TRY(append_i16(bounding_box_x_max)); |
602 | 1.80k | TRY(append_i16(bounding_box_y_max)); |
603 | | |
604 | 0 | bool have_instructions = false; |
605 | 1.80k | u16 flags = to_underlying(OpenType::Glyf::CompositeFlags::MoreComponents); |
606 | 5.21k | while (flags & to_underlying(OpenType::Glyf::CompositeFlags::MoreComponents)) { |
607 | | // 1a. Read a UInt16 from compositeStream. This is interpreted as a component flag word as in the TrueType spec. |
608 | | // Based on the flag values, there are between 4 and 14 additional argument bytes, |
609 | | // interpreted as glyph index, arg1, arg2, and optional scale or affine matrix. |
610 | | |
611 | 3.44k | flags = TRY(composite_stream.read_value<BigEndian<u16>>()); |
612 | | |
613 | 3.43k | if (flags & to_underlying(OpenType::Glyf::CompositeFlags::WeHaveInstructions)) { |
614 | 1.13k | have_instructions = true; |
615 | 1.13k | } |
616 | | |
617 | | // 2a. Read the number of argument bytes as determined in step 1a from the composite stream, |
618 | | // and store these in the reconstructed glyph. |
619 | | // If the flag word read in step 1a has the FLAG_MORE_COMPONENTS bit (bit 5) set, go back to step 1a. |
620 | | |
621 | 3.43k | size_t argument_byte_count = 2; |
622 | | |
623 | 3.43k | if (flags & to_underlying(OpenType::Glyf::CompositeFlags::Arg1AndArg2AreWords)) { |
624 | 981 | argument_byte_count += 4; |
625 | 2.45k | } else { |
626 | 2.45k | argument_byte_count += 2; |
627 | 2.45k | } |
628 | | |
629 | 3.43k | if (flags & to_underlying(OpenType::Glyf::CompositeFlags::WeHaveAScale)) { |
630 | 811 | argument_byte_count += 2; |
631 | 2.62k | } else if (flags & to_underlying(OpenType::Glyf::CompositeFlags::WeHaveAnXAndYScale)) { |
632 | 305 | argument_byte_count += 4; |
633 | 2.32k | } else if (flags & to_underlying(OpenType::Glyf::CompositeFlags::WeHaveATwoByTwo)) { |
634 | 311 | argument_byte_count += 8; |
635 | 311 | } |
636 | | |
637 | 3.43k | TRY(append_u16(flags)); |
638 | 6.83k | TRY(reconstructed_glyf_table.try_append(TRY(composite_stream.read_in_place<u8>(argument_byte_count)))); |
639 | 6.83k | } |
640 | | |
641 | 1.77k | if (have_instructions) { |
642 | 417 | auto number_of_instructions = TRY(read_255_u_short(glyph_stream)); |
643 | 415 | TRY(append_u16(number_of_instructions)); |
644 | | |
645 | 415 | if (number_of_instructions) |
646 | 240 | TRY(reconstructed_glyf_table.try_append(TRY(instruction_stream.read_in_place<u8>(number_of_instructions)))); |
647 | 415 | } |
648 | 68.0k | } else if (number_of_contours > 0) { |
649 | | // Decoding of Simple Glyphs |
650 | | |
651 | | // For a simple glyph (when nContour > 0), the process continues as follows: |
652 | | // Each of these is the number of points of that contour. |
653 | | // Convert this into the endPtsOfContours[] array by computing the cumulative sum, then subtracting one. |
654 | | |
655 | 68.0k | Vector<size_t> end_points_of_contours; |
656 | 68.0k | size_t number_of_points = 0; |
657 | | |
658 | 284k | for (size_t contour_index = 0; contour_index < static_cast<size_t>(number_of_contours); ++contour_index) { |
659 | 216k | size_t number_of_points_for_this_contour = TRY(read_255_u_short(number_of_points_stream)); |
660 | 216k | if (Checked<size_t>::addition_would_overflow(number_of_points, number_of_points_for_this_contour)) |
661 | 0 | return Error::from_string_literal("EOVERFLOW 1"); |
662 | | |
663 | 216k | number_of_points += number_of_points_for_this_contour; |
664 | 216k | if (number_of_points == 0) |
665 | 16 | return Error::from_string_literal("EOVERFLOW 2"); |
666 | | |
667 | 432k | TRY(end_points_of_contours.try_append(number_of_points - 1)); |
668 | 432k | } |
669 | | |
670 | 67.9k | auto points = TRY(retrieve_points_of_simple_glyph(flag_stream, glyph_stream, number_of_points)); |
671 | | |
672 | 67.8k | auto instruction_size = TRY(read_255_u_short(glyph_stream)); |
673 | 67.8k | auto instructions_buffer = TRY(ByteBuffer::create_zeroed(instruction_size)); |
674 | 67.8k | if (instruction_size != 0) |
675 | 5.50k | TRY(instruction_stream.read_until_filled(instructions_buffer)); |
676 | | |
677 | 67.7k | i16 bounding_box_x_min = 0; |
678 | 67.7k | i16 bounding_box_y_min = 0; |
679 | 67.7k | i16 bounding_box_x_max = 0; |
680 | 67.7k | i16 bounding_box_y_max = 0; |
681 | | |
682 | 67.7k | if (has_bounding_box) { |
683 | 579 | bounding_box_x_min = TRY(bounding_box_stream.read_value<BigEndian<i16>>()); |
684 | 570 | bounding_box_y_min = TRY(bounding_box_stream.read_value<BigEndian<i16>>()); |
685 | 569 | bounding_box_x_max = TRY(bounding_box_stream.read_value<BigEndian<i16>>()); |
686 | 568 | bounding_box_y_max = TRY(bounding_box_stream.read_value<BigEndian<i16>>()); |
687 | 67.2k | } else { |
688 | 3.76M | for (size_t point_index = 0; point_index < points.size(); ++point_index) { |
689 | 3.69M | auto& point = points.at(point_index); |
690 | | |
691 | 3.69M | if (point_index == 0) { |
692 | 67.2k | bounding_box_x_min = bounding_box_x_max = point.x; |
693 | 67.2k | bounding_box_y_min = bounding_box_y_max = point.y; |
694 | 67.2k | continue; |
695 | 67.2k | } |
696 | | |
697 | 3.62M | bounding_box_x_min = min(bounding_box_x_min, point.x); |
698 | 3.62M | bounding_box_x_max = max(bounding_box_x_max, point.x); |
699 | 3.62M | bounding_box_y_min = min(bounding_box_y_min, point.y); |
700 | 3.62M | bounding_box_y_max = max(bounding_box_y_max, point.y); |
701 | 3.62M | } |
702 | 67.2k | } |
703 | | |
704 | 135k | TRY(append_i16(number_of_contours)); |
705 | 67.7k | TRY(append_i16(bounding_box_x_min)); |
706 | 67.7k | TRY(append_i16(bounding_box_y_min)); |
707 | 67.7k | TRY(append_i16(bounding_box_x_max)); |
708 | 67.7k | TRY(append_i16(bounding_box_y_max)); |
709 | | |
710 | 0 | for (auto end_point : end_points_of_contours) |
711 | 162k | TRY(append_u16(end_point)); |
712 | | |
713 | 135k | TRY(append_u16(instruction_size)); |
714 | 67.7k | if (instruction_size != 0) |
715 | 5.47k | TRY(append_bytes(instructions_buffer)); |
716 | | |
717 | 67.7k | Vector<FontPoint> relative_points; |
718 | 67.7k | TRY(relative_points.try_ensure_capacity(points.size())); |
719 | | |
720 | 0 | { |
721 | 67.7k | i16 previous_point_x = 0; |
722 | 67.7k | i16 previous_point_y = 0; |
723 | 3.70M | for (auto& point : points) { |
724 | 3.70M | i16 x = point.x - previous_point_x; |
725 | 3.70M | i16 y = point.y - previous_point_y; |
726 | 3.70M | relative_points.unchecked_append({ x, y, point.on_curve }); |
727 | 3.70M | previous_point_x = point.x; |
728 | 3.70M | previous_point_y = point.y; |
729 | 3.70M | } |
730 | 67.7k | } |
731 | | |
732 | 67.7k | Optional<u8> last_flags; |
733 | 67.7k | u8 repeat_count = 0; |
734 | | |
735 | 3.70M | for (auto& point : relative_points) { |
736 | 3.70M | u8 flags = 0; |
737 | | |
738 | 3.70M | if (point.on_curve) |
739 | 3.04M | flags |= SimpleGlyphFlags::OnCurve; |
740 | | |
741 | 3.70M | if (point.x == 0) { |
742 | 2.27M | flags |= SimpleGlyphFlags::XIsSameOrPositiveXShortVector; |
743 | 2.27M | } else if (point.x > -256 && point.x < 256) { |
744 | 1.18M | flags |= SimpleGlyphFlags::XShortVector; |
745 | | |
746 | 1.18M | if (point.x > 0) |
747 | 588k | flags |= SimpleGlyphFlags::XIsSameOrPositiveXShortVector; |
748 | 1.18M | } |
749 | | |
750 | 3.70M | if (point.y == 0) { |
751 | 2.14M | flags |= SimpleGlyphFlags::YIsSameOrPositiveYShortVector; |
752 | 2.14M | } else if (point.y > -256 && point.y < 256) { |
753 | 1.17M | flags |= SimpleGlyphFlags::YShortVector; |
754 | | |
755 | 1.17M | if (point.y > 0) |
756 | 576k | flags |= SimpleGlyphFlags::YIsSameOrPositiveYShortVector; |
757 | 1.17M | } |
758 | | |
759 | 3.70M | if (last_flags.has_value() && flags == last_flags.value() && repeat_count != 0xff) { |
760 | | // NOTE: Update the previous entry to say it's repeating. |
761 | 1.60M | reconstructed_glyf_table[reconstructed_glyf_table.size() - 1] |= SimpleGlyphFlags::RepeatFlag; |
762 | 1.60M | ++repeat_count; |
763 | 2.10M | } else { |
764 | 2.10M | if (repeat_count != 0) { |
765 | 229k | TRY(reconstructed_glyf_table.try_append(repeat_count)); |
766 | 0 | repeat_count = 0; |
767 | 229k | } |
768 | 4.20M | TRY(reconstructed_glyf_table.try_append(flags)); |
769 | 4.20M | } |
770 | 3.70M | last_flags = flags; |
771 | 3.70M | } |
772 | 67.7k | if (repeat_count != 0) { |
773 | 25.8k | TRY(reconstructed_glyf_table.try_append(repeat_count)); |
774 | 25.8k | } |
775 | | |
776 | 3.70M | for (auto& point : relative_points) { |
777 | 3.70M | if (point.x == 0) { |
778 | | // No need to write to the table. |
779 | 2.27M | } else if (point.x > -256 && point.x < 256) { |
780 | 1.18M | TRY(reconstructed_glyf_table.try_append(abs(point.x))); |
781 | 1.18M | } else { |
782 | 252k | TRY(append_i16(point.x)); |
783 | 252k | } |
784 | 3.70M | } |
785 | | |
786 | 3.70M | for (auto& point : relative_points) { |
787 | 3.70M | if (point.y == 0) { |
788 | | // No need to write to the table. |
789 | 2.14M | } else if (point.y > -256 && point.y < 256) { |
790 | 1.17M | TRY(reconstructed_glyf_table.try_append(abs(point.y))); |
791 | 1.17M | } else { |
792 | 384k | TRY(append_i16(point.y)); |
793 | 384k | } |
794 | 3.70M | } |
795 | 67.7k | } |
796 | | |
797 | | // NOTE: Make sure each glyph starts on a 4-byte boundary. |
798 | | // I haven't found the spec text for this, but it matches other implementations. |
799 | 185k | while (reconstructed_glyf_table.size() % 4 != 0) { |
800 | 95.0k | TRY(reconstructed_glyf_table.try_append(0)); |
801 | 95.0k | } |
802 | | |
803 | 180k | TRY(loca_indexes.try_append(starting_glyf_table_size)); |
804 | 180k | } |
805 | | |
806 | 1.27k | TRY(loca_indexes.try_append(reconstructed_glyf_table.size())); |
807 | | |
808 | 636 | size_t loca_element_size_in_bytes = loca_element_size == LocaElementSize::TwoBytes ? sizeof(u16) : sizeof(u32); |
809 | 1.27k | size_t loca_table_buffer_size = loca_indexes.size() * loca_element_size_in_bytes; |
810 | 1.27k | ByteBuffer loca_table_buffer; |
811 | 1.27k | TRY(loca_table_buffer.try_ensure_capacity(loca_table_buffer_size)); |
812 | 62.6k | for (auto loca_index : loca_indexes) { |
813 | 62.6k | if (loca_element_size == LocaElementSize::TwoBytes) { |
814 | 24.7k | auto value = BigEndian<u16>(loca_index >> 1); |
815 | 24.7k | loca_table_buffer.append({ &value, sizeof(value) }); |
816 | 37.8k | } else { |
817 | 37.8k | auto value = BigEndian<u32>(loca_index); |
818 | 37.8k | loca_table_buffer.append({ &value, sizeof(value) }); |
819 | 37.8k | } |
820 | 62.6k | } |
821 | | |
822 | 636 | return GlyfAndLocaTableBuffers { .glyf_table = move(reconstructed_glyf_table), .loca_table = move(loca_table_buffer) }; |
823 | 1.27k | } |
824 | | |
825 | | ErrorOr<NonnullRefPtr<Font>> Font::try_load_from_resource(Core::Resource const& resource) |
826 | 0 | { |
827 | 0 | return try_load_from_externally_owned_memory(resource.data()); |
828 | 0 | } |
829 | | |
830 | | ErrorOr<NonnullRefPtr<Font>> Font::try_load_from_externally_owned_memory(ReadonlyBytes bytes) |
831 | 3.88k | { |
832 | 3.88k | FixedMemoryStream stream(bytes); |
833 | 3.88k | return try_load_from_externally_owned_memory(stream); |
834 | 3.88k | } |
835 | | |
836 | | ErrorOr<NonnullRefPtr<Font>> Font::try_load_from_externally_owned_memory(SeekableStream& stream) |
837 | 3.88k | { |
838 | 3.88k | auto header = TRY(stream.read_value<Header>()); |
839 | | |
840 | | // The signature field in the WOFF2 header MUST contain the value of 0x774F4632 ('wOF2'), which distinguishes it from WOFF 1.0 files. |
841 | | // If the field does not contain this value, user agents MUST reject the file as invalid. |
842 | 3.87k | if (header.signature != WOFF2_SIGNATURE) |
843 | 49 | return Error::from_string_literal("Invalid WOFF2 signature"); |
844 | | |
845 | | // The interpretation of the WOFF2 Header is the same as the WOFF Header in [WOFF1], with the addition of one new totalCompressedSize field. |
846 | | // NOTE: See WOFF/Font.cpp for more comments about this. |
847 | | |
848 | 3.82k | static constexpr size_t MAX_BUFFER_SIZE = 10 * MiB; |
849 | 3.82k | if (header.length > TRY(stream.size())) |
850 | 27 | return Error::from_string_literal("Invalid WOFF length"); |
851 | 3.80k | if (header.num_tables == 0 || header.num_tables > NumericLimits<u16>::max() / 16) |
852 | 11 | return Error::from_string_literal("Invalid WOFF numTables"); |
853 | 3.79k | if (header.total_compressed_size > MAX_BUFFER_SIZE) |
854 | 15 | return Error::from_string_literal("Compressed font is more than 10 MiB"); |
855 | 3.77k | if (header.meta_length == 0 && header.meta_offset != 0) |
856 | 47 | return Error::from_string_literal("Invalid WOFF meta block offset"); |
857 | 3.72k | if (header.priv_length == 0 && header.priv_offset != 0) |
858 | 41 | return Error::from_string_literal("Invalid WOFF private block offset"); |
859 | 3.68k | if (header.flavor == TTCF_SIGNAURE) |
860 | 2 | return Error::from_string_literal("Font collections not yet supported"); |
861 | | |
862 | | // NOTE: "The "totalSfntSize" value in the WOFF2 Header is intended to be used for reference purposes only. It may represent the size of the uncompressed input font file, |
863 | | // but if the transformed 'glyf' and 'loca' tables are present, the uncompressed size of the reconstructed tables and the total decompressed font size may differ |
864 | | // substantially from the original total size specified in the WOFF2 Header." |
865 | | // We use it as an initial size of the font buffer and extend it as necessary. |
866 | 3.68k | auto font_buffer_size = clamp(header.total_sfnt_size, sizeof(OpenType::TableDirectory) + header.num_tables * sizeof(TableDirectoryEntry), MAX_BUFFER_SIZE); |
867 | 3.68k | auto font_buffer = TRY(ByteBuffer::create_zeroed(font_buffer_size)); |
868 | | |
869 | 0 | u16 search_range = pow_2_less_than_or_equal(header.num_tables); |
870 | 3.68k | OpenType::TableDirectory table_directory { |
871 | 3.68k | .sfnt_version = header.flavor, |
872 | 3.68k | .num_tables = header.num_tables, |
873 | 3.68k | .search_range = search_range * 16, |
874 | 3.68k | .entry_selector = log2(search_range), |
875 | 3.68k | .range_shift = header.num_tables * 16 - search_range * 16, |
876 | 3.68k | }; |
877 | 3.68k | font_buffer.overwrite(0, &table_directory, sizeof(table_directory)); |
878 | | |
879 | 3.68k | Vector<TableDirectoryEntry> table_entries; |
880 | 3.68k | TRY(table_entries.try_ensure_capacity(header.num_tables)); |
881 | | |
882 | 0 | u64 total_length_of_all_tables = 0; |
883 | | |
884 | 67.1k | for (size_t table_entry_index = 0; table_entry_index < header.num_tables; ++table_entry_index) { |
885 | 63.8k | TableDirectoryEntry table_directory_entry; |
886 | 63.8k | u8 const flags_byte = TRY(stream.read_value<u8>()); |
887 | | |
888 | 0 | switch ((flags_byte & 0xC0) >> 6) { |
889 | 52.0k | case 0: |
890 | 52.0k | table_directory_entry.transformation_version = TransformationVersion::Version0; |
891 | 52.0k | break; |
892 | 7.46k | case 1: |
893 | 7.46k | table_directory_entry.transformation_version = TransformationVersion::Version1; |
894 | 7.46k | break; |
895 | 1.56k | case 2: |
896 | 1.56k | table_directory_entry.transformation_version = TransformationVersion::Version2; |
897 | 1.56k | break; |
898 | 2.59k | case 3: |
899 | 2.59k | table_directory_entry.transformation_version = TransformationVersion::Version3; |
900 | 2.59k | break; |
901 | 0 | default: |
902 | 0 | VERIFY_NOT_REACHED(); |
903 | 63.6k | } |
904 | | |
905 | 63.6k | u8 tag_number = flags_byte & 0x3F; |
906 | | |
907 | 63.6k | if (tag_number != 0x3F) { |
908 | 60.5k | table_directory_entry.tag = known_tag_names[tag_number]; |
909 | 60.5k | } else { |
910 | 3.08k | table_directory_entry.tag = TRY(stream.read_value<OpenType::Tag>()); |
911 | 3.07k | } |
912 | | |
913 | 63.6k | table_directory_entry.original_length = TRY(read_uint_base_128(stream)); |
914 | | |
915 | 0 | bool needs_to_read_transform_length = false; |
916 | 63.5k | if (table_directory_entry.tag == OpenType::Tag("glyf") || table_directory_entry.tag == OpenType::Tag("loca")) |
917 | 7.26k | needs_to_read_transform_length = table_directory_entry.transformation_version == TransformationVersion::Version0; |
918 | 56.2k | else |
919 | 56.2k | needs_to_read_transform_length = table_directory_entry.transformation_version != TransformationVersion::Version0; |
920 | | |
921 | 63.5k | if (needs_to_read_transform_length) { |
922 | 10.0k | u32 transform_length = TRY(read_uint_base_128(stream)); |
923 | 0 | table_directory_entry.transform_length = transform_length; |
924 | 9.95k | total_length_of_all_tables += transform_length; |
925 | 53.5k | } else { |
926 | 53.5k | total_length_of_all_tables += table_directory_entry.original_length; |
927 | 53.5k | } |
928 | | |
929 | 63.4k | table_entries.unchecked_append(move(table_directory_entry)); |
930 | 63.4k | } |
931 | | |
932 | | // FIXME: Read in collection header and entries. |
933 | | |
934 | 34.9k | auto glyf_table = table_entries.find_if([](TableDirectoryEntry const& entry) { |
935 | 34.9k | return entry.tag == OpenType::Tag("glyf"); |
936 | 34.9k | }); |
937 | | |
938 | 38.7k | auto loca_table = table_entries.find_if([](TableDirectoryEntry const& entry) { |
939 | 38.7k | return entry.tag == OpenType::Tag("loca"); |
940 | 38.7k | }); |
941 | | |
942 | | // "In other words, both glyf and loca tables must either be present in their transformed format or with null transform applied to both tables." |
943 | 3.30k | if (glyf_table.is_end() != loca_table.is_end()) |
944 | 14 | return Error::from_string_literal("Must have both 'loca' and 'glyf' tables if one of them is present"); |
945 | | |
946 | 3.29k | if (!glyf_table.is_end() && !loca_table.is_end()) { |
947 | 992 | if (glyf_table->transformation_version != loca_table->transformation_version) |
948 | 12 | return Error::from_string_literal("The 'loca' and 'glyf' tables must have the same transformation version"); |
949 | 992 | } |
950 | | |
951 | 3.28k | if (!loca_table.is_end()) { |
952 | 980 | if (loca_table->has_transformation() && loca_table->transform_length.value() != 0) |
953 | 64 | return Error::from_string_literal("Transformed 'loca' table must have a transform length of 0"); |
954 | 980 | } |
955 | | |
956 | 3.21k | auto compressed_bytes_read_buffer = TRY(ByteBuffer::create_zeroed(header.total_compressed_size)); |
957 | 3.21k | auto compressed_bytes = TRY(stream.read_some(compressed_bytes_read_buffer)); |
958 | 3.21k | if (compressed_bytes.size() != header.total_compressed_size) |
959 | 167 | return Error::from_string_literal("Not enough data to read in the reported size of the compressed data"); |
960 | | |
961 | 3.05k | auto compressed_stream = FixedMemoryStream(compressed_bytes); |
962 | 3.05k | auto brotli_stream = Compress::BrotliDecompressionStream { MaybeOwned<Stream>(compressed_stream) }; |
963 | 3.05k | auto decompressed_table_data = TRY(brotli_stream.read_until_eof()); |
964 | 1.36k | if (decompressed_table_data.size() != total_length_of_all_tables) |
965 | 114 | return Error::from_string_literal("Size of the decompressed data is not equal to the total of the reported lengths of each table"); |
966 | | |
967 | 1.24k | auto decompressed_data_stream = FixedMemoryStream(decompressed_table_data.bytes()); |
968 | 1.24k | size_t font_buffer_offset = SFNT_HEADER_SIZE + header.num_tables * SFNT_TABLE_SIZE; |
969 | 1.24k | Optional<GlyfAndLocaTableBuffers> glyf_and_loca_buffer; |
970 | 11.4k | for (size_t table_entry_index = 0; table_entry_index < header.num_tables; ++table_entry_index) { |
971 | 10.6k | auto& table_entry = table_entries.at(table_entry_index); |
972 | 10.6k | u32 length_to_read = table_entry.has_transformation() ? table_entry.transform_length.value() : table_entry.original_length; |
973 | | |
974 | 10.6k | auto table_buffer = TRY(ByteBuffer::create_zeroed(length_to_read)); |
975 | 10.6k | auto table_bytes = TRY(decompressed_data_stream.read_some(table_buffer)); |
976 | 10.6k | if (table_bytes.size() != length_to_read) |
977 | 0 | return Error::from_string_literal("Not enough data to read decompressed table"); |
978 | | |
979 | 10.6k | size_t table_directory_offset = SFNT_HEADER_SIZE + table_entry_index * SFNT_TABLE_SIZE; |
980 | | |
981 | 10.6k | if (table_entry.has_transformation()) { |
982 | 1.51k | if (table_entry.tag == OpenType::Tag("glyf")) { |
983 | 995 | auto table_stream = FixedMemoryStream(table_bytes); |
984 | 995 | glyf_and_loca_buffer = TRY(create_glyf_and_loca_tables_from_transformed_glyf_table(table_stream)); |
985 | | |
986 | 636 | if (font_buffer.size() < (font_buffer_offset + glyf_and_loca_buffer->glyf_table.size())) |
987 | 48 | TRY(font_buffer.try_resize(font_buffer_offset + glyf_and_loca_buffer->glyf_table.size())); |
988 | | |
989 | 636 | OpenType::TableRecord table_record { |
990 | 636 | .table_tag = table_entry.tag, |
991 | 636 | .checksum = 0, // FIXME: WOFF2 does not give us the original checksum. |
992 | 636 | .offset = font_buffer_offset, |
993 | 636 | .length = glyf_and_loca_buffer->glyf_table.size(), |
994 | 636 | }; |
995 | 636 | font_buffer.overwrite(table_directory_offset, &table_record, sizeof(table_record)); |
996 | | |
997 | 636 | font_buffer.overwrite(font_buffer_offset, glyf_and_loca_buffer->glyf_table.data(), glyf_and_loca_buffer->glyf_table.size()); |
998 | 636 | font_buffer_offset += glyf_and_loca_buffer->glyf_table.size(); |
999 | 636 | } else if (table_entry.tag == OpenType::Tag("loca")) { |
1000 | | // FIXME: Handle loca table coming before glyf table in input? |
1001 | 431 | VERIFY(glyf_and_loca_buffer.has_value()); |
1002 | 431 | if (font_buffer.size() < (font_buffer_offset + glyf_and_loca_buffer->loca_table.size())) |
1003 | 53 | TRY(font_buffer.try_resize(font_buffer_offset + glyf_and_loca_buffer->loca_table.size())); |
1004 | | |
1005 | 431 | OpenType::TableRecord table_record { |
1006 | 431 | .table_tag = table_entry.tag, |
1007 | 431 | .checksum = 0, // FIXME: WOFF2 does not give us the original checksum. |
1008 | 431 | .offset = font_buffer_offset, |
1009 | 431 | .length = glyf_and_loca_buffer->loca_table.size(), |
1010 | 431 | }; |
1011 | 431 | font_buffer.overwrite(table_directory_offset, &table_record, sizeof(table_record)); |
1012 | | |
1013 | 431 | font_buffer.overwrite(font_buffer_offset, glyf_and_loca_buffer->loca_table.data(), glyf_and_loca_buffer->loca_table.size()); |
1014 | 431 | font_buffer_offset += glyf_and_loca_buffer->loca_table.size(); |
1015 | 431 | } else if (table_entry.tag == OpenType::Tag("hmtx")) { |
1016 | 1 | return Error::from_string_literal("Decoding transformed hmtx table not yet supported"); |
1017 | 90 | } else { |
1018 | 90 | return Error::from_string_literal("Unknown transformation"); |
1019 | 90 | } |
1020 | 9.11k | } else { |
1021 | 9.11k | OpenType::TableRecord table_record { |
1022 | 9.11k | .table_tag = table_entry.tag, |
1023 | 9.11k | .checksum = 0, // FIXME: WOFF2 does not give us the original checksum. |
1024 | 9.11k | .offset = font_buffer_offset, |
1025 | 9.11k | .length = length_to_read, |
1026 | 9.11k | }; |
1027 | 9.11k | font_buffer.overwrite(table_directory_offset, &table_record, sizeof(table_record)); |
1028 | | |
1029 | 9.11k | if (font_buffer.size() < (font_buffer_offset + length_to_read)) |
1030 | 1.13k | TRY(font_buffer.try_resize(font_buffer_offset + length_to_read)); |
1031 | 9.11k | font_buffer.overwrite(font_buffer_offset, table_buffer.data(), length_to_read); |
1032 | | |
1033 | 9.11k | font_buffer_offset += length_to_read; |
1034 | 9.11k | } |
1035 | 10.6k | } |
1036 | | |
1037 | 796 | auto input_font = TRY(OpenType::Font::try_load_from_externally_owned_memory(font_buffer.bytes())); |
1038 | 0 | return adopt_ref(*new Font(input_font, move(font_buffer))); |
1039 | 796 | } |
1040 | | |
1041 | | } |