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