OLD | NEW |
1 // Copyright 2014 Google Inc. All Rights Reserved. | 1 // Copyright 2014 Google Inc. All Rights Reserved. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
13 // limitations under the License. | 13 // limitations under the License. |
14 // | 14 // |
15 // Library for converting WOFF2 format font files to their TTF versions. | 15 // Library for converting WOFF2 format font files to their TTF versions. |
16 | 16 |
17 #include "./woff2_dec.h" | 17 #include "./woff2_dec.h" |
18 | 18 |
19 #include <stdlib.h> | 19 #include <stdlib.h> |
20 #include <algorithm> | 20 #include <algorithm> |
21 #include <complex> | 21 #include <complex> |
22 #include <cstring> | 22 #include <cstring> |
23 #include <limits> | 23 #include <limits> |
24 #include <string> | 24 #include <string> |
25 #include <vector> | 25 #include <vector> |
26 #include <map> | 26 #include <map> |
27 #include <memory> | 27 #include <memory> |
| 28 #include <utility> |
28 | 29 |
29 #include "./decode.h" | 30 #include "./decode.h" |
30 #include "./buffer.h" | 31 #include "./buffer.h" |
31 #include "./port.h" | 32 #include "./port.h" |
32 #include "./round.h" | 33 #include "./round.h" |
33 #include "./store_bytes.h" | 34 #include "./store_bytes.h" |
34 #include "./table_tags.h" | 35 #include "./table_tags.h" |
35 #include "./variable_length.h" | 36 #include "./variable_length.h" |
36 #include "./woff2_common.h" | 37 #include "./woff2_common.h" |
37 | 38 |
(...skipping 20 matching lines...) Expand all Loading... |
58 const int FLAG_MORE_COMPONENTS = 1 << 5; | 59 const int FLAG_MORE_COMPONENTS = 1 << 5; |
59 const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6; | 60 const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6; |
60 const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7; | 61 const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7; |
61 const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; | 62 const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; |
62 | 63 |
63 const size_t kCheckSumAdjustmentOffset = 8; | 64 const size_t kCheckSumAdjustmentOffset = 8; |
64 | 65 |
65 const size_t kEndPtsOfContoursOffset = 10; | 66 const size_t kEndPtsOfContoursOffset = 10; |
66 const size_t kCompositeGlyphBegin = 10; | 67 const size_t kCompositeGlyphBegin = 10; |
67 | 68 |
| 69 // 98% of Google Fonts have no glyph above 5k bytes |
| 70 // Largest glyph ever observed was 72k bytes |
| 71 const size_t kDefaultGlyphBuf = 5120; |
| 72 |
68 // metadata for a TTC font entry | 73 // metadata for a TTC font entry |
69 struct TtcFont { | 74 struct TtcFont { |
70 uint32_t flavor; | 75 uint32_t flavor; |
71 uint32_t dst_offset; | 76 uint32_t dst_offset; |
| 77 uint32_t header_checksum; |
72 std::vector<uint16_t> table_indices; | 78 std::vector<uint16_t> table_indices; |
73 }; | 79 }; |
74 | 80 |
| 81 struct WOFF2Header { |
| 82 uint32_t flavor; |
| 83 uint32_t header_version; |
| 84 uint16_t num_tables; |
| 85 uint64_t compressed_offset; |
| 86 uint32_t compressed_length; |
| 87 uint32_t uncompressed_size; |
| 88 std::vector<Table> tables; // num_tables unique tables |
| 89 std::vector<TtcFont> ttc_fonts; // metadata to help rebuild font |
| 90 }; |
| 91 |
| 92 /** |
| 93 * Accumulates data we may need to reconstruct a single font. One per font |
| 94 * created for a TTC. |
| 95 */ |
| 96 struct WOFF2FontInfo { |
| 97 uint16_t num_glyphs; |
| 98 uint16_t index_format; |
| 99 uint16_t num_hmetrics; |
| 100 std::vector<int16_t> x_mins; |
| 101 std::map<uint32_t, uint32_t> table_entry_by_tag; |
| 102 }; |
| 103 |
| 104 // Accumulates metadata as we rebuild the font |
| 105 struct RebuildMetadata { |
| 106 uint32_t header_checksum; // set by WriteHeaders |
| 107 std::vector<WOFF2FontInfo> font_infos; |
| 108 // checksums for tables that have been written. |
| 109 // (tag, src_offset) => checksum. Need both because 0-length loca. |
| 110 std::map<std::pair<uint32_t, uint32_t>, uint32_t> checksums; |
| 111 }; |
| 112 |
75 int WithSign(int flag, int baseval) { | 113 int WithSign(int flag, int baseval) { |
76 // Precondition: 0 <= baseval < 65536 (to avoid integer overflow) | 114 // Precondition: 0 <= baseval < 65536 (to avoid integer overflow) |
77 return (flag & 1) ? baseval : -baseval; | 115 return (flag & 1) ? baseval : -baseval; |
78 } | 116 } |
79 | 117 |
80 bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, | 118 bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, |
81 unsigned int n_points, Point* result, size_t* in_bytes_consumed) { | 119 unsigned int n_points, Point* result, size_t* in_bytes_consumed) { |
82 int x = 0; | 120 int x = 0; |
83 int y = 0; | 121 int y = 0; |
84 | 122 |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
237 y_offset = Store16(dst, y_offset, dy); | 275 y_offset = Store16(dst, y_offset, dy); |
238 } | 276 } |
239 last_y += dy; | 277 last_y += dy; |
240 } | 278 } |
241 *glyph_size = y_offset; | 279 *glyph_size = y_offset; |
242 return true; | 280 return true; |
243 } | 281 } |
244 | 282 |
245 // Compute the bounding box of the coordinates, and store into a glyf buffer. | 283 // Compute the bounding box of the coordinates, and store into a glyf buffer. |
246 // A precondition is that there are at least 10 bytes available. | 284 // A precondition is that there are at least 10 bytes available. |
| 285 // dst should point to the beginning of a 'glyf' record. |
247 void ComputeBbox(unsigned int n_points, const Point* points, uint8_t* dst) { | 286 void ComputeBbox(unsigned int n_points, const Point* points, uint8_t* dst) { |
248 int x_min = 0; | 287 int x_min = 0; |
249 int y_min = 0; | 288 int y_min = 0; |
250 int x_max = 0; | 289 int x_max = 0; |
251 int y_max = 0; | 290 int y_max = 0; |
252 | 291 |
253 if (n_points > 0) { | 292 if (n_points > 0) { |
254 x_min = points[0].x; | 293 x_min = points[0].x; |
255 x_max = points[0].x; | 294 x_max = points[0].x; |
256 y_min = points[0].y; | 295 y_min = points[0].y; |
257 y_max = points[0].y; | 296 y_max = points[0].y; |
258 } | 297 } |
259 for (unsigned int i = 1; i < n_points; ++i) { | 298 for (unsigned int i = 1; i < n_points; ++i) { |
260 int x = points[i].x; | 299 int x = points[i].x; |
261 int y = points[i].y; | 300 int y = points[i].y; |
262 x_min = std::min(x, x_min); | 301 x_min = std::min(x, x_min); |
263 x_max = std::max(x, x_max); | 302 x_max = std::max(x, x_max); |
264 y_min = std::min(y, y_min); | 303 y_min = std::min(y, y_min); |
265 y_max = std::max(y, y_max); | 304 y_max = std::max(y, y_max); |
266 } | 305 } |
267 size_t offset = 2; | 306 size_t offset = 2; |
268 offset = Store16(dst, offset, x_min); | 307 offset = Store16(dst, offset, x_min); |
269 offset = Store16(dst, offset, y_min); | 308 offset = Store16(dst, offset, y_min); |
270 offset = Store16(dst, offset, x_max); | 309 offset = Store16(dst, offset, x_max); |
271 offset = Store16(dst, offset, y_max); | 310 offset = Store16(dst, offset, y_max); |
272 } | 311 } |
273 | 312 |
274 bool ProcessComposite(Buffer* composite_stream, uint8_t* dst, | 313 |
275 size_t dst_size, size_t* glyph_size, bool* have_instructions) { | 314 bool SizeOfComposite(Buffer composite_stream, size_t* size, |
276 size_t start_offset = composite_stream->offset(); | 315 bool* have_instructions) { |
| 316 size_t start_offset = composite_stream.offset(); |
277 bool we_have_instructions = false; | 317 bool we_have_instructions = false; |
278 | 318 |
279 uint16_t flags = FLAG_MORE_COMPONENTS; | 319 uint16_t flags = FLAG_MORE_COMPONENTS; |
280 while (flags & FLAG_MORE_COMPONENTS) { | 320 while (flags & FLAG_MORE_COMPONENTS) { |
281 if (PREDICT_FALSE(!composite_stream->ReadU16(&flags))) { | 321 if (PREDICT_FALSE(!composite_stream.ReadU16(&flags))) { |
282 return FONT_COMPRESSION_FAILURE(); | 322 return FONT_COMPRESSION_FAILURE(); |
283 } | 323 } |
284 we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0; | 324 we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0; |
285 size_t arg_size = 2; // glyph index | 325 size_t arg_size = 2; // glyph index |
286 if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) { | 326 if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) { |
287 arg_size += 4; | 327 arg_size += 4; |
288 } else { | 328 } else { |
289 arg_size += 2; | 329 arg_size += 2; |
290 } | 330 } |
291 if (flags & FLAG_WE_HAVE_A_SCALE) { | 331 if (flags & FLAG_WE_HAVE_A_SCALE) { |
292 arg_size += 2; | 332 arg_size += 2; |
293 } else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) { | 333 } else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) { |
294 arg_size += 4; | 334 arg_size += 4; |
295 } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) { | 335 } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) { |
296 arg_size += 8; | 336 arg_size += 8; |
297 } | 337 } |
298 if (PREDICT_FALSE(!composite_stream->Skip(arg_size))) { | 338 if (PREDICT_FALSE(!composite_stream.Skip(arg_size))) { |
299 return FONT_COMPRESSION_FAILURE(); | 339 return FONT_COMPRESSION_FAILURE(); |
300 } | 340 } |
301 } | 341 } |
302 size_t composite_glyph_size = composite_stream->offset() - start_offset; | 342 |
303 if (PREDICT_FALSE(composite_glyph_size + kCompositeGlyphBegin > dst_size)) { | 343 *size = composite_stream.offset() - start_offset; |
| 344 *have_instructions = we_have_instructions; |
| 345 |
| 346 return true; |
| 347 } |
| 348 |
| 349 bool Pad4(WOFF2Out* out) { |
| 350 uint8_t zeroes[] = {0, 0, 0}; |
| 351 if (PREDICT_FALSE(out->Size() + 3 < out->Size())) { |
304 return FONT_COMPRESSION_FAILURE(); | 352 return FONT_COMPRESSION_FAILURE(); |
305 } | 353 } |
306 Store16(dst, 0, 0xffff); // nContours = -1 for composite glyph | 354 uint32_t pad_bytes = Round4(out->Size()) - out->Size(); |
307 std::memcpy(dst + kCompositeGlyphBegin, | 355 if (pad_bytes > 0) { |
308 composite_stream->buffer() + start_offset, | 356 if (PREDICT_FALSE(!out->Write(&zeroes, pad_bytes))) { |
309 composite_glyph_size); | 357 return FONT_COMPRESSION_FAILURE(); |
310 *glyph_size = kCompositeGlyphBegin + composite_glyph_size; | 358 } |
311 *have_instructions = we_have_instructions; | 359 } |
312 return true; | 360 return true; |
313 } | 361 } |
314 | 362 |
315 // Build TrueType loca table | 363 // Build TrueType loca table |
316 bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format, | 364 bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format, |
317 uint8_t* dst, size_t dst_size) { | 365 uint32_t* checksum, WOFF2Out* out) { |
| 366 // TODO(user) figure out what index format to use based on whether max |
| 367 // offset fits into uint16_t or not |
318 const uint64_t loca_size = loca_values.size(); | 368 const uint64_t loca_size = loca_values.size(); |
319 const uint64_t offset_size = index_format ? 4 : 2; | 369 const uint64_t offset_size = index_format ? 4 : 2; |
320 if (PREDICT_FALSE((loca_size << 2) >> 2 != loca_size)) { | 370 if (PREDICT_FALSE((loca_size << 2) >> 2 != loca_size)) { |
321 return FONT_COMPRESSION_FAILURE(); | 371 return FONT_COMPRESSION_FAILURE(); |
322 } | 372 } |
323 if (PREDICT_FALSE(offset_size * loca_size > dst_size)) { | 373 std::vector<uint8_t> loca_content(loca_size * offset_size); |
324 return FONT_COMPRESSION_FAILURE(); | 374 uint8_t* dst = &loca_content[0]; |
325 } | |
326 size_t offset = 0; | 375 size_t offset = 0; |
327 for (size_t i = 0; i < loca_values.size(); ++i) { | 376 for (size_t i = 0; i < loca_values.size(); ++i) { |
328 uint32_t value = loca_values[i]; | 377 uint32_t value = loca_values[i]; |
329 if (index_format) { | 378 if (index_format) { |
330 offset = StoreU32(dst, offset, value); | 379 offset = StoreU32(dst, offset, value); |
331 } else { | 380 } else { |
332 offset = Store16(dst, offset, value >> 1); | 381 offset = Store16(dst, offset, value >> 1); |
333 } | 382 } |
334 } | 383 } |
| 384 *checksum = ComputeULongSum(&loca_content[0], loca_content.size()); |
| 385 if (PREDICT_FALSE(!out->Write(&loca_content[0], loca_content.size()))) { |
| 386 return FONT_COMPRESSION_FAILURE(); |
| 387 } |
335 return true; | 388 return true; |
336 } | 389 } |
337 | 390 |
338 // Reconstruct entire glyf table based on transformed original | 391 // Reconstruct entire glyf table based on transformed original |
339 bool ReconstructGlyf(const uint8_t* data, size_t data_size, | 392 bool ReconstructGlyf(const uint8_t* data, Table* glyf_table, |
340 uint8_t* dst, size_t dst_size, | 393 uint32_t* glyf_checksum, Table * loca_table, |
341 uint8_t* loca_buf, size_t loca_size) { | 394 uint32_t* loca_checksum, WOFF2FontInfo* info, |
| 395 WOFF2Out* out) { |
342 static const int kNumSubStreams = 7; | 396 static const int kNumSubStreams = 7; |
343 Buffer file(data, data_size); | 397 Buffer file(data, glyf_table->transform_length); |
344 uint32_t version; | 398 uint32_t version; |
345 std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams); | 399 std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams); |
| 400 const size_t glyf_start = out->Size(); |
346 | 401 |
347 if (PREDICT_FALSE(!file.ReadU32(&version))) { | 402 if (PREDICT_FALSE(!file.ReadU32(&version))) { |
348 return FONT_COMPRESSION_FAILURE(); | 403 return FONT_COMPRESSION_FAILURE(); |
349 } | 404 } |
350 uint16_t num_glyphs; | 405 if (PREDICT_FALSE(!file.ReadU16(&info->num_glyphs) || |
351 uint16_t index_format; | 406 !file.ReadU16(&info->index_format))) { |
352 if (PREDICT_FALSE(!file.ReadU16(&num_glyphs) || | |
353 !file.ReadU16(&index_format))) { | |
354 return FONT_COMPRESSION_FAILURE(); | 407 return FONT_COMPRESSION_FAILURE(); |
355 } | 408 } |
356 | 409 |
357 unsigned int offset = (2 + kNumSubStreams) * 4; | 410 unsigned int offset = (2 + kNumSubStreams) * 4; |
358 if (PREDICT_FALSE(offset > data_size)) { | 411 if (PREDICT_FALSE(offset > glyf_table->transform_length)) { |
359 return FONT_COMPRESSION_FAILURE(); | 412 return FONT_COMPRESSION_FAILURE(); |
360 } | 413 } |
361 // Invariant from here on: data_size >= offset | 414 // Invariant from here on: data_size >= offset |
362 for (int i = 0; i < kNumSubStreams; ++i) { | 415 for (int i = 0; i < kNumSubStreams; ++i) { |
363 uint32_t substream_size; | 416 uint32_t substream_size; |
364 if (PREDICT_FALSE(!file.ReadU32(&substream_size))) { | 417 if (PREDICT_FALSE(!file.ReadU32(&substream_size))) { |
365 return FONT_COMPRESSION_FAILURE(); | 418 return FONT_COMPRESSION_FAILURE(); |
366 } | 419 } |
367 if (PREDICT_FALSE(substream_size > data_size - offset)) { | 420 if (PREDICT_FALSE(substream_size > glyf_table->transform_length - offset)) { |
368 return FONT_COMPRESSION_FAILURE(); | 421 return FONT_COMPRESSION_FAILURE(); |
369 } | 422 } |
370 substreams[i] = std::make_pair(data + offset, substream_size); | 423 substreams[i] = std::make_pair(data + offset, substream_size); |
371 offset += substream_size; | 424 offset += substream_size; |
372 } | 425 } |
373 Buffer n_contour_stream(substreams[0].first, substreams[0].second); | 426 Buffer n_contour_stream(substreams[0].first, substreams[0].second); |
374 Buffer n_points_stream(substreams[1].first, substreams[1].second); | 427 Buffer n_points_stream(substreams[1].first, substreams[1].second); |
375 Buffer flag_stream(substreams[2].first, substreams[2].second); | 428 Buffer flag_stream(substreams[2].first, substreams[2].second); |
376 Buffer glyph_stream(substreams[3].first, substreams[3].second); | 429 Buffer glyph_stream(substreams[3].first, substreams[3].second); |
377 Buffer composite_stream(substreams[4].first, substreams[4].second); | 430 Buffer composite_stream(substreams[4].first, substreams[4].second); |
378 Buffer bbox_stream(substreams[5].first, substreams[5].second); | 431 Buffer bbox_stream(substreams[5].first, substreams[5].second); |
379 Buffer instruction_stream(substreams[6].first, substreams[6].second); | 432 Buffer instruction_stream(substreams[6].first, substreams[6].second); |
380 | 433 |
381 std::vector<uint32_t> loca_values(num_glyphs + 1); | 434 std::vector<uint32_t> loca_values(info->num_glyphs + 1); |
382 std::vector<unsigned int> n_points_vec; | 435 std::vector<unsigned int> n_points_vec; |
383 std::unique_ptr<Point[]> points; | 436 std::unique_ptr<Point[]> points; |
384 size_t points_size = 0; | 437 size_t points_size = 0; |
385 uint32_t loca_offset = 0; | |
386 const uint8_t* bbox_bitmap = bbox_stream.buffer(); | 438 const uint8_t* bbox_bitmap = bbox_stream.buffer(); |
387 // Safe because num_glyphs is bounded | 439 // Safe because num_glyphs is bounded |
388 unsigned int bitmap_length = ((num_glyphs + 31) >> 5) << 2; | 440 unsigned int bitmap_length = ((info->num_glyphs + 31) >> 5) << 2; |
389 if (!bbox_stream.Skip(bitmap_length)) { | 441 if (!bbox_stream.Skip(bitmap_length)) { |
390 return FONT_COMPRESSION_FAILURE(); | 442 return FONT_COMPRESSION_FAILURE(); |
391 } | 443 } |
392 for (unsigned int i = 0; i < num_glyphs; ++i) { | 444 |
| 445 // Temp buffer for glyph's. |
| 446 size_t glyph_buf_size = kDefaultGlyphBuf; |
| 447 std::unique_ptr<uint8_t[]> glyph_buf(new uint8_t[glyph_buf_size]); |
| 448 |
| 449 info->x_mins.resize(info->num_glyphs); |
| 450 for (unsigned int i = 0; i < info->num_glyphs; ++i) { |
393 size_t glyph_size = 0; | 451 size_t glyph_size = 0; |
394 uint16_t n_contours = 0; | 452 uint16_t n_contours = 0; |
395 bool have_bbox = false; | 453 bool have_bbox = false; |
396 if (bbox_bitmap[i >> 3] & (0x80 >> (i & 7))) { | 454 if (bbox_bitmap[i >> 3] & (0x80 >> (i & 7))) { |
397 have_bbox = true; | 455 have_bbox = true; |
398 } | 456 } |
399 if (PREDICT_FALSE(!n_contour_stream.ReadU16(&n_contours))) { | 457 if (PREDICT_FALSE(!n_contour_stream.ReadU16(&n_contours))) { |
400 return FONT_COMPRESSION_FAILURE(); | 458 return FONT_COMPRESSION_FAILURE(); |
401 } | 459 } |
402 uint8_t* glyf_dst = dst + loca_offset; | 460 |
403 size_t glyf_dst_size = dst_size - loca_offset; | |
404 if (n_contours == 0xffff) { | 461 if (n_contours == 0xffff) { |
405 // composite glyph | 462 // composite glyph |
406 bool have_instructions = false; | 463 bool have_instructions = false; |
407 unsigned int instruction_size = 0; | 464 unsigned int instruction_size = 0; |
408 if (PREDICT_FALSE(!have_bbox)) { | 465 if (PREDICT_FALSE(!have_bbox)) { |
409 // composite glyphs must have an explicit bbox | 466 // composite glyphs must have an explicit bbox |
410 return FONT_COMPRESSION_FAILURE(); | 467 return FONT_COMPRESSION_FAILURE(); |
411 } | 468 } |
412 if (PREDICT_FALSE(!ProcessComposite(&composite_stream, glyf_dst, | 469 |
413 glyf_dst_size, &glyph_size, &have_instructions))) { | 470 size_t composite_size; |
414 return FONT_COMPRESSION_FAILURE(); | 471 if (PREDICT_FALSE(!SizeOfComposite(composite_stream, &composite_size, |
415 } | 472 &have_instructions))) { |
416 if (PREDICT_FALSE(!bbox_stream.Read(glyf_dst + 2, 8))) { | |
417 return FONT_COMPRESSION_FAILURE(); | 473 return FONT_COMPRESSION_FAILURE(); |
418 } | 474 } |
419 if (have_instructions) { | 475 if (have_instructions) { |
420 if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { | 476 if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { |
421 return FONT_COMPRESSION_FAILURE(); | 477 return FONT_COMPRESSION_FAILURE(); |
422 } | 478 } |
423 if (PREDICT_FALSE(instruction_size + 2 > glyf_dst_size - glyph_size)) { | 479 } |
424 return FONT_COMPRESSION_FAILURE(); | 480 |
425 } | 481 size_t size_needed = 2 + composite_size + instruction_size; |
426 Store16(glyf_dst, glyph_size, instruction_size); | 482 if (PREDICT_FALSE(glyph_buf_size < size_needed)) { |
427 if (PREDICT_FALSE(!instruction_stream.Read(glyf_dst + glyph_size + 2, | 483 glyph_buf.reset(new uint8_t[size_needed]); |
| 484 glyph_buf_size = size_needed; |
| 485 } |
| 486 |
| 487 glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); |
| 488 if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { |
| 489 return FONT_COMPRESSION_FAILURE(); |
| 490 } |
| 491 glyph_size += 8; |
| 492 |
| 493 if (PREDICT_FALSE(!composite_stream.Read(glyph_buf.get() + glyph_size, |
| 494 composite_size))) { |
| 495 return FONT_COMPRESSION_FAILURE(); |
| 496 } |
| 497 glyph_size += composite_size; |
| 498 if (have_instructions) { |
| 499 glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); |
| 500 if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, |
428 instruction_size))) { | 501 instruction_size))) { |
429 return FONT_COMPRESSION_FAILURE(); | 502 return FONT_COMPRESSION_FAILURE(); |
430 } | 503 } |
431 glyph_size += instruction_size + 2; | 504 glyph_size += instruction_size; |
432 } | 505 } |
433 } else if (n_contours > 0) { | 506 } else if (n_contours > 0) { |
434 // simple glyph | 507 // simple glyph |
435 n_points_vec.clear(); | 508 n_points_vec.clear(); |
436 unsigned int total_n_points = 0; | 509 unsigned int total_n_points = 0; |
437 unsigned int n_points_contour; | 510 unsigned int n_points_contour; |
438 for (unsigned int j = 0; j < n_contours; ++j) { | 511 for (unsigned int j = 0; j < n_contours; ++j) { |
439 if (PREDICT_FALSE( | 512 if (PREDICT_FALSE( |
440 !Read255UShort(&n_points_stream, &n_points_contour))) { | 513 !Read255UShort(&n_points_stream, &n_points_contour))) { |
441 return FONT_COMPRESSION_FAILURE(); | 514 return FONT_COMPRESSION_FAILURE(); |
(...skipping 15 matching lines...) Expand all Loading... |
457 size_t triplet_size = glyph_stream.length() - glyph_stream.offset(); | 530 size_t triplet_size = glyph_stream.length() - glyph_stream.offset(); |
458 size_t triplet_bytes_consumed = 0; | 531 size_t triplet_bytes_consumed = 0; |
459 if (points_size < total_n_points) { | 532 if (points_size < total_n_points) { |
460 points_size = total_n_points; | 533 points_size = total_n_points; |
461 points.reset(new Point[points_size]); | 534 points.reset(new Point[points_size]); |
462 } | 535 } |
463 if (PREDICT_FALSE(!TripletDecode(flags_buf, triplet_buf, triplet_size, | 536 if (PREDICT_FALSE(!TripletDecode(flags_buf, triplet_buf, triplet_size, |
464 total_n_points, points.get(), &triplet_bytes_consumed))) { | 537 total_n_points, points.get(), &triplet_bytes_consumed))) { |
465 return FONT_COMPRESSION_FAILURE(); | 538 return FONT_COMPRESSION_FAILURE(); |
466 } | 539 } |
467 const uint32_t header_and_endpts_contours_size = | |
468 kEndPtsOfContoursOffset + 2 * n_contours; | |
469 if (PREDICT_FALSE(glyf_dst_size < header_and_endpts_contours_size)) { | |
470 return FONT_COMPRESSION_FAILURE(); | |
471 } | |
472 Store16(glyf_dst, 0, n_contours); | |
473 if (have_bbox) { | |
474 if (PREDICT_FALSE(!bbox_stream.Read(glyf_dst + 2, 8))) { | |
475 return FONT_COMPRESSION_FAILURE(); | |
476 } | |
477 } else { | |
478 ComputeBbox(total_n_points, points.get(), glyf_dst); | |
479 } | |
480 size_t offset = kEndPtsOfContoursOffset; | |
481 int end_point = -1; | |
482 for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) { | |
483 end_point += n_points_vec[contour_ix]; | |
484 if (PREDICT_FALSE(end_point >= 65536)) { | |
485 return FONT_COMPRESSION_FAILURE(); | |
486 } | |
487 offset = Store16(glyf_dst, offset, end_point); | |
488 } | |
489 if (PREDICT_FALSE(!flag_stream.Skip(flag_size))) { | 540 if (PREDICT_FALSE(!flag_stream.Skip(flag_size))) { |
490 return FONT_COMPRESSION_FAILURE(); | 541 return FONT_COMPRESSION_FAILURE(); |
491 } | 542 } |
492 if (PREDICT_FALSE(!glyph_stream.Skip(triplet_bytes_consumed))) { | 543 if (PREDICT_FALSE(!glyph_stream.Skip(triplet_bytes_consumed))) { |
493 return FONT_COMPRESSION_FAILURE(); | 544 return FONT_COMPRESSION_FAILURE(); |
494 } | 545 } |
495 unsigned int instruction_size; | 546 unsigned int instruction_size; |
496 if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { | 547 if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { |
497 return FONT_COMPRESSION_FAILURE(); | 548 return FONT_COMPRESSION_FAILURE(); |
498 } | 549 } |
499 if (PREDICT_FALSE(glyf_dst_size - header_and_endpts_contours_size < | 550 |
500 instruction_size + 2)) { | 551 if (PREDICT_FALSE(total_n_points >= (1 << 27) |
| 552 || instruction_size >= (1 << 30))) { |
501 return FONT_COMPRESSION_FAILURE(); | 553 return FONT_COMPRESSION_FAILURE(); |
502 } | 554 } |
503 uint8_t* instruction_dst = glyf_dst + header_and_endpts_contours_size; | 555 size_t size_needed = 12 + 2 * n_contours + 5 * total_n_points |
504 Store16(instruction_dst, 0, instruction_size); | 556 + instruction_size; |
505 if (PREDICT_FALSE( | 557 if (PREDICT_FALSE(glyph_buf_size < size_needed)) { |
506 !instruction_stream.Read(instruction_dst + 2, instruction_size))) { | 558 glyph_buf.reset(new uint8_t[size_needed]); |
| 559 glyph_buf_size = size_needed; |
| 560 } |
| 561 |
| 562 glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); |
| 563 if (have_bbox) { |
| 564 if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { |
| 565 return FONT_COMPRESSION_FAILURE(); |
| 566 } |
| 567 } else { |
| 568 ComputeBbox(total_n_points, points.get(), glyph_buf.get()); |
| 569 } |
| 570 glyph_size = kEndPtsOfContoursOffset; |
| 571 int end_point = -1; |
| 572 for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) { |
| 573 end_point += n_points_vec[contour_ix]; |
| 574 if (PREDICT_FALSE(end_point >= 65536)) { |
| 575 return FONT_COMPRESSION_FAILURE(); |
| 576 } |
| 577 glyph_size = Store16(glyph_buf.get(), glyph_size, end_point); |
| 578 } |
| 579 |
| 580 glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); |
| 581 if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, |
| 582 instruction_size))) { |
507 return FONT_COMPRESSION_FAILURE(); | 583 return FONT_COMPRESSION_FAILURE(); |
508 } | 584 } |
| 585 glyph_size += instruction_size; |
| 586 |
509 if (PREDICT_FALSE(!StorePoints(total_n_points, points.get(), n_contours, | 587 if (PREDICT_FALSE(!StorePoints(total_n_points, points.get(), n_contours, |
510 instruction_size, glyf_dst, glyf_dst_size, &glyph_size))) { | 588 instruction_size, glyph_buf.get(), glyph_buf_size, &glyph_size))) { |
511 return FONT_COMPRESSION_FAILURE(); | 589 return FONT_COMPRESSION_FAILURE(); |
512 } | 590 } |
513 } else { | |
514 glyph_size = 0; | |
515 } | 591 } |
516 loca_values[i] = loca_offset; | 592 |
517 if (PREDICT_FALSE(glyph_size + 3 < glyph_size)) { | 593 loca_values[i] = out->Size() - glyf_start; |
| 594 if (PREDICT_FALSE(!out->Write(glyph_buf.get(), glyph_size))) { |
518 return FONT_COMPRESSION_FAILURE(); | 595 return FONT_COMPRESSION_FAILURE(); |
519 } | 596 } |
520 glyph_size = Round4(glyph_size); | 597 |
521 if (PREDICT_FALSE(glyph_size > dst_size - loca_offset)) { | 598 // TODO(user) Old code aligned glyphs ... but do we actually need to? |
522 // This shouldn't happen, but this test defensively maintains the | 599 if (PREDICT_FALSE(!Pad4(out))) { |
523 // invariant that loca_offset <= dst_size. | |
524 return FONT_COMPRESSION_FAILURE(); | 600 return FONT_COMPRESSION_FAILURE(); |
525 } | 601 } |
526 loca_offset += glyph_size; | 602 |
| 603 *glyf_checksum += ComputeULongSum(glyph_buf.get(), glyph_size); |
| 604 |
| 605 // We may need x_min to reconstruct 'hmtx' |
| 606 Buffer x_min_buf(glyph_buf.get() + 2, 2); |
| 607 int16_t x_min; |
| 608 if (PREDICT_FALSE(!x_min_buf.ReadS16(&x_min))) { |
| 609 return FONT_COMPRESSION_FAILURE(); |
| 610 } |
| 611 |
| 612 info->x_mins[i] = n_contours != 0 ? x_min : 0; |
527 } | 613 } |
528 loca_values[num_glyphs] = loca_offset; | 614 |
529 return StoreLoca(loca_values, index_format, loca_buf, loca_size); | 615 // glyf_table dst_offset was set by ReconstructFont |
| 616 glyf_table->dst_length = out->Size() - glyf_table->dst_offset; |
| 617 loca_table->dst_offset = out->Size(); |
| 618 // loca[n] will be equal the length of the glyph data ('glyf') table |
| 619 loca_values[info->num_glyphs] = glyf_table->dst_length; |
| 620 if (PREDICT_FALSE(!StoreLoca(loca_values, info->index_format, loca_checksum, |
| 621 out))) { |
| 622 return FONT_COMPRESSION_FAILURE(); |
| 623 } |
| 624 loca_table->dst_length = out->Size() - loca_table->dst_offset; |
| 625 |
| 626 return true; |
| 627 } |
| 628 |
| 629 Table* FindTable(std::vector<Table*>* tables, uint32_t tag) { |
| 630 for (Table* table : *tables) { |
| 631 if (table->tag == tag) { |
| 632 return table; |
| 633 } |
| 634 } |
| 635 return NULL; |
530 } | 636 } |
531 | 637 |
532 // This is linear search, but could be changed to binary because we | 638 // This is linear search, but could be changed to binary because we |
533 // do have a guarantee that the tables are sorted by tag. But the total | 639 // do have a guarantee that the tables are sorted by tag. But the total |
534 // cpu time is expected to be very small in any case. | 640 // cpu time is expected to be very small in any case. |
535 const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) { | 641 const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) { |
536 size_t n_tables = tables.size(); | 642 size_t n_tables = tables.size(); |
537 for (size_t i = 0; i < n_tables; ++i) { | 643 for (size_t i = 0; i < n_tables; ++i) { |
538 if (tables[i].tag == tag) { | 644 if (tables[i].tag == tag) { |
539 return &tables[i]; | 645 return &tables[i]; |
540 } | 646 } |
541 } | 647 } |
542 return NULL; | 648 return NULL; |
543 } | 649 } |
544 | 650 |
545 bool ReconstructTransformedGlyf(const uint8_t* transformed_buf, | 651 // https://www.microsoft.com/typography/otspec/maxp.htm |
546 size_t transformed_size, const Table* glyf_table, const Table* loca_table, | 652 bool ReadNumGlyphs(const Table* maxp_table, |
547 uint8_t* dst, size_t dst_length) { | 653 const uint8_t* dst, size_t dst_length, |
548 if (PREDICT_FALSE(glyf_table == NULL || loca_table == NULL)) { | 654 uint16_t* num_glyphs) { |
| 655 if (PREDICT_FALSE(static_cast<uint64_t>(maxp_table->dst_offset + |
| 656 maxp_table->dst_length) > dst_length)) { |
549 return FONT_COMPRESSION_FAILURE(); | 657 return FONT_COMPRESSION_FAILURE(); |
550 } | 658 } |
551 if (PREDICT_FALSE(static_cast<uint64_t>(glyf_table->dst_offset + | 659 Buffer buffer(dst + maxp_table->dst_offset, maxp_table->dst_length); |
552 glyf_table->dst_length) > dst_length)) { | 660 // Skip 4 to reach 'maxp' numGlyphs |
| 661 if (PREDICT_FALSE(!buffer.Skip(4) || !buffer.ReadU16(num_glyphs))) { |
553 return FONT_COMPRESSION_FAILURE(); | 662 return FONT_COMPRESSION_FAILURE(); |
554 } | 663 } |
555 if (PREDICT_FALSE(static_cast<uint64_t>(loca_table->dst_offset + | 664 return true; |
556 loca_table->dst_length) > dst_length)) { | 665 } |
| 666 |
| 667 // Get numberOfHMetrics, https://www.microsoft.com/typography/otspec/hhea.htm |
| 668 bool ReadNumHMetrics(const uint8_t* data, size_t data_size, |
| 669 uint16_t* num_hmetrics) { |
| 670 // Skip 34 to reach 'hhea' numberOfHMetrics |
| 671 Buffer buffer(data, data_size); |
| 672 if (PREDICT_FALSE(!buffer.Skip(34) || !buffer.ReadU16(num_hmetrics))) { |
557 return FONT_COMPRESSION_FAILURE(); | 673 return FONT_COMPRESSION_FAILURE(); |
558 } | 674 } |
559 return ReconstructGlyf(transformed_buf, transformed_size, | 675 return true; |
560 dst + glyf_table->dst_offset, glyf_table->dst_length, | |
561 dst + loca_table->dst_offset, loca_table->dst_length); | |
562 } | 676 } |
563 | 677 |
564 bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag, | 678 // x_min for glyph; https://www.microsoft.com/typography/otspec/glyf.htm |
565 const uint8_t* transformed_buf, size_t transformed_size, | 679 bool ReadGlyphXMin(Buffer* glyf_buff, Buffer* loca_buff, int16_t loca_format, |
566 uint8_t* dst, size_t dst_length) { | 680 uint16_t index, int16_t* x_min) { |
567 if (tag == kGlyfTableTag) { | 681 uint32_t offset1, offset2; |
568 const Table* glyf_table = FindTable(tables, tag); | 682 loca_buff->set_offset((loca_format == 0 ? 2 : 4) * index); |
569 const Table* loca_table = FindTable(tables, kLocaTableTag); | 683 if (loca_format == 0) { |
570 return ReconstructTransformedGlyf(transformed_buf, transformed_size, | 684 uint16_t tmp1, tmp2; |
571 glyf_table, loca_table, dst, dst_length); | 685 if (PREDICT_FALSE(!loca_buff->ReadU16(&tmp1) || |
572 } else if (tag == kLocaTableTag) { | 686 !loca_buff->ReadU16(&tmp2))) { |
573 // processing was already done by glyf table, but validate | 687 return FONT_COMPRESSION_FAILURE(); |
574 if (PREDICT_FALSE(!FindTable(tables, kGlyfTableTag))) { | 688 } |
| 689 // https://www.microsoft.com/typography/otspec/loca.htm |
| 690 // "The actual local offset divided by 2 is stored." |
| 691 offset1 = tmp1 * 2; |
| 692 offset2 = tmp2 * 2; |
| 693 } else if (PREDICT_FALSE(!loca_buff->ReadU32(&offset1) || |
| 694 !loca_buff->ReadU32(&offset2))) { |
| 695 return FONT_COMPRESSION_FAILURE(); |
| 696 } |
| 697 |
| 698 if (offset1 != offset2) { |
| 699 glyf_buff->set_offset(offset1 + 2); |
| 700 if (!glyf_buff->ReadS16(x_min)) { |
575 return FONT_COMPRESSION_FAILURE(); | 701 return FONT_COMPRESSION_FAILURE(); |
576 } | 702 } |
577 } else { | 703 } else { |
578 // transform for the tag is not known | 704 *x_min = 0; |
| 705 } |
| 706 return true; |
| 707 } |
| 708 |
| 709 // http://dev.w3.org/webfonts/WOFF2/spec/Overview.html#hmtx_table_format |
| 710 bool ReconstructTransformedHmtx(const uint8_t* transformed_buf, |
| 711 size_t transformed_size, |
| 712 uint16_t num_glyphs, |
| 713 uint16_t num_hmetrics, |
| 714 const std::vector<int16_t>& x_mins, |
| 715 uint32_t* checksum, |
| 716 WOFF2Out* out) { |
| 717 Buffer hmtx_buff_in(transformed_buf, transformed_size); |
| 718 |
| 719 uint8_t hmtx_flags; |
| 720 if (PREDICT_FALSE(!hmtx_buff_in.ReadU8(&hmtx_flags))) { |
579 return FONT_COMPRESSION_FAILURE(); | 721 return FONT_COMPRESSION_FAILURE(); |
580 } | 722 } |
| 723 |
| 724 std::vector<uint16_t> advance_widths; |
| 725 std::vector<int16_t> lsbs; |
| 726 bool has_proportional_lsbs = (hmtx_flags & 1) == 0; |
| 727 bool has_monospace_lsbs = (hmtx_flags & 2) == 0; |
| 728 |
| 729 // you say you transformed but there is little evidence of it |
| 730 if (has_proportional_lsbs && has_monospace_lsbs) { |
| 731 return FONT_COMPRESSION_FAILURE(); |
| 732 } |
| 733 |
| 734 assert(x_mins.size() == num_glyphs); |
| 735 |
| 736 for (uint16_t i = 0; i < num_hmetrics; i++) { |
| 737 uint16_t advance_width; |
| 738 if (PREDICT_FALSE(!hmtx_buff_in.ReadU16(&advance_width))) { |
| 739 return FONT_COMPRESSION_FAILURE(); |
| 740 } |
| 741 advance_widths.push_back(advance_width); |
| 742 } |
| 743 |
| 744 for (uint16_t i = 0; i < num_hmetrics; i++) { |
| 745 int16_t lsb; |
| 746 if (has_proportional_lsbs) { |
| 747 if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) { |
| 748 return FONT_COMPRESSION_FAILURE(); |
| 749 } |
| 750 } else { |
| 751 lsb = x_mins[i]; |
| 752 } |
| 753 lsbs.push_back(lsb); |
| 754 } |
| 755 |
| 756 for (uint16_t i = num_hmetrics; i < num_glyphs; i++) { |
| 757 int16_t lsb; |
| 758 if (has_monospace_lsbs) { |
| 759 if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) { |
| 760 return FONT_COMPRESSION_FAILURE(); |
| 761 } |
| 762 } else { |
| 763 lsb = x_mins[i]; |
| 764 } |
| 765 lsbs.push_back(lsb); |
| 766 } |
| 767 |
| 768 // bake me a shiny new hmtx table |
| 769 uint32_t hmtx_output_size = 2 * num_glyphs + 2 * num_hmetrics; |
| 770 std::vector<uint8_t> hmtx_table(hmtx_output_size); |
| 771 uint8_t* dst = &hmtx_table[0]; |
| 772 size_t dst_offset = 0; |
| 773 for (uint32_t i = 0; i < num_glyphs; i++) { |
| 774 if (i < num_hmetrics) { |
| 775 Store16(advance_widths[i], &dst_offset, dst); |
| 776 } |
| 777 Store16(lsbs[i], &dst_offset, dst); |
| 778 } |
| 779 |
| 780 *checksum = ComputeULongSum(&hmtx_table[0], hmtx_output_size); |
| 781 if (PREDICT_FALSE(!out->Write(&hmtx_table[0], hmtx_output_size))) { |
| 782 return FONT_COMPRESSION_FAILURE(); |
| 783 } |
| 784 |
581 return true; | 785 return true; |
582 } | 786 } |
583 | 787 |
584 uint32_t ComputeChecksum(const Table* table, const uint8_t* dst) { | 788 uint32_t ComputeChecksum(const Table* table, const uint8_t* dst) { |
585 return ComputeULongSum(dst + table->dst_offset, table->dst_length); | 789 return ComputeULongSum(dst + table->dst_offset, table->dst_length); |
586 } | 790 } |
587 | 791 |
588 const Table* FindTable(TtcFont ttc_font, const std::vector<Table>& tables, | 792 const Table* FindTable(TtcFont ttc_font, const std::vector<Table>& tables, |
589 uint32_t tag) { | 793 uint32_t tag) { |
590 for (const auto i : ttc_font.table_indices) { | 794 for (const auto i : ttc_font.table_indices) { |
591 if (tables[i].tag == tag) return &tables[i]; | 795 if (tables[i].tag == tag) return &tables[i]; |
592 } | 796 } |
593 return NULL; | 797 return NULL; |
594 } | 798 } |
595 | 799 |
596 bool FixCollectionChecksums(size_t header_version, | |
597 const std::vector<Table>& tables, const std::vector<TtcFont>& ttc_fonts, | |
598 uint8_t* dst) { | |
599 size_t offset = CollectionHeaderSize(header_version, ttc_fonts.size()); | |
600 | |
601 for (const auto& ttc_font : ttc_fonts) { | |
602 offset += 12; // move to start of Offset Table | |
603 const std::vector<uint16_t>& table_indices = ttc_font.table_indices; | |
604 | |
605 const Table* head_table = FindTable(ttc_font, tables, kHeadTableTag); | |
606 if (PREDICT_FALSE(head_table == NULL || | |
607 head_table->dst_length < kCheckSumAdjustmentOffset + 4)) { | |
608 return FONT_COMPRESSION_FAILURE(); | |
609 } | |
610 | |
611 size_t first_table_offset = std::numeric_limits<size_t>::max(); | |
612 for (const auto index : table_indices) { | |
613 const auto& table = tables[index]; | |
614 if (table.dst_offset < first_table_offset) { | |
615 first_table_offset = table.dst_offset; | |
616 } | |
617 } | |
618 | |
619 size_t adjustment_offset = head_table->dst_offset | |
620 + kCheckSumAdjustmentOffset; | |
621 StoreU32(dst, adjustment_offset, 0); | |
622 | |
623 uint32_t file_checksum = 0; | |
624 // compute each tables checksum | |
625 for (size_t i = 0; i < table_indices.size(); i++) { | |
626 const Table& table = tables[table_indices[i]]; | |
627 uint32_t table_checksum = ComputeChecksum(&table, dst); | |
628 size_t checksum_offset = offset + 4; // skip past tag to checkSum | |
629 | |
630 // write the checksum for the Table Record | |
631 StoreU32(dst, checksum_offset, table_checksum); | |
632 file_checksum += table_checksum; | |
633 // next Table Record | |
634 offset += 16; | |
635 } | |
636 | |
637 size_t header_size = kSfntHeaderSize + | |
638 kSfntEntrySize * table_indices.size(); | |
639 uint32_t header_checksum = ComputeULongSum(dst + ttc_font.dst_offset, | |
640 header_size); | |
641 | |
642 file_checksum += header_checksum; | |
643 uint32_t checksum_adjustment = 0xb1b0afba - file_checksum; | |
644 StoreU32(dst, adjustment_offset, checksum_adjustment); | |
645 } | |
646 | |
647 return true; | |
648 } | |
649 | |
650 bool FixChecksums(const std::vector<Table>& tables, uint8_t* dst) { | |
651 const Table* head_table = FindTable(tables, kHeadTableTag); | |
652 if (PREDICT_FALSE(head_table == NULL || | |
653 head_table->dst_length < kCheckSumAdjustmentOffset + 4)) { | |
654 return FONT_COMPRESSION_FAILURE(); | |
655 } | |
656 size_t adjustment_offset = head_table->dst_offset + kCheckSumAdjustmentOffset; | |
657 StoreU32(dst, adjustment_offset, 0); | |
658 size_t n_tables = tables.size(); | |
659 uint32_t file_checksum = 0; | |
660 for (size_t i = 0; i < n_tables; ++i) { | |
661 uint32_t checksum = ComputeChecksum(&tables[i], dst); | |
662 StoreU32(dst, kSfntHeaderSize + i * kSfntEntrySize + 4, checksum); | |
663 file_checksum += checksum; | |
664 } | |
665 file_checksum += ComputeULongSum(dst, | |
666 kSfntHeaderSize + kSfntEntrySize * n_tables); | |
667 uint32_t checksum_adjustment = 0xb1b0afba - file_checksum; | |
668 StoreU32(dst, adjustment_offset, checksum_adjustment); | |
669 return true; | |
670 } | |
671 | |
672 bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size, | 800 bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size, |
673 const uint8_t* src_buf, size_t src_size) { | 801 const uint8_t* src_buf, size_t src_size) { |
674 size_t uncompressed_size = dst_size; | 802 size_t uncompressed_size = dst_size; |
675 int ok = BrotliDecompressBuffer(src_size, src_buf, | 803 int ok = BrotliDecompressBuffer(src_size, src_buf, |
676 &uncompressed_size, dst_buf); | 804 &uncompressed_size, dst_buf); |
677 if (PREDICT_FALSE(!ok || uncompressed_size != dst_size)) { | 805 if (PREDICT_FALSE(!ok || uncompressed_size != dst_size)) { |
678 return FONT_COMPRESSION_FAILURE(); | 806 return FONT_COMPRESSION_FAILURE(); |
679 } | 807 } |
680 return true; | 808 return true; |
681 } | 809 } |
682 | 810 |
683 bool ReadTableDirectory(Buffer* file, std::vector<Table>* tables, | 811 bool ReadTableDirectory(Buffer* file, std::vector<Table>* tables, |
684 size_t num_tables) { | 812 size_t num_tables) { |
| 813 uint32_t src_offset = 0; |
685 for (size_t i = 0; i < num_tables; ++i) { | 814 for (size_t i = 0; i < num_tables; ++i) { |
686 Table* table = &(*tables)[i]; | 815 Table* table = &(*tables)[i]; |
687 uint8_t flag_byte; | 816 uint8_t flag_byte; |
688 if (PREDICT_FALSE(!file->ReadU8(&flag_byte))) { | 817 if (PREDICT_FALSE(!file->ReadU8(&flag_byte))) { |
689 return FONT_COMPRESSION_FAILURE(); | 818 return FONT_COMPRESSION_FAILURE(); |
690 } | 819 } |
691 uint32_t tag; | 820 uint32_t tag; |
692 if ((flag_byte & 0x3f) == 0x3f) { | 821 if ((flag_byte & 0x3f) == 0x3f) { |
693 if (PREDICT_FALSE(!file->ReadU32(&tag))) { | 822 if (PREDICT_FALSE(!file->ReadU32(&tag))) { |
694 return FONT_COMPRESSION_FAILURE(); | 823 return FONT_COMPRESSION_FAILURE(); |
695 } | 824 } |
696 } else { | 825 } else { |
697 tag = kKnownTags[flag_byte & 0x3f]; | 826 tag = kKnownTags[flag_byte & 0x3f]; |
698 } | 827 } |
699 // Bits 6 and 7 are reserved and must be 0. | |
700 if (PREDICT_FALSE((flag_byte & 0xC0)) != 0) { | |
701 return FONT_COMPRESSION_FAILURE(); | |
702 } | |
703 uint32_t flags = 0; | 828 uint32_t flags = 0; |
704 // Always transform the glyf and loca tables | 829 uint8_t xform_version = (flag_byte >> 6) & 0x03; |
| 830 |
| 831 // 0 means xform for glyph/loca, non-0 for others |
705 if (tag == kGlyfTableTag || tag == kLocaTableTag) { | 832 if (tag == kGlyfTableTag || tag == kLocaTableTag) { |
| 833 if (xform_version == 0) { |
| 834 flags |= kWoff2FlagsTransform; |
| 835 } |
| 836 } else if (xform_version != 0) { |
706 flags |= kWoff2FlagsTransform; | 837 flags |= kWoff2FlagsTransform; |
707 } | 838 } |
| 839 flags |= xform_version; |
| 840 |
708 uint32_t dst_length; | 841 uint32_t dst_length; |
709 if (PREDICT_FALSE(!ReadBase128(file, &dst_length))) { | 842 if (PREDICT_FALSE(!ReadBase128(file, &dst_length))) { |
710 return FONT_COMPRESSION_FAILURE(); | 843 return FONT_COMPRESSION_FAILURE(); |
711 } | 844 } |
712 uint32_t transform_length = dst_length; | 845 uint32_t transform_length = dst_length; |
713 if ((flags & kWoff2FlagsTransform) != 0) { | 846 if ((flags & kWoff2FlagsTransform) != 0) { |
714 if (PREDICT_FALSE(!ReadBase128(file, &transform_length))) { | 847 if (PREDICT_FALSE(!ReadBase128(file, &transform_length))) { |
715 return FONT_COMPRESSION_FAILURE(); | 848 return FONT_COMPRESSION_FAILURE(); |
716 } | 849 } |
717 if (PREDICT_FALSE(tag == kLocaTableTag && transform_length)) { | 850 if (PREDICT_FALSE(tag == kLocaTableTag && transform_length)) { |
718 return FONT_COMPRESSION_FAILURE(); | 851 return FONT_COMPRESSION_FAILURE(); |
719 } | 852 } |
720 } | 853 } |
| 854 if (PREDICT_FALSE(src_offset + transform_length < src_offset)) { |
| 855 return FONT_COMPRESSION_FAILURE(); |
| 856 } |
| 857 table->src_offset = src_offset; |
| 858 table->src_length = transform_length; |
| 859 src_offset += transform_length; |
| 860 |
721 table->tag = tag; | 861 table->tag = tag; |
722 table->flags = flags; | 862 table->flags = flags; |
723 table->transform_length = transform_length; | 863 table->transform_length = transform_length; |
724 table->dst_length = dst_length; | 864 table->dst_length = dst_length; |
725 } | 865 } |
726 return true; | 866 return true; |
727 } | 867 } |
728 | 868 |
729 } // namespace | |
730 | |
731 size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) { | |
732 Buffer file(data, length); | |
733 uint32_t total_length; | |
734 | |
735 if (!file.Skip(16) || | |
736 !file.ReadU32(&total_length)) { | |
737 return 0; | |
738 } | |
739 return total_length; | |
740 } | |
741 | |
742 // Writes a single Offset Table entry | 869 // Writes a single Offset Table entry |
743 size_t StoreOffsetTable(uint8_t* result, size_t offset, uint32_t flavor, | 870 size_t StoreOffsetTable(uint8_t* result, size_t offset, uint32_t flavor, |
744 uint16_t num_tables) { | 871 uint16_t num_tables) { |
745 offset = StoreU32(result, offset, flavor); // sfnt version | 872 offset = StoreU32(result, offset, flavor); // sfnt version |
746 offset = Store16(result, offset, num_tables); // num_tables | 873 offset = Store16(result, offset, num_tables); // num_tables |
747 unsigned max_pow2 = 0; | 874 unsigned max_pow2 = 0; |
748 while (1u << (max_pow2 + 1) <= num_tables) { | 875 while (1u << (max_pow2 + 1) <= num_tables) { |
749 max_pow2++; | 876 max_pow2++; |
750 } | 877 } |
751 const uint16_t output_search_range = (1u << max_pow2) << 4; | 878 const uint16_t output_search_range = (1u << max_pow2) << 4; |
752 offset = Store16(result, offset, output_search_range); // searchRange | 879 offset = Store16(result, offset, output_search_range); // searchRange |
753 offset = Store16(result, offset, max_pow2); // entrySelector | 880 offset = Store16(result, offset, max_pow2); // entrySelector |
754 // rangeShift | 881 // rangeShift |
755 offset = Store16(result, offset, (num_tables << 4) - output_search_range); | 882 offset = Store16(result, offset, (num_tables << 4) - output_search_range); |
756 return offset; | 883 return offset; |
757 } | 884 } |
758 | 885 |
759 size_t StoreTableEntry(uint8_t* result, const Table& table, size_t offset) { | 886 size_t StoreTableEntry(uint8_t* result, uint32_t offset, uint32_t tag) { |
760 offset = StoreU32(result, offset, table.tag); | 887 offset = StoreU32(result, offset, tag); |
761 offset = StoreU32(result, offset, 0); // checksum, to fill in later | 888 offset = StoreU32(result, offset, 0); |
762 offset = StoreU32(result, offset, table.dst_offset); | 889 offset = StoreU32(result, offset, 0); |
763 offset = StoreU32(result, offset, table.dst_length); | 890 offset = StoreU32(result, offset, 0); |
764 return offset; | 891 return offset; |
765 } | 892 } |
766 | 893 |
767 // First table goes after all the headers, table directory, etc | 894 // First table goes after all the headers, table directory, etc |
768 uint64_t ComputeOffsetToFirstTable(const uint32_t header_version, | 895 uint64_t ComputeOffsetToFirstTable(const WOFF2Header& hdr) { |
769 const uint16_t num_tables, | |
770 const std::vector<TtcFont>& ttc_fonts) { | |
771 uint64_t offset = kSfntHeaderSize + | 896 uint64_t offset = kSfntHeaderSize + |
772 kSfntEntrySize * static_cast<uint64_t>(num_tables); | 897 kSfntEntrySize * static_cast<uint64_t>(hdr.num_tables); |
773 if (header_version) { | 898 if (hdr.header_version) { |
774 offset = CollectionHeaderSize(header_version, ttc_fonts.size()) | 899 offset = CollectionHeaderSize(hdr.header_version, hdr.ttc_fonts.size()) |
775 + kSfntHeaderSize * ttc_fonts.size(); | 900 + kSfntHeaderSize * hdr.ttc_fonts.size(); |
776 for (const auto& ttc_font : ttc_fonts) { | 901 for (const auto& ttc_font : hdr.ttc_fonts) { |
777 offset += | 902 offset += kSfntEntrySize * ttc_font.table_indices.size(); |
778 kSfntEntrySize * ttc_font.table_indices.size(); | |
779 } | 903 } |
780 } | 904 } |
781 return offset; | 905 return offset; |
782 } | 906 } |
783 | 907 |
784 bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, | 908 std::vector<Table*> Tables(WOFF2Header* hdr, size_t font_index) { |
785 const uint8_t* data, size_t length) { | 909 std::vector<Table*> tables; |
| 910 if (PREDICT_FALSE(hdr->header_version)) { |
| 911 for (auto index : hdr->ttc_fonts[font_index].table_indices) { |
| 912 tables.push_back(&hdr->tables[index]); |
| 913 } |
| 914 } else { |
| 915 for (auto& table : hdr->tables) { |
| 916 tables.push_back(&table); |
| 917 } |
| 918 } |
| 919 return tables; |
| 920 } |
| 921 |
| 922 // Offset tables assumed to have been written in with 0's initially. |
| 923 // WOFF2Header isn't const so we can use [] instead of at() (which upsets FF) |
| 924 bool ReconstructFont(uint8_t* transformed_buf, |
| 925 const uint32_t transformed_buf_size, |
| 926 RebuildMetadata* metadata, |
| 927 WOFF2Header* hdr, |
| 928 size_t font_index, |
| 929 WOFF2Out* out) { |
| 930 size_t dest_offset = out->Size(); |
| 931 uint8_t table_entry[12]; |
| 932 WOFF2FontInfo* info = &metadata->font_infos[font_index]; |
| 933 std::vector<Table*> tables = Tables(hdr, font_index); |
| 934 |
| 935 // 'glyf' without 'loca' doesn't make sense |
| 936 if (PREDICT_FALSE(static_cast<bool>(FindTable(&tables, kGlyfTableTag)) != |
| 937 static_cast<bool>(FindTable(&tables, kLocaTableTag)))) { |
| 938 return FONT_COMPRESSION_FAILURE(); |
| 939 } |
| 940 |
| 941 uint32_t font_checksum = metadata->header_checksum; |
| 942 if (hdr->header_version) { |
| 943 font_checksum = hdr->ttc_fonts[font_index].header_checksum; |
| 944 } |
| 945 |
| 946 uint32_t loca_checksum = 0; |
| 947 for (size_t i = 0; i < tables.size(); i++) { |
| 948 Table& table = *tables[i]; |
| 949 |
| 950 std::pair<uint32_t, uint32_t> checksum_key = {table.tag, table.src_offset}; |
| 951 bool reused = metadata->checksums.find(checksum_key) |
| 952 != metadata->checksums.end(); |
| 953 if (PREDICT_FALSE(font_index == 0 && reused)) { |
| 954 return FONT_COMPRESSION_FAILURE(); |
| 955 } |
| 956 |
| 957 // TODO(user) a collection with optimized hmtx that reused glyf/loca |
| 958 // would fail. We don't optimize hmtx for collections yet. |
| 959 if (PREDICT_FALSE(static_cast<uint64_t>(table.src_offset + table.src_length) |
| 960 > transformed_buf_size)) { |
| 961 return FONT_COMPRESSION_FAILURE(); |
| 962 } |
| 963 |
| 964 if (table.tag == kHheaTableTag) { |
| 965 if (!ReadNumHMetrics(transformed_buf + table.src_offset, |
| 966 table.src_length, &info->num_hmetrics)) { |
| 967 return FONT_COMPRESSION_FAILURE(); |
| 968 } |
| 969 } |
| 970 |
| 971 uint32_t checksum = 0; |
| 972 if (!reused) { |
| 973 if ((table.flags & kWoff2FlagsTransform) != kWoff2FlagsTransform) { |
| 974 if (table.tag == kHeadTableTag) { |
| 975 if (PREDICT_FALSE(table.src_length < 12)) { |
| 976 return FONT_COMPRESSION_FAILURE(); |
| 977 } |
| 978 // checkSumAdjustment = 0 |
| 979 StoreU32(transformed_buf + table.src_offset, 8, 0); |
| 980 } |
| 981 table.dst_offset = dest_offset; |
| 982 checksum = ComputeULongSum(transformed_buf + table.src_offset, |
| 983 table.src_length); |
| 984 out->Write(transformed_buf + table.src_offset, table.src_length); |
| 985 } else { |
| 986 if (table.tag == kGlyfTableTag) { |
| 987 table.dst_offset = dest_offset; |
| 988 |
| 989 Table* loca_table = FindTable(&tables, kLocaTableTag); |
| 990 if (PREDICT_FALSE(!ReconstructGlyf(transformed_buf + table.src_offset, |
| 991 &table, &checksum, loca_table, &loca_checksum, info, out))) { |
| 992 return FONT_COMPRESSION_FAILURE(); |
| 993 } |
| 994 } else if (table.tag == kLocaTableTag) { |
| 995 // All the work was done by ReconstructGlyf. We already know checksum. |
| 996 checksum = loca_checksum; |
| 997 } else if (table.tag == kHmtxTableTag) { |
| 998 table.dst_offset = dest_offset; |
| 999 // Tables are sorted so all the info we need has been gathered. |
| 1000 if (PREDICT_FALSE(!ReconstructTransformedHmtx( |
| 1001 transformed_buf + table.src_offset, table.src_length, |
| 1002 info->num_glyphs, info->num_hmetrics, info->x_mins, &checksum, |
| 1003 out))) { |
| 1004 return FONT_COMPRESSION_FAILURE(); |
| 1005 } |
| 1006 } else { |
| 1007 return FONT_COMPRESSION_FAILURE(); // transform unknown |
| 1008 } |
| 1009 } |
| 1010 metadata->checksums[checksum_key] = checksum; |
| 1011 } else { |
| 1012 checksum = metadata->checksums[checksum_key]; |
| 1013 } |
| 1014 font_checksum += checksum; |
| 1015 |
| 1016 // update the table entry with real values. |
| 1017 StoreU32(table_entry, 0, checksum); |
| 1018 StoreU32(table_entry, 4, table.dst_offset); |
| 1019 StoreU32(table_entry, 8, table.dst_length); |
| 1020 if (PREDICT_FALSE(!out->Write(table_entry, |
| 1021 info->table_entry_by_tag[table.tag] + 4, 12))) { |
| 1022 return FONT_COMPRESSION_FAILURE(); |
| 1023 } |
| 1024 |
| 1025 // We replaced 0's. Update overall checksum. |
| 1026 font_checksum += ComputeULongSum(table_entry, 12); |
| 1027 |
| 1028 if (PREDICT_FALSE(!Pad4(out))) { |
| 1029 return FONT_COMPRESSION_FAILURE(); |
| 1030 } |
| 1031 |
| 1032 if (PREDICT_FALSE(static_cast<uint64_t>(table.dst_offset + table.dst_length) |
| 1033 > out->Size())) { |
| 1034 return FONT_COMPRESSION_FAILURE(); |
| 1035 } |
| 1036 dest_offset = out->Size(); |
| 1037 } |
| 1038 |
| 1039 // Update 'head' checkSumAdjustment. We already set it to 0 and summed font. |
| 1040 Table* head_table = FindTable(&tables, kHeadTableTag); |
| 1041 if (head_table) { |
| 1042 if (PREDICT_FALSE(head_table->dst_length < 12)) { |
| 1043 return FONT_COMPRESSION_FAILURE(); |
| 1044 } |
| 1045 uint8_t checksum_adjustment[4]; |
| 1046 StoreU32(checksum_adjustment, 0, 0xB1B0AFBA - font_checksum); |
| 1047 if (PREDICT_FALSE(!out->Write(checksum_adjustment, |
| 1048 head_table->dst_offset + 8, 4))) { |
| 1049 return FONT_COMPRESSION_FAILURE(); |
| 1050 } |
| 1051 } |
| 1052 |
| 1053 return true; |
| 1054 } |
| 1055 |
| 1056 bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { |
786 Buffer file(data, length); | 1057 Buffer file(data, length); |
787 | 1058 |
788 uint32_t signature; | 1059 uint32_t signature; |
789 uint32_t flavor; | |
790 if (PREDICT_FALSE(!file.ReadU32(&signature) || signature != kWoff2Signature || | 1060 if (PREDICT_FALSE(!file.ReadU32(&signature) || signature != kWoff2Signature || |
791 !file.ReadU32(&flavor))) { | 1061 !file.ReadU32(&hdr->flavor))) { |
792 return FONT_COMPRESSION_FAILURE(); | 1062 return FONT_COMPRESSION_FAILURE(); |
793 } | 1063 } |
794 | 1064 |
795 // TODO(user): Should call IsValidVersionTag() here. | 1065 // TODO(user): Should call IsValidVersionTag() here. |
796 | 1066 |
797 uint32_t reported_length; | 1067 uint32_t reported_length; |
798 if (PREDICT_FALSE( | 1068 if (PREDICT_FALSE( |
799 !file.ReadU32(&reported_length) || length != reported_length)) { | 1069 !file.ReadU32(&reported_length) || length != reported_length)) { |
800 return FONT_COMPRESSION_FAILURE(); | 1070 return FONT_COMPRESSION_FAILURE(); |
801 } | 1071 } |
802 uint16_t num_tables; | 1072 if (PREDICT_FALSE(!file.ReadU16(&hdr->num_tables) || !hdr->num_tables)) { |
803 if (PREDICT_FALSE(!file.ReadU16(&num_tables) || !num_tables)) { | 1073 return FONT_COMPRESSION_FAILURE(); |
| 1074 } |
| 1075 |
| 1076 // We don't care about these fields of the header: |
| 1077 // uint16_t reserved |
| 1078 // uint32_t total_sfnt_size, we don't believe this, will compute later |
| 1079 if (PREDICT_FALSE(!file.Skip(6))) { |
| 1080 return FONT_COMPRESSION_FAILURE(); |
| 1081 } |
| 1082 if (PREDICT_FALSE(!file.ReadU32(&hdr->compressed_length))) { |
804 return FONT_COMPRESSION_FAILURE(); | 1083 return FONT_COMPRESSION_FAILURE(); |
805 } | 1084 } |
806 // We don't care about these fields of the header: | 1085 // We don't care about these fields of the header: |
807 // uint16_t reserved | |
808 // uint32_t total_sfnt_size, the caller already passes it as result_length | |
809 if (PREDICT_FALSE(!file.Skip(6))) { | |
810 return FONT_COMPRESSION_FAILURE(); | |
811 } | |
812 uint32_t compressed_length; | |
813 if (PREDICT_FALSE(!file.ReadU32(&compressed_length))) { | |
814 return FONT_COMPRESSION_FAILURE(); | |
815 } | |
816 // We don't care about these fields of the header: | |
817 // uint16_t major_version, minor_version | 1086 // uint16_t major_version, minor_version |
818 if (PREDICT_FALSE(!file.Skip(2 * 2))) { | 1087 if (PREDICT_FALSE(!file.Skip(2 * 2))) { |
819 return FONT_COMPRESSION_FAILURE(); | 1088 return FONT_COMPRESSION_FAILURE(); |
820 } | 1089 } |
821 uint32_t meta_offset; | 1090 uint32_t meta_offset; |
822 uint32_t meta_length; | 1091 uint32_t meta_length; |
823 uint32_t meta_length_orig; | 1092 uint32_t meta_length_orig; |
824 if (PREDICT_FALSE(!file.ReadU32(&meta_offset) || | 1093 if (PREDICT_FALSE(!file.ReadU32(&meta_offset) || |
825 !file.ReadU32(&meta_length) || | 1094 !file.ReadU32(&meta_length) || |
826 !file.ReadU32(&meta_length_orig))) { | 1095 !file.ReadU32(&meta_length_orig))) { |
(...skipping 10 matching lines...) Expand all Loading... |
837 if (PREDICT_FALSE(!file.ReadU32(&priv_offset) || | 1106 if (PREDICT_FALSE(!file.ReadU32(&priv_offset) || |
838 !file.ReadU32(&priv_length))) { | 1107 !file.ReadU32(&priv_length))) { |
839 return FONT_COMPRESSION_FAILURE(); | 1108 return FONT_COMPRESSION_FAILURE(); |
840 } | 1109 } |
841 if (priv_offset) { | 1110 if (priv_offset) { |
842 if (PREDICT_FALSE( | 1111 if (PREDICT_FALSE( |
843 priv_offset >= length || length - priv_offset < priv_length)) { | 1112 priv_offset >= length || length - priv_offset < priv_length)) { |
844 return FONT_COMPRESSION_FAILURE(); | 1113 return FONT_COMPRESSION_FAILURE(); |
845 } | 1114 } |
846 } | 1115 } |
847 std::vector<Table> tables(num_tables); | 1116 hdr->tables.resize(hdr->num_tables); |
848 if (PREDICT_FALSE(!ReadTableDirectory(&file, &tables, num_tables))) { | 1117 if (PREDICT_FALSE(!ReadTableDirectory( |
| 1118 &file, &hdr->tables, hdr->num_tables))) { |
849 return FONT_COMPRESSION_FAILURE(); | 1119 return FONT_COMPRESSION_FAILURE(); |
850 } | 1120 } |
851 | 1121 |
852 uint32_t header_version = 0; | 1122 // Before we sort for output the last table end is the uncompressed size. |
853 // for each font in a ttc, metadata to use when rebuilding | 1123 Table& last_table = hdr->tables.back(); |
854 std::vector<TtcFont> ttc_fonts; | 1124 hdr->uncompressed_size = last_table.src_offset + last_table.src_length; |
855 std::map<const Table*, const Table*> loca_by_glyf; | 1125 if (PREDICT_FALSE(hdr->uncompressed_size < last_table.src_offset)) { |
| 1126 return FONT_COMPRESSION_FAILURE(); |
| 1127 } |
856 | 1128 |
857 if (flavor == kTtcFontFlavor) { | 1129 hdr->header_version = 0; |
858 if (PREDICT_FALSE(!file.ReadU32(&header_version))) { | 1130 |
| 1131 if (hdr->flavor == kTtcFontFlavor) { |
| 1132 if (PREDICT_FALSE(!file.ReadU32(&hdr->header_version))) { |
| 1133 return FONT_COMPRESSION_FAILURE(); |
| 1134 } |
| 1135 if (PREDICT_FALSE(hdr->header_version != 0x00010000 |
| 1136 && hdr->header_version != 0x00020000)) { |
859 return FONT_COMPRESSION_FAILURE(); | 1137 return FONT_COMPRESSION_FAILURE(); |
860 } | 1138 } |
861 uint32_t num_fonts; | 1139 uint32_t num_fonts; |
862 if (PREDICT_FALSE(!Read255UShort(&file, &num_fonts) || !num_fonts)) { | 1140 if (PREDICT_FALSE(!Read255UShort(&file, &num_fonts) || !num_fonts)) { |
863 return FONT_COMPRESSION_FAILURE(); | 1141 return FONT_COMPRESSION_FAILURE(); |
864 } | 1142 } |
865 ttc_fonts.resize(num_fonts); | 1143 hdr->ttc_fonts.resize(num_fonts); |
866 | 1144 |
867 for (uint32_t i = 0; i < num_fonts; i++) { | 1145 for (uint32_t i = 0; i < num_fonts; i++) { |
868 TtcFont& ttc_font = ttc_fonts[i]; | 1146 TtcFont& ttc_font = hdr->ttc_fonts[i]; |
869 uint32_t num_tables; | 1147 uint32_t num_tables; |
870 if (PREDICT_FALSE(!Read255UShort(&file, &num_tables) || !num_tables)) { | 1148 if (PREDICT_FALSE(!Read255UShort(&file, &num_tables) || !num_tables)) { |
871 return FONT_COMPRESSION_FAILURE(); | 1149 return FONT_COMPRESSION_FAILURE(); |
872 } | 1150 } |
873 if (PREDICT_FALSE(!file.ReadU32(&ttc_font.flavor))) { | 1151 if (PREDICT_FALSE(!file.ReadU32(&ttc_font.flavor))) { |
874 return FONT_COMPRESSION_FAILURE(); | 1152 return FONT_COMPRESSION_FAILURE(); |
875 } | 1153 } |
876 | 1154 |
877 ttc_font.table_indices.resize(num_tables); | 1155 ttc_font.table_indices.resize(num_tables); |
878 | 1156 |
879 const Table* glyf_table = NULL; | 1157 const Table* glyf_table = NULL; |
880 const Table* loca_table = NULL; | 1158 const Table* loca_table = NULL; |
881 uint16_t glyf_idx; | |
882 uint16_t loca_idx; | |
883 | 1159 |
884 for (uint32_t j = 0; j < num_tables; j++) { | 1160 for (uint32_t j = 0; j < num_tables; j++) { |
885 unsigned int table_idx; | 1161 unsigned int table_idx; |
886 if (PREDICT_FALSE(!Read255UShort(&file, &table_idx)) || | 1162 if (PREDICT_FALSE(!Read255UShort(&file, &table_idx)) || |
887 table_idx >= tables.size()) { | 1163 table_idx >= hdr->tables.size()) { |
888 return FONT_COMPRESSION_FAILURE(); | 1164 return FONT_COMPRESSION_FAILURE(); |
889 } | 1165 } |
890 ttc_font.table_indices[j] = table_idx; | 1166 ttc_font.table_indices[j] = table_idx; |
891 | 1167 |
892 const Table& table = tables[table_idx]; | 1168 const Table& table = hdr->tables[table_idx]; |
893 if (table.tag == kLocaTableTag) { | 1169 if (table.tag == kLocaTableTag) { |
894 loca_table = &table; | 1170 loca_table = &table; |
895 loca_idx = table_idx; | |
896 } | 1171 } |
897 if (table.tag == kGlyfTableTag) { | 1172 if (table.tag == kGlyfTableTag) { |
898 glyf_table = &table; | 1173 glyf_table = &table; |
899 glyf_idx = table_idx; | |
900 } | 1174 } |
901 | 1175 |
902 } | 1176 } |
903 | 1177 |
904 if (PREDICT_FALSE((glyf_table == NULL) != (loca_table == NULL))) { | 1178 if (PREDICT_FALSE((glyf_table == NULL) != (loca_table == NULL))) { |
905 #ifdef FONT_COMPRESSION_BIN | 1179 #ifdef FONT_COMPRESSION_BIN |
906 fprintf(stderr, "Cannot have just one of glyf/loca\n"); | 1180 fprintf(stderr, "Cannot have just one of glyf/loca\n"); |
907 #endif | 1181 #endif |
908 return FONT_COMPRESSION_FAILURE(); | 1182 return FONT_COMPRESSION_FAILURE(); |
909 } | 1183 } |
910 | |
911 if (glyf_table != NULL && loca_table != NULL) { | |
912 loca_by_glyf[glyf_table] = loca_table; | |
913 } | |
914 } | 1184 } |
915 } | 1185 } |
916 | 1186 |
917 const uint64_t first_table_offset = | 1187 const uint64_t first_table_offset = ComputeOffsetToFirstTable(*hdr); |
918 ComputeOffsetToFirstTable(header_version, num_tables, ttc_fonts); | |
919 | 1188 |
920 if (PREDICT_FALSE(first_table_offset > result_length)) { | 1189 hdr->compressed_offset = file.offset(); |
| 1190 if (PREDICT_FALSE(hdr->compressed_offset > |
| 1191 std::numeric_limits<uint32_t>::max())) { |
921 return FONT_COMPRESSION_FAILURE(); | 1192 return FONT_COMPRESSION_FAILURE(); |
922 } | 1193 } |
923 | 1194 uint64_t src_offset = Round4(hdr->compressed_offset + hdr->compressed_length); |
924 uint64_t compressed_offset = file.offset(); | |
925 if (PREDICT_FALSE(compressed_offset > std::numeric_limits<uint32_t>::max())) { | |
926 return FONT_COMPRESSION_FAILURE(); | |
927 } | |
928 uint64_t src_offset = Round4(compressed_offset + compressed_length); | |
929 uint64_t dst_offset = first_table_offset; | 1195 uint64_t dst_offset = first_table_offset; |
930 | 1196 |
931 | 1197 |
932 for (uint16_t i = 0; i < num_tables; ++i) { | 1198 if (PREDICT_FALSE(src_offset > length)) { |
933 Table* table = &tables[i]; | |
934 table->dst_offset = dst_offset; | |
935 dst_offset += table->dst_length; | |
936 if (PREDICT_FALSE(dst_offset > std::numeric_limits<uint32_t>::max())) { | |
937 return FONT_COMPRESSION_FAILURE(); | |
938 } | |
939 dst_offset = Round4(dst_offset); | |
940 } | |
941 if (PREDICT_FALSE(src_offset > length || dst_offset != result_length)) { | |
942 #ifdef FONT_COMPRESSION_BIN | 1199 #ifdef FONT_COMPRESSION_BIN |
943 fprintf(stderr, "offset fail; src_offset %" PRIu64 " length %lu " | 1200 fprintf(stderr, "offset fail; src_offset %" PRIu64 " length %lu " |
944 "dst_offset %" PRIu64 " result_length %lu\n", | 1201 "dst_offset %" PRIu64 "\n", |
945 src_offset, length, dst_offset, result_length); | 1202 src_offset, length, dst_offset); |
946 #endif | 1203 #endif |
947 return FONT_COMPRESSION_FAILURE(); | 1204 return FONT_COMPRESSION_FAILURE(); |
948 } | 1205 } |
949 | |
950 // Re-order tables in output (OTSpec) order | |
951 std::vector<Table> sorted_tables(tables); | |
952 if (header_version) { | |
953 // collection; we have to sort the table offset vector in each font | |
954 for (auto& ttc_font : ttc_fonts) { | |
955 std::map<uint32_t, uint16_t> sorted_index_by_tag; | |
956 for (auto table_index : ttc_font.table_indices) { | |
957 sorted_index_by_tag[tables[table_index].tag] = table_index; | |
958 } | |
959 uint16_t index = 0; | |
960 for (auto& i : sorted_index_by_tag) { | |
961 ttc_font.table_indices[index++] = i.second; | |
962 } | |
963 } | |
964 } else { | |
965 // non-collection; we can just sort the tables | |
966 std::sort(sorted_tables.begin(), sorted_tables.end()); | |
967 } | |
968 | |
969 if (meta_offset) { | 1206 if (meta_offset) { |
970 if (PREDICT_FALSE(src_offset != meta_offset)) { | 1207 if (PREDICT_FALSE(src_offset != meta_offset)) { |
971 return FONT_COMPRESSION_FAILURE(); | 1208 return FONT_COMPRESSION_FAILURE(); |
972 } | 1209 } |
973 src_offset = Round4(meta_offset + meta_length); | 1210 src_offset = Round4(meta_offset + meta_length); |
974 if (PREDICT_FALSE(src_offset > std::numeric_limits<uint32_t>::max())) { | 1211 if (PREDICT_FALSE(src_offset > std::numeric_limits<uint32_t>::max())) { |
975 return FONT_COMPRESSION_FAILURE(); | 1212 return FONT_COMPRESSION_FAILURE(); |
976 } | 1213 } |
977 } | 1214 } |
978 | 1215 |
979 if (priv_offset) { | 1216 if (priv_offset) { |
980 if (PREDICT_FALSE(src_offset != priv_offset)) { | 1217 if (PREDICT_FALSE(src_offset != priv_offset)) { |
981 return FONT_COMPRESSION_FAILURE(); | 1218 return FONT_COMPRESSION_FAILURE(); |
982 } | 1219 } |
983 src_offset = Round4(priv_offset + priv_length); | 1220 src_offset = Round4(priv_offset + priv_length); |
984 if (PREDICT_FALSE(src_offset > std::numeric_limits<uint32_t>::max())) { | 1221 if (PREDICT_FALSE(src_offset > std::numeric_limits<uint32_t>::max())) { |
985 return FONT_COMPRESSION_FAILURE(); | 1222 return FONT_COMPRESSION_FAILURE(); |
986 } | 1223 } |
987 } | 1224 } |
988 | 1225 |
989 if (PREDICT_FALSE(src_offset != Round4(length))) { | 1226 if (PREDICT_FALSE(src_offset != Round4(length))) { |
990 return FONT_COMPRESSION_FAILURE(); | 1227 return FONT_COMPRESSION_FAILURE(); |
991 } | 1228 } |
992 | 1229 |
| 1230 return true; |
| 1231 } |
| 1232 |
| 1233 // Write everything before the actual table data |
| 1234 bool WriteHeaders(const uint8_t* data, size_t length, RebuildMetadata* metadata, |
| 1235 WOFF2Header* hdr, WOFF2Out* out) { |
| 1236 std::vector<uint8_t> output(ComputeOffsetToFirstTable(*hdr), 0); |
| 1237 |
| 1238 // Re-order tables in output (OTSpec) order |
| 1239 std::vector<Table> sorted_tables(hdr->tables); |
| 1240 if (hdr->header_version) { |
| 1241 // collection; we have to sort the table offset vector in each font |
| 1242 for (auto& ttc_font : hdr->ttc_fonts) { |
| 1243 std::map<uint32_t, uint16_t> sorted_index_by_tag; |
| 1244 for (auto table_index : ttc_font.table_indices) { |
| 1245 sorted_index_by_tag[hdr->tables[table_index].tag] = table_index; |
| 1246 } |
| 1247 uint16_t index = 0; |
| 1248 for (auto& i : sorted_index_by_tag) { |
| 1249 ttc_font.table_indices[index++] = i.second; |
| 1250 } |
| 1251 } |
| 1252 } else { |
| 1253 // non-collection; we can just sort the tables |
| 1254 std::sort(sorted_tables.begin(), sorted_tables.end()); |
| 1255 } |
| 1256 |
993 // Start building the font | 1257 // Start building the font |
| 1258 uint8_t* result = &output[0]; |
994 size_t offset = 0; | 1259 size_t offset = 0; |
995 size_t offset_table = 0; | 1260 if (hdr->header_version) { |
996 if (header_version) { | |
997 // TTC header | 1261 // TTC header |
998 offset = StoreU32(result, offset, flavor); // TAG TTCTag | 1262 offset = StoreU32(result, offset, hdr->flavor); // TAG TTCTag |
999 offset = StoreU32(result, offset, header_version); // FIXED Version | 1263 offset = StoreU32(result, offset, hdr->header_version); // FIXED Version |
1000 offset = StoreU32(result, offset, ttc_fonts.size()); // ULONG numFonts | 1264 offset = StoreU32(result, offset, hdr->ttc_fonts.size()); // ULONG numFonts |
1001 // Space for ULONG OffsetTable[numFonts] (zeroed initially) | 1265 // Space for ULONG OffsetTable[numFonts] (zeroed initially) |
1002 offset_table = offset; // keep start of offset table for later | 1266 size_t offset_table = offset; // keep start of offset table for later |
1003 for (size_t i = 0; i < ttc_fonts.size(); i++) { | 1267 for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) { |
1004 offset = StoreU32(result, offset, 0); // will fill real values in later | 1268 offset = StoreU32(result, offset, 0); // will fill real values in later |
1005 } | 1269 } |
1006 // space for DSIG fields for header v2 | 1270 // space for DSIG fields for header v2 |
1007 if (header_version == 0x00020000) { | 1271 if (hdr->header_version == 0x00020000) { |
1008 offset = StoreU32(result, offset, 0); // ULONG ulDsigTag | 1272 offset = StoreU32(result, offset, 0); // ULONG ulDsigTag |
1009 offset = StoreU32(result, offset, 0); // ULONG ulDsigLength | 1273 offset = StoreU32(result, offset, 0); // ULONG ulDsigLength |
1010 offset = StoreU32(result, offset, 0); // ULONG ulDsigOffset | 1274 offset = StoreU32(result, offset, 0); // ULONG ulDsigOffset |
1011 } | 1275 } |
1012 | 1276 |
1013 // write Offset Tables and store the location of each in TTC Header | 1277 // write Offset Tables and store the location of each in TTC Header |
1014 for (auto& ttc_font : ttc_fonts) { | 1278 metadata->font_infos.resize(hdr->ttc_fonts.size()); |
| 1279 for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) { |
| 1280 TtcFont& ttc_font = hdr->ttc_fonts[i]; |
| 1281 |
1015 // write Offset Table location into TTC Header | 1282 // write Offset Table location into TTC Header |
1016 offset_table = StoreU32(result, offset_table, offset); | 1283 offset_table = StoreU32(result, offset_table, offset); |
1017 | 1284 |
1018 // write the actual offset table so our header doesn't lie | 1285 // write the actual offset table so our header doesn't lie |
1019 ttc_font.dst_offset = offset; | 1286 ttc_font.dst_offset = offset; |
1020 offset = StoreOffsetTable(result, offset, ttc_font.flavor, | 1287 offset = StoreOffsetTable(result, offset, ttc_font.flavor, |
1021 ttc_font.table_indices.size()); | 1288 ttc_font.table_indices.size()); |
1022 | 1289 |
1023 // write table entries | |
1024 for (const auto table_index : ttc_font.table_indices) { | 1290 for (const auto table_index : ttc_font.table_indices) { |
1025 offset = StoreTableEntry(result, tables[table_index], offset); | 1291 uint32_t tag = hdr->tables[table_index].tag; |
| 1292 metadata->font_infos[i].table_entry_by_tag[tag] = offset; |
| 1293 offset = StoreTableEntry(result, offset, tag); |
1026 } | 1294 } |
| 1295 |
| 1296 ttc_font.header_checksum = ComputeULongSum(&output[ttc_font.dst_offset], |
| 1297 offset - ttc_font.dst_offset); |
1027 } | 1298 } |
1028 } else { | 1299 } else { |
1029 offset = StoreOffsetTable(result, offset, flavor, num_tables); | 1300 metadata->font_infos.resize(1); |
1030 for (uint16_t i = 0; i < num_tables; ++i) { | 1301 offset = StoreOffsetTable(result, offset, hdr->flavor, hdr->num_tables); |
1031 offset = StoreTableEntry(result, sorted_tables[i], offset); | 1302 for (uint16_t i = 0; i < hdr->num_tables; ++i) { |
| 1303 metadata->font_infos[0].table_entry_by_tag[sorted_tables[i].tag] = offset; |
| 1304 offset = StoreTableEntry(result, offset, sorted_tables[i].tag); |
1032 } | 1305 } |
1033 } | 1306 } |
1034 | 1307 |
1035 std::vector<uint8_t> uncompressed_buf; | 1308 if (PREDICT_FALSE(!out->Write(&output[0], output.size()))) { |
1036 const uint8_t* transform_buf = NULL; | 1309 return FONT_COMPRESSION_FAILURE(); |
1037 uint64_t total_size = 0; | 1310 } |
1038 for (uint16_t i = 0; i < num_tables; ++i) { | 1311 metadata->header_checksum = ComputeULongSum(&output[0], output.size()); |
1039 total_size += tables[i].transform_length; | 1312 return true; |
1040 if (PREDICT_FALSE(total_size > std::numeric_limits<uint32_t>::max())) { | 1313 } |
| 1314 |
| 1315 } // namespace |
| 1316 |
| 1317 size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) { |
| 1318 Buffer file(data, length); |
| 1319 uint32_t total_length; |
| 1320 |
| 1321 if (!file.Skip(16) || |
| 1322 !file.ReadU32(&total_length)) { |
| 1323 return 0; |
| 1324 } |
| 1325 return total_length; |
| 1326 } |
| 1327 |
| 1328 bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length, |
| 1329 const uint8_t *data, size_t length) { |
| 1330 WOFF2MemoryOut out(result, result_length); |
| 1331 return ConvertWOFF2ToTTF(data, length, &out); |
| 1332 } |
| 1333 |
| 1334 bool ConvertWOFF2ToTTF(const uint8_t* data, size_t length, |
| 1335 WOFF2Out* out) { |
| 1336 RebuildMetadata metadata; |
| 1337 WOFF2Header hdr; |
| 1338 if (!ReadWOFF2Header(data, length, &hdr)) { |
| 1339 return FONT_COMPRESSION_FAILURE(); |
| 1340 } |
| 1341 |
| 1342 if (!WriteHeaders(data, length, &metadata, &hdr, out)) { |
| 1343 return FONT_COMPRESSION_FAILURE(); |
| 1344 } |
| 1345 |
| 1346 const uint8_t* src_buf = data + hdr.compressed_offset; |
| 1347 std::vector<uint8_t> uncompressed_buf(hdr.uncompressed_size); |
| 1348 if (PREDICT_FALSE(!Woff2Uncompress(&uncompressed_buf[0], |
| 1349 hdr.uncompressed_size, src_buf, |
| 1350 hdr.compressed_length))) { |
| 1351 return FONT_COMPRESSION_FAILURE(); |
| 1352 } |
| 1353 |
| 1354 for (size_t i = 0; i < metadata.font_infos.size(); i++) { |
| 1355 if (PREDICT_FALSE(!ReconstructFont(&uncompressed_buf[0], |
| 1356 hdr.uncompressed_size, |
| 1357 &metadata, &hdr, i, out))) { |
1041 return FONT_COMPRESSION_FAILURE(); | 1358 return FONT_COMPRESSION_FAILURE(); |
1042 } | 1359 } |
1043 } | 1360 } |
1044 uncompressed_buf.resize(total_size); | |
1045 const uint8_t* src_buf = data + compressed_offset; | |
1046 if (PREDICT_FALSE(!Woff2Uncompress(&uncompressed_buf[0], total_size, | |
1047 src_buf, compressed_length))) { | |
1048 return FONT_COMPRESSION_FAILURE(); | |
1049 } | |
1050 transform_buf = &uncompressed_buf[0]; | |
1051 | |
1052 for (uint16_t i = 0; i < num_tables; ++i) { | |
1053 const Table* table = &tables[i]; | |
1054 uint32_t flags = table->flags; | |
1055 size_t transform_length = table->transform_length; | |
1056 | |
1057 if ((flags & kWoff2FlagsTransform) == 0) { | |
1058 if (PREDICT_FALSE(transform_length != table->dst_length)) { | |
1059 return FONT_COMPRESSION_FAILURE(); | |
1060 } | |
1061 if (PREDICT_FALSE(static_cast<uint64_t>(table->dst_offset + | |
1062 transform_length) > result_length)) { | |
1063 return FONT_COMPRESSION_FAILURE(); | |
1064 } | |
1065 | |
1066 std::memcpy(result + table->dst_offset, transform_buf, | |
1067 transform_length); | |
1068 } else { | |
1069 if (header_version) { | |
1070 if (table->tag == kGlyfTableTag) { | |
1071 const Table* loca_table = loca_by_glyf[table]; | |
1072 if (PREDICT_FALSE(!ReconstructTransformedGlyf(transform_buf, | |
1073 transform_length, table, loca_table, result, result_length))) { | |
1074 return FONT_COMPRESSION_FAILURE(); | |
1075 } | |
1076 } else if (PREDICT_FALSE(table->tag != kLocaTableTag)) { | |
1077 // transform for this tag not known | |
1078 return FONT_COMPRESSION_FAILURE(); | |
1079 } | |
1080 } else { | |
1081 if (PREDICT_FALSE(!ReconstructTransformed(tables, table->tag, | |
1082 transform_buf, transform_length, result, result_length))) { | |
1083 return FONT_COMPRESSION_FAILURE(); | |
1084 } | |
1085 } | |
1086 } | |
1087 transform_buf += transform_length; | |
1088 if (PREDICT_FALSE( | |
1089 transform_buf > &uncompressed_buf[0] + uncompressed_buf.size())) { | |
1090 return FONT_COMPRESSION_FAILURE(); | |
1091 } | |
1092 } | |
1093 | |
1094 if (header_version) { | |
1095 if (PREDICT_FALSE( | |
1096 !FixCollectionChecksums(header_version, tables, ttc_fonts, result))) { | |
1097 return FONT_COMPRESSION_FAILURE(); | |
1098 } | |
1099 } else { | |
1100 if (PREDICT_FALSE(!FixChecksums(sorted_tables, result))) { | |
1101 return FONT_COMPRESSION_FAILURE(); | |
1102 } | |
1103 } | |
1104 | 1361 |
1105 return true; | 1362 return true; |
1106 } | 1363 } |
1107 | 1364 |
1108 } // namespace woff2 | 1365 } // namespace woff2 |
OLD | NEW |