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