hb-ot-layout.cc revision ea278d3895fe0c92801d692cd71d8d9f1de7c048
1/* 2 * Copyright © 1998-2004 David Turner and Werner Lemberg 3 * Copyright © 2006 Behdad Esfahbod 4 * Copyright © 2007,2008,2009 Red Hat, Inc. 5 * 6 * This is part of HarfBuzz, a text shaping 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#include "hb-ot-layout-private.hh" 30 31#include "hb-ot-layout-gdef-table.hh" 32#include "hb-ot-layout-gsub-table.hh" 33#include "hb-ot-layout-gpos-table.hh" 34#include "hb-ot-maxp-table.hh" 35 36 37#include <stdlib.h> 38#include <string.h> 39 40 41 42hb_ot_layout_t * 43_hb_ot_layout_create (hb_face_t *face) 44{ 45 /* TODO Remove this object altogether */ 46 hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t)); 47 48 layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GDEF)); 49 layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob); 50 51 layout->gsub_blob = Sanitizer<GSUB>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GSUB)); 52 layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_blob); 53 54 layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GPOS)); 55 layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob); 56 57 return layout; 58} 59 60void 61_hb_ot_layout_destroy (hb_ot_layout_t *layout) 62{ 63 hb_blob_destroy (layout->gdef_blob); 64 hb_blob_destroy (layout->gsub_blob); 65 hb_blob_destroy (layout->gpos_blob); 66 67 free (layout); 68} 69 70static inline const GDEF& 71_get_gdef (hb_face_t *face) 72{ 73 /* XXX ensure ot_layout, and speed up */ 74 return *hb_ot_layout_from_face (face)->gdef; 75} 76static inline const GSUB& 77_get_gsub (hb_face_t *face) 78{ 79 /* XXX ensure ot_layout, and speed up */ 80 return *hb_ot_layout_from_face (face)->gsub; 81} 82static inline const GPOS& 83_get_gpos (hb_face_t *face) 84{ 85 /* XXX ensure ot_layout, and speed up */ 86 return *hb_ot_layout_from_face (face)->gpos; 87} 88 89 90/* 91 * GDEF 92 */ 93 94hb_bool_t 95hb_ot_layout_has_glyph_classes (hb_face_t *face) 96{ 97 return _get_gdef (face).has_glyph_classes (); 98} 99 100static inline unsigned int 101_hb_ot_layout_get_glyph_property (hb_face_t *face, 102 hb_glyph_info_t *info) 103{ 104 if (!info->props_cache()) 105 { 106 const GDEF &gdef = _get_gdef (face); 107 info->props_cache() = gdef.get_glyph_props (info->codepoint); 108 } 109 110 return info->props_cache(); 111} 112 113static inline hb_bool_t 114_hb_ot_layout_match_properties_mark (hb_face_t *face, 115 hb_codepoint_t glyph, 116 unsigned int glyph_props, 117 unsigned int lookup_props) 118{ 119 /* If using mark filtering sets, the high short of 120 * lookup_props has the set index. 121 */ 122 if (lookup_props & LookupFlag::UseMarkFilteringSet) 123 return _get_gdef (face).mark_set_covers (lookup_props >> 16, glyph); 124 125 /* The second byte of lookup_props has the meaning 126 * "ignore marks of attachment type different than 127 * the attachment type specified." 128 */ 129 if (lookup_props & LookupFlag::MarkAttachmentType) 130 return (lookup_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType); 131 132 return true; 133} 134 135static inline hb_bool_t 136_hb_ot_layout_match_properties (hb_face_t *face, 137 hb_codepoint_t glyph, 138 unsigned int glyph_props, 139 unsigned int lookup_props) 140{ 141 /* Not covered, if, for example, glyph class is ligature and 142 * lookup_props includes LookupFlags::IgnoreLigatures 143 */ 144 if (glyph_props & lookup_props & LookupFlag::IgnoreFlags) 145 return false; 146 147 if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_CLASS_MARK)) 148 return _hb_ot_layout_match_properties_mark (face, glyph, glyph_props, lookup_props); 149 150 return true; 151} 152 153hb_bool_t 154_hb_ot_layout_check_glyph_property (hb_face_t *face, 155 hb_glyph_info_t *ginfo, 156 unsigned int lookup_props, 157 unsigned int *property_out) 158{ 159 unsigned int property; 160 161 property = _hb_ot_layout_get_glyph_property (face, ginfo); 162 *property_out = property; 163 164 return _hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props); 165} 166 167hb_bool_t 168_hb_ot_layout_skip_mark (hb_face_t *face, 169 hb_glyph_info_t *ginfo, 170 unsigned int lookup_props, 171 unsigned int *property_out) 172{ 173 unsigned int property; 174 175 property = _hb_ot_layout_get_glyph_property (face, ginfo); 176 if (property_out) 177 *property_out = property; 178 179 /* If it's a mark, skip it if we don't accept it. */ 180 if (unlikely (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)) 181 return !_hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props); 182 183 /* If not a mark, don't skip. */ 184 return false; 185} 186 187 188 189unsigned int 190hb_ot_layout_get_attach_points (hb_face_t *face, 191 hb_codepoint_t glyph, 192 unsigned int start_offset, 193 unsigned int *point_count /* IN/OUT */, 194 unsigned int *point_array /* OUT */) 195{ 196 return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array); 197} 198 199unsigned int 200hb_ot_layout_get_ligature_carets (hb_font_t *font, 201 hb_direction_t direction, 202 hb_codepoint_t glyph, 203 unsigned int start_offset, 204 unsigned int *caret_count /* IN/OUT */, 205 int *caret_array /* OUT */) 206{ 207 return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); 208} 209 210/* 211 * GSUB/GPOS 212 */ 213 214static const GSUBGPOS& 215get_gsubgpos_table (hb_face_t *face, 216 hb_tag_t table_tag) 217{ 218 switch (table_tag) { 219 case HB_OT_TAG_GSUB: return _get_gsub (face); 220 case HB_OT_TAG_GPOS: return _get_gpos (face); 221 default: return Null(GSUBGPOS); 222 } 223} 224 225 226unsigned int 227hb_ot_layout_table_get_script_tags (hb_face_t *face, 228 hb_tag_t table_tag, 229 unsigned int start_offset, 230 unsigned int *script_count /* IN/OUT */, 231 hb_tag_t *script_tags /* OUT */) 232{ 233 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 234 235 return g.get_script_tags (start_offset, script_count, script_tags); 236} 237 238hb_bool_t 239hb_ot_layout_table_find_script (hb_face_t *face, 240 hb_tag_t table_tag, 241 hb_tag_t script_tag, 242 unsigned int *script_index) 243{ 244 ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 245 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 246 247 if (g.find_script_index (script_tag, script_index)) 248 return true; 249 250 /* try finding 'DFLT' */ 251 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) 252 return false; 253 254 /* try with 'dflt'; MS site has had typos and many fonts use it now :(. 255 * including many versions of DejaVu Sans Mono! */ 256 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) 257 return false; 258 259 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 260 return false; 261} 262 263hb_bool_t 264hb_ot_layout_table_choose_script (hb_face_t *face, 265 hb_tag_t table_tag, 266 const hb_tag_t *script_tags, 267 unsigned int *script_index, 268 hb_tag_t *chosen_script) 269{ 270 ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 271 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 272 273 while (*script_tags) 274 { 275 if (g.find_script_index (*script_tags, script_index)) { 276 if (chosen_script) 277 *chosen_script = *script_tags; 278 return true; 279 } 280 script_tags++; 281 } 282 283 /* try finding 'DFLT' */ 284 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { 285 if (chosen_script) 286 *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; 287 return false; 288 } 289 290 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 291 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) { 292 if (chosen_script) 293 *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE; 294 return false; 295 } 296 297 /* try with 'latn'; some old fonts put their features there even though 298 they're really trying to support Thai, for example :( */ 299#define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') 300 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { 301 if (chosen_script) 302 *chosen_script = HB_OT_TAG_LATIN_SCRIPT; 303 return false; 304 } 305 306 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 307 if (chosen_script) 308 *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 309 return false; 310} 311 312unsigned int 313hb_ot_layout_table_get_feature_tags (hb_face_t *face, 314 hb_tag_t table_tag, 315 unsigned int start_offset, 316 unsigned int *feature_count /* IN/OUT */, 317 hb_tag_t *feature_tags /* OUT */) 318{ 319 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 320 321 return g.get_feature_tags (start_offset, feature_count, feature_tags); 322} 323 324 325unsigned int 326hb_ot_layout_script_get_language_tags (hb_face_t *face, 327 hb_tag_t table_tag, 328 unsigned int script_index, 329 unsigned int start_offset, 330 unsigned int *language_count /* IN/OUT */, 331 hb_tag_t *language_tags /* OUT */) 332{ 333 const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 334 335 return s.get_lang_sys_tags (start_offset, language_count, language_tags); 336} 337 338hb_bool_t 339hb_ot_layout_script_find_language (hb_face_t *face, 340 hb_tag_t table_tag, 341 unsigned int script_index, 342 hb_tag_t language_tag, 343 unsigned int *language_index) 344{ 345 ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); 346 const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 347 348 if (s.find_lang_sys_index (language_tag, language_index)) 349 return true; 350 351 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 352 if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) 353 return false; 354 355 if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; 356 return false; 357} 358 359hb_bool_t 360hb_ot_layout_language_get_required_feature_index (hb_face_t *face, 361 hb_tag_t table_tag, 362 unsigned int script_index, 363 unsigned int language_index, 364 unsigned int *feature_index) 365{ 366 const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index); 367 368 if (feature_index) *feature_index = l.get_required_feature_index (); 369 370 return l.has_required_feature (); 371} 372 373unsigned int 374hb_ot_layout_language_get_feature_indexes (hb_face_t *face, 375 hb_tag_t table_tag, 376 unsigned int script_index, 377 unsigned int language_index, 378 unsigned int start_offset, 379 unsigned int *feature_count /* IN/OUT */, 380 unsigned int *feature_indexes /* OUT */) 381{ 382 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 383 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 384 385 return l.get_feature_indexes (start_offset, feature_count, feature_indexes); 386} 387 388unsigned int 389hb_ot_layout_language_get_feature_tags (hb_face_t *face, 390 hb_tag_t table_tag, 391 unsigned int script_index, 392 unsigned int language_index, 393 unsigned int start_offset, 394 unsigned int *feature_count /* IN/OUT */, 395 hb_tag_t *feature_tags /* OUT */) 396{ 397 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 398 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 399 400 ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t)); 401 unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); 402 403 if (feature_tags) { 404 unsigned int count = *feature_count; 405 for (unsigned int i = 0; i < count; i++) 406 feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); 407 } 408 409 return ret; 410} 411 412 413hb_bool_t 414hb_ot_layout_language_find_feature (hb_face_t *face, 415 hb_tag_t table_tag, 416 unsigned int script_index, 417 unsigned int language_index, 418 hb_tag_t feature_tag, 419 unsigned int *feature_index) 420{ 421 ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); 422 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 423 const LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 424 425 unsigned int num_features = l.get_feature_count (); 426 for (unsigned int i = 0; i < num_features; i++) { 427 unsigned int f_index = l.get_feature_index (i); 428 429 if (feature_tag == g.get_feature_tag (f_index)) { 430 if (feature_index) *feature_index = f_index; 431 return true; 432 } 433 } 434 435 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 436 return false; 437} 438 439unsigned int 440hb_ot_layout_feature_get_lookup_indexes (hb_face_t *face, 441 hb_tag_t table_tag, 442 unsigned int feature_index, 443 unsigned int start_offset, 444 unsigned int *lookup_count /* IN/OUT */, 445 unsigned int *lookup_indexes /* OUT */) 446{ 447 const GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 448 const Feature &f = g.get_feature (feature_index); 449 450 return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); 451} 452 453 454/* 455 * GSUB 456 */ 457 458hb_bool_t 459hb_ot_layout_has_substitution (hb_face_t *face) 460{ 461 return &_get_gsub (face) != &Null(GSUB); 462} 463 464hb_bool_t 465hb_ot_layout_would_substitute_lookup (hb_face_t *face, 466 const hb_codepoint_t *glyphs, 467 unsigned int glyphs_length, 468 unsigned int lookup_index) 469{ 470 if (unlikely (glyphs_length < 1 || glyphs_length > 2)) return false; 471 hb_would_apply_context_t c (face, glyphs[0], glyphs_length == 2 ? glyphs[1] : -1); 472 return _get_gsub (face).would_substitute_lookup (&c, lookup_index); 473} 474 475void 476hb_ot_layout_substitute_start (hb_buffer_t *buffer) 477{ 478 GSUB::substitute_start (buffer); 479} 480 481hb_bool_t 482hb_ot_layout_substitute_lookup (hb_face_t *face, 483 hb_buffer_t *buffer, 484 unsigned int lookup_index, 485 hb_mask_t mask) 486{ 487 hb_apply_context_t c (NULL, face, buffer, mask); 488 return _get_gsub (face).substitute_lookup (&c, lookup_index); 489} 490 491void 492hb_ot_layout_substitute_finish (hb_buffer_t *buffer HB_UNUSED) 493{ 494 GSUB::substitute_finish (buffer); 495} 496 497void 498hb_ot_layout_substitute_closure_lookup (hb_face_t *face, 499 hb_set_t *glyphs, 500 unsigned int lookup_index) 501{ 502 hb_closure_context_t c (face, glyphs); 503 _get_gsub (face).closure_lookup (&c, lookup_index); 504} 505 506/* 507 * GPOS 508 */ 509 510hb_bool_t 511hb_ot_layout_has_positioning (hb_face_t *face) 512{ 513 return &_get_gpos (face) != &Null(GPOS); 514} 515 516void 517hb_ot_layout_position_start (hb_buffer_t *buffer) 518{ 519 GPOS::position_start (buffer); 520} 521 522hb_bool_t 523hb_ot_layout_position_lookup (hb_font_t *font, 524 hb_buffer_t *buffer, 525 unsigned int lookup_index, 526 hb_mask_t mask) 527{ 528 hb_apply_context_t c (font, font->face, buffer, mask); 529 return _get_gpos (font->face).position_lookup (&c, lookup_index); 530} 531 532void 533hb_ot_layout_position_finish (hb_buffer_t *buffer) 534{ 535 GPOS::position_finish (buffer); 536} 537 538 539