hb-ot-layout.cc 28.8 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
 */

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

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

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

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

B
Behdad Esfahbod 已提交
43

44
HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
45

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

53 54
  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 已提交
55

56 57
  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 已提交
58

59 60
  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 已提交
61

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

65 66
  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));
67

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

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

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

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

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

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

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

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

120

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

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

S
Sascha Brawer 已提交
131 132 133
/**
 * Since: 0.9.7
 **/
134 135 136 137 138 139
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 已提交
140

S
Sascha Brawer 已提交
141 142 143
/**
 * Since: 0.9.7
 **/
144 145 146 147 148 149 150 151
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 已提交
152
unsigned int
153
hb_ot_layout_get_attach_points (hb_face_t      *face,
B
Behdad Esfahbod 已提交
154
				hb_codepoint_t  glyph,
B
Behdad Esfahbod 已提交
155
				unsigned int    start_offset,
B
Behdad Esfahbod 已提交
156 157 158
				unsigned int   *point_count /* IN/OUT */,
				unsigned int   *point_array /* OUT */)
{
B
Behdad Esfahbod 已提交
159
  return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
B
Behdad Esfahbod 已提交
160 161
}

B
Behdad Esfahbod 已提交
162
unsigned int
163 164 165 166 167 168
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 已提交
169
{
170
  return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
B
Behdad Esfahbod 已提交
171 172
}

173

174 175 176 177
/*
 * GSUB/GPOS
 */

178
static const OT::GSUBGPOS&
179 180
get_gsubgpos_table (hb_face_t *face,
		    hb_tag_t   table_tag)
181
{
182
  switch (table_tag) {
183 184
    case HB_OT_TAG_GSUB: return _get_gsub (face);
    case HB_OT_TAG_GPOS: return _get_gpos (face);
185
    default:             return OT::Null(OT::GSUBGPOS);
186 187 188 189
  }
}


B
Behdad Esfahbod 已提交
190
unsigned int
191 192
hb_ot_layout_table_get_script_tags (hb_face_t    *face,
				    hb_tag_t      table_tag,
B
Behdad Esfahbod 已提交
193
				    unsigned int  start_offset,
194 195
				    unsigned int *script_count /* IN/OUT */,
				    hb_tag_t     *script_tags /* OUT */)
196
{
197
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
198

B
Behdad Esfahbod 已提交
199
  return g.get_script_tags (start_offset, script_count, script_tags);
200 201
}

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

204
hb_bool_t
205 206 207 208
hb_ot_layout_table_find_script (hb_face_t    *face,
				hb_tag_t      table_tag,
				hb_tag_t      script_tag,
				unsigned int *script_index)
209
{
210 211
  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
212 213

  if (g.find_script_index (script_tag, script_index))
214
    return true;
215 216

  /* try finding 'DFLT' */
217
  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
218
    return false;
219

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

B
Minor  
Behdad Esfahbod 已提交
225 226 227 228 229
  /* 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 已提交
230
  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
231
  return false;
B
Behdad Esfahbod 已提交
232 233 234 235 236 237
}

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 已提交
238 239
				  unsigned int   *script_index,
				  hb_tag_t       *chosen_script)
B
Behdad Esfahbod 已提交
240
{
241 242
  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 已提交
243 244 245

  while (*script_tags)
  {
B
Behdad Esfahbod 已提交
246 247 248
    if (g.find_script_index (*script_tags, script_index)) {
      if (chosen_script)
        *chosen_script = *script_tags;
249
      return true;
B
Behdad Esfahbod 已提交
250
    }
B
Behdad Esfahbod 已提交
251 252 253 254
    script_tags++;
  }

  /* try finding 'DFLT' */
B
Behdad Esfahbod 已提交
255 256 257
  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
    if (chosen_script)
      *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
258
    return false;
B
Behdad Esfahbod 已提交
259
  }
B
Behdad Esfahbod 已提交
260 261

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

268 269 270 271 272
  /* 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;
273
    return false;
274 275
  }

276
  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
B
Behdad Esfahbod 已提交
277 278
  if (chosen_script)
    *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
279
  return false;
280 281
}

B
Behdad Esfahbod 已提交
282
unsigned int
283 284
hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
				     hb_tag_t      table_tag,
B
Behdad Esfahbod 已提交
285
				     unsigned int  start_offset,
286 287
				     unsigned int *feature_count /* IN/OUT */,
				     hb_tag_t     *feature_tags /* OUT */)
288
{
289
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
290

B
Behdad Esfahbod 已提交
291
  return g.get_feature_tags (start_offset, feature_count, feature_tags);
292 293
}

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
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;
}

316

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

B
Behdad Esfahbod 已提交
327
  return s.get_lang_sys_tags (start_offset, language_count, language_tags);
328 329 330
}

hb_bool_t
331 332 333 334 335
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)
336
{
337 338
  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);
339

340
  if (s.find_lang_sys_index (language_tag, language_index))
341
    return true;
342 343

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

347
  if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
348
  return false;
349
}
350

B
Behdad Esfahbod 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
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 已提交
366 367 368
/**
 * Since: 0.9.30
 **/
369
hb_bool_t
370 371 372 373 374 375
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)
376
{
377 378
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
379

380 381 382
  unsigned int index = l.get_required_feature_index ();
  if (feature_index) *feature_index = index;
  if (feature_tag) *feature_tag = g.get_feature_tag (index);
383

384 385
  return l.has_required_feature ();
}
386

B
Behdad Esfahbod 已提交
387
unsigned int
388 389 390 391
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 已提交
392
					   unsigned int  start_offset,
393 394
					   unsigned int *feature_count /* IN/OUT */,
					   unsigned int *feature_indexes /* OUT */)
395
{
396 397
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
398

B
Behdad Esfahbod 已提交
399
  return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
400 401
}

B
Behdad Esfahbod 已提交
402
unsigned int
403 404 405 406
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 已提交
407
					unsigned int  start_offset,
408 409
					unsigned int *feature_count /* IN/OUT */,
					hb_tag_t     *feature_tags /* OUT */)
410
{
411 412
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
413

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

B
Behdad Esfahbod 已提交
417 418 419 420 421
  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]);
  }
422 423

  return ret;
424 425 426 427
}


hb_bool_t
428 429 430 431 432 433
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)
434
{
435 436 437
  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);
438

B
Behdad Esfahbod 已提交
439 440
  unsigned int num_features = l.get_feature_count ();
  for (unsigned int i = 0; i < num_features; i++) {
441 442 443 444
    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;
445
      return true;
446 447
    }
  }
448

449
  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
450
  return false;
451
}
452

S
Sascha Brawer 已提交
453 454 455
/**
 * Since: 0.9.7
 **/
B
Behdad Esfahbod 已提交
456
unsigned int
457 458 459 460 461 462
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 */)
463
{
464 465
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
  const OT::Feature &f = g.get_feature (feature_index);
466

B
Behdad Esfahbod 已提交
467
  return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
468 469
}

S
Sascha Brawer 已提交
470 471 472
/**
 * Since: 0.9.22
 **/
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
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 已提交
488
  return 0;
489 490
}

491 492 493 494 495 496 497 498 499 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
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)
  {
526
    unsigned int required_feature_index;
527 528 529 530 531 532
    if (hb_ot_layout_language_get_required_feature (face,
						    table_tag,
						    script_index,
						    language_index,
						    &required_feature_index,
						    NULL))
533 534 535 536 537
      _hb_ot_layout_collect_lookups_lookups (face,
					     table_tag,
					     required_feature_index,
					     lookup_indexes);

538
    /* All features */
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
    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
  {
563 564 565
    for (; *features; features++)
    {
      unsigned int feature_index;
B
Behdad Esfahbod 已提交
566 567 568 569 570 571 572 573 574 575
      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);
576 577 578 579 580 581 582 583 584 585 586 587
    }
  }
}

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 */)
{
588 589 590 591 592 593 594
  _hb_ot_layout_collect_lookups_features (face,
					  table_tag,
					  script_index,
					  HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
					  features,
					  lookup_indexes);

595 596 597
  if (!languages)
  {
    /* All languages */
B
Behdad Esfahbod 已提交
598 599 600 601
    unsigned int count = hb_ot_layout_script_get_language_tags (face,
								table_tag,
								script_index,
								0, NULL, NULL);
602
    for (unsigned int language_index = 0; language_index < count; language_index++)
B
Behdad Esfahbod 已提交
603 604 605 606 607 608
      _hb_ot_layout_collect_lookups_features (face,
					      table_tag,
					      script_index,
					      language_index,
					      features,
					      lookup_indexes);
609 610 611
  }
  else
  {
612 613 614
    for (; *languages; languages++)
    {
      unsigned int language_index;
B
Behdad Esfahbod 已提交
615 616 617 618 619 620 621 622 623 624 625
      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);
626 627 628 629
    }
  }
}

S
Sascha Brawer 已提交
630 631 632
/**
 * Since: 0.9.8
 **/
633 634 635 636 637 638 639 640 641 642 643
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 已提交
644 645 646
    unsigned int count = hb_ot_layout_table_get_script_tags (face,
							     table_tag,
							     0, NULL, NULL);
647
    for (unsigned int script_index = 0; script_index < count; script_index++)
B
Behdad Esfahbod 已提交
648 649 650 651 652 653
      _hb_ot_layout_collect_lookups_languages (face,
					       table_tag,
					       script_index,
					       languages,
					       features,
					       lookup_indexes);
654 655 656
  }
  else
  {
657 658 659
    for (; *scripts; scripts++)
    {
      unsigned int script_index;
B
Behdad Esfahbod 已提交
660 661 662 663 664 665 666 667 668 669
      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);
670 671 672 673
    }
  }
}

S
Sascha Brawer 已提交
674 675 676
/**
 * Since: 0.9.7
 **/
677 678 679 680 681 682 683 684 685 686 687
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 已提交
688 689 690 691 692
  OT::hb_collect_glyphs_context_t c (face,
				     glyphs_before,
				     glyphs_input,
				     glyphs_after,
				     glyphs_output);
693

694 695
  switch (table_tag)
  {
696 697
    case HB_OT_TAG_GSUB:
    {
698
      const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
B
Behdad Esfahbod 已提交
699
      l.collect_glyphs (&c);
700 701 702 703
      return;
    }
    case HB_OT_TAG_GPOS:
    {
704
      const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
B
Behdad Esfahbod 已提交
705
      l.collect_glyphs (&c);
706 707 708 709 710
      return;
    }
  }
}

B
Behdad Esfahbod 已提交
711 712

/*
713
 * OT::GSUB
B
Behdad Esfahbod 已提交
714 715 716
 */

hb_bool_t
717 718
hb_ot_layout_has_substitution (hb_face_t *face)
{
719
  return &_get_gsub (face) != &OT::Null(OT::GSUB);
720 721
}

S
Sascha Brawer 已提交
722 723 724
/**
 * Since: 0.9.7
 **/
725
hb_bool_t
726
hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
727
				      unsigned int          lookup_index,
728 729
				      const hb_codepoint_t *glyphs,
				      unsigned int          glyphs_length,
730
				      hb_bool_t             zero_context)
731
{
732
  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
733
  return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
734 735
}

B
Behdad Esfahbod 已提交
736
hb_bool_t
737
hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
738
					   unsigned int          lookup_index,
B
Behdad Esfahbod 已提交
739 740
					   const hb_codepoint_t *glyphs,
					   unsigned int          glyphs_length,
741
					   hb_bool_t             zero_context)
B
Behdad Esfahbod 已提交
742
{
743
  if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
B
Behdad Esfahbod 已提交
744 745 746 747
  OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context);

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

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

751
void
752
hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
753
{
754
  OT::GSUB::substitute_start (font, buffer);
755 756 757
}

void
758
hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
759
{
760
  OT::GSUB::substitute_finish (font, buffer);
761 762
}

S
Sascha Brawer 已提交
763 764 765
/**
 * Since: 0.9.7
 **/
B
Behdad Esfahbod 已提交
766
void
767
hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
768 769
				        unsigned int  lookup_index,
				        hb_set_t     *glyphs)
770
{
771 772 773 774 775
  OT::hb_closure_context_t c (face, glyphs);

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

  l.closure (&c);
776
}
777

778
/*
779
 * OT::GPOS
780 781 782
 */

hb_bool_t
783 784
hb_ot_layout_has_positioning (hb_face_t *face)
{
785
  return &_get_gpos (face) != &OT::Null(OT::GPOS);
786 787
}

788
void
789
hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
790
{
791
  OT::GPOS::position_start (font, buffer);
792 793
}

794
void
795
hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
796
{
797
  OT::GPOS::position_finish (font, buffer);
798
}
799

S
Sascha Brawer 已提交
800 801 802
/**
 * Since: 0.9.8
 **/
803
hb_bool_t
804 805 806 807 808 809
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 */)
810 811
{
  const OT::GPOS &gpos = _get_gpos (face);
812
  const hb_tag_t tag = HB_TAG ('s','i','z','e');
813

814 815 816
  unsigned int num_features = gpos.get_feature_count ();
  for (unsigned int i = 0; i < num_features; i++)
  {
817
    if (tag == gpos.get_feature_tag (i))
818
    {
819
      const OT::Feature &f = gpos.get_feature (i);
820
      const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
821

822
      if (params.designSize)
823
      {
824
#define PARAM(a, A) if (a) *a = params.A
825 826 827 828 829 830
	PARAM (design_size, designSize);
	PARAM (subfamily_id, subfamilyID);
	PARAM (subfamily_name_id, subfamilyNameID);
	PARAM (range_start, rangeStart);
	PARAM (range_end, rangeEnd);
#undef PARAM
831 832 833

	return true;
      }
834 835 836
    }
  }

837
#define PARAM(a, A) if (a) *a = 0
838 839 840 841 842
  PARAM (design_size, designSize);
  PARAM (subfamily_id, subfamilyID);
  PARAM (subfamily_name_id, subfamilyNameID);
  PARAM (range_start, rangeStart);
  PARAM (range_end, rangeEnd);
843
#undef PARAM
844

845
  return false;
846
}
B
Behdad Esfahbod 已提交
847 848 849


/*
B
Behdad Esfahbod 已提交
850
 * Parts of different types are implemented here such that they have direct
B
Behdad Esfahbod 已提交
851 852 853 854
 * access to GSUB/GPOS lookups.
 */


B
Behdad Esfahbod 已提交
855 856 857
struct GSUBProxy
{
  static const unsigned int table_index = 0;
B
Behdad Esfahbod 已提交
858
  static const bool inplace = false;
B
Behdad Esfahbod 已提交
859 860 861 862 863 864 865 866 867 868 869 870 871
  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 已提交
872
  static const bool inplace = true;
B
Behdad Esfahbod 已提交
873 874 875 876 877 878 879 880 881 882 883
  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;
};


884 885 886 887 888 889 890 891 892 893 894 895
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;
  while (buffer->idx < buffer->len)
  {
    if (accel.may_have (buffer->cur().codepoint) &&
	(buffer->cur().mask & c->lookup_mask) &&
B
Minor  
Behdad Esfahbod 已提交
896
	c->check_glyph_property (&buffer->cur(), c->lookup_props) &&
897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
	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 已提交
917
	c->check_glyph_property (&buffer->cur(), c->lookup_props) &&
918 919 920 921 922 923 924 925 926 927
	obj.apply (c))
      ret = true;
    /* The reverse lookup doesn't "advance" cursor (for good reason). */
    buffer->idx--;

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

928 929
struct hb_apply_forward_context_t
{
B
Behdad Esfahbod 已提交
930
  inline const char *get_name (void) { return "APPLY_FWD"; }
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950
  static const unsigned int max_debug_depth = HB_DEBUG_APPLY;
  typedef bool return_t;
  template <typename T, typename F>
  inline bool may_dispatch (const T *obj, const F *format) { return true; }
  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;
};

951
template <typename Proxy>
952
static inline void
953 954 955 956
apply_string (OT::hb_apply_context_t *c,
	      const typename Proxy::Lookup &lookup,
	      const hb_ot_layout_lookup_accelerator_t &accel)
{
B
Behdad Esfahbod 已提交
957
  hb_buffer_t *buffer = c->buffer;
958

B
Behdad Esfahbod 已提交
959
  if (unlikely (!buffer->len || !c->lookup_mask))
960
    return;
961 962 963 964 965 966 967

  c->set_lookup (lookup);

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

971 972 973 974 975 976 977 978 979
    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)
980
    {
B
Behdad Esfahbod 已提交
981
      if (!Proxy::inplace)
B
Behdad Esfahbod 已提交
982
	buffer->swap_buffers ();
983
      else
984
	assert (!buffer->has_separate_output ());
985
    }
986 987 988 989 990
  }
  else
  {
    /* in-place backward substitution/positioning */
    if (Proxy::table_index == 0)
B
Behdad Esfahbod 已提交
991 992
      buffer->remove_output ();
    buffer->idx = buffer->len - 1;
993

994
    apply_backward (c, lookup, accel);
995 996 997
  }
}

B
Behdad Esfahbod 已提交
998 999
template <typename Proxy>
inline void hb_ot_map_t::apply (const Proxy &proxy,
B
Behdad Esfahbod 已提交
1000 1001 1002 1003
				const hb_ot_shape_plan_t *plan,
				hb_font_t *font,
				hb_buffer_t *buffer) const
{
B
Behdad Esfahbod 已提交
1004
  const unsigned int table_index = proxy.table_index;
B
Behdad Esfahbod 已提交
1005
  unsigned int i = 0;
B
Behdad Esfahbod 已提交
1006
  OT::hb_apply_context_t c (table_index, font, buffer);
1007
  c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
B
Behdad Esfahbod 已提交
1008 1009 1010 1011

  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 已提交
1012 1013 1014 1015
    {
      unsigned int lookup_index = lookups[table_index][i].index;
      c.set_lookup_mask (lookups[table_index][i].mask);
      c.set_auto_zwj (lookups[table_index][i].auto_zwj);
1016 1017 1018
      apply_string<Proxy> (&c,
			   proxy.table.get_lookup (lookup_index),
			   proxy.accels[lookup_index]);
B
Behdad Esfahbod 已提交
1019
    }
B
Behdad Esfahbod 已提交
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030

    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 已提交
1031 1032
  GSUBProxy proxy (font->face);
  apply (proxy, plan, font, buffer);
B
Behdad Esfahbod 已提交
1033 1034 1035 1036
}

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 已提交
1037 1038 1039 1040 1041 1042 1043 1044 1045
  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)
{
1046
  apply_string<GSUBProxy> (c, lookup, accel);
B
Behdad Esfahbod 已提交
1047
}