/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 | 43.1M | int WithSign(int flag, int baseval) { |
110 | | // Precondition: 0 <= baseval < 65536 (to avoid integer overflow) |
111 | 43.1M | return (flag & 1) ? baseval : -baseval; |
112 | 43.1M | } |
113 | | |
114 | 53.3M | bool _SafeIntAddition(int a, int b, int* result) { |
115 | 53.3M | if (PREDICT_FALSE( |
116 | 53.3M | ((a > 0) && (b > std::numeric_limits<int>::max() - a)) || |
117 | 53.3M | ((a < 0) && (b < std::numeric_limits<int>::min() - a)))) { |
118 | 68 | return false; |
119 | 68 | } |
120 | 53.3M | *result = a + b; |
121 | 53.3M | return true; |
122 | 53.3M | } |
123 | | |
124 | | bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, |
125 | 147k | unsigned int n_points, Point* result, size_t* in_bytes_consumed) { |
126 | 147k | int x = 0; |
127 | 147k | int y = 0; |
128 | | |
129 | 147k | if (PREDICT_FALSE(n_points > in_size)) { |
130 | 29 | return FONT_COMPRESSION_FAILURE(); |
131 | 29 | } |
132 | 147k | unsigned int triplet_index = 0; |
133 | | |
134 | 26.8M | for (unsigned int i = 0; i < n_points; ++i) { |
135 | 26.6M | uint8_t flag = flags_in[i]; |
136 | 26.6M | bool on_curve = !(flag >> 7); |
137 | 26.6M | flag &= 0x7f; |
138 | 26.6M | unsigned int n_data_bytes; |
139 | 26.6M | if (flag < 84) { |
140 | 18.7M | n_data_bytes = 1; |
141 | 18.7M | } else if (flag < 120) { |
142 | 2.72M | n_data_bytes = 2; |
143 | 5.19M | } else if (flag < 124) { |
144 | 324k | n_data_bytes = 3; |
145 | 4.87M | } else { |
146 | 4.87M | n_data_bytes = 4; |
147 | 4.87M | } |
148 | 26.6M | if (PREDICT_FALSE(triplet_index + n_data_bytes > in_size || |
149 | 26.6M | triplet_index + n_data_bytes < triplet_index)) { |
150 | 43 | return FONT_COMPRESSION_FAILURE(); |
151 | 43 | } |
152 | 26.6M | int dx, dy; |
153 | 26.6M | if (flag < 10) { |
154 | 8.39M | dx = 0; |
155 | 8.39M | dy = WithSign(flag, ((flag & 14) << 7) + in[triplet_index]); |
156 | 18.2M | } else if (flag < 20) { |
157 | 1.80M | dx = WithSign(flag, (((flag - 10) & 14) << 7) + in[triplet_index]); |
158 | 1.80M | dy = 0; |
159 | 16.4M | } else if (flag < 84) { |
160 | 8.57M | int b0 = flag - 20; |
161 | 8.57M | int b1 = in[triplet_index]; |
162 | 8.57M | dx = WithSign(flag, 1 + (b0 & 0x30) + (b1 >> 4)); |
163 | 8.57M | dy = WithSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f)); |
164 | 8.57M | } else if (flag < 120) { |
165 | 2.72M | int b0 = flag - 84; |
166 | 2.72M | dx = WithSign(flag, 1 + ((b0 / 12) << 8) + in[triplet_index]); |
167 | 2.72M | dy = WithSign(flag >> 1, |
168 | 2.72M | 1 + (((b0 % 12) >> 2) << 8) + in[triplet_index + 1]); |
169 | 5.19M | } else if (flag < 124) { |
170 | 324k | int b2 = in[triplet_index + 1]; |
171 | 324k | dx = WithSign(flag, (in[triplet_index] << 4) + (b2 >> 4)); |
172 | 324k | dy = WithSign(flag >> 1, ((b2 & 0x0f) << 8) + in[triplet_index + 2]); |
173 | 4.87M | } else { |
174 | 4.87M | dx = WithSign(flag, (in[triplet_index] << 8) + in[triplet_index + 1]); |
175 | 4.87M | dy = WithSign(flag >> 1, |
176 | 4.87M | (in[triplet_index + 2] << 8) + in[triplet_index + 3]); |
177 | 4.87M | } |
178 | 26.6M | triplet_index += n_data_bytes; |
179 | 26.6M | if (!_SafeIntAddition(x, dx, &x)) { |
180 | 34 | return false; |
181 | 34 | } |
182 | 26.6M | if (!_SafeIntAddition(y, dy, &y)) { |
183 | 34 | return false; |
184 | 34 | } |
185 | 26.6M | *result++ = {x, y, on_curve}; |
186 | 26.6M | } |
187 | 147k | *in_bytes_consumed = triplet_index; |
188 | 147k | return true; |
189 | 147k | } |
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 | 147k | 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 | 147k | unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 + |
200 | 147k | instruction_length; |
201 | 147k | int last_flag = -1; |
202 | 147k | int repeat_count = 0; |
203 | 147k | int last_x = 0; |
204 | 147k | int last_y = 0; |
205 | 147k | unsigned int x_bytes = 0; |
206 | 147k | unsigned int y_bytes = 0; |
207 | | |
208 | 10.6M | for (unsigned int i = 0; i < n_points; ++i) { |
209 | 10.4M | const Point& point = points[i]; |
210 | 10.4M | int flag = point.on_curve ? kGlyfOnCurve : 0; |
211 | 10.4M | if (has_overlap_bit && i == 0) { |
212 | 294 | flag |= kOverlapSimple; |
213 | 294 | } |
214 | | |
215 | 10.4M | int dx = point.x - last_x; |
216 | 10.4M | int dy = point.y - last_y; |
217 | 10.4M | if (dx == 0) { |
218 | 5.99M | flag |= kGlyfThisXIsSame; |
219 | 5.99M | } else if (dx > -256 && dx < 256) { |
220 | 3.53M | flag |= kGlyfXShort | (dx > 0 ? kGlyfThisXIsSame : 0); |
221 | 3.53M | x_bytes += 1; |
222 | 3.53M | } else { |
223 | 963k | x_bytes += 2; |
224 | 963k | } |
225 | 10.4M | if (dy == 0) { |
226 | 4.88M | flag |= kGlyfThisYIsSame; |
227 | 5.61M | } else if (dy > -256 && dy < 256) { |
228 | 3.98M | flag |= kGlyfYShort | (dy > 0 ? kGlyfThisYIsSame : 0); |
229 | 3.98M | y_bytes += 1; |
230 | 3.98M | } else { |
231 | 1.62M | y_bytes += 2; |
232 | 1.62M | } |
233 | | |
234 | 10.4M | if (flag == last_flag && repeat_count != 255) { |
235 | 3.83M | dst[flag_offset - 1] |= kGlyfRepeat; |
236 | 3.83M | repeat_count++; |
237 | 6.65M | } else { |
238 | 6.65M | if (repeat_count != 0) { |
239 | 899k | if (PREDICT_FALSE(flag_offset >= dst_size)) { |
240 | 0 | return FONT_COMPRESSION_FAILURE(); |
241 | 0 | } |
242 | 899k | dst[flag_offset++] = repeat_count; |
243 | 899k | } |
244 | 6.65M | if (PREDICT_FALSE(flag_offset >= dst_size)) { |
245 | 0 | return FONT_COMPRESSION_FAILURE(); |
246 | 0 | } |
247 | 6.65M | dst[flag_offset++] = flag; |
248 | 6.65M | repeat_count = 0; |
249 | 6.65M | } |
250 | 10.4M | last_x = point.x; |
251 | 10.4M | last_y = point.y; |
252 | 10.4M | last_flag = flag; |
253 | 10.4M | } |
254 | | |
255 | 147k | if (repeat_count != 0) { |
256 | 43.8k | if (PREDICT_FALSE(flag_offset >= dst_size)) { |
257 | 0 | return FONT_COMPRESSION_FAILURE(); |
258 | 0 | } |
259 | 43.8k | dst[flag_offset++] = repeat_count; |
260 | 43.8k | } |
261 | 147k | unsigned int xy_bytes = x_bytes + y_bytes; |
262 | 147k | if (PREDICT_FALSE(xy_bytes < x_bytes || |
263 | 147k | flag_offset + xy_bytes < flag_offset || |
264 | 147k | flag_offset + xy_bytes > dst_size)) { |
265 | 0 | return FONT_COMPRESSION_FAILURE(); |
266 | 0 | } |
267 | | |
268 | 147k | int x_offset = flag_offset; |
269 | 147k | int y_offset = flag_offset + x_bytes; |
270 | 147k | last_x = 0; |
271 | 147k | last_y = 0; |
272 | 10.6M | for (unsigned int i = 0; i < n_points; ++i) { |
273 | 10.4M | int dx = points[i].x - last_x; |
274 | 10.4M | if (dx == 0) { |
275 | | // pass |
276 | 5.99M | } else if (dx > -256 && dx < 256) { |
277 | 3.53M | dst[x_offset++] = std::abs(dx); |
278 | 3.53M | } else { |
279 | | // will always fit for valid input, but overflow is harmless |
280 | 963k | x_offset = Store16(dst, x_offset, dx); |
281 | 963k | } |
282 | 10.4M | last_x += dx; |
283 | 10.4M | int dy = points[i].y - last_y; |
284 | 10.4M | if (dy == 0) { |
285 | | // pass |
286 | 5.61M | } else if (dy > -256 && dy < 256) { |
287 | 3.98M | dst[y_offset++] = std::abs(dy); |
288 | 3.98M | } else { |
289 | 1.62M | y_offset = Store16(dst, y_offset, dy); |
290 | 1.62M | } |
291 | 10.4M | last_y += dy; |
292 | 10.4M | } |
293 | 147k | *glyph_size = y_offset; |
294 | 147k | return true; |
295 | 147k | } |
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 | 146k | void ComputeBbox(unsigned int n_points, const Point* points, uint8_t* dst) { |
301 | 146k | int x_min = 0; |
302 | 146k | int y_min = 0; |
303 | 146k | int x_max = 0; |
304 | 146k | int y_max = 0; |
305 | | |
306 | 146k | if (n_points > 0) { |
307 | 141k | x_min = points[0].x; |
308 | 141k | x_max = points[0].x; |
309 | 141k | y_min = points[0].y; |
310 | 141k | y_max = points[0].y; |
311 | 141k | } |
312 | 13.1M | for (unsigned int i = 1; i < n_points; ++i) { |
313 | 13.0M | int x = points[i].x; |
314 | 13.0M | int y = points[i].y; |
315 | 13.0M | x_min = std::min(x, x_min); |
316 | 13.0M | x_max = std::max(x, x_max); |
317 | 13.0M | y_min = std::min(y, y_min); |
318 | 13.0M | y_max = std::max(y, y_max); |
319 | 13.0M | } |
320 | 146k | size_t offset = 2; |
321 | 146k | offset = Store16(dst, offset, x_min); |
322 | 146k | offset = Store16(dst, offset, y_min); |
323 | 146k | offset = Store16(dst, offset, x_max); |
324 | 146k | offset = Store16(dst, offset, y_max); |
325 | 146k | } |
326 | | |
327 | | |
328 | | bool SizeOfComposite(Buffer composite_stream, size_t* size, |
329 | 1.33k | bool* have_instructions) { |
330 | 1.33k | size_t start_offset = composite_stream.offset(); |
331 | 1.33k | bool we_have_instructions = false; |
332 | | |
333 | 1.33k | uint16_t flags = FLAG_MORE_COMPONENTS; |
334 | 9.08k | while (flags & FLAG_MORE_COMPONENTS) { |
335 | 7.78k | if (PREDICT_FALSE(!composite_stream.ReadU16(&flags))) { |
336 | 11 | return FONT_COMPRESSION_FAILURE(); |
337 | 11 | } |
338 | 7.77k | we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0; |
339 | 7.77k | size_t arg_size = 2; // glyph index |
340 | 7.77k | if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) { |
341 | 3.41k | arg_size += 4; |
342 | 4.35k | } else { |
343 | 4.35k | arg_size += 2; |
344 | 4.35k | } |
345 | 7.77k | if (flags & FLAG_WE_HAVE_A_SCALE) { |
346 | 2.91k | arg_size += 2; |
347 | 4.85k | } else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) { |
348 | 1.09k | arg_size += 4; |
349 | 3.76k | } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) { |
350 | 744 | arg_size += 8; |
351 | 744 | } |
352 | 7.77k | if (PREDICT_FALSE(!composite_stream.Skip(arg_size))) { |
353 | 23 | return FONT_COMPRESSION_FAILURE(); |
354 | 23 | } |
355 | 7.77k | } |
356 | | |
357 | 1.30k | *size = composite_stream.offset() - start_offset; |
358 | 1.30k | *have_instructions = we_have_instructions; |
359 | | |
360 | 1.30k | return true; |
361 | 1.33k | } |
362 | | |
363 | 191k | bool Pad4(WOFF2Out* out) { |
364 | 191k | uint8_t zeroes[] = {0, 0, 0}; |
365 | 191k | if (PREDICT_FALSE(out->Size() + 3 < out->Size())) { |
366 | 0 | return FONT_COMPRESSION_FAILURE(); |
367 | 0 | } |
368 | 191k | uint32_t pad_bytes = Round4(out->Size()) - out->Size(); |
369 | 191k | if (pad_bytes > 0) { |
370 | 112k | if (PREDICT_FALSE(!out->Write(&zeroes, pad_bytes))) { |
371 | 0 | return FONT_COMPRESSION_FAILURE(); |
372 | 0 | } |
373 | 112k | } |
374 | 191k | return true; |
375 | 191k | } |
376 | | |
377 | | // Build TrueType loca table |
378 | | bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format, |
379 | 151 | 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 | 151 | const uint64_t loca_size = loca_values.size(); |
383 | 151 | const uint64_t offset_size = index_format ? 4 : 2; |
384 | 151 | if (PREDICT_FALSE((loca_size << 2) >> 2 != loca_size)) { |
385 | 0 | return FONT_COMPRESSION_FAILURE(); |
386 | 0 | } |
387 | 151 | std::vector<uint8_t> loca_content(loca_size * offset_size); |
388 | 151 | uint8_t* dst = &loca_content[0]; |
389 | 151 | size_t offset = 0; |
390 | 124k | for (size_t i = 0; i < loca_values.size(); ++i) { |
391 | 124k | uint32_t value = loca_values[i]; |
392 | 124k | if (index_format) { |
393 | 95.4k | offset = StoreU32(dst, offset, value); |
394 | 95.4k | } else { |
395 | 29.1k | offset = Store16(dst, offset, value >> 1); |
396 | 29.1k | } |
397 | 124k | } |
398 | 151 | *checksum = ComputeULongSum(&loca_content[0], loca_content.size()); |
399 | 151 | if (PREDICT_FALSE(!out->Write(&loca_content[0], loca_content.size()))) { |
400 | 0 | return FONT_COMPRESSION_FAILURE(); |
401 | 0 | } |
402 | 151 | return true; |
403 | 151 | } |
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 | 1.29k | WOFF2Out* out) { |
410 | 1.29k | static const int kNumSubStreams = 7; |
411 | 1.29k | Buffer file(data, glyf_table->transform_length); |
412 | 1.29k | uint16_t version; |
413 | 1.29k | std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams); |
414 | 1.29k | const size_t glyf_start = out->Size(); |
415 | | |
416 | 1.29k | if (PREDICT_FALSE(!file.ReadU16(&version))) { |
417 | 13 | return FONT_COMPRESSION_FAILURE(); |
418 | 13 | } |
419 | | |
420 | 1.28k | uint16_t flags; |
421 | 1.28k | if (PREDICT_FALSE(!file.ReadU16(&flags))) { |
422 | 3 | return FONT_COMPRESSION_FAILURE(); |
423 | 3 | } |
424 | 1.28k | bool has_overlap_bitmap = (flags & FLAG_OVERLAP_SIMPLE_BITMAP); |
425 | | |
426 | 1.28k | if (PREDICT_FALSE(!file.ReadU16(&info->num_glyphs) || |
427 | 1.28k | !file.ReadU16(&info->index_format))) { |
428 | 37 | return FONT_COMPRESSION_FAILURE(); |
429 | 37 | } |
430 | | |
431 | | // https://dev.w3.org/webfonts/WOFF2/spec/#conform-mustRejectLoca |
432 | | // dst_length here is origLength in the spec |
433 | 1.24k | uint32_t expected_loca_dst_length = (info->index_format ? 4 : 2) |
434 | 1.24k | * (static_cast<uint32_t>(info->num_glyphs) + 1); |
435 | 1.24k | if (PREDICT_FALSE(loca_table->dst_length != expected_loca_dst_length)) { |
436 | 110 | return FONT_COMPRESSION_FAILURE(); |
437 | 110 | } |
438 | | |
439 | 1.13k | unsigned int offset = (2 + kNumSubStreams) * 4; |
440 | 1.13k | if (PREDICT_FALSE(offset > glyf_table->transform_length)) { |
441 | 3 | return FONT_COMPRESSION_FAILURE(); |
442 | 3 | } |
443 | | // Invariant from here on: data_size >= offset |
444 | 8.47k | for (int i = 0; i < kNumSubStreams; ++i) { |
445 | 7.43k | uint32_t substream_size; |
446 | 7.43k | if (PREDICT_FALSE(!file.ReadU32(&substream_size))) { |
447 | 0 | return FONT_COMPRESSION_FAILURE(); |
448 | 0 | } |
449 | 7.43k | if (PREDICT_FALSE(substream_size > glyf_table->transform_length - offset)) { |
450 | 88 | return FONT_COMPRESSION_FAILURE(); |
451 | 88 | } |
452 | 7.34k | substreams[i] = std::make_pair(data + offset, substream_size); |
453 | 7.34k | offset += substream_size; |
454 | 7.34k | } |
455 | 1.04k | Buffer n_contour_stream(substreams[0].first, substreams[0].second); |
456 | 1.04k | Buffer n_points_stream(substreams[1].first, substreams[1].second); |
457 | 1.04k | Buffer flag_stream(substreams[2].first, substreams[2].second); |
458 | 1.04k | Buffer glyph_stream(substreams[3].first, substreams[3].second); |
459 | 1.04k | Buffer composite_stream(substreams[4].first, substreams[4].second); |
460 | 1.04k | Buffer bbox_stream(substreams[5].first, substreams[5].second); |
461 | 1.04k | Buffer instruction_stream(substreams[6].first, substreams[6].second); |
462 | | |
463 | 1.04k | const uint8_t* overlap_bitmap = nullptr; |
464 | 1.04k | unsigned int overlap_bitmap_length = 0; |
465 | 1.04k | if (has_overlap_bitmap) { |
466 | 351 | overlap_bitmap_length = (info->num_glyphs + 7) >> 3; |
467 | 351 | overlap_bitmap = data + offset; |
468 | 351 | if (PREDICT_FALSE(overlap_bitmap_length > |
469 | 351 | glyf_table->transform_length - offset)) { |
470 | 11 | return FONT_COMPRESSION_FAILURE(); |
471 | 11 | } |
472 | 351 | } |
473 | | |
474 | 1.03k | std::vector<uint32_t> loca_values(info->num_glyphs + 1); |
475 | 1.03k | std::vector<unsigned int> n_points_vec; |
476 | 1.03k | std::unique_ptr<Point[]> points; |
477 | 1.03k | size_t points_size = 0; |
478 | 1.03k | const uint8_t* bbox_bitmap = bbox_stream.buffer(); |
479 | | // Safe because num_glyphs is bounded |
480 | 1.03k | unsigned int bitmap_length = ((info->num_glyphs + 31) >> 5) << 2; |
481 | 1.03k | if (!bbox_stream.Skip(bitmap_length)) { |
482 | 3 | return FONT_COMPRESSION_FAILURE(); |
483 | 3 | } |
484 | | |
485 | | // Temp buffer for glyph's. |
486 | 1.02k | size_t glyph_buf_size = kDefaultGlyphBuf; |
487 | 1.02k | std::unique_ptr<uint8_t[]> glyph_buf(new uint8_t[glyph_buf_size]); |
488 | | |
489 | 1.02k | info->x_mins.resize(info->num_glyphs); |
490 | 174k | for (unsigned int i = 0; i < info->num_glyphs; ++i) { |
491 | 174k | size_t glyph_size = 0; |
492 | 174k | uint16_t n_contours = 0; |
493 | 174k | bool have_bbox = false; |
494 | 174k | if (bbox_bitmap[i >> 3] & (0x80 >> (i & 7))) { |
495 | 2.58k | have_bbox = true; |
496 | 2.58k | } |
497 | 174k | if (PREDICT_FALSE(!n_contour_stream.ReadU16(&n_contours))) { |
498 | 20 | return FONT_COMPRESSION_FAILURE(); |
499 | 20 | } |
500 | | |
501 | 174k | if (n_contours == 0xffff) { |
502 | | // composite glyph |
503 | 1.39k | bool have_instructions = false; |
504 | 1.39k | unsigned int instruction_size = 0; |
505 | 1.39k | if (PREDICT_FALSE(!have_bbox)) { |
506 | | // composite glyphs must have an explicit bbox |
507 | 64 | return FONT_COMPRESSION_FAILURE(); |
508 | 64 | } |
509 | | |
510 | 1.33k | size_t composite_size; |
511 | 1.33k | if (PREDICT_FALSE(!SizeOfComposite(composite_stream, &composite_size, |
512 | 1.33k | &have_instructions))) { |
513 | 34 | return FONT_COMPRESSION_FAILURE(); |
514 | 34 | } |
515 | 1.30k | if (have_instructions) { |
516 | 426 | if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { |
517 | 2 | return FONT_COMPRESSION_FAILURE(); |
518 | 2 | } |
519 | 426 | } |
520 | | |
521 | 1.29k | size_t size_needed = 12 + composite_size + instruction_size; |
522 | 1.29k | if (PREDICT_FALSE(glyph_buf_size < size_needed)) { |
523 | 12 | glyph_buf.reset(new uint8_t[size_needed]); |
524 | 12 | glyph_buf_size = size_needed; |
525 | 12 | } |
526 | | |
527 | 1.29k | glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); |
528 | 1.29k | if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { |
529 | 21 | return FONT_COMPRESSION_FAILURE(); |
530 | 21 | } |
531 | 1.27k | glyph_size += 8; |
532 | | |
533 | 1.27k | if (PREDICT_FALSE(!composite_stream.Read(glyph_buf.get() + glyph_size, |
534 | 1.27k | composite_size))) { |
535 | 0 | return FONT_COMPRESSION_FAILURE(); |
536 | 0 | } |
537 | 1.27k | glyph_size += composite_size; |
538 | 1.27k | if (have_instructions) { |
539 | 411 | glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); |
540 | 411 | if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, |
541 | 411 | instruction_size))) { |
542 | 59 | return FONT_COMPRESSION_FAILURE(); |
543 | 59 | } |
544 | 352 | glyph_size += instruction_size; |
545 | 352 | } |
546 | 173k | } else if (n_contours > 0) { |
547 | | // simple glyph |
548 | 147k | n_points_vec.clear(); |
549 | 147k | unsigned int total_n_points = 0; |
550 | 147k | unsigned int n_points_contour; |
551 | 1.36M | for (unsigned int j = 0; j < n_contours; ++j) { |
552 | 1.21M | if (PREDICT_FALSE( |
553 | 1.21M | !Read255UShort(&n_points_stream, &n_points_contour))) { |
554 | 180 | return FONT_COMPRESSION_FAILURE(); |
555 | 180 | } |
556 | 1.21M | n_points_vec.push_back(n_points_contour); |
557 | 1.21M | if (PREDICT_FALSE(total_n_points + n_points_contour < total_n_points)) { |
558 | 0 | return FONT_COMPRESSION_FAILURE(); |
559 | 0 | } |
560 | 1.21M | total_n_points += n_points_contour; |
561 | 1.21M | } |
562 | 147k | unsigned int flag_size = total_n_points; |
563 | 147k | if (PREDICT_FALSE( |
564 | 147k | flag_size > flag_stream.length() - flag_stream.offset())) { |
565 | 120 | return FONT_COMPRESSION_FAILURE(); |
566 | 120 | } |
567 | 147k | const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset(); |
568 | 147k | const uint8_t* triplet_buf = glyph_stream.buffer() + |
569 | 147k | glyph_stream.offset(); |
570 | 147k | size_t triplet_size = glyph_stream.length() - glyph_stream.offset(); |
571 | 147k | size_t triplet_bytes_consumed = 0; |
572 | 147k | if (points_size < total_n_points) { |
573 | 2.42k | points_size = total_n_points; |
574 | 2.42k | points.reset(new Point[points_size]); |
575 | 2.42k | } |
576 | 147k | if (PREDICT_FALSE(!TripletDecode(flags_buf, triplet_buf, triplet_size, |
577 | 147k | total_n_points, points.get(), &triplet_bytes_consumed))) { |
578 | 140 | return FONT_COMPRESSION_FAILURE(); |
579 | 140 | } |
580 | 147k | if (PREDICT_FALSE(!flag_stream.Skip(flag_size))) { |
581 | 0 | return FONT_COMPRESSION_FAILURE(); |
582 | 0 | } |
583 | 147k | if (PREDICT_FALSE(!glyph_stream.Skip(triplet_bytes_consumed))) { |
584 | 0 | return FONT_COMPRESSION_FAILURE(); |
585 | 0 | } |
586 | 147k | unsigned int instruction_size; |
587 | 147k | if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { |
588 | 20 | return FONT_COMPRESSION_FAILURE(); |
589 | 20 | } |
590 | | |
591 | 147k | if (PREDICT_FALSE(total_n_points >= (1 << 27) |
592 | 147k | || instruction_size >= (1 << 30))) { |
593 | 0 | return FONT_COMPRESSION_FAILURE(); |
594 | 0 | } |
595 | 147k | size_t size_needed = 12 + 2 * n_contours + 5 * total_n_points |
596 | 147k | + instruction_size; |
597 | 147k | if (PREDICT_FALSE(glyph_buf_size < size_needed)) { |
598 | 364 | glyph_buf.reset(new uint8_t[size_needed]); |
599 | 364 | glyph_buf_size = size_needed; |
600 | 364 | } |
601 | | |
602 | 147k | glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); |
603 | 147k | if (have_bbox) { |
604 | 1.15k | if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { |
605 | 38 | return FONT_COMPRESSION_FAILURE(); |
606 | 38 | } |
607 | 146k | } else { |
608 | 146k | ComputeBbox(total_n_points, points.get(), glyph_buf.get()); |
609 | 146k | } |
610 | 147k | glyph_size = kEndPtsOfContoursOffset; |
611 | 147k | int end_point = -1; |
612 | 567k | for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) { |
613 | 420k | end_point += n_points_vec[contour_ix]; |
614 | 420k | if (PREDICT_FALSE(end_point >= 65536)) { |
615 | 13 | return FONT_COMPRESSION_FAILURE(); |
616 | 13 | } |
617 | 420k | glyph_size = Store16(glyph_buf.get(), glyph_size, end_point); |
618 | 420k | } |
619 | | |
620 | 147k | glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); |
621 | 147k | if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, |
622 | 147k | instruction_size))) { |
623 | 150 | return FONT_COMPRESSION_FAILURE(); |
624 | 150 | } |
625 | 147k | glyph_size += instruction_size; |
626 | | |
627 | 147k | bool has_overlap_bit = |
628 | 147k | has_overlap_bitmap && overlap_bitmap[i >> 3] & (0x80 >> (i & 7)); |
629 | | |
630 | 147k | if (PREDICT_FALSE(!StorePoints( |
631 | 147k | total_n_points, points.get(), n_contours, instruction_size, |
632 | 147k | has_overlap_bit, glyph_buf.get(), glyph_buf_size, &glyph_size))) { |
633 | 0 | return FONT_COMPRESSION_FAILURE(); |
634 | 0 | } |
635 | 147k | } else { |
636 | | // n_contours == 0; empty glyph. Must NOT have a bbox. |
637 | 25.3k | if (PREDICT_FALSE(have_bbox)) { |
638 | | #ifdef FONT_COMPRESSION_BIN |
639 | | fprintf(stderr, "Empty glyph has a bbox\n"); |
640 | | #endif |
641 | 17 | return FONT_COMPRESSION_FAILURE(); |
642 | 17 | } |
643 | 25.3k | } |
644 | | |
645 | 173k | loca_values[i] = out->Size() - glyf_start; |
646 | 173k | 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 | 173k | if (PREDICT_FALSE(!Pad4(out))) { |
652 | 0 | return FONT_COMPRESSION_FAILURE(); |
653 | 0 | } |
654 | | |
655 | 173k | *glyf_checksum += ComputeULongSum(glyph_buf.get(), glyph_size); |
656 | | |
657 | | // We may need x_min to reconstruct 'hmtx' |
658 | 173k | if (n_contours > 0) { |
659 | 148k | Buffer x_min_buf(glyph_buf.get() + 2, 2); |
660 | 148k | if (PREDICT_FALSE(!x_min_buf.ReadS16(&info->x_mins[i]))) { |
661 | 0 | return FONT_COMPRESSION_FAILURE(); |
662 | 0 | } |
663 | 148k | } |
664 | 173k | } |
665 | | |
666 | | // glyf_table dst_offset was set by ReconstructFont |
667 | 151 | glyf_table->dst_length = out->Size() - glyf_table->dst_offset; |
668 | 151 | loca_table->dst_offset = out->Size(); |
669 | | // loca[n] will be equal the length of the glyph data ('glyf') table |
670 | 151 | loca_values[info->num_glyphs] = glyf_table->dst_length; |
671 | 151 | if (PREDICT_FALSE(!StoreLoca(loca_values, info->index_format, loca_checksum, |
672 | 151 | out))) { |
673 | 0 | return FONT_COMPRESSION_FAILURE(); |
674 | 0 | } |
675 | 151 | loca_table->dst_length = out->Size() - loca_table->dst_offset; |
676 | | |
677 | 151 | return true; |
678 | 151 | } |
679 | | |
680 | 8.97k | Table* FindTable(std::vector<Table*>* tables, uint32_t tag) { |
681 | 57.6k | for (Table* table : *tables) { |
682 | 57.6k | if (table->tag == tag) { |
683 | 4.32k | return table; |
684 | 4.32k | } |
685 | 57.6k | } |
686 | 4.65k | return NULL; |
687 | 8.97k | } |
688 | | |
689 | | // Get numberOfHMetrics, https://www.microsoft.com/typography/otspec/hhea.htm |
690 | | bool ReadNumHMetrics(const uint8_t* data, size_t data_size, |
691 | 876 | uint16_t* num_hmetrics) { |
692 | | // Skip 34 to reach 'hhea' numberOfHMetrics |
693 | 876 | Buffer buffer(data, data_size); |
694 | 876 | if (PREDICT_FALSE(!buffer.Skip(34) || !buffer.ReadU16(num_hmetrics))) { |
695 | 15 | return FONT_COMPRESSION_FAILURE(); |
696 | 15 | } |
697 | 861 | return true; |
698 | 876 | } |
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 | 121 | WOFF2Out* out) { |
708 | 121 | Buffer hmtx_buff_in(transformed_buf, transformed_size); |
709 | | |
710 | 121 | uint8_t hmtx_flags; |
711 | 121 | if (PREDICT_FALSE(!hmtx_buff_in.ReadU8(&hmtx_flags))) { |
712 | 2 | return FONT_COMPRESSION_FAILURE(); |
713 | 2 | } |
714 | | |
715 | 119 | std::vector<uint16_t> advance_widths; |
716 | 119 | std::vector<int16_t> lsbs; |
717 | 119 | bool has_proportional_lsbs = (hmtx_flags & 1) == 0; |
718 | 119 | bool has_monospace_lsbs = (hmtx_flags & 2) == 0; |
719 | | |
720 | | // Bits 2-7 are reserved and MUST be zero. |
721 | 119 | 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 | 6 | return FONT_COMPRESSION_FAILURE(); |
726 | 6 | } |
727 | | |
728 | | // you say you transformed but there is little evidence of it |
729 | 113 | if (has_proportional_lsbs && has_monospace_lsbs) { |
730 | 15 | return FONT_COMPRESSION_FAILURE(); |
731 | 15 | } |
732 | | |
733 | 98 | assert(x_mins.size() == num_glyphs); |
734 | | |
735 | | // num_glyphs 0 is OK if there is no 'glyf' but cannot then xform 'hmtx'. |
736 | 98 | if (PREDICT_FALSE(num_hmetrics > num_glyphs)) { |
737 | 33 | return FONT_COMPRESSION_FAILURE(); |
738 | 33 | } |
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 | 65 | if (PREDICT_FALSE(num_hmetrics < 1)) { |
743 | 5 | return FONT_COMPRESSION_FAILURE(); |
744 | 5 | } |
745 | | |
746 | 338 | for (uint16_t i = 0; i < num_hmetrics; i++) { |
747 | 280 | uint16_t advance_width; |
748 | 280 | if (PREDICT_FALSE(!hmtx_buff_in.ReadU16(&advance_width))) { |
749 | 2 | return FONT_COMPRESSION_FAILURE(); |
750 | 2 | } |
751 | 278 | advance_widths.push_back(advance_width); |
752 | 278 | } |
753 | | |
754 | 324 | for (uint16_t i = 0; i < num_hmetrics; i++) { |
755 | 268 | int16_t lsb; |
756 | 268 | if (has_proportional_lsbs) { |
757 | 116 | if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) { |
758 | 2 | return FONT_COMPRESSION_FAILURE(); |
759 | 2 | } |
760 | 152 | } else { |
761 | 152 | lsb = x_mins[i]; |
762 | 152 | } |
763 | 266 | lsbs.push_back(lsb); |
764 | 266 | } |
765 | | |
766 | 290 | for (uint16_t i = num_hmetrics; i < num_glyphs; i++) { |
767 | 239 | int16_t lsb; |
768 | 239 | if (has_monospace_lsbs) { |
769 | 118 | if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) { |
770 | 5 | return FONT_COMPRESSION_FAILURE(); |
771 | 5 | } |
772 | 121 | } else { |
773 | 121 | lsb = x_mins[i]; |
774 | 121 | } |
775 | 234 | lsbs.push_back(lsb); |
776 | 234 | } |
777 | | |
778 | | // bake me a shiny new hmtx table |
779 | 51 | uint32_t hmtx_output_size = 2 * num_glyphs + 2 * num_hmetrics; |
780 | 51 | std::vector<uint8_t> hmtx_table(hmtx_output_size); |
781 | 51 | uint8_t* dst = &hmtx_table[0]; |
782 | 51 | size_t dst_offset = 0; |
783 | 529 | for (uint32_t i = 0; i < num_glyphs; i++) { |
784 | 478 | if (i < num_hmetrics) { |
785 | 251 | Store16(advance_widths[i], &dst_offset, dst); |
786 | 251 | } |
787 | 478 | Store16(lsbs[i], &dst_offset, dst); |
788 | 478 | } |
789 | | |
790 | 51 | *checksum = ComputeULongSum(&hmtx_table[0], hmtx_output_size); |
791 | 51 | if (PREDICT_FALSE(!out->Write(&hmtx_table[0], hmtx_output_size))) { |
792 | 0 | return FONT_COMPRESSION_FAILURE(); |
793 | 0 | } |
794 | | |
795 | 51 | return true; |
796 | 51 | } |
797 | | |
798 | | bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size, |
799 | 10.7k | const uint8_t* src_buf, size_t src_size) { |
800 | 10.7k | size_t uncompressed_size = dst_size; |
801 | 10.7k | BrotliDecoderResult result = BrotliDecoderDecompress( |
802 | 10.7k | src_size, src_buf, &uncompressed_size, dst_buf); |
803 | 10.7k | if (PREDICT_FALSE(result != BROTLI_DECODER_RESULT_SUCCESS || |
804 | 10.7k | uncompressed_size != dst_size)) { |
805 | 8.41k | return FONT_COMPRESSION_FAILURE(); |
806 | 8.41k | } |
807 | 2.32k | return true; |
808 | 10.7k | } |
809 | | |
810 | | bool ReadTableDirectory(Buffer* file, std::vector<Table>* tables, |
811 | 14.2k | size_t num_tables) { |
812 | 14.2k | uint32_t src_offset = 0; |
813 | 1.62M | for (size_t i = 0; i < num_tables; ++i) { |
814 | 1.60M | Table* table = &(*tables)[i]; |
815 | 1.60M | uint8_t flag_byte; |
816 | 1.60M | if (PREDICT_FALSE(!file->ReadU8(&flag_byte))) { |
817 | 176 | return FONT_COMPRESSION_FAILURE(); |
818 | 176 | } |
819 | 1.60M | uint32_t tag; |
820 | 1.60M | if ((flag_byte & 0x3f) == 0x3f) { |
821 | 16.9k | if (PREDICT_FALSE(!file->ReadU32(&tag))) { |
822 | 21 | return FONT_COMPRESSION_FAILURE(); |
823 | 21 | } |
824 | 1.59M | } else { |
825 | 1.59M | tag = kKnownTags[flag_byte & 0x3f]; |
826 | 1.59M | } |
827 | 1.60M | uint32_t flags = 0; |
828 | 1.60M | uint8_t xform_version = (flag_byte >> 6) & 0x03; |
829 | | |
830 | | // 0 means xform for glyph/loca, non-0 for others |
831 | 1.60M | if (tag == kGlyfTableTag || tag == kLocaTableTag) { |
832 | 28.4k | if (xform_version == 0) { |
833 | 10.9k | flags |= kWoff2FlagsTransform; |
834 | 10.9k | } |
835 | 1.58M | } else if (xform_version != 0) { |
836 | 127k | flags |= kWoff2FlagsTransform; |
837 | 127k | } |
838 | 1.60M | flags |= xform_version; |
839 | | |
840 | 1.60M | uint32_t dst_length; |
841 | 1.60M | if (PREDICT_FALSE(!ReadBase128(file, &dst_length))) { |
842 | 200 | return FONT_COMPRESSION_FAILURE(); |
843 | 200 | } |
844 | 1.60M | uint32_t transform_length = dst_length; |
845 | 1.60M | if ((flags & kWoff2FlagsTransform) != 0) { |
846 | 138k | if (PREDICT_FALSE(!ReadBase128(file, &transform_length))) { |
847 | 37 | return FONT_COMPRESSION_FAILURE(); |
848 | 37 | } |
849 | 138k | if (PREDICT_FALSE(tag == kLocaTableTag && transform_length)) { |
850 | 120 | return FONT_COMPRESSION_FAILURE(); |
851 | 120 | } |
852 | 138k | } |
853 | 1.60M | if (PREDICT_FALSE(src_offset + transform_length < src_offset)) { |
854 | 5 | return FONT_COMPRESSION_FAILURE(); |
855 | 5 | } |
856 | 1.60M | table->src_offset = src_offset; |
857 | 1.60M | table->src_length = transform_length; |
858 | 1.60M | src_offset += transform_length; |
859 | | |
860 | 1.60M | table->tag = tag; |
861 | 1.60M | table->flags = flags; |
862 | 1.60M | table->transform_length = transform_length; |
863 | 1.60M | table->dst_length = dst_length; |
864 | 1.60M | } |
865 | 13.6k | return true; |
866 | 14.2k | } |
867 | | |
868 | | // Writes a single Offset Table entry |
869 | | size_t StoreOffsetTable(uint8_t* result, size_t offset, uint32_t flavor, |
870 | 18.3k | uint16_t num_tables) { |
871 | 18.3k | offset = StoreU32(result, offset, flavor); // sfnt version |
872 | 18.3k | offset = Store16(result, offset, num_tables); // num_tables |
873 | 18.3k | unsigned max_pow2 = 0; |
874 | 67.7k | while (1u << (max_pow2 + 1) <= num_tables) { |
875 | 49.4k | max_pow2++; |
876 | 49.4k | } |
877 | 18.3k | const uint16_t output_search_range = (1u << max_pow2) << 4; |
878 | 18.3k | offset = Store16(result, offset, output_search_range); // searchRange |
879 | 18.3k | offset = Store16(result, offset, max_pow2); // entrySelector |
880 | | // rangeShift |
881 | 18.3k | offset = Store16(result, offset, (num_tables << 4) - output_search_range); |
882 | 18.3k | return offset; |
883 | 18.3k | } |
884 | | |
885 | 645k | size_t StoreTableEntry(uint8_t* result, uint32_t offset, uint32_t tag) { |
886 | 645k | offset = StoreU32(result, offset, tag); |
887 | 645k | offset = StoreU32(result, offset, 0); |
888 | 645k | offset = StoreU32(result, offset, 0); |
889 | 645k | offset = StoreU32(result, offset, 0); |
890 | 645k | return offset; |
891 | 645k | } |
892 | | |
893 | | // First table goes after all the headers, table directory, etc |
894 | 25.4k | uint64_t ComputeOffsetToFirstTable(const WOFF2Header& hdr) { |
895 | 25.4k | uint64_t offset = kSfntHeaderSize + |
896 | 25.4k | kSfntEntrySize * static_cast<uint64_t>(hdr.num_tables); |
897 | 25.4k | if (hdr.header_version) { |
898 | 1.02k | offset = CollectionHeaderSize(hdr.header_version, hdr.ttc_fonts.size()) |
899 | 1.02k | + kSfntHeaderSize * hdr.ttc_fonts.size(); |
900 | 14.9k | for (const auto& ttc_font : hdr.ttc_fonts) { |
901 | 14.9k | offset += kSfntEntrySize * ttc_font.table_indices.size(); |
902 | 14.9k | } |
903 | 1.02k | } |
904 | 25.4k | return offset; |
905 | 25.4k | } |
906 | | |
907 | 3.11k | std::vector<Table*> Tables(WOFF2Header* hdr, size_t font_index) { |
908 | 3.11k | std::vector<Table*> tables; |
909 | 3.11k | if (PREDICT_FALSE(hdr->header_version)) { |
910 | 5.77k | for (auto index : hdr->ttc_fonts[font_index].table_indices) { |
911 | 5.77k | tables.push_back(&hdr->tables[index]); |
912 | 5.77k | } |
913 | 2.04k | } else { |
914 | 21.9k | for (auto& table : hdr->tables) { |
915 | 21.9k | tables.push_back(&table); |
916 | 21.9k | } |
917 | 2.04k | } |
918 | 3.11k | return tables; |
919 | 3.11k | } |
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 | 3.11k | WOFF2Out* out) { |
929 | 3.11k | size_t dest_offset = out->Size(); |
930 | 3.11k | uint8_t table_entry[12]; |
931 | 3.11k | WOFF2FontInfo* info = &metadata->font_infos[font_index]; |
932 | 3.11k | std::vector<Table*> tables = Tables(hdr, font_index); |
933 | | |
934 | | // 'glyf' without 'loca' doesn't make sense |
935 | 3.11k | const Table* glyf_table = FindTable(&tables, kGlyfTableTag); |
936 | 3.11k | const Table* loca_table = FindTable(&tables, kLocaTableTag); |
937 | 3.11k | if (PREDICT_FALSE(static_cast<bool>(glyf_table) != |
938 | 3.11k | static_cast<bool>(loca_table))) { |
939 | | #ifdef FONT_COMPRESSION_BIN |
940 | | fprintf(stderr, "Cannot have just one of glyf/loca\n"); |
941 | | #endif |
942 | 5 | return FONT_COMPRESSION_FAILURE(); |
943 | 5 | } |
944 | | |
945 | 3.10k | if (glyf_table != NULL) { |
946 | 1.37k | if (PREDICT_FALSE((glyf_table->flags & kWoff2FlagsTransform) |
947 | 1.37k | != (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 | 1.37k | } |
954 | | |
955 | 3.10k | uint32_t font_checksum = metadata->header_checksum; |
956 | 3.10k | if (hdr->header_version) { |
957 | 1.07k | font_checksum = hdr->ttc_fonts[font_index].header_checksum; |
958 | 1.07k | } |
959 | | |
960 | 3.10k | uint32_t loca_checksum = 0; |
961 | 20.9k | for (size_t i = 0; i < tables.size(); i++) { |
962 | 19.4k | Table& table = *tables[i]; |
963 | | |
964 | 19.4k | std::pair<uint32_t, uint32_t> checksum_key = {table.tag, table.src_offset}; |
965 | 19.4k | bool reused = metadata->checksums.find(checksum_key) |
966 | 19.4k | != metadata->checksums.end(); |
967 | 19.4k | if (PREDICT_FALSE(font_index == 0 && reused)) { |
968 | 57 | return FONT_COMPRESSION_FAILURE(); |
969 | 57 | } |
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 | 19.4k | if (PREDICT_FALSE(static_cast<uint64_t>(table.src_offset) + table.src_length |
974 | 19.4k | > transformed_buf_size)) { |
975 | 0 | return FONT_COMPRESSION_FAILURE(); |
976 | 0 | } |
977 | | |
978 | 19.4k | if (table.tag == kHheaTableTag) { |
979 | 876 | if (!ReadNumHMetrics(transformed_buf + table.src_offset, |
980 | 876 | table.src_length, &info->num_hmetrics)) { |
981 | 15 | return FONT_COMPRESSION_FAILURE(); |
982 | 15 | } |
983 | 876 | } |
984 | | |
985 | 19.4k | uint32_t checksum = 0; |
986 | 19.4k | if (!reused) { |
987 | 14.7k | if ((table.flags & kWoff2FlagsTransform) != kWoff2FlagsTransform) { |
988 | 12.8k | if (table.tag == kHeadTableTag) { |
989 | 211 | if (PREDICT_FALSE(table.src_length < 12)) { |
990 | 16 | return FONT_COMPRESSION_FAILURE(); |
991 | 16 | } |
992 | | // checkSumAdjustment = 0 |
993 | 195 | StoreU32(transformed_buf + table.src_offset, 8, 0); |
994 | 195 | } |
995 | 12.8k | table.dst_offset = dest_offset; |
996 | 12.8k | checksum = ComputeULongSum(transformed_buf + table.src_offset, |
997 | 12.8k | table.src_length); |
998 | 12.8k | if (PREDICT_FALSE(!out->Write(transformed_buf + table.src_offset, |
999 | 12.8k | table.src_length))) { |
1000 | 0 | return FONT_COMPRESSION_FAILURE(); |
1001 | 0 | } |
1002 | 12.8k | } else { |
1003 | 1.88k | if (table.tag == kGlyfTableTag) { |
1004 | 1.29k | table.dst_offset = dest_offset; |
1005 | | |
1006 | 1.29k | Table* loca_table = FindTable(&tables, kLocaTableTag); |
1007 | 1.29k | if (PREDICT_FALSE(!ReconstructGlyf(transformed_buf + table.src_offset, |
1008 | 1.29k | &table, &checksum, loca_table, &loca_checksum, info, out))) { |
1009 | 1.14k | return FONT_COMPRESSION_FAILURE(); |
1010 | 1.14k | } |
1011 | 1.29k | } else if (table.tag == kLocaTableTag) { |
1012 | | // All the work was done by ReconstructGlyf. We already know checksum. |
1013 | 169 | checksum = loca_checksum; |
1014 | 415 | } else if (table.tag == kHmtxTableTag) { |
1015 | 121 | table.dst_offset = dest_offset; |
1016 | | // Tables are sorted so all the info we need has been gathered. |
1017 | 121 | if (PREDICT_FALSE(!ReconstructTransformedHmtx( |
1018 | 121 | transformed_buf + table.src_offset, table.src_length, |
1019 | 121 | info->num_glyphs, info->num_hmetrics, info->x_mins, &checksum, |
1020 | 121 | out))) { |
1021 | 70 | return FONT_COMPRESSION_FAILURE(); |
1022 | 70 | } |
1023 | 294 | } else { |
1024 | 294 | return FONT_COMPRESSION_FAILURE(); // transform unknown |
1025 | 294 | } |
1026 | 1.88k | } |
1027 | 13.1k | metadata->checksums[checksum_key] = checksum; |
1028 | 13.1k | } else { |
1029 | 4.69k | checksum = metadata->checksums[checksum_key]; |
1030 | 4.69k | } |
1031 | 17.8k | font_checksum += checksum; |
1032 | | |
1033 | | // update the table entry with real values. |
1034 | 17.8k | StoreU32(table_entry, 0, checksum); |
1035 | 17.8k | StoreU32(table_entry, 4, table.dst_offset); |
1036 | 17.8k | StoreU32(table_entry, 8, table.dst_length); |
1037 | 17.8k | if (PREDICT_FALSE(!out->Write(table_entry, |
1038 | 17.8k | 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 | 17.8k | font_checksum += ComputeULongSum(table_entry, 12); |
1044 | | |
1045 | 17.8k | if (PREDICT_FALSE(!Pad4(out))) { |
1046 | 0 | return FONT_COMPRESSION_FAILURE(); |
1047 | 0 | } |
1048 | | |
1049 | 17.8k | if (PREDICT_FALSE(static_cast<uint64_t>(table.dst_offset + table.dst_length) |
1050 | 17.8k | > out->Size())) { |
1051 | 55 | return FONT_COMPRESSION_FAILURE(); |
1052 | 55 | } |
1053 | 17.8k | dest_offset = out->Size(); |
1054 | 17.8k | } |
1055 | | |
1056 | | // Update 'head' checkSumAdjustment. We already set it to 0 and summed font. |
1057 | 1.45k | Table* head_table = FindTable(&tables, kHeadTableTag); |
1058 | 1.45k | if (head_table) { |
1059 | 273 | if (PREDICT_FALSE(head_table->dst_length < 12)) { |
1060 | 2 | return FONT_COMPRESSION_FAILURE(); |
1061 | 2 | } |
1062 | 271 | uint8_t checksum_adjustment[4]; |
1063 | 271 | StoreU32(checksum_adjustment, 0, 0xB1B0AFBA - font_checksum); |
1064 | 271 | if (PREDICT_FALSE(!out->Write(checksum_adjustment, |
1065 | 271 | head_table->dst_offset + 8, 4))) { |
1066 | 0 | return FONT_COMPRESSION_FAILURE(); |
1067 | 0 | } |
1068 | 271 | } |
1069 | | |
1070 | 1.45k | return true; |
1071 | 1.45k | } |
1072 | | |
1073 | 15.0k | bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { |
1074 | 15.0k | Buffer file(data, length); |
1075 | | |
1076 | 15.0k | uint32_t signature; |
1077 | 15.0k | if (PREDICT_FALSE(!file.ReadU32(&signature) || signature != kWoff2Signature || |
1078 | 15.0k | !file.ReadU32(&hdr->flavor))) { |
1079 | 175 | return FONT_COMPRESSION_FAILURE(); |
1080 | 175 | } |
1081 | | |
1082 | | // TODO(user): Should call IsValidVersionTag() here. |
1083 | | |
1084 | 14.9k | uint32_t reported_length; |
1085 | 14.9k | if (PREDICT_FALSE( |
1086 | 14.9k | !file.ReadU32(&reported_length) || length != reported_length)) { |
1087 | 159 | return FONT_COMPRESSION_FAILURE(); |
1088 | 159 | } |
1089 | 14.7k | if (PREDICT_FALSE(!file.ReadU16(&hdr->num_tables) || !hdr->num_tables)) { |
1090 | 6 | return FONT_COMPRESSION_FAILURE(); |
1091 | 6 | } |
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 | 14.7k | if (PREDICT_FALSE(!file.Skip(6))) { |
1097 | 41 | return FONT_COMPRESSION_FAILURE(); |
1098 | 41 | } |
1099 | 14.6k | if (PREDICT_FALSE(!file.ReadU32(&hdr->compressed_length))) { |
1100 | 8 | return FONT_COMPRESSION_FAILURE(); |
1101 | 8 | } |
1102 | | // We don't care about these fields of the header: |
1103 | | // uint16_t major_version, minor_version |
1104 | 14.6k | if (PREDICT_FALSE(!file.Skip(2 * 2))) { |
1105 | 6 | return FONT_COMPRESSION_FAILURE(); |
1106 | 6 | } |
1107 | 14.6k | uint32_t meta_offset; |
1108 | 14.6k | uint32_t meta_length; |
1109 | 14.6k | uint32_t meta_length_orig; |
1110 | 14.6k | if (PREDICT_FALSE(!file.ReadU32(&meta_offset) || |
1111 | 14.6k | !file.ReadU32(&meta_length) || |
1112 | 14.6k | !file.ReadU32(&meta_length_orig))) { |
1113 | 18 | return FONT_COMPRESSION_FAILURE(); |
1114 | 18 | } |
1115 | 14.6k | if (meta_offset) { |
1116 | 731 | if (PREDICT_FALSE( |
1117 | 731 | meta_offset >= length || length - meta_offset < meta_length)) { |
1118 | 216 | return FONT_COMPRESSION_FAILURE(); |
1119 | 216 | } |
1120 | 731 | } |
1121 | 14.4k | uint32_t priv_offset; |
1122 | 14.4k | uint32_t priv_length; |
1123 | 14.4k | if (PREDICT_FALSE(!file.ReadU32(&priv_offset) || |
1124 | 14.4k | !file.ReadU32(&priv_length))) { |
1125 | 24 | return FONT_COMPRESSION_FAILURE(); |
1126 | 24 | } |
1127 | 14.4k | if (priv_offset) { |
1128 | 532 | if (PREDICT_FALSE( |
1129 | 532 | priv_offset >= length || length - priv_offset < priv_length)) { |
1130 | 210 | return FONT_COMPRESSION_FAILURE(); |
1131 | 210 | } |
1132 | 532 | } |
1133 | 14.2k | hdr->tables.resize(hdr->num_tables); |
1134 | 14.2k | if (PREDICT_FALSE(!ReadTableDirectory( |
1135 | 14.2k | &file, &hdr->tables, hdr->num_tables))) { |
1136 | 559 | return FONT_COMPRESSION_FAILURE(); |
1137 | 559 | } |
1138 | | |
1139 | | // Before we sort for output the last table end is the uncompressed size. |
1140 | 13.6k | Table& last_table = hdr->tables.back(); |
1141 | 13.6k | hdr->uncompressed_size = last_table.src_offset + last_table.src_length; |
1142 | 13.6k | if (PREDICT_FALSE(hdr->uncompressed_size < last_table.src_offset)) { |
1143 | 0 | return FONT_COMPRESSION_FAILURE(); |
1144 | 0 | } |
1145 | | |
1146 | 13.6k | hdr->header_version = 0; |
1147 | | |
1148 | 13.6k | if (hdr->flavor == kTtcFontFlavor) { |
1149 | 1.19k | if (PREDICT_FALSE(!file.ReadU32(&hdr->header_version))) { |
1150 | 20 | return FONT_COMPRESSION_FAILURE(); |
1151 | 20 | } |
1152 | 1.17k | if (PREDICT_FALSE(hdr->header_version != 0x00010000 |
1153 | 1.17k | && hdr->header_version != 0x00020000)) { |
1154 | 136 | return FONT_COMPRESSION_FAILURE(); |
1155 | 136 | } |
1156 | 1.04k | uint32_t num_fonts; |
1157 | 1.04k | if (PREDICT_FALSE(!Read255UShort(&file, &num_fonts) || !num_fonts)) { |
1158 | 14 | return FONT_COMPRESSION_FAILURE(); |
1159 | 14 | } |
1160 | 1.02k | hdr->ttc_fonts.resize(num_fonts); |
1161 | | |
1162 | 11.8k | for (uint32_t i = 0; i < num_fonts; i++) { |
1163 | 11.3k | TtcFont& ttc_font = hdr->ttc_fonts[i]; |
1164 | 11.3k | uint32_t num_tables; |
1165 | 11.3k | if (PREDICT_FALSE(!Read255UShort(&file, &num_tables) || !num_tables)) { |
1166 | 116 | return FONT_COMPRESSION_FAILURE(); |
1167 | 116 | } |
1168 | 11.2k | if (PREDICT_FALSE(!file.ReadU32(&ttc_font.flavor))) { |
1169 | 61 | return FONT_COMPRESSION_FAILURE(); |
1170 | 61 | } |
1171 | | |
1172 | 11.1k | ttc_font.table_indices.resize(num_tables); |
1173 | | |
1174 | | |
1175 | 11.1k | unsigned int glyf_idx = 0; |
1176 | 11.1k | unsigned int loca_idx = 0; |
1177 | | |
1178 | 424k | for (uint32_t j = 0; j < num_tables; j++) { |
1179 | 413k | unsigned int table_idx; |
1180 | 413k | if (PREDICT_FALSE(!Read255UShort(&file, &table_idx)) || |
1181 | 413k | table_idx >= hdr->tables.size()) { |
1182 | 293 | return FONT_COMPRESSION_FAILURE(); |
1183 | 293 | } |
1184 | 413k | ttc_font.table_indices[j] = table_idx; |
1185 | | |
1186 | 413k | const Table& table = hdr->tables[table_idx]; |
1187 | 413k | if (table.tag == kLocaTableTag) { |
1188 | 24.0k | loca_idx = table_idx; |
1189 | 24.0k | } |
1190 | 413k | if (table.tag == kGlyfTableTag) { |
1191 | 1.75k | glyf_idx = table_idx; |
1192 | 1.75k | } |
1193 | | |
1194 | 413k | } |
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 | 10.8k | if (glyf_idx > 0 || loca_idx > 0) { |
1199 | 700 | 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 | 39 | return FONT_COMPRESSION_FAILURE(); |
1204 | 39 | } |
1205 | 700 | } |
1206 | 10.8k | } |
1207 | 1.02k | } |
1208 | | |
1209 | 12.9k | const uint64_t first_table_offset = ComputeOffsetToFirstTable(*hdr); |
1210 | | |
1211 | 12.9k | hdr->compressed_offset = file.offset(); |
1212 | 12.9k | if (PREDICT_FALSE(hdr->compressed_offset > |
1213 | 12.9k | std::numeric_limits<uint32_t>::max())) { |
1214 | 0 | return FONT_COMPRESSION_FAILURE(); |
1215 | 0 | } |
1216 | 12.9k | uint64_t src_offset = Round4(hdr->compressed_offset + hdr->compressed_length); |
1217 | 12.9k | uint64_t dst_offset = first_table_offset; |
1218 | | |
1219 | | |
1220 | 12.9k | 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 | 269 | return FONT_COMPRESSION_FAILURE(); |
1227 | 269 | } |
1228 | 12.7k | if (meta_offset) { |
1229 | 149 | if (PREDICT_FALSE(src_offset != meta_offset)) { |
1230 | 75 | return FONT_COMPRESSION_FAILURE(); |
1231 | 75 | } |
1232 | 74 | src_offset = Round4(meta_offset + meta_length); |
1233 | 74 | if (PREDICT_FALSE(src_offset > std::numeric_limits<uint32_t>::max())) { |
1234 | 0 | return FONT_COMPRESSION_FAILURE(); |
1235 | 0 | } |
1236 | 74 | } |
1237 | | |
1238 | 12.6k | if (priv_offset) { |
1239 | 81 | if (PREDICT_FALSE(src_offset != priv_offset)) { |
1240 | 74 | return FONT_COMPRESSION_FAILURE(); |
1241 | 74 | } |
1242 | 7 | src_offset = Round4(priv_offset + priv_length); |
1243 | 7 | if (PREDICT_FALSE(src_offset > std::numeric_limits<uint32_t>::max())) { |
1244 | 0 | return FONT_COMPRESSION_FAILURE(); |
1245 | 0 | } |
1246 | 7 | } |
1247 | | |
1248 | 12.5k | if (PREDICT_FALSE(src_offset != Round4(length))) { |
1249 | 89 | return FONT_COMPRESSION_FAILURE(); |
1250 | 89 | } |
1251 | | |
1252 | 12.4k | return true; |
1253 | 12.5k | } |
1254 | | |
1255 | | // Write everything before the actual table data |
1256 | | bool WriteHeaders(const uint8_t* data, size_t length, RebuildMetadata* metadata, |
1257 | 12.4k | WOFF2Header* hdr, WOFF2Out* out) { |
1258 | 12.4k | std::vector<uint8_t> output(ComputeOffsetToFirstTable(*hdr), 0); |
1259 | | |
1260 | | // Re-order tables in output (OTSpec) order |
1261 | 12.4k | std::vector<Table> sorted_tables(hdr->tables); |
1262 | 12.4k | if (hdr->header_version) { |
1263 | | // collection; we have to sort the table offset vector in each font |
1264 | 6.38k | for (auto& ttc_font : hdr->ttc_fonts) { |
1265 | 6.38k | std::map<uint32_t, uint16_t> sorted_index_by_tag; |
1266 | 210k | for (auto table_index : ttc_font.table_indices) { |
1267 | 210k | sorted_index_by_tag[hdr->tables[table_index].tag] = table_index; |
1268 | 210k | } |
1269 | 6.38k | uint16_t index = 0; |
1270 | 55.3k | for (auto& i : sorted_index_by_tag) { |
1271 | 55.3k | ttc_font.table_indices[index++] = i.second; |
1272 | 55.3k | } |
1273 | 6.38k | } |
1274 | 11.9k | } else { |
1275 | | // non-collection; we can just sort the tables |
1276 | 11.9k | std::sort(sorted_tables.begin(), sorted_tables.end()); |
1277 | 11.9k | } |
1278 | | |
1279 | | // Start building the font |
1280 | 12.4k | uint8_t* result = &output[0]; |
1281 | 12.4k | size_t offset = 0; |
1282 | 12.4k | if (hdr->header_version) { |
1283 | | // TTC header |
1284 | 500 | offset = StoreU32(result, offset, hdr->flavor); // TAG TTCTag |
1285 | 500 | offset = StoreU32(result, offset, hdr->header_version); // FIXED Version |
1286 | 500 | offset = StoreU32(result, offset, hdr->ttc_fonts.size()); // ULONG numFonts |
1287 | | // Space for ULONG OffsetTable[numFonts] (zeroed initially) |
1288 | 500 | size_t offset_table = offset; // keep start of offset table for later |
1289 | 6.88k | for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) { |
1290 | 6.38k | offset = StoreU32(result, offset, 0); // will fill real values in later |
1291 | 6.38k | } |
1292 | | // space for DSIG fields for header v2 |
1293 | 500 | if (hdr->header_version == 0x00020000) { |
1294 | 371 | offset = StoreU32(result, offset, 0); // ULONG ulDsigTag |
1295 | 371 | offset = StoreU32(result, offset, 0); // ULONG ulDsigLength |
1296 | 371 | offset = StoreU32(result, offset, 0); // ULONG ulDsigOffset |
1297 | 371 | } |
1298 | | |
1299 | | // write Offset Tables and store the location of each in TTC Header |
1300 | 500 | metadata->font_infos.resize(hdr->ttc_fonts.size()); |
1301 | 6.88k | for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) { |
1302 | 6.38k | TtcFont& ttc_font = hdr->ttc_fonts[i]; |
1303 | | |
1304 | | // write Offset Table location into TTC Header |
1305 | 6.38k | offset_table = StoreU32(result, offset_table, offset); |
1306 | | |
1307 | | // write the actual offset table so our header doesn't lie |
1308 | 6.38k | ttc_font.dst_offset = offset; |
1309 | 6.38k | offset = StoreOffsetTable(result, offset, ttc_font.flavor, |
1310 | 6.38k | ttc_font.table_indices.size()); |
1311 | | |
1312 | 210k | for (const auto table_index : ttc_font.table_indices) { |
1313 | 210k | uint32_t tag = hdr->tables[table_index].tag; |
1314 | 210k | metadata->font_infos[i].table_entry_by_tag[tag] = offset; |
1315 | 210k | offset = StoreTableEntry(result, offset, tag); |
1316 | 210k | } |
1317 | | |
1318 | 6.38k | ttc_font.header_checksum = ComputeULongSum(&output[ttc_font.dst_offset], |
1319 | 6.38k | offset - ttc_font.dst_offset); |
1320 | 6.38k | } |
1321 | 11.9k | } else { |
1322 | 11.9k | metadata->font_infos.resize(1); |
1323 | 11.9k | offset = StoreOffsetTable(result, offset, hdr->flavor, hdr->num_tables); |
1324 | 446k | for (uint16_t i = 0; i < hdr->num_tables; ++i) { |
1325 | 435k | metadata->font_infos[0].table_entry_by_tag[sorted_tables[i].tag] = offset; |
1326 | 435k | offset = StoreTableEntry(result, offset, sorted_tables[i].tag); |
1327 | 435k | } |
1328 | 11.9k | } |
1329 | | |
1330 | 12.4k | if (PREDICT_FALSE(!out->Write(&output[0], output.size()))) { |
1331 | 0 | return FONT_COMPRESSION_FAILURE(); |
1332 | 0 | } |
1333 | 12.4k | metadata->header_checksum = ComputeULongSum(&output[0], output.size()); |
1334 | 12.4k | return true; |
1335 | 12.4k | } |
1336 | | |
1337 | | } // namespace |
1338 | | |
1339 | 7.28k | size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) { |
1340 | 7.28k | Buffer file(data, length); |
1341 | 7.28k | uint32_t total_length; |
1342 | | |
1343 | 7.28k | if (!file.Skip(16) || |
1344 | 7.28k | !file.ReadU32(&total_length)) { |
1345 | 140 | return 0; |
1346 | 140 | } |
1347 | 7.14k | return total_length; |
1348 | 7.28k | } |
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 | 15.0k | WOFF2Out* out) { |
1358 | 15.0k | RebuildMetadata metadata; |
1359 | 15.0k | WOFF2Header hdr; |
1360 | 15.0k | if (!ReadWOFF2Header(data, length, &hdr)) { |
1361 | 2.60k | return FONT_COMPRESSION_FAILURE(); |
1362 | 2.60k | } |
1363 | | |
1364 | 12.4k | if (!WriteHeaders(data, length, &metadata, &hdr, out)) { |
1365 | 0 | return FONT_COMPRESSION_FAILURE(); |
1366 | 0 | } |
1367 | | |
1368 | 12.4k | const float compression_ratio = (float) hdr.uncompressed_size / length; |
1369 | 12.4k | if (compression_ratio > kMaxPlausibleCompressionRatio) { |
1370 | | #ifdef FONT_COMPRESSION_BIN |
1371 | | fprintf(stderr, "Implausible compression ratio %.01f\n", compression_ratio); |
1372 | | #endif |
1373 | 1.62k | return FONT_COMPRESSION_FAILURE(); |
1374 | 1.62k | } |
1375 | | |
1376 | 10.8k | const uint8_t* src_buf = data + hdr.compressed_offset; |
1377 | 10.8k | std::vector<uint8_t> uncompressed_buf(hdr.uncompressed_size); |
1378 | 10.8k | if (PREDICT_FALSE(hdr.uncompressed_size < 1)) { |
1379 | 104 | return FONT_COMPRESSION_FAILURE(); |
1380 | 104 | } |
1381 | 10.7k | if (PREDICT_FALSE(!Woff2Uncompress(&uncompressed_buf[0], |
1382 | 10.7k | hdr.uncompressed_size, src_buf, |
1383 | 10.7k | hdr.compressed_length))) { |
1384 | 8.41k | return FONT_COMPRESSION_FAILURE(); |
1385 | 8.41k | } |
1386 | | |
1387 | 3.77k | for (size_t i = 0; i < metadata.font_infos.size(); i++) { |
1388 | 3.11k | if (PREDICT_FALSE(!ReconstructFont(&uncompressed_buf[0], |
1389 | 3.11k | hdr.uncompressed_size, |
1390 | 3.11k | &metadata, &hdr, i, out))) { |
1391 | 1.66k | return FONT_COMPRESSION_FAILURE(); |
1392 | 1.66k | } |
1393 | 3.11k | } |
1394 | | |
1395 | 663 | return true; |
1396 | 2.32k | } |
1397 | | |
1398 | | } // namespace woff2 |