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.
19 // The OpenType Font File
20 // http://www.microsoft.com/typography/otspec/cmap.htm
24 // Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer
25 #define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE_MSG_TAG_(header, msg_, tag_)
26 #define OTS_FAILURE_MSG_HDR(msg_) OTS_FAILURE_MSG_(header, msg_)
27 #define OTS_WARNING_MSG_HDR(msg_) OTS_WARNING_MSG_(header, msg_)
30 struct OpenTypeTable
{
35 uint32_t uncompressed_length
;
38 bool CheckTag(uint32_t tag_value
) {
39 for (unsigned i
= 0; i
< 4; ++i
) {
40 const uint32_t check
= tag_value
& 0xff;
41 if (check
< 32 || check
> 126) {
42 return false; // non-ASCII character found.
49 uint32_t Tag(const char *tag_str
) {
51 std::memcpy(&ret
, tag_str
, 4);
61 bool operator<(const OutputTable
& other
) const {
62 return ntohl(tag
) < ntohl(other
.tag
);
69 for (std::vector
<uint8_t*>::iterator
70 i
= hunks_
.begin(); i
!= hunks_
.end(); ++i
) {
75 uint8_t* Allocate(size_t length
) {
76 uint8_t* p
= new uint8_t[length
];
82 std::vector
<uint8_t*> hunks_
;
87 bool (*parse
)(ots::OpenTypeFile
*otf
, const uint8_t *data
, size_t length
);
88 bool (*serialise
)(ots::OTSStream
*out
, ots::OpenTypeFile
*file
);
89 bool (*should_serialise
)(ots::OpenTypeFile
*file
);
90 void (*free
)(ots::OpenTypeFile
*file
);
93 { "maxp", ots::ots_maxp_parse
, ots::ots_maxp_serialise
,
94 ots::ots_maxp_should_serialise
, ots::ots_maxp_free
, true },
95 { "head", ots::ots_head_parse
, ots::ots_head_serialise
,
96 ots::ots_head_should_serialise
, ots::ots_head_free
, true },
97 { "OS/2", ots::ots_os2_parse
, ots::ots_os2_serialise
,
98 ots::ots_os2_should_serialise
, ots::ots_os2_free
, true },
99 { "cmap", ots::ots_cmap_parse
, ots::ots_cmap_serialise
,
100 ots::ots_cmap_should_serialise
, ots::ots_cmap_free
, true },
101 { "hhea", ots::ots_hhea_parse
, ots::ots_hhea_serialise
,
102 ots::ots_hhea_should_serialise
, ots::ots_hhea_free
, true },
103 { "hmtx", ots::ots_hmtx_parse
, ots::ots_hmtx_serialise
,
104 ots::ots_hmtx_should_serialise
, ots::ots_hmtx_free
, true },
105 { "name", ots::ots_name_parse
, ots::ots_name_serialise
,
106 ots::ots_name_should_serialise
, ots::ots_name_free
, true },
107 { "post", ots::ots_post_parse
, ots::ots_post_serialise
,
108 ots::ots_post_should_serialise
, ots::ots_post_free
, true },
109 { "loca", ots::ots_loca_parse
, ots::ots_loca_serialise
,
110 ots::ots_loca_should_serialise
, ots::ots_loca_free
, false },
111 { "glyf", ots::ots_glyf_parse
, ots::ots_glyf_serialise
,
112 ots::ots_glyf_should_serialise
, ots::ots_glyf_free
, false },
113 { "CFF ", ots::ots_cff_parse
, ots::ots_cff_serialise
,
114 ots::ots_cff_should_serialise
, ots::ots_cff_free
, false },
115 { "VDMX", ots::ots_vdmx_parse
, ots::ots_vdmx_serialise
,
116 ots::ots_vdmx_should_serialise
, ots::ots_vdmx_free
, false },
117 { "hdmx", ots::ots_hdmx_parse
, ots::ots_hdmx_serialise
,
118 ots::ots_hdmx_should_serialise
, ots::ots_hdmx_free
, false },
119 { "gasp", ots::ots_gasp_parse
, ots::ots_gasp_serialise
,
120 ots::ots_gasp_should_serialise
, ots::ots_gasp_free
, false },
121 { "cvt ", ots::ots_cvt_parse
, ots::ots_cvt_serialise
,
122 ots::ots_cvt_should_serialise
, ots::ots_cvt_free
, false },
123 { "fpgm", ots::ots_fpgm_parse
, ots::ots_fpgm_serialise
,
124 ots::ots_fpgm_should_serialise
, ots::ots_fpgm_free
, false },
125 { "prep", ots::ots_prep_parse
, ots::ots_prep_serialise
,
126 ots::ots_prep_should_serialise
, ots::ots_prep_free
, false },
127 { "LTSH", ots::ots_ltsh_parse
, ots::ots_ltsh_serialise
,
128 ots::ots_ltsh_should_serialise
, ots::ots_ltsh_free
, false },
129 { "VORG", ots::ots_vorg_parse
, ots::ots_vorg_serialise
,
130 ots::ots_vorg_should_serialise
, ots::ots_vorg_free
, false },
131 { "kern", ots::ots_kern_parse
, ots::ots_kern_serialise
,
132 ots::ots_kern_should_serialise
, ots::ots_kern_free
, false },
133 // We need to parse GDEF table in advance of parsing GSUB/GPOS tables
134 // because they could refer GDEF table.
135 { "GDEF", ots::ots_gdef_parse
, ots::ots_gdef_serialise
,
136 ots::ots_gdef_should_serialise
, ots::ots_gdef_free
, false },
137 { "GPOS", ots::ots_gpos_parse
, ots::ots_gpos_serialise
,
138 ots::ots_gpos_should_serialise
, ots::ots_gpos_free
, false },
139 { "GSUB", ots::ots_gsub_parse
, ots::ots_gsub_serialise
,
140 ots::ots_gsub_should_serialise
, ots::ots_gsub_free
, false },
141 { "vhea", ots::ots_vhea_parse
, ots::ots_vhea_serialise
,
142 ots::ots_vhea_should_serialise
, ots::ots_vhea_free
, false },
143 { "vmtx", ots::ots_vmtx_parse
, ots::ots_vmtx_serialise
,
144 ots::ots_vmtx_should_serialise
, ots::ots_vmtx_free
, false },
145 { "MATH", ots::ots_math_parse
, ots::ots_math_serialise
,
146 ots::ots_math_should_serialise
, ots::ots_math_free
, false },
147 { 0, NULL
, NULL
, NULL
, NULL
, false },
150 bool ProcessGeneric(ots::OpenTypeFile
*header
,
152 ots::OTSStream
*output
,
153 const uint8_t *data
, size_t length
,
154 const std::vector
<OpenTypeTable
>& tables
,
157 bool ProcessTTF(ots::OpenTypeFile
*header
,
158 ots::OTSStream
*output
, const uint8_t *data
, size_t length
) {
159 ots::Buffer
file(data
, length
);
161 // we disallow all files > 1GB in size for sanity.
162 if (length
> 1024 * 1024 * 1024) {
163 return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
166 if (!file
.ReadTag(&header
->version
)) {
167 return OTS_FAILURE_MSG_HDR("error reading version tag");
169 if (!ots::IsValidVersionTag(header
->version
)) {
170 return OTS_FAILURE_MSG_HDR("invalid version tag");
173 if (!file
.ReadU16(&header
->num_tables
) ||
174 !file
.ReadU16(&header
->search_range
) ||
175 !file
.ReadU16(&header
->entry_selector
) ||
176 !file
.ReadU16(&header
->range_shift
)) {
177 return OTS_FAILURE_MSG_HDR("error reading table directory search header");
180 // search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid
181 // overflow num_tables is, at most, 2^16 / 16 = 2^12
182 if (header
->num_tables
>= 4096 || header
->num_tables
< 1) {
183 return OTS_FAILURE_MSG_HDR("excessive (or zero) number of tables");
186 unsigned max_pow2
= 0;
187 while (1u << (max_pow2
+ 1) <= header
->num_tables
) {
190 const uint16_t expected_search_range
= (1u << max_pow2
) << 4;
192 // Don't call ots_failure() here since ~25% of fonts (250+ fonts) in
193 // http://www.princexml.com/fonts/ have unexpected search_range value.
194 if (header
->search_range
!= expected_search_range
) {
195 OTS_WARNING_MSG_HDR("bad search range");
196 header
->search_range
= expected_search_range
; // Fix the value.
199 // entry_selector is Log2(maximum power of 2 <= numTables)
200 if (header
->entry_selector
!= max_pow2
) {
201 return OTS_FAILURE_MSG_HDR("incorrect entrySelector for table directory");
204 // range_shift is NumTables x 16-searchRange. We know that 16*num_tables
205 // doesn't over flow because we range checked it above. Also, we know that
206 // it's > header->search_range by construction of search_range.
207 const uint16_t expected_range_shift
=
208 16 * header
->num_tables
- header
->search_range
;
209 if (header
->range_shift
!= expected_range_shift
) {
210 OTS_WARNING_MSG_HDR("bad range shift");
211 header
->range_shift
= expected_range_shift
; // the same as above.
214 // Next up is the list of tables.
215 std::vector
<OpenTypeTable
> tables
;
217 for (unsigned i
= 0; i
< header
->num_tables
; ++i
) {
219 if (!file
.ReadTag(&table
.tag
) ||
220 !file
.ReadU32(&table
.chksum
) ||
221 !file
.ReadU32(&table
.offset
) ||
222 !file
.ReadU32(&table
.length
)) {
223 return OTS_FAILURE_MSG_HDR("error reading table directory");
226 table
.uncompressed_length
= table
.length
;
227 tables
.push_back(table
);
230 return ProcessGeneric(header
, header
->version
, output
, data
, length
,
234 bool ProcessWOFF(ots::OpenTypeFile
*header
,
235 ots::OTSStream
*output
, const uint8_t *data
, size_t length
) {
236 ots::Buffer
file(data
, length
);
238 // we disallow all files > 1GB in size for sanity.
239 if (length
> 1024 * 1024 * 1024) {
240 return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
244 if (!file
.ReadTag(&woff_tag
)) {
245 return OTS_FAILURE_MSG_HDR("error reading WOFF marker");
248 if (woff_tag
!= Tag("wOFF")) {
249 return OTS_FAILURE_MSG_HDR("invalid WOFF marker");
252 if (!file
.ReadTag(&header
->version
)) {
253 return OTS_FAILURE_MSG_HDR("error reading version tag");
255 if (!ots::IsValidVersionTag(header
->version
)) {
256 return OTS_FAILURE_MSG_HDR("invalid version tag");
259 header
->search_range
= 0;
260 header
->entry_selector
= 0;
261 header
->range_shift
= 0;
263 uint32_t reported_length
;
264 if (!file
.ReadU32(&reported_length
) || length
!= reported_length
) {
265 return OTS_FAILURE_MSG_HDR("incorrect file size in WOFF header");
268 if (!file
.ReadU16(&header
->num_tables
) || !header
->num_tables
) {
269 return OTS_FAILURE_MSG_HDR("error reading number of tables");
272 uint16_t reserved_value
;
273 if (!file
.ReadU16(&reserved_value
) || reserved_value
) {
274 return OTS_FAILURE_MSG_HDR("error in reserved field of WOFF header");
277 uint32_t reported_total_sfnt_size
;
278 if (!file
.ReadU32(&reported_total_sfnt_size
)) {
279 return OTS_FAILURE_MSG_HDR("error reading total sfnt size");
282 // We don't care about these fields of the header:
283 // uint16_t major_version, minor_version
284 if (!file
.Skip(2 * 2)) {
285 return OTS_FAILURE_MSG_HDR("Failed to read 'majorVersion' or 'minorVersion'");
288 // Checks metadata block size.
289 uint32_t meta_offset
;
290 uint32_t meta_length
;
291 uint32_t meta_length_orig
;
292 if (!file
.ReadU32(&meta_offset
) ||
293 !file
.ReadU32(&meta_length
) ||
294 !file
.ReadU32(&meta_length_orig
)) {
295 return OTS_FAILURE_MSG_HDR("Failed to read header metadata block fields");
298 if (meta_offset
>= length
|| length
- meta_offset
< meta_length
) {
299 return OTS_FAILURE_MSG_HDR("Invalid metadata block offset or length");
303 // Checks private data block size.
304 uint32_t priv_offset
;
305 uint32_t priv_length
;
306 if (!file
.ReadU32(&priv_offset
) ||
307 !file
.ReadU32(&priv_length
)) {
308 return OTS_FAILURE_MSG_HDR("Failed to read header private block fields");
311 if (priv_offset
>= length
|| length
- priv_offset
< priv_length
) {
312 return OTS_FAILURE_MSG_HDR("Invalid private block offset or length");
316 // Next up is the list of tables.
317 std::vector
<OpenTypeTable
> tables
;
319 uint32_t first_index
= 0;
320 uint32_t last_index
= 0;
321 // Size of sfnt header plus size of table records.
322 uint64_t total_sfnt_size
= 12 + 16 * header
->num_tables
;
323 for (unsigned i
= 0; i
< header
->num_tables
; ++i
) {
325 if (!file
.ReadTag(&table
.tag
) ||
326 !file
.ReadU32(&table
.offset
) ||
327 !file
.ReadU32(&table
.length
) ||
328 !file
.ReadU32(&table
.uncompressed_length
) ||
329 !file
.ReadU32(&table
.chksum
)) {
330 return OTS_FAILURE_MSG_HDR("error reading table directory");
333 total_sfnt_size
+= ots::Round4(table
.uncompressed_length
);
334 if (total_sfnt_size
> std::numeric_limits
<uint32_t>::max()) {
335 return OTS_FAILURE_MSG_HDR("sfnt size overflow");
337 tables
.push_back(table
);
338 if (i
== 0 || tables
[first_index
].offset
> table
.offset
)
340 if (i
== 0 || tables
[last_index
].offset
< table
.offset
)
344 if (reported_total_sfnt_size
!= total_sfnt_size
) {
345 return OTS_FAILURE_MSG_HDR("uncompressed sfnt size mismatch");
348 // Table data must follow immediately after the header.
349 if (tables
[first_index
].offset
!= ots::Round4(file
.offset())) {
350 return OTS_FAILURE_MSG_HDR("junk before tables in WOFF file");
353 if (tables
[last_index
].offset
>= length
||
354 length
- tables
[last_index
].offset
< tables
[last_index
].length
) {
355 return OTS_FAILURE_MSG_HDR("invalid table location/size");
357 // Blocks must follow immediately after the previous block.
358 // (Except for padding with a maximum of three null bytes)
359 uint64_t block_end
= ots::Round4(
360 static_cast<uint64_t>(tables
[last_index
].offset
) +
361 static_cast<uint64_t>(tables
[last_index
].length
));
362 if (block_end
> std::numeric_limits
<uint32_t>::max()) {
363 return OTS_FAILURE_MSG_HDR("invalid table location/size");
366 if (block_end
!= meta_offset
) {
367 return OTS_FAILURE_MSG_HDR("Invalid metadata block offset");
369 block_end
= ots::Round4(static_cast<uint64_t>(meta_offset
) +
370 static_cast<uint64_t>(meta_length
));
371 if (block_end
> std::numeric_limits
<uint32_t>::max()) {
372 return OTS_FAILURE_MSG_HDR("Invalid metadata block length");
376 if (block_end
!= priv_offset
) {
377 return OTS_FAILURE_MSG_HDR("Invalid private block offset");
379 block_end
= ots::Round4(static_cast<uint64_t>(priv_offset
) +
380 static_cast<uint64_t>(priv_length
));
381 if (block_end
> std::numeric_limits
<uint32_t>::max()) {
382 return OTS_FAILURE_MSG_HDR("Invalid private block length");
385 if (block_end
!= ots::Round4(length
)) {
386 return OTS_FAILURE_MSG_HDR("File length mismatch (trailing junk?)");
389 return ProcessGeneric(header
, woff_tag
, output
, data
, length
, tables
, file
);
392 bool ProcessWOFF2(ots::OpenTypeFile
*header
,
393 ots::OTSStream
*output
, const uint8_t *data
, size_t length
) {
394 size_t decompressed_size
= ots::ComputeWOFF2FinalSize(data
, length
);
395 if (decompressed_size
== 0) {
396 return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is set to 0");
398 // decompressed font must be <= 30MB
399 if (decompressed_size
> 30 * 1024 * 1024) {
400 return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 font exceeds 30MB");
403 std::vector
<uint8_t> decompressed_buffer(decompressed_size
);
404 if (!ots::ConvertWOFF2ToSFNT(header
, &decompressed_buffer
[0], decompressed_size
,
406 return OTS_FAILURE_MSG_HDR("Failed to convert WOFF 2.0 font to SFNT");
408 return ProcessTTF(header
, output
, &decompressed_buffer
[0], decompressed_size
);
411 ots::TableAction
GetTableAction(ots::OpenTypeFile
*header
, uint32_t tag
) {
412 ots::TableAction action
= ots::TABLE_ACTION_DEFAULT
;
414 action
= header
->context
->GetTableAction(htonl(tag
));
416 if (action
== ots::TABLE_ACTION_DEFAULT
) {
417 action
= ots::TABLE_ACTION_DROP
;
419 for (unsigned i
= 0; ; ++i
) {
420 if (table_parsers
[i
].parse
== NULL
) break;
422 if (Tag(table_parsers
[i
].tag
) == tag
) {
423 action
= ots::TABLE_ACTION_SANITIZE
;
429 assert(action
!= ots::TABLE_ACTION_DEFAULT
); // Should never return this.
433 bool GetTableData(const uint8_t *data
,
434 const OpenTypeTable table
,
436 size_t *table_length
,
437 const uint8_t **table_data
) {
438 if (table
.uncompressed_length
!= table
.length
) {
439 // Compressed table. Need to uncompress into memory first.
440 *table_length
= table
.uncompressed_length
;
441 *table_data
= (*arena
).Allocate(*table_length
);
442 uLongf dest_len
= *table_length
;
443 int r
= uncompress((Bytef
*) *table_data
, &dest_len
,
444 data
+ table
.offset
, table
.length
);
445 if (r
!= Z_OK
|| dest_len
!= *table_length
) {
449 // Uncompressed table. We can process directly from memory.
450 *table_data
= data
+ table
.offset
;
451 *table_length
= table
.length
;
457 bool ProcessGeneric(ots::OpenTypeFile
*header
, uint32_t signature
,
458 ots::OTSStream
*output
,
459 const uint8_t *data
, size_t length
,
460 const std::vector
<OpenTypeTable
>& tables
,
462 const size_t data_offset
= file
.offset();
464 uint32_t uncompressed_sum
= 0;
466 for (unsigned i
= 0; i
< header
->num_tables
; ++i
) {
467 // the tables must be sorted by tag (when taken as big-endian numbers).
468 // This also remove the possibility of duplicate tables.
470 const uint32_t this_tag
= ntohl(tables
[i
].tag
);
471 const uint32_t prev_tag
= ntohl(tables
[i
- 1].tag
);
472 if (this_tag
<= prev_tag
) {
473 OTS_WARNING_MSG_HDR("Table directory is not correctly ordered");
477 // all tag names must be built from printable ASCII characters
478 if (!CheckTag(tables
[i
].tag
)) {
479 return OTS_FAILURE_MSG_TAG("invalid table tag", &tables
[i
].tag
);
482 // tables must be 4-byte aligned
483 if (tables
[i
].offset
& 3) {
484 return OTS_FAILURE_MSG_TAG("misaligned table", &tables
[i
].tag
);
487 // and must be within the file
488 if (tables
[i
].offset
< data_offset
|| tables
[i
].offset
>= length
) {
489 return OTS_FAILURE_MSG_TAG("invalid table offset", &tables
[i
].tag
);
491 // disallow all tables with a zero length
492 if (tables
[i
].length
< 1) {
493 // Note: malayalam.ttf has zero length CVT table...
494 return OTS_FAILURE_MSG_TAG("zero-length table", &tables
[i
].tag
);
496 // disallow all tables with a length > 1GB
497 if (tables
[i
].length
> 1024 * 1024 * 1024) {
498 return OTS_FAILURE_MSG_TAG("table length exceeds 1GB", &tables
[i
].tag
);
500 // disallow tables where the uncompressed size is < the compressed size.
501 if (tables
[i
].uncompressed_length
< tables
[i
].length
) {
502 return OTS_FAILURE_MSG_TAG("invalid compressed table", &tables
[i
].tag
);
504 if (tables
[i
].uncompressed_length
> tables
[i
].length
) {
505 // We'll probably be decompressing this table.
507 // disallow all tables which uncompress to > 30 MB
508 if (tables
[i
].uncompressed_length
> 30 * 1024 * 1024) {
509 return OTS_FAILURE_MSG_TAG("uncompressed length exceeds 30MB", &tables
[i
].tag
);
511 if (uncompressed_sum
+ tables
[i
].uncompressed_length
< uncompressed_sum
) {
512 return OTS_FAILURE_MSG_TAG("overflow of uncompressed sum", &tables
[i
].tag
);
515 uncompressed_sum
+= tables
[i
].uncompressed_length
;
517 // since we required that the file be < 1GB in length, and that the table
518 // length is < 1GB, the following addtion doesn't overflow
519 uint32_t end_byte
= tables
[i
].offset
+ tables
[i
].length
;
520 // Tables in the WOFF file must be aligned 4-byte boundary.
521 if (signature
== Tag("wOFF")) {
522 end_byte
= ots::Round4(end_byte
);
524 if (!end_byte
|| end_byte
> length
) {
525 return OTS_FAILURE_MSG_TAG("table overruns end of file", &tables
[i
].tag
);
529 // All decompressed tables uncompressed must be <= 30MB.
530 if (uncompressed_sum
> 30 * 1024 * 1024) {
531 return OTS_FAILURE_MSG_HDR("uncompressed sum exceeds 30MB");
534 std::map
<uint32_t, OpenTypeTable
> table_map
;
535 for (unsigned i
= 0; i
< header
->num_tables
; ++i
) {
536 table_map
[tables
[i
].tag
] = tables
[i
];
539 // check that the tables are not overlapping.
540 std::vector
<std::pair
<uint32_t, uint8_t> > overlap_checker
;
541 for (unsigned i
= 0; i
< header
->num_tables
; ++i
) {
542 overlap_checker
.push_back(
543 std::make_pair(tables
[i
].offset
, static_cast<uint8_t>(1) /* start */));
544 overlap_checker
.push_back(
545 std::make_pair(tables
[i
].offset
+ tables
[i
].length
,
546 static_cast<uint8_t>(0) /* end */));
548 std::sort(overlap_checker
.begin(), overlap_checker
.end());
549 int overlap_count
= 0;
550 for (unsigned i
= 0; i
< overlap_checker
.size(); ++i
) {
551 overlap_count
+= (overlap_checker
[i
].second
? 1 : -1);
552 if (overlap_count
> 1) {
553 return OTS_FAILURE_MSG_HDR("overlapping tables");
559 for (unsigned i
= 0; ; ++i
) {
560 if (table_parsers
[i
].parse
== NULL
) break;
562 uint32_t tag
= Tag(table_parsers
[i
].tag
);
563 const std::map
<uint32_t, OpenTypeTable
>::const_iterator it
= table_map
.find(tag
);
565 ots::TableAction action
= GetTableAction(header
, tag
);
566 if (it
== table_map
.end()) {
567 if (table_parsers
[i
].required
&& action
== ots::TABLE_ACTION_SANITIZE
) {
568 return OTS_FAILURE_MSG_TAG("missing required table", table_parsers
[i
].tag
);
573 const uint8_t* table_data
;
576 if (!GetTableData(data
, it
->second
, &arena
, &table_length
, &table_data
)) {
577 return OTS_FAILURE_MSG_TAG("uncompress failed", table_parsers
[i
].tag
);
580 if (action
== ots::TABLE_ACTION_SANITIZE
&&
581 !table_parsers
[i
].parse(header
, table_data
, table_length
)) {
582 // TODO: parsers should generate specific messages detailing the failure;
583 // once those are all added, we won't need a generic failure message here
584 return OTS_FAILURE_MSG_TAG("failed to parse table", table_parsers
[i
].tag
);
589 // font with PostScript glyph
590 if (header
->version
!= Tag("OTTO")) {
591 return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
593 if (header
->glyf
|| header
->loca
) {
594 // mixing outline formats is not recommended
595 return OTS_FAILURE_MSG_HDR("font contains both PS and TT glyphs");
598 if (!header
->glyf
|| !header
->loca
) {
599 // No TrueType glyph found.
600 #define PASSTHRU_TABLE(TAG) (table_map.find(Tag(TAG)) != table_map.end() && \
601 GetTableAction(header, Tag(TAG)) == ots::TABLE_ACTION_PASSTHRU)
602 // We don't sanitise bitmap table, but don't reject bitmap-only fonts if
603 // we keep the tables.
604 if (!PASSTHRU_TABLE("CBDT") || !PASSTHRU_TABLE("CBLC")) {
605 return OTS_FAILURE_MSG_HDR("no supported glyph shapes table(s) present");
607 #undef PASSTHRU_TABLE
611 uint16_t num_output_tables
= 0;
612 for (unsigned i
= 0; ; ++i
) {
613 if (table_parsers
[i
].parse
== NULL
) {
617 if (table_parsers
[i
].should_serialise(header
)) {
622 for (std::map
<uint32_t, OpenTypeTable
>::const_iterator it
= table_map
.begin();
623 it
!= table_map
.end(); ++it
) {
624 ots::TableAction action
= GetTableAction(header
, it
->first
);
625 if (action
== ots::TABLE_ACTION_PASSTHRU
) {
630 uint16_t max_pow2
= 0;
631 while (1u << (max_pow2
+ 1) <= num_output_tables
) {
634 const uint16_t output_search_range
= (1u << max_pow2
) << 4;
636 // most of the errors here are highly unlikely - they'd only occur if the
637 // output stream returns a failure, e.g. lack of space to write
638 output
->ResetChecksum();
639 if (!output
->WriteTag(header
->version
) ||
640 !output
->WriteU16(num_output_tables
) ||
641 !output
->WriteU16(output_search_range
) ||
642 !output
->WriteU16(max_pow2
) ||
643 !output
->WriteU16((num_output_tables
<< 4) - output_search_range
)) {
644 return OTS_FAILURE_MSG_HDR("error writing output");
646 const uint32_t offset_table_chksum
= output
->chksum();
648 const size_t table_record_offset
= output
->Tell();
649 if (!output
->Pad(16 * num_output_tables
)) {
650 return OTS_FAILURE_MSG_HDR("error writing output");
653 std::vector
<OutputTable
> out_tables
;
655 size_t head_table_offset
= 0;
656 for (unsigned i
= 0; ; ++i
) {
657 if (table_parsers
[i
].parse
== NULL
) {
661 if (!table_parsers
[i
].should_serialise(header
)) {
666 uint32_t tag
= Tag(table_parsers
[i
].tag
);
668 out
.offset
= output
->Tell();
670 output
->ResetChecksum();
671 if (tag
== Tag("head")) {
672 head_table_offset
= out
.offset
;
674 if (!table_parsers
[i
].serialise(output
, header
)) {
675 return OTS_FAILURE_MSG_TAG("failed to serialize table", table_parsers
[i
].tag
);
678 const size_t end_offset
= output
->Tell();
679 if (end_offset
<= out
.offset
) {
680 // paranoid check. |end_offset| is supposed to be greater than the offset,
681 // as long as the Tell() interface is implemented correctly.
682 return OTS_FAILURE_MSG_HDR("error writing output");
684 out
.length
= end_offset
- out
.offset
;
686 // align tables to four bytes
687 if (!output
->Pad((4 - (end_offset
& 3)) % 4)) {
688 return OTS_FAILURE_MSG_HDR("error writing output");
690 out
.chksum
= output
->chksum();
691 out_tables
.push_back(out
);
694 for (std::map
<uint32_t, OpenTypeTable
>::const_iterator it
= table_map
.begin();
695 it
!= table_map
.end(); ++it
) {
696 ots::TableAction action
= GetTableAction(header
, it
->first
);
697 if (action
== ots::TABLE_ACTION_PASSTHRU
) {
699 out
.tag
= it
->second
.tag
;
700 out
.offset
= output
->Tell();
702 output
->ResetChecksum();
703 if (it
->second
.tag
== Tag("head")) {
704 head_table_offset
= out
.offset
;
707 const uint8_t* table_data
;
710 if (!GetTableData(data
, it
->second
, &arena
, &table_length
, &table_data
)) {
711 return OTS_FAILURE_MSG_HDR("Failed to uncompress table");
714 if (!output
->Write(table_data
, table_length
)) {
715 return OTS_FAILURE_MSG_HDR("Failed to serialize table");
718 const size_t end_offset
= output
->Tell();
719 if (end_offset
<= out
.offset
) {
720 // paranoid check. |end_offset| is supposed to be greater than the offset,
721 // as long as the Tell() interface is implemented correctly.
722 return OTS_FAILURE_MSG_HDR("error writing output");
724 out
.length
= end_offset
- out
.offset
;
726 // align tables to four bytes
727 if (!output
->Pad((4 - (end_offset
& 3)) % 4)) {
728 return OTS_FAILURE_MSG_HDR("error writing output");
730 out
.chksum
= output
->chksum();
731 out_tables
.push_back(out
);
735 const size_t end_of_file
= output
->Tell();
737 // Need to sort the output tables for inclusion in the file
738 std::sort(out_tables
.begin(), out_tables
.end());
739 if (!output
->Seek(table_record_offset
)) {
740 return OTS_FAILURE_MSG_HDR("error writing output");
743 output
->ResetChecksum();
744 uint32_t tables_chksum
= 0;
745 for (unsigned i
= 0; i
< out_tables
.size(); ++i
) {
746 if (!output
->WriteTag(out_tables
[i
].tag
) ||
747 !output
->WriteU32(out_tables
[i
].chksum
) ||
748 !output
->WriteU32(out_tables
[i
].offset
) ||
749 !output
->WriteU32(out_tables
[i
].length
)) {
750 return OTS_FAILURE_MSG_HDR("error writing output");
752 tables_chksum
+= out_tables
[i
].chksum
;
754 const uint32_t table_record_chksum
= output
->chksum();
756 // http://www.microsoft.com/typography/otspec/otff.htm
757 const uint32_t file_chksum
758 = offset_table_chksum
+ tables_chksum
+ table_record_chksum
;
759 const uint32_t chksum_magic
= static_cast<uint32_t>(0xb1b0afba) - file_chksum
;
761 // seek into the 'head' table and write in the checksum magic value
762 if (!head_table_offset
) {
763 return OTS_FAILURE_MSG_HDR("internal error!");
765 if (!output
->Seek(head_table_offset
+ 8)) {
766 return OTS_FAILURE_MSG_HDR("error writing output");
768 if (!output
->WriteU32(chksum_magic
)) {
769 return OTS_FAILURE_MSG_HDR("error writing output");
772 if (!output
->Seek(end_of_file
)) {
773 return OTS_FAILURE_MSG_HDR("error writing output");
783 bool IsValidVersionTag(uint32_t tag
) {
784 return tag
== Tag("\x00\x01\x00\x00") ||
785 // OpenType fonts with CFF data have 'OTTO' tag.
786 tag
== Tag("OTTO") ||
787 // Older Mac fonts might have 'true' or 'typ1' tag.
788 tag
== Tag("true") ||
792 bool OTSContext::Process(OTSStream
*output
,
797 header
.context
= this;
800 return OTS_FAILURE_MSG_(&header
, "file less than 4 bytes");
804 if (data
[0] == 'w' && data
[1] == 'O' && data
[2] == 'F' && data
[3] == 'F') {
805 result
= ProcessWOFF(&header
, output
, data
, length
);
806 } else if (data
[0] == 'w' && data
[1] == 'O' && data
[2] == 'F' && data
[3] == '2') {
807 result
= ProcessWOFF2(&header
, output
, data
, length
);
809 result
= ProcessTTF(&header
, output
, data
, length
);
812 for (unsigned i
= 0; ; ++i
) {
813 if (table_parsers
[i
].parse
== NULL
) break;
814 table_parsers
[i
].free(&header
);