hb-ot-layout.cc revision 89ca8eeb83fedde06727d386369a0a39d410f12b
1/* 2 * Copyright © 1998-2004 David Turner and Werner Lemberg 3 * Copyright © 2006 Behdad Esfahbod 4 * Copyright © 2007,2008,2009 Red Hat, Inc. 5 * Copyright © 2012 Google, Inc. 6 * 7 * This is part of HarfBuzz, a text shaping library. 8 * 9 * Permission is hereby granted, without written agreement and without 10 * license or royalty fees, to use, copy, modify, and distribute this 11 * software and its documentation for any purpose, provided that the 12 * above copyright notice and the following two paragraphs appear in 13 * all copies of this software. 14 * 15 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 16 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 17 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 18 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 19 * DAMAGE. 20 * 21 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 22 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 23 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 24 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 25 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 26 * 27 * Red Hat Author(s): Behdad Esfahbod 28 * Google Author(s): Behdad Esfahbod 29 */ 30 31#include "hb-ot-layout-private.hh" 32 33#include "hb-ot-layout-gdef-table.hh" 34#include "hb-ot-layout-gsub-table.hh" 35#include "hb-ot-layout-gpos-table.hh" 36#include "hb-ot-maxp-table.hh" 37 38#include <stdlib.h> 39#include <string.h> 40 41 42HB_SHAPER_DATA_ENSURE_DECLARE(ot, face) 43 44hb_ot_layout_t * 45_hb_ot_layout_create (hb_face_t *face) 46{ 47 hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t)); 48 if (unlikely (!layout)) 49 return NULL; 50 51 layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF)); 52 layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob); 53 54 layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB)); 55 layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob); 56 57 layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS)); 58 layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob); 59 60 layout->gsub_lookup_count = layout->gsub->get_lookup_count (); 61 layout->gpos_lookup_count = layout->gpos->get_lookup_count (); 62 63 layout->gsub_digests = (hb_set_digest_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_set_digest_t)); 64 layout->gpos_digests = (hb_set_digest_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_set_digest_t)); 65 66 if (unlikely ((layout->gsub_lookup_count && !layout->gsub_digests) || 67 (layout->gpos_lookup_count && !layout->gpos_digests))) 68 { 69 _hb_ot_layout_destroy (layout); 70 return NULL; 71 } 72 73 for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) 74 layout->gsub->add_coverage (&layout->gsub_digests[i], i); 75 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) 76 layout->gpos->add_coverage (&layout->gpos_digests[i], i); 77 78 return layout; 79} 80 81void 82_hb_ot_layout_destroy (hb_ot_layout_t *layout) 83{ 84 hb_blob_destroy (layout->gdef_blob); 85 hb_blob_destroy (layout->gsub_blob); 86 hb_blob_destroy (layout->gpos_blob); 87 88 free (layout->gsub_digests); 89 free (layout->gpos_digests); 90 91 free (layout); 92} 93 94static inline const OT::GDEF& 95_get_gdef (hb_face_t *face) 96{ 97 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF); 98 return *hb_ot_layout_from_face (face)->gdef; 99} 100static inline const OT::GSUB& 101_get_gsub (hb_face_t *face) 102{ 103 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB); 104 return *hb_ot_layout_from_face (face)->gsub; 105} 106static inline const OT::GPOS& 107_get_gpos (hb_face_t *face) 108{ 109 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS); 110 return *hb_ot_layout_from_face (face)->gpos; 111} 112 113 114/* 115 * GDEF 116 */ 117 118hb_bool_t 119hb_ot_layout_has_glyph_classes (hb_face_t *face) 120{ 121 return _get_gdef (face).has_glyph_classes (); 122} 123 124hb_ot_layout_glyph_class_t 125hb_ot_layout_get_glyph_class (hb_face_t *face, 126 hb_codepoint_t glyph) 127{ 128 return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph); 129} 130 131void 132hb_ot_layout_get_glyphs_in_class (hb_face_t *face, 133 hb_ot_layout_glyph_class_t klass, 134 hb_set_t *glyphs /* OUT */) 135{ 136 return _get_gdef (face).get_glyphs_in_class (klass, glyphs); 137} 138 139unsigned int 140hb_ot_layout_get_attach_points (hb_face_t *face, 141 hb_codepoint_t glyph, 142 unsigned int start_offset, 143 unsigned int *point_count /* IN/OUT */, 144 unsigned int *point_array /* OUT */) 145{ 146 return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array); 147} 148 149unsigned int 150hb_ot_layout_get_ligature_carets (hb_font_t *font, 151 hb_direction_t direction, 152 hb_codepoint_t glyph, 153 unsigned int start_offset, 154 unsigned int *caret_count /* IN/OUT */, 155 int *caret_array /* OUT */) 156{ 157 return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); 158} 159 160 161/* 162 * GSUB/GPOS 163 */ 164 165static const OT::GSUBGPOS& 166get_gsubgpos_table (hb_face_t *face, 167 hb_tag_t table_tag) 168{ 169 switch (table_tag) { 170 case HB_OT_TAG_GSUB: return _get_gsub (face); 171 case HB_OT_TAG_GPOS: return _get_gpos (face); 172 default: return OT::Null(OT::GSUBGPOS); 173 } 174} 175 176 177unsigned int 178hb_ot_layout_table_get_script_tags (hb_face_t *face, 179 hb_tag_t table_tag, 180 unsigned int start_offset, 181 unsigned int *script_count /* IN/OUT */, 182 hb_tag_t *script_tags /* OUT */) 183{ 184 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 185 186 return g.get_script_tags (start_offset, script_count, script_tags); 187} 188 189hb_bool_t 190hb_ot_layout_table_find_script (hb_face_t *face, 191 hb_tag_t table_tag, 192 hb_tag_t script_tag, 193 unsigned int *script_index) 194{ 195 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 196 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 197 198 if (g.find_script_index (script_tag, script_index)) 199 return true; 200 201 /* try finding 'DFLT' */ 202 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) 203 return false; 204 205 /* try with 'dflt'; MS site has had typos and many fonts use it now :(. 206 * including many versions of DejaVu Sans Mono! */ 207 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) 208 return false; 209 210 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 211 return false; 212} 213 214hb_bool_t 215hb_ot_layout_table_choose_script (hb_face_t *face, 216 hb_tag_t table_tag, 217 const hb_tag_t *script_tags, 218 unsigned int *script_index, 219 hb_tag_t *chosen_script) 220{ 221 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 222 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 223 224 while (*script_tags) 225 { 226 if (g.find_script_index (*script_tags, script_index)) { 227 if (chosen_script) 228 *chosen_script = *script_tags; 229 return true; 230 } 231 script_tags++; 232 } 233 234 /* try finding 'DFLT' */ 235 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { 236 if (chosen_script) 237 *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; 238 return false; 239 } 240 241 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 242 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) { 243 if (chosen_script) 244 *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE; 245 return false; 246 } 247 248 /* try with 'latn'; some old fonts put their features there even though 249 they're really trying to support Thai, for example :( */ 250#define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') 251 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { 252 if (chosen_script) 253 *chosen_script = HB_OT_TAG_LATIN_SCRIPT; 254 return false; 255 } 256 257 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 258 if (chosen_script) 259 *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 260 return false; 261} 262 263unsigned int 264hb_ot_layout_table_get_feature_tags (hb_face_t *face, 265 hb_tag_t table_tag, 266 unsigned int start_offset, 267 unsigned int *feature_count /* IN/OUT */, 268 hb_tag_t *feature_tags /* OUT */) 269{ 270 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 271 272 return g.get_feature_tags (start_offset, feature_count, feature_tags); 273} 274 275 276unsigned int 277hb_ot_layout_script_get_language_tags (hb_face_t *face, 278 hb_tag_t table_tag, 279 unsigned int script_index, 280 unsigned int start_offset, 281 unsigned int *language_count /* IN/OUT */, 282 hb_tag_t *language_tags /* OUT */) 283{ 284 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 285 286 return s.get_lang_sys_tags (start_offset, language_count, language_tags); 287} 288 289hb_bool_t 290hb_ot_layout_script_find_language (hb_face_t *face, 291 hb_tag_t table_tag, 292 unsigned int script_index, 293 hb_tag_t language_tag, 294 unsigned int *language_index) 295{ 296 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); 297 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 298 299 if (s.find_lang_sys_index (language_tag, language_index)) 300 return true; 301 302 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 303 if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) 304 return false; 305 306 if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; 307 return false; 308} 309 310hb_bool_t 311hb_ot_layout_language_get_required_feature_index (hb_face_t *face, 312 hb_tag_t table_tag, 313 unsigned int script_index, 314 unsigned int language_index, 315 unsigned int *feature_index) 316{ 317 const OT::LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index); 318 319 if (feature_index) *feature_index = l.get_required_feature_index (); 320 321 return l.has_required_feature (); 322} 323 324unsigned int 325hb_ot_layout_language_get_feature_indexes (hb_face_t *face, 326 hb_tag_t table_tag, 327 unsigned int script_index, 328 unsigned int language_index, 329 unsigned int start_offset, 330 unsigned int *feature_count /* IN/OUT */, 331 unsigned int *feature_indexes /* OUT */) 332{ 333 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 334 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 335 336 return l.get_feature_indexes (start_offset, feature_count, feature_indexes); 337} 338 339unsigned int 340hb_ot_layout_language_get_feature_tags (hb_face_t *face, 341 hb_tag_t table_tag, 342 unsigned int script_index, 343 unsigned int language_index, 344 unsigned int start_offset, 345 unsigned int *feature_count /* IN/OUT */, 346 hb_tag_t *feature_tags /* OUT */) 347{ 348 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 349 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 350 351 ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t)); 352 unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); 353 354 if (feature_tags) { 355 unsigned int count = *feature_count; 356 for (unsigned int i = 0; i < count; i++) 357 feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); 358 } 359 360 return ret; 361} 362 363 364hb_bool_t 365hb_ot_layout_language_find_feature (hb_face_t *face, 366 hb_tag_t table_tag, 367 unsigned int script_index, 368 unsigned int language_index, 369 hb_tag_t feature_tag, 370 unsigned int *feature_index) 371{ 372 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); 373 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 374 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 375 376 unsigned int num_features = l.get_feature_count (); 377 for (unsigned int i = 0; i < num_features; i++) { 378 unsigned int f_index = l.get_feature_index (i); 379 380 if (feature_tag == g.get_feature_tag (f_index)) { 381 if (feature_index) *feature_index = f_index; 382 return true; 383 } 384 } 385 386 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 387 return false; 388} 389 390unsigned int 391hb_ot_layout_feature_get_lookups (hb_face_t *face, 392 hb_tag_t table_tag, 393 unsigned int feature_index, 394 unsigned int start_offset, 395 unsigned int *lookup_count /* IN/OUT */, 396 unsigned int *lookup_indexes /* OUT */) 397{ 398 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 399 const OT::Feature &f = g.get_feature (feature_index); 400 401 return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); 402} 403 404 405/* 406 * OT::GSUB 407 */ 408 409hb_bool_t 410hb_ot_layout_has_substitution (hb_face_t *face) 411{ 412 return &_get_gsub (face) != &OT::Null(OT::GSUB); 413} 414 415hb_bool_t 416hb_ot_layout_lookup_would_substitute (hb_face_t *face, 417 unsigned int lookup_index, 418 const hb_codepoint_t *glyphs, 419 unsigned int glyphs_length, 420 hb_bool_t zero_context) 421{ 422 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false; 423 return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context); 424} 425 426hb_bool_t 427hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, 428 unsigned int lookup_index, 429 const hb_codepoint_t *glyphs, 430 unsigned int glyphs_length, 431 hb_bool_t zero_context) 432{ 433 if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false; 434 OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context); 435 436 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); 437 438 return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]); 439} 440 441void 442hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) 443{ 444 OT::GSUB::substitute_start (font, buffer); 445} 446 447hb_bool_t 448hb_ot_layout_substitute_lookup (hb_font_t *font, 449 hb_buffer_t *buffer, 450 unsigned int lookup_index, 451 hb_mask_t mask) 452{ 453 if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gsub_lookup_count)) return false; 454 455 OT::hb_apply_context_t c (font, buffer, mask); 456 457 const OT::SubstLookup& l = hb_ot_layout_from_face (font->face)->gsub->get_lookup (lookup_index); 458 459 return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gsub_digests[lookup_index]); 460} 461 462void 463hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer) 464{ 465 OT::GSUB::substitute_finish (font, buffer); 466} 467 468void 469hb_ot_layout_lookup_substitute_closure (hb_face_t *face, 470 unsigned int lookup_index, 471 hb_set_t *glyphs) 472{ 473 OT::hb_closure_context_t c (face, glyphs); 474 _get_gsub (face).closure_lookup (&c, lookup_index); 475} 476 477/* 478 * OT::GPOS 479 */ 480 481hb_bool_t 482hb_ot_layout_has_positioning (hb_face_t *face) 483{ 484 return &_get_gpos (face) != &OT::Null(OT::GPOS); 485} 486 487void 488hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) 489{ 490 OT::GPOS::position_start (font, buffer); 491} 492 493hb_bool_t 494hb_ot_layout_position_lookup (hb_font_t *font, 495 hb_buffer_t *buffer, 496 unsigned int lookup_index, 497 hb_mask_t mask) 498{ 499 if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gpos_lookup_count)) return false; 500 501 OT::hb_apply_context_t c (font, buffer, mask); 502 503 const OT::PosLookup& l = hb_ot_layout_from_face (font->face)->gpos->get_lookup (lookup_index); 504 505 return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gpos_digests[lookup_index]); 506} 507 508void 509hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer, hb_bool_t zero_width_attached_marks) 510{ 511 OT::GPOS::position_finish (font, buffer, zero_width_attached_marks); 512} 513