2 * Copyright (C) 1998-2004 David Turner and Werner Lemberg
3 * Copyright (C) 2006 Behdad Esfahbod
4 * Copyright (C) 2007,2008,2009 Red Hat, Inc.
6 * This is part of HarfBuzz, an OpenType Layout engine library.
8 * Permission is hereby granted, without written agreement and without
9 * license or royalty fees, to use, copy, modify, and distribute this
10 * software and its documentation for any purpose, provided that the
11 * above copyright notice and the following two paragraphs appear in
12 * all copies of this software.
14 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
15 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
17 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
20 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
21 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
23 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
24 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
26 * Red Hat Author(s): Behdad Esfahbod
29 #define HB_OT_LAYOUT_CC
31 #include "hb-ot-layout-private.h"
33 #include "hb-ot-layout-open-private.h"
34 #include "hb-ot-layout-gdef-private.h"
35 #include "hb-ot-layout-gsub-private.h"
36 #include "hb-ot-layout-gpos-private.h"
44 hb_ot_layout_create (void)
46 hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
48 layout->gdef = &Null(GDEF);
49 layout->gsub = &Null(GSUB);
50 layout->gpos = &Null(GPOS);
56 hb_ot_layout_has_substitution (hb_ot_layout_t *layout)
58 return layout->gsub != &Null(GSUB);
62 hb_ot_layout_has_positioning (hb_ot_layout_t *layout)
64 return layout->gpos != &Null(GPOS);
68 hb_ot_layout_create_for_data (const char *font_data,
71 hb_ot_layout_t *layout;
73 if (HB_UNLIKELY (font_data == NULL))
74 return hb_ot_layout_create ();
76 layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
78 const OpenTypeFontFile &font = OpenTypeFontFile::get_for_data (font_data);
79 const OpenTypeFontFace &face = font.get_face (face_index);
81 layout->gdef = &GDEF::get_for_data (font.get_table_data (face.get_table_by_tag (GDEF::Tag)));
82 layout->gsub = &GSUB::get_for_data (font.get_table_data (face.get_table_by_tag (GSUB::Tag)));
83 layout->gpos = &GPOS::get_for_data (font.get_table_data (face.get_table_by_tag (GPOS::Tag)));
89 hb_ot_layout_create_for_tables (const char *gdef_data,
90 const char *gsub_data,
91 const char *gpos_data)
93 hb_ot_layout_t *layout;
95 if (HB_UNLIKELY (gdef_data == NULL && gsub_data == NULL && gpos_data == NULL))
96 return hb_ot_layout_create ();
98 layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
100 layout->gdef = &GDEF::get_for_data (gdef_data);
101 layout->gsub = &GSUB::get_for_data (gsub_data);
102 layout->gpos = &GPOS::get_for_data (gpos_data);
108 hb_ot_layout_destroy (hb_ot_layout_t *layout)
114 hb_ot_layout_set_scale (hb_ot_layout_t *layout,
115 hb_16dot16_t x_scale, hb_16dot16_t y_scale)
117 layout->gpos_info.x_scale = x_scale;
118 layout->gpos_info.y_scale = y_scale;
122 hb_ot_layout_set_ppem (hb_ot_layout_t *layout,
123 unsigned int x_ppem, unsigned int y_ppem)
125 layout->gpos_info.x_ppem = x_ppem;
126 layout->gpos_info.y_ppem = y_ppem;
134 /* TODO the public class_t is a mess */
137 hb_ot_layout_has_font_glyph_classes (hb_ot_layout_t *layout)
139 return layout->gdef->has_glyph_classes ();
142 HB_INTERNAL hb_bool_t
143 _hb_ot_layout_has_new_glyph_classes (hb_ot_layout_t *layout)
145 return layout->new_gdef.len > 0;
148 HB_INTERNAL unsigned int
149 _hb_ot_layout_get_glyph_property (hb_ot_layout_t *layout,
150 hb_codepoint_t glyph)
152 hb_ot_layout_class_t klass;
154 klass = layout->gdef->get_glyph_class (glyph);
156 if (!klass && glyph < layout->new_gdef.len)
157 klass = layout->new_gdef.klasses[glyph];
161 case GDEF::UnclassifiedGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
162 case GDEF::BaseGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
163 case GDEF::LigatureGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
164 case GDEF::ComponentGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT;
165 case GDEF::MarkGlyph:
166 /* TODO old harfbuzz doesn't always parse mark attachments as it says it was
167 * introduced without a version bump, so it may not be safe */
168 klass = layout->gdef->get_mark_attachment_type (glyph);
169 return HB_OT_LAYOUT_GLYPH_CLASS_MARK + (klass << 8);
173 HB_INTERNAL hb_bool_t
174 _hb_ot_layout_check_glyph_property (hb_ot_layout_t *layout,
175 hb_internal_glyph_info_t *ginfo,
176 unsigned int lookup_flags,
177 unsigned int *property_out)
179 unsigned int property;
181 if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
182 ginfo->gproperty = _hb_ot_layout_get_glyph_property (layout, ginfo->codepoint);
183 property = ginfo->gproperty;
185 *property_out = property;
187 /* Not covered, if, for example, glyph class is ligature and
188 * lookup_flags includes LookupFlags::IgnoreLigatures
190 if (property & lookup_flags)
193 if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
195 /* If using mark filtering sets, the high short of
196 * lookup_flags has the set index.
198 if (lookup_flags & LookupFlag::UseMarkFilteringSet)
199 return layout->gdef->mark_set_covers (lookup_flags >> 16, ginfo->codepoint);
201 /* The second byte of lookup_flags has the meaning
202 * "ignore marks of attachment type different than
203 * the attachment type specified."
205 if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType)
206 return (lookup_flags & LookupFlag::MarkAttachmentType) == (property & LookupFlag::MarkAttachmentType);
212 HB_INTERNAL hb_bool_t
213 _hb_ot_layout_skip_mark (hb_ot_layout_t *layout,
214 hb_internal_glyph_info_t *ginfo,
215 unsigned int lookup_flags,
216 unsigned int *property_out)
218 unsigned int property;
220 if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
221 ginfo->gproperty = _hb_ot_layout_get_glyph_property (layout, ginfo->codepoint);
222 property = ginfo->gproperty;
224 *property_out = property;
226 if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
228 /* Skip mark if lookup_flags includes LookupFlags::IgnoreMarks */
229 if (lookup_flags & LookupFlag::IgnoreMarks)
232 /* If using mark filtering sets, the high short of lookup_flags has the set index. */
233 if (lookup_flags & LookupFlag::UseMarkFilteringSet)
234 return !layout->gdef->mark_set_covers (lookup_flags >> 16, ginfo->codepoint);
236 /* The second byte of lookup_flags has the meaning "ignore marks of attachment type
237 * different than the attachment type specified." */
238 if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType)
239 return (lookup_flags & LookupFlag::MarkAttachmentType) != (property & LookupFlag::MarkAttachmentType);
246 _hb_ot_layout_set_glyph_property (hb_ot_layout_t *layout,
247 hb_codepoint_t glyph,
248 unsigned int property)
249 { hb_ot_layout_set_glyph_class (layout, glyph, (hb_ot_layout_glyph_class_t) (property & 0xff)); }
252 hb_ot_layout_glyph_class_t
253 hb_ot_layout_get_glyph_class (hb_ot_layout_t *layout,
254 hb_codepoint_t glyph)
255 { return (hb_ot_layout_glyph_class_t) (_hb_ot_layout_get_glyph_property (layout, glyph) & 0xff); }
258 hb_ot_layout_set_glyph_class (hb_ot_layout_t *layout,
259 hb_codepoint_t glyph,
260 hb_ot_layout_glyph_class_t klass)
262 /* TODO optimize this? similar to old harfbuzz code for example */
264 hb_ot_layout_class_t gdef_klass;
265 int len = layout->new_gdef.len;
267 if (HB_UNLIKELY (glyph > 65535))
270 /* XXX this is not threadsafe */
273 unsigned char *new_klasses;
275 new_len = len == 0 ? 120 : 2 * len;
278 new_klasses = (unsigned char *) realloc (layout->new_gdef.klasses, new_len * sizeof (unsigned char));
280 if (HB_UNLIKELY (!new_klasses))
283 memset (new_klasses + len, 0, new_len - len);
285 layout->new_gdef.klasses = new_klasses;
286 layout->new_gdef.len = new_len;
291 case HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED: gdef_klass = GDEF::UnclassifiedGlyph; break;
292 case HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH: gdef_klass = GDEF::BaseGlyph; break;
293 case HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE: gdef_klass = GDEF::LigatureGlyph; break;
294 case HB_OT_LAYOUT_GLYPH_CLASS_MARK: gdef_klass = GDEF::MarkGlyph; break;
295 case HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT: gdef_klass = GDEF::ComponentGlyph; break;
298 layout->new_gdef.klasses[glyph] = gdef_klass;
303 hb_ot_layout_build_glyph_classes (hb_ot_layout_t *layout,
304 uint16_t num_total_glyphs,
305 hb_codepoint_t *glyphs,
306 unsigned char *klasses,
309 if (HB_UNLIKELY (!count || !glyphs || !klasses))
312 if (layout->new_gdef.len == 0) {
313 layout->new_gdef.klasses = (unsigned char *) calloc (num_total_glyphs, sizeof (unsigned char));
314 layout->new_gdef.len = count;
317 for (unsigned int i = 0; i < count; i++)
318 hb_ot_layout_set_glyph_class (layout, glyphs[i], (hb_ot_layout_glyph_class_t) klasses[i]);
322 hb_ot_layout_get_attach_points (hb_ot_layout_t *layout,
323 hb_codepoint_t glyph,
324 unsigned int *point_count /* IN/OUT */,
325 unsigned int *point_array /* OUT */)
327 return layout->gdef->get_attach_points (glyph, point_count, point_array);
331 hb_ot_layout_get_lig_carets (hb_ot_layout_t *layout,
332 hb_codepoint_t glyph,
333 unsigned int *caret_count /* IN/OUT */,
334 int *caret_array /* OUT */)
336 return layout->gdef->get_lig_carets (layout, glyph, caret_count, caret_array);
343 static const GSUBGPOS&
344 get_gsubgpos_table (hb_ot_layout_t *layout,
345 hb_ot_layout_table_type_t table_type)
347 switch (table_type) {
348 case HB_OT_LAYOUT_TABLE_TYPE_GSUB: return *(layout->gsub);
349 case HB_OT_LAYOUT_TABLE_TYPE_GPOS: return *(layout->gpos);
350 default: return Null(GSUBGPOS);
356 hb_ot_layout_table_get_script_count (hb_ot_layout_t *layout,
357 hb_ot_layout_table_type_t table_type)
359 const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
361 return g.get_script_count ();
365 hb_ot_layout_table_get_script_tag (hb_ot_layout_t *layout,
366 hb_ot_layout_table_type_t table_type,
367 unsigned int script_index)
369 const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
371 return g.get_script_tag (script_index);
375 hb_ot_layout_table_find_script (hb_ot_layout_t *layout,
376 hb_ot_layout_table_type_t table_type,
378 unsigned int *script_index)
380 ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
381 const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
383 if (g.find_script_index (script_tag, script_index))
386 /* try finding 'DFLT' */
387 if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_SCRIPT, script_index))
390 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
391 if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, script_index))
394 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
399 hb_ot_layout_table_get_feature_count (hb_ot_layout_t *layout,
400 hb_ot_layout_table_type_t table_type)
402 const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
404 return g.get_feature_count ();
408 hb_ot_layout_table_get_feature_tag (hb_ot_layout_t *layout,
409 hb_ot_layout_table_type_t table_type,
410 unsigned int feature_index)
412 const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
414 return g.get_feature_tag (feature_index);
418 hb_ot_layout_table_find_feature (hb_ot_layout_t *layout,
419 hb_ot_layout_table_type_t table_type,
420 hb_tag_t feature_tag,
421 unsigned int *feature_index)
423 ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
424 const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
426 if (g.find_feature_index (feature_tag, feature_index))
429 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
434 hb_ot_layout_table_get_lookup_count (hb_ot_layout_t *layout,
435 hb_ot_layout_table_type_t table_type)
437 const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
439 return g.get_lookup_count ();
444 hb_ot_layout_script_get_language_count (hb_ot_layout_t *layout,
445 hb_ot_layout_table_type_t table_type,
446 unsigned int script_index)
448 const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
450 return s.get_lang_sys_count ();
454 hb_ot_layout_script_get_language_tag (hb_ot_layout_t *layout,
455 hb_ot_layout_table_type_t table_type,
456 unsigned int script_index,
457 unsigned int language_index)
459 const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
461 return s.get_lang_sys_tag (language_index);
465 hb_ot_layout_script_find_language (hb_ot_layout_t *layout,
466 hb_ot_layout_table_type_t table_type,
467 unsigned int script_index,
468 hb_tag_t language_tag,
469 unsigned int *language_index)
471 ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
472 const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
474 if (s.find_lang_sys_index (language_tag, language_index))
477 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
478 if (s.find_lang_sys_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, language_index))
481 if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
486 hb_ot_layout_language_get_required_feature_index (hb_ot_layout_t *layout,
487 hb_ot_layout_table_type_t table_type,
488 unsigned int script_index,
489 unsigned int language_index,
490 unsigned int *feature_index)
492 const LangSys &l = get_gsubgpos_table (layout, table_type).get_script (script_index).get_lang_sys (language_index);
494 if (feature_index) *feature_index = l.get_required_feature_index ();
496 return l.has_required_feature ();
500 hb_ot_layout_language_get_feature_count (hb_ot_layout_t *layout,
501 hb_ot_layout_table_type_t table_type,
502 unsigned int script_index,
503 unsigned int language_index)
505 const LangSys &l = get_gsubgpos_table (layout, table_type).get_script (script_index).get_lang_sys (language_index);
507 return l.get_feature_count ();
511 hb_ot_layout_language_get_feature_index (hb_ot_layout_t *layout,
512 hb_ot_layout_table_type_t table_type,
513 unsigned int script_index,
514 unsigned int language_index,
515 unsigned int num_feature)
517 const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
518 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
520 return l.get_feature_index (num_feature);
524 hb_ot_layout_language_get_feature_tag (hb_ot_layout_t *layout,
525 hb_ot_layout_table_type_t table_type,
526 unsigned int script_index,
527 unsigned int language_index,
528 unsigned int num_feature)
530 const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
531 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
532 unsigned int feature_index = l.get_feature_index (num_feature);
534 return g.get_feature_tag (feature_index);
539 hb_ot_layout_language_find_feature (hb_ot_layout_t *layout,
540 hb_ot_layout_table_type_t table_type,
541 unsigned int script_index,
542 unsigned int language_index,
543 hb_tag_t feature_tag,
544 unsigned int *feature_index)
546 ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
547 const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
548 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
550 unsigned int num_features = l.get_feature_count ();
551 for (unsigned int i = 0; i < num_features; i++) {
552 unsigned int f_index = l.get_feature_index (i);
554 if (feature_tag == g.get_feature_tag (f_index)) {
555 if (feature_index) *feature_index = f_index;
560 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
565 hb_ot_layout_feature_get_lookup_count (hb_ot_layout_t *layout,
566 hb_ot_layout_table_type_t table_type,
567 unsigned int feature_index)
569 const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
570 const Feature &f = g.get_feature (feature_index);
572 return f.get_lookup_count ();
576 hb_ot_layout_feature_get_lookup_index (hb_ot_layout_t *layout,
577 hb_ot_layout_table_type_t table_type,
578 unsigned int feature_index,
579 unsigned int num_lookup)
581 const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
582 const Feature &f = g.get_feature (feature_index);
584 return f.get_lookup_index (num_lookup);
592 hb_ot_layout_substitute_lookup (hb_ot_layout_t *layout,
594 unsigned int lookup_index,
595 hb_ot_layout_feature_mask_t mask)
597 return layout->gsub->substitute_lookup (layout, buffer, lookup_index, mask);
605 hb_ot_layout_position_lookup (hb_ot_layout_t *layout,
607 unsigned int lookup_index,
608 hb_ot_layout_feature_mask_t mask)
610 return layout->gpos->position_lookup (layout, buffer, lookup_index, mask);