1 // Copyright 2014 Google Inc. All Rights Reserved.
3 // Licensed under the Apache License, Version 2.0 (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
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 // Library for converting WOFF2 format font files to their TTF versions.
17 #include "./woff2_dec.h"
30 #include "./store_bytes.h"
31 #include "./table_tags.h"
32 #include "./woff2_common.h"
43 const int kGlyfOnCurve
= 1 << 0;
44 const int kGlyfXShort
= 1 << 1;
45 const int kGlyfYShort
= 1 << 2;
46 const int kGlyfRepeat
= 1 << 3;
47 const int kGlyfThisXIsSame
= 1 << 4;
48 const int kGlyfThisYIsSame
= 1 << 5;
50 // composite glyph flags
51 // See CompositeGlyph.java in sfntly for full definitions
52 const int FLAG_ARG_1_AND_2_ARE_WORDS
= 1 << 0;
53 const int FLAG_WE_HAVE_A_SCALE
= 1 << 3;
54 const int FLAG_MORE_COMPONENTS
= 1 << 5;
55 const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE
= 1 << 6;
56 const int FLAG_WE_HAVE_A_TWO_BY_TWO
= 1 << 7;
57 const int FLAG_WE_HAVE_INSTRUCTIONS
= 1 << 8;
59 const size_t kSfntHeaderSize
= 12;
60 const size_t kSfntEntrySize
= 16;
61 const size_t kCheckSumAdjustmentOffset
= 8;
63 const size_t kEndPtsOfContoursOffset
= 10;
64 const size_t kCompositeGlyphBegin
= 10;
66 // Based on section 6.1.1 of MicroType Express draft spec
67 bool Read255UShort(Buffer
* buf
, unsigned int* value
) {
68 static const int kWordCode
= 253;
69 static const int kOneMoreByteCode2
= 254;
70 static const int kOneMoreByteCode1
= 255;
71 static const int kLowestUCode
= 253;
73 if (!buf
->ReadU8(&code
)) {
74 return FONT_COMPRESSION_FAILURE();
76 if (code
== kWordCode
) {
78 if (!buf
->ReadU16(&result
)) {
79 return FONT_COMPRESSION_FAILURE();
83 } else if (code
== kOneMoreByteCode1
) {
85 if (!buf
->ReadU8(&result
)) {
86 return FONT_COMPRESSION_FAILURE();
88 *value
= result
+ kLowestUCode
;
90 } else if (code
== kOneMoreByteCode2
) {
92 if (!buf
->ReadU8(&result
)) {
93 return FONT_COMPRESSION_FAILURE();
95 *value
= result
+ kLowestUCode
* 2;
103 bool ReadBase128(Buffer
* buf
, uint32_t* value
) {
105 for (size_t i
= 0; i
< 5; ++i
) {
107 if (!buf
->ReadU8(&code
)) {
108 return FONT_COMPRESSION_FAILURE();
110 // If any of the top seven bits are set then we're about to overflow.
111 if (result
& 0xfe000000) {
112 return FONT_COMPRESSION_FAILURE();
114 result
= (result
<< 7) | (code
& 0x7f);
115 if ((code
& 0x80) == 0) {
120 // Make sure not to exceed the size bound
121 return FONT_COMPRESSION_FAILURE();
124 int WithSign(int flag
, int baseval
) {
125 // Precondition: 0 <= baseval < 65536 (to avoid integer overflow)
126 return (flag
& 1) ? baseval
: -baseval
;
129 bool TripletDecode(const uint8_t* flags_in
, const uint8_t* in
, size_t in_size
,
130 unsigned int n_points
, std::vector
<Point
>* result
,
131 size_t* in_bytes_consumed
) {
135 if (n_points
> in_size
) {
136 return FONT_COMPRESSION_FAILURE();
138 unsigned int triplet_index
= 0;
140 for (unsigned int i
= 0; i
< n_points
; ++i
) {
141 uint8_t flag
= flags_in
[i
];
142 bool on_curve
= !(flag
>> 7);
144 unsigned int n_data_bytes
;
147 } else if (flag
< 120) {
149 } else if (flag
< 124) {
154 if (triplet_index
+ n_data_bytes
> in_size
||
155 triplet_index
+ n_data_bytes
< triplet_index
) {
156 return FONT_COMPRESSION_FAILURE();
161 dy
= WithSign(flag
, ((flag
& 14) << 7) + in
[triplet_index
]);
162 } else if (flag
< 20) {
163 dx
= WithSign(flag
, (((flag
- 10) & 14) << 7) + in
[triplet_index
]);
165 } else if (flag
< 84) {
167 int b1
= in
[triplet_index
];
168 dx
= WithSign(flag
, 1 + (b0
& 0x30) + (b1
>> 4));
169 dy
= WithSign(flag
>> 1, 1 + ((b0
& 0x0c) << 2) + (b1
& 0x0f));
170 } else if (flag
< 120) {
172 dx
= WithSign(flag
, 1 + ((b0
/ 12) << 8) + in
[triplet_index
]);
173 dy
= WithSign(flag
>> 1,
174 1 + (((b0
% 12) >> 2) << 8) + in
[triplet_index
+ 1]);
175 } else if (flag
< 124) {
176 int b2
= in
[triplet_index
+ 1];
177 dx
= WithSign(flag
, (in
[triplet_index
] << 4) + (b2
>> 4));
178 dy
= WithSign(flag
>> 1, ((b2
& 0x0f) << 8) + in
[triplet_index
+ 2]);
180 dx
= WithSign(flag
, (in
[triplet_index
] << 8) + in
[triplet_index
+ 1]);
181 dy
= WithSign(flag
>> 1,
182 (in
[triplet_index
+ 2] << 8) + in
[triplet_index
+ 3]);
184 triplet_index
+= n_data_bytes
;
185 // Possible overflow but coordinate values are not security sensitive
188 result
->push_back(Point());
189 Point
& back
= result
->back();
192 back
.on_curve
= on_curve
;
194 *in_bytes_consumed
= triplet_index
;
198 // This function stores just the point data. On entry, dst points to the
199 // beginning of a simple glyph. Returns true on success.
200 bool StorePoints(const std::vector
<Point
>& points
,
201 unsigned int n_contours
, unsigned int instruction_length
,
202 uint8_t* dst
, size_t dst_size
, size_t* glyph_size
) {
203 // I believe that n_contours < 65536, in which case this is safe. However, a
204 // comment and/or an assert would be good.
205 unsigned int flag_offset
= kEndPtsOfContoursOffset
+ 2 * n_contours
+ 2 +
208 int repeat_count
= 0;
211 unsigned int x_bytes
= 0;
212 unsigned int y_bytes
= 0;
214 for (unsigned int i
= 0; i
< points
.size(); ++i
) {
215 const Point
& point
= points
[i
];
216 int flag
= point
.on_curve
? kGlyfOnCurve
: 0;
217 int dx
= point
.x
- last_x
;
218 int dy
= point
.y
- last_y
;
220 flag
|= kGlyfThisXIsSame
;
221 } else if (dx
> -256 && dx
< 256) {
222 flag
|= kGlyfXShort
| (dx
> 0 ? kGlyfThisXIsSame
: 0);
228 flag
|= kGlyfThisYIsSame
;
229 } else if (dy
> -256 && dy
< 256) {
230 flag
|= kGlyfYShort
| (dy
> 0 ? kGlyfThisYIsSame
: 0);
236 if (flag
== last_flag
&& repeat_count
!= 255) {
237 dst
[flag_offset
- 1] |= kGlyfRepeat
;
240 if (repeat_count
!= 0) {
241 if (flag_offset
>= dst_size
) {
242 return FONT_COMPRESSION_FAILURE();
244 dst
[flag_offset
++] = repeat_count
;
246 if (flag_offset
>= dst_size
) {
247 return FONT_COMPRESSION_FAILURE();
249 dst
[flag_offset
++] = flag
;
257 if (repeat_count
!= 0) {
258 if (flag_offset
>= dst_size
) {
259 return FONT_COMPRESSION_FAILURE();
261 dst
[flag_offset
++] = repeat_count
;
263 unsigned int xy_bytes
= x_bytes
+ y_bytes
;
264 if (xy_bytes
< x_bytes
||
265 flag_offset
+ xy_bytes
< flag_offset
||
266 flag_offset
+ xy_bytes
> dst_size
) {
267 return FONT_COMPRESSION_FAILURE();
270 int x_offset
= flag_offset
;
271 int y_offset
= flag_offset
+ x_bytes
;
274 for (unsigned int i
= 0; i
< points
.size(); ++i
) {
275 int dx
= points
[i
].x
- last_x
;
278 } else if (dx
> -256 && dx
< 256) {
279 dst
[x_offset
++] = std::abs(dx
);
281 // will always fit for valid input, but overflow is harmless
282 x_offset
= Store16(dst
, x_offset
, dx
);
285 int dy
= points
[i
].y
- last_y
;
288 } else if (dy
> -256 && dy
< 256) {
289 dst
[y_offset
++] = std::abs(dy
);
291 y_offset
= Store16(dst
, y_offset
, dy
);
295 *glyph_size
= y_offset
;
299 // Compute the bounding box of the coordinates, and store into a glyf buffer.
300 // A precondition is that there are at least 10 bytes available.
301 void ComputeBbox(const std::vector
<Point
>& points
, uint8_t* dst
) {
307 for (unsigned int i
= 0; i
< points
.size(); ++i
) {
310 if (i
== 0 || x
< x_min
) x_min
= x
;
311 if (i
== 0 || x
> x_max
) x_max
= x
;
312 if (i
== 0 || y
< y_min
) y_min
= y
;
313 if (i
== 0 || y
> y_max
) y_max
= y
;
316 offset
= Store16(dst
, offset
, x_min
);
317 offset
= Store16(dst
, offset
, y_min
);
318 offset
= Store16(dst
, offset
, x_max
);
319 offset
= Store16(dst
, offset
, y_max
);
322 // Process entire bbox stream. This is done as a separate pass to allow for
323 // composite bbox computations (an optional more aggressive transform).
324 bool ProcessBboxStream(Buffer
* bbox_stream
, unsigned int n_glyphs
,
325 const std::vector
<uint32_t>& loca_values
, uint8_t* glyf_buf
,
326 size_t glyf_buf_length
) {
327 const uint8_t* buf
= bbox_stream
->buffer();
328 if (n_glyphs
>= 65536 || loca_values
.size() != n_glyphs
+ 1) {
329 return FONT_COMPRESSION_FAILURE();
331 // Safe because n_glyphs is bounded
332 unsigned int bitmap_length
= ((n_glyphs
+ 31) >> 5) << 2;
333 if (!bbox_stream
->Skip(bitmap_length
)) {
334 return FONT_COMPRESSION_FAILURE();
336 for (unsigned int i
= 0; i
< n_glyphs
; ++i
) {
337 if (buf
[i
>> 3] & (0x80 >> (i
& 7))) {
338 uint32_t loca_offset
= loca_values
[i
];
339 if (loca_values
[i
+ 1] - loca_offset
< kEndPtsOfContoursOffset
) {
340 return FONT_COMPRESSION_FAILURE();
342 if (glyf_buf_length
< 2 + 10 ||
343 loca_offset
> glyf_buf_length
- 2 - 10) {
344 return FONT_COMPRESSION_FAILURE();
346 if (!bbox_stream
->Read(glyf_buf
+ loca_offset
+ 2, 8)) {
347 return FONT_COMPRESSION_FAILURE();
354 bool ProcessComposite(Buffer
* composite_stream
, uint8_t* dst
,
355 size_t dst_size
, size_t* glyph_size
, bool* have_instructions
) {
356 size_t start_offset
= composite_stream
->offset();
357 bool we_have_instructions
= false;
359 uint16_t flags
= FLAG_MORE_COMPONENTS
;
360 while (flags
& FLAG_MORE_COMPONENTS
) {
361 if (!composite_stream
->ReadU16(&flags
)) {
362 return FONT_COMPRESSION_FAILURE();
364 we_have_instructions
|= (flags
& FLAG_WE_HAVE_INSTRUCTIONS
) != 0;
365 size_t arg_size
= 2; // glyph index
366 if (flags
& FLAG_ARG_1_AND_2_ARE_WORDS
) {
371 if (flags
& FLAG_WE_HAVE_A_SCALE
) {
373 } else if (flags
& FLAG_WE_HAVE_AN_X_AND_Y_SCALE
) {
375 } else if (flags
& FLAG_WE_HAVE_A_TWO_BY_TWO
) {
378 if (!composite_stream
->Skip(arg_size
)) {
379 return FONT_COMPRESSION_FAILURE();
382 size_t composite_glyph_size
= composite_stream
->offset() - start_offset
;
383 if (composite_glyph_size
+ kCompositeGlyphBegin
> dst_size
) {
384 return FONT_COMPRESSION_FAILURE();
386 Store16(dst
, 0, 0xffff); // nContours = -1 for composite glyph
387 std::memcpy(dst
+ kCompositeGlyphBegin
,
388 composite_stream
->buffer() + start_offset
,
389 composite_glyph_size
);
390 *glyph_size
= kCompositeGlyphBegin
+ composite_glyph_size
;
391 *have_instructions
= we_have_instructions
;
395 // Build TrueType loca table
396 bool StoreLoca(const std::vector
<uint32_t>& loca_values
, int index_format
,
397 uint8_t* dst
, size_t dst_size
) {
398 const uint64_t loca_size
= loca_values
.size();
399 const uint64_t offset_size
= index_format
? 4 : 2;
400 if ((loca_size
<< 2) >> 2 != loca_size
) {
401 return FONT_COMPRESSION_FAILURE();
403 if (offset_size
* loca_size
> dst_size
) {
404 return FONT_COMPRESSION_FAILURE();
407 for (size_t i
= 0; i
< loca_values
.size(); ++i
) {
408 uint32_t value
= loca_values
[i
];
410 offset
= StoreU32(dst
, offset
, value
);
412 offset
= Store16(dst
, offset
, value
>> 1);
418 // Reconstruct entire glyf table based on transformed original
419 bool ReconstructGlyf(const uint8_t* data
, size_t data_size
,
420 uint8_t* dst
, size_t dst_size
,
421 uint8_t* loca_buf
, size_t loca_size
) {
422 static const int kNumSubStreams
= 7;
423 Buffer
file(data
, data_size
);
425 std::vector
<std::pair
<const uint8_t*, size_t> > substreams(kNumSubStreams
);
427 if (!file
.ReadU32(&version
)) {
428 return FONT_COMPRESSION_FAILURE();
431 uint16_t index_format
;
432 if (!file
.ReadU16(&num_glyphs
) ||
433 !file
.ReadU16(&index_format
)) {
434 return FONT_COMPRESSION_FAILURE();
436 unsigned int offset
= (2 + kNumSubStreams
) * 4;
437 if (offset
> data_size
) {
438 return FONT_COMPRESSION_FAILURE();
440 // Invariant from here on: data_size >= offset
441 for (int i
= 0; i
< kNumSubStreams
; ++i
) {
442 uint32_t substream_size
;
443 if (!file
.ReadU32(&substream_size
)) {
444 return FONT_COMPRESSION_FAILURE();
446 if (substream_size
> data_size
- offset
) {
447 return FONT_COMPRESSION_FAILURE();
449 substreams
[i
] = std::make_pair(data
+ offset
, substream_size
);
450 offset
+= substream_size
;
452 Buffer
n_contour_stream(substreams
[0].first
, substreams
[0].second
);
453 Buffer
n_points_stream(substreams
[1].first
, substreams
[1].second
);
454 Buffer
flag_stream(substreams
[2].first
, substreams
[2].second
);
455 Buffer
glyph_stream(substreams
[3].first
, substreams
[3].second
);
456 Buffer
composite_stream(substreams
[4].first
, substreams
[4].second
);
457 Buffer
bbox_stream(substreams
[5].first
, substreams
[5].second
);
458 Buffer
instruction_stream(substreams
[6].first
, substreams
[6].second
);
460 std::vector
<uint32_t> loca_values(num_glyphs
+ 1);
461 std::vector
<unsigned int> n_points_vec
;
462 std::vector
<Point
> points
;
463 uint32_t loca_offset
= 0;
464 for (unsigned int i
= 0; i
< num_glyphs
; ++i
) {
465 size_t glyph_size
= 0;
466 uint16_t n_contours
= 0;
467 if (!n_contour_stream
.ReadU16(&n_contours
)) {
468 return FONT_COMPRESSION_FAILURE();
470 uint8_t* glyf_dst
= dst
+ loca_offset
;
471 size_t glyf_dst_size
= dst_size
- loca_offset
;
472 if (n_contours
== 0xffff) {
474 bool have_instructions
= false;
475 unsigned int instruction_size
= 0;
476 if (!ProcessComposite(&composite_stream
, glyf_dst
, glyf_dst_size
,
477 &glyph_size
, &have_instructions
)) {
478 return FONT_COMPRESSION_FAILURE();
480 if (have_instructions
) {
481 if (!Read255UShort(&glyph_stream
, &instruction_size
)) {
482 return FONT_COMPRESSION_FAILURE();
484 if (instruction_size
+ 2 > glyf_dst_size
- glyph_size
) {
485 return FONT_COMPRESSION_FAILURE();
487 Store16(glyf_dst
, glyph_size
, instruction_size
);
488 if (!instruction_stream
.Read(glyf_dst
+ glyph_size
+ 2,
490 return FONT_COMPRESSION_FAILURE();
492 glyph_size
+= instruction_size
+ 2;
494 } else if (n_contours
> 0) {
496 n_points_vec
.clear();
498 unsigned int total_n_points
= 0;
499 unsigned int n_points_contour
;
500 for (unsigned int j
= 0; j
< n_contours
; ++j
) {
501 if (!Read255UShort(&n_points_stream
, &n_points_contour
)) {
502 return FONT_COMPRESSION_FAILURE();
504 n_points_vec
.push_back(n_points_contour
);
505 if (total_n_points
+ n_points_contour
< total_n_points
) {
506 return FONT_COMPRESSION_FAILURE();
508 total_n_points
+= n_points_contour
;
510 unsigned int flag_size
= total_n_points
;
511 if (flag_size
> flag_stream
.length() - flag_stream
.offset()) {
512 return FONT_COMPRESSION_FAILURE();
514 const uint8_t* flags_buf
= flag_stream
.buffer() + flag_stream
.offset();
515 const uint8_t* triplet_buf
= glyph_stream
.buffer() +
516 glyph_stream
.offset();
517 size_t triplet_size
= glyph_stream
.length() - glyph_stream
.offset();
518 size_t triplet_bytes_consumed
= 0;
519 if (!TripletDecode(flags_buf
, triplet_buf
, triplet_size
, total_n_points
,
520 &points
, &triplet_bytes_consumed
)) {
521 return FONT_COMPRESSION_FAILURE();
523 const uint32_t header_and_endpts_contours_size
=
524 kEndPtsOfContoursOffset
+ 2 * n_contours
;
525 if (glyf_dst_size
< header_and_endpts_contours_size
) {
526 return FONT_COMPRESSION_FAILURE();
528 Store16(glyf_dst
, 0, n_contours
);
529 ComputeBbox(points
, glyf_dst
);
530 size_t offset
= kEndPtsOfContoursOffset
;
532 for (unsigned int contour_ix
= 0; contour_ix
< n_contours
; ++contour_ix
) {
533 end_point
+= n_points_vec
[contour_ix
];
534 if (end_point
>= 65536) {
535 return FONT_COMPRESSION_FAILURE();
537 offset
= Store16(glyf_dst
, offset
, end_point
);
539 if (!flag_stream
.Skip(flag_size
)) {
540 return FONT_COMPRESSION_FAILURE();
542 if (!glyph_stream
.Skip(triplet_bytes_consumed
)) {
543 return FONT_COMPRESSION_FAILURE();
545 unsigned int instruction_size
;
546 if (!Read255UShort(&glyph_stream
, &instruction_size
)) {
547 return FONT_COMPRESSION_FAILURE();
549 if (glyf_dst_size
- header_and_endpts_contours_size
<
550 instruction_size
+ 2) {
551 return FONT_COMPRESSION_FAILURE();
553 uint8_t* instruction_dst
= glyf_dst
+ header_and_endpts_contours_size
;
554 Store16(instruction_dst
, 0, instruction_size
);
555 if (!instruction_stream
.Read(instruction_dst
+ 2, instruction_size
)) {
556 return FONT_COMPRESSION_FAILURE();
558 if (!StorePoints(points
, n_contours
, instruction_size
,
559 glyf_dst
, glyf_dst_size
, &glyph_size
)) {
560 return FONT_COMPRESSION_FAILURE();
565 loca_values
[i
] = loca_offset
;
566 if (glyph_size
+ 3 < glyph_size
) {
567 return FONT_COMPRESSION_FAILURE();
569 glyph_size
= Round4(glyph_size
);
570 if (glyph_size
> dst_size
- loca_offset
) {
571 // This shouldn't happen, but this test defensively maintains the
572 // invariant that loca_offset <= dst_size.
573 return FONT_COMPRESSION_FAILURE();
575 loca_offset
+= glyph_size
;
577 loca_values
[num_glyphs
] = loca_offset
;
578 if (!ProcessBboxStream(&bbox_stream
, num_glyphs
, loca_values
,
580 return FONT_COMPRESSION_FAILURE();
582 return StoreLoca(loca_values
, index_format
, loca_buf
, loca_size
);
585 // This is linear search, but could be changed to binary because we
586 // do have a guarantee that the tables are sorted by tag. But the total
587 // cpu time is expected to be very small in any case.
588 const Table
* FindTable(const std::vector
<Table
>& tables
, uint32_t tag
) {
589 size_t n_tables
= tables
.size();
590 for (size_t i
= 0; i
< n_tables
; ++i
) {
591 if (tables
[i
].tag
== tag
) {
598 bool ReconstructTransformed(const std::vector
<Table
>& tables
, uint32_t tag
,
599 const uint8_t* transformed_buf
, size_t transformed_size
,
600 uint8_t* dst
, size_t dst_length
) {
601 if (tag
== kGlyfTableTag
) {
602 const Table
* glyf_table
= FindTable(tables
, tag
);
603 const Table
* loca_table
= FindTable(tables
, kLocaTableTag
);
604 if (glyf_table
== NULL
|| loca_table
== NULL
) {
605 return FONT_COMPRESSION_FAILURE();
607 if (static_cast<uint64_t>(glyf_table
->dst_offset
+ glyf_table
->dst_length
) >
609 return FONT_COMPRESSION_FAILURE();
611 if (static_cast<uint64_t>(loca_table
->dst_offset
+ loca_table
->dst_length
) >
613 return FONT_COMPRESSION_FAILURE();
615 return ReconstructGlyf(transformed_buf
, transformed_size
,
616 dst
+ glyf_table
->dst_offset
, glyf_table
->dst_length
,
617 dst
+ loca_table
->dst_offset
, loca_table
->dst_length
);
618 } else if (tag
== kLocaTableTag
) {
619 // processing was already done by glyf table, but validate
620 if (!FindTable(tables
, kGlyfTableTag
)) {
621 return FONT_COMPRESSION_FAILURE();
624 // transform for the tag is not known
625 return FONT_COMPRESSION_FAILURE();
630 uint32_t ComputeChecksum(const uint8_t* buf
, size_t size
) {
631 uint32_t checksum
= 0;
632 for (size_t i
= 0; i
< size
; i
+= 4) {
633 // We assume the addition is mod 2^32, which is valid because unsigned
634 checksum
+= (buf
[i
] << 24) | (buf
[i
+ 1] << 16) |
635 (buf
[i
+ 2] << 8) | buf
[i
+ 3];
640 bool FixChecksums(const std::vector
<Table
>& tables
, uint8_t* dst
) {
641 const Table
* head_table
= FindTable(tables
, kHeadTableTag
);
642 if (head_table
== NULL
||
643 head_table
->dst_length
< kCheckSumAdjustmentOffset
+ 4) {
644 return FONT_COMPRESSION_FAILURE();
646 size_t adjustment_offset
= head_table
->dst_offset
+ kCheckSumAdjustmentOffset
;
647 StoreU32(dst
, adjustment_offset
, 0);
648 size_t n_tables
= tables
.size();
649 uint32_t file_checksum
= 0;
650 for (size_t i
= 0; i
< n_tables
; ++i
) {
651 const Table
* table
= &tables
[i
];
652 size_t table_length
= table
->dst_length
;
653 uint8_t* table_data
= dst
+ table
->dst_offset
;
654 uint32_t checksum
= ComputeChecksum(table_data
, table_length
);
655 StoreU32(dst
, kSfntHeaderSize
+ i
* kSfntEntrySize
+ 4, checksum
);
656 file_checksum
+= checksum
;
658 file_checksum
+= ComputeChecksum(dst
,
659 kSfntHeaderSize
+ kSfntEntrySize
* n_tables
);
660 uint32_t checksum_adjustment
= 0xb1b0afba - file_checksum
;
661 StoreU32(dst
, adjustment_offset
, checksum_adjustment
);
665 bool Woff2Uncompress(uint8_t* dst_buf
, size_t dst_size
,
666 const uint8_t* src_buf
, size_t src_size
) {
667 size_t uncompressed_size
= dst_size
;
668 int ok
= BrotliDecompressBuffer(src_size
, src_buf
,
669 &uncompressed_size
, dst_buf
);
670 if (!ok
|| uncompressed_size
!= dst_size
) {
671 return FONT_COMPRESSION_FAILURE();
676 bool ReadShortDirectory(Buffer
* file
, std::vector
<Table
>* tables
,
678 for (size_t i
= 0; i
< num_tables
; ++i
) {
679 Table
* table
= &(*tables
)[i
];
681 if (!file
->ReadU8(&flag_byte
)) {
682 return FONT_COMPRESSION_FAILURE();
685 if ((flag_byte
& 0x3f) == 0x3f) {
686 if (!file
->ReadU32(&tag
)) {
687 return FONT_COMPRESSION_FAILURE();
690 tag
= kKnownTags
[flag_byte
& 0x3f];
692 // Bits 6 and 7 are reserved and must be 0.
693 if ((flag_byte
& 0xC0) != 0) {
694 return FONT_COMPRESSION_FAILURE();
698 flags
|= kWoff2FlagsContinueStream
;
700 // Always transform the glyf and loca tables
701 if (tag
== kGlyfTableTag
|| tag
== kLocaTableTag
) {
702 flags
|= kWoff2FlagsTransform
;
705 if (!ReadBase128(file
, &dst_length
)) {
706 return FONT_COMPRESSION_FAILURE();
708 uint32_t transform_length
= dst_length
;
709 if ((flags
& kWoff2FlagsTransform
) != 0) {
710 if (!ReadBase128(file
, &transform_length
)) {
711 return FONT_COMPRESSION_FAILURE();
715 table
->flags
= flags
;
716 table
->transform_length
= transform_length
;
717 table
->dst_length
= dst_length
;
724 size_t ComputeWOFF2FinalSize(const uint8_t* data
, size_t length
) {
725 Buffer
file(data
, length
);
726 uint32_t total_length
;
728 if (!file
.Skip(16) ||
729 !file
.ReadU32(&total_length
)) {
735 bool ConvertWOFF2ToTTF(uint8_t* result
, size_t result_length
,
736 const uint8_t* data
, size_t length
) {
737 Buffer
file(data
, length
);
741 if (!file
.ReadU32(&signature
) || signature
!= kWoff2Signature
||
742 !file
.ReadU32(&flavor
)) {
743 return FONT_COMPRESSION_FAILURE();
746 // TODO(user): Should call IsValidVersionTag() here.
748 uint32_t reported_length
;
749 if (!file
.ReadU32(&reported_length
) || length
!= reported_length
) {
750 return FONT_COMPRESSION_FAILURE();
753 if (!file
.ReadU16(&num_tables
) || !num_tables
) {
754 return FONT_COMPRESSION_FAILURE();
756 // We don't care about these fields of the header:
758 // uint32_t total_sfnt_size
760 return FONT_COMPRESSION_FAILURE();
762 uint32_t compressed_length
;
763 if (!file
.ReadU32(&compressed_length
)) {
764 return FONT_COMPRESSION_FAILURE();
766 // We don't care about these fields of the header:
767 // uint16_t major_version, minor_version
768 // uint32_t meta_offset, meta_length, meta_orig_length
769 // uint32_t priv_offset, priv_length
770 if (!file
.Skip(24)) {
771 return FONT_COMPRESSION_FAILURE();
773 std::vector
<Table
> tables(num_tables
);
774 // Note: change below to ReadLongDirectory to enable long format.
775 if (!ReadShortDirectory(&file
, &tables
, num_tables
)) {
776 return FONT_COMPRESSION_FAILURE();
778 uint64_t src_offset
= file
.offset();
779 uint64_t dst_offset
= kSfntHeaderSize
+
780 kSfntEntrySize
* static_cast<uint64_t>(num_tables
);
781 uint64_t uncompressed_sum
= 0;
782 for (uint16_t i
= 0; i
< num_tables
; ++i
) {
783 Table
* table
= &tables
[i
];
784 table
->src_offset
= src_offset
;
785 table
->src_length
= (i
== 0 ? compressed_length
: 0);
786 src_offset
+= table
->src_length
;
787 if (src_offset
> std::numeric_limits
<uint32_t>::max()) {
788 return FONT_COMPRESSION_FAILURE();
790 src_offset
= Round4(src_offset
); // TODO: reconsider
791 table
->dst_offset
= dst_offset
;
792 dst_offset
+= table
->dst_length
;
793 if (dst_offset
> std::numeric_limits
<uint32_t>::max()) {
794 return FONT_COMPRESSION_FAILURE();
796 dst_offset
= Round4(dst_offset
);
798 uncompressed_sum
+= table
->src_length
;
799 if (uncompressed_sum
> std::numeric_limits
<uint32_t>::max()) {
800 return FONT_COMPRESSION_FAILURE();
803 // Enforce same 30M limit on uncompressed tables as OTS
804 if (uncompressed_sum
> 30 * 1024 * 1024) {
805 return FONT_COMPRESSION_FAILURE();
807 if (src_offset
> length
|| dst_offset
> result_length
) {
808 return FONT_COMPRESSION_FAILURE();
811 const uint32_t sfnt_header_and_table_directory_size
= 12 + 16 * num_tables
;
812 if (sfnt_header_and_table_directory_size
> result_length
) {
813 return FONT_COMPRESSION_FAILURE();
816 // Start building the font
818 offset
= StoreU32(result
, offset
, flavor
);
819 offset
= Store16(result
, offset
, num_tables
);
820 unsigned max_pow2
= 0;
821 while (1u << (max_pow2
+ 1) <= num_tables
) {
824 const uint16_t output_search_range
= (1u << max_pow2
) << 4;
825 offset
= Store16(result
, offset
, output_search_range
);
826 offset
= Store16(result
, offset
, max_pow2
);
827 offset
= Store16(result
, offset
, (num_tables
<< 4) - output_search_range
);
829 // sort tags in the table directory in ascending alphabetical order
830 std::vector
<Table
> sorted_tables(tables
);
831 std::sort(sorted_tables
.begin(), sorted_tables
.end());
833 for (uint16_t i
= 0; i
< num_tables
; ++i
) {
834 const Table
* table
= &sorted_tables
[i
];
835 offset
= StoreU32(result
, offset
, table
->tag
);
836 offset
= StoreU32(result
, offset
, 0); // checksum, to fill in later
837 offset
= StoreU32(result
, offset
, table
->dst_offset
);
838 offset
= StoreU32(result
, offset
, table
->dst_length
);
840 std::vector
<uint8_t> uncompressed_buf
;
841 bool continue_valid
= false;
842 const uint8_t* transform_buf
= NULL
;
843 for (uint16_t i
= 0; i
< num_tables
; ++i
) {
844 const Table
* table
= &tables
[i
];
845 uint32_t flags
= table
->flags
;
846 const uint8_t* src_buf
= data
+ table
->src_offset
;
847 size_t transform_length
= table
->transform_length
;
848 if ((flags
& kWoff2FlagsContinueStream
) != 0) {
849 if (!continue_valid
) {
850 return FONT_COMPRESSION_FAILURE();
852 } else if ((flags
& kWoff2FlagsContinueStream
) == 0) {
853 uint64_t total_size
= transform_length
;
854 for (uint16_t j
= i
+ 1; j
< num_tables
; ++j
) {
855 if ((tables
[j
].flags
& kWoff2FlagsContinueStream
) == 0) {
858 total_size
+= tables
[j
].transform_length
;
859 if (total_size
> std::numeric_limits
<uint32_t>::max()) {
860 return FONT_COMPRESSION_FAILURE();
863 uncompressed_buf
.resize(total_size
);
864 if (!Woff2Uncompress(&uncompressed_buf
[0], total_size
,
865 src_buf
, compressed_length
)) {
866 return FONT_COMPRESSION_FAILURE();
868 transform_buf
= &uncompressed_buf
[0];
869 continue_valid
= true;
871 return FONT_COMPRESSION_FAILURE();
874 if ((flags
& kWoff2FlagsTransform
) == 0) {
875 if (transform_length
!= table
->dst_length
) {
876 return FONT_COMPRESSION_FAILURE();
878 if (static_cast<uint64_t>(table
->dst_offset
+ transform_length
) >
880 return FONT_COMPRESSION_FAILURE();
882 std::memcpy(result
+ table
->dst_offset
, transform_buf
,
885 if (!ReconstructTransformed(tables
, table
->tag
,
886 transform_buf
, transform_length
, result
, result_length
)) {
887 return FONT_COMPRESSION_FAILURE();
890 if (continue_valid
) {
891 transform_buf
+= transform_length
;
892 if (transform_buf
> &uncompressed_buf
[0] + uncompressed_buf
.size()) {
893 return FONT_COMPRESSION_FAILURE();
898 return FixChecksums(sorted_tables
, result
);