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