hb-ot-layout-common-private.hh revision bea34c7cbb583cf7660776e95cab3171590b8427
1/*
2 * Copyright (C) 2007,2008,2009  Red Hat, Inc.
3 *
4 *  This is part of HarfBuzz, a text shaping library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 *
24 * Red Hat Author(s): Behdad Esfahbod
25 */
26
27#ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH
28#define HB_OT_LAYOUT_COMMON_PRIVATE_HH
29
30#include "hb-ot-layout-private.h"
31
32#include "hb-open-type-private.hh"
33
34
35#define NO_CONTEXT		((unsigned int) 0x110000)
36#define NOT_COVERED		((unsigned int) 0x110000)
37#define MAX_NESTING_LEVEL	8
38
39
40/*
41 *
42 * OpenType Layout Common Table Formats
43 *
44 */
45
46
47/*
48 * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList
49 */
50
51template <typename Type>
52struct Record
53{
54  inline bool sanitize (hb_sanitize_context_t *context, void *base) {
55    TRACE_SANITIZE ();
56    return context->check_struct (this)
57	&& offset.sanitize (context, base);
58  }
59
60  Tag		tag;		/* 4-byte Tag identifier */
61  OffsetTo<Type>
62		offset;		/* Offset from beginning of object holding
63				 * the Record */
64  public:
65  DEFINE_SIZE_STATIC (6);
66};
67
68template <typename Type>
69struct RecordArrayOf : ArrayOf<Record<Type> > {
70  inline const Tag& get_tag (unsigned int i) const
71  {
72    if (unlikely (i >= this->len)) return Null(Tag);
73    return (*this)[i].tag;
74  }
75  inline unsigned int get_tags (unsigned int start_offset,
76				unsigned int *record_count /* IN/OUT */,
77				hb_tag_t     *record_tags /* OUT */) const
78  {
79    if (record_count) {
80      const Record<Type> *array = this->sub_array (start_offset, record_count);
81      unsigned int count = *record_count;
82      for (unsigned int i = 0; i < count; i++)
83	record_tags[i] = array[i].tag;
84    }
85    return this->len;
86  }
87  inline bool find_index (hb_tag_t tag, unsigned int *index) const
88  {
89    Tag t;
90    t.set (tag);
91    /* TODO: bsearch (need to sort in sanitize) */
92    const Record<Type> *a = this->array();
93    unsigned int count = this->len;
94    for (unsigned int i = 0; i < count; i++)
95    {
96      if (t == a[i].tag)
97      {
98        if (index) *index = i;
99        return true;
100      }
101    }
102    if (index) *index = NO_INDEX;
103    return false;
104  }
105};
106
107template <typename Type>
108struct RecordListOf : RecordArrayOf<Type>
109{
110  inline const Type& operator [] (unsigned int i) const
111  { return this+RecordArrayOf<Type>::operator [](i).offset; }
112
113  inline bool sanitize (hb_sanitize_context_t *context) {
114    TRACE_SANITIZE ();
115    return RecordArrayOf<Type>::sanitize (context, CharP(this));
116  }
117};
118
119
120struct IndexArray : ArrayOf<USHORT>
121{
122  inline unsigned int operator [] (unsigned int i) const
123  {
124    if (unlikely (i >= this->len))
125      return NO_INDEX;
126    return this->array()[i];
127  }
128  inline unsigned int get_indexes (unsigned int start_offset,
129				   unsigned int *_count /* IN/OUT */,
130				   unsigned int *_indexes /* OUT */) const
131  {
132    if (_count) {
133      const USHORT *array = this->sub_array (start_offset, _count);
134      unsigned int count = *_count;
135      for (unsigned int i = 0; i < count; i++)
136	_indexes[i] = array[i];
137    }
138    return this->len;
139  }
140};
141
142
143struct Script;
144struct LangSys;
145struct Feature;
146
147
148struct LangSys
149{
150  inline unsigned int get_feature_count (void) const
151  { return featureIndex.len; }
152  inline hb_tag_t get_feature_index (unsigned int i) const
153  { return featureIndex[i]; }
154  inline unsigned int get_feature_indexes (unsigned int start_offset,
155					   unsigned int *feature_count /* IN/OUT */,
156					   unsigned int *feature_indexes /* OUT */) const
157  { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); }
158
159  inline bool has_required_feature (void) const { return reqFeatureIndex != 0xffff; }
160  inline int get_required_feature_index (void) const
161  {
162    if (reqFeatureIndex == 0xffff)
163      return NO_INDEX;
164   return reqFeatureIndex;;
165  }
166
167  inline bool sanitize (hb_sanitize_context_t *context) {
168    TRACE_SANITIZE ();
169    return context->check_struct (this)
170	&& featureIndex.sanitize (context);
171  }
172
173  Offset	lookupOrder;	/* = Null (reserved for an offset to a
174				 * reordering table) */
175  USHORT	reqFeatureIndex;/* Index of a feature required for this
176				 * language system--if no required features
177				 * = 0xFFFF */
178  IndexArray	featureIndex;	/* Array of indices into the FeatureList */
179  public:
180  DEFINE_SIZE_VAR (6, USHORT);
181};
182DEFINE_NULL_DATA (LangSys, "\0\0\xFF\xFF");
183
184
185struct Script
186{
187  inline unsigned int get_lang_sys_count (void) const
188  { return langSys.len; }
189  inline const Tag& get_lang_sys_tag (unsigned int i) const
190  { return langSys.get_tag (i); }
191  inline unsigned int get_lang_sys_tags (unsigned int start_offset,
192					 unsigned int *lang_sys_count /* IN/OUT */,
193					 hb_tag_t     *lang_sys_tags /* OUT */) const
194  { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); }
195  inline const LangSys& get_lang_sys (unsigned int i) const
196  {
197    if (i == NO_INDEX) return get_default_lang_sys ();
198    return this+langSys[i].offset;
199  }
200  inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const
201  { return langSys.find_index (tag, index); }
202
203  inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; }
204  inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
205
206  inline bool sanitize (hb_sanitize_context_t *context) {
207    TRACE_SANITIZE ();
208    return defaultLangSys.sanitize (context, this)
209	&& langSys.sanitize (context, this);
210  }
211
212  private:
213  OffsetTo<LangSys>
214		defaultLangSys;	/* Offset to DefaultLangSys table--from
215				 * beginning of Script table--may be Null */
216  RecordArrayOf<LangSys>
217		langSys;	/* Array of LangSysRecords--listed
218				 * alphabetically by LangSysTag */
219  public:
220  DEFINE_SIZE_VAR (4, Record<LangSys>);
221};
222
223typedef RecordListOf<Script> ScriptList;
224
225
226struct Feature
227{
228  inline unsigned int get_lookup_count (void) const
229  { return lookupIndex.len; }
230  inline hb_tag_t get_lookup_index (unsigned int i) const
231  { return lookupIndex[i]; }
232  inline unsigned int get_lookup_indexes (unsigned int start_index,
233					  unsigned int *lookup_count /* IN/OUT */,
234					  unsigned int *lookup_tags /* OUT */) const
235  { return lookupIndex.get_indexes (start_index, lookup_count, lookup_tags); }
236
237  inline bool sanitize (hb_sanitize_context_t *context) {
238    TRACE_SANITIZE ();
239    return context->check_struct (this)
240	&& lookupIndex.sanitize (context);
241  }
242
243  /* LONGTERMTODO: implement get_feature_parameters() */
244  /* LONGTERMTODO: implement FeatureSize and other special features? */
245  Offset	featureParams;	/* Offset to Feature Parameters table (if one
246				 * has been defined for the feature), relative
247				 * to the beginning of the Feature Table; = Null
248				 * if not required */
249  IndexArray	 lookupIndex;	/* Array of LookupList indices */
250  public:
251  DEFINE_SIZE_VAR (4, USHORT);
252};
253
254typedef RecordListOf<Feature> FeatureList;
255
256
257struct LookupFlag : USHORT
258{
259  enum {
260    RightToLeft		= 0x0001u,
261    IgnoreBaseGlyphs	= 0x0002u,
262    IgnoreLigatures	= 0x0004u,
263    IgnoreMarks		= 0x0008u,
264    IgnoreFlags		= 0x000Eu,
265    UseMarkFilteringSet	= 0x0010u,
266    Reserved		= 0x00E0u,
267    MarkAttachmentType	= 0xFF00u
268  };
269  public:
270  DEFINE_SIZE_STATIC (2);
271};
272
273struct Lookup
274{
275  inline unsigned int get_subtable_count (void) const { return subTable.len; }
276
277  inline unsigned int get_type (void) const { return lookupType; }
278  inline unsigned int get_flag (void) const
279  {
280    unsigned int flag = lookupFlag;
281    if (unlikely (flag & LookupFlag::UseMarkFilteringSet))
282    {
283      const USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
284      flag += (markFilteringSet << 16);
285    }
286    return flag;
287  }
288
289  inline bool sanitize (hb_sanitize_context_t *context) {
290    TRACE_SANITIZE ();
291    /* Real sanitize of the subtables is done by GSUB/GPOS/... */
292    if (!(context->check_struct (this)
293       && likely (subTable.sanitize (context)))) return false;
294    if (unlikely (lookupFlag & LookupFlag::UseMarkFilteringSet))
295    {
296      USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
297      if (!markFilteringSet.sanitize (context)) return false;
298    }
299    return true;
300  }
301
302  USHORT	lookupType;		/* Different enumerations for GSUB and GPOS */
303  USHORT	lookupFlag;		/* Lookup qualifiers */
304  ArrayOf<Offset>
305		subTable;		/* Array of SubTables */
306  USHORT	markFilteringSetX[VAR];	/* Index (base 0) into GDEF mark glyph sets
307					 * structure. This field is only present if bit
308					 * UseMarkFilteringSet of lookup flags is set. */
309  public:
310  DEFINE_SIZE_VAR2 (6, Offset, USHORT);
311};
312
313typedef OffsetListOf<Lookup> LookupList;
314
315
316/*
317 * Coverage Table
318 */
319
320struct CoverageFormat1
321{
322  friend struct Coverage;
323
324  private:
325  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
326  {
327    if (unlikely (glyph_id > 0xFFFF))
328      return NOT_COVERED;
329    GlyphID gid;
330    gid.set (glyph_id);
331    /* TODO: bsearch (need to sort in sanitize) */
332    unsigned int num_glyphs = glyphArray.len;
333    for (unsigned int i = 0; i < num_glyphs; i++)
334      if (gid == glyphArray[i])
335        return i;
336    return NOT_COVERED;
337  }
338
339  inline bool sanitize (hb_sanitize_context_t *context) {
340    TRACE_SANITIZE ();
341    return glyphArray.sanitize (context);
342  }
343
344  private:
345  USHORT	coverageFormat;	/* Format identifier--format = 1 */
346  ArrayOf<GlyphID>
347		glyphArray;	/* Array of GlyphIDs--in numerical order */
348  public:
349  DEFINE_SIZE_VAR (4, GlyphID);
350};
351
352struct CoverageRangeRecord
353{
354  friend struct CoverageFormat2;
355
356  private:
357  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
358  {
359    if (glyph_id >= start && glyph_id <= end)
360      return (unsigned int) startCoverageIndex + (glyph_id - start);
361    return NOT_COVERED;
362  }
363
364  public:
365  inline bool sanitize (hb_sanitize_context_t *context) {
366    TRACE_SANITIZE ();
367    return context->check_struct (this);
368  }
369
370  private:
371  GlyphID	start;			/* First GlyphID in the range */
372  GlyphID	end;			/* Last GlyphID in the range */
373  USHORT	startCoverageIndex;	/* Coverage Index of first GlyphID in
374					 * range */
375  public:
376  DEFINE_SIZE_STATIC (6);
377};
378DEFINE_NULL_DATA (CoverageRangeRecord, "\000\001");
379
380struct CoverageFormat2
381{
382  friend struct Coverage;
383
384  private:
385  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
386  {
387    /* TODO: bsearch (need to sort in sanitize) */
388    unsigned int count = rangeRecord.len;
389    for (unsigned int i = 0; i < count; i++)
390    {
391      unsigned int coverage = rangeRecord[i].get_coverage (glyph_id);
392      if (coverage != NOT_COVERED)
393        return coverage;
394    }
395    return NOT_COVERED;
396  }
397
398  inline bool sanitize (hb_sanitize_context_t *context) {
399    TRACE_SANITIZE ();
400    return rangeRecord.sanitize (context);
401  }
402
403  private:
404  USHORT	coverageFormat;	/* Format identifier--format = 2 */
405  ArrayOf<CoverageRangeRecord>
406		rangeRecord;	/* Array of glyph ranges--ordered by
407				 * Start GlyphID. rangeCount entries
408				 * long */
409  public:
410  DEFINE_SIZE_VAR (4, CoverageRangeRecord);
411};
412
413struct Coverage
414{
415  inline unsigned int operator () (hb_codepoint_t glyph_id) const { return get_coverage (glyph_id); }
416
417  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
418  {
419    switch (u.format) {
420    case 1: return u.format1->get_coverage(glyph_id);
421    case 2: return u.format2->get_coverage(glyph_id);
422    default:return NOT_COVERED;
423    }
424  }
425
426  inline bool sanitize (hb_sanitize_context_t *context) {
427    TRACE_SANITIZE ();
428    if (!u.format.sanitize (context)) return false;
429    switch (u.format) {
430    case 1: return u.format1->sanitize (context);
431    case 2: return u.format2->sanitize (context);
432    default:return true;
433    }
434  }
435
436  private:
437  union {
438  USHORT		format;		/* Format identifier */
439  CoverageFormat1	format1[VAR];
440  CoverageFormat2	format2[VAR];
441  } u;
442};
443
444
445/*
446 * Class Definition Table
447 */
448
449struct ClassDefFormat1
450{
451  friend struct ClassDef;
452
453  private:
454  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const
455  {
456    if ((unsigned int) (glyph_id - startGlyph) < classValue.len)
457      return classValue[glyph_id - startGlyph];
458    return 0;
459  }
460
461  inline bool sanitize (hb_sanitize_context_t *context) {
462    TRACE_SANITIZE ();
463    return context->check_struct (this)
464	&& classValue.sanitize (context);
465  }
466
467  USHORT	classFormat;		/* Format identifier--format = 1 */
468  GlyphID	startGlyph;		/* First GlyphID of the classValueArray */
469  ArrayOf<USHORT>
470		classValue;		/* Array of Class Values--one per GlyphID */
471  public:
472  DEFINE_SIZE_VAR (6, USHORT);
473};
474
475struct ClassRangeRecord
476{
477  friend struct ClassDefFormat2;
478
479  private:
480  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const
481  {
482    if (glyph_id >= start && glyph_id <= end)
483      return classValue;
484    return 0;
485  }
486
487  public:
488  inline bool sanitize (hb_sanitize_context_t *context) {
489    TRACE_SANITIZE ();
490    return context->check_struct (this);
491  }
492
493  private:
494  GlyphID	start;		/* First GlyphID in the range */
495  GlyphID	end;		/* Last GlyphID in the range */
496  USHORT	classValue;	/* Applied to all glyphs in the range */
497  public:
498  DEFINE_SIZE_STATIC (6);
499};
500DEFINE_NULL_DATA (ClassRangeRecord, "\000\001");
501
502struct ClassDefFormat2
503{
504  friend struct ClassDef;
505
506  private:
507  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const
508  {
509    /* TODO: bsearch (need to sort in sanitize) */
510    unsigned int count = rangeRecord.len;
511    for (unsigned int i = 0; i < count; i++)
512    {
513      int classValue = rangeRecord[i].get_class (glyph_id);
514      if (classValue > 0)
515        return classValue;
516    }
517    return 0;
518  }
519
520  inline bool sanitize (hb_sanitize_context_t *context) {
521    TRACE_SANITIZE ();
522    return rangeRecord.sanitize (context);
523  }
524
525  USHORT	classFormat;	/* Format identifier--format = 2 */
526  ArrayOf<ClassRangeRecord>
527		rangeRecord;	/* Array of glyph ranges--ordered by
528				 * Start GlyphID */
529  public:
530  DEFINE_SIZE_VAR (4, ClassRangeRecord);
531};
532
533struct ClassDef
534{
535  inline hb_ot_layout_class_t operator () (hb_codepoint_t glyph_id) const { return get_class (glyph_id); }
536
537  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const
538  {
539    switch (u.format) {
540    case 1: return u.format1->get_class(glyph_id);
541    case 2: return u.format2->get_class(glyph_id);
542    default:return 0;
543    }
544  }
545
546  inline bool sanitize (hb_sanitize_context_t *context) {
547    TRACE_SANITIZE ();
548    if (!u.format.sanitize (context)) return false;
549    switch (u.format) {
550    case 1: return u.format1->sanitize (context);
551    case 2: return u.format2->sanitize (context);
552    default:return true;
553    }
554  }
555
556  private:
557  union {
558  USHORT		format;		/* Format identifier */
559  ClassDefFormat1	format1[VAR];
560  ClassDefFormat2	format2[VAR];
561  } u;
562};
563
564
565/*
566 * Device Tables
567 */
568
569struct Device
570{
571  inline int operator () (unsigned int ppem_size) const { return get_delta (ppem_size); }
572
573  inline int get_delta (unsigned int ppem_size) const
574  {
575    unsigned int f = deltaFormat;
576    if (unlikely (f < 1 || f > 3))
577      return 0;
578
579    if (ppem_size < startSize || ppem_size > endSize)
580      return 0;
581
582    unsigned int s = ppem_size - startSize;
583
584    unsigned int byte = deltaValue[s >> (4 - f)];
585    unsigned int bits = (byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f)));
586    unsigned int mask = (0xFFFF >> (16 - (1 << f)));
587
588    int delta = bits & mask;
589
590    if ((unsigned int) delta >= ((mask + 1) >> 1))
591      delta -= mask + 1;
592
593    return delta;
594  }
595
596  inline unsigned int get_size () const
597  {
598    unsigned int f = deltaFormat;
599    if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * USHORT::get_size ();
600    return USHORT::get_size () * (4 + ((endSize - startSize) >> (4 - f)));
601  }
602
603  inline bool sanitize (hb_sanitize_context_t *context) {
604    TRACE_SANITIZE ();
605    return context->check_struct (this)
606	&& context->check_range (this, this->get_size ());
607  }
608
609  private:
610  USHORT	startSize;		/* Smallest size to correct--in ppem */
611  USHORT	endSize;		/* Largest size to correct--in ppem */
612  USHORT	deltaFormat;		/* Format of DeltaValue array data: 1, 2, or 3
613					 * 1	Signed 2-bit value, 8 values per uint16
614					 * 2	Signed 4-bit value, 4 values per uint16
615					 * 3	Signed 8-bit value, 2 values per uint16
616					 */
617  USHORT	deltaValue[VAR];	/* Array of compressed data */
618  public:
619  DEFINE_SIZE_VAR (6, USHORT);
620};
621
622
623#endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */
624