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 
36 namespace OT {
37 
38 
39 /*
40  *
41  * The OpenType Font File
42  *
43  */
44 
45 
46 /*
47  * Organization of an OpenType Font
48  */
49 
50 struct OpenTypeFontFile;
51 struct OpenTypeOffsetTable;
52 struct TTCHeader;
53 
54 
55 typedef struct TableRecord
56 {
cmpOT::TableRecord57   int cmp (Tag t) const { return -t.cmp (tag); }
58 
cmpOT::TableRecord59   HB_INTERNAL static int cmp (const void *pa, const void *pb)
60   {
61     const TableRecord *a = (const TableRecord *) pa;
62     const TableRecord *b = (const TableRecord *) pb;
63     return b->cmp (a->tag);
64   }
65 
sanitizeOT::TableRecord66   bool sanitize (hb_sanitize_context_t *c) const
67   {
68     TRACE_SANITIZE (this);
69     return_trace (c->check_struct (this));
70   }
71 
72   Tag		tag;		/* 4-byte identifier. */
73   CheckSum	checkSum;	/* CheckSum for this table. */
74   Offset32	offset;		/* Offset from beginning of TrueType font
75 				 * file. */
76   HBUINT32	length;		/* Length of this table. */
77   public:
78   DEFINE_SIZE_STATIC (16);
79 } OpenTypeTable;
80 
81 typedef struct OpenTypeOffsetTable
82 {
83   friend struct OpenTypeFontFile;
84 
get_table_countOT::OpenTypeOffsetTable85   unsigned int get_table_count () const { return tables.len; }
get_tableOT::OpenTypeOffsetTable86   const TableRecord& get_table (unsigned int i) const
87   { return tables[i]; }
get_table_tagsOT::OpenTypeOffsetTable88   unsigned int get_table_tags (unsigned int  start_offset,
89 			       unsigned int *table_count, /* IN/OUT */
90 			       hb_tag_t     *table_tags /* OUT */) const
91   {
92     if (table_count)
93     {
94       + tables.sub_array (start_offset, table_count)
95       | hb_map (&TableRecord::tag)
96       | hb_sink (hb_array (table_tags, *table_count))
97       ;
98     }
99     return tables.len;
100   }
find_table_indexOT::OpenTypeOffsetTable101   bool find_table_index (hb_tag_t tag, unsigned int *table_index) const
102   {
103     Tag t;
104     t = tag;
105     return tables.bfind (t, table_index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
106   }
get_table_by_tagOT::OpenTypeOffsetTable107   const TableRecord& get_table_by_tag (hb_tag_t tag) const
108   {
109     unsigned int table_index;
110     find_table_index (tag, &table_index);
111     return get_table (table_index);
112   }
113 
114   public:
115 
116   template <typename item_t>
serializeOT::OpenTypeOffsetTable117   bool serialize (hb_serialize_context_t *c,
118 		  hb_tag_t sfnt_tag,
119 		  hb_array_t<item_t> items)
120   {
121     TRACE_SERIALIZE (this);
122     /* Alloc 12 for the OTHeader. */
123     if (unlikely (!c->extend_min (*this))) return_trace (false);
124     /* Write sfntVersion (bytes 0..3). */
125     sfnt_version = sfnt_tag;
126     /* Take space for numTables, searchRange, entrySelector, RangeShift
127      * and the TableRecords themselves.  */
128     if (unlikely (!tables.serialize (c, items.length))) return_trace (false);
129 
130     const char *dir_end = (const char *) c->head;
131     HBUINT32 *checksum_adjustment = nullptr;
132 
133     /* Write OffsetTables, alloc for and write actual table blobs. */
134     for (unsigned int i = 0; i < tables.len; i++)
135     {
136       TableRecord &rec = tables.arrayZ[i];
137       hb_blob_t *blob = items[i].blob;
138       rec.tag = items[i].tag;
139       rec.length = blob->length;
140       rec.offset.serialize (c, this);
141 
142       /* Allocate room for the table and copy it. */
143       char *start = (char *) c->allocate_size<void> (rec.length);
144       if (unlikely (!start)) return false;
145 
146       if (likely (rec.length))
147 	memcpy (start, blob->data, rec.length);
148 
149       /* 4-byte alignment. */
150       c->align (4);
151       const char *end = (const char *) c->head;
152 
153       if (items[i].tag == HB_OT_TAG_head &&
154 	  (unsigned) (end - start) >= head::static_size)
155       {
156 	head *h = (head *) start;
157 	checksum_adjustment = &h->checkSumAdjustment;
158 	*checksum_adjustment = 0;
159       }
160 
161       rec.checkSum.set_for_data (start, end - start);
162     }
163 
164     tables.qsort ();
165 
166     if (checksum_adjustment)
167     {
168       CheckSum checksum;
169 
170       /* The following line is a slower version of the following block. */
171       //checksum.set_for_data (this, (const char *) c->head - (const char *) this);
172       checksum.set_for_data (this, dir_end - (const char *) this);
173       for (unsigned int i = 0; i < items.length; i++)
174       {
175 	TableRecord &rec = tables.arrayZ[i];
176 	checksum = checksum + rec.checkSum;
177       }
178 
179       *checksum_adjustment = 0xB1B0AFBAu - checksum;
180     }
181 
182     return_trace (true);
183   }
184 
sanitizeOT::OpenTypeOffsetTable185   bool sanitize (hb_sanitize_context_t *c) const
186   {
187     TRACE_SANITIZE (this);
188     return_trace (c->check_struct (this) && tables.sanitize (c));
189   }
190 
191   protected:
192   Tag		sfnt_version;	/* '\0\001\0\00' if TrueType / 'OTTO' if CFF */
193   BinSearchArrayOf<TableRecord>
194 		tables;
195   public:
196   DEFINE_SIZE_ARRAY (12, tables);
197 } OpenTypeFontFace;
198 
199 
200 /*
201  * TrueType Collections
202  */
203 
204 struct TTCHeaderVersion1
205 {
206   friend struct TTCHeader;
207 
get_face_countOT::TTCHeaderVersion1208   unsigned int get_face_count () const { return table.len; }
get_faceOT::TTCHeaderVersion1209   const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; }
210 
sanitizeOT::TTCHeaderVersion1211   bool sanitize (hb_sanitize_context_t *c) const
212   {
213     TRACE_SANITIZE (this);
214     return_trace (table.sanitize (c, this));
215   }
216 
217   protected:
218   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
219   FixedVersion<>version;	/* Version of the TTC Header (1.0),
220 				 * 0x00010000u */
221   Array32Of<Offset32To<OpenTypeOffsetTable>>
222 		table;		/* Array of offsets to the OffsetTable for each font
223 				 * from the beginning of the file */
224   public:
225   DEFINE_SIZE_ARRAY (12, table);
226 };
227 
228 struct TTCHeader
229 {
230   friend struct OpenTypeFontFile;
231 
232   private:
233 
get_face_countOT::TTCHeader234   unsigned int get_face_count () const
235   {
236     switch (u.header.version.major) {
237     case 2: /* version 2 is compatible with version 1 */
238     case 1: return u.version1.get_face_count ();
239     default:return 0;
240     }
241   }
get_faceOT::TTCHeader242   const OpenTypeFontFace& get_face (unsigned int i) const
243   {
244     switch (u.header.version.major) {
245     case 2: /* version 2 is compatible with version 1 */
246     case 1: return u.version1.get_face (i);
247     default:return Null (OpenTypeFontFace);
248     }
249   }
250 
sanitizeOT::TTCHeader251   bool sanitize (hb_sanitize_context_t *c) const
252   {
253     TRACE_SANITIZE (this);
254     if (unlikely (!u.header.version.sanitize (c))) return_trace (false);
255     switch (u.header.version.major) {
256     case 2: /* version 2 is compatible with version 1 */
257     case 1: return_trace (u.version1.sanitize (c));
258     default:return_trace (true);
259     }
260   }
261 
262   protected:
263   union {
264   struct {
265   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
266   FixedVersion<>version;	/* Version of the TTC Header (1.0 or 2.0),
267 				 * 0x00010000u or 0x00020000u */
268   }			header;
269   TTCHeaderVersion1	version1;
270   } u;
271 };
272 
273 /*
274  * Mac Resource Fork
275  *
276  * http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html
277  */
278 
279 struct ResourceRecord
280 {
get_faceOT::ResourceRecord281   const OpenTypeFontFace & get_face (const void *data_base) const
282   { return * reinterpret_cast<const OpenTypeFontFace *> ((data_base+offset).arrayZ); }
283 
sanitizeOT::ResourceRecord284   bool sanitize (hb_sanitize_context_t *c,
285 		 const void *data_base) const
286   {
287     TRACE_SANITIZE (this);
288     return_trace (c->check_struct (this) &&
289 		  offset.sanitize (c, data_base) &&
290 		  get_face (data_base).sanitize (c));
291   }
292 
293   protected:
294   HBUINT16	id;		/* Resource ID. */
295   HBINT16	nameOffset;	/* Offset from beginning of resource name list
296 				 * to resource name, -1 means there is none. */
297   HBUINT8	attrs;		/* Resource attributes */
298   NNOffset24To<Array32Of<HBUINT8>>
299 		offset;		/* Offset from beginning of data block to
300 				 * data for this resource */
301   HBUINT32	reserved;	/* Reserved for handle to resource */
302   public:
303   DEFINE_SIZE_STATIC (12);
304 };
305 
306 #define HB_TAG_sfnt HB_TAG ('s','f','n','t')
307 
308 struct ResourceTypeRecord
309 {
get_resource_countOT::ResourceTypeRecord310   unsigned int get_resource_count () const
311   { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; }
312 
is_sfntOT::ResourceTypeRecord313   bool is_sfnt () const { return tag == HB_TAG_sfnt; }
314 
get_resource_recordOT::ResourceTypeRecord315   const ResourceRecord& get_resource_record (unsigned int i,
316 					     const void *type_base) const
317   { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; }
318 
sanitizeOT::ResourceTypeRecord319   bool sanitize (hb_sanitize_context_t *c,
320 		 const void *type_base,
321 		 const void *data_base) const
322   {
323     TRACE_SANITIZE (this);
324     return_trace (c->check_struct (this) &&
325 		  resourcesZ.sanitize (c, type_base,
326 				       get_resource_count (),
327 				       data_base));
328   }
329 
330   protected:
331   Tag		tag;		/* Resource type. */
332   HBUINT16	resCountM1;	/* Number of resources minus 1. */
333   NNOffset16To<UnsizedArrayOf<ResourceRecord>>
334 		resourcesZ;	/* Offset from beginning of resource type list
335 				 * to reference item list for this type. */
336   public:
337   DEFINE_SIZE_STATIC (8);
338 };
339 
340 struct ResourceMap
341 {
get_face_countOT::ResourceMap342   unsigned int get_face_count () const
343   {
344     unsigned int count = get_type_count ();
345     for (unsigned int i = 0; i < count; i++)
346     {
347       const ResourceTypeRecord& type = get_type_record (i);
348       if (type.is_sfnt ())
349 	return type.get_resource_count ();
350     }
351     return 0;
352   }
353 
get_faceOT::ResourceMap354   const OpenTypeFontFace& get_face (unsigned int idx,
355 				    const void *data_base) const
356   {
357     unsigned int count = get_type_count ();
358     for (unsigned int i = 0; i < count; i++)
359     {
360       const ResourceTypeRecord& type = get_type_record (i);
361       /* The check for idx < count is here because ResourceRecord is NOT null-safe.
362        * Because an offset of 0 there does NOT mean null. */
363       if (type.is_sfnt () && idx < type.get_resource_count ())
364 	return type.get_resource_record (idx, &(this+typeList)).get_face (data_base);
365     }
366     return Null (OpenTypeFontFace);
367   }
368 
sanitizeOT::ResourceMap369   bool sanitize (hb_sanitize_context_t *c, const void *data_base) const
370   {
371     TRACE_SANITIZE (this);
372     return_trace (c->check_struct (this) &&
373 		  typeList.sanitize (c, this,
374 				     &(this+typeList),
375 				     data_base));
376   }
377 
378   private:
get_type_countOT::ResourceMap379   unsigned int get_type_count () const { return (this+typeList).lenM1 + 1; }
380 
get_type_recordOT::ResourceMap381   const ResourceTypeRecord& get_type_record (unsigned int i) const
382   { return (this+typeList)[i]; }
383 
384   protected:
385   HBUINT8	reserved0[16];	/* Reserved for copy of resource header */
386   HBUINT32	reserved1;	/* Reserved for handle to next resource map */
387   HBUINT16	resreved2;	/* Reserved for file reference number */
388   HBUINT16	attrs;		/* Resource fork attribute */
389   NNOffset16To<ArrayOfM1<ResourceTypeRecord>>
390 		typeList;	/* Offset from beginning of map to
391 				 * resource type list */
392   Offset16	nameList;	/* Offset from beginning of map to
393 				 * resource name list */
394   public:
395   DEFINE_SIZE_STATIC (28);
396 };
397 
398 struct ResourceForkHeader
399 {
get_face_countOT::ResourceForkHeader400   unsigned int get_face_count () const
401   { return (this+map).get_face_count (); }
402 
get_faceOT::ResourceForkHeader403   const OpenTypeFontFace& get_face (unsigned int idx,
404 				    unsigned int *base_offset = nullptr) const
405   {
406     const OpenTypeFontFace &face = (this+map).get_face (idx, &(this+data));
407     if (base_offset)
408       *base_offset = (const char *) &face - (const char *) this;
409     return face;
410   }
411 
sanitizeOT::ResourceForkHeader412   bool sanitize (hb_sanitize_context_t *c) const
413   {
414     TRACE_SANITIZE (this);
415     return_trace (c->check_struct (this) &&
416 		  data.sanitize (c, this, dataLen) &&
417 		  map.sanitize (c, this, &(this+data)));
418   }
419 
420   protected:
421   NNOffset32To<UnsizedArrayOf<HBUINT8>>
422 		data;		/* Offset from beginning of resource fork
423 				 * to resource data */
424   NNOffset32To<ResourceMap >
425 		map;		/* Offset from beginning of resource fork
426 				 * to resource map */
427   HBUINT32	dataLen;	/* Length of resource data */
428   HBUINT32	mapLen;		/* Length of resource map */
429   public:
430   DEFINE_SIZE_STATIC (16);
431 };
432 
433 /*
434  * OpenType Font File
435  */
436 
437 struct OpenTypeFontFile
438 {
439   enum {
440     CFFTag		= HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */
441     TrueTypeTag		= HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */
442     TTCTag		= HB_TAG ('t','t','c','f'), /* TrueType Collection */
443     DFontTag		= HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */
444     TrueTag		= HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */
445     Typ1Tag		= HB_TAG ('t','y','p','1')  /* Obsolete Apple Type1 font in SFNT container */
446   };
447 
get_tagOT::OpenTypeFontFile448   hb_tag_t get_tag () const { return u.tag; }
449 
get_face_countOT::OpenTypeFontFile450   unsigned int get_face_count () const
451   {
452     switch (u.tag) {
453     case CFFTag:	/* All the non-collection tags */
454     case TrueTag:
455     case Typ1Tag:
456     case TrueTypeTag:	return 1;
457     case TTCTag:	return u.ttcHeader.get_face_count ();
458     case DFontTag:	return u.rfHeader.get_face_count ();
459     default:		return 0;
460     }
461   }
get_faceOT::OpenTypeFontFile462   const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const
463   {
464     if (base_offset)
465       *base_offset = 0;
466     switch (u.tag) {
467     /* Note: for non-collection SFNT data we ignore index.  This is because
468      * Apple dfont container is a container of SFNT's.  So each SFNT is a
469      * non-TTC, but the index is more than zero. */
470     case CFFTag:	/* All the non-collection tags */
471     case TrueTag:
472     case Typ1Tag:
473     case TrueTypeTag:	return u.fontFace;
474     case TTCTag:	return u.ttcHeader.get_face (i);
475     case DFontTag:	return u.rfHeader.get_face (i, base_offset);
476     default:		return Null (OpenTypeFontFace);
477     }
478   }
479 
480   template <typename item_t>
serialize_singleOT::OpenTypeFontFile481   bool serialize_single (hb_serialize_context_t *c,
482 			 hb_tag_t sfnt_tag,
483 			 hb_array_t<item_t> items)
484   {
485     TRACE_SERIALIZE (this);
486     assert (sfnt_tag != TTCTag);
487     if (unlikely (!c->extend_min (*this))) return_trace (false);
488     return_trace (u.fontFace.serialize (c, sfnt_tag, items));
489   }
490 
sanitizeOT::OpenTypeFontFile491   bool sanitize (hb_sanitize_context_t *c) const
492   {
493     TRACE_SANITIZE (this);
494     if (unlikely (!u.tag.sanitize (c))) return_trace (false);
495     switch (u.tag) {
496     case CFFTag:	/* All the non-collection tags */
497     case TrueTag:
498     case Typ1Tag:
499     case TrueTypeTag:	return_trace (u.fontFace.sanitize (c));
500     case TTCTag:	return_trace (u.ttcHeader.sanitize (c));
501     case DFontTag:	return_trace (u.rfHeader.sanitize (c));
502     default:		return_trace (true);
503     }
504   }
505 
506   protected:
507   union {
508   Tag			tag;		/* 4-byte identifier. */
509   OpenTypeFontFace	fontFace;
510   TTCHeader		ttcHeader;
511   ResourceForkHeader	rfHeader;
512   } u;
513   public:
514   DEFINE_SIZE_UNION (4, tag);
515 };
516 
517 
518 } /* namespace OT */
519 
520 
521 #endif /* HB_OPEN_FILE_HH */
522