hb-ot-layout.cc 29.3 KB
Newer Older
B
Behdad Esfahbod 已提交
1
/*
B
Behdad Esfahbod 已提交
2 3 4
 * Copyright © 1998-2004  David Turner and Werner Lemberg
 * Copyright © 2006  Behdad Esfahbod
 * Copyright © 2007,2008,2009  Red Hat, Inc.
5
 * Copyright © 2012,2013  Google, Inc.
B
Behdad Esfahbod 已提交
6
 *
B
Behdad Esfahbod 已提交
7
 *  This is part of HarfBuzz, a text shaping library.
B
Behdad Esfahbod 已提交
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Red Hat Author(s): Behdad Esfahbod
B
Behdad Esfahbod 已提交
28
 * Google Author(s): Behdad Esfahbod
B
Behdad Esfahbod 已提交
29 30
 */

B
Behdad Esfahbod 已提交
31
#include "hb-open-type-private.hh"
32
#include "hb-ot-layout-private.hh"
B
Behdad Esfahbod 已提交
33

34 35 36
#include "hb-ot-layout-gdef-table.hh"
#include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gpos-table.hh"
37
#include "hb-ot-layout-jstf-table.hh"
B
Behdad Esfahbod 已提交
38

B
Behdad Esfahbod 已提交
39 40
#include "hb-ot-map-private.hh"

B
Behdad Esfahbod 已提交
41
#include <stdlib.h>
B
Behdad Esfahbod 已提交
42 43
#include <string.h>

B
Behdad Esfahbod 已提交
44

45
HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
46

B
Behdad Esfahbod 已提交
47
hb_ot_layout_t *
48
_hb_ot_layout_create (hb_face_t *face)
49
{
B
Behdad Esfahbod 已提交
50
  hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
B
Minor  
Behdad Esfahbod 已提交
51 52
  if (unlikely (!layout))
    return NULL;
53

54 55
  layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF));
  layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob);
B
Behdad Esfahbod 已提交
56

57 58
  layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
  layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob);
B
Behdad Esfahbod 已提交
59

60 61
  layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
  layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
B
Behdad Esfahbod 已提交
62

63 64 65
  layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
  layout->gpos_lookup_count = layout->gpos->get_lookup_count ();

66 67
  layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
  layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
68

69 70
  if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) ||
		(layout->gpos_lookup_count && !layout->gpos_accels)))
71 72 73 74 75
  {
    _hb_ot_layout_destroy (layout);
    return NULL;
  }

76
  for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
77
    layout->gsub_accels[i].init (layout->gsub->get_lookup (i));
78
  for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
79
    layout->gpos_accels[i].init (layout->gpos->get_lookup (i));
80

B
Behdad Esfahbod 已提交
81
  return layout;
B
Behdad Esfahbod 已提交
82 83
}

84
void
85
_hb_ot_layout_destroy (hb_ot_layout_t *layout)
B
Behdad Esfahbod 已提交
86
{
87
  for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
88
    layout->gsub_accels[i].fini ();
89
  for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
90
    layout->gpos_accels[i].fini ();
91 92 93

  free (layout->gsub_accels);
  free (layout->gpos_accels);
94

B
Bradley Grainger 已提交
95 96 97 98
  hb_blob_destroy (layout->gdef_blob);
  hb_blob_destroy (layout->gsub_blob);
  hb_blob_destroy (layout->gpos_blob);

B
Behdad Esfahbod 已提交
99
  free (layout);
100
}
B
Behdad Esfahbod 已提交
101

102
static inline const OT::GDEF&
103 104
_get_gdef (hb_face_t *face)
{
105
  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF);
106
  return *hb_ot_layout_from_face (face)->gdef;
107
}
108
static inline const OT::GSUB&
109
_get_gsub (hb_face_t *face)
B
Behdad Esfahbod 已提交
110
{
111
  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB);
112
  return *hb_ot_layout_from_face (face)->gsub;
B
Behdad Esfahbod 已提交
113
}
114
static inline const OT::GPOS&
115
_get_gpos (hb_face_t *face)
B
Behdad Esfahbod 已提交
116
{
117
  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS);
118
  return *hb_ot_layout_from_face (face)->gpos;
B
Behdad Esfahbod 已提交
119 120
}

121

B
Behdad Esfahbod 已提交
122 123 124 125 126
/*
 * GDEF
 */

hb_bool_t
127
hb_ot_layout_has_glyph_classes (hb_face_t *face)
B
Behdad Esfahbod 已提交
128
{
129
  return _get_gdef (face).has_glyph_classes ();
B
Behdad Esfahbod 已提交
130 131
}

S
Sascha Brawer 已提交
132
/**
B
Behdad Esfahbod 已提交
133 134
 * hb_ot_layout_get_glyph_class:
 *
S
Sascha Brawer 已提交
135 136
 * Since: 0.9.7
 **/
137 138 139 140 141 142
hb_ot_layout_glyph_class_t
hb_ot_layout_get_glyph_class (hb_face_t      *face,
			      hb_codepoint_t  glyph)
{
  return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph);
}
B
Behdad Esfahbod 已提交
143

S
Sascha Brawer 已提交
144
/**
B
Behdad Esfahbod 已提交
145 146
 * hb_ot_layout_get_glyphs_in_class:
 *
S
Sascha Brawer 已提交
147 148
 * Since: 0.9.7
 **/
149 150 151 152 153 154 155 156
void
hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
				  hb_ot_layout_glyph_class_t  klass,
				  hb_set_t                   *glyphs /* OUT */)
{
  return _get_gdef (face).get_glyphs_in_class (klass, glyphs);
}

B
Behdad Esfahbod 已提交
157
unsigned int
158
hb_ot_layout_get_attach_points (hb_face_t      *face,
B
Behdad Esfahbod 已提交
159
				hb_codepoint_t  glyph,
B
Behdad Esfahbod 已提交
160
				unsigned int    start_offset,
B
Behdad Esfahbod 已提交
161 162 163
				unsigned int   *point_count /* IN/OUT */,
				unsigned int   *point_array /* OUT */)
{
B
Behdad Esfahbod 已提交
164
  return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
B
Behdad Esfahbod 已提交
165 166
}

B
Behdad Esfahbod 已提交
167
unsigned int
168 169 170 171 172 173
hb_ot_layout_get_ligature_carets (hb_font_t      *font,
				  hb_direction_t  direction,
				  hb_codepoint_t  glyph,
				  unsigned int    start_offset,
				  unsigned int   *caret_count /* IN/OUT */,
				  int            *caret_array /* OUT */)
B
Behdad Esfahbod 已提交
174
{
175
  return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
B
Behdad Esfahbod 已提交
176 177
}

178

179 180 181 182
/*
 * GSUB/GPOS
 */

183
static const OT::GSUBGPOS&
184 185
get_gsubgpos_table (hb_face_t *face,
		    hb_tag_t   table_tag)
186
{
187
  switch (table_tag) {
188 189
    case HB_OT_TAG_GSUB: return _get_gsub (face);
    case HB_OT_TAG_GPOS: return _get_gpos (face);
190
    default:             return OT::Null(OT::GSUBGPOS);
191 192 193 194
  }
}


B
Behdad Esfahbod 已提交
195
unsigned int
196 197
hb_ot_layout_table_get_script_tags (hb_face_t    *face,
				    hb_tag_t      table_tag,
B
Behdad Esfahbod 已提交
198
				    unsigned int  start_offset,
199 200
				    unsigned int *script_count /* IN/OUT */,
				    hb_tag_t     *script_tags /* OUT */)
201
{
202
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
203

B
Behdad Esfahbod 已提交
204
  return g.get_script_tags (start_offset, script_count, script_tags);
205 206
}

B
Minor  
Behdad Esfahbod 已提交
207 208
#define HB_OT_TAG_LATIN_SCRIPT		HB_TAG ('l', 'a', 't', 'n')

209
hb_bool_t
210 211 212 213
hb_ot_layout_table_find_script (hb_face_t    *face,
				hb_tag_t      table_tag,
				hb_tag_t      script_tag,
				unsigned int *script_index)
214
{
215 216
  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
217 218

  if (g.find_script_index (script_tag, script_index))
219
    return true;
220 221

  /* try finding 'DFLT' */
222
  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
223
    return false;
224

225 226
  /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
   * including many versions of DejaVu Sans Mono! */
227
  if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
228
    return false;
B
Behdad Esfahbod 已提交
229

B
Minor  
Behdad Esfahbod 已提交
230 231 232 233 234
  /* try with 'latn'; some old fonts put their features there even though
     they're really trying to support Thai, for example :( */
  if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index))
    return false;

B
Behdad Esfahbod 已提交
235
  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
236
  return false;
B
Behdad Esfahbod 已提交
237 238 239 240 241 242
}

hb_bool_t
hb_ot_layout_table_choose_script (hb_face_t      *face,
				  hb_tag_t        table_tag,
				  const hb_tag_t *script_tags,
B
Behdad Esfahbod 已提交
243 244
				  unsigned int   *script_index,
				  hb_tag_t       *chosen_script)
B
Behdad Esfahbod 已提交
245
{
246 247
  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
B
Behdad Esfahbod 已提交
248 249 250

  while (*script_tags)
  {
B
Behdad Esfahbod 已提交
251 252 253
    if (g.find_script_index (*script_tags, script_index)) {
      if (chosen_script)
        *chosen_script = *script_tags;
254
      return true;
B
Behdad Esfahbod 已提交
255
    }
B
Behdad Esfahbod 已提交
256 257 258 259
    script_tags++;
  }

  /* try finding 'DFLT' */
B
Behdad Esfahbod 已提交
260 261 262
  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
    if (chosen_script)
      *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
263
    return false;
B
Behdad Esfahbod 已提交
264
  }
B
Behdad Esfahbod 已提交
265 266

  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
B
Behdad Esfahbod 已提交
267 268 269
  if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
    if (chosen_script)
      *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
270
    return false;
B
Behdad Esfahbod 已提交
271
  }
272

273 274 275 276 277
  /* try with 'latn'; some old fonts put their features there even though
     they're really trying to support Thai, for example :( */
  if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
    if (chosen_script)
      *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
278
    return false;
279 280
  }

281
  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
B
Behdad Esfahbod 已提交
282 283
  if (chosen_script)
    *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
284
  return false;
285 286
}

B
Behdad Esfahbod 已提交
287
unsigned int
288 289
hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
				     hb_tag_t      table_tag,
B
Behdad Esfahbod 已提交
290
				     unsigned int  start_offset,
291 292
				     unsigned int *feature_count /* IN/OUT */,
				     hb_tag_t     *feature_tags /* OUT */)
293
{
294
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
295

B
Behdad Esfahbod 已提交
296
  return g.get_feature_tags (start_offset, feature_count, feature_tags);
297 298
}

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
hb_bool_t
hb_ot_layout_table_find_feature (hb_face_t    *face,
				 hb_tag_t      table_tag,
				 hb_tag_t      feature_tag,
				 unsigned int *feature_index)
{
  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);

  unsigned int num_features = g.get_feature_count ();
  for (unsigned int i = 0; i < num_features; i++)
  {
    if (feature_tag == g.get_feature_tag (i)) {
      if (feature_index) *feature_index = i;
      return true;
    }
  }

  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
  return false;
}

321

B
Behdad Esfahbod 已提交
322
unsigned int
323 324 325
hb_ot_layout_script_get_language_tags (hb_face_t    *face,
				       hb_tag_t      table_tag,
				       unsigned int  script_index,
B
Behdad Esfahbod 已提交
326
				       unsigned int  start_offset,
327 328
				       unsigned int *language_count /* IN/OUT */,
				       hb_tag_t     *language_tags /* OUT */)
329
{
330
  const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
331

B
Behdad Esfahbod 已提交
332
  return s.get_lang_sys_tags (start_offset, language_count, language_tags);
333 334 335
}

hb_bool_t
336 337 338 339 340
hb_ot_layout_script_find_language (hb_face_t    *face,
				   hb_tag_t      table_tag,
				   unsigned int  script_index,
				   hb_tag_t      language_tag,
				   unsigned int *language_index)
341
{
342 343
  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
  const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
344

345
  if (s.find_lang_sys_index (language_tag, language_index))
346
    return true;
347 348

  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
349
  if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
350
    return false;
351

352
  if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
353
  return false;
354
}
355

B
Behdad Esfahbod 已提交
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
hb_bool_t
hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
						  hb_tag_t      table_tag,
						  unsigned int  script_index,
						  unsigned int  language_index,
						  unsigned int *feature_index)
{
  return hb_ot_layout_language_get_required_feature (face,
						     table_tag,
						     script_index,
						     language_index,
						     feature_index,
						     NULL);
}

S
Sascha Brawer 已提交
371
/**
B
Behdad Esfahbod 已提交
372 373
 * hb_ot_layout_language_get_required_feature:
 *
S
Sascha Brawer 已提交
374 375
 * Since: 0.9.30
 **/
376
hb_bool_t
377 378 379 380 381 382
hb_ot_layout_language_get_required_feature (hb_face_t    *face,
					    hb_tag_t      table_tag,
					    unsigned int  script_index,
					    unsigned int  language_index,
					    unsigned int *feature_index,
					    hb_tag_t     *feature_tag)
383
{
384 385
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
386

387 388 389
  unsigned int index = l.get_required_feature_index ();
  if (feature_index) *feature_index = index;
  if (feature_tag) *feature_tag = g.get_feature_tag (index);
390

391 392
  return l.has_required_feature ();
}
393

B
Behdad Esfahbod 已提交
394
unsigned int
395 396 397 398
hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
					   hb_tag_t      table_tag,
					   unsigned int  script_index,
					   unsigned int  language_index,
B
Behdad Esfahbod 已提交
399
					   unsigned int  start_offset,
400 401
					   unsigned int *feature_count /* IN/OUT */,
					   unsigned int *feature_indexes /* OUT */)
402
{
403 404
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
405

B
Behdad Esfahbod 已提交
406
  return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
407 408
}

B
Behdad Esfahbod 已提交
409
unsigned int
410 411 412 413
hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
					hb_tag_t      table_tag,
					unsigned int  script_index,
					unsigned int  language_index,
B
Behdad Esfahbod 已提交
414
					unsigned int  start_offset,
415 416
					unsigned int *feature_count /* IN/OUT */,
					hb_tag_t     *feature_tags /* OUT */)
417
{
418 419
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
420

421
  ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
B
Behdad Esfahbod 已提交
422
  unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
423

B
Behdad Esfahbod 已提交
424 425 426 427 428
  if (feature_tags) {
    unsigned int count = *feature_count;
    for (unsigned int i = 0; i < count; i++)
      feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
  }
429 430

  return ret;
431 432 433 434
}


hb_bool_t
435 436 437 438 439 440
hb_ot_layout_language_find_feature (hb_face_t    *face,
				    hb_tag_t      table_tag,
				    unsigned int  script_index,
				    unsigned int  language_index,
				    hb_tag_t      feature_tag,
				    unsigned int *feature_index)
441
{
442 443 444
  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
445

B
Behdad Esfahbod 已提交
446 447
  unsigned int num_features = l.get_feature_count ();
  for (unsigned int i = 0; i < num_features; i++) {
448 449 450 451
    unsigned int f_index = l.get_feature_index (i);

    if (feature_tag == g.get_feature_tag (f_index)) {
      if (feature_index) *feature_index = f_index;
452
      return true;
453 454
    }
  }
455

456
  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
457
  return false;
458
}
459

S
Sascha Brawer 已提交
460
/**
K
Khaled Hosny 已提交
461 462
 * hb_ot_layout_feature_get_lookups:
 *
S
Sascha Brawer 已提交
463 464
 * Since: 0.9.7
 **/
B
Behdad Esfahbod 已提交
465
unsigned int
466 467 468 469 470 471
hb_ot_layout_feature_get_lookups (hb_face_t    *face,
				  hb_tag_t      table_tag,
				  unsigned int  feature_index,
				  unsigned int  start_offset,
				  unsigned int *lookup_count /* IN/OUT */,
				  unsigned int *lookup_indexes /* OUT */)
472
{
473 474
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
  const OT::Feature &f = g.get_feature (feature_index);
475

B
Behdad Esfahbod 已提交
476
  return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
477 478
}

S
Sascha Brawer 已提交
479
/**
K
Khaled Hosny 已提交
480 481
 * hb_ot_layout_table_get_lookup_count:
 *
S
Sascha Brawer 已提交
482 483
 * Since: 0.9.22
 **/
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
unsigned int
hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
				     hb_tag_t      table_tag)
{
  switch (table_tag)
  {
    case HB_OT_TAG_GSUB:
    {
      return hb_ot_layout_from_face (face)->gsub_lookup_count;
    }
    case HB_OT_TAG_GPOS:
    {
      return hb_ot_layout_from_face (face)->gpos_lookup_count;
    }
  }
B
Behdad Esfahbod 已提交
499
  return 0;
500 501
}

502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
static void
_hb_ot_layout_collect_lookups_lookups (hb_face_t      *face,
				       hb_tag_t        table_tag,
				       unsigned int    feature_index,
				       hb_set_t       *lookup_indexes /* OUT */)
{
  unsigned int lookup_indices[32];
  unsigned int offset, len;

  offset = 0;
  do {
    len = ARRAY_LENGTH (lookup_indices);
    hb_ot_layout_feature_get_lookups (face,
				      table_tag,
				      feature_index,
				      offset, &len,
				      lookup_indices);

    for (unsigned int i = 0; i < len; i++)
      lookup_indexes->add (lookup_indices[i]);

    offset += len;
  } while (len == ARRAY_LENGTH (lookup_indices));
}

static void
_hb_ot_layout_collect_lookups_features (hb_face_t      *face,
					hb_tag_t        table_tag,
					unsigned int    script_index,
					unsigned int    language_index,
					const hb_tag_t *features,
					hb_set_t       *lookup_indexes /* OUT */)
{
  if (!features)
  {
537
    unsigned int required_feature_index;
538 539 540 541 542 543
    if (hb_ot_layout_language_get_required_feature (face,
						    table_tag,
						    script_index,
						    language_index,
						    &required_feature_index,
						    NULL))
544 545 546 547 548
      _hb_ot_layout_collect_lookups_lookups (face,
					     table_tag,
					     required_feature_index,
					     lookup_indexes);

549
    /* All features */
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
    unsigned int feature_indices[32];
    unsigned int offset, len;

    offset = 0;
    do {
      len = ARRAY_LENGTH (feature_indices);
      hb_ot_layout_language_get_feature_indexes (face,
						 table_tag,
						 script_index,
						 language_index,
						 offset, &len,
						 feature_indices);

      for (unsigned int i = 0; i < len; i++)
	_hb_ot_layout_collect_lookups_lookups (face,
					       table_tag,
					       feature_indices[i],
					       lookup_indexes);

      offset += len;
    } while (len == ARRAY_LENGTH (feature_indices));
  }
  else
  {
574 575 576
    for (; *features; features++)
    {
      unsigned int feature_index;
B
Behdad Esfahbod 已提交
577 578 579 580 581 582 583 584 585 586
      if (hb_ot_layout_language_find_feature (face,
					      table_tag,
					      script_index,
					      language_index,
					      *features,
					      &feature_index))
        _hb_ot_layout_collect_lookups_lookups (face,
					       table_tag,
					       feature_index,
					       lookup_indexes);
587 588 589 590 591 592 593 594 595 596 597 598
    }
  }
}

static void
_hb_ot_layout_collect_lookups_languages (hb_face_t      *face,
					 hb_tag_t        table_tag,
					 unsigned int    script_index,
					 const hb_tag_t *languages,
					 const hb_tag_t *features,
					 hb_set_t       *lookup_indexes /* OUT */)
{
599 600 601 602 603 604 605
  _hb_ot_layout_collect_lookups_features (face,
					  table_tag,
					  script_index,
					  HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
					  features,
					  lookup_indexes);

606 607 608
  if (!languages)
  {
    /* All languages */
B
Behdad Esfahbod 已提交
609 610 611 612
    unsigned int count = hb_ot_layout_script_get_language_tags (face,
								table_tag,
								script_index,
								0, NULL, NULL);
613
    for (unsigned int language_index = 0; language_index < count; language_index++)
B
Behdad Esfahbod 已提交
614 615 616 617 618 619
      _hb_ot_layout_collect_lookups_features (face,
					      table_tag,
					      script_index,
					      language_index,
					      features,
					      lookup_indexes);
620 621 622
  }
  else
  {
623 624 625
    for (; *languages; languages++)
    {
      unsigned int language_index;
B
Behdad Esfahbod 已提交
626 627 628 629 630 631 632 633 634 635 636
      if (hb_ot_layout_script_find_language (face,
					     table_tag,
					     script_index,
					     *languages,
					     &language_index))
        _hb_ot_layout_collect_lookups_features (face,
						table_tag,
						script_index,
						language_index,
						features,
						lookup_indexes);
637 638 639 640
    }
  }
}

S
Sascha Brawer 已提交
641
/**
K
Khaled Hosny 已提交
642 643
 * hb_ot_layout_collect_lookups:
 *
S
Sascha Brawer 已提交
644 645
 * Since: 0.9.8
 **/
646 647 648 649 650 651 652 653 654 655 656
void
hb_ot_layout_collect_lookups (hb_face_t      *face,
			      hb_tag_t        table_tag,
			      const hb_tag_t *scripts,
			      const hb_tag_t *languages,
			      const hb_tag_t *features,
			      hb_set_t       *lookup_indexes /* OUT */)
{
  if (!scripts)
  {
    /* All scripts */
B
Behdad Esfahbod 已提交
657 658 659
    unsigned int count = hb_ot_layout_table_get_script_tags (face,
							     table_tag,
							     0, NULL, NULL);
660
    for (unsigned int script_index = 0; script_index < count; script_index++)
B
Behdad Esfahbod 已提交
661 662 663 664 665 666
      _hb_ot_layout_collect_lookups_languages (face,
					       table_tag,
					       script_index,
					       languages,
					       features,
					       lookup_indexes);
667 668 669
  }
  else
  {
670 671 672
    for (; *scripts; scripts++)
    {
      unsigned int script_index;
B
Behdad Esfahbod 已提交
673 674 675 676 677 678 679 680 681 682
      if (hb_ot_layout_table_find_script (face,
					  table_tag,
					  *scripts,
					  &script_index))
        _hb_ot_layout_collect_lookups_languages (face,
						 table_tag,
						 script_index,
						 languages,
						 features,
						 lookup_indexes);
683 684 685 686
    }
  }
}

S
Sascha Brawer 已提交
687
/**
K
Khaled Hosny 已提交
688 689
 * hb_ot_layout_lookup_collect_glyphs:
 *
S
Sascha Brawer 已提交
690 691
 * Since: 0.9.7
 **/
692 693 694 695 696 697 698 699 700 701 702
void
hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
				    hb_tag_t      table_tag,
				    unsigned int  lookup_index,
				    hb_set_t     *glyphs_before, /* OUT. May be NULL */
				    hb_set_t     *glyphs_input,  /* OUT. May be NULL */
				    hb_set_t     *glyphs_after,  /* OUT. May be NULL */
				    hb_set_t     *glyphs_output  /* OUT. May be NULL */)
{
  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;

B
Behdad Esfahbod 已提交
703 704 705 706 707
  OT::hb_collect_glyphs_context_t c (face,
				     glyphs_before,
				     glyphs_input,
				     glyphs_after,
				     glyphs_output);
708

709 710
  switch (table_tag)
  {
711 712
    case HB_OT_TAG_GSUB:
    {
713
      const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
B
Behdad Esfahbod 已提交
714
      l.collect_glyphs (&c);
715 716 717 718
      return;
    }
    case HB_OT_TAG_GPOS:
    {
719
      const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
B
Behdad Esfahbod 已提交
720
      l.collect_glyphs (&c);
721 722 723 724 725
      return;
    }
  }
}

B
Behdad Esfahbod 已提交
726 727

/*
728
 * OT::GSUB
B
Behdad Esfahbod 已提交
729 730 731
 */

hb_bool_t
732 733
hb_ot_layout_has_substitution (hb_face_t *face)
{
734
  return &_get_gsub (face) != &OT::Null(OT::GSUB);
735 736
}

S
Sascha Brawer 已提交
737
/**
K
Khaled Hosny 已提交
738 739
 * hb_ot_layout_lookup_would_substitute:
 *
S
Sascha Brawer 已提交
740 741
 * Since: 0.9.7
 **/
742
hb_bool_t
743
hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
744
				      unsigned int          lookup_index,
745 746
				      const hb_codepoint_t *glyphs,
				      unsigned int          glyphs_length,
747
				      hb_bool_t             zero_context)
748
{
749
  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
750
  return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
751 752
}

B
Behdad Esfahbod 已提交
753
hb_bool_t
754
hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
755
					   unsigned int          lookup_index,
B
Behdad Esfahbod 已提交
756 757
					   const hb_codepoint_t *glyphs,
					   unsigned int          glyphs_length,
758
					   hb_bool_t             zero_context)
B
Behdad Esfahbod 已提交
759
{
760
  if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
761
  OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context);
B
Behdad Esfahbod 已提交
762 763 764

  const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);

765
  return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index]);
B
Behdad Esfahbod 已提交
766 767
}

768
void
769
hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
770
{
771
  OT::GSUB::substitute_start (font, buffer);
772 773 774
}

void
775
hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
776
{
777
  OT::GSUB::substitute_finish (font, buffer);
778 779
}

S
Sascha Brawer 已提交
780
/**
K
Khaled Hosny 已提交
781 782
 * hb_ot_layout_lookup_substitute_closure:
 *
S
Sascha Brawer 已提交
783 784
 * Since: 0.9.7
 **/
B
Behdad Esfahbod 已提交
785
void
786
hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
787 788
				        unsigned int  lookup_index,
				        hb_set_t     *glyphs)
789
{
790 791 792 793 794
  OT::hb_closure_context_t c (face, glyphs);

  const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);

  l.closure (&c);
795
}
796

797
/*
798
 * OT::GPOS
799 800 801
 */

hb_bool_t
802 803
hb_ot_layout_has_positioning (hb_face_t *face)
{
804
  return &_get_gpos (face) != &OT::Null(OT::GPOS);
805 806
}

807
void
808
hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
809
{
810
  OT::GPOS::position_start (font, buffer);
811 812
}

813
void
814
hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
815
{
816
  OT::GPOS::position_finish (font, buffer);
817
}
818

S
Sascha Brawer 已提交
819
/**
K
Khaled Hosny 已提交
820 821
 * hb_ot_layout_get_size_params:
 *
B
Behdad Esfahbod 已提交
822
 * Since: 0.9.10
S
Sascha Brawer 已提交
823
 **/
824
hb_bool_t
825 826 827 828 829 830
hb_ot_layout_get_size_params (hb_face_t    *face,
			      unsigned int *design_size,       /* OUT.  May be NULL */
			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
			      unsigned int *range_start,       /* OUT.  May be NULL */
			      unsigned int *range_end          /* OUT.  May be NULL */)
831 832
{
  const OT::GPOS &gpos = _get_gpos (face);
833
  const hb_tag_t tag = HB_TAG ('s','i','z','e');
834

835 836 837
  unsigned int num_features = gpos.get_feature_count ();
  for (unsigned int i = 0; i < num_features; i++)
  {
838
    if (tag == gpos.get_feature_tag (i))
839
    {
840
      const OT::Feature &f = gpos.get_feature (i);
841
      const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
842

843
      if (params.designSize)
844
      {
845
#define PARAM(a, A) if (a) *a = params.A
846 847 848 849 850 851
	PARAM (design_size, designSize);
	PARAM (subfamily_id, subfamilyID);
	PARAM (subfamily_name_id, subfamilyNameID);
	PARAM (range_start, rangeStart);
	PARAM (range_end, rangeEnd);
#undef PARAM
852 853 854

	return true;
      }
855 856 857
    }
  }

858
#define PARAM(a, A) if (a) *a = 0
859 860 861 862 863
  PARAM (design_size, designSize);
  PARAM (subfamily_id, subfamilyID);
  PARAM (subfamily_name_id, subfamilyNameID);
  PARAM (range_start, rangeStart);
  PARAM (range_end, rangeEnd);
864
#undef PARAM
865

866
  return false;
867
}
B
Behdad Esfahbod 已提交
868 869 870


/*
B
Behdad Esfahbod 已提交
871
 * Parts of different types are implemented here such that they have direct
B
Behdad Esfahbod 已提交
872 873 874 875
 * access to GSUB/GPOS lookups.
 */


B
Behdad Esfahbod 已提交
876 877 878
struct GSUBProxy
{
  static const unsigned int table_index = 0;
B
Behdad Esfahbod 已提交
879
  static const bool inplace = false;
B
Behdad Esfahbod 已提交
880 881 882 883 884 885 886 887 888 889 890 891 892
  typedef OT::SubstLookup Lookup;

  GSUBProxy (hb_face_t *face) :
    table (*hb_ot_layout_from_face (face)->gsub),
    accels (hb_ot_layout_from_face (face)->gsub_accels) {}

  const OT::GSUB &table;
  const hb_ot_layout_lookup_accelerator_t *accels;
};

struct GPOSProxy
{
  static const unsigned int table_index = 1;
B
Behdad Esfahbod 已提交
893
  static const bool inplace = true;
B
Behdad Esfahbod 已提交
894 895 896 897 898 899 900 901 902 903 904
  typedef OT::PosLookup Lookup;

  GPOSProxy (hb_face_t *face) :
    table (*hb_ot_layout_from_face (face)->gpos),
    accels (hb_ot_layout_from_face (face)->gpos_accels) {}

  const OT::GPOS &table;
  const hb_ot_layout_lookup_accelerator_t *accels;
};


905 906 907 908 909 910 911 912
template <typename Obj>
static inline bool
apply_forward (OT::hb_apply_context_t *c,
	       const Obj &obj,
	       const hb_ot_layout_lookup_accelerator_t &accel)
{
  bool ret = false;
  hb_buffer_t *buffer = c->buffer;
913
  while (buffer->idx < buffer->len && !buffer->in_error)
914 915 916
  {
    if (accel.may_have (buffer->cur().codepoint) &&
	(buffer->cur().mask & c->lookup_mask) &&
B
Minor  
Behdad Esfahbod 已提交
917
	c->check_glyph_property (&buffer->cur(), c->lookup_props) &&
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
	obj.apply (c))
      ret = true;
    else
      buffer->next_glyph ();
  }
  return ret;
}

template <typename Obj>
static inline bool
apply_backward (OT::hb_apply_context_t *c,
		const Obj &obj,
		const hb_ot_layout_lookup_accelerator_t &accel)
{
  bool ret = false;
  hb_buffer_t *buffer = c->buffer;
  do
  {
    if (accel.may_have (buffer->cur().codepoint) &&
	(buffer->cur().mask & c->lookup_mask) &&
B
Minor  
Behdad Esfahbod 已提交
938
	c->check_glyph_property (&buffer->cur(), c->lookup_props) &&
939 940 941 942 943 944 945 946 947 948
	obj.apply (c))
      ret = true;
    /* The reverse lookup doesn't "advance" cursor (for good reason). */
    buffer->idx--;

  }
  while ((int) buffer->idx >= 0);
  return ret;
}

B
Behdad Esfahbod 已提交
949 950
struct hb_apply_forward_context_t :
       OT::hb_dispatch_context_t<hb_apply_forward_context_t, bool, HB_DEBUG_APPLY>
951
{
B
Behdad Esfahbod 已提交
952
  inline const char *get_name (void) { return "APPLY_FWD"; }
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
  template <typename T>
  inline return_t dispatch (const T &obj) { return apply_forward (c, obj, accel); }
  static return_t default_return_value (void) { return false; }
  bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return true; }

  hb_apply_forward_context_t (OT::hb_apply_context_t *c_,
			      const hb_ot_layout_lookup_accelerator_t &accel_) :
				c (c_),
				accel (accel_),
				debug_depth (0) {}

  OT::hb_apply_context_t *c;
  const hb_ot_layout_lookup_accelerator_t &accel;
  unsigned int debug_depth;
};

969
template <typename Proxy>
970
static inline void
971 972 973 974
apply_string (OT::hb_apply_context_t *c,
	      const typename Proxy::Lookup &lookup,
	      const hb_ot_layout_lookup_accelerator_t &accel)
{
B
Behdad Esfahbod 已提交
975
  hb_buffer_t *buffer = c->buffer;
976

977
  if (unlikely (!buffer->len || !c->lookup_mask))
978
    return;
979

980
  c->set_lookup_props (lookup.get_props ());
981 982 983 984 985

  if (likely (!lookup.is_reverse ()))
  {
    /* in/out forward substitution/positioning */
    if (Proxy::table_index == 0)
B
Behdad Esfahbod 已提交
986 987
      buffer->clear_output ();
    buffer->idx = 0;
988

989 990 991 992 993 994 995 996 997
    bool ret;
    if (lookup.get_subtable_count () == 1)
    {
      hb_apply_forward_context_t c_forward (c, accel);
      ret = lookup.dispatch (&c_forward);
    }
    else
      ret = apply_forward (c, lookup, accel);
    if (ret)
998
    {
B
Behdad Esfahbod 已提交
999
      if (!Proxy::inplace)
B
Behdad Esfahbod 已提交
1000
	buffer->swap_buffers ();
1001
      else
1002
	assert (!buffer->has_separate_output ());
1003
    }
1004 1005 1006 1007 1008
  }
  else
  {
    /* in-place backward substitution/positioning */
    if (Proxy::table_index == 0)
B
Behdad Esfahbod 已提交
1009 1010
      buffer->remove_output ();
    buffer->idx = buffer->len - 1;
1011

1012
    apply_backward (c, lookup, accel);
1013 1014 1015
  }
}

B
Behdad Esfahbod 已提交
1016 1017
template <typename Proxy>
inline void hb_ot_map_t::apply (const Proxy &proxy,
B
Behdad Esfahbod 已提交
1018 1019 1020 1021
				const hb_ot_shape_plan_t *plan,
				hb_font_t *font,
				hb_buffer_t *buffer) const
{
B
Behdad Esfahbod 已提交
1022
  const unsigned int table_index = proxy.table_index;
B
Behdad Esfahbod 已提交
1023
  unsigned int i = 0;
B
Behdad Esfahbod 已提交
1024
  OT::hb_apply_context_t c (table_index, font, buffer);
1025
  c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
B
Behdad Esfahbod 已提交
1026 1027 1028 1029

  for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) {
    const stage_map_t *stage = &stages[table_index][stage_index];
    for (; i < stage->last_lookup; i++)
B
Behdad Esfahbod 已提交
1030 1031
    {
      unsigned int lookup_index = lookups[table_index][i].index;
1032
      if (!buffer->message (font, "start lookup %d", lookup_index)) continue;
1033
      c.set_lookup_index (lookup_index);
B
Behdad Esfahbod 已提交
1034 1035
      c.set_lookup_mask (lookups[table_index][i].mask);
      c.set_auto_zwj (lookups[table_index][i].auto_zwj);
1036 1037 1038
      apply_string<Proxy> (&c,
			   proxy.table.get_lookup (lookup_index),
			   proxy.accels[lookup_index]);
1039
      (void) buffer->message (font, "end lookup %d", lookup_index);
B
Behdad Esfahbod 已提交
1040
    }
B
Behdad Esfahbod 已提交
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051

    if (stage->pause_func)
    {
      buffer->clear_output ();
      stage->pause_func (plan, font, buffer);
    }
  }
}

void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
{
B
Behdad Esfahbod 已提交
1052 1053
  GSUBProxy proxy (font->face);
  apply (proxy, plan, font, buffer);
B
Behdad Esfahbod 已提交
1054 1055 1056 1057
}

void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
{
B
Behdad Esfahbod 已提交
1058 1059 1060 1061 1062 1063 1064 1065 1066
  GPOSProxy proxy (font->face);
  apply (proxy, plan, font, buffer);
}

HB_INTERNAL void
hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
				const OT::SubstLookup &lookup,
				const hb_ot_layout_lookup_accelerator_t &accel)
{
1067
  apply_string<GSUBProxy> (c, lookup, accel);
B
Behdad Esfahbod 已提交
1068
}