1// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ots.h"
6
7#include <sys/types.h>
8#include <zlib.h>
9
10#include <algorithm>
11#include <cstdlib>
12#include <cstring>
13#include <limits>
14#include <map>
15#include <vector>
16
17#include "woff2.h"
18
19// The OpenType Font File
20// http://www.microsoft.com/typography/otspec/cmap.htm
21
22namespace {
23
24bool g_debug_output = true;
25bool g_enable_woff2 = false;
26
27struct OpenTypeTable {
28  uint32_t tag;
29  uint32_t chksum;
30  uint32_t offset;
31  uint32_t length;
32  uint32_t uncompressed_length;
33};
34
35bool CheckTag(uint32_t tag_value) {
36  for (unsigned i = 0; i < 4; ++i) {
37    const uint32_t check = tag_value & 0xff;
38    if (check < 32 || check > 126) {
39      return false;  // non-ASCII character found.
40    }
41    tag_value >>= 8;
42  }
43  return true;
44}
45
46uint32_t Tag(const char *tag_str) {
47  uint32_t ret;
48  std::memcpy(&ret, tag_str, 4);
49  return ret;
50}
51
52struct OutputTable {
53  uint32_t tag;
54  size_t offset;
55  size_t length;
56  uint32_t chksum;
57
58  static bool SortByTag(const OutputTable& a, const OutputTable& b) {
59    const uint32_t atag = ntohl(a.tag);
60    const uint32_t btag = ntohl(b.tag);
61    return atag < btag;
62  }
63};
64
65struct Arena {
66 public:
67  ~Arena() {
68    for (std::vector<uint8_t*>::iterator
69         i = hunks_.begin(); i != hunks_.end(); ++i) {
70      delete[] *i;
71    }
72  }
73
74  uint8_t* Allocate(size_t length) {
75    uint8_t* p = new uint8_t[length];
76    hunks_.push_back(p);
77    return p;
78  }
79
80 private:
81  std::vector<uint8_t*> hunks_;
82};
83
84const struct {
85  const char* tag;
86  bool (*parse)(ots::OpenTypeFile *otf, const uint8_t *data, size_t length);
87  bool (*serialise)(ots::OTSStream *out, ots::OpenTypeFile *file);
88  bool (*should_serialise)(ots::OpenTypeFile *file);
89  void (*free)(ots::OpenTypeFile *file);
90  bool required;
91} table_parsers[] = {
92  { "maxp", ots::ots_maxp_parse, ots::ots_maxp_serialise,
93    ots::ots_maxp_should_serialise, ots::ots_maxp_free, true },
94  { "head", ots::ots_head_parse, ots::ots_head_serialise,
95    ots::ots_head_should_serialise, ots::ots_head_free, true },
96  { "OS/2", ots::ots_os2_parse, ots::ots_os2_serialise,
97    ots::ots_os2_should_serialise, ots::ots_os2_free, true },
98  { "cmap", ots::ots_cmap_parse, ots::ots_cmap_serialise,
99    ots::ots_cmap_should_serialise, ots::ots_cmap_free, true },
100  { "hhea", ots::ots_hhea_parse, ots::ots_hhea_serialise,
101    ots::ots_hhea_should_serialise, ots::ots_hhea_free, true },
102  { "hmtx", ots::ots_hmtx_parse, ots::ots_hmtx_serialise,
103    ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, true },
104  { "name", ots::ots_name_parse, ots::ots_name_serialise,
105    ots::ots_name_should_serialise, ots::ots_name_free, true },
106  { "post", ots::ots_post_parse, ots::ots_post_serialise,
107    ots::ots_post_should_serialise, ots::ots_post_free, true },
108  { "loca", ots::ots_loca_parse, ots::ots_loca_serialise,
109    ots::ots_loca_should_serialise, ots::ots_loca_free, false },
110  { "glyf", ots::ots_glyf_parse, ots::ots_glyf_serialise,
111    ots::ots_glyf_should_serialise, ots::ots_glyf_free, false },
112  { "CFF ", ots::ots_cff_parse, ots::ots_cff_serialise,
113    ots::ots_cff_should_serialise, ots::ots_cff_free, false },
114  { "VDMX", ots::ots_vdmx_parse, ots::ots_vdmx_serialise,
115    ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, false },
116  { "hdmx", ots::ots_hdmx_parse, ots::ots_hdmx_serialise,
117    ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, false },
118  { "gasp", ots::ots_gasp_parse, ots::ots_gasp_serialise,
119    ots::ots_gasp_should_serialise, ots::ots_gasp_free, false },
120  { "cvt ", ots::ots_cvt_parse, ots::ots_cvt_serialise,
121    ots::ots_cvt_should_serialise, ots::ots_cvt_free, false },
122  { "fpgm", ots::ots_fpgm_parse, ots::ots_fpgm_serialise,
123    ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, false },
124  { "prep", ots::ots_prep_parse, ots::ots_prep_serialise,
125    ots::ots_prep_should_serialise, ots::ots_prep_free, false },
126  { "LTSH", ots::ots_ltsh_parse, ots::ots_ltsh_serialise,
127    ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false },
128  { "VORG", ots::ots_vorg_parse, ots::ots_vorg_serialise,
129    ots::ots_vorg_should_serialise, ots::ots_vorg_free, false },
130  { "kern", ots::ots_kern_parse, ots::ots_kern_serialise,
131    ots::ots_kern_should_serialise, ots::ots_kern_free, false },
132  // We need to parse GDEF table in advance of parsing GSUB/GPOS tables
133  // because they could refer GDEF table.
134  { "GDEF", ots::ots_gdef_parse, ots::ots_gdef_serialise,
135    ots::ots_gdef_should_serialise, ots::ots_gdef_free, false },
136  { "GPOS", ots::ots_gpos_parse, ots::ots_gpos_serialise,
137    ots::ots_gpos_should_serialise, ots::ots_gpos_free, false },
138  { "GSUB", ots::ots_gsub_parse, ots::ots_gsub_serialise,
139    ots::ots_gsub_should_serialise, ots::ots_gsub_free, false },
140  { "vhea", ots::ots_vhea_parse, ots::ots_vhea_serialise,
141    ots::ots_vhea_should_serialise, ots::ots_vhea_free, false },
142  { "vmtx", ots::ots_vmtx_parse, ots::ots_vmtx_serialise,
143    ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false },
144  { "MATH", ots::ots_math_parse, ots::ots_math_serialise,
145    ots::ots_math_should_serialise, ots::ots_math_free, false },
146  { "CBDT", ots::ots_cbdt_parse, ots::ots_cbdt_serialise,
147    ots::ots_cbdt_should_serialise, ots::ots_cbdt_free, false },
148  { "CBLC", ots::ots_cblc_parse, ots::ots_cblc_serialise,
149    ots::ots_cblc_should_serialise, ots::ots_cblc_free, false },
150  // TODO(bashi): Support mort, base, and jstf tables.
151  { 0, NULL, NULL, NULL, NULL, false },
152};
153
154bool ProcessGeneric(ots::OpenTypeFile *header,
155                    uint32_t signature,
156                    ots::OTSStream *output,
157                    const uint8_t *data, size_t length,
158                    const std::vector<OpenTypeTable>& tables,
159                    ots::Buffer& file);
160
161bool ProcessTTF(ots::OpenTypeFile *header,
162                ots::OTSStream *output, const uint8_t *data, size_t length) {
163  ots::Buffer file(data, length);
164
165  // we disallow all files > 1GB in size for sanity.
166  if (length > 1024 * 1024 * 1024) {
167    return OTS_FAILURE();
168  }
169
170  if (!file.ReadTag(&header->version)) {
171    return OTS_FAILURE();
172  }
173  if (!ots::IsValidVersionTag(header->version)) {
174      return OTS_FAILURE();
175  }
176
177  if (!file.ReadU16(&header->num_tables) ||
178      !file.ReadU16(&header->search_range) ||
179      !file.ReadU16(&header->entry_selector) ||
180      !file.ReadU16(&header->range_shift)) {
181    return OTS_FAILURE();
182  }
183
184  // search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid
185  // overflow num_tables is, at most, 2^16 / 16 = 2^12
186  if (header->num_tables >= 4096 || header->num_tables < 1) {
187    return OTS_FAILURE();
188  }
189
190  unsigned max_pow2 = 0;
191  while (1u << (max_pow2 + 1) <= header->num_tables) {
192    max_pow2++;
193  }
194  const uint16_t expected_search_range = (1u << max_pow2) << 4;
195
196  // Don't call ots_failure() here since ~25% of fonts (250+ fonts) in
197  // http://www.princexml.com/fonts/ have unexpected search_range value.
198  if (header->search_range != expected_search_range) {
199    OTS_WARNING("bad search range");
200    header->search_range = expected_search_range;  // Fix the value.
201  }
202
203  // entry_selector is Log2(maximum power of 2 <= numTables)
204  if (header->entry_selector != max_pow2) {
205    return OTS_FAILURE();
206  }
207
208  // range_shift is NumTables x 16-searchRange. We know that 16*num_tables
209  // doesn't over flow because we range checked it above. Also, we know that
210  // it's > header->search_range by construction of search_range.
211  const uint32_t expected_range_shift
212      = 16 * header->num_tables - header->search_range;
213  if (header->range_shift != expected_range_shift) {
214    OTS_WARNING("bad range shift");
215    header->range_shift = expected_range_shift;  // the same as above.
216  }
217
218  // Next up is the list of tables.
219  std::vector<OpenTypeTable> tables;
220
221  for (unsigned i = 0; i < header->num_tables; ++i) {
222    OpenTypeTable table;
223    if (!file.ReadTag(&table.tag) ||
224        !file.ReadU32(&table.chksum) ||
225        !file.ReadU32(&table.offset) ||
226        !file.ReadU32(&table.length)) {
227      return OTS_FAILURE();
228    }
229
230    table.uncompressed_length = table.length;
231    tables.push_back(table);
232  }
233
234  return ProcessGeneric(header, header->version, output, data, length,
235                        tables, file);
236}
237
238bool ProcessWOFF(ots::OpenTypeFile *header,
239                 ots::OTSStream *output, const uint8_t *data, size_t length) {
240  ots::Buffer file(data, length);
241
242  // we disallow all files > 1GB in size for sanity.
243  if (length > 1024 * 1024 * 1024) {
244    return OTS_FAILURE();
245  }
246
247  uint32_t woff_tag;
248  if (!file.ReadTag(&woff_tag)) {
249    return OTS_FAILURE();
250  }
251
252  if (woff_tag != Tag("wOFF")) {
253    return OTS_FAILURE();
254  }
255
256  if (!file.ReadTag(&header->version)) {
257    return OTS_FAILURE();
258  }
259  if (!ots::IsValidVersionTag(header->version)) {
260      return OTS_FAILURE();
261  }
262
263  header->search_range = 0;
264  header->entry_selector = 0;
265  header->range_shift = 0;
266
267  uint32_t reported_length;
268  if (!file.ReadU32(&reported_length) || length != reported_length) {
269    return OTS_FAILURE();
270  }
271
272  if (!file.ReadU16(&header->num_tables) || !header->num_tables) {
273    return OTS_FAILURE();
274  }
275
276  uint16_t reserved_value;
277  if (!file.ReadU16(&reserved_value) || reserved_value) {
278    return OTS_FAILURE();
279  }
280
281  uint32_t reported_total_sfnt_size;
282  if (!file.ReadU32(&reported_total_sfnt_size)) {
283    return OTS_FAILURE();
284  }
285
286  // We don't care about these fields of the header:
287  //   uint16_t major_version, minor_version
288  if (!file.Skip(2 * 2)) {
289    return OTS_FAILURE();
290  }
291
292  // Checks metadata block size.
293  uint32_t meta_offset;
294  uint32_t meta_length;
295  uint32_t meta_length_orig;
296  if (!file.ReadU32(&meta_offset) ||
297      !file.ReadU32(&meta_length) ||
298      !file.ReadU32(&meta_length_orig)) {
299    return OTS_FAILURE();
300  }
301  if (meta_offset) {
302    if (meta_offset >= length || length - meta_offset < meta_length) {
303      return OTS_FAILURE();
304    }
305  }
306
307  // Checks private data block size.
308  uint32_t priv_offset;
309  uint32_t priv_length;
310  if (!file.ReadU32(&priv_offset) ||
311      !file.ReadU32(&priv_length)) {
312    return OTS_FAILURE();
313  }
314  if (priv_offset) {
315    if (priv_offset >= length || length - priv_offset < priv_length) {
316      return OTS_FAILURE();
317    }
318  }
319
320  // Next up is the list of tables.
321  std::vector<OpenTypeTable> tables;
322
323  uint32_t first_index = 0;
324  uint32_t last_index = 0;
325  // Size of sfnt header plus size of table records.
326  uint64_t total_sfnt_size = 12 + 16 * header->num_tables;
327  for (unsigned i = 0; i < header->num_tables; ++i) {
328    OpenTypeTable table;
329    if (!file.ReadTag(&table.tag) ||
330        !file.ReadU32(&table.offset) ||
331        !file.ReadU32(&table.length) ||
332        !file.ReadU32(&table.uncompressed_length) ||
333        !file.ReadU32(&table.chksum)) {
334      return OTS_FAILURE();
335    }
336
337    total_sfnt_size += ots::Round4(table.uncompressed_length);
338    if (total_sfnt_size > std::numeric_limits<uint32_t>::max()) {
339      return OTS_FAILURE();
340    }
341    tables.push_back(table);
342    if (i == 0 || tables[first_index].offset > table.offset)
343      first_index = i;
344    if (i == 0 || tables[last_index].offset < table.offset)
345      last_index = i;
346  }
347
348  if (reported_total_sfnt_size != total_sfnt_size) {
349    return OTS_FAILURE();
350  }
351
352  // Table data must follow immediately after the header.
353  if (tables[first_index].offset != ots::Round4(file.offset())) {
354    return OTS_FAILURE();
355  }
356
357  if (tables[last_index].offset >= length ||
358      length - tables[last_index].offset < tables[last_index].length) {
359    return OTS_FAILURE();
360  }
361  // Blocks must follow immediately after the previous block.
362  // (Except for padding with a maximum of three null bytes)
363  uint64_t block_end = ots::Round4(
364      static_cast<uint64_t>(tables[last_index].offset) +
365      static_cast<uint64_t>(tables[last_index].length));
366  if (block_end > std::numeric_limits<uint32_t>::max()) {
367    return OTS_FAILURE();
368  }
369  if (meta_offset) {
370    if (block_end != meta_offset) {
371      return OTS_FAILURE();
372    }
373    block_end = ots::Round4(static_cast<uint64_t>(meta_offset) +
374                            static_cast<uint64_t>(meta_length));
375    if (block_end > std::numeric_limits<uint32_t>::max()) {
376      return OTS_FAILURE();
377    }
378  }
379  if (priv_offset) {
380    if (block_end != priv_offset) {
381      return OTS_FAILURE();
382    }
383    block_end = ots::Round4(static_cast<uint64_t>(priv_offset) +
384                            static_cast<uint64_t>(priv_length));
385    if (block_end > std::numeric_limits<uint32_t>::max()) {
386      return OTS_FAILURE();
387    }
388  }
389  if (block_end != ots::Round4(length)) {
390    return OTS_FAILURE();
391  }
392
393  return ProcessGeneric(header, woff_tag, output, data, length, tables, file);
394}
395
396bool ProcessWOFF2(ots::OpenTypeFile *header,
397                  ots::OTSStream *output, const uint8_t *data, size_t length) {
398  size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length);
399  if (decompressed_size == 0) {
400    return OTS_FAILURE();
401  }
402  // decompressed font must be <= 30MB
403  if (decompressed_size > 30 * 1024 * 1024) {
404    return OTS_FAILURE();
405  }
406
407  std::vector<uint8_t> decompressed_buffer(decompressed_size);
408  if (!ots::ConvertWOFF2ToTTF(&decompressed_buffer[0], decompressed_size,
409                              data, length)) {
410    return OTS_FAILURE();
411  }
412  return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size);
413}
414
415bool ProcessGeneric(ots::OpenTypeFile *header, uint32_t signature,
416                    ots::OTSStream *output,
417                    const uint8_t *data, size_t length,
418                    const std::vector<OpenTypeTable>& tables,
419                    ots::Buffer& file) {
420  const size_t data_offset = file.offset();
421
422  uint32_t uncompressed_sum = 0;
423
424  for (unsigned i = 0; i < header->num_tables; ++i) {
425    // the tables must be sorted by tag (when taken as big-endian numbers).
426    // This also remove the possibility of duplicate tables.
427    if (i) {
428      const uint32_t this_tag = ntohl(tables[i].tag);
429      const uint32_t prev_tag = ntohl(tables[i - 1].tag);
430      if (this_tag <= prev_tag) {
431        return OTS_FAILURE();
432      }
433    }
434
435    // all tag names must be built from printable ASCII characters
436    if (!CheckTag(tables[i].tag)) {
437      return OTS_FAILURE();
438    }
439
440    // tables must be 4-byte aligned
441    if (tables[i].offset & 3) {
442      return OTS_FAILURE();
443    }
444
445    // and must be within the file
446    if (tables[i].offset < data_offset || tables[i].offset >= length) {
447      return OTS_FAILURE();
448    }
449    // disallow all tables with a zero length
450    if (tables[i].length < 1) {
451      // Note: malayalam.ttf has zero length CVT table...
452      return OTS_FAILURE();
453    }
454    // disallow all tables with a length > 1GB
455    if (tables[i].length > 1024 * 1024 * 1024) {
456      return OTS_FAILURE();
457    }
458    // disallow tables where the uncompressed size is < the compressed size.
459    if (tables[i].uncompressed_length < tables[i].length) {
460      return OTS_FAILURE();
461    }
462    if (tables[i].uncompressed_length > tables[i].length) {
463      // We'll probably be decompressing this table.
464
465      // disallow all tables which uncompress to > 30 MB
466      if (tables[i].uncompressed_length > 30 * 1024 * 1024) {
467        return OTS_FAILURE();
468      }
469      if (uncompressed_sum + tables[i].uncompressed_length < uncompressed_sum) {
470        return OTS_FAILURE();
471      }
472
473      uncompressed_sum += tables[i].uncompressed_length;
474    }
475    // since we required that the file be < 1GB in length, and that the table
476    // length is < 1GB, the following addtion doesn't overflow
477    uint32_t end_byte = tables[i].offset + tables[i].length;
478    // Tables in the WOFF file must be aligned 4-byte boundary.
479    if (signature == Tag("wOFF")) {
480        end_byte = ots::Round4(end_byte);
481    }
482    if (!end_byte || end_byte > length) {
483      return OTS_FAILURE();
484    }
485  }
486
487  // All decompressed tables uncompressed must be <= 30MB.
488  if (uncompressed_sum > 30 * 1024 * 1024) {
489    return OTS_FAILURE();
490  }
491
492  std::map<uint32_t, OpenTypeTable> table_map;
493  for (unsigned i = 0; i < header->num_tables; ++i) {
494    table_map[tables[i].tag] = tables[i];
495  }
496
497  // check that the tables are not overlapping.
498  std::vector<std::pair<uint32_t, uint8_t> > overlap_checker;
499  for (unsigned i = 0; i < header->num_tables; ++i) {
500    overlap_checker.push_back(
501        std::make_pair(tables[i].offset, static_cast<uint8_t>(1) /* start */));
502    overlap_checker.push_back(
503        std::make_pair(tables[i].offset + tables[i].length,
504                       static_cast<uint8_t>(0) /* end */));
505  }
506  std::sort(overlap_checker.begin(), overlap_checker.end());
507  int overlap_count = 0;
508  for (unsigned i = 0; i < overlap_checker.size(); ++i) {
509    overlap_count += (overlap_checker[i].second ? 1 : -1);
510    if (overlap_count > 1) {
511      return OTS_FAILURE();
512    }
513  }
514
515  Arena arena;
516
517  for (unsigned i = 0; ; ++i) {
518    if (table_parsers[i].parse == NULL) break;
519
520    const std::map<uint32_t, OpenTypeTable>::const_iterator it
521        = table_map.find(Tag(table_parsers[i].tag));
522
523    if (it == table_map.end()) {
524      if (table_parsers[i].required) {
525        return OTS_FAILURE();
526      }
527      continue;
528    }
529
530    const uint8_t* table_data;
531    size_t table_length;
532
533    if (it->second.uncompressed_length != it->second.length) {
534      // compressed table. Need to uncompress into memory first.
535      table_length = it->second.uncompressed_length;
536      table_data = arena.Allocate(table_length);
537      uLongf dest_len = table_length;
538      int r = uncompress((Bytef*) table_data, &dest_len,
539                         data + it->second.offset, it->second.length);
540      if (r != Z_OK || dest_len != table_length) {
541        return OTS_FAILURE();
542      }
543    } else {
544      // uncompressed table. We can process directly from memory.
545      table_data = data + it->second.offset;
546      table_length = it->second.length;
547    }
548
549    if (!table_parsers[i].parse(header, table_data, table_length)) {
550      return OTS_FAILURE();
551    }
552  }
553
554  if (header->cff) {
555    // font with PostScript glyph
556    if (header->version != Tag("OTTO")) {
557      return OTS_FAILURE();
558    }
559    if (header->glyf || header->loca) {
560      // mixing outline formats is not recommended
561      return OTS_FAILURE();
562    }
563  } else {
564    if ((!header->glyf || !header->loca) && (!header->cbdt || !header->cblc)) {
565      // No TrueType glyph or color bitmap found.
566      return OTS_FAILURE();
567    }
568  }
569
570  unsigned num_output_tables = 0;
571  for (unsigned i = 0; ; ++i) {
572    if (table_parsers[i].parse == NULL) {
573      break;
574    }
575
576    if (table_parsers[i].should_serialise(header)) {
577      num_output_tables++;
578    }
579  }
580
581  unsigned max_pow2 = 0;
582  while (1u << (max_pow2 + 1) <= num_output_tables) {
583    max_pow2++;
584  }
585  const uint16_t output_search_range = (1u << max_pow2) << 4;
586
587  output->ResetChecksum();
588  if (!output->WriteTag(header->version) ||
589      !output->WriteU16(num_output_tables) ||
590      !output->WriteU16(output_search_range) ||
591      !output->WriteU16(max_pow2) ||
592      !output->WriteU16((num_output_tables << 4) - output_search_range)) {
593    return OTS_FAILURE();
594  }
595  const uint32_t offset_table_chksum = output->chksum();
596
597  const size_t table_record_offset = output->Tell();
598  if (!output->Pad(16 * num_output_tables)) {
599    return OTS_FAILURE();
600  }
601
602  std::vector<OutputTable> out_tables;
603
604  size_t head_table_offset = 0;
605  for (unsigned i = 0; ; ++i) {
606    if (table_parsers[i].parse == NULL) {
607      break;
608    }
609
610    if (!table_parsers[i].should_serialise(header)) {
611      continue;
612    }
613
614    OutputTable out;
615    uint32_t tag = Tag(table_parsers[i].tag);
616    out.tag = tag;
617    out.offset = output->Tell();
618
619    output->ResetChecksum();
620    if (tag == Tag("head")) {
621      head_table_offset = out.offset;
622    }
623    if (!table_parsers[i].serialise(output, header)) {
624      return OTS_FAILURE();
625    }
626
627    const size_t end_offset = output->Tell();
628    if (end_offset <= out.offset) {
629      // paranoid check. |end_offset| is supposed to be greater than the offset,
630      // as long as the Tell() interface is implemented correctly.
631      return OTS_FAILURE();
632    }
633    out.length = end_offset - out.offset;
634
635    // align tables to four bytes
636    if (!output->Pad((4 - (end_offset & 3)) % 4)) {
637      return OTS_FAILURE();
638    }
639    out.chksum = output->chksum();
640    out_tables.push_back(out);
641  }
642
643  const size_t end_of_file = output->Tell();
644
645  // Need to sort the output tables for inclusion in the file
646  std::sort(out_tables.begin(), out_tables.end(), OutputTable::SortByTag);
647  if (!output->Seek(table_record_offset)) {
648    return OTS_FAILURE();
649  }
650
651  output->ResetChecksum();
652  uint32_t tables_chksum = 0;
653  for (unsigned i = 0; i < out_tables.size(); ++i) {
654    if (!output->WriteTag(out_tables[i].tag) ||
655        !output->WriteU32(out_tables[i].chksum) ||
656        !output->WriteU32(out_tables[i].offset) ||
657        !output->WriteU32(out_tables[i].length)) {
658      return OTS_FAILURE();
659    }
660    tables_chksum += out_tables[i].chksum;
661  }
662  const uint32_t table_record_chksum = output->chksum();
663
664  // http://www.microsoft.com/typography/otspec/otff.htm
665  const uint32_t file_chksum
666      = offset_table_chksum + tables_chksum + table_record_chksum;
667  const uint32_t chksum_magic = static_cast<uint32_t>(0xb1b0afba) - file_chksum;
668
669  // seek into the 'head' table and write in the checksum magic value
670  if (!head_table_offset) {
671    return OTS_FAILURE();  // not reached.
672  }
673  if (!output->Seek(head_table_offset + 8)) {
674    return OTS_FAILURE();
675  }
676  if (!output->WriteU32(chksum_magic)) {
677    return OTS_FAILURE();
678  }
679
680  if (!output->Seek(end_of_file)) {
681    return OTS_FAILURE();
682  }
683
684  return true;
685}
686
687}  // namespace
688
689namespace ots {
690
691bool g_drop_color_bitmap_tables = true;
692
693bool IsValidVersionTag(uint32_t tag) {
694  return tag == Tag("\x00\x01\x00\x00") ||
695         // OpenType fonts with CFF data have 'OTTO' tag.
696         tag == Tag("OTTO") ||
697         // Older Mac fonts might have 'true' or 'typ1' tag.
698         tag == Tag("true") ||
699         tag == Tag("typ1");
700}
701
702void DisableDebugOutput() {
703  g_debug_output = false;
704}
705
706void EnableWOFF2() {
707  g_enable_woff2 = true;
708}
709
710void DoNotDropColorBitmapTables() {
711  g_drop_color_bitmap_tables = false;
712}
713
714bool Process(OTSStream *output, const uint8_t *data, size_t length) {
715  OpenTypeFile header;
716  if (length < 4) {
717    return OTS_FAILURE();
718  }
719
720  bool result;
721  if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') {
722    result = ProcessWOFF(&header, output, data, length);
723  } else if (g_enable_woff2 &&
724             data[0] == 'w' && data[1] == 'O' && data[2] == 'F' &&
725             data[3] == '2') {
726    result = ProcessWOFF2(&header, output, data, length);
727  } else {
728    result = ProcessTTF(&header, output, data, length);
729  }
730
731  for (unsigned i = 0; ; ++i) {
732    if (table_parsers[i].parse == NULL) break;
733    table_parsers[i].free(&header);
734  }
735  return result;
736}
737
738#if !defined(_MSC_VER) && defined(OTS_DEBUG)
739bool Failure(const char *f, int l, const char *fn) {
740  if (g_debug_output) {
741    std::fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn);
742    std::fflush(stderr);
743  }
744  return false;
745}
746
747void Warning(const char *f, int l, const char *format, ...) {
748  if (g_debug_output) {
749    std::fprintf(stderr, "WARNING at %s:%d: ", f, l);
750    std::va_list va;
751    va_start(va, format);
752    std::vfprintf(stderr, format, va);
753    va_end(va);
754    std::fprintf(stderr, "\n");
755    std::fflush(stderr);
756  }
757}
758#endif
759
760}  // namespace ots
761