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-open-file-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_init (hb_ot_layout_t *layout,
47 layout->gdef = &Null(GDEF);
48 layout->gsub = &Null(GSUB);
49 layout->gpos = &Null(GPOS);
54 hb_ot_layout_create_for_data (const char *font_data,
57 hb_ot_layout_t *layout;
59 if (HB_UNLIKELY (font_data == NULL))
60 return hb_ot_layout_create ();
62 layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
64 const OpenTypeFontFile &font = OpenTypeFontFile::get_for_data (font_data);
65 const OpenTypeFontFace &face = font.get_face (face_index);
67 layout->gdef = &GDEF::get_for_data (font.get_table_data (face.get_table_by_tag (GDEF::Tag)));
68 layout->gsub = &GSUB::get_for_data (font.get_table_data (face.get_table_by_tag (GSUB::Tag)));
69 layout->gpos = &GPOS::get_for_data (font.get_table_data (face.get_table_by_tag (GPOS::Tag)));
75 hb_ot_layout_create_for_tables (const char *gdef_data,
76 const char *gsub_data,
77 const char *gpos_data)
79 hb_ot_layout_t *layout;
81 if (HB_UNLIKELY (gdef_data == NULL && gsub_data == NULL && gpos_data == NULL))
82 return hb_ot_layout_create ();
84 layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
86 layout->gdef = &GDEF::get_for_data (gdef_data);
87 layout->gsub = &GSUB::get_for_data (gsub_data);
88 layout->gpos = &GPOS::get_for_data (gpos_data);
95 _hb_ot_layout_fini (hb_ot_layout_t *layout,
100 static hb_ot_layout_t *
101 _hb_ot_face_get_layout (hb_face_t *face)
103 return NULL; /* XXX */
110 /* TODO the public class_t is a mess */
113 hb_ot_layout_has_font_glyph_classes (hb_face_t *face)
115 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
116 return layout->gdef->has_glyph_classes ();
119 HB_INTERNAL hb_bool_t
120 _hb_ot_layout_has_new_glyph_classes (hb_ot_layout_t *layout)
122 return layout->new_gdef.len > 0;
125 HB_INTERNAL unsigned int
126 _hb_ot_layout_get_glyph_property (hb_ot_layout_t *layout,
127 hb_codepoint_t glyph)
129 hb_ot_layout_class_t klass;
131 klass = layout->gdef->get_glyph_class (glyph);
133 if (!klass && glyph < layout->new_gdef.len)
134 klass = layout->new_gdef.klasses[glyph];
138 case GDEF::UnclassifiedGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
139 case GDEF::BaseGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
140 case GDEF::LigatureGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
141 case GDEF::ComponentGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT;
142 case GDEF::MarkGlyph:
143 /* TODO old harfbuzz doesn't always parse mark attachments as it says it was
144 * introduced without a version bump, so it may not be safe */
145 klass = layout->gdef->get_mark_attachment_type (glyph);
146 return HB_OT_LAYOUT_GLYPH_CLASS_MARK + (klass << 8);
150 HB_INTERNAL hb_bool_t
151 _hb_ot_layout_check_glyph_property (hb_ot_layout_t *layout,
152 hb_internal_glyph_info_t *ginfo,
153 unsigned int lookup_flags,
154 unsigned int *property_out)
156 unsigned int property;
158 if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
159 ginfo->gproperty = _hb_ot_layout_get_glyph_property (layout, ginfo->codepoint);
160 property = ginfo->gproperty;
162 *property_out = property;
164 /* Not covered, if, for example, glyph class is ligature and
165 * lookup_flags includes LookupFlags::IgnoreLigatures
167 if (property & lookup_flags)
170 if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
172 /* If using mark filtering sets, the high short of
173 * lookup_flags has the set index.
175 if (lookup_flags & LookupFlag::UseMarkFilteringSet)
176 return layout->gdef->mark_set_covers (lookup_flags >> 16, ginfo->codepoint);
178 /* The second byte of lookup_flags has the meaning
179 * "ignore marks of attachment type different than
180 * the attachment type specified."
182 if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType)
183 return (lookup_flags & LookupFlag::MarkAttachmentType) == (property & LookupFlag::MarkAttachmentType);
189 HB_INTERNAL hb_bool_t
190 _hb_ot_layout_skip_mark (hb_ot_layout_t *layout,
191 hb_internal_glyph_info_t *ginfo,
192 unsigned int lookup_flags,
193 unsigned int *property_out)
195 unsigned int property;
197 if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
198 ginfo->gproperty = _hb_ot_layout_get_glyph_property (layout, ginfo->codepoint);
199 property = ginfo->gproperty;
201 *property_out = property;
203 if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
205 /* Skip mark if lookup_flags includes LookupFlags::IgnoreMarks */
206 if (lookup_flags & LookupFlag::IgnoreMarks)
209 /* If using mark filtering sets, the high short of lookup_flags has the set index. */
210 if (lookup_flags & LookupFlag::UseMarkFilteringSet)
211 return !layout->gdef->mark_set_covers (lookup_flags >> 16, ginfo->codepoint);
213 /* The second byte of lookup_flags has the meaning "ignore marks of attachment type
214 * different than the attachment type specified." */
215 if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType)
216 return (lookup_flags & LookupFlag::MarkAttachmentType) != (property & LookupFlag::MarkAttachmentType);
223 _hb_ot_layout_set_glyph_class (hb_ot_layout_t *layout,
224 hb_codepoint_t glyph,
225 hb_ot_layout_glyph_class_t klass)
227 /* TODO optimize this? similar to old harfbuzz code for example */
229 hb_ot_layout_class_t gdef_klass;
230 int len = layout->new_gdef.len;
232 if (HB_UNLIKELY (glyph > 65535))
235 /* XXX this is not threadsafe */
238 unsigned char *new_klasses;
240 new_len = len == 0 ? 120 : 2 * len;
243 new_klasses = (unsigned char *) realloc (layout->new_gdef.klasses, new_len * sizeof (unsigned char));
245 if (HB_UNLIKELY (!new_klasses))
248 memset (new_klasses + len, 0, new_len - len);
250 layout->new_gdef.klasses = new_klasses;
251 layout->new_gdef.len = new_len;
256 case HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED: gdef_klass = GDEF::UnclassifiedGlyph; break;
257 case HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH: gdef_klass = GDEF::BaseGlyph; break;
258 case HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE: gdef_klass = GDEF::LigatureGlyph; break;
259 case HB_OT_LAYOUT_GLYPH_CLASS_MARK: gdef_klass = GDEF::MarkGlyph; break;
260 case HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT: gdef_klass = GDEF::ComponentGlyph; break;
263 layout->new_gdef.klasses[glyph] = gdef_klass;
268 _hb_ot_layout_set_glyph_property (hb_ot_layout_t *layout,
269 hb_codepoint_t glyph,
270 unsigned int property)
271 { _hb_ot_layout_set_glyph_class (layout, glyph, (hb_ot_layout_glyph_class_t) (property & 0xff)); }
274 hb_ot_layout_glyph_class_t
275 hb_ot_layout_get_glyph_class (hb_face_t *face,
276 hb_codepoint_t glyph)
278 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
279 return (hb_ot_layout_glyph_class_t) (_hb_ot_layout_get_glyph_property (layout, glyph) & 0xff);
283 hb_ot_layout_set_glyph_class (hb_face_t *face,
284 hb_codepoint_t glyph,
285 hb_ot_layout_glyph_class_t klass)
287 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
288 _hb_ot_layout_set_glyph_class (layout, glyph, klass);
292 hb_ot_layout_build_glyph_classes (hb_face_t *face,
293 uint16_t num_total_glyphs,
294 hb_codepoint_t *glyphs,
295 unsigned char *klasses,
298 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
300 if (HB_UNLIKELY (!count || !glyphs || !klasses))
303 if (layout->new_gdef.len == 0) {
304 layout->new_gdef.klasses = (unsigned char *) calloc (num_total_glyphs, sizeof (unsigned char));
305 layout->new_gdef.len = count;
308 for (unsigned int i = 0; i < count; i++)
309 _hb_ot_layout_set_glyph_class (layout, glyphs[i], (hb_ot_layout_glyph_class_t) klasses[i]);
313 hb_ot_layout_get_attach_points (hb_face_t *face,
314 hb_codepoint_t glyph,
315 unsigned int *point_count /* IN/OUT */,
316 unsigned int *point_array /* OUT */)
318 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
319 return layout->gdef->get_attach_points (glyph, point_count, point_array);
323 hb_ot_layout_get_lig_carets (hb_face_t *face,
325 hb_codepoint_t glyph,
326 unsigned int *caret_count /* IN/OUT */,
327 int *caret_array /* OUT */)
329 hb_ot_layout_context_t context;
331 context.layout = _hb_ot_face_get_layout (face);
332 return context.layout->gdef->get_lig_carets (&context, glyph, caret_count, caret_array);
339 static const GSUBGPOS&
340 get_gsubgpos_table (hb_ot_layout_t *layout,
344 case HB_OT_TAG_GSUB: return *(layout->gsub);
345 case HB_OT_TAG_GPOS: return *(layout->gpos);
346 default: return Null(GSUBGPOS);
352 hb_ot_layout_table_get_script_count (hb_face_t *face,
355 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
356 const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
358 return g.get_script_count ();
362 hb_ot_layout_table_get_script_tag (hb_face_t *face,
364 unsigned int script_index)
366 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
367 const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
369 return g.get_script_tag (script_index);
373 hb_ot_layout_table_find_script (hb_face_t *face,
376 unsigned int *script_index)
378 ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
379 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
380 const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
382 if (g.find_script_index (script_tag, script_index))
385 /* try finding 'DFLT' */
386 if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_SCRIPT, script_index))
389 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
390 if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, script_index))
393 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
398 hb_ot_layout_table_get_feature_count (hb_face_t *face,
401 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
402 const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
404 return g.get_feature_count ();
408 hb_ot_layout_table_get_feature_tag (hb_face_t *face,
410 unsigned int feature_index)
412 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
413 const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
415 return g.get_feature_tag (feature_index);
419 hb_ot_layout_table_find_feature (hb_face_t *face,
421 hb_tag_t feature_tag,
422 unsigned int *feature_index)
424 ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
425 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
426 const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
428 if (g.find_feature_index (feature_tag, feature_index))
431 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
436 hb_ot_layout_table_get_lookup_count (hb_face_t *face,
439 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
440 const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
442 return g.get_lookup_count ();
447 hb_ot_layout_script_get_language_count (hb_face_t *face,
449 unsigned int script_index)
451 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
452 const Script &s = get_gsubgpos_table (layout, table_tag).get_script (script_index);
454 return s.get_lang_sys_count ();
458 hb_ot_layout_script_get_language_tag (hb_face_t *face,
460 unsigned int script_index,
461 unsigned int language_index)
463 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
464 const Script &s = get_gsubgpos_table (layout, table_tag).get_script (script_index);
466 return s.get_lang_sys_tag (language_index);
470 hb_ot_layout_script_find_language (hb_face_t *face,
472 unsigned int script_index,
473 hb_tag_t language_tag,
474 unsigned int *language_index)
476 ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
477 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
478 const Script &s = get_gsubgpos_table (layout, table_tag).get_script (script_index);
480 if (s.find_lang_sys_index (language_tag, language_index))
483 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
484 if (s.find_lang_sys_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, language_index))
487 if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
492 hb_ot_layout_language_get_required_feature_index (hb_face_t *face,
494 unsigned int script_index,
495 unsigned int language_index,
496 unsigned int *feature_index)
498 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
499 const LangSys &l = get_gsubgpos_table (layout, table_tag).get_script (script_index).get_lang_sys (language_index);
501 if (feature_index) *feature_index = l.get_required_feature_index ();
503 return l.has_required_feature ();
507 hb_ot_layout_language_get_feature_count (hb_face_t *face,
509 unsigned int script_index,
510 unsigned int language_index)
512 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
513 const LangSys &l = get_gsubgpos_table (layout, table_tag).get_script (script_index).get_lang_sys (language_index);
515 return l.get_feature_count ();
519 hb_ot_layout_language_get_feature_index (hb_face_t *face,
521 unsigned int script_index,
522 unsigned int language_index,
523 unsigned int num_feature)
525 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
526 const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
527 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
529 return l.get_feature_index (num_feature);
533 hb_ot_layout_language_get_feature_tag (hb_face_t *face,
535 unsigned int script_index,
536 unsigned int language_index,
537 unsigned int num_feature)
539 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
540 const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
541 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
542 unsigned int feature_index = l.get_feature_index (num_feature);
544 return g.get_feature_tag (feature_index);
549 hb_ot_layout_language_find_feature (hb_face_t *face,
551 unsigned int script_index,
552 unsigned int language_index,
553 hb_tag_t feature_tag,
554 unsigned int *feature_index)
556 ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
557 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
558 const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
559 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
561 unsigned int num_features = l.get_feature_count ();
562 for (unsigned int i = 0; i < num_features; i++) {
563 unsigned int f_index = l.get_feature_index (i);
565 if (feature_tag == g.get_feature_tag (f_index)) {
566 if (feature_index) *feature_index = f_index;
571 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
576 hb_ot_layout_feature_get_lookup_count (hb_face_t *face,
578 unsigned int feature_index)
580 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
581 const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
582 const Feature &f = g.get_feature (feature_index);
584 return f.get_lookup_count ();
588 hb_ot_layout_feature_get_lookup_index (hb_face_t *face,
590 unsigned int feature_index,
591 unsigned int num_lookup)
593 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
594 const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
595 const Feature &f = g.get_feature (feature_index);
597 return f.get_lookup_index (num_lookup);
605 hb_ot_layout_has_substitution (hb_face_t *face)
607 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
608 return layout->gsub != &Null(GSUB);
612 hb_ot_layout_substitute_lookup (hb_face_t *face,
614 unsigned int lookup_index,
615 hb_ot_layout_feature_mask_t mask)
617 hb_ot_layout_context_t context;
619 context.layout = _hb_ot_face_get_layout (face);
620 return context.layout->gsub->substitute_lookup (&context, buffer, lookup_index, mask);
628 hb_ot_layout_has_positioning (hb_face_t *face)
630 hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
631 return layout->gpos != &Null(GPOS);
635 hb_ot_layout_position_lookup (hb_face_t *face,
638 unsigned int lookup_index,
639 hb_ot_layout_feature_mask_t mask)
641 hb_ot_layout_context_t context;
643 context.layout = _hb_ot_face_get_layout (font->face);
644 return context.layout->gpos->position_lookup (&context, buffer, lookup_index, mask);