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