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