hb-ot-layout.cc revision 23c86aa0009324433e78fcd0c47f2c0ff14b1949
1/* 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. 5 * 6 * This is part of HarfBuzz, an OpenType Layout engine library. 7 * 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. 13 * 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 18 * DAMAGE. 19 * 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. 25 * 26 * Red Hat Author(s): Behdad Esfahbod 27 */ 28 29#define HB_OT_LAYOUT_CC 30 31#include "hb-ot-layout-private.h" 32 33#include "hb-ot-layout-gdef-private.hh" 34#include "hb-ot-layout-gsub-private.hh" 35#include "hb-ot-layout-gpos-private.hh" 36 37 38#include <stdlib.h> 39#include <string.h> 40 41 42void 43_hb_ot_layout_init (hb_ot_layout_t *layout) 44{ 45 layout->gdef = NULL; 46 layout->gsub = NULL; 47 layout->gpos = NULL; 48} 49 50void 51_hb_ot_layout_fini (hb_ot_layout_t *layout) 52{ 53} 54 55static hb_ot_layout_t * 56_hb_ot_face_get_layout (hb_face_t *face) 57{ 58 return &face->ot_layout; 59} 60 61static const GDEF& 62_get_gdef (hb_face_t *face) 63{ 64#if 0 65 if (HB_UNLIKELY (!layout->face)) 66 return Null(GDEF); 67 68 if (HB_UNLIKELY (!layout->gdef)) { 69 hb_blob_t *blob = hb_face_get_table (face, HB_OT_TAG_GDEF); 70 unsigned int length; 71 const char *data = hb_blob_get_data (blob, 72 layout->gdef = &GDEF::get_for_data (font.get_table_data (face.get_table_by_tag (GDEF::Tag))); 73 layout->gdef = &Null(GDEF); 74 } 75 76 return *layout->gdef; 77#endif 78} 79 80static const GSUB& 81_get_gsub (hb_face_t *face) 82{ 83 return Null(GSUB); 84} 85 86static const GPOS& 87_get_gpos (hb_face_t *face) 88{ 89 return Null(GPOS); 90} 91 92 93/* 94 * GDEF 95 */ 96 97/* TODO the public class_t is a mess */ 98 99hb_bool_t 100hb_ot_layout_has_font_glyph_classes (hb_face_t *face) 101{ 102 return _get_gdef (face).has_glyph_classes (); 103} 104 105HB_INTERNAL hb_bool_t 106_hb_ot_layout_has_new_glyph_classes (hb_face_t *face) 107{ 108 return face->ot_layout.new_gdef.len > 0; 109} 110 111static unsigned int 112_hb_ot_layout_get_glyph_property (hb_face_t *face, 113 hb_codepoint_t glyph) 114{ 115 hb_ot_layout_class_t klass; 116 const GDEF &gdef = _get_gdef (face); 117 118 klass = gdef.get_glyph_class (glyph); 119 120 if (!klass && glyph < face->ot_layout.new_gdef.len) 121 klass = face->ot_layout.new_gdef.klasses[glyph]; 122 123 switch (klass) { 124 default: 125 case GDEF::UnclassifiedGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED; 126 case GDEF::BaseGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH; 127 case GDEF::LigatureGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE; 128 case GDEF::ComponentGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT; 129 case GDEF::MarkGlyph: 130 /* TODO old harfbuzz doesn't always parse mark attachments as it says it was 131 * introduced without a version bump, so it may not be safe */ 132 klass = gdef.get_mark_attachment_type (glyph); 133 return HB_OT_LAYOUT_GLYPH_CLASS_MARK + (klass << 8); 134 } 135} 136 137HB_INTERNAL hb_bool_t 138_hb_ot_layout_check_glyph_property (hb_face_t *face, 139 hb_internal_glyph_info_t *ginfo, 140 unsigned int lookup_flags, 141 unsigned int *property_out) 142{ 143 unsigned int property; 144 145 if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN) 146 ginfo->gproperty = _hb_ot_layout_get_glyph_property (face, ginfo->codepoint); 147 property = ginfo->gproperty; 148 if (property_out) 149 *property_out = property; 150 151 /* Not covered, if, for example, glyph class is ligature and 152 * lookup_flags includes LookupFlags::IgnoreLigatures 153 */ 154 if (property & lookup_flags) 155 return false; 156 157 if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) 158 { 159 /* If using mark filtering sets, the high short of 160 * lookup_flags has the set index. 161 */ 162 if (lookup_flags & LookupFlag::UseMarkFilteringSet) 163 return _get_gdef (face).mark_set_covers (lookup_flags >> 16, ginfo->codepoint); 164 165 /* The second byte of lookup_flags has the meaning 166 * "ignore marks of attachment type different than 167 * the attachment type specified." 168 */ 169 if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType) 170 return (lookup_flags & LookupFlag::MarkAttachmentType) == (property & LookupFlag::MarkAttachmentType); 171 } 172 173 return true; 174} 175 176HB_INTERNAL hb_bool_t 177_hb_ot_layout_skip_mark (hb_face_t *face, 178 hb_internal_glyph_info_t *ginfo, 179 unsigned int lookup_flags, 180 unsigned int *property_out) 181{ 182 unsigned int property; 183 184 if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN) 185 ginfo->gproperty = _hb_ot_layout_get_glyph_property (face, ginfo->codepoint); 186 property = ginfo->gproperty; 187 if (property_out) 188 *property_out = property; 189 190 if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) 191 { 192 /* Skip mark if lookup_flags includes LookupFlags::IgnoreMarks */ 193 if (lookup_flags & LookupFlag::IgnoreMarks) 194 return true; 195 196 /* If using mark filtering sets, the high short of lookup_flags has the set index. */ 197 if (lookup_flags & LookupFlag::UseMarkFilteringSet) 198 return !_get_gdef (face).mark_set_covers (lookup_flags >> 16, ginfo->codepoint); 199 200 /* The second byte of lookup_flags has the meaning "ignore marks of attachment type 201 * different than the attachment type specified." */ 202 if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType) 203 return (lookup_flags & LookupFlag::MarkAttachmentType) != (property & LookupFlag::MarkAttachmentType); 204 } 205 206 return false; 207} 208 209HB_INTERNAL void 210_hb_ot_layout_set_glyph_class (hb_face_t *face, 211 hb_codepoint_t glyph, 212 hb_ot_layout_glyph_class_t klass) 213{ 214 if (HB_OBJECT_IS_INERT (face)) 215 return; 216 217 /* TODO optimize this? similar to old harfbuzz code for example */ 218 219 hb_ot_layout_t *layout = &face->ot_layout; 220 hb_ot_layout_class_t gdef_klass; 221 int len = layout->new_gdef.len; 222 223 if (HB_UNLIKELY (glyph > 65535)) 224 return; 225 226 /* XXX this is not threadsafe */ 227 if (glyph >= len) { 228 int new_len; 229 unsigned char *new_klasses; 230 231 new_len = len == 0 ? 120 : 2 * len; 232 if (new_len > 65535) 233 new_len = 65535; 234 new_klasses = (unsigned char *) realloc (layout->new_gdef.klasses, new_len * sizeof (unsigned char)); 235 236 if (HB_UNLIKELY (!new_klasses)) 237 return; 238 239 memset (new_klasses + len, 0, new_len - len); 240 241 layout->new_gdef.klasses = new_klasses; 242 layout->new_gdef.len = new_len; 243 } 244 245 switch (klass) { 246 default: 247 case HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED: gdef_klass = GDEF::UnclassifiedGlyph; break; 248 case HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH: gdef_klass = GDEF::BaseGlyph; break; 249 case HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE: gdef_klass = GDEF::LigatureGlyph; break; 250 case HB_OT_LAYOUT_GLYPH_CLASS_MARK: gdef_klass = GDEF::MarkGlyph; break; 251 case HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT: gdef_klass = GDEF::ComponentGlyph; break; 252 } 253 254 layout->new_gdef.klasses[glyph] = gdef_klass; 255 return; 256} 257 258HB_INTERNAL void 259_hb_ot_layout_set_glyph_property (hb_face_t *face, 260 hb_codepoint_t glyph, 261 unsigned int property) 262{ _hb_ot_layout_set_glyph_class (face, glyph, (hb_ot_layout_glyph_class_t) (property & 0xff)); } 263 264 265hb_ot_layout_glyph_class_t 266hb_ot_layout_get_glyph_class (hb_face_t *face, 267 hb_codepoint_t glyph) 268{ 269 return (hb_ot_layout_glyph_class_t) (_hb_ot_layout_get_glyph_property (face, glyph) & 0xff); 270} 271 272void 273hb_ot_layout_set_glyph_class (hb_face_t *face, 274 hb_codepoint_t glyph, 275 hb_ot_layout_glyph_class_t klass) 276{ 277 _hb_ot_layout_set_glyph_class (face, glyph, klass); 278} 279 280void 281hb_ot_layout_build_glyph_classes (hb_face_t *face, 282 uint16_t num_total_glyphs, 283 hb_codepoint_t *glyphs, 284 unsigned char *klasses, 285 uint16_t count) 286{ 287 if (HB_OBJECT_IS_INERT (face)) 288 return; 289 290 hb_ot_layout_t *layout = &face->ot_layout; 291 292 if (HB_UNLIKELY (!count || !glyphs || !klasses)) 293 return; 294 295 if (layout->new_gdef.len == 0) { 296 layout->new_gdef.klasses = (unsigned char *) calloc (num_total_glyphs, sizeof (unsigned char)); 297 layout->new_gdef.len = count; 298 } 299 300 for (unsigned int i = 0; i < count; i++) 301 _hb_ot_layout_set_glyph_class (face, glyphs[i], (hb_ot_layout_glyph_class_t) klasses[i]); 302} 303 304hb_bool_t 305hb_ot_layout_get_attach_points (hb_face_t *face, 306 hb_codepoint_t glyph, 307 unsigned int *point_count /* IN/OUT */, 308 unsigned int *point_array /* OUT */) 309{ 310 return _get_gdef (face).get_attach_points (glyph, point_count, point_array); 311} 312 313hb_bool_t 314hb_ot_layout_get_lig_carets (hb_face_t *face, 315 hb_font_t *font, 316 hb_codepoint_t glyph, 317 unsigned int *caret_count /* IN/OUT */, 318 int *caret_array /* OUT */) 319{ 320 hb_ot_layout_context_t context; 321 context.font = font; 322 context.face = face; 323 return _get_gdef (face).get_lig_carets (&context, glyph, caret_count, caret_array); 324} 325 326/* 327 * GSUB/GPOS 328 */ 329 330static const GSUBGPOS& 331get_gsubgpos_table (hb_face_t *face, 332 hb_tag_t table_tag) 333{ 334 switch (table_tag) { 335 case HB_OT_TAG_GSUB: return _get_gsub (face); 336 case HB_OT_TAG_GPOS: return _get_gpos (face); 337 default: return Null(GSUBGPOS); 338 } 339} 340 341 342unsigned int 343hb_ot_layout_table_get_script_count (hb_face_t *face, 344 hb_tag_t table_tag) 345{ 346 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 347 348 return g.get_script_count (); 349} 350 351hb_tag_t 352hb_ot_layout_table_get_script_tag (hb_face_t *face, 353 hb_tag_t table_tag, 354 unsigned int script_index) 355{ 356 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 357 358 return g.get_script_tag (script_index); 359} 360 361hb_bool_t 362hb_ot_layout_table_find_script (hb_face_t *face, 363 hb_tag_t table_tag, 364 hb_tag_t script_tag, 365 unsigned int *script_index) 366{ 367 ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 368 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 369 370 if (g.find_script_index (script_tag, script_index)) 371 return TRUE; 372 373 /* try finding 'DFLT' */ 374 if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_SCRIPT, script_index)) 375 return FALSE; 376 377 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 378 if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, script_index)) 379 return FALSE; 380 381 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 382 return FALSE; 383} 384 385unsigned int 386hb_ot_layout_table_get_feature_count (hb_face_t *face, 387 hb_tag_t table_tag) 388{ 389 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 390 391 return g.get_feature_count (); 392} 393 394hb_tag_t 395hb_ot_layout_table_get_feature_tag (hb_face_t *face, 396 hb_tag_t table_tag, 397 unsigned int feature_index) 398{ 399 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 400 401 return g.get_feature_tag (feature_index); 402} 403 404hb_bool_t 405hb_ot_layout_table_find_feature (hb_face_t *face, 406 hb_tag_t table_tag, 407 hb_tag_t feature_tag, 408 unsigned int *feature_index) 409{ 410 ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); 411 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 412 413 if (g.find_feature_index (feature_tag, feature_index)) 414 return TRUE; 415 416 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 417 return FALSE; 418} 419 420unsigned int 421hb_ot_layout_table_get_lookup_count (hb_face_t *face, 422 hb_tag_t table_tag) 423{ 424 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 425 426 return g.get_lookup_count (); 427} 428 429 430unsigned int 431hb_ot_layout_script_get_language_count (hb_face_t *face, 432 hb_tag_t table_tag, 433 unsigned int script_index) 434{ 435 const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 436 437 return s.get_lang_sys_count (); 438} 439 440hb_tag_t 441hb_ot_layout_script_get_language_tag (hb_face_t *face, 442 hb_tag_t table_tag, 443 unsigned int script_index, 444 unsigned int language_index) 445{ 446 const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 447 448 return s.get_lang_sys_tag (language_index); 449} 450 451hb_bool_t 452hb_ot_layout_script_find_language (hb_face_t *face, 453 hb_tag_t table_tag, 454 unsigned int script_index, 455 hb_tag_t language_tag, 456 unsigned int *language_index) 457{ 458 ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); 459 const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 460 461 if (s.find_lang_sys_index (language_tag, language_index)) 462 return TRUE; 463 464 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 465 if (s.find_lang_sys_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, language_index)) 466 return FALSE; 467 468 if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; 469 return FALSE; 470} 471 472hb_bool_t 473hb_ot_layout_language_get_required_feature_index (hb_face_t *face, 474 hb_tag_t table_tag, 475 unsigned int script_index, 476 unsigned int language_index, 477 unsigned int *feature_index) 478{ 479 const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index); 480 481 if (feature_index) *feature_index = l.get_required_feature_index (); 482 483 return l.has_required_feature (); 484} 485 486unsigned int 487hb_ot_layout_language_get_feature_count (hb_face_t *face, 488 hb_tag_t table_tag, 489 unsigned int script_index, 490 unsigned int language_index) 491{ 492 const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index); 493 494 return l.get_feature_count (); 495} 496 497unsigned int 498hb_ot_layout_language_get_feature_index (hb_face_t *face, 499 hb_tag_t table_tag, 500 unsigned int script_index, 501 unsigned int language_index, 502 unsigned int num_feature) 503{ 504 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 505 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 506 507 return l.get_feature_index (num_feature); 508} 509 510hb_tag_t 511hb_ot_layout_language_get_feature_tag (hb_face_t *face, 512 hb_tag_t table_tag, 513 unsigned int script_index, 514 unsigned int language_index, 515 unsigned int num_feature) 516{ 517 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 518 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 519 unsigned int feature_index = l.get_feature_index (num_feature); 520 521 return g.get_feature_tag (feature_index); 522} 523 524 525hb_bool_t 526hb_ot_layout_language_find_feature (hb_face_t *face, 527 hb_tag_t table_tag, 528 unsigned int script_index, 529 unsigned int language_index, 530 hb_tag_t feature_tag, 531 unsigned int *feature_index) 532{ 533 ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); 534 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 535 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 536 537 unsigned int num_features = l.get_feature_count (); 538 for (unsigned int i = 0; i < num_features; i++) { 539 unsigned int f_index = l.get_feature_index (i); 540 541 if (feature_tag == g.get_feature_tag (f_index)) { 542 if (feature_index) *feature_index = f_index; 543 return TRUE; 544 } 545 } 546 547 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 548 return FALSE; 549} 550 551unsigned int 552hb_ot_layout_feature_get_lookup_count (hb_face_t *face, 553 hb_tag_t table_tag, 554 unsigned int feature_index) 555{ 556 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 557 const Feature &f = g.get_feature (feature_index); 558 559 return f.get_lookup_count (); 560} 561 562unsigned int 563hb_ot_layout_feature_get_lookup_index (hb_face_t *face, 564 hb_tag_t table_tag, 565 unsigned int feature_index, 566 unsigned int num_lookup) 567{ 568 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 569 const Feature &f = g.get_feature (feature_index); 570 571 return f.get_lookup_index (num_lookup); 572} 573 574/* 575 * GSUB 576 */ 577 578hb_bool_t 579hb_ot_layout_has_substitution (hb_face_t *face) 580{ 581 return &_get_gsub (face) != &Null(GSUB); 582} 583 584hb_bool_t 585hb_ot_layout_substitute_lookup (hb_face_t *face, 586 hb_buffer_t *buffer, 587 unsigned int lookup_index, 588 hb_ot_layout_feature_mask_t mask) 589{ 590 hb_ot_layout_context_t context; 591 context.font = NULL; 592 context.face = face; 593 return _get_gsub (face).substitute_lookup (&context, buffer, lookup_index, mask); 594} 595 596/* 597 * GPOS 598 */ 599 600hb_bool_t 601hb_ot_layout_has_positioning (hb_face_t *face) 602{ 603 return &_get_gpos (face) != &Null(GPOS); 604} 605 606hb_bool_t 607hb_ot_layout_position_lookup (hb_face_t *face, 608 hb_font_t *font, 609 hb_buffer_t *buffer, 610 unsigned int lookup_index, 611 hb_ot_layout_feature_mask_t mask) 612{ 613 hb_ot_layout_context_t context; 614 context.font = font; 615 context.face = face; 616 return _get_gpos (face).position_lookup (&context, buffer, lookup_index, mask); 617} 618