1 // Copyright (c) 2009-2017 The OTS 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.
17 #include "../RLBoxWOFF2Host.h"
19 // The OpenType Font File
20 // http://www.microsoft.com/typography/otspec/otff.htm
73 for (auto& hunk
: hunks_
) {
78 uint8_t* Allocate(size_t length
) {
79 uint8_t* p
= new uint8_t[length
];
85 std::vector
<uint8_t*> hunks_
;
88 bool CheckTag(uint32_t tag_value
) {
89 for (unsigned i
= 0; i
< 4; ++i
) {
90 const uint32_t check
= tag_value
& 0xff;
91 if (check
< 32 || check
> 126) {
92 return false; // non-ASCII character found.
103 #define OTS_MSG_TAG_(level,otf_,msg_,tag_) \
104 (OTS_MESSAGE_(level,otf_,"%c%c%c%c: %s", OTS_UNTAG(tag_), msg_), false)
106 // Generate a message with or without a table tag, when 'header' is the FontFile pointer
107 #define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_MSG_TAG_(0, header, msg_, tag_)
108 #define OTS_FAILURE_MSG_HDR(...) OTS_FAILURE_MSG_(header, __VA_ARGS__)
109 #define OTS_WARNING_MSG_HDR(...) OTS_WARNING_MSG_(header, __VA_ARGS__)
115 } supported_tables
[] = {
116 { OTS_TAG_MAXP
, true },
117 { OTS_TAG_HEAD
, true },
118 { OTS_TAG_OS2
, true },
119 { OTS_TAG_CMAP
, true },
120 { OTS_TAG_HHEA
, true },
121 { OTS_TAG_HMTX
, true },
122 { OTS_TAG_NAME
, true },
123 { OTS_TAG_POST
, true },
124 { OTS_TAG_LOCA
, false },
125 { OTS_TAG_GLYF
, false },
126 { OTS_TAG_CFF
, false },
127 { OTS_TAG_VDMX
, false },
128 { OTS_TAG_HDMX
, false },
129 { OTS_TAG_GASP
, false },
130 { OTS_TAG_CVT
, false },
131 { OTS_TAG_FPGM
, false },
132 { OTS_TAG_PREP
, false },
133 { OTS_TAG_LTSH
, false },
134 { OTS_TAG_VORG
, false },
135 { OTS_TAG_KERN
, false },
136 // We need to parse fvar table before other tables that may need to know
137 // the number of variation axes (if any)
138 { OTS_TAG_FVAR
, false },
139 { OTS_TAG_AVAR
, false },
140 { OTS_TAG_CVAR
, false },
141 { OTS_TAG_GVAR
, false },
142 { OTS_TAG_HVAR
, false },
143 { OTS_TAG_MVAR
, false },
144 { OTS_TAG_STAT
, false },
145 { OTS_TAG_VVAR
, false },
146 { OTS_TAG_CFF2
, false },
147 // We need to parse GDEF table in advance of parsing GSUB/GPOS tables
148 // because they could refer GDEF table.
149 { OTS_TAG_GDEF
, false },
150 { OTS_TAG_GPOS
, false },
151 { OTS_TAG_GSUB
, false },
152 { OTS_TAG_VHEA
, false },
153 { OTS_TAG_VMTX
, false },
154 { OTS_TAG_MATH
, false },
157 { OTS_TAG_GLOC
, false },
158 { OTS_TAG_GLAT
, false },
159 { OTS_TAG_FEAT
, false },
160 { OTS_TAG_SILF
, false },
161 { OTS_TAG_SILE
, false },
162 { OTS_TAG_SILL
, false },
167 bool ValidateVersionTag(ots::Font
*font
) {
168 switch (font
->version
) {
170 case OTS_TAG('O','T','T','O'):
172 case OTS_TAG('t','r','u','e'):
173 font
->version
= 0x000010000;
180 bool ProcessGeneric(ots::FontFile
*header
,
183 ots::OTSStream
*output
,
184 const uint8_t *data
, size_t length
,
185 const std::vector
<ots::TableEntry
>& tables
,
188 bool ProcessTTF(ots::FontFile
*header
,
190 ots::OTSStream
*output
, const uint8_t *data
, size_t length
,
191 uint32_t offset
= 0) {
192 ots::Buffer
file(data
+ offset
, length
- offset
);
194 if (offset
> length
) {
195 return OTS_FAILURE_MSG_HDR("offset beyond end of file");
198 // we disallow all files > 1GB in size for sanity.
199 if (length
> 1024 * 1024 * 1024) {
200 return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
203 if (!file
.ReadU32(&font
->version
)) {
204 return OTS_FAILURE_MSG_HDR("error reading sfntVersion");
206 if (!ValidateVersionTag(font
)) {
207 return OTS_FAILURE_MSG_HDR("invalid sfntVersion: %d", font
->version
);
210 if (!file
.ReadU16(&font
->num_tables
) ||
211 !file
.ReadU16(&font
->search_range
) ||
212 !file
.ReadU16(&font
->entry_selector
) ||
213 !file
.ReadU16(&font
->range_shift
)) {
214 return OTS_FAILURE_MSG_HDR("error reading table directory search header");
217 // search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid
218 // overflow num_tables is, at most, 2^16 / 16 = 2^12
219 if (font
->num_tables
>= 4096 || font
->num_tables
< 1) {
220 return OTS_FAILURE_MSG_HDR("excessive (or zero) number of tables");
223 unsigned max_pow2
= 0;
224 while (1u << (max_pow2
+ 1) <= font
->num_tables
) {
227 const uint16_t expected_search_range
= (1u << max_pow2
) << 4;
229 // Don't call ots_failure() here since ~25% of fonts (250+ fonts) in
230 // http://www.princexml.com/fonts/ have unexpected search_range value.
231 if (font
->search_range
!= expected_search_range
) {
232 OTS_WARNING_MSG_HDR("bad table directory searchRange");
233 font
->search_range
= expected_search_range
; // Fix the value.
236 // entry_selector is Log2(maximum power of 2 <= numTables)
237 if (font
->entry_selector
!= max_pow2
) {
238 OTS_WARNING_MSG_HDR("bad table directory entrySelector");
239 font
->entry_selector
= max_pow2
; // Fix the value.
242 // range_shift is NumTables x 16-searchRange. We know that 16*num_tables
243 // doesn't over flow because we range checked it above. Also, we know that
244 // it's > font->search_range by construction of search_range.
245 const uint16_t expected_range_shift
=
246 16 * font
->num_tables
- font
->search_range
;
247 if (font
->range_shift
!= expected_range_shift
) {
248 OTS_WARNING_MSG_HDR("bad table directory rangeShift");
249 font
->range_shift
= expected_range_shift
; // the same as above.
252 // Next up is the list of tables.
253 std::vector
<ots::TableEntry
> tables
;
255 for (unsigned i
= 0; i
< font
->num_tables
; ++i
) {
256 ots::TableEntry table
;
257 if (!file
.ReadU32(&table
.tag
) ||
258 !file
.ReadU32(&table
.chksum
) ||
259 !file
.ReadU32(&table
.offset
) ||
260 !file
.ReadU32(&table
.length
)) {
261 return OTS_FAILURE_MSG_HDR("error reading table directory");
264 table
.uncompressed_length
= table
.length
;
265 tables
.push_back(table
);
268 return ProcessGeneric(header
, font
, font
->version
, output
, data
, length
,
272 bool ProcessTTC(ots::FontFile
*header
,
273 ots::OTSStream
*output
,
277 ots::Buffer
file(data
, length
);
279 // we disallow all files > 1GB in size for sanity.
280 if (length
> 1024 * 1024 * 1024) {
281 return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
285 if (!file
.ReadU32(&ttc_tag
)) {
286 return OTS_FAILURE_MSG_HDR("Error reading TTC tag");
288 if (ttc_tag
!= OTS_TAG('t','t','c','f')) {
289 return OTS_FAILURE_MSG_HDR("Invalid TTC tag");
292 uint32_t ttc_version
;
293 if (!file
.ReadU32(&ttc_version
)) {
294 return OTS_FAILURE_MSG_HDR("Error reading TTC version");
296 if (ttc_version
!= 0x00010000 && ttc_version
!= 0x00020000) {
297 return OTS_FAILURE_MSG_HDR("Invalid TTC version");
301 if (!file
.ReadU32(&num_fonts
)) {
302 return OTS_FAILURE_MSG_HDR("Error reading number of TTC fonts");
304 // Limit the allowed number of subfonts to have same memory allocation.
305 if (num_fonts
> 0x10000) {
306 return OTS_FAILURE_MSG_HDR("Too many fonts in TTC");
309 std::vector
<uint32_t> offsets(num_fonts
);
310 for (unsigned i
= 0; i
< num_fonts
; i
++) {
311 if (!file
.ReadU32(&offsets
[i
])) {
312 return OTS_FAILURE_MSG_HDR("Error reading offset to OffsetTable");
316 if (ttc_version
== 0x00020000) {
317 // We don't care about these fields of the header:
318 // uint32_t dsig_tag, dsig_length, dsig_offset
319 if (!file
.Skip(3 * 4)) {
320 return OTS_FAILURE_MSG_HDR("Error reading DSIG offset and length in TTC font");
324 if (index
== static_cast<uint32_t>(-1)) {
325 if (!output
->WriteU32(ttc_tag
) ||
326 !output
->WriteU32(0x00010000) ||
327 !output
->WriteU32(num_fonts
) ||
328 !output
->Seek((3 + num_fonts
) * 4)) {
329 return OTS_FAILURE_MSG_HDR("Error writing output");
332 // Keep references to the fonts processed in the loop below, as we need
333 // them for reused tables.
334 std::vector
<ots::Font
> fonts(num_fonts
, ots::Font(header
));
336 for (unsigned i
= 0; i
< num_fonts
; i
++) {
337 uint32_t out_offset
= output
->Tell();
338 if (!output
->Seek((3 + i
) * 4) ||
339 !output
->WriteU32(out_offset
) ||
340 !output
->Seek(out_offset
)) {
341 return OTS_FAILURE_MSG_HDR("Error writing output");
343 if (!ProcessTTF(header
, &fonts
[i
], output
, data
, length
, offsets
[i
])) {
350 if (index
>= num_fonts
) {
351 return OTS_FAILURE_MSG_HDR("Requested font index is bigger than the number of fonts in the TTC file");
354 ots::Font
font(header
);
355 return ProcessTTF(header
, &font
, output
, data
, length
, offsets
[index
]);
359 bool ProcessWOFF(ots::FontFile
*header
,
361 ots::OTSStream
*output
, const uint8_t *data
, size_t length
) {
362 ots::Buffer
file(data
, length
);
364 // we disallow all files > 1GB in size for sanity.
365 if (length
> 1024 * 1024 * 1024) {
366 return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
370 if (!file
.ReadU32(&woff_tag
)) {
371 return OTS_FAILURE_MSG_HDR("error reading WOFF marker");
374 if (woff_tag
!= OTS_TAG('w','O','F','F')) {
375 return OTS_FAILURE_MSG_HDR("invalid WOFF marker");
378 if (!file
.ReadU32(&font
->version
)) {
379 return OTS_FAILURE_MSG_HDR("error reading sfntVersion");
381 if (!ValidateVersionTag(font
)) {
382 return OTS_FAILURE_MSG_HDR("invalid sfntVersion: %d", font
->version
);
385 uint32_t reported_length
;
386 if (!file
.ReadU32(&reported_length
) || length
!= reported_length
) {
387 return OTS_FAILURE_MSG_HDR("incorrect file size in WOFF header");
390 if (!file
.ReadU16(&font
->num_tables
) || !font
->num_tables
) {
391 return OTS_FAILURE_MSG_HDR("error reading number of tables");
394 uint16_t reserved_value
;
395 if (!file
.ReadU16(&reserved_value
) || reserved_value
) {
396 return OTS_FAILURE_MSG_HDR("error in reserved field of WOFF header");
399 uint32_t reported_total_sfnt_size
;
400 if (!file
.ReadU32(&reported_total_sfnt_size
)) {
401 return OTS_FAILURE_MSG_HDR("error reading total sfnt size");
404 // We don't care about these fields of the header:
405 // uint16_t major_version, minor_version
406 if (!file
.Skip(2 * 2)) {
407 return OTS_FAILURE_MSG_HDR("Failed to read 'majorVersion' or 'minorVersion'");
410 // Checks metadata block size.
411 uint32_t meta_offset
;
412 uint32_t meta_length
;
413 uint32_t meta_length_orig
;
414 if (!file
.ReadU32(&meta_offset
) ||
415 !file
.ReadU32(&meta_length
) ||
416 !file
.ReadU32(&meta_length_orig
)) {
417 return OTS_FAILURE_MSG_HDR("Failed to read header metadata block fields");
420 if (meta_offset
>= length
|| length
- meta_offset
< meta_length
) {
421 return OTS_FAILURE_MSG_HDR("Invalid metadata block offset or length");
425 // Checks private data block size.
426 uint32_t priv_offset
;
427 uint32_t priv_length
;
428 if (!file
.ReadU32(&priv_offset
) ||
429 !file
.ReadU32(&priv_length
)) {
430 return OTS_FAILURE_MSG_HDR("Failed to read header private block fields");
433 if (priv_offset
>= length
|| length
- priv_offset
< priv_length
) {
434 return OTS_FAILURE_MSG_HDR("Invalid private block offset or length");
438 // Next up is the list of tables.
439 std::vector
<ots::TableEntry
> tables
;
441 uint32_t first_index
= 0;
442 uint32_t last_index
= 0;
443 // Size of sfnt header plus size of table records.
444 uint64_t total_sfnt_size
= 12 + 16 * font
->num_tables
;
445 for (unsigned i
= 0; i
< font
->num_tables
; ++i
) {
446 ots::TableEntry table
;
447 if (!file
.ReadU32(&table
.tag
) ||
448 !file
.ReadU32(&table
.offset
) ||
449 !file
.ReadU32(&table
.length
) ||
450 !file
.ReadU32(&table
.uncompressed_length
) ||
451 !file
.ReadU32(&table
.chksum
)) {
452 return OTS_FAILURE_MSG_HDR("error reading table directory");
455 total_sfnt_size
+= ots::Round4(table
.uncompressed_length
);
456 if (total_sfnt_size
> std::numeric_limits
<uint32_t>::max()) {
457 return OTS_FAILURE_MSG_HDR("sfnt size overflow");
459 tables
.push_back(table
);
460 if (i
== 0 || tables
[first_index
].offset
> table
.offset
)
462 if (i
== 0 || tables
[last_index
].offset
< table
.offset
)
466 if (reported_total_sfnt_size
!= total_sfnt_size
) {
467 return OTS_FAILURE_MSG_HDR("uncompressed sfnt size mismatch");
470 // Table data must follow immediately after the header.
471 if (tables
[first_index
].offset
!= ots::Round4(file
.offset())) {
472 return OTS_FAILURE_MSG_HDR("junk before tables in WOFF file");
475 if (tables
[last_index
].offset
>= length
||
476 length
- tables
[last_index
].offset
< tables
[last_index
].length
) {
477 return OTS_FAILURE_MSG_HDR("invalid table location/size");
479 // Blocks must follow immediately after the previous block.
480 // (Except for padding with a maximum of three null bytes)
481 uint64_t block_end
= ots::Round4(
482 static_cast<uint64_t>(tables
[last_index
].offset
) +
483 static_cast<uint64_t>(tables
[last_index
].length
));
484 if (block_end
> std::numeric_limits
<uint32_t>::max()) {
485 return OTS_FAILURE_MSG_HDR("invalid table location/size");
488 if (block_end
!= meta_offset
) {
489 return OTS_FAILURE_MSG_HDR("Invalid metadata block offset");
491 block_end
= ots::Round4(static_cast<uint64_t>(meta_offset
) +
492 static_cast<uint64_t>(meta_length
));
493 if (block_end
> std::numeric_limits
<uint32_t>::max()) {
494 return OTS_FAILURE_MSG_HDR("Invalid metadata block length");
498 if (block_end
!= priv_offset
) {
499 return OTS_FAILURE_MSG_HDR("Invalid private block offset");
501 block_end
= ots::Round4(static_cast<uint64_t>(priv_offset
) +
502 static_cast<uint64_t>(priv_length
));
503 if (block_end
> std::numeric_limits
<uint32_t>::max()) {
504 return OTS_FAILURE_MSG_HDR("Invalid private block length");
507 if (block_end
!= ots::Round4(length
)) {
508 return OTS_FAILURE_MSG_HDR("File length mismatch (trailing junk?)");
511 return ProcessGeneric(header
, font
, woff_tag
, output
, data
, length
, tables
, file
);
514 bool ProcessWOFF2(ots::FontFile
* header
, ots::OTSStream
* output
,
515 const uint8_t* data
, size_t length
, uint32_t index
) {
516 return RLBoxProcessWOFF2(header
, output
, data
, length
, index
, ProcessTTC
, ProcessTTF
);
519 ots::TableAction
GetTableAction(const ots::FontFile
*header
, uint32_t tag
) {
520 ots::TableAction action
= header
->context
->GetTableAction(tag
);
522 if (action
== ots::TABLE_ACTION_DEFAULT
) {
523 action
= ots::TABLE_ACTION_DROP
;
525 for (unsigned i
= 0; ; ++i
) {
526 if (supported_tables
[i
].tag
== 0) break;
528 if (supported_tables
[i
].tag
== tag
) {
529 action
= ots::TABLE_ACTION_SANITIZE
;
535 assert(action
!= ots::TABLE_ACTION_DEFAULT
); // Should never return this.
539 bool GetTableData(const uint8_t *data
,
540 const ots::TableEntry
& table
,
542 size_t *table_length
,
543 const uint8_t **table_data
) {
544 if (table
.uncompressed_length
!= table
.length
) {
545 // Compressed table. Need to uncompress into memory first.
546 *table_length
= table
.uncompressed_length
;
547 *table_data
= arena
.Allocate(*table_length
);
548 uLongf dest_len
= *table_length
;
549 int r
= uncompress((Bytef
*) *table_data
, &dest_len
,
550 data
+ table
.offset
, table
.length
);
551 if (r
!= Z_OK
|| dest_len
!= *table_length
) {
555 // Uncompressed table. We can process directly from memory.
556 *table_data
= data
+ table
.offset
;
557 *table_length
= table
.length
;
563 bool ProcessGeneric(ots::FontFile
*header
,
566 ots::OTSStream
*output
,
567 const uint8_t *data
, size_t length
,
568 const std::vector
<ots::TableEntry
>& tables
,
570 const size_t data_offset
= file
.offset();
572 uint32_t uncompressed_sum
= 0;
574 for (unsigned i
= 0; i
< font
->num_tables
; ++i
) {
575 // the tables must be sorted by tag (when taken as big-endian numbers).
576 // This also remove the possibility of duplicate tables.
578 const uint32_t this_tag
= tables
[i
].tag
;
579 const uint32_t prev_tag
= tables
[i
- 1].tag
;
580 if (this_tag
<= prev_tag
) {
581 OTS_WARNING_MSG_HDR("Table directory is not correctly ordered");
585 // all tag names must be built from printable ASCII characters
586 if (!ots::CheckTag(tables
[i
].tag
)) {
587 OTS_WARNING_MSG_HDR("Invalid table tag: 0x%X", tables
[i
].tag
);
590 // tables must be 4-byte aligned
591 if (tables
[i
].offset
& 3) {
592 return OTS_FAILURE_MSG_TAG("misaligned table", tables
[i
].tag
);
595 // and must be within the file
596 if (tables
[i
].offset
< data_offset
|| tables
[i
].offset
>= length
) {
597 return OTS_FAILURE_MSG_TAG("invalid table offset", tables
[i
].tag
);
599 // disallow all tables with a zero length
600 if (tables
[i
].length
< 1) {
601 // Note: malayalam.ttf has zero length CVT table...
602 return OTS_FAILURE_MSG_TAG("zero-length table", tables
[i
].tag
);
604 // disallow all tables with a length > 1GB
605 if (tables
[i
].length
> 1024 * 1024 * 1024) {
606 return OTS_FAILURE_MSG_TAG("table length exceeds 1GB", tables
[i
].tag
);
608 // disallow tables where the uncompressed size is < the compressed size.
609 if (tables
[i
].uncompressed_length
< tables
[i
].length
) {
610 return OTS_FAILURE_MSG_TAG("invalid compressed table", tables
[i
].tag
);
612 if (tables
[i
].uncompressed_length
> tables
[i
].length
) {
613 // We'll probably be decompressing this table.
615 // disallow all tables which decompress to > OTS_MAX_DECOMPRESSED_TABLE_SIZE
616 if (tables
[i
].uncompressed_length
> OTS_MAX_DECOMPRESSED_TABLE_SIZE
) {
617 return OTS_FAILURE_MSG_HDR("%c%c%c%c: decompressed table length exceeds %gMB",
618 OTS_UNTAG(tables
[i
].tag
),
619 OTS_MAX_DECOMPRESSED_TABLE_SIZE
/ (1024.0 * 1024.0));
621 if (uncompressed_sum
+ tables
[i
].uncompressed_length
< uncompressed_sum
) {
622 return OTS_FAILURE_MSG_TAG("overflow of decompressed sum", tables
[i
].tag
);
625 uncompressed_sum
+= tables
[i
].uncompressed_length
;
627 // since we required that the file be < 1GB in length, and that the table
628 // length is < 1GB, the following addtion doesn't overflow
629 uint32_t end_byte
= tables
[i
].offset
+ tables
[i
].length
;
630 // Tables in the WOFF file must be aligned 4-byte boundary.
631 if (signature
== OTS_TAG('w','O','F','F')) {
632 end_byte
= ots::Round4(end_byte
);
634 if (!end_byte
|| end_byte
> length
) {
635 return OTS_FAILURE_MSG_TAG("table overruns end of file", tables
[i
].tag
);
639 // All decompressed tables decompressed must be <= OTS_MAX_DECOMPRESSED_FILE_SIZE.
640 if (uncompressed_sum
> OTS_MAX_DECOMPRESSED_FILE_SIZE
) {
641 return OTS_FAILURE_MSG_HDR("decompressed sum exceeds %gMB",
642 OTS_MAX_DECOMPRESSED_FILE_SIZE
/ (1024.0 * 1024.0));
645 // check that the tables are not overlapping.
646 std::vector
<std::pair
<uint32_t, uint8_t> > overlap_checker
;
647 for (unsigned i
= 0; i
< font
->num_tables
; ++i
) {
648 overlap_checker
.push_back(
649 std::make_pair(tables
[i
].offset
, static_cast<uint8_t>(1) /* start */));
650 overlap_checker
.push_back(
651 std::make_pair(tables
[i
].offset
+ tables
[i
].length
,
652 static_cast<uint8_t>(0) /* end */));
654 std::sort(overlap_checker
.begin(), overlap_checker
.end());
655 int overlap_count
= 0;
656 for (unsigned i
= 0; i
< overlap_checker
.size(); ++i
) {
657 overlap_count
+= (overlap_checker
[i
].second
? 1 : -1);
658 if (overlap_count
> 1) {
659 return OTS_FAILURE_MSG_HDR("overlapping tables");
663 std::map
<uint32_t, ots::TableEntry
> table_map
;
664 for (unsigned i
= 0; i
< font
->num_tables
; ++i
) {
665 table_map
[tables
[i
].tag
] = tables
[i
];
669 // Parse known tables first as we need to parse them in specific order.
670 for (unsigned i
= 0; ; ++i
) {
671 if (supported_tables
[i
].tag
== 0) break;
673 uint32_t tag
= supported_tables
[i
].tag
;
674 const auto &it
= table_map
.find(tag
);
675 if (it
== table_map
.cend()) {
676 if (supported_tables
[i
].required
) {
677 return OTS_FAILURE_MSG_TAG("missing required table", tag
);
680 if (!font
->ParseTable(it
->second
, data
, arena
)) {
681 return OTS_FAILURE_MSG_TAG("Failed to parse table", tag
);
686 // Then parse any tables left.
687 for (const auto &table_entry
: tables
) {
688 if (!font
->GetTable(table_entry
.tag
)) {
689 if (!font
->ParseTable(table_entry
, data
, arena
)) {
690 return OTS_FAILURE_MSG_TAG("Failed to parse table", table_entry
.tag
);
695 ots::Table
*glyf
= font
->GetTable(OTS_TAG_GLYF
);
696 ots::Table
*loca
= font
->GetTable(OTS_TAG_LOCA
);
697 ots::Table
*cff
= font
->GetTable(OTS_TAG_CFF
);
698 ots::Table
*cff2
= font
->GetTable(OTS_TAG_CFF2
);
701 if (font
->version
!= 0x000010000) {
702 OTS_WARNING_MSG_HDR("wrong sfntVersion for glyph data");
703 font
->version
= 0x000010000;
706 cff
->Drop("font contains both CFF and glyf/loca tables");
708 cff2
->Drop("font contains both CFF and glyf/loca tables");
709 } else if (cff
|| cff2
) {
710 if (font
->version
!= OTS_TAG('O','T','T','O')) {
711 OTS_WARNING_MSG_HDR("wrong sfntVersion for glyph data");
712 font
->version
= OTS_TAG('O','T','T','O');
715 glyf
->Drop("font contains both CFF and glyf tables");
717 loca
->Drop("font contains both CFF and loca tables");
718 } else if (font
->GetTable(OTS_TAG('C','B','D','T')) &&
719 font
->GetTable(OTS_TAG('C','B','L','C'))) {
720 // We don't sanitize bitmap tables, but don’t reject bitmap-only fonts if
721 // we are asked to pass them thru.
723 return OTS_FAILURE_MSG_HDR("no supported glyph data table(s) present");
726 uint16_t num_output_tables
= 0;
727 for (const auto &it
: table_map
) {
728 ots::Table
*table
= font
->GetTable(it
.first
);
733 uint16_t max_pow2
= 0;
734 while (1u << (max_pow2
+ 1) <= num_output_tables
) {
737 const uint16_t output_search_range
= (1u << max_pow2
) << 4;
739 // most of the errors here are highly unlikely - they'd only occur if the
740 // output stream returns a failure, e.g. lack of space to write
741 output
->ResetChecksum();
742 if (!output
->WriteU32(font
->version
) ||
743 !output
->WriteU16(num_output_tables
) ||
744 !output
->WriteU16(output_search_range
) ||
745 !output
->WriteU16(max_pow2
) ||
746 !output
->WriteU16((num_output_tables
<< 4) - output_search_range
)) {
747 return OTS_FAILURE_MSG_HDR("error writing output");
749 const uint32_t offset_table_chksum
= output
->chksum();
751 const size_t table_record_offset
= output
->Tell();
752 if (!output
->Pad(16 * num_output_tables
)) {
753 return OTS_FAILURE_MSG_HDR("error writing output");
756 std::vector
<ots::TableEntry
> out_tables
;
758 size_t head_table_offset
= 0;
759 for (const auto &it
: table_map
) {
760 uint32_t input_offset
= it
.second
.offset
;
761 const auto &ot
= header
->table_entries
.find(input_offset
);
762 if (ot
!= header
->table_entries
.end()) {
763 ots::TableEntry out
= ot
->second
;
764 if (out
.tag
== OTS_TAG('h','e','a','d')) {
765 head_table_offset
= out
.offset
;
767 out_tables
.push_back(out
);
771 out
.offset
= output
->Tell();
773 if (out
.tag
== OTS_TAG('h','e','a','d')) {
774 head_table_offset
= out
.offset
;
777 ots::Table
*table
= font
->GetTable(out
.tag
);
779 output
->ResetChecksum();
780 if (!table
->Serialize(output
)) {
781 return OTS_FAILURE_MSG_TAG("Failed to serialize table", out
.tag
);
784 const size_t end_offset
= output
->Tell();
785 if (end_offset
<= out
.offset
) {
786 // paranoid check. |end_offset| is supposed to be greater than the offset,
787 // as long as the Tell() interface is implemented correctly.
788 return OTS_FAILURE_MSG_TAG("Table is empty or have -ve size", out
.tag
);
790 out
.length
= end_offset
- out
.offset
;
792 // align tables to four bytes
793 if (!output
->Pad((4 - (end_offset
& 3)) % 4)) {
794 return OTS_FAILURE_MSG_TAG("Failed to pad table to 4 bytes", out
.tag
);
796 out
.chksum
= output
->chksum();
797 out_tables
.push_back(out
);
798 header
->table_entries
[input_offset
] = out
;
803 const size_t end_of_file
= output
->Tell();
805 // Need to sort the output tables for inclusion in the file
806 std::sort(out_tables
.begin(), out_tables
.end());
807 if (!output
->Seek(table_record_offset
)) {
808 return OTS_FAILURE_MSG_HDR("error writing output");
811 output
->ResetChecksum();
812 uint32_t tables_chksum
= 0;
813 for (unsigned i
= 0; i
< out_tables
.size(); ++i
) {
814 if (!output
->WriteU32(out_tables
[i
].tag
) ||
815 !output
->WriteU32(out_tables
[i
].chksum
) ||
816 !output
->WriteU32(out_tables
[i
].offset
) ||
817 !output
->WriteU32(out_tables
[i
].length
)) {
818 return OTS_FAILURE_MSG_HDR("error writing output");
820 tables_chksum
+= out_tables
[i
].chksum
;
822 const uint32_t table_record_chksum
= output
->chksum();
824 // http://www.microsoft.com/typography/otspec/otff.htm
825 const uint32_t file_chksum
826 = offset_table_chksum
+ tables_chksum
+ table_record_chksum
;
827 const uint32_t chksum_magic
= static_cast<uint32_t>(0xb1b0afba) - file_chksum
;
829 // seek into the 'head' table and write in the checksum magic value
830 if (!head_table_offset
) {
831 return OTS_FAILURE_MSG_HDR("internal error!");
833 if (!output
->Seek(head_table_offset
+ 8)) {
834 return OTS_FAILURE_MSG_HDR("error writing output");
836 if (!output
->WriteU32(chksum_magic
)) {
837 return OTS_FAILURE_MSG_HDR("error writing output");
840 if (!output
->Seek(end_of_file
)) {
841 return OTS_FAILURE_MSG_HDR("error writing output");
851 FontFile::~FontFile() {
852 for (const auto& it
: tables
) {
858 bool Font::ParseTable(const TableEntry
& table_entry
, const uint8_t* data
,
860 uint32_t tag
= table_entry
.tag
;
861 TableAction action
= GetTableAction(file
, tag
);
862 if (action
== TABLE_ACTION_DROP
) {
866 const auto &it
= file
->tables
.find(table_entry
);
867 if (it
!= file
->tables
.end()) {
868 m_tables
[tag
] = it
->second
;
875 if (action
== TABLE_ACTION_PASSTHRU
) {
876 table
= new TablePassthru(this, tag
);
879 case OTS_TAG_AVAR
: table
= new OpenTypeAVAR(this, tag
); break;
880 case OTS_TAG_CFF
: table
= new OpenTypeCFF(this, tag
); break;
881 case OTS_TAG_CFF2
: table
= new OpenTypeCFF2(this, tag
); break;
882 case OTS_TAG_CMAP
: table
= new OpenTypeCMAP(this, tag
); break;
883 case OTS_TAG_CVAR
: table
= new OpenTypeCVAR(this, tag
); break;
884 case OTS_TAG_CVT
: table
= new OpenTypeCVT(this, tag
); break;
885 case OTS_TAG_FPGM
: table
= new OpenTypeFPGM(this, tag
); break;
886 case OTS_TAG_FVAR
: table
= new OpenTypeFVAR(this, tag
); break;
887 case OTS_TAG_GASP
: table
= new OpenTypeGASP(this, tag
); break;
888 case OTS_TAG_GDEF
: table
= new OpenTypeGDEF(this, tag
); break;
889 case OTS_TAG_GLYF
: table
= new OpenTypeGLYF(this, tag
); break;
890 case OTS_TAG_GPOS
: table
= new OpenTypeGPOS(this, tag
); break;
891 case OTS_TAG_GSUB
: table
= new OpenTypeGSUB(this, tag
); break;
892 case OTS_TAG_GVAR
: table
= new OpenTypeGVAR(this, tag
); break;
893 case OTS_TAG_HDMX
: table
= new OpenTypeHDMX(this, tag
); break;
894 case OTS_TAG_HEAD
: table
= new OpenTypeHEAD(this, tag
); break;
895 case OTS_TAG_HHEA
: table
= new OpenTypeHHEA(this, tag
); break;
896 case OTS_TAG_HMTX
: table
= new OpenTypeHMTX(this, tag
); break;
897 case OTS_TAG_HVAR
: table
= new OpenTypeHVAR(this, tag
); break;
898 case OTS_TAG_KERN
: table
= new OpenTypeKERN(this, tag
); break;
899 case OTS_TAG_LOCA
: table
= new OpenTypeLOCA(this, tag
); break;
900 case OTS_TAG_LTSH
: table
= new OpenTypeLTSH(this, tag
); break;
901 case OTS_TAG_MATH
: table
= new OpenTypeMATH(this, tag
); break;
902 case OTS_TAG_MAXP
: table
= new OpenTypeMAXP(this, tag
); break;
903 case OTS_TAG_MVAR
: table
= new OpenTypeMVAR(this, tag
); break;
904 case OTS_TAG_NAME
: table
= new OpenTypeNAME(this, tag
); break;
905 case OTS_TAG_OS2
: table
= new OpenTypeOS2(this, tag
); break;
906 case OTS_TAG_POST
: table
= new OpenTypePOST(this, tag
); break;
907 case OTS_TAG_PREP
: table
= new OpenTypePREP(this, tag
); break;
908 case OTS_TAG_STAT
: table
= new OpenTypeSTAT(this, tag
); break;
909 case OTS_TAG_VDMX
: table
= new OpenTypeVDMX(this, tag
); break;
910 case OTS_TAG_VHEA
: table
= new OpenTypeVHEA(this, tag
); break;
911 case OTS_TAG_VMTX
: table
= new OpenTypeVMTX(this, tag
); break;
912 case OTS_TAG_VORG
: table
= new OpenTypeVORG(this, tag
); break;
913 case OTS_TAG_VVAR
: table
= new OpenTypeVVAR(this, tag
); break;
916 case OTS_TAG_FEAT
: table
= new OpenTypeFEAT(this, tag
); break;
917 case OTS_TAG_GLAT
: table
= new OpenTypeGLAT(this, tag
); break;
918 case OTS_TAG_GLOC
: table
= new OpenTypeGLOC(this, tag
); break;
919 case OTS_TAG_SILE
: table
= new OpenTypeSILE(this, tag
); break;
920 case OTS_TAG_SILF
: table
= new OpenTypeSILF(this, tag
); break;
921 case OTS_TAG_SILL
: table
= new OpenTypeSILL(this, tag
); break;
928 const uint8_t* table_data
;
931 ret
= GetTableData(data
, table_entry
, arena
, &table_length
, &table_data
);
933 // FIXME: Parsing some tables will fail if the table is not added to
935 m_tables
[tag
] = table
;
936 ret
= table
->Parse(table_data
, table_length
);
938 file
->tables
[table_entry
] = table
;
950 Table
* Font::GetTable(uint32_t tag
) const {
951 const auto &it
= m_tables
.find(tag
);
952 if (it
!= m_tables
.end() && it
->second
&& it
->second
->ShouldSerialize())
957 Table
* Font::GetTypedTable(uint32_t tag
) const {
958 Table
* t
= GetTable(tag
);
959 if (t
&& t
->Type() == tag
)
964 void Font::DropGraphite() {
965 file
->context
->Message(0, "Dropping all Graphite tables");
966 for (const std::pair
<uint32_t, Table
*> entry
: m_tables
) {
967 if (entry
.first
== OTS_TAG_FEAT
||
968 entry
.first
== OTS_TAG_GLAT
||
969 entry
.first
== OTS_TAG_GLOC
||
970 entry
.first
== OTS_TAG_SILE
||
971 entry
.first
== OTS_TAG_SILF
||
972 entry
.first
== OTS_TAG_SILL
) {
973 entry
.second
->Drop("Discarding Graphite table");
978 void Font::DropVariations() {
979 file
->context
->Message(0, "Dropping all Variation tables");
980 for (const std::pair
<uint32_t, Table
*> entry
: m_tables
) {
981 if (entry
.first
== OTS_TAG_AVAR
||
982 entry
.first
== OTS_TAG_CVAR
||
983 entry
.first
== OTS_TAG_FVAR
||
984 entry
.first
== OTS_TAG_GVAR
||
985 entry
.first
== OTS_TAG_HVAR
||
986 entry
.first
== OTS_TAG_MVAR
||
987 entry
.first
== OTS_TAG_STAT
||
988 entry
.first
== OTS_TAG_VVAR
) {
989 entry
.second
->Drop("Discarding Variations table");
994 bool Table::ShouldSerialize() {
995 return m_shouldSerialize
;
998 void Table::Message(int level
, const char *format
, va_list va
) {
999 char msg
[206] = { OTS_UNTAG(m_tag
), ':', ' ' };
1000 std::vsnprintf(msg
+ 6, 200, format
, va
);
1001 m_font
->file
->context
->Message(level
, msg
);
1004 bool Table::Error(const char *format
, ...) {
1006 va_start(va
, format
);
1007 Message(0, format
, va
);
1013 bool Table::Warning(const char *format
, ...) {
1015 va_start(va
, format
);
1016 Message(1, format
, va
);
1022 bool Table::Drop(const char *format
, ...) {
1023 m_shouldSerialize
= false;
1026 va_start(va
, format
);
1027 Message(0, format
, va
);
1028 m_font
->file
->context
->Message(0, "Table discarded");
1034 bool Table::DropGraphite(const char *format
, ...) {
1036 va_start(va
, format
);
1037 Message(0, format
, va
);
1040 m_font
->DropGraphite();
1044 bool Table::DropVariations(const char *format
, ...) {
1046 va_start(va
, format
);
1047 Message(0, format
, va
);
1050 m_font
->DropVariations();
1054 bool TablePassthru::Parse(const uint8_t *data
, size_t length
) {
1060 bool TablePassthru::Serialize(OTSStream
*out
) {
1061 if (!out
->Write(m_data
, m_length
)) {
1062 return Error("Failed to write table");
1068 bool OTSContext::Process(OTSStream
*output
,
1069 const uint8_t *data
,
1074 header
.context
= this;
1077 return OTS_FAILURE_MSG_(&header
, "file less than 4 bytes");
1081 if (data
[0] == 'w' && data
[1] == 'O' && data
[2] == 'F' && data
[3] == 'F') {
1082 result
= ProcessWOFF(&header
, &font
, output
, data
, length
);
1083 } else if (data
[0] == 'w' && data
[1] == 'O' && data
[2] == 'F' && data
[3] == '2') {
1084 result
= ProcessWOFF2(&header
, output
, data
, length
, index
);
1085 } else if (data
[0] == 't' && data
[1] == 't' && data
[2] == 'c' && data
[3] == 'f') {
1086 result
= ProcessTTC(&header
, output
, data
, length
, index
);
1088 result
= ProcessTTF(&header
, &font
, output
, data
, length
);