befb3acbb29c7dc674b075d454b7692819479187
[framework/uifw/harfbuzz.git] / src / hb-ot-layout-gsubgpos-private.h
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_GSUBGPOS_PRIVATE_H
28 #define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_H
29
30 #include "hb-ot-layout-gdef-private.h"
31 #include "harfbuzz-buffer-private.h" /* XXX */
32
33
34 #define LOOKUP_ARGS_DEF \
35         hb_ot_layout_t *layout, \
36         hb_buffer_t    *buffer, \
37         unsigned int    context_length HB_GNUC_UNUSED, \
38         unsigned int    nesting_level_left HB_GNUC_UNUSED, \
39         unsigned int    lookup_flag, \
40         unsigned int    property HB_GNUC_UNUSED /* propety of first glyph */
41 #define LOOKUP_ARGS \
42         layout, \
43         buffer, \
44         context_length, \
45         nesting_level_left, \
46         lookup_flag, \
47         property
48
49
50 /* Context lookups */
51
52 typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, char *data);
53 typedef bool (*apply_lookup_func_t) (LOOKUP_ARGS_DEF, unsigned int lookup_index);
54
55 struct ContextLookupContext {
56   inline bool match (hb_codepoint_t glyph_id, const USHORT &value) const {
57     return match_func (glyph_id, value, match_data);
58   }
59   inline bool apply (LOOKUP_ARGS_DEF, unsigned int lookup_index) const {
60     return apply_func (LOOKUP_ARGS, lookup_index);
61   }
62
63   match_func_t match_func;
64   char *match_data;
65   apply_lookup_func_t apply_func;
66 };
67
68 struct LookupRecord {
69
70   inline bool apply (LOOKUP_ARGS_DEF, ContextLookupContext &context) const {
71     return context.apply (LOOKUP_ARGS, lookupListIndex);
72   }
73
74   USHORT        sequenceIndex;          /* Index into current glyph
75                                          * sequence--first glyph = 0 */
76   USHORT        lookupListIndex;        /* Lookup to apply to that
77                                          * position--zero--based */
78 };
79 ASSERT_SIZE (LookupRecord, 4);
80
81 static inline bool context_lookup (LOOKUP_ARGS_DEF,
82                                    USHORT glyphCount, /* Including the first glyph (not matched) */
83                                    USHORT recordCount,
84                                    const USHORT value[], /* Array of match values--start with second glyph */
85                                    const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
86                                    ContextLookupContext &context) {
87
88   unsigned int i, j;
89   unsigned int count = glyphCount;
90
91   if (HB_UNLIKELY (buffer->in_pos + count > buffer->in_length ||
92                    context_length < count))
93     return false; /* Not enough glyphs in input or context */
94
95   /* XXX context_length should also be checked when skipping glyphs, right?
96    * What does context_length really mean, anyway? */
97
98   for (i = 1, j = buffer->in_pos + 1; i < count; i++, j++) {
99     while (!_hb_ot_layout_check_glyph_property (layout, IN_ITEM (j), lookup_flag, &property)) {
100       if (HB_UNLIKELY (j + count - i == buffer->in_length))
101         return false;
102       j++;
103     }
104
105     if (HB_LIKELY (context.match (IN_GLYPH(j), value[i - 1])))
106       return false;
107   }
108
109   /* XXX right? or j - buffer_inpos? */
110   context_length = count;
111
112   /* XXX We have to jump non-matching glyphs when applying too, right? */
113   unsigned int record_count = recordCount;
114   const LookupRecord *record = lookupRecord;
115   for (i = 0; i < count;)
116   {
117     if (record_count && i == record->sequenceIndex)
118     {
119       unsigned int old_pos = buffer->in_pos;
120
121       /* Apply a lookup */
122       bool done = record->apply (LOOKUP_ARGS, context);
123
124       record++;
125       record_count--;
126       i += buffer->in_pos - old_pos;
127
128       if (!done)
129         goto not_applied;
130     }
131     else
132     {
133     not_applied:
134       /* No lookup applied for this index */
135       _hb_buffer_next_glyph (buffer);
136       i++;
137     }
138   }
139
140   return true;
141 }
142 struct Rule {
143
144   friend struct RuleSet;
145
146   private:
147   DEFINE_ARRAY_TYPE (USHORT, value, (glyphCount ? glyphCount - 1 : 0));
148
149   inline bool apply (LOOKUP_ARGS_DEF, ContextLookupContext &context) const {
150     const LookupRecord *record = (const LookupRecord *) ((const char *) value + sizeof (value[0]) * (glyphCount ? glyphCount - 1 : 0));
151     return context_lookup (LOOKUP_ARGS,
152                            glyphCount,
153                            recordCount,
154                            value,
155                            record,
156                            context);
157   }
158
159   private:
160   USHORT        glyphCount;             /* Total number of glyphs in input
161                                          * glyph sequence--includes the  first
162                                          * glyph */
163   USHORT        recordCount;            /* Number of LookupRecords */
164   USHORT        value[];                /* Array of match values--start with
165                                          * second glyph */
166   LookupRecord  lookupRecord[];         /* Array of LookupRecords--in
167                                          * design order */
168 };
169 ASSERT_SIZE (Rule, 4);
170
171 struct RuleSet {
172
173   inline bool apply (LOOKUP_ARGS_DEF, ContextLookupContext &context) const {
174
175     unsigned int num_rules = rule.len;
176     for (unsigned int i = 0; i < num_rules; i++) {
177       if ((this+rule[i]).apply (LOOKUP_ARGS, context))
178         return true;
179     }
180
181     return false;
182   }
183
184   private:
185   OffsetArrayOf<Rule>
186                 rule;                   /* Array SubRule tables
187                                          * ordered by preference */
188 };
189
190
191 static inline bool glyph_match (hb_codepoint_t glyph_id, const USHORT &value, char *data) {
192   return glyph_id == value;
193 }
194
195 struct ContextFormat1 {
196
197   friend struct Context;
198
199   private:
200
201   inline bool apply (LOOKUP_ARGS_DEF, apply_lookup_func_t apply_func) const {
202
203     unsigned int index = (this+coverage) (IN_CURGLYPH ());
204     const RuleSet &rule_set = this+ruleSet[index];
205     struct ContextLookupContext context = {
206       glyph_match, NULL,
207       apply_func
208     };
209     return rule_set.apply (LOOKUP_ARGS, context);
210   }
211
212   private:
213   USHORT        format;                 /* Format identifier--format = 1 */
214   OffsetTo<Coverage>
215                 coverage;               /* Offset to Coverage table--from
216                                          * beginning of table */
217   OffsetArrayOf<RuleSet>
218                 ruleSet;                /* Array of RuleSet tables
219                                          * ordered by Coverage Index */
220 };
221 ASSERT_SIZE (ContextFormat1, 6);
222
223
224 static inline bool class_match (hb_codepoint_t glyph_id, const USHORT &value, char *data) {
225   const ClassDef &class_def = * (const ClassDef *) data;
226   return class_def.get_class (glyph_id) == value;
227 }
228
229 struct ContextFormat2 {
230
231   friend struct Context;
232
233   private:
234
235   inline bool apply (LOOKUP_ARGS_DEF, apply_lookup_func_t apply_func) const {
236
237     unsigned int index = (this+coverage) (IN_CURGLYPH ());
238     const RuleSet &rule_set = this+ruleSet[index];
239     /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
240      * them across subrule lookups.  Not sure it's worth it.
241      */
242     struct ContextLookupContext context = {
243       class_match, (char *) &(this+classDef),
244       apply_func
245     };
246     return rule_set.apply (LOOKUP_ARGS, context);
247   }
248
249   private:
250   USHORT        format;                 /* Format identifier--format = 2 */
251   OffsetTo<Coverage>
252                 coverage;               /* Offset to Coverage table--from
253                                          * beginning of table */
254   OffsetTo<ClassDef>
255                 classDef;               /* Offset to glyph ClassDef table--from
256                                          * beginning of table */
257   OffsetArrayOf<RuleSet>
258                 ruleSet;                /* Array of RuleSet tables
259                                          * ordered by class */
260 };
261 ASSERT_SIZE (ContextFormat2, 8);
262
263
264 static inline bool coverage_match (hb_codepoint_t glyph_id, const USHORT &value, char *data) {
265   const OffsetTo<Coverage> &coverage = * (const OffsetTo<Coverage> *) &value;
266   return (data+coverage) (glyph_id) != NOT_COVERED;
267 }
268
269 struct ContextFormat3 {
270
271   friend struct Context;
272
273   private:
274
275   /* Coverage tables, in glyph sequence order */
276   DEFINE_OFFSET_ARRAY_TYPE (Coverage, coverage, glyphCount);
277
278   inline bool apply_coverage (LOOKUP_ARGS_DEF, apply_lookup_func_t apply_func) const {
279     const LookupRecord *record = (const LookupRecord *) ((const char *) coverage + sizeof (coverage[0]) * glyphCount);
280     struct ContextLookupContext context = {
281       coverage_match, (char *) this,
282       apply_func
283     };
284     return context_lookup (LOOKUP_ARGS,
285                            glyphCount,
286                            recordCount,
287                            (const USHORT *) (coverage + 1),
288                            record,
289                            context);
290   }
291
292   inline bool apply (LOOKUP_ARGS_DEF, apply_lookup_func_t apply_func) const {
293
294     if ((*this)[0].get_coverage (IN_CURGLYPH () == NOT_COVERED))
295       return false;
296
297     return apply_coverage (LOOKUP_ARGS, apply_func);
298   }
299
300   private:
301   USHORT        format;                 /* Format identifier--format = 3 */
302   USHORT        glyphCount;             /* Number of glyphs in the input glyph
303                                          * sequence */
304   USHORT        recordCount;            /* Number of LookupRecords */
305   Offset        coverage[];             /* Array of offsets to Coverage
306                                          * table--from beginning of
307                                          * table--in glyph
308                                          * sequence order */
309   LookupRecord  lookupRecord[];         /* Array of LookupRecords--in
310                                          * design order */
311 };
312 ASSERT_SIZE (ContextFormat3, 6);
313
314 struct Context {
315
316   protected:
317   bool apply (LOOKUP_ARGS_DEF, apply_lookup_func_t apply_func) const {
318     switch (u.format) {
319     case 1: return u.format1->apply (LOOKUP_ARGS, apply_func);
320     case 2: return u.format2->apply (LOOKUP_ARGS, apply_func);
321     case 3: return u.format3->apply (LOOKUP_ARGS, apply_func);
322     default:return false;
323     }
324   }
325
326   private:
327   union {
328   USHORT                format;         /* Format identifier */
329   ContextFormat1        format1[];
330   ContextFormat2        format2[];
331   ContextFormat3        format3[];
332   } u;
333 };
334 ASSERT_SIZE (Context, 2);
335
336 /*
337  * GSUB/GPOS Common
338  */
339
340 struct GSUBGPOS {
341   static const hb_tag_t GSUBTag         = HB_TAG ('G','S','U','B');
342   static const hb_tag_t GPOSTag         = HB_TAG ('G','P','O','S');
343
344   STATIC_DEFINE_GET_FOR_DATA (GSUBGPOS);
345   /* XXX check version here? */
346
347   DEFINE_TAG_LIST_INTERFACE (Script,  script ); /* get_script_count (), get_script (i), get_script_tag (i) */
348   DEFINE_TAG_LIST_INTERFACE (Feature, feature); /* get_feature_count(), get_feature(i), get_feature_tag(i) */
349   DEFINE_LIST_INTERFACE     (Lookup,  lookup ); /* get_lookup_count (), get_lookup (i) */
350
351   // LONGTERMTODO bsearch
352   DEFINE_TAG_FIND_INTERFACE (Script,  script ); /* find_script_index (), get_script_by_tag (tag) */
353   DEFINE_TAG_FIND_INTERFACE (Feature, feature); /* find_feature_index(), get_feature_by_tag(tag) */
354
355   private:
356   Fixed_Version version;        /* Version of the GSUB/GPOS table--initially set
357                                  * to 0x00010000 */
358   OffsetTo<ScriptList>
359                 scriptList;     /* ScriptList table */
360   OffsetTo<FeatureList>
361                 featureList;    /* FeatureList table */
362   OffsetTo<LookupList>
363                 lookupList;     /* LookupList table */
364 };
365 ASSERT_SIZE (GSUBGPOS, 10);
366
367
368 #endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_H */