1/*
2 * Copyright © 2007,2008,2009 Red Hat, Inc.
3 * Copyright © 2012 Google, Inc.
4 *
5 * This is part of HarfBuzz, a text shaping library.
6 *
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
12 *
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 * DAMAGE.
18 *
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 *
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
27 */
28
29#ifndef HB_OPEN_FILE_HH
30#define HB_OPEN_FILE_HH
31
32#include "hb-open-type.hh"
33#include "hb-ot-head-table.hh"
34
35
36namespace OT {
37
38
39/*
40 *
41 * The OpenType Font File
42 *
43 */
44
45
46/*
47 * Organization of an OpenType Font
48 */
49
50struct OpenTypeFontFile;
51struct OffsetTable;
52struct TTCHeader;
53
54
55typedef struct TableRecord
56{
57 int cmp (Tag t) const
58 { return -t.cmp (tag); }
59
60 static int cmp (const void *pa, const void *pb)
61 {
62 const TableRecord *a = (const TableRecord *) pa;
63 const TableRecord *b = (const TableRecord *) pb;
64 return b->cmp (a->tag);
65 }
66
67 inline bool sanitize (hb_sanitize_context_t *c) const
68 {
69 TRACE_SANITIZE (this);
70 return_trace (c->check_struct (this));
71 }
72
73 Tag tag; /* 4-byte identifier. */
74 CheckSum checkSum; /* CheckSum for this table. */
75 Offset32 offset; /* Offset from beginning of TrueType font
76 * file. */
77 HBUINT32 length; /* Length of this table. */
78 public:
79 DEFINE_SIZE_STATIC (16);
80} OpenTypeTable;
81
82typedef struct OffsetTable
83{
84 friend struct OpenTypeFontFile;
85
86 inline unsigned int get_table_count (void) const
87 { return tables.len; }
88 inline const TableRecord& get_table (unsigned int i) const
89 {
90 return tables[i];
91 }
92 inline unsigned int get_table_tags (unsigned int start_offset,
93 unsigned int *table_count, /* IN/OUT */
94 hb_tag_t *table_tags /* OUT */) const
95 {
96 if (table_count)
97 {
98 if (start_offset >= tables.len)
99 *table_count = 0;
100 else
101 *table_count = MIN<unsigned int> (*table_count, tables.len - start_offset);
102
103 const TableRecord *sub_tables = tables.arrayZ + start_offset;
104 unsigned int count = *table_count;
105 for (unsigned int i = 0; i < count; i++)
106 table_tags[i] = sub_tables[i].tag;
107 }
108 return tables.len;
109 }
110 inline bool find_table_index (hb_tag_t tag, unsigned int *table_index) const
111 {
112 Tag t;
113 t.set (tag);
114 /* Linear-search for small tables to work around fonts with unsorted
115 * table list. */
116 int i = tables.len < 64 ? tables.lsearch (t) : tables.bsearch (t);
117 if (table_index)
118 *table_index = i == -1 ? (unsigned) Index::NOT_FOUND_INDEX : (unsigned) i;
119 return i != -1;
120 }
121 inline const TableRecord& get_table_by_tag (hb_tag_t tag) const
122 {
123 unsigned int table_index;
124 find_table_index (tag, &table_index);
125 return get_table (table_index);
126 }
127
128 public:
129
130 inline bool serialize (hb_serialize_context_t *c,
131 hb_tag_t sfnt_tag,
132 Supplier<hb_tag_t> &tags,
133 Supplier<hb_blob_t *> &blobs,
134 unsigned int table_count)
135 {
136 TRACE_SERIALIZE (this);
137 /* Alloc 12 for the OTHeader. */
138 if (unlikely (!c->extend_min (*this))) return_trace (false);
139 /* Write sfntVersion (bytes 0..3). */
140 sfnt_version.set (sfnt_tag);
141 /* Take space for numTables, searchRange, entrySelector, RangeShift
142 * and the TableRecords themselves. */
143 if (unlikely (!tables.serialize (c, table_count))) return_trace (false);
144
145 const char *dir_end = (const char *) c->head;
146 HBUINT32 *checksum_adjustment = nullptr;
147
148 /* Write OffsetTables, alloc for and write actual table blobs. */
149 for (unsigned int i = 0; i < table_count; i++)
150 {
151 TableRecord &rec = tables.arrayZ[i];
152 hb_blob_t *blob = blobs[i];
153 rec.tag.set (tags[i]);
154 rec.length.set (hb_blob_get_length (blob));
155 rec.offset.serialize (c, this);
156
157 /* Allocate room for the table and copy it. */
158 char *start = (char *) c->allocate_size<void> (rec.length);
159 if (unlikely (!start)) {return false;}
160
161 memcpy (start, hb_blob_get_data (blob, nullptr), rec.length);
162
163 /* 4-byte allignment. */
164 c->align (4);
165 const char *end = (const char *) c->head;
166
167 if (tags[i] == HB_OT_TAG_head && end - start >= head::static_size)
168 {
169 head *h = (head *) start;
170 checksum_adjustment = &h->checkSumAdjustment;
171 checksum_adjustment->set (0);
172 }
173
174 rec.checkSum.set_for_data (start, end - start);
175 }
176 tags += table_count;
177 blobs += table_count;
178
179 tables.qsort ();
180
181 if (checksum_adjustment)
182 {
183 CheckSum checksum;
184
185 /* The following line is a slower version of the following block. */
186 //checksum.set_for_data (this, (const char *) c->head - (const char *) this);
187 checksum.set_for_data (this, dir_end - (const char *) this);
188 for (unsigned int i = 0; i < table_count; i++)
189 {
190 TableRecord &rec = tables.arrayZ[i];
191 checksum.set (checksum + rec.checkSum);
192 }
193
194 checksum_adjustment->set (0xB1B0AFBAu - checksum);
195 }
196
197 return_trace (true);
198 }
199
200 inline bool sanitize (hb_sanitize_context_t *c) const
201 {
202 TRACE_SANITIZE (this);
203 return_trace (c->check_struct (this) && tables.sanitize (c));
204 }
205
206 protected:
207 Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */
208 BinSearchArrayOf<TableRecord>
209 tables;
210 public:
211 DEFINE_SIZE_ARRAY (12, tables);
212} OpenTypeFontFace;
213
214
215/*
216 * TrueType Collections
217 */
218
219struct TTCHeaderVersion1
220{
221 friend struct TTCHeader;
222
223 inline unsigned int get_face_count (void) const { return table.len; }
224 inline const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; }
225
226 inline bool sanitize (hb_sanitize_context_t *c) const
227 {
228 TRACE_SANITIZE (this);
229 return_trace (table.sanitize (c, this));
230 }
231
232 protected:
233 Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */
234 FixedVersion<>version; /* Version of the TTC Header (1.0),
235 * 0x00010000u */
236 LArrayOf<LOffsetTo<OffsetTable> >
237 table; /* Array of offsets to the OffsetTable for each font
238 * from the beginning of the file */
239 public:
240 DEFINE_SIZE_ARRAY (12, table);
241};
242
243struct TTCHeader
244{
245 friend struct OpenTypeFontFile;
246
247 private:
248
249 inline unsigned int get_face_count (void) const
250 {
251 switch (u.header.version.major) {
252 case 2: /* version 2 is compatible with version 1 */
253 case 1: return u.version1.get_face_count ();
254 default:return 0;
255 }
256 }
257 inline const OpenTypeFontFace& get_face (unsigned int i) const
258 {
259 switch (u.header.version.major) {
260 case 2: /* version 2 is compatible with version 1 */
261 case 1: return u.version1.get_face (i);
262 default:return Null(OpenTypeFontFace);
263 }
264 }
265
266 inline bool sanitize (hb_sanitize_context_t *c) const
267 {
268 TRACE_SANITIZE (this);
269 if (unlikely (!u.header.version.sanitize (c))) return_trace (false);
270 switch (u.header.version.major) {
271 case 2: /* version 2 is compatible with version 1 */
272 case 1: return_trace (u.version1.sanitize (c));
273 default:return_trace (true);
274 }
275 }
276
277 protected:
278 union {
279 struct {
280 Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */
281 FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0),
282 * 0x00010000u or 0x00020000u */
283 } header;
284 TTCHeaderVersion1 version1;
285 } u;
286};
287
288/*
289 * Mac Resource Fork
290 */
291
292struct ResourceRefItem
293{
294 inline bool sanitize (hb_sanitize_context_t *c) const
295 {
296 TRACE_SANITIZE (this);
297 // actual data sanitization is done on ResourceForkHeader sanitizer
298 return_trace (likely (c->check_struct (this)));
299 }
300
301 HBINT16 id; /* Resource ID, is really should be signed? */
302 HBINT16 nameOffset; /* Offset from beginning of resource name list
303 * to resource name, minus means there is no */
304 HBUINT8 attr; /* Resource attributes */
305 HBUINT24 dataOffset; /* Offset from beginning of resource data to
306 * data for this resource */
307 HBUINT32 reserved; /* Reserved for handle to resource */
308 public:
309 DEFINE_SIZE_STATIC (12);
310};
311
312struct ResourceTypeItem
313{
314 inline bool sanitize (hb_sanitize_context_t *c) const
315 {
316 TRACE_SANITIZE (this);
317 // RefList sanitization is done on ResourceMap sanitizer
318 return_trace (likely (c->check_struct (this)));
319 }
320
321 inline unsigned int get_resource_count () const
322 {
323 return numRes + 1;
324 }
325
326 inline bool is_sfnt () const
327 {
328 return type == HB_TAG ('s','f','n','t');
329 }
330
331 inline const ResourceRefItem& get_ref_item (const void *base,
332 unsigned int i) const
333 {
334 return (base+refList)[i];
335 }
336
337 protected:
338 Tag type; /* Resource type */
339 HBUINT16 numRes; /* Number of resource this type in map minus 1 */
340 OffsetTo<UnsizedArrayOf<ResourceRefItem> >
341 refList; /* Offset from beginning of resource type list
342 * to reference list for this type */
343 public:
344 DEFINE_SIZE_STATIC (8);
345};
346
347struct ResourceMap
348{
349 inline bool sanitize (hb_sanitize_context_t *c) const
350 {
351 TRACE_SANITIZE (this);
352 if (unlikely (!c->check_struct (this)))
353 return_trace (false);
354 for (unsigned int i = 0; i < get_types_count (); ++i)
355 {
356 const ResourceTypeItem& type = get_type (i);
357 if (unlikely (!type.sanitize (c)))
358 return_trace (false);
359 for (unsigned int j = 0; j < type.get_resource_count (); ++j)
360 if (unlikely (!get_ref_item (type, j).sanitize (c)))
361 return_trace (false);
362 }
363 return_trace (true);
364 }
365
366 inline const ResourceTypeItem& get_type (unsigned int i) const
367 {
368 // Why offset from the second byte of the object? I'm not sure
369 return ((&reserved[2])+typeList)[i];
370 }
371
372 inline unsigned int get_types_count () const
373 {
374 return nTypes + 1;
375 }
376
377 inline const ResourceRefItem &get_ref_item (const ResourceTypeItem &type,
378 unsigned int i) const
379 {
380 return type.get_ref_item (&(this+typeList), i);
381 }
382
383 inline const PString& get_name (const ResourceRefItem &item,
384 unsigned int i) const
385 {
386 if (item.nameOffset == -1)
387 return Null (PString);
388
389 return StructAtOffset<PString> (this, nameList + item.nameOffset);
390 }
391
392 protected:
393 HBUINT8 reserved[16]; /* Reserved for copy of resource header */
394 LOffsetTo<ResourceMap>
395 reserved1; /* Reserved for handle to next resource map */
396 HBUINT16 reserved2; /* Reserved for file reference number */
397 HBUINT16 attr; /* Resource fork attribute */
398 OffsetTo<UnsizedArrayOf<ResourceTypeItem> >
399 typeList; /* Offset from beginning of map to
400 * resource type list */
401 HBUINT16 nameList; /* Offset from beginning of map to
402 * resource name list */
403 HBUINT16 nTypes; /* Number of types in the map minus 1 */
404 public:
405 DEFINE_SIZE_STATIC (30);
406};
407
408struct ResourceForkHeader
409{
410 inline unsigned int get_face_count () const
411 {
412 const ResourceMap &resource_map = this+map;
413 for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
414 {
415 const ResourceTypeItem& type = resource_map.get_type (i);
416 if (type.is_sfnt ())
417 return type.get_resource_count ();
418 }
419 return 0;
420 }
421
422 inline const LArrayOf<HBUINT8>& get_data (const ResourceTypeItem& type,
423 unsigned int idx) const
424 {
425 const ResourceMap &resource_map = this+map;
426 unsigned int offset = dataOffset;
427 offset += resource_map.get_ref_item (type, idx).dataOffset;
428 return StructAtOffset<LArrayOf<HBUINT8> > (this, offset);
429 }
430
431 inline const OpenTypeFontFace& get_face (unsigned int idx) const
432 {
433 const ResourceMap &resource_map = this+map;
434 for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
435 {
436 const ResourceTypeItem& type = resource_map.get_type (i);
437 if (type.is_sfnt () && idx < type.get_resource_count ())
438 return (OpenTypeFontFace&) get_data (type, idx).arrayZ;
439 }
440 return Null (OpenTypeFontFace);
441 }
442
443 inline bool sanitize (hb_sanitize_context_t *c) const
444 {
445 TRACE_SANITIZE (this);
446 if (unlikely (!c->check_struct (this)))
447 return_trace (false);
448
449 const ResourceMap &resource_map = this+map;
450 if (unlikely (!resource_map.sanitize (c)))
451 return_trace (false);
452
453 for (unsigned int i = 0; i < resource_map.get_types_count (); ++i)
454 {
455 const ResourceTypeItem& type = resource_map.get_type (i);
456 for (unsigned int j = 0; j < type.get_resource_count (); ++j)
457 {
458 const LArrayOf<HBUINT8>& data = get_data (type, j);
459 if (unlikely (!(data.sanitize (c) &&
460 ((OpenTypeFontFace&) data.arrayZ).sanitize (c))))
461 return_trace (false);
462 }
463 }
464
465 return_trace (true);
466 }
467
468 protected:
469 HBUINT32 dataOffset; /* Offset from beginning of resource fork
470 * to resource data */
471 LOffsetTo<ResourceMap>
472 map; /* Offset from beginning of resource fork
473 * to resource map */
474 HBUINT32 dataLen; /* Length of resource data */
475 HBUINT32 mapLen; /* Length of resource map */
476 public:
477 DEFINE_SIZE_STATIC (16);
478};
479
480/*
481 * OpenType Font File
482 */
483
484struct OpenTypeFontFile
485{
486 enum {
487 CFFTag = HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */
488 TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */
489 TTCTag = HB_TAG ('t','t','c','f'), /* TrueType Collection */
490 DFontTag = HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */
491 TrueTag = HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */
492 Typ1Tag = HB_TAG ('t','y','p','1') /* Obsolete Apple Type1 font in SFNT container */
493 };
494
495 inline hb_tag_t get_tag (void) const { return u.tag; }
496
497 inline unsigned int get_face_count (void) const
498 {
499 switch (u.tag) {
500 case CFFTag: /* All the non-collection tags */
501 case TrueTag:
502 case Typ1Tag:
503 case TrueTypeTag: return 1;
504 case TTCTag: return u.ttcHeader.get_face_count ();
505// case DFontTag: return u.rfHeader.get_face_count ();
506 default: return 0;
507 }
508 }
509 inline const OpenTypeFontFace& get_face (unsigned int i) const
510 {
511 switch (u.tag) {
512 /* Note: for non-collection SFNT data we ignore index. This is because
513 * Apple dfont container is a container of SFNT's. So each SFNT is a
514 * non-TTC, but the index is more than zero. */
515 case CFFTag: /* All the non-collection tags */
516 case TrueTag:
517 case Typ1Tag:
518 case TrueTypeTag: return u.fontFace;
519 case TTCTag: return u.ttcHeader.get_face (i);
520// case DFontTag: return u.rfHeader.get_face (i);
521 default: return Null(OpenTypeFontFace);
522 }
523 }
524
525 inline bool serialize_single (hb_serialize_context_t *c,
526 hb_tag_t sfnt_tag,
527 Supplier<hb_tag_t> &tags,
528 Supplier<hb_blob_t *> &blobs,
529 unsigned int table_count)
530 {
531 TRACE_SERIALIZE (this);
532 assert (sfnt_tag != TTCTag);
533 if (unlikely (!c->extend_min (*this))) return_trace (false);
534 return_trace (u.fontFace.serialize (c, sfnt_tag, tags, blobs, table_count));
535 }
536
537 inline bool sanitize (hb_sanitize_context_t *c) const
538 {
539 TRACE_SANITIZE (this);
540 if (unlikely (!u.tag.sanitize (c))) return_trace (false);
541 switch (u.tag) {
542 case CFFTag: /* All the non-collection tags */
543 case TrueTag:
544 case Typ1Tag:
545 case TrueTypeTag: return_trace (u.fontFace.sanitize (c));
546 case TTCTag: return_trace (u.ttcHeader.sanitize (c));
547// case DFontTag: return_trace (u.rfHeader.sanitize (c));
548 default: return_trace (true);
549 }
550 }
551
552 protected:
553 union {
554 Tag tag; /* 4-byte identifier. */
555 OpenTypeFontFace fontFace;
556 TTCHeader ttcHeader;
557 ResourceForkHeader rfHeader;
558 } u;
559 public:
560 DEFINE_SIZE_UNION (4, tag);
561};
562
563
564} /* namespace OT */
565
566
567#endif /* HB_OPEN_FILE_HH */
568