hb-ot-layout-common-private.hh revision cd33cb9ed84308da72bd7c64b9355dc2410c63ec
1/*
2 * Copyright (C) 2007,2008,2009  Red Hat, Inc.
3 *
4 *  This is part of HarfBuzz, an OpenType Layout engine 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 (SANITIZE_ARG_DEF, const void *base) {
55    SANITIZE_DEBUG ();
56    return SANITIZE (tag) && SANITIZE_BASE (offset, base);
57  }
58
59  Tag		tag;		/* 4-byte Tag identifier */
60  OffsetTo<Type>
61		offset;		/* Offset from beginning of object holding
62				 * the Record */
63};
64
65template <typename Type>
66struct RecordArrayOf : ArrayOf<Record<Type> > {
67  inline const Tag& get_tag (unsigned int i) const
68  {
69    if (HB_UNLIKELY (i >= this->len)) return Null(Tag);
70    return this->array[i].tag;
71  }
72  inline bool get_tags (unsigned int *record_count /* IN/OUT */,
73			hb_tag_t     *record_tags /* OUT */) const
74  {
75    unsigned int count = MIN (this->len, *record_count);
76    for (unsigned int i = 0; i < count; i++)
77      record_tags[i] = this->array[i].tag;
78
79    *record_count = this->len;
80    return !!this->len;
81  }
82  inline bool find_index (hb_tag_t tag, unsigned int *index) const
83  {
84    const Tag t = tag;
85    // TODO bsearch
86    unsigned int count = this->len;
87    for (unsigned int i = 0; i < count; i++)
88    {
89      if (t == this->array[i].tag)
90      {
91        if (index) *index = i;
92        return true;
93      }
94    }
95    if (index) *index = NO_INDEX;
96    return false;
97  }
98};
99
100template <typename Type>
101struct RecordListOf : RecordArrayOf<Type>
102{
103  inline const Type& operator [] (unsigned int i) const
104  { return this+RecordArrayOf<Type>::operator[](i).offset; }
105
106  inline bool sanitize (SANITIZE_ARG_DEF) {
107    SANITIZE_DEBUG ();
108    return RecordArrayOf<Type>::sanitize (SANITIZE_ARG, CONST_CHARP(this));
109  }
110};
111
112
113struct IndexArray : ArrayOf<USHORT>
114{
115  inline unsigned int operator [] (unsigned int i) const
116  {
117    if (HB_UNLIKELY (i >= this->len))
118      return NO_INDEX;
119    return this->array[i];
120  }
121  inline bool get_indexes (unsigned int *_count /* IN/OUT */,
122			   unsigned int *_indexes /* OUT */) const
123  {
124    unsigned int count = MIN (this->len, *_count);
125    for (unsigned int i = 0; i < count; i++)
126      _indexes[i] = this->array[i];
127
128    *_count = this->len;
129    return !!this->len;
130  }
131};
132
133
134struct Script;
135struct LangSys;
136struct Feature;
137
138
139struct LangSys
140{
141  inline unsigned int get_feature_count (void) const
142  { return featureIndex.len; }
143  inline hb_tag_t get_feature_index (unsigned int i) const
144  { return featureIndex[i]; }
145  inline bool get_feature_indexes (unsigned int *feature_count /* IN/OUT */,
146				   hb_tag_t     *feature_tags /* OUT */) const
147  { return featureIndex.get_indexes (feature_count, feature_tags); }
148
149  inline bool has_required_feature (void) const { return reqFeatureIndex != 0xffff; }
150  inline int get_required_feature_index (void) const
151  {
152    if (reqFeatureIndex == 0xffff)
153      return NO_INDEX;
154   return reqFeatureIndex;;
155  }
156
157  inline bool sanitize (SANITIZE_ARG_DEF) {
158    SANITIZE_DEBUG ();
159    return SANITIZE_SELF () && SANITIZE (featureIndex);
160  }
161
162  Offset	lookupOrder;	/* = Null (reserved for an offset to a
163				 * reordering table) */
164  USHORT	reqFeatureIndex;/* Index of a feature required for this
165				 * language system--if no required features
166				 * = 0xFFFF */
167  IndexArray	featureIndex;	/* Array of indices into the FeatureList */
168};
169ASSERT_SIZE_DATA (LangSys, 6, "\0\0\xFF\xFF");
170
171
172struct Script
173{
174  inline unsigned int get_lang_sys_count (void) const
175  { return langSys.len; }
176  inline const Tag& get_lang_sys_tag (unsigned int i) const
177  { return langSys.get_tag (i); }
178  inline bool get_lang_sys_tags (unsigned int *lang_sys_count /* IN/OUT */,
179				 hb_tag_t     *lang_sys_tags /* OUT */) const
180  { return langSys.get_tags (lang_sys_count, lang_sys_tags); }
181  inline const LangSys& get_lang_sys (unsigned int i) const
182  {
183    if (i == NO_INDEX) return get_default_lang_sys ();
184    return this+langSys[i].offset;
185  }
186  inline bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const
187  { return langSys.find_index (tag, index); }
188
189  inline bool has_default_lang_sys (void) const { return defaultLangSys != 0; }
190  inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; }
191
192  inline bool sanitize (SANITIZE_ARG_DEF) {
193    SANITIZE_DEBUG ();
194    return SANITIZE_THIS (defaultLangSys) && SANITIZE_THIS (langSys);
195  }
196
197  private:
198  OffsetTo<LangSys>
199		defaultLangSys;	/* Offset to DefaultLangSys table--from
200				 * beginning of Script table--may be Null */
201  RecordArrayOf<LangSys>
202		langSys;	/* Array of LangSysRecords--listed
203				 * alphabetically by LangSysTag */
204};
205ASSERT_SIZE (Script, 4);
206
207typedef RecordListOf<Script> ScriptList;
208ASSERT_SIZE (ScriptList, 2);
209
210
211struct Feature
212{
213  inline unsigned int get_lookup_count (void) const
214  { return lookupIndex.len; }
215  inline hb_tag_t get_lookup_index (unsigned int i) const
216  { return lookupIndex[i]; }
217  inline bool get_lookup_indexes (unsigned int *lookup_count /* IN/OUT */,
218				  hb_tag_t     *lookup_tags /* OUT */) const
219  { return lookupIndex.get_indexes (lookup_count, lookup_tags); }
220
221  inline bool sanitize (SANITIZE_ARG_DEF) {
222    SANITIZE_DEBUG ();
223    return SANITIZE_SELF () && SANITIZE (lookupIndex);
224  }
225
226  /* TODO: implement get_feature_parameters() */
227  /* TODO: implement FeatureSize and other special features? */
228  Offset	featureParams;	/* Offset to Feature Parameters table (if one
229				 * has been defined for the feature), relative
230				 * to the beginning of the Feature Table; = Null
231				 * if not required */
232  IndexArray	 lookupIndex;	/* Array of LookupList indices */
233};
234ASSERT_SIZE (Feature, 4);
235
236typedef RecordListOf<Feature> FeatureList;
237ASSERT_SIZE (FeatureList, 2);
238
239
240struct LookupFlag : USHORT
241{
242  enum {
243    RightToLeft		= 0x0001u,
244    IgnoreBaseGlyphs	= 0x0002u,
245    IgnoreLigatures	= 0x0004u,
246    IgnoreMarks		= 0x0008u,
247    UseMarkFilteringSet	= 0x0010u,
248    Reserved		= 0x00E0u,
249    MarkAttachmentType	= 0xFF00u,
250  };
251};
252ASSERT_SIZE (LookupFlag, 2);
253
254struct LookupSubTable
255{
256  inline bool sanitize (SANITIZE_ARG_DEF) {
257    SANITIZE_DEBUG ();
258    return SANITIZE_SELF ();
259  }
260
261  private:
262  USHORT	format;		/* Subtable format.  Different for GSUB and GPOS */
263};
264ASSERT_SIZE (LookupSubTable, 2);
265
266struct Lookup
267{
268  inline const LookupSubTable& get_subtable (unsigned int i) const { return this+subTable[i]; }
269  inline unsigned int get_subtable_count (void) const { return subTable.len; }
270
271  inline unsigned int get_type (void) const { return lookupType; }
272  inline unsigned int get_flag (void) const
273  {
274    unsigned int flag = lookupFlag;
275    if (HB_UNLIKELY (flag & LookupFlag::UseMarkFilteringSet))
276    {
277      const USHORT &markFilteringSet = CONST_NEXT (USHORT, subTable);
278      flag += (markFilteringSet << 16);
279    }
280    return flag;
281  }
282
283  inline bool sanitize (SANITIZE_ARG_DEF) {
284    SANITIZE_DEBUG ();
285    if (!(SANITIZE_SELF () && SANITIZE_THIS (subTable))) return false;
286    if (HB_UNLIKELY (lookupFlag & LookupFlag::UseMarkFilteringSet))
287    {
288      USHORT &markFilteringSet = NEXT (USHORT, subTable);
289      if (!SANITIZE (markFilteringSet)) return false;
290    }
291    return true;
292  }
293
294  USHORT	lookupType;		/* Different enumerations for GSUB and GPOS */
295  USHORT	lookupFlag;		/* Lookup qualifiers */
296  OffsetArrayOf<LookupSubTable>
297		subTable;		/* Array of SubTables */
298  USHORT	markFilteringSetX[0];	/* Index (base 0) into GDEF mark glyph sets
299					 * structure. This field is only present if bit
300					 * UseMarkFilteringSet of lookup flags is set. */
301};
302ASSERT_SIZE (Lookup, 6);
303
304typedef OffsetListOf<Lookup> LookupList;
305ASSERT_SIZE (LookupList, 2);
306
307
308/*
309 * Coverage Table
310 */
311
312struct CoverageFormat1
313{
314  friend struct Coverage;
315
316  private:
317  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
318  {
319    if (HB_UNLIKELY (glyph_id > 0xFFFF))
320      return NOT_COVERED;
321    GlyphID gid;
322    gid = glyph_id;
323    // TODO: bsearch
324    unsigned int num_glyphs = glyphArray.len;
325    for (unsigned int i = 0; i < num_glyphs; i++)
326      if (gid == glyphArray[i])
327        return i;
328    return NOT_COVERED;
329  }
330
331  inline bool sanitize (SANITIZE_ARG_DEF) {
332    SANITIZE_DEBUG ();
333    return SANITIZE (glyphArray);
334  }
335
336  private:
337  USHORT	coverageFormat;	/* Format identifier--format = 1 */
338  ArrayOf<GlyphID>
339		glyphArray;	/* Array of GlyphIDs--in numerical order */
340};
341ASSERT_SIZE (CoverageFormat1, 4);
342
343struct CoverageRangeRecord
344{
345  friend struct CoverageFormat2;
346
347  private:
348  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
349  {
350    if (glyph_id >= start && glyph_id <= end)
351      return (unsigned int) startCoverageIndex + (glyph_id - start);
352    return NOT_COVERED;
353  }
354
355  public:
356  inline bool sanitize (SANITIZE_ARG_DEF) {
357    SANITIZE_DEBUG ();
358    return SANITIZE_SELF ();
359  }
360
361  private:
362  GlyphID	start;			/* First GlyphID in the range */
363  GlyphID	end;			/* Last GlyphID in the range */
364  USHORT	startCoverageIndex;	/* Coverage Index of first GlyphID in
365					 * range */
366};
367ASSERT_SIZE_DATA (CoverageRangeRecord, 6, "\000\001");
368
369struct CoverageFormat2
370{
371  friend struct Coverage;
372
373  private:
374  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
375  {
376    // TODO: bsearch
377    unsigned int count = rangeRecord.len;
378    for (unsigned int i = 0; i < count; i++)
379    {
380      unsigned int coverage = rangeRecord[i].get_coverage (glyph_id);
381      if (coverage != NOT_COVERED)
382        return coverage;
383    }
384    return NOT_COVERED;
385  }
386
387  inline bool sanitize (SANITIZE_ARG_DEF) {
388    SANITIZE_DEBUG ();
389    return SANITIZE (rangeRecord);
390  }
391
392  private:
393  USHORT	coverageFormat;	/* Format identifier--format = 2 */
394  ArrayOf<CoverageRangeRecord>
395		rangeRecord;	/* Array of glyph ranges--ordered by
396				 * Start GlyphID. rangeCount entries
397				 * long */
398};
399ASSERT_SIZE (CoverageFormat2, 4);
400
401struct Coverage
402{
403  inline unsigned int operator() (hb_codepoint_t glyph_id) const { return get_coverage (glyph_id); }
404
405  inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
406  {
407    switch (u.format) {
408    case 1: return u.format1->get_coverage(glyph_id);
409    case 2: return u.format2->get_coverage(glyph_id);
410    default:return NOT_COVERED;
411    }
412  }
413
414  inline bool sanitize (SANITIZE_ARG_DEF) {
415    SANITIZE_DEBUG ();
416    if (!SANITIZE (u.format)) return false;
417    switch (u.format) {
418    case 1: return u.format1->sanitize (SANITIZE_ARG);
419    case 2: return u.format2->sanitize (SANITIZE_ARG);
420    default:return true;
421    }
422  }
423
424  private:
425  union {
426  USHORT		format;		/* Format identifier */
427  CoverageFormat1	format1[];
428  CoverageFormat2	format2[];
429  } u;
430};
431ASSERT_SIZE (Coverage, 2);
432
433
434/*
435 * Class Definition Table
436 */
437
438struct ClassDefFormat1
439{
440  friend struct ClassDef;
441
442  private:
443  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const
444  {
445    if ((unsigned int) (glyph_id - startGlyph) < classValue.len)
446      return classValue[glyph_id - startGlyph];
447    return 0;
448  }
449
450  inline bool sanitize (SANITIZE_ARG_DEF) {
451    SANITIZE_DEBUG ();
452    return SANITIZE_SELF () && SANITIZE (classValue);
453  }
454
455  USHORT	classFormat;		/* Format identifier--format = 1 */
456  GlyphID	startGlyph;		/* First GlyphID of the classValueArray */
457  ArrayOf<USHORT>
458		classValue;		/* Array of Class Values--one per GlyphID */
459};
460ASSERT_SIZE (ClassDefFormat1, 6);
461
462struct ClassRangeRecord
463{
464  friend struct ClassDefFormat2;
465
466  private:
467  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const
468  {
469    if (glyph_id >= start && glyph_id <= end)
470      return classValue;
471    return 0;
472  }
473
474  public:
475  inline bool sanitize (SANITIZE_ARG_DEF) {
476    SANITIZE_DEBUG ();
477    return SANITIZE_SELF ();
478  }
479
480  private:
481  GlyphID	start;		/* First GlyphID in the range */
482  GlyphID	end;		/* Last GlyphID in the range */
483  USHORT	classValue;	/* Applied to all glyphs in the range */
484};
485ASSERT_SIZE_DATA (ClassRangeRecord, 6, "\000\001");
486
487struct ClassDefFormat2
488{
489  friend struct ClassDef;
490
491  private:
492  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const
493  {
494    // TODO: bsearch
495    unsigned int count = rangeRecord.len;
496    for (unsigned int i = 0; i < count; i++)
497    {
498      int classValue = rangeRecord[i].get_class (glyph_id);
499      if (classValue > 0)
500        return classValue;
501    }
502    return 0;
503  }
504
505  inline bool sanitize (SANITIZE_ARG_DEF) {
506    SANITIZE_DEBUG ();
507    return SANITIZE (rangeRecord);
508  }
509
510  USHORT	classFormat;	/* Format identifier--format = 2 */
511  ArrayOf<ClassRangeRecord>
512		rangeRecord;	/* Array of glyph ranges--ordered by
513				 * Start GlyphID */
514};
515ASSERT_SIZE (ClassDefFormat2, 4);
516
517struct ClassDef
518{
519  inline unsigned int operator() (hb_codepoint_t glyph_id) const { return get_class (glyph_id); }
520
521  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const
522  {
523    switch (u.format) {
524    case 1: return u.format1->get_class(glyph_id);
525    case 2: return u.format2->get_class(glyph_id);
526    default:return 0;
527    }
528  }
529
530  inline bool sanitize (SANITIZE_ARG_DEF) {
531    SANITIZE_DEBUG ();
532    if (!SANITIZE (u.format)) return false;
533    switch (u.format) {
534    case 1: return u.format1->sanitize (SANITIZE_ARG);
535    case 2: return u.format2->sanitize (SANITIZE_ARG);
536    default:return true;
537    }
538  }
539
540  private:
541  union {
542  USHORT		format;		/* Format identifier */
543  ClassDefFormat1	format1[];
544  ClassDefFormat2	format2[];
545  } u;
546};
547ASSERT_SIZE (ClassDef, 2);
548
549
550/*
551 * Device Tables
552 */
553
554struct Device
555{
556  inline int operator() (unsigned int ppem_size) const { return get_delta (ppem_size); }
557
558  inline int get_delta (unsigned int ppem_size) const
559  {
560    unsigned int f = deltaFormat;
561    if (HB_UNLIKELY (f < 1 || f > 3))
562      return 0;
563
564    if (ppem_size < startSize || ppem_size > endSize)
565      return 0;
566
567    unsigned int s = ppem_size - startSize;
568
569    unsigned int byte = deltaValue[s >> (4 - f)];
570    unsigned int bits = (byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f)));
571    unsigned int mask = (0xFFFF >> (16 - (1 << f)));
572
573    int delta = bits & mask;
574
575    if ((unsigned int) delta >= ((mask + 1) >> 1))
576      delta -= mask + 1;
577
578    return delta;
579  }
580
581  inline unsigned int get_size () const
582  {
583    unsigned int f = deltaFormat;
584    if (HB_UNLIKELY (f < 1 || f > 3 || startSize > endSize)) return sizeof (*this);
585    return sizeof (*this) + ((endSize - startSize + (1 << (4 - f)) - 1) >> (4 - f));
586  }
587
588  inline bool sanitize (SANITIZE_ARG_DEF) {
589    SANITIZE_DEBUG ();
590    return SANITIZE_GET_SIZE ();
591  }
592
593  private:
594  USHORT	startSize;	/* Smallest size to correct--in ppem */
595  USHORT	endSize;	/* Largest size to correct--in ppem */
596  USHORT	deltaFormat;	/* Format of DeltaValue array data: 1, 2, or 3 */
597  USHORT	deltaValue[];	/* Array of compressed data */
598};
599ASSERT_SIZE (Device, 6);
600
601
602#endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */
603