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