/src/woff2/src/woff2_dec.cc
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright 2014 Google Inc. All Rights Reserved. |
2 | | |
3 | | Distributed under MIT license. |
4 | | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT |
5 | | */ |
6 | | |
7 | | /* Library for converting WOFF2 format font files to their TTF versions. */ |
8 | | |
9 | | #include <woff2/decode.h> |
10 | | |
11 | | #include <stdlib.h> |
12 | | #include <algorithm> |
13 | | #include <complex> |
14 | | #include <cstring> |
15 | | #include <limits> |
16 | | #include <string> |
17 | | #include <vector> |
18 | | #include <map> |
19 | | #include <memory> |
20 | | #include <utility> |
21 | | |
22 | | #include <brotli/decode.h> |
23 | | #include "./buffer.h" |
24 | | #include "./port.h" |
25 | | #include "./round.h" |
26 | | #include "./store_bytes.h" |
27 | | #include "./table_tags.h" |
28 | | #include "./variable_length.h" |
29 | | #include "./woff2_common.h" |
30 | | |
31 | | namespace woff2 { |
32 | | |
33 | | namespace { |
34 | | |
35 | | // simple glyph flags |
36 | | const int kGlyfOnCurve = 1 << 0; |
37 | | const int kGlyfXShort = 1 << 1; |
38 | | const int kGlyfYShort = 1 << 2; |
39 | | const int kGlyfRepeat = 1 << 3; |
40 | | const int kGlyfThisXIsSame = 1 << 4; |
41 | | const int kGlyfThisYIsSame = 1 << 5; |
42 | | const int kOverlapSimple = 1 << 6; |
43 | | |
44 | | // composite glyph flags |
45 | | // See CompositeGlyph.java in sfntly for full definitions |
46 | | const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0; |
47 | | const int FLAG_WE_HAVE_A_SCALE = 1 << 3; |
48 | | const int FLAG_MORE_COMPONENTS = 1 << 5; |
49 | | const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6; |
50 | | const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7; |
51 | | const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; |
52 | | |
53 | | // glyf flags |
54 | | const int FLAG_OVERLAP_SIMPLE_BITMAP = 1 << 0; |
55 | | |
56 | | const size_t kCheckSumAdjustmentOffset = 8; |
57 | | |
58 | | const size_t kEndPtsOfContoursOffset = 10; |
59 | | const size_t kCompositeGlyphBegin = 10; |
60 | | |
61 | | // 98% of Google Fonts have no glyph above 5k bytes |
62 | | // Largest glyph ever observed was 72k bytes |
63 | | const size_t kDefaultGlyphBuf = 5120; |
64 | | |
65 | | // Over 14k test fonts the max compression ratio seen to date was ~20. |
66 | | // >100 suggests you wrote a bad uncompressed size. |
67 | | const float kMaxPlausibleCompressionRatio = 100.0; |
68 | | |
69 | | // metadata for a TTC font entry |
70 | | struct TtcFont { |
71 | | uint32_t flavor; |
72 | | uint32_t dst_offset; |
73 | | uint32_t header_checksum; |
74 | | std::vector<uint16_t> table_indices; |
75 | | }; |
76 | | |
77 | | struct WOFF2Header { |
78 | | uint32_t flavor; |
79 | | uint32_t header_version; |
80 | | uint16_t num_tables; |
81 | | uint64_t compressed_offset; |
82 | | uint32_t compressed_length; |
83 | | uint32_t uncompressed_size; |
84 | | std::vector<Table> tables; // num_tables unique tables |
85 | | std::vector<TtcFont> ttc_fonts; // metadata to help rebuild font |
86 | | }; |
87 | | |
88 | | /** |
89 | | * Accumulates data we may need to reconstruct a single font. One per font |
90 | | * created for a TTC. |
91 | | */ |
92 | | struct WOFF2FontInfo { |
93 | | uint16_t num_glyphs; |
94 | | uint16_t index_format; |
95 | | uint16_t num_hmetrics; |
96 | | std::vector<int16_t> x_mins; |
97 | | std::map<uint32_t, uint32_t> table_entry_by_tag; |
98 | | }; |
99 | | |
100 | | // Accumulates metadata as we rebuild the font |
101 | | struct RebuildMetadata { |
102 | | uint32_t header_checksum; // set by WriteHeaders |
103 | | std::vector<WOFF2FontInfo> font_infos; |
104 | | // checksums for tables that have been written. |
105 | | // (tag, src_offset) => checksum. Need both because 0-length loca. |
106 | | std::map<std::pair<uint32_t, uint32_t>, uint32_t> checksums; |
107 | | }; |
108 | | |
109 | 15.5M | int WithSign(int flag, int baseval) { |
110 | | // Precondition: 0 <= baseval < 65536 (to avoid integer overflow) |
111 | 15.5M | return (flag & 1) ? baseval : -baseval; |
112 | 15.5M | } |
113 | | |
114 | 18.9M | bool _SafeIntAddition(int a, int b, int* result) { |
115 | 18.9M | if (PREDICT_FALSE( |
116 | 18.9M | ((a > 0) && (b > std::numeric_limits<int>::max() - a)) || |
117 | 18.9M | ((a < 0) && (b < std::numeric_limits<int>::min() - a)))) { |
118 | 34 | return false; |
119 | 34 | } |
120 | 18.9M | *result = a + b; |
121 | 18.9M | return true; |
122 | 18.9M | } |
123 | | |
124 | | bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, |
125 | 51.5k | unsigned int n_points, Point* result, size_t* in_bytes_consumed) { |
126 | 51.5k | int x = 0; |
127 | 51.5k | int y = 0; |
128 | | |
129 | 51.5k | if (PREDICT_FALSE(n_points > in_size)) { |
130 | 15 | return FONT_COMPRESSION_FAILURE(); |
131 | 15 | } |
132 | 51.5k | unsigned int triplet_index = 0; |
133 | | |
134 | 9.53M | for (unsigned int i = 0; i < n_points; ++i) { |
135 | 9.47M | uint8_t flag = flags_in[i]; |
136 | 9.47M | bool on_curve = !(flag >> 7); |
137 | 9.47M | flag &= 0x7f; |
138 | 9.47M | unsigned int n_data_bytes; |
139 | 9.47M | if (flag < 84) { |
140 | 5.80M | n_data_bytes = 1; |
141 | 5.80M | } else if (flag < 120) { |
142 | 979k | n_data_bytes = 2; |
143 | 2.69M | } else if (flag < 124) { |
144 | 66.0k | n_data_bytes = 3; |
145 | 2.62M | } else { |
146 | 2.62M | n_data_bytes = 4; |
147 | 2.62M | } |
148 | 9.47M | if (PREDICT_FALSE(triplet_index + n_data_bytes > in_size || |
149 | 9.47M | triplet_index + n_data_bytes < triplet_index)) { |
150 | 35 | return FONT_COMPRESSION_FAILURE(); |
151 | 35 | } |
152 | 9.47M | int dx, dy; |
153 | 9.47M | if (flag < 10) { |
154 | 2.66M | dx = 0; |
155 | 2.66M | dy = WithSign(flag, ((flag & 14) << 7) + in[triplet_index]); |
156 | 6.81M | } else if (flag < 20) { |
157 | 760k | dx = WithSign(flag, (((flag - 10) & 14) << 7) + in[triplet_index]); |
158 | 760k | dy = 0; |
159 | 6.05M | } else if (flag < 84) { |
160 | 2.37M | int b0 = flag - 20; |
161 | 2.37M | int b1 = in[triplet_index]; |
162 | 2.37M | dx = WithSign(flag, 1 + (b0 & 0x30) + (b1 >> 4)); |
163 | 2.37M | dy = WithSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f)); |
164 | 3.67M | } else if (flag < 120) { |
165 | 979k | int b0 = flag - 84; |
166 | 979k | dx = WithSign(flag, 1 + ((b0 / 12) << 8) + in[triplet_index]); |
167 | 979k | dy = WithSign(flag >> 1, |
168 | 979k | 1 + (((b0 % 12) >> 2) << 8) + in[triplet_index + 1]); |
169 | 2.69M | } else if (flag < 124) { |
170 | 66.0k | int b2 = in[triplet_index + 1]; |
171 | 66.0k | dx = WithSign(flag, (in[triplet_index] << 4) + (b2 >> 4)); |
172 | 66.0k | dy = WithSign(flag >> 1, ((b2 & 0x0f) << 8) + in[triplet_index + 2]); |
173 | 2.62M | } else { |
174 | 2.62M | dx = WithSign(flag, (in[triplet_index] << 8) + in[triplet_index + 1]); |
175 | 2.62M | dy = WithSign(flag >> 1, |
176 | 2.62M | (in[triplet_index + 2] << 8) + in[triplet_index + 3]); |
177 | 2.62M | } |
178 | 9.47M | triplet_index += n_data_bytes; |
179 | 9.47M | if (!_SafeIntAddition(x, dx, &x)) { |
180 | 17 | return false; |
181 | 17 | } |
182 | 9.47M | if (!_SafeIntAddition(y, dy, &y)) { |
183 | 17 | return false; |
184 | 17 | } |
185 | 9.47M | *result++ = {x, y, on_curve}; |
186 | 9.47M | } |
187 | 51.4k | *in_bytes_consumed = triplet_index; |
188 | 51.4k | return true; |
189 | 51.5k | } |
190 | | |
191 | | // This function stores just the point data. On entry, dst points to the |
192 | | // beginning of a simple glyph. Returns true on success. |
193 | | bool StorePoints(unsigned int n_points, const Point* points, |
194 | | unsigned int n_contours, unsigned int instruction_length, |
195 | | bool has_overlap_bit, uint8_t* dst, size_t dst_size, |
196 | 51.3k | size_t* glyph_size) { |
197 | | // I believe that n_contours < 65536, in which case this is safe. However, a |
198 | | // comment and/or an assert would be good. |
199 | 51.3k | unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 + |
200 | 51.3k | instruction_length; |
201 | 51.3k | int last_flag = -1; |
202 | 51.3k | int repeat_count = 0; |
203 | 51.3k | int last_x = 0; |
204 | 51.3k | int last_y = 0; |
205 | 51.3k | unsigned int x_bytes = 0; |
206 | 51.3k | unsigned int y_bytes = 0; |
207 | | |
208 | 3.81M | for (unsigned int i = 0; i < n_points; ++i) { |
209 | 3.76M | const Point& point = points[i]; |
210 | 3.76M | int flag = point.on_curve ? kGlyfOnCurve : 0; |
211 | 3.76M | if (has_overlap_bit && i == 0) { |
212 | 133 | flag |= kOverlapSimple; |
213 | 133 | } |
214 | | |
215 | 3.76M | int dx = point.x - last_x; |
216 | 3.76M | int dy = point.y - last_y; |
217 | 3.76M | if (dx == 0) { |
218 | 2.26M | flag |= kGlyfThisXIsSame; |
219 | 2.26M | } else if (dx > -256 && dx < 256) { |
220 | 1.16M | flag |= kGlyfXShort | (dx > 0 ? kGlyfThisXIsSame : 0); |
221 | 1.16M | x_bytes += 1; |
222 | 1.16M | } else { |
223 | 330k | x_bytes += 2; |
224 | 330k | } |
225 | 3.76M | if (dy == 0) { |
226 | 1.76M | flag |= kGlyfThisYIsSame; |
227 | 2.00M | } else if (dy > -256 && dy < 256) { |
228 | 1.40M | flag |= kGlyfYShort | (dy > 0 ? kGlyfThisYIsSame : 0); |
229 | 1.40M | y_bytes += 1; |
230 | 1.40M | } else { |
231 | 599k | y_bytes += 2; |
232 | 599k | } |
233 | | |
234 | 3.76M | if (flag == last_flag && repeat_count != 255) { |
235 | 1.40M | dst[flag_offset - 1] |= kGlyfRepeat; |
236 | 1.40M | repeat_count++; |
237 | 2.36M | } else { |
238 | 2.36M | if (repeat_count != 0) { |
239 | 326k | if (PREDICT_FALSE(flag_offset >= dst_size)) { |
240 | 0 | return FONT_COMPRESSION_FAILURE(); |
241 | 0 | } |
242 | 326k | dst[flag_offset++] = repeat_count; |
243 | 326k | } |
244 | 2.36M | if (PREDICT_FALSE(flag_offset >= dst_size)) { |
245 | 0 | return FONT_COMPRESSION_FAILURE(); |
246 | 0 | } |
247 | 2.36M | dst[flag_offset++] = flag; |
248 | 2.36M | repeat_count = 0; |
249 | 2.36M | } |
250 | 3.76M | last_x = point.x; |
251 | 3.76M | last_y = point.y; |
252 | 3.76M | last_flag = flag; |
253 | 3.76M | } |
254 | | |
255 | 51.3k | if (repeat_count != 0) { |
256 | 15.5k | if (PREDICT_FALSE(flag_offset >= dst_size)) { |
257 | 0 | return FONT_COMPRESSION_FAILURE(); |
258 | 0 | } |
259 | 15.5k | dst[flag_offset++] = repeat_count; |
260 | 15.5k | } |
261 | 51.3k | unsigned int xy_bytes = x_bytes + y_bytes; |
262 | 51.3k | if (PREDICT_FALSE(xy_bytes < x_bytes || |
263 | 51.3k | flag_offset + xy_bytes < flag_offset || |
264 | 51.3k | flag_offset + xy_bytes > dst_size)) { |
265 | 0 | return FONT_COMPRESSION_FAILURE(); |
266 | 0 | } |
267 | | |
268 | 51.3k | int x_offset = flag_offset; |
269 | 51.3k | int y_offset = flag_offset + x_bytes; |
270 | 51.3k | last_x = 0; |
271 | 51.3k | last_y = 0; |
272 | 3.81M | for (unsigned int i = 0; i < n_points; ++i) { |
273 | 3.76M | int dx = points[i].x - last_x; |
274 | 3.76M | if (dx == 0) { |
275 | | // pass |
276 | 2.26M | } else if (dx > -256 && dx < 256) { |
277 | 1.16M | dst[x_offset++] = std::abs(dx); |
278 | 1.16M | } else { |
279 | | // will always fit for valid input, but overflow is harmless |
280 | 330k | x_offset = Store16(dst, x_offset, dx); |
281 | 330k | } |
282 | 3.76M | last_x += dx; |
283 | 3.76M | int dy = points[i].y - last_y; |
284 | 3.76M | if (dy == 0) { |
285 | | // pass |
286 | 2.00M | } else if (dy > -256 && dy < 256) { |
287 | 1.40M | dst[y_offset++] = std::abs(dy); |
288 | 1.40M | } else { |
289 | 599k | y_offset = Store16(dst, y_offset, dy); |
290 | 599k | } |
291 | 3.76M | last_y += dy; |
292 | 3.76M | } |
293 | 51.3k | *glyph_size = y_offset; |
294 | 51.3k | return true; |
295 | 51.3k | } |
296 | | |
297 | | // Compute the bounding box of the coordinates, and store into a glyf buffer. |
298 | | // A precondition is that there are at least 10 bytes available. |
299 | | // dst should point to the beginning of a 'glyf' record. |
300 | 51.0k | void ComputeBbox(unsigned int n_points, const Point* points, uint8_t* dst) { |
301 | 51.0k | int x_min = 0; |
302 | 51.0k | int y_min = 0; |
303 | 51.0k | int x_max = 0; |
304 | 51.0k | int y_max = 0; |
305 | | |
306 | 51.0k | if (n_points > 0) { |
307 | 49.6k | x_min = points[0].x; |
308 | 49.6k | x_max = points[0].x; |
309 | 49.6k | y_min = points[0].y; |
310 | 49.6k | y_max = points[0].y; |
311 | 49.6k | } |
312 | 4.57M | for (unsigned int i = 1; i < n_points; ++i) { |
313 | 4.52M | int x = points[i].x; |
314 | 4.52M | int y = points[i].y; |
315 | 4.52M | x_min = std::min(x, x_min); |
316 | 4.52M | x_max = std::max(x, x_max); |
317 | 4.52M | y_min = std::min(y, y_min); |
318 | 4.52M | y_max = std::max(y, y_max); |
319 | 4.52M | } |
320 | 51.0k | size_t offset = 2; |
321 | 51.0k | offset = Store16(dst, offset, x_min); |
322 | 51.0k | offset = Store16(dst, offset, y_min); |
323 | 51.0k | offset = Store16(dst, offset, x_max); |
324 | 51.0k | offset = Store16(dst, offset, y_max); |
325 | 51.0k | } |
326 | | |
327 | | |
328 | | bool SizeOfComposite(Buffer composite_stream, size_t* size, |
329 | 521 | bool* have_instructions) { |
330 | 521 | size_t start_offset = composite_stream.offset(); |
331 | 521 | bool we_have_instructions = false; |
332 | | |
333 | 521 | uint16_t flags = FLAG_MORE_COMPONENTS; |
334 | 3.30k | while (flags & FLAG_MORE_COMPONENTS) { |
335 | 2.80k | if (PREDICT_FALSE(!composite_stream.ReadU16(&flags))) { |
336 | 5 | return FONT_COMPRESSION_FAILURE(); |
337 | 5 | } |
338 | 2.79k | we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0; |
339 | 2.79k | size_t arg_size = 2; // glyph index |
340 | 2.79k | if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) { |
341 | 1.53k | arg_size += 4; |
342 | 1.53k | } else { |
343 | 1.25k | arg_size += 2; |
344 | 1.25k | } |
345 | 2.79k | if (flags & FLAG_WE_HAVE_A_SCALE) { |
346 | 1.09k | arg_size += 2; |
347 | 1.69k | } else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) { |
348 | 576 | arg_size += 4; |
349 | 1.12k | } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) { |
350 | 253 | arg_size += 8; |
351 | 253 | } |
352 | 2.79k | if (PREDICT_FALSE(!composite_stream.Skip(arg_size))) { |
353 | 11 | return FONT_COMPRESSION_FAILURE(); |
354 | 11 | } |
355 | 2.79k | } |
356 | | |
357 | 505 | *size = composite_stream.offset() - start_offset; |
358 | 505 | *have_instructions = we_have_instructions; |
359 | | |
360 | 505 | return true; |
361 | 521 | } |
362 | | |
363 | 81.5k | bool Pad4(WOFF2Out* out) { |
364 | 81.5k | uint8_t zeroes[] = {0, 0, 0}; |
365 | 81.5k | if (PREDICT_FALSE(out->Size() + 3 < out->Size())) { |
366 | 0 | return FONT_COMPRESSION_FAILURE(); |
367 | 0 | } |
368 | 81.5k | uint32_t pad_bytes = Round4(out->Size()) - out->Size(); |
369 | 81.5k | if (pad_bytes > 0) { |
370 | 40.5k | if (PREDICT_FALSE(!out->Write(&zeroes, pad_bytes))) { |
371 | 0 | return FONT_COMPRESSION_FAILURE(); |
372 | 0 | } |
373 | 40.5k | } |
374 | 81.5k | return true; |
375 | 81.5k | } |
376 | | |
377 | | // Build TrueType loca table |
378 | | bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format, |
379 | 73 | uint32_t* checksum, WOFF2Out* out) { |
380 | | // TODO(user) figure out what index format to use based on whether max |
381 | | // offset fits into uint16_t or not |
382 | 73 | const uint64_t loca_size = loca_values.size(); |
383 | 73 | const uint64_t offset_size = index_format ? 4 : 2; |
384 | 73 | if (PREDICT_FALSE((loca_size << 2) >> 2 != loca_size)) { |
385 | 0 | return FONT_COMPRESSION_FAILURE(); |
386 | 0 | } |
387 | 73 | std::vector<uint8_t> loca_content(loca_size * offset_size); |
388 | 73 | uint8_t* dst = &loca_content[0]; |
389 | 73 | size_t offset = 0; |
390 | 54.2k | for (size_t i = 0; i < loca_values.size(); ++i) { |
391 | 54.1k | uint32_t value = loca_values[i]; |
392 | 54.1k | if (index_format) { |
393 | 28.9k | offset = StoreU32(dst, offset, value); |
394 | 28.9k | } else { |
395 | 25.2k | offset = Store16(dst, offset, value >> 1); |
396 | 25.2k | } |
397 | 54.1k | } |
398 | 73 | *checksum = ComputeULongSum(&loca_content[0], loca_content.size()); |
399 | 73 | if (PREDICT_FALSE(!out->Write(&loca_content[0], loca_content.size()))) { |
400 | 0 | return FONT_COMPRESSION_FAILURE(); |
401 | 0 | } |
402 | 73 | return true; |
403 | 73 | } |
404 | | |
405 | | // Reconstruct entire glyf table based on transformed original |
406 | | bool ReconstructGlyf(const uint8_t* data, Table* glyf_table, |
407 | | uint32_t* glyf_checksum, Table * loca_table, |
408 | | uint32_t* loca_checksum, WOFF2FontInfo* info, |
409 | 682 | WOFF2Out* out) { |
410 | 682 | static const int kNumSubStreams = 7; |
411 | 682 | Buffer file(data, glyf_table->transform_length); |
412 | 682 | uint16_t version; |
413 | 682 | std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams); |
414 | 682 | const size_t glyf_start = out->Size(); |
415 | | |
416 | 682 | if (PREDICT_FALSE(!file.ReadU16(&version))) { |
417 | 13 | return FONT_COMPRESSION_FAILURE(); |
418 | 13 | } |
419 | | |
420 | 669 | uint16_t flags; |
421 | 669 | if (PREDICT_FALSE(!file.ReadU16(&flags))) { |
422 | 1 | return FONT_COMPRESSION_FAILURE(); |
423 | 1 | } |
424 | 668 | bool has_overlap_bitmap = (flags & FLAG_OVERLAP_SIMPLE_BITMAP); |
425 | | |
426 | 668 | if (PREDICT_FALSE(!file.ReadU16(&info->num_glyphs) || |
427 | 668 | !file.ReadU16(&info->index_format))) { |
428 | 20 | return FONT_COMPRESSION_FAILURE(); |
429 | 20 | } |
430 | | |
431 | | // https://dev.w3.org/webfonts/WOFF2/spec/#conform-mustRejectLoca |
432 | | // dst_length here is origLength in the spec |
433 | 648 | uint32_t expected_loca_dst_length = (info->index_format ? 4 : 2) |
434 | 648 | * (static_cast<uint32_t>(info->num_glyphs) + 1); |
435 | 648 | if (PREDICT_FALSE(loca_table->dst_length != expected_loca_dst_length)) { |
436 | 59 | return FONT_COMPRESSION_FAILURE(); |
437 | 59 | } |
438 | | |
439 | 589 | unsigned int offset = (2 + kNumSubStreams) * 4; |
440 | 589 | if (PREDICT_FALSE(offset > glyf_table->transform_length)) { |
441 | 1 | return FONT_COMPRESSION_FAILURE(); |
442 | 1 | } |
443 | | // Invariant from here on: data_size >= offset |
444 | 4.37k | for (int i = 0; i < kNumSubStreams; ++i) { |
445 | 3.83k | uint32_t substream_size; |
446 | 3.83k | if (PREDICT_FALSE(!file.ReadU32(&substream_size))) { |
447 | 0 | return FONT_COMPRESSION_FAILURE(); |
448 | 0 | } |
449 | 3.83k | if (PREDICT_FALSE(substream_size > glyf_table->transform_length - offset)) { |
450 | 49 | return FONT_COMPRESSION_FAILURE(); |
451 | 49 | } |
452 | 3.78k | substreams[i] = std::make_pair(data + offset, substream_size); |
453 | 3.78k | offset += substream_size; |
454 | 3.78k | } |
455 | 539 | Buffer n_contour_stream(substreams[0].first, substreams[0].second); |
456 | 539 | Buffer n_points_stream(substreams[1].first, substreams[1].second); |
457 | 539 | Buffer flag_stream(substreams[2].first, substreams[2].second); |
458 | 539 | Buffer glyph_stream(substreams[3].first, substreams[3].second); |
459 | 539 | Buffer composite_stream(substreams[4].first, substreams[4].second); |
460 | 539 | Buffer bbox_stream(substreams[5].first, substreams[5].second); |
461 | 539 | Buffer instruction_stream(substreams[6].first, substreams[6].second); |
462 | | |
463 | 539 | const uint8_t* overlap_bitmap = nullptr; |
464 | 539 | unsigned int overlap_bitmap_length = 0; |
465 | 539 | if (has_overlap_bitmap) { |
466 | 179 | overlap_bitmap_length = (info->num_glyphs + 7) >> 3; |
467 | 179 | overlap_bitmap = data + offset; |
468 | 179 | if (PREDICT_FALSE(overlap_bitmap_length > |
469 | 179 | glyf_table->transform_length - offset)) { |
470 | 5 | return FONT_COMPRESSION_FAILURE(); |
471 | 5 | } |
472 | 179 | } |
473 | | |
474 | 534 | std::vector<uint32_t> loca_values(info->num_glyphs + 1); |
475 | 534 | std::vector<unsigned int> n_points_vec; |
476 | 534 | std::unique_ptr<Point[]> points; |
477 | 534 | size_t points_size = 0; |
478 | 534 | const uint8_t* bbox_bitmap = bbox_stream.buffer(); |
479 | | // Safe because num_glyphs is bounded |
480 | 534 | unsigned int bitmap_length = ((info->num_glyphs + 31) >> 5) << 2; |
481 | 534 | if (!bbox_stream.Skip(bitmap_length)) { |
482 | 1 | return FONT_COMPRESSION_FAILURE(); |
483 | 1 | } |
484 | | |
485 | | // Temp buffer for glyph's. |
486 | 533 | size_t glyph_buf_size = kDefaultGlyphBuf; |
487 | 533 | std::unique_ptr<uint8_t[]> glyph_buf(new uint8_t[glyph_buf_size]); |
488 | | |
489 | 533 | info->x_mins.resize(info->num_glyphs); |
490 | 73.1k | for (unsigned int i = 0; i < info->num_glyphs; ++i) { |
491 | 73.0k | size_t glyph_size = 0; |
492 | 73.0k | uint16_t n_contours = 0; |
493 | 73.0k | bool have_bbox = false; |
494 | 73.0k | if (bbox_bitmap[i >> 3] & (0x80 >> (i & 7))) { |
495 | 962 | have_bbox = true; |
496 | 962 | } |
497 | 73.0k | if (PREDICT_FALSE(!n_contour_stream.ReadU16(&n_contours))) { |
498 | 5 | return FONT_COMPRESSION_FAILURE(); |
499 | 5 | } |
500 | | |
501 | 73.0k | if (n_contours == 0xffff) { |
502 | | // composite glyph |
503 | 544 | bool have_instructions = false; |
504 | 544 | unsigned int instruction_size = 0; |
505 | 544 | if (PREDICT_FALSE(!have_bbox)) { |
506 | | // composite glyphs must have an explicit bbox |
507 | 23 | return FONT_COMPRESSION_FAILURE(); |
508 | 23 | } |
509 | | |
510 | 521 | size_t composite_size; |
511 | 521 | if (PREDICT_FALSE(!SizeOfComposite(composite_stream, &composite_size, |
512 | 521 | &have_instructions))) { |
513 | 16 | return FONT_COMPRESSION_FAILURE(); |
514 | 16 | } |
515 | 505 | if (have_instructions) { |
516 | 201 | if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { |
517 | 1 | return FONT_COMPRESSION_FAILURE(); |
518 | 1 | } |
519 | 201 | } |
520 | | |
521 | 504 | size_t size_needed = 12 + composite_size + instruction_size; |
522 | 504 | if (PREDICT_FALSE(glyph_buf_size < size_needed)) { |
523 | 8 | glyph_buf.reset(new uint8_t[size_needed]); |
524 | 8 | glyph_buf_size = size_needed; |
525 | 8 | } |
526 | | |
527 | 504 | glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); |
528 | 504 | if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { |
529 | 9 | return FONT_COMPRESSION_FAILURE(); |
530 | 9 | } |
531 | 495 | glyph_size += 8; |
532 | | |
533 | 495 | if (PREDICT_FALSE(!composite_stream.Read(glyph_buf.get() + glyph_size, |
534 | 495 | composite_size))) { |
535 | 0 | return FONT_COMPRESSION_FAILURE(); |
536 | 0 | } |
537 | 495 | glyph_size += composite_size; |
538 | 495 | if (have_instructions) { |
539 | 192 | glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); |
540 | 192 | if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, |
541 | 192 | instruction_size))) { |
542 | 28 | return FONT_COMPRESSION_FAILURE(); |
543 | 28 | } |
544 | 164 | glyph_size += instruction_size; |
545 | 164 | } |
546 | 72.5k | } else if (n_contours > 0) { |
547 | | // simple glyph |
548 | 51.7k | n_points_vec.clear(); |
549 | 51.7k | unsigned int total_n_points = 0; |
550 | 51.7k | unsigned int n_points_contour; |
551 | 523k | for (unsigned int j = 0; j < n_contours; ++j) { |
552 | 472k | if (PREDICT_FALSE( |
553 | 472k | !Read255UShort(&n_points_stream, &n_points_contour))) { |
554 | 96 | return FONT_COMPRESSION_FAILURE(); |
555 | 96 | } |
556 | 471k | n_points_vec.push_back(n_points_contour); |
557 | 471k | if (PREDICT_FALSE(total_n_points + n_points_contour < total_n_points)) { |
558 | 0 | return FONT_COMPRESSION_FAILURE(); |
559 | 0 | } |
560 | 471k | total_n_points += n_points_contour; |
561 | 471k | } |
562 | 51.6k | unsigned int flag_size = total_n_points; |
563 | 51.6k | if (PREDICT_FALSE( |
564 | 51.6k | flag_size > flag_stream.length() - flag_stream.offset())) { |
565 | 70 | return FONT_COMPRESSION_FAILURE(); |
566 | 70 | } |
567 | 51.5k | const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset(); |
568 | 51.5k | const uint8_t* triplet_buf = glyph_stream.buffer() + |
569 | 51.5k | glyph_stream.offset(); |
570 | 51.5k | size_t triplet_size = glyph_stream.length() - glyph_stream.offset(); |
571 | 51.5k | size_t triplet_bytes_consumed = 0; |
572 | 51.5k | if (points_size < total_n_points) { |
573 | 981 | points_size = total_n_points; |
574 | 981 | points.reset(new Point[points_size]); |
575 | 981 | } |
576 | 51.5k | if (PREDICT_FALSE(!TripletDecode(flags_buf, triplet_buf, triplet_size, |
577 | 51.5k | total_n_points, points.get(), &triplet_bytes_consumed))) { |
578 | 84 | return FONT_COMPRESSION_FAILURE(); |
579 | 84 | } |
580 | 51.4k | if (PREDICT_FALSE(!flag_stream.Skip(flag_size))) { |
581 | 0 | return FONT_COMPRESSION_FAILURE(); |
582 | 0 | } |
583 | 51.4k | if (PREDICT_FALSE(!glyph_stream.Skip(triplet_bytes_consumed))) { |
584 | 0 | return FONT_COMPRESSION_FAILURE(); |
585 | 0 | } |
586 | 51.4k | unsigned int instruction_size; |
587 | 51.4k | if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { |
588 | 15 | return FONT_COMPRESSION_FAILURE(); |
589 | 15 | } |
590 | | |
591 | 51.4k | if (PREDICT_FALSE(total_n_points >= (1 << 27) |
592 | 51.4k | || instruction_size >= (1 << 30))) { |
593 | 0 | return FONT_COMPRESSION_FAILURE(); |
594 | 0 | } |
595 | 51.4k | size_t size_needed = 12 + 2 * n_contours + 5 * total_n_points |
596 | 51.4k | + instruction_size; |
597 | 51.4k | if (PREDICT_FALSE(glyph_buf_size < size_needed)) { |
598 | 137 | glyph_buf.reset(new uint8_t[size_needed]); |
599 | 137 | glyph_buf_size = size_needed; |
600 | 137 | } |
601 | | |
602 | 51.4k | glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); |
603 | 51.4k | if (have_bbox) { |
604 | 387 | if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { |
605 | 26 | return FONT_COMPRESSION_FAILURE(); |
606 | 26 | } |
607 | 51.0k | } else { |
608 | 51.0k | ComputeBbox(total_n_points, points.get(), glyph_buf.get()); |
609 | 51.0k | } |
610 | 51.4k | glyph_size = kEndPtsOfContoursOffset; |
611 | 51.4k | int end_point = -1; |
612 | 210k | for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) { |
613 | 158k | end_point += n_points_vec[contour_ix]; |
614 | 158k | if (PREDICT_FALSE(end_point >= 65536)) { |
615 | 5 | return FONT_COMPRESSION_FAILURE(); |
616 | 5 | } |
617 | 158k | glyph_size = Store16(glyph_buf.get(), glyph_size, end_point); |
618 | 158k | } |
619 | | |
620 | 51.4k | glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); |
621 | 51.4k | if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, |
622 | 51.4k | instruction_size))) { |
623 | 76 | return FONT_COMPRESSION_FAILURE(); |
624 | 76 | } |
625 | 51.3k | glyph_size += instruction_size; |
626 | | |
627 | 51.3k | bool has_overlap_bit = |
628 | 51.3k | has_overlap_bitmap && overlap_bitmap[i >> 3] & (0x80 >> (i & 7)); |
629 | | |
630 | 51.3k | if (PREDICT_FALSE(!StorePoints( |
631 | 51.3k | total_n_points, points.get(), n_contours, instruction_size, |
632 | 51.3k | has_overlap_bit, glyph_buf.get(), glyph_buf_size, &glyph_size))) { |
633 | 0 | return FONT_COMPRESSION_FAILURE(); |
634 | 0 | } |
635 | 51.3k | } else { |
636 | | // n_contours == 0; empty glyph. Must NOT have a bbox. |
637 | 20.8k | if (PREDICT_FALSE(have_bbox)) { |
638 | | #ifdef FONT_COMPRESSION_BIN |
639 | | fprintf(stderr, "Empty glyph has a bbox\n"); |
640 | | #endif |
641 | 6 | return FONT_COMPRESSION_FAILURE(); |
642 | 6 | } |
643 | 20.8k | } |
644 | | |
645 | 72.6k | loca_values[i] = out->Size() - glyf_start; |
646 | 72.6k | if (PREDICT_FALSE(!out->Write(glyph_buf.get(), glyph_size))) { |
647 | 0 | return FONT_COMPRESSION_FAILURE(); |
648 | 0 | } |
649 | | |
650 | | // TODO(user) Old code aligned glyphs ... but do we actually need to? |
651 | 72.6k | if (PREDICT_FALSE(!Pad4(out))) { |
652 | 0 | return FONT_COMPRESSION_FAILURE(); |
653 | 0 | } |
654 | | |
655 | 72.6k | *glyf_checksum += ComputeULongSum(glyph_buf.get(), glyph_size); |
656 | | |
657 | | // We may need x_min to reconstruct 'hmtx' |
658 | 72.6k | if (n_contours > 0) { |
659 | 51.8k | Buffer x_min_buf(glyph_buf.get() + 2, 2); |
660 | 51.8k | if (PREDICT_FALSE(!x_min_buf.ReadS16(&info->x_mins[i]))) { |
661 | 0 | return FONT_COMPRESSION_FAILURE(); |
662 | 0 | } |
663 | 51.8k | } |
664 | 72.6k | } |
665 | | |
666 | | // glyf_table dst_offset was set by ReconstructFont |
667 | 73 | glyf_table->dst_length = out->Size() - glyf_table->dst_offset; |
668 | 73 | loca_table->dst_offset = out->Size(); |
669 | | // loca[n] will be equal the length of the glyph data ('glyf') table |
670 | 73 | loca_values[info->num_glyphs] = glyf_table->dst_length; |
671 | 73 | if (PREDICT_FALSE(!StoreLoca(loca_values, info->index_format, loca_checksum, |
672 | 73 | out))) { |
673 | 0 | return FONT_COMPRESSION_FAILURE(); |
674 | 0 | } |
675 | 73 | loca_table->dst_length = out->Size() - loca_table->dst_offset; |
676 | | |
677 | 73 | return true; |
678 | 73 | } |
679 | | |
680 | 4.57k | Table* FindTable(std::vector<Table*>* tables, uint32_t tag) { |
681 | 28.2k | for (Table* table : *tables) { |
682 | 28.2k | if (table->tag == tag) { |
683 | 2.24k | return table; |
684 | 2.24k | } |
685 | 28.2k | } |
686 | 2.32k | return NULL; |
687 | 4.57k | } |
688 | | |
689 | | // Get numberOfHMetrics, https://www.microsoft.com/typography/otspec/hhea.htm |
690 | | bool ReadNumHMetrics(const uint8_t* data, size_t data_size, |
691 | 433 | uint16_t* num_hmetrics) { |
692 | | // Skip 34 to reach 'hhea' numberOfHMetrics |
693 | 433 | Buffer buffer(data, data_size); |
694 | 433 | if (PREDICT_FALSE(!buffer.Skip(34) || !buffer.ReadU16(num_hmetrics))) { |
695 | 13 | return FONT_COMPRESSION_FAILURE(); |
696 | 13 | } |
697 | 420 | return true; |
698 | 433 | } |
699 | | |
700 | | // http://dev.w3.org/webfonts/WOFF2/spec/Overview.html#hmtx_table_format |
701 | | bool ReconstructTransformedHmtx(const uint8_t* transformed_buf, |
702 | | size_t transformed_size, |
703 | | uint16_t num_glyphs, |
704 | | uint16_t num_hmetrics, |
705 | | const std::vector<int16_t>& x_mins, |
706 | | uint32_t* checksum, |
707 | 73 | WOFF2Out* out) { |
708 | 73 | Buffer hmtx_buff_in(transformed_buf, transformed_size); |
709 | | |
710 | 73 | uint8_t hmtx_flags; |
711 | 73 | if (PREDICT_FALSE(!hmtx_buff_in.ReadU8(&hmtx_flags))) { |
712 | 1 | return FONT_COMPRESSION_FAILURE(); |
713 | 1 | } |
714 | | |
715 | 72 | std::vector<uint16_t> advance_widths; |
716 | 72 | std::vector<int16_t> lsbs; |
717 | 72 | bool has_proportional_lsbs = (hmtx_flags & 1) == 0; |
718 | 72 | bool has_monospace_lsbs = (hmtx_flags & 2) == 0; |
719 | | |
720 | | // Bits 2-7 are reserved and MUST be zero. |
721 | 72 | if ((hmtx_flags & 0xFC) != 0) { |
722 | | #ifdef FONT_COMPRESSION_BIN |
723 | | fprintf(stderr, "Illegal hmtx flags; bits 2-7 must be 0\n"); |
724 | | #endif |
725 | 10 | return FONT_COMPRESSION_FAILURE(); |
726 | 10 | } |
727 | | |
728 | | // you say you transformed but there is little evidence of it |
729 | 62 | if (has_proportional_lsbs && has_monospace_lsbs) { |
730 | 5 | return FONT_COMPRESSION_FAILURE(); |
731 | 5 | } |
732 | | |
733 | 57 | assert(x_mins.size() == num_glyphs); |
734 | | |
735 | | // num_glyphs 0 is OK if there is no 'glyf' but cannot then xform 'hmtx'. |
736 | 57 | if (PREDICT_FALSE(num_hmetrics > num_glyphs)) { |
737 | 17 | return FONT_COMPRESSION_FAILURE(); |
738 | 17 | } |
739 | | |
740 | | // https://www.microsoft.com/typography/otspec/hmtx.htm |
741 | | // "...only one entry need be in the array, but that entry is required." |
742 | 40 | if (PREDICT_FALSE(num_hmetrics < 1)) { |
743 | 3 | return FONT_COMPRESSION_FAILURE(); |
744 | 3 | } |
745 | | |
746 | 228 | for (uint16_t i = 0; i < num_hmetrics; i++) { |
747 | 192 | uint16_t advance_width; |
748 | 192 | if (PREDICT_FALSE(!hmtx_buff_in.ReadU16(&advance_width))) { |
749 | 1 | return FONT_COMPRESSION_FAILURE(); |
750 | 1 | } |
751 | 191 | advance_widths.push_back(advance_width); |
752 | 191 | } |
753 | | |
754 | 219 | for (uint16_t i = 0; i < num_hmetrics; i++) { |
755 | 185 | int16_t lsb; |
756 | 185 | if (has_proportional_lsbs) { |
757 | 59 | if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) { |
758 | 2 | return FONT_COMPRESSION_FAILURE(); |
759 | 2 | } |
760 | 126 | } else { |
761 | 126 | lsb = x_mins[i]; |
762 | 126 | } |
763 | 183 | lsbs.push_back(lsb); |
764 | 183 | } |
765 | | |
766 | 180 | for (uint16_t i = num_hmetrics; i < num_glyphs; i++) { |
767 | 147 | int16_t lsb; |
768 | 147 | if (has_monospace_lsbs) { |
769 | 90 | if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) { |
770 | 1 | return FONT_COMPRESSION_FAILURE(); |
771 | 1 | } |
772 | 90 | } else { |
773 | 57 | lsb = x_mins[i]; |
774 | 57 | } |
775 | 146 | lsbs.push_back(lsb); |
776 | 146 | } |
777 | | |
778 | | // bake me a shiny new hmtx table |
779 | 33 | uint32_t hmtx_output_size = 2 * num_glyphs + 2 * num_hmetrics; |
780 | 33 | std::vector<uint8_t> hmtx_table(hmtx_output_size); |
781 | 33 | uint8_t* dst = &hmtx_table[0]; |
782 | 33 | size_t dst_offset = 0; |
783 | 348 | for (uint32_t i = 0; i < num_glyphs; i++) { |
784 | 315 | if (i < num_hmetrics) { |
785 | 171 | Store16(advance_widths[i], &dst_offset, dst); |
786 | 171 | } |
787 | 315 | Store16(lsbs[i], &dst_offset, dst); |
788 | 315 | } |
789 | | |
790 | 33 | *checksum = ComputeULongSum(&hmtx_table[0], hmtx_output_size); |
791 | 33 | if (PREDICT_FALSE(!out->Write(&hmtx_table[0], hmtx_output_size))) { |
792 | 0 | return FONT_COMPRESSION_FAILURE(); |
793 | 0 | } |
794 | | |
795 | 33 | return true; |
796 | 33 | } |
797 | | |
798 | | bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size, |
799 | 5.85k | const uint8_t* src_buf, size_t src_size) { |
800 | 5.85k | size_t uncompressed_size = dst_size; |
801 | 5.85k | BrotliDecoderResult result = BrotliDecoderDecompress( |
802 | 5.85k | src_size, src_buf, &uncompressed_size, dst_buf); |
803 | 5.85k | if (PREDICT_FALSE(result != BROTLI_DECODER_RESULT_SUCCESS || |
804 | 5.85k | uncompressed_size != dst_size)) { |
805 | 4.58k | return FONT_COMPRESSION_FAILURE(); |
806 | 4.58k | } |
807 | 1.27k | return true; |
808 | 5.85k | } |
809 | | |
810 | | bool ReadTableDirectory(Buffer* file, std::vector<Table>* tables, |
811 | 7.27k | size_t num_tables) { |
812 | 7.27k | uint32_t src_offset = 0; |
813 | 1.70M | for (size_t i = 0; i < num_tables; ++i) { |
814 | 1.70M | Table* table = &(*tables)[i]; |
815 | 1.70M | uint8_t flag_byte; |
816 | 1.70M | if (PREDICT_FALSE(!file->ReadU8(&flag_byte))) { |
817 | 75 | return FONT_COMPRESSION_FAILURE(); |
818 | 75 | } |
819 | 1.70M | uint32_t tag; |
820 | 1.70M | if ((flag_byte & 0x3f) == 0x3f) { |
821 | 18.6k | if (PREDICT_FALSE(!file->ReadU32(&tag))) { |
822 | 7 | return FONT_COMPRESSION_FAILURE(); |
823 | 7 | } |
824 | 1.68M | } else { |
825 | 1.68M | tag = kKnownTags[flag_byte & 0x3f]; |
826 | 1.68M | } |
827 | 1.70M | uint32_t flags = 0; |
828 | 1.70M | uint8_t xform_version = (flag_byte >> 6) & 0x03; |
829 | | |
830 | | // 0 means xform for glyph/loca, non-0 for others |
831 | 1.70M | if (tag == kGlyfTableTag || tag == kLocaTableTag) { |
832 | 22.7k | if (xform_version == 0) { |
833 | 6.84k | flags |= kWoff2FlagsTransform; |
834 | 6.84k | } |
835 | 1.67M | } else if (xform_version != 0) { |
836 | 115k | flags |= kWoff2FlagsTransform; |
837 | 115k | } |
838 | 1.70M | flags |= xform_version; |
839 | | |
840 | 1.70M | uint32_t dst_length; |
841 | 1.70M | if (PREDICT_FALSE(!ReadBase128(file, &dst_length))) { |
842 | 88 | return FONT_COMPRESSION_FAILURE(); |
843 | 88 | } |
844 | 1.70M | uint32_t transform_length = dst_length; |
845 | 1.70M | if ((flags & kWoff2FlagsTransform) != 0) { |
846 | 122k | if (PREDICT_FALSE(!ReadBase128(file, &transform_length))) { |
847 | 16 | return FONT_COMPRESSION_FAILURE(); |
848 | 16 | } |
849 | 122k | if (PREDICT_FALSE(tag == kLocaTableTag && transform_length)) { |
850 | 59 | return FONT_COMPRESSION_FAILURE(); |
851 | 59 | } |
852 | 122k | } |
853 | 1.70M | if (PREDICT_FALSE(src_offset + transform_length < src_offset)) { |
854 | 1 | return FONT_COMPRESSION_FAILURE(); |
855 | 1 | } |
856 | 1.70M | table->src_offset = src_offset; |
857 | 1.70M | table->src_length = transform_length; |
858 | 1.70M | src_offset += transform_length; |
859 | | |
860 | 1.70M | table->tag = tag; |
861 | 1.70M | table->flags = flags; |
862 | 1.70M | table->transform_length = transform_length; |
863 | 1.70M | table->dst_length = dst_length; |
864 | 1.70M | } |
865 | 7.03k | return true; |
866 | 7.27k | } |
867 | | |
868 | | // Writes a single Offset Table entry |
869 | | size_t StoreOffsetTable(uint8_t* result, size_t offset, uint32_t flavor, |
870 | 9.31k | uint16_t num_tables) { |
871 | 9.31k | offset = StoreU32(result, offset, flavor); // sfnt version |
872 | 9.31k | offset = Store16(result, offset, num_tables); // num_tables |
873 | 9.31k | unsigned max_pow2 = 0; |
874 | 32.3k | while (1u << (max_pow2 + 1) <= num_tables) { |
875 | 22.9k | max_pow2++; |
876 | 22.9k | } |
877 | 9.31k | const uint16_t output_search_range = (1u << max_pow2) << 4; |
878 | 9.31k | offset = Store16(result, offset, output_search_range); // searchRange |
879 | 9.31k | offset = Store16(result, offset, max_pow2); // entrySelector |
880 | | // rangeShift |
881 | 9.31k | offset = Store16(result, offset, (num_tables << 4) - output_search_range); |
882 | 9.31k | return offset; |
883 | 9.31k | } |
884 | | |
885 | 1.12M | size_t StoreTableEntry(uint8_t* result, uint32_t offset, uint32_t tag) { |
886 | 1.12M | offset = StoreU32(result, offset, tag); |
887 | 1.12M | offset = StoreU32(result, offset, 0); |
888 | 1.12M | offset = StoreU32(result, offset, 0); |
889 | 1.12M | offset = StoreU32(result, offset, 0); |
890 | 1.12M | return offset; |
891 | 1.12M | } |
892 | | |
893 | | // First table goes after all the headers, table directory, etc |
894 | 13.1k | uint64_t ComputeOffsetToFirstTable(const WOFF2Header& hdr) { |
895 | 13.1k | uint64_t offset = kSfntHeaderSize + |
896 | 13.1k | kSfntEntrySize * static_cast<uint64_t>(hdr.num_tables); |
897 | 13.1k | if (hdr.header_version) { |
898 | 508 | offset = CollectionHeaderSize(hdr.header_version, hdr.ttc_fonts.size()) |
899 | 508 | + kSfntHeaderSize * hdr.ttc_fonts.size(); |
900 | 6.37k | for (const auto& ttc_font : hdr.ttc_fonts) { |
901 | 6.37k | offset += kSfntEntrySize * ttc_font.table_indices.size(); |
902 | 6.37k | } |
903 | 508 | } |
904 | 13.1k | return offset; |
905 | 13.1k | } |
906 | | |
907 | 1.59k | std::vector<Table*> Tables(WOFF2Header* hdr, size_t font_index) { |
908 | 1.59k | std::vector<Table*> tables; |
909 | 1.59k | if (PREDICT_FALSE(hdr->header_version)) { |
910 | 2.41k | for (auto index : hdr->ttc_fonts[font_index].table_indices) { |
911 | 2.41k | tables.push_back(&hdr->tables[index]); |
912 | 2.41k | } |
913 | 1.14k | } else { |
914 | 11.4k | for (auto& table : hdr->tables) { |
915 | 11.4k | tables.push_back(&table); |
916 | 11.4k | } |
917 | 1.14k | } |
918 | 1.59k | return tables; |
919 | 1.59k | } |
920 | | |
921 | | // Offset tables assumed to have been written in with 0's initially. |
922 | | // WOFF2Header isn't const so we can use [] instead of at() (which upsets FF) |
923 | | bool ReconstructFont(uint8_t* transformed_buf, |
924 | | const uint32_t transformed_buf_size, |
925 | | RebuildMetadata* metadata, |
926 | | WOFF2Header* hdr, |
927 | | size_t font_index, |
928 | 1.59k | WOFF2Out* out) { |
929 | 1.59k | size_t dest_offset = out->Size(); |
930 | 1.59k | uint8_t table_entry[12]; |
931 | 1.59k | WOFF2FontInfo* info = &metadata->font_infos[font_index]; |
932 | 1.59k | std::vector<Table*> tables = Tables(hdr, font_index); |
933 | | |
934 | | // 'glyf' without 'loca' doesn't make sense |
935 | 1.59k | const Table* glyf_table = FindTable(&tables, kGlyfTableTag); |
936 | 1.59k | const Table* loca_table = FindTable(&tables, kLocaTableTag); |
937 | 1.59k | if (PREDICT_FALSE(static_cast<bool>(glyf_table) != |
938 | 1.59k | static_cast<bool>(loca_table))) { |
939 | | #ifdef FONT_COMPRESSION_BIN |
940 | | fprintf(stderr, "Cannot have just one of glyf/loca\n"); |
941 | | #endif |
942 | 1 | return FONT_COMPRESSION_FAILURE(); |
943 | 1 | } |
944 | | |
945 | 1.59k | if (glyf_table != NULL) { |
946 | 717 | if (PREDICT_FALSE((glyf_table->flags & kWoff2FlagsTransform) |
947 | 717 | != (loca_table->flags & kWoff2FlagsTransform))) { |
948 | | #ifdef FONT_COMPRESSION_BIN |
949 | | fprintf(stderr, "Cannot transform just one of glyf/loca\n"); |
950 | | #endif |
951 | 2 | return FONT_COMPRESSION_FAILURE(); |
952 | 2 | } |
953 | 717 | } |
954 | | |
955 | 1.58k | uint32_t font_checksum = metadata->header_checksum; |
956 | 1.58k | if (hdr->header_version) { |
957 | 447 | font_checksum = hdr->ttc_fonts[font_index].header_checksum; |
958 | 447 | } |
959 | | |
960 | 1.58k | uint32_t loca_checksum = 0; |
961 | 10.4k | for (size_t i = 0; i < tables.size(); i++) { |
962 | 9.73k | Table& table = *tables[i]; |
963 | | |
964 | 9.73k | std::pair<uint32_t, uint32_t> checksum_key = {table.tag, table.src_offset}; |
965 | 9.73k | bool reused = metadata->checksums.find(checksum_key) |
966 | 9.73k | != metadata->checksums.end(); |
967 | 9.73k | if (PREDICT_FALSE(font_index == 0 && reused)) { |
968 | 27 | return FONT_COMPRESSION_FAILURE(); |
969 | 27 | } |
970 | | |
971 | | // TODO(user) a collection with optimized hmtx that reused glyf/loca |
972 | | // would fail. We don't optimize hmtx for collections yet. |
973 | 9.70k | if (PREDICT_FALSE(static_cast<uint64_t>(table.src_offset) + table.src_length |
974 | 9.70k | > transformed_buf_size)) { |
975 | 0 | return FONT_COMPRESSION_FAILURE(); |
976 | 0 | } |
977 | | |
978 | 9.70k | if (table.tag == kHheaTableTag) { |
979 | 433 | if (!ReadNumHMetrics(transformed_buf + table.src_offset, |
980 | 433 | table.src_length, &info->num_hmetrics)) { |
981 | 13 | return FONT_COMPRESSION_FAILURE(); |
982 | 13 | } |
983 | 433 | } |
984 | | |
985 | 9.69k | uint32_t checksum = 0; |
986 | 9.69k | if (!reused) { |
987 | 7.78k | if ((table.flags & kWoff2FlagsTransform) != kWoff2FlagsTransform) { |
988 | 6.78k | if (table.tag == kHeadTableTag) { |
989 | 167 | if (PREDICT_FALSE(table.src_length < 12)) { |
990 | 6 | return FONT_COMPRESSION_FAILURE(); |
991 | 6 | } |
992 | | // checkSumAdjustment = 0 |
993 | 161 | StoreU32(transformed_buf + table.src_offset, 8, 0); |
994 | 161 | } |
995 | 6.78k | table.dst_offset = dest_offset; |
996 | 6.78k | checksum = ComputeULongSum(transformed_buf + table.src_offset, |
997 | 6.78k | table.src_length); |
998 | 6.78k | if (PREDICT_FALSE(!out->Write(transformed_buf + table.src_offset, |
999 | 6.78k | table.src_length))) { |
1000 | 0 | return FONT_COMPRESSION_FAILURE(); |
1001 | 0 | } |
1002 | 6.78k | } else { |
1003 | 996 | if (table.tag == kGlyfTableTag) { |
1004 | 682 | table.dst_offset = dest_offset; |
1005 | | |
1006 | 682 | Table* loca_table = FindTable(&tables, kLocaTableTag); |
1007 | 682 | if (PREDICT_FALSE(!ReconstructGlyf(transformed_buf + table.src_offset, |
1008 | 682 | &table, &checksum, loca_table, &loca_checksum, info, out))) { |
1009 | 609 | return FONT_COMPRESSION_FAILURE(); |
1010 | 609 | } |
1011 | 682 | } else if (table.tag == kLocaTableTag) { |
1012 | | // All the work was done by ReconstructGlyf. We already know checksum. |
1013 | 89 | checksum = loca_checksum; |
1014 | 225 | } else if (table.tag == kHmtxTableTag) { |
1015 | 73 | table.dst_offset = dest_offset; |
1016 | | // Tables are sorted so all the info we need has been gathered. |
1017 | 73 | if (PREDICT_FALSE(!ReconstructTransformedHmtx( |
1018 | 73 | transformed_buf + table.src_offset, table.src_length, |
1019 | 73 | info->num_glyphs, info->num_hmetrics, info->x_mins, &checksum, |
1020 | 73 | out))) { |
1021 | 40 | return FONT_COMPRESSION_FAILURE(); |
1022 | 40 | } |
1023 | 152 | } else { |
1024 | 152 | return FONT_COMPRESSION_FAILURE(); // transform unknown |
1025 | 152 | } |
1026 | 996 | } |
1027 | 6.97k | metadata->checksums[checksum_key] = checksum; |
1028 | 6.97k | } else { |
1029 | 1.90k | checksum = metadata->checksums[checksum_key]; |
1030 | 1.90k | } |
1031 | 8.88k | font_checksum += checksum; |
1032 | | |
1033 | | // update the table entry with real values. |
1034 | 8.88k | StoreU32(table_entry, 0, checksum); |
1035 | 8.88k | StoreU32(table_entry, 4, table.dst_offset); |
1036 | 8.88k | StoreU32(table_entry, 8, table.dst_length); |
1037 | 8.88k | if (PREDICT_FALSE(!out->Write(table_entry, |
1038 | 8.88k | info->table_entry_by_tag[table.tag] + 4, 12))) { |
1039 | 0 | return FONT_COMPRESSION_FAILURE(); |
1040 | 0 | } |
1041 | | |
1042 | | // We replaced 0's. Update overall checksum. |
1043 | 8.88k | font_checksum += ComputeULongSum(table_entry, 12); |
1044 | | |
1045 | 8.88k | if (PREDICT_FALSE(!Pad4(out))) { |
1046 | 0 | return FONT_COMPRESSION_FAILURE(); |
1047 | 0 | } |
1048 | | |
1049 | 8.88k | if (PREDICT_FALSE(static_cast<uint64_t>(table.dst_offset + table.dst_length) |
1050 | 8.88k | > out->Size())) { |
1051 | 34 | return FONT_COMPRESSION_FAILURE(); |
1052 | 34 | } |
1053 | 8.85k | dest_offset = out->Size(); |
1054 | 8.85k | } |
1055 | | |
1056 | | // Update 'head' checkSumAdjustment. We already set it to 0 and summed font. |
1057 | 707 | Table* head_table = FindTable(&tables, kHeadTableTag); |
1058 | 707 | if (head_table) { |
1059 | 129 | if (PREDICT_FALSE(head_table->dst_length < 12)) { |
1060 | 1 | return FONT_COMPRESSION_FAILURE(); |
1061 | 1 | } |
1062 | 128 | uint8_t checksum_adjustment[4]; |
1063 | 128 | StoreU32(checksum_adjustment, 0, 0xB1B0AFBA - font_checksum); |
1064 | 128 | if (PREDICT_FALSE(!out->Write(checksum_adjustment, |
1065 | 128 | head_table->dst_offset + 8, 4))) { |
1066 | 0 | return FONT_COMPRESSION_FAILURE(); |
1067 | 0 | } |
1068 | 128 | } |
1069 | | |
1070 | 706 | return true; |
1071 | 707 | } |
1072 | | |
1073 | 7.66k | bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { |
1074 | 7.66k | Buffer file(data, length); |
1075 | | |
1076 | 7.66k | uint32_t signature; |
1077 | 7.66k | if (PREDICT_FALSE(!file.ReadU32(&signature) || signature != kWoff2Signature || |
1078 | 7.66k | !file.ReadU32(&hdr->flavor))) { |
1079 | 56 | return FONT_COMPRESSION_FAILURE(); |
1080 | 56 | } |
1081 | | |
1082 | | // TODO(user): Should call IsValidVersionTag() here. |
1083 | | |
1084 | 7.61k | uint32_t reported_length; |
1085 | 7.61k | if (PREDICT_FALSE( |
1086 | 7.61k | !file.ReadU32(&reported_length) || length != reported_length)) { |
1087 | 70 | return FONT_COMPRESSION_FAILURE(); |
1088 | 70 | } |
1089 | 7.54k | if (PREDICT_FALSE(!file.ReadU16(&hdr->num_tables) || !hdr->num_tables)) { |
1090 | 3 | return FONT_COMPRESSION_FAILURE(); |
1091 | 3 | } |
1092 | | |
1093 | | // We don't care about these fields of the header: |
1094 | | // uint16_t reserved |
1095 | | // uint32_t total_sfnt_size, we don't believe this, will compute later |
1096 | 7.54k | if (PREDICT_FALSE(!file.Skip(6))) { |
1097 | 21 | return FONT_COMPRESSION_FAILURE(); |
1098 | 21 | } |
1099 | 7.51k | if (PREDICT_FALSE(!file.ReadU32(&hdr->compressed_length))) { |
1100 | 4 | return FONT_COMPRESSION_FAILURE(); |
1101 | 4 | } |
1102 | | // We don't care about these fields of the header: |
1103 | | // uint16_t major_version, minor_version |
1104 | 7.51k | if (PREDICT_FALSE(!file.Skip(2 * 2))) { |
1105 | 3 | return FONT_COMPRESSION_FAILURE(); |
1106 | 3 | } |
1107 | 7.51k | uint32_t meta_offset; |
1108 | 7.51k | uint32_t meta_length; |
1109 | 7.51k | uint32_t meta_length_orig; |
1110 | 7.51k | if (PREDICT_FALSE(!file.ReadU32(&meta_offset) || |
1111 | 7.51k | !file.ReadU32(&meta_length) || |
1112 | 7.51k | !file.ReadU32(&meta_length_orig))) { |
1113 | 9 | return FONT_COMPRESSION_FAILURE(); |
1114 | 9 | } |
1115 | 7.50k | if (meta_offset) { |
1116 | 319 | if (PREDICT_FALSE( |
1117 | 319 | meta_offset >= length || length - meta_offset < meta_length)) { |
1118 | 110 | return FONT_COMPRESSION_FAILURE(); |
1119 | 110 | } |
1120 | 319 | } |
1121 | 7.39k | uint32_t priv_offset; |
1122 | 7.39k | uint32_t priv_length; |
1123 | 7.39k | if (PREDICT_FALSE(!file.ReadU32(&priv_offset) || |
1124 | 7.39k | !file.ReadU32(&priv_length))) { |
1125 | 11 | return FONT_COMPRESSION_FAILURE(); |
1126 | 11 | } |
1127 | 7.38k | if (priv_offset) { |
1128 | 277 | if (PREDICT_FALSE( |
1129 | 277 | priv_offset >= length || length - priv_offset < priv_length)) { |
1130 | 105 | return FONT_COMPRESSION_FAILURE(); |
1131 | 105 | } |
1132 | 277 | } |
1133 | 7.27k | hdr->tables.resize(hdr->num_tables); |
1134 | 7.27k | if (PREDICT_FALSE(!ReadTableDirectory( |
1135 | 7.27k | &file, &hdr->tables, hdr->num_tables))) { |
1136 | 246 | return FONT_COMPRESSION_FAILURE(); |
1137 | 246 | } |
1138 | | |
1139 | | // Before we sort for output the last table end is the uncompressed size. |
1140 | 7.03k | Table& last_table = hdr->tables.back(); |
1141 | 7.03k | hdr->uncompressed_size = last_table.src_offset + last_table.src_length; |
1142 | 7.03k | if (PREDICT_FALSE(hdr->uncompressed_size < last_table.src_offset)) { |
1143 | 0 | return FONT_COMPRESSION_FAILURE(); |
1144 | 0 | } |
1145 | | |
1146 | 7.03k | hdr->header_version = 0; |
1147 | | |
1148 | 7.03k | if (hdr->flavor == kTtcFontFlavor) { |
1149 | 602 | if (PREDICT_FALSE(!file.ReadU32(&hdr->header_version))) { |
1150 | 13 | return FONT_COMPRESSION_FAILURE(); |
1151 | 13 | } |
1152 | 589 | if (PREDICT_FALSE(hdr->header_version != 0x00010000 |
1153 | 589 | && hdr->header_version != 0x00020000)) { |
1154 | 64 | return FONT_COMPRESSION_FAILURE(); |
1155 | 64 | } |
1156 | 525 | uint32_t num_fonts; |
1157 | 525 | if (PREDICT_FALSE(!Read255UShort(&file, &num_fonts) || !num_fonts)) { |
1158 | 8 | return FONT_COMPRESSION_FAILURE(); |
1159 | 8 | } |
1160 | 517 | hdr->ttc_fonts.resize(num_fonts); |
1161 | | |
1162 | 4.96k | for (uint32_t i = 0; i < num_fonts; i++) { |
1163 | 4.71k | TtcFont& ttc_font = hdr->ttc_fonts[i]; |
1164 | 4.71k | uint32_t num_tables; |
1165 | 4.71k | if (PREDICT_FALSE(!Read255UShort(&file, &num_tables) || !num_tables)) { |
1166 | 77 | return FONT_COMPRESSION_FAILURE(); |
1167 | 77 | } |
1168 | 4.63k | if (PREDICT_FALSE(!file.ReadU32(&ttc_font.flavor))) { |
1169 | 25 | return FONT_COMPRESSION_FAILURE(); |
1170 | 25 | } |
1171 | | |
1172 | 4.60k | ttc_font.table_indices.resize(num_tables); |
1173 | | |
1174 | | |
1175 | 4.60k | unsigned int glyf_idx = 0; |
1176 | 4.60k | unsigned int loca_idx = 0; |
1177 | | |
1178 | 163k | for (uint32_t j = 0; j < num_tables; j++) { |
1179 | 158k | unsigned int table_idx; |
1180 | 158k | if (PREDICT_FALSE(!Read255UShort(&file, &table_idx)) || |
1181 | 158k | table_idx >= hdr->tables.size()) { |
1182 | 138 | return FONT_COMPRESSION_FAILURE(); |
1183 | 138 | } |
1184 | 158k | ttc_font.table_indices[j] = table_idx; |
1185 | | |
1186 | 158k | const Table& table = hdr->tables[table_idx]; |
1187 | 158k | if (table.tag == kLocaTableTag) { |
1188 | 1.93k | loca_idx = table_idx; |
1189 | 1.93k | } |
1190 | 158k | if (table.tag == kGlyfTableTag) { |
1191 | 1.89k | glyf_idx = table_idx; |
1192 | 1.89k | } |
1193 | | |
1194 | 158k | } |
1195 | | |
1196 | | // if we have both glyf and loca make sure they are consecutive |
1197 | | // if we have just one we'll reject the font elsewhere |
1198 | 4.47k | if (glyf_idx > 0 || loca_idx > 0) { |
1199 | 253 | if (PREDICT_FALSE(glyf_idx > loca_idx || loca_idx - glyf_idx != 1)) { |
1200 | | #ifdef FONT_COMPRESSION_BIN |
1201 | | fprintf(stderr, "TTC font %d has non-consecutive glyf/loca\n", i); |
1202 | | #endif |
1203 | 21 | return FONT_COMPRESSION_FAILURE(); |
1204 | 21 | } |
1205 | 253 | } |
1206 | 4.47k | } |
1207 | 517 | } |
1208 | | |
1209 | 6.68k | const uint64_t first_table_offset = ComputeOffsetToFirstTable(*hdr); |
1210 | | |
1211 | 6.68k | hdr->compressed_offset = file.offset(); |
1212 | 6.68k | if (PREDICT_FALSE(hdr->compressed_offset > |
1213 | 6.68k | std::numeric_limits<uint32_t>::max())) { |
1214 | 0 | return FONT_COMPRESSION_FAILURE(); |
1215 | 0 | } |
1216 | 6.68k | uint64_t src_offset = Round4(hdr->compressed_offset + hdr->compressed_length); |
1217 | 6.68k | uint64_t dst_offset = first_table_offset; |
1218 | | |
1219 | | |
1220 | 6.68k | if (PREDICT_FALSE(src_offset > length)) { |
1221 | | #ifdef FONT_COMPRESSION_BIN |
1222 | | fprintf(stderr, "offset fail; src_offset %" PRIu64 " length %lu " |
1223 | | "dst_offset %" PRIu64 "\n", |
1224 | | src_offset, length, dst_offset); |
1225 | | #endif |
1226 | 146 | return FONT_COMPRESSION_FAILURE(); |
1227 | 146 | } |
1228 | 6.53k | if (meta_offset) { |
1229 | 65 | if (PREDICT_FALSE(src_offset != meta_offset)) { |
1230 | 38 | return FONT_COMPRESSION_FAILURE(); |
1231 | 38 | } |
1232 | 27 | src_offset = Round4(meta_offset + meta_length); |
1233 | 27 | if (PREDICT_FALSE(src_offset > std::numeric_limits<uint32_t>::max())) { |
1234 | 0 | return FONT_COMPRESSION_FAILURE(); |
1235 | 0 | } |
1236 | 27 | } |
1237 | | |
1238 | 6.50k | if (priv_offset) { |
1239 | 68 | if (PREDICT_FALSE(src_offset != priv_offset)) { |
1240 | 36 | return FONT_COMPRESSION_FAILURE(); |
1241 | 36 | } |
1242 | 32 | src_offset = Round4(priv_offset + priv_length); |
1243 | 32 | if (PREDICT_FALSE(src_offset > std::numeric_limits<uint32_t>::max())) { |
1244 | 0 | return FONT_COMPRESSION_FAILURE(); |
1245 | 0 | } |
1246 | 32 | } |
1247 | | |
1248 | 6.46k | if (PREDICT_FALSE(src_offset != Round4(length))) { |
1249 | 49 | return FONT_COMPRESSION_FAILURE(); |
1250 | 49 | } |
1251 | | |
1252 | 6.41k | return true; |
1253 | 6.46k | } |
1254 | | |
1255 | | // Write everything before the actual table data |
1256 | | bool WriteHeaders(const uint8_t* data, size_t length, RebuildMetadata* metadata, |
1257 | 6.41k | WOFF2Header* hdr, WOFF2Out* out) { |
1258 | 6.41k | std::vector<uint8_t> output(ComputeOffsetToFirstTable(*hdr), 0); |
1259 | | |
1260 | | // Re-order tables in output (OTSpec) order |
1261 | 6.41k | std::vector<Table> sorted_tables(hdr->tables); |
1262 | 6.41k | if (hdr->header_version) { |
1263 | | // collection; we have to sort the table offset vector in each font |
1264 | 3.15k | for (auto& ttc_font : hdr->ttc_fonts) { |
1265 | 3.15k | std::map<uint32_t, uint16_t> sorted_index_by_tag; |
1266 | 87.1k | for (auto table_index : ttc_font.table_indices) { |
1267 | 87.1k | sorted_index_by_tag[hdr->tables[table_index].tag] = table_index; |
1268 | 87.1k | } |
1269 | 3.15k | uint16_t index = 0; |
1270 | 23.1k | for (auto& i : sorted_index_by_tag) { |
1271 | 23.1k | ttc_font.table_indices[index++] = i.second; |
1272 | 23.1k | } |
1273 | 3.15k | } |
1274 | 6.16k | } else { |
1275 | | // non-collection; we can just sort the tables |
1276 | 6.16k | std::sort(sorted_tables.begin(), sorted_tables.end()); |
1277 | 6.16k | } |
1278 | | |
1279 | | // Start building the font |
1280 | 6.41k | uint8_t* result = &output[0]; |
1281 | 6.41k | size_t offset = 0; |
1282 | 6.41k | if (hdr->header_version) { |
1283 | | // TTC header |
1284 | 252 | offset = StoreU32(result, offset, hdr->flavor); // TAG TTCTag |
1285 | 252 | offset = StoreU32(result, offset, hdr->header_version); // FIXED Version |
1286 | 252 | offset = StoreU32(result, offset, hdr->ttc_fonts.size()); // ULONG numFonts |
1287 | | // Space for ULONG OffsetTable[numFonts] (zeroed initially) |
1288 | 252 | size_t offset_table = offset; // keep start of offset table for later |
1289 | 3.40k | for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) { |
1290 | 3.15k | offset = StoreU32(result, offset, 0); // will fill real values in later |
1291 | 3.15k | } |
1292 | | // space for DSIG fields for header v2 |
1293 | 252 | if (hdr->header_version == 0x00020000) { |
1294 | 154 | offset = StoreU32(result, offset, 0); // ULONG ulDsigTag |
1295 | 154 | offset = StoreU32(result, offset, 0); // ULONG ulDsigLength |
1296 | 154 | offset = StoreU32(result, offset, 0); // ULONG ulDsigOffset |
1297 | 154 | } |
1298 | | |
1299 | | // write Offset Tables and store the location of each in TTC Header |
1300 | 252 | metadata->font_infos.resize(hdr->ttc_fonts.size()); |
1301 | 3.40k | for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) { |
1302 | 3.15k | TtcFont& ttc_font = hdr->ttc_fonts[i]; |
1303 | | |
1304 | | // write Offset Table location into TTC Header |
1305 | 3.15k | offset_table = StoreU32(result, offset_table, offset); |
1306 | | |
1307 | | // write the actual offset table so our header doesn't lie |
1308 | 3.15k | ttc_font.dst_offset = offset; |
1309 | 3.15k | offset = StoreOffsetTable(result, offset, ttc_font.flavor, |
1310 | 3.15k | ttc_font.table_indices.size()); |
1311 | | |
1312 | 87.1k | for (const auto table_index : ttc_font.table_indices) { |
1313 | 87.1k | uint32_t tag = hdr->tables[table_index].tag; |
1314 | 87.1k | metadata->font_infos[i].table_entry_by_tag[tag] = offset; |
1315 | 87.1k | offset = StoreTableEntry(result, offset, tag); |
1316 | 87.1k | } |
1317 | | |
1318 | 3.15k | ttc_font.header_checksum = ComputeULongSum(&output[ttc_font.dst_offset], |
1319 | 3.15k | offset - ttc_font.dst_offset); |
1320 | 3.15k | } |
1321 | 6.16k | } else { |
1322 | 6.16k | metadata->font_infos.resize(1); |
1323 | 6.16k | offset = StoreOffsetTable(result, offset, hdr->flavor, hdr->num_tables); |
1324 | 1.04M | for (uint16_t i = 0; i < hdr->num_tables; ++i) { |
1325 | 1.03M | metadata->font_infos[0].table_entry_by_tag[sorted_tables[i].tag] = offset; |
1326 | 1.03M | offset = StoreTableEntry(result, offset, sorted_tables[i].tag); |
1327 | 1.03M | } |
1328 | 6.16k | } |
1329 | | |
1330 | 6.41k | if (PREDICT_FALSE(!out->Write(&output[0], output.size()))) { |
1331 | 0 | return FONT_COMPRESSION_FAILURE(); |
1332 | 0 | } |
1333 | 6.41k | metadata->header_checksum = ComputeULongSum(&output[0], output.size()); |
1334 | 6.41k | return true; |
1335 | 6.41k | } |
1336 | | |
1337 | | } // namespace |
1338 | | |
1339 | 0 | size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) { |
1340 | 0 | Buffer file(data, length); |
1341 | 0 | uint32_t total_length; |
1342 | |
|
1343 | 0 | if (!file.Skip(16) || |
1344 | 0 | !file.ReadU32(&total_length)) { |
1345 | 0 | return 0; |
1346 | 0 | } |
1347 | 0 | return total_length; |
1348 | 0 | } |
1349 | | |
1350 | | bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length, |
1351 | 0 | const uint8_t *data, size_t length) { |
1352 | 0 | WOFF2MemoryOut out(result, result_length); |
1353 | 0 | return ConvertWOFF2ToTTF(data, length, &out); |
1354 | 0 | } |
1355 | | |
1356 | | bool ConvertWOFF2ToTTF(const uint8_t* data, size_t length, |
1357 | 7.66k | WOFF2Out* out) { |
1358 | 7.66k | RebuildMetadata metadata; |
1359 | 7.66k | WOFF2Header hdr; |
1360 | 7.66k | if (!ReadWOFF2Header(data, length, &hdr)) { |
1361 | 1.25k | return FONT_COMPRESSION_FAILURE(); |
1362 | 1.25k | } |
1363 | | |
1364 | 6.41k | if (!WriteHeaders(data, length, &metadata, &hdr, out)) { |
1365 | 0 | return FONT_COMPRESSION_FAILURE(); |
1366 | 0 | } |
1367 | | |
1368 | 6.41k | const float compression_ratio = (float) hdr.uncompressed_size / length; |
1369 | 6.41k | if (compression_ratio > kMaxPlausibleCompressionRatio) { |
1370 | | #ifdef FONT_COMPRESSION_BIN |
1371 | | fprintf(stderr, "Implausible compression ratio %.01f\n", compression_ratio); |
1372 | | #endif |
1373 | 498 | return FONT_COMPRESSION_FAILURE(); |
1374 | 498 | } |
1375 | | |
1376 | 5.91k | const uint8_t* src_buf = data + hdr.compressed_offset; |
1377 | 5.91k | std::vector<uint8_t> uncompressed_buf(hdr.uncompressed_size); |
1378 | 5.91k | if (PREDICT_FALSE(hdr.uncompressed_size < 1)) { |
1379 | 63 | return FONT_COMPRESSION_FAILURE(); |
1380 | 63 | } |
1381 | 5.85k | if (PREDICT_FALSE(!Woff2Uncompress(&uncompressed_buf[0], |
1382 | 5.85k | hdr.uncompressed_size, src_buf, |
1383 | 5.85k | hdr.compressed_length))) { |
1384 | 4.58k | return FONT_COMPRESSION_FAILURE(); |
1385 | 4.58k | } |
1386 | | |
1387 | 1.97k | for (size_t i = 0; i < metadata.font_infos.size(); i++) { |
1388 | 1.59k | if (PREDICT_FALSE(!ReconstructFont(&uncompressed_buf[0], |
1389 | 1.59k | hdr.uncompressed_size, |
1390 | 1.59k | &metadata, &hdr, i, out))) { |
1391 | 885 | return FONT_COMPRESSION_FAILURE(); |
1392 | 885 | } |
1393 | 1.59k | } |
1394 | | |
1395 | 388 | return true; |
1396 | 1.27k | } |
1397 | | |
1398 | | } // namespace woff2 |