hb-ot-layout.cc 29.1 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 133 134
/**
 * Since: 0.9.7
 **/
135 136 137 138 139 140
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 已提交
141

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

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

174

175 176 177 178
/*
 * GSUB/GPOS
 */

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

317

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

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

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

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

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

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

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

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

385 386
  return l.has_required_feature ();
}
387

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

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

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

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

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

  return ret;
425 426 427 428
}


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

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

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

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

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

S
Sascha Brawer 已提交
471 472 473
/**
 * Since: 0.9.22
 **/
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
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 已提交
489
  return 0;
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 526
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)
  {
527
    unsigned int required_feature_index;
528 529 530 531 532 533
    if (hb_ot_layout_language_get_required_feature (face,
						    table_tag,
						    script_index,
						    language_index,
						    &required_feature_index,
						    NULL))
534 535 536 537 538
      _hb_ot_layout_collect_lookups_lookups (face,
					     table_tag,
					     required_feature_index,
					     lookup_indexes);

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

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

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

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

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

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

B
Behdad Esfahbod 已提交
712 713

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

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

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

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

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

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

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

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

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

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

  l.closure (&c);
777
}
778

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

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

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

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

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

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

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

	return true;
      }
835 836 837
    }
  }

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

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


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


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


885 886 887 888 889 890 891 892
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;
893
  while (buffer->idx < buffer->len && !buffer->in_error)
894 895 896
  {
    if (accel.may_have (buffer->cur().codepoint) &&
	(buffer->cur().mask & c->lookup_mask) &&
B
Minor  
Behdad Esfahbod 已提交
897
	c->check_glyph_property (&buffer->cur(), c->lookup_props) &&
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
	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 已提交
918
	c->check_glyph_property (&buffer->cur(), c->lookup_props) &&
919 920 921 922 923 924 925 926 927 928
	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 已提交
929 930
struct hb_apply_forward_context_t :
       OT::hb_dispatch_context_t<hb_apply_forward_context_t, bool, HB_DEBUG_APPLY>
931
{
B
Behdad Esfahbod 已提交
932
  inline const char *get_name (void) { return "APPLY_FWD"; }
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
  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;
};

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

957
  if (unlikely (!buffer->len || !c->lookup_mask))
958
    return;
959

960
  c->set_lookup_props (lookup.get_props ());
961 962 963 964 965

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

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

992
    apply_backward (c, lookup, accel);
993 994 995
  }
}

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

  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 已提交
1010
    {
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
#if 0
      char buf[4096];
      hb_buffer_serialize_glyphs (buffer, 0, buffer->len,
				  buf, sizeof (buf), NULL,
				  font,
				  HB_BUFFER_SERIALIZE_FORMAT_TEXT,
				  Proxy::table_index == 0 ?
				  HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS :
				  HB_BUFFER_SERIALIZE_FLAG_DEFAULT);
      printf ("buf: [%s]\n", buf);
#endif

B
Behdad Esfahbod 已提交
1023
      unsigned int lookup_index = lookups[table_index][i].index;
1024
      c.set_lookup_index (lookup_index);
B
Behdad Esfahbod 已提交
1025 1026
      c.set_lookup_mask (lookups[table_index][i].mask);
      c.set_auto_zwj (lookups[table_index][i].auto_zwj);
1027 1028 1029
      apply_string<Proxy> (&c,
			   proxy.table.get_lookup (lookup_index),
			   proxy.accels[lookup_index]);
B
Behdad Esfahbod 已提交
1030
    }
B
Behdad Esfahbod 已提交
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041

    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 已提交
1042 1043
  GSUBProxy proxy (font->face);
  apply (proxy, plan, font, buffer);
B
Behdad Esfahbod 已提交
1044 1045 1046 1047
}

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 已提交
1048 1049 1050 1051 1052 1053 1054 1055 1056
  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)
{
1057
  apply_string<GSUBProxy> (c, lookup, accel);
B
Behdad Esfahbod 已提交
1058
}