gfx/ots/src/ots.cc

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

mercurial