1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/harfbuzz/src/hb-ot-layout.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,930 @@ 1.4 +/* 1.5 + * Copyright © 1998-2004 David Turner and Werner Lemberg 1.6 + * Copyright © 2006 Behdad Esfahbod 1.7 + * Copyright © 2007,2008,2009 Red Hat, Inc. 1.8 + * Copyright © 2012,2013 Google, Inc. 1.9 + * 1.10 + * This is part of HarfBuzz, a text shaping library. 1.11 + * 1.12 + * Permission is hereby granted, without written agreement and without 1.13 + * license or royalty fees, to use, copy, modify, and distribute this 1.14 + * software and its documentation for any purpose, provided that the 1.15 + * above copyright notice and the following two paragraphs appear in 1.16 + * all copies of this software. 1.17 + * 1.18 + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 1.19 + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 1.20 + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 1.21 + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 1.22 + * DAMAGE. 1.23 + * 1.24 + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 1.25 + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 1.26 + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 1.27 + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 1.28 + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 1.29 + * 1.30 + * Red Hat Author(s): Behdad Esfahbod 1.31 + * Google Author(s): Behdad Esfahbod 1.32 + */ 1.33 + 1.34 +#include "hb-ot-layout-private.hh" 1.35 + 1.36 +#include "hb-ot-layout-gdef-table.hh" 1.37 +#include "hb-ot-layout-gsub-table.hh" 1.38 +#include "hb-ot-layout-gpos-table.hh" 1.39 +#include "hb-ot-layout-jstf-table.hh" 1.40 + 1.41 +#include "hb-ot-map-private.hh" 1.42 + 1.43 +#include <stdlib.h> 1.44 +#include <string.h> 1.45 + 1.46 + 1.47 +HB_SHAPER_DATA_ENSURE_DECLARE(ot, face) 1.48 + 1.49 +hb_ot_layout_t * 1.50 +_hb_ot_layout_create (hb_face_t *face) 1.51 +{ 1.52 + hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t)); 1.53 + if (unlikely (!layout)) 1.54 + return NULL; 1.55 + 1.56 + layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF)); 1.57 + layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob); 1.58 + 1.59 + layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB)); 1.60 + layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob); 1.61 + 1.62 + layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS)); 1.63 + layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob); 1.64 + 1.65 + layout->gsub_lookup_count = layout->gsub->get_lookup_count (); 1.66 + layout->gpos_lookup_count = layout->gpos->get_lookup_count (); 1.67 + 1.68 + layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t)); 1.69 + layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t)); 1.70 + 1.71 + if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) || 1.72 + (layout->gpos_lookup_count && !layout->gpos_accels))) 1.73 + { 1.74 + _hb_ot_layout_destroy (layout); 1.75 + return NULL; 1.76 + } 1.77 + 1.78 + for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) 1.79 + layout->gsub_accels[i].init (layout->gsub->get_lookup (i)); 1.80 + for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) 1.81 + layout->gpos_accels[i].init (layout->gpos->get_lookup (i)); 1.82 + 1.83 + return layout; 1.84 +} 1.85 + 1.86 +void 1.87 +_hb_ot_layout_destroy (hb_ot_layout_t *layout) 1.88 +{ 1.89 + for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) 1.90 + layout->gsub_accels[i].fini (layout->gsub->get_lookup (i)); 1.91 + for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) 1.92 + layout->gpos_accels[i].fini (layout->gpos->get_lookup (i)); 1.93 + 1.94 + free (layout->gsub_accels); 1.95 + free (layout->gpos_accels); 1.96 + 1.97 + hb_blob_destroy (layout->gdef_blob); 1.98 + hb_blob_destroy (layout->gsub_blob); 1.99 + hb_blob_destroy (layout->gpos_blob); 1.100 + 1.101 + free (layout); 1.102 +} 1.103 + 1.104 +static inline const OT::GDEF& 1.105 +_get_gdef (hb_face_t *face) 1.106 +{ 1.107 + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF); 1.108 + return *hb_ot_layout_from_face (face)->gdef; 1.109 +} 1.110 +static inline const OT::GSUB& 1.111 +_get_gsub (hb_face_t *face) 1.112 +{ 1.113 + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB); 1.114 + return *hb_ot_layout_from_face (face)->gsub; 1.115 +} 1.116 +static inline const OT::GPOS& 1.117 +_get_gpos (hb_face_t *face) 1.118 +{ 1.119 + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS); 1.120 + return *hb_ot_layout_from_face (face)->gpos; 1.121 +} 1.122 + 1.123 + 1.124 +/* 1.125 + * GDEF 1.126 + */ 1.127 + 1.128 +hb_bool_t 1.129 +hb_ot_layout_has_glyph_classes (hb_face_t *face) 1.130 +{ 1.131 + return _get_gdef (face).has_glyph_classes (); 1.132 +} 1.133 + 1.134 +hb_ot_layout_glyph_class_t 1.135 +hb_ot_layout_get_glyph_class (hb_face_t *face, 1.136 + hb_codepoint_t glyph) 1.137 +{ 1.138 + return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph); 1.139 +} 1.140 + 1.141 +void 1.142 +hb_ot_layout_get_glyphs_in_class (hb_face_t *face, 1.143 + hb_ot_layout_glyph_class_t klass, 1.144 + hb_set_t *glyphs /* OUT */) 1.145 +{ 1.146 + return _get_gdef (face).get_glyphs_in_class (klass, glyphs); 1.147 +} 1.148 + 1.149 +unsigned int 1.150 +hb_ot_layout_get_attach_points (hb_face_t *face, 1.151 + hb_codepoint_t glyph, 1.152 + unsigned int start_offset, 1.153 + unsigned int *point_count /* IN/OUT */, 1.154 + unsigned int *point_array /* OUT */) 1.155 +{ 1.156 + return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array); 1.157 +} 1.158 + 1.159 +unsigned int 1.160 +hb_ot_layout_get_ligature_carets (hb_font_t *font, 1.161 + hb_direction_t direction, 1.162 + hb_codepoint_t glyph, 1.163 + unsigned int start_offset, 1.164 + unsigned int *caret_count /* IN/OUT */, 1.165 + int *caret_array /* OUT */) 1.166 +{ 1.167 + return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); 1.168 +} 1.169 + 1.170 + 1.171 +/* 1.172 + * GSUB/GPOS 1.173 + */ 1.174 + 1.175 +static const OT::GSUBGPOS& 1.176 +get_gsubgpos_table (hb_face_t *face, 1.177 + hb_tag_t table_tag) 1.178 +{ 1.179 + switch (table_tag) { 1.180 + case HB_OT_TAG_GSUB: return _get_gsub (face); 1.181 + case HB_OT_TAG_GPOS: return _get_gpos (face); 1.182 + default: return OT::Null(OT::GSUBGPOS); 1.183 + } 1.184 +} 1.185 + 1.186 + 1.187 +unsigned int 1.188 +hb_ot_layout_table_get_script_tags (hb_face_t *face, 1.189 + hb_tag_t table_tag, 1.190 + unsigned int start_offset, 1.191 + unsigned int *script_count /* IN/OUT */, 1.192 + hb_tag_t *script_tags /* OUT */) 1.193 +{ 1.194 + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1.195 + 1.196 + return g.get_script_tags (start_offset, script_count, script_tags); 1.197 +} 1.198 + 1.199 +#define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') 1.200 + 1.201 +hb_bool_t 1.202 +hb_ot_layout_table_find_script (hb_face_t *face, 1.203 + hb_tag_t table_tag, 1.204 + hb_tag_t script_tag, 1.205 + unsigned int *script_index) 1.206 +{ 1.207 + ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 1.208 + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1.209 + 1.210 + if (g.find_script_index (script_tag, script_index)) 1.211 + return true; 1.212 + 1.213 + /* try finding 'DFLT' */ 1.214 + if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) 1.215 + return false; 1.216 + 1.217 + /* try with 'dflt'; MS site has had typos and many fonts use it now :(. 1.218 + * including many versions of DejaVu Sans Mono! */ 1.219 + if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) 1.220 + return false; 1.221 + 1.222 + /* try with 'latn'; some old fonts put their features there even though 1.223 + they're really trying to support Thai, for example :( */ 1.224 + if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) 1.225 + return false; 1.226 + 1.227 + if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 1.228 + return false; 1.229 +} 1.230 + 1.231 +hb_bool_t 1.232 +hb_ot_layout_table_choose_script (hb_face_t *face, 1.233 + hb_tag_t table_tag, 1.234 + const hb_tag_t *script_tags, 1.235 + unsigned int *script_index, 1.236 + hb_tag_t *chosen_script) 1.237 +{ 1.238 + ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); 1.239 + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1.240 + 1.241 + while (*script_tags) 1.242 + { 1.243 + if (g.find_script_index (*script_tags, script_index)) { 1.244 + if (chosen_script) 1.245 + *chosen_script = *script_tags; 1.246 + return true; 1.247 + } 1.248 + script_tags++; 1.249 + } 1.250 + 1.251 + /* try finding 'DFLT' */ 1.252 + if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { 1.253 + if (chosen_script) 1.254 + *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; 1.255 + return false; 1.256 + } 1.257 + 1.258 + /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 1.259 + if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) { 1.260 + if (chosen_script) 1.261 + *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE; 1.262 + return false; 1.263 + } 1.264 + 1.265 + /* try with 'latn'; some old fonts put their features there even though 1.266 + they're really trying to support Thai, for example :( */ 1.267 + if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { 1.268 + if (chosen_script) 1.269 + *chosen_script = HB_OT_TAG_LATIN_SCRIPT; 1.270 + return false; 1.271 + } 1.272 + 1.273 + if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 1.274 + if (chosen_script) 1.275 + *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX; 1.276 + return false; 1.277 +} 1.278 + 1.279 +unsigned int 1.280 +hb_ot_layout_table_get_feature_tags (hb_face_t *face, 1.281 + hb_tag_t table_tag, 1.282 + unsigned int start_offset, 1.283 + unsigned int *feature_count /* IN/OUT */, 1.284 + hb_tag_t *feature_tags /* OUT */) 1.285 +{ 1.286 + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1.287 + 1.288 + return g.get_feature_tags (start_offset, feature_count, feature_tags); 1.289 +} 1.290 + 1.291 + 1.292 +unsigned int 1.293 +hb_ot_layout_script_get_language_tags (hb_face_t *face, 1.294 + hb_tag_t table_tag, 1.295 + unsigned int script_index, 1.296 + unsigned int start_offset, 1.297 + unsigned int *language_count /* IN/OUT */, 1.298 + hb_tag_t *language_tags /* OUT */) 1.299 +{ 1.300 + const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 1.301 + 1.302 + return s.get_lang_sys_tags (start_offset, language_count, language_tags); 1.303 +} 1.304 + 1.305 +hb_bool_t 1.306 +hb_ot_layout_script_find_language (hb_face_t *face, 1.307 + hb_tag_t table_tag, 1.308 + unsigned int script_index, 1.309 + hb_tag_t language_tag, 1.310 + unsigned int *language_index) 1.311 +{ 1.312 + ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); 1.313 + const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); 1.314 + 1.315 + if (s.find_lang_sys_index (language_tag, language_index)) 1.316 + return true; 1.317 + 1.318 + /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ 1.319 + if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) 1.320 + return false; 1.321 + 1.322 + if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; 1.323 + return false; 1.324 +} 1.325 + 1.326 +hb_bool_t 1.327 +hb_ot_layout_language_get_required_feature_index (hb_face_t *face, 1.328 + hb_tag_t table_tag, 1.329 + unsigned int script_index, 1.330 + unsigned int language_index, 1.331 + unsigned int *feature_index) 1.332 +{ 1.333 + const OT::LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index); 1.334 + 1.335 + if (feature_index) *feature_index = l.get_required_feature_index (); 1.336 + 1.337 + return l.has_required_feature (); 1.338 +} 1.339 + 1.340 +unsigned int 1.341 +hb_ot_layout_language_get_feature_indexes (hb_face_t *face, 1.342 + hb_tag_t table_tag, 1.343 + unsigned int script_index, 1.344 + unsigned int language_index, 1.345 + unsigned int start_offset, 1.346 + unsigned int *feature_count /* IN/OUT */, 1.347 + unsigned int *feature_indexes /* OUT */) 1.348 +{ 1.349 + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1.350 + const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 1.351 + 1.352 + return l.get_feature_indexes (start_offset, feature_count, feature_indexes); 1.353 +} 1.354 + 1.355 +unsigned int 1.356 +hb_ot_layout_language_get_feature_tags (hb_face_t *face, 1.357 + hb_tag_t table_tag, 1.358 + unsigned int script_index, 1.359 + unsigned int language_index, 1.360 + unsigned int start_offset, 1.361 + unsigned int *feature_count /* IN/OUT */, 1.362 + hb_tag_t *feature_tags /* OUT */) 1.363 +{ 1.364 + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1.365 + const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 1.366 + 1.367 + ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t)); 1.368 + unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); 1.369 + 1.370 + if (feature_tags) { 1.371 + unsigned int count = *feature_count; 1.372 + for (unsigned int i = 0; i < count; i++) 1.373 + feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); 1.374 + } 1.375 + 1.376 + return ret; 1.377 +} 1.378 + 1.379 + 1.380 +hb_bool_t 1.381 +hb_ot_layout_language_find_feature (hb_face_t *face, 1.382 + hb_tag_t table_tag, 1.383 + unsigned int script_index, 1.384 + unsigned int language_index, 1.385 + hb_tag_t feature_tag, 1.386 + unsigned int *feature_index) 1.387 +{ 1.388 + ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); 1.389 + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1.390 + const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); 1.391 + 1.392 + unsigned int num_features = l.get_feature_count (); 1.393 + for (unsigned int i = 0; i < num_features; i++) { 1.394 + unsigned int f_index = l.get_feature_index (i); 1.395 + 1.396 + if (feature_tag == g.get_feature_tag (f_index)) { 1.397 + if (feature_index) *feature_index = f_index; 1.398 + return true; 1.399 + } 1.400 + } 1.401 + 1.402 + if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; 1.403 + return false; 1.404 +} 1.405 + 1.406 +unsigned int 1.407 +hb_ot_layout_feature_get_lookups (hb_face_t *face, 1.408 + hb_tag_t table_tag, 1.409 + unsigned int feature_index, 1.410 + unsigned int start_offset, 1.411 + unsigned int *lookup_count /* IN/OUT */, 1.412 + unsigned int *lookup_indexes /* OUT */) 1.413 +{ 1.414 + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); 1.415 + const OT::Feature &f = g.get_feature (feature_index); 1.416 + 1.417 + return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); 1.418 +} 1.419 + 1.420 +unsigned int 1.421 +hb_ot_layout_table_get_lookup_count (hb_face_t *face, 1.422 + hb_tag_t table_tag) 1.423 +{ 1.424 + switch (table_tag) 1.425 + { 1.426 + case HB_OT_TAG_GSUB: 1.427 + { 1.428 + return hb_ot_layout_from_face (face)->gsub_lookup_count; 1.429 + } 1.430 + case HB_OT_TAG_GPOS: 1.431 + { 1.432 + return hb_ot_layout_from_face (face)->gpos_lookup_count; 1.433 + } 1.434 + } 1.435 + return 0; 1.436 +} 1.437 + 1.438 +static void 1.439 +_hb_ot_layout_collect_lookups_lookups (hb_face_t *face, 1.440 + hb_tag_t table_tag, 1.441 + unsigned int feature_index, 1.442 + hb_set_t *lookup_indexes /* OUT */) 1.443 +{ 1.444 + unsigned int lookup_indices[32]; 1.445 + unsigned int offset, len; 1.446 + 1.447 + offset = 0; 1.448 + do { 1.449 + len = ARRAY_LENGTH (lookup_indices); 1.450 + hb_ot_layout_feature_get_lookups (face, 1.451 + table_tag, 1.452 + feature_index, 1.453 + offset, &len, 1.454 + lookup_indices); 1.455 + 1.456 + for (unsigned int i = 0; i < len; i++) 1.457 + lookup_indexes->add (lookup_indices[i]); 1.458 + 1.459 + offset += len; 1.460 + } while (len == ARRAY_LENGTH (lookup_indices)); 1.461 +} 1.462 + 1.463 +static void 1.464 +_hb_ot_layout_collect_lookups_features (hb_face_t *face, 1.465 + hb_tag_t table_tag, 1.466 + unsigned int script_index, 1.467 + unsigned int language_index, 1.468 + const hb_tag_t *features, 1.469 + hb_set_t *lookup_indexes /* OUT */) 1.470 +{ 1.471 + if (!features) 1.472 + { 1.473 + unsigned int required_feature_index; 1.474 + if (hb_ot_layout_language_get_required_feature_index (face, 1.475 + table_tag, 1.476 + script_index, 1.477 + language_index, 1.478 + &required_feature_index)) 1.479 + _hb_ot_layout_collect_lookups_lookups (face, 1.480 + table_tag, 1.481 + required_feature_index, 1.482 + lookup_indexes); 1.483 + 1.484 + /* All features */ 1.485 + unsigned int feature_indices[32]; 1.486 + unsigned int offset, len; 1.487 + 1.488 + offset = 0; 1.489 + do { 1.490 + len = ARRAY_LENGTH (feature_indices); 1.491 + hb_ot_layout_language_get_feature_indexes (face, 1.492 + table_tag, 1.493 + script_index, 1.494 + language_index, 1.495 + offset, &len, 1.496 + feature_indices); 1.497 + 1.498 + for (unsigned int i = 0; i < len; i++) 1.499 + _hb_ot_layout_collect_lookups_lookups (face, 1.500 + table_tag, 1.501 + feature_indices[i], 1.502 + lookup_indexes); 1.503 + 1.504 + offset += len; 1.505 + } while (len == ARRAY_LENGTH (feature_indices)); 1.506 + } 1.507 + else 1.508 + { 1.509 + for (; *features; features++) 1.510 + { 1.511 + unsigned int feature_index; 1.512 + if (hb_ot_layout_language_find_feature (face, 1.513 + table_tag, 1.514 + script_index, 1.515 + language_index, 1.516 + *features, 1.517 + &feature_index)) 1.518 + _hb_ot_layout_collect_lookups_lookups (face, 1.519 + table_tag, 1.520 + feature_index, 1.521 + lookup_indexes); 1.522 + } 1.523 + } 1.524 +} 1.525 + 1.526 +static void 1.527 +_hb_ot_layout_collect_lookups_languages (hb_face_t *face, 1.528 + hb_tag_t table_tag, 1.529 + unsigned int script_index, 1.530 + const hb_tag_t *languages, 1.531 + const hb_tag_t *features, 1.532 + hb_set_t *lookup_indexes /* OUT */) 1.533 +{ 1.534 + _hb_ot_layout_collect_lookups_features (face, 1.535 + table_tag, 1.536 + script_index, 1.537 + HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, 1.538 + features, 1.539 + lookup_indexes); 1.540 + 1.541 + if (!languages) 1.542 + { 1.543 + /* All languages */ 1.544 + unsigned int count = hb_ot_layout_script_get_language_tags (face, 1.545 + table_tag, 1.546 + script_index, 1.547 + 0, NULL, NULL); 1.548 + for (unsigned int language_index = 0; language_index < count; language_index++) 1.549 + _hb_ot_layout_collect_lookups_features (face, 1.550 + table_tag, 1.551 + script_index, 1.552 + language_index, 1.553 + features, 1.554 + lookup_indexes); 1.555 + } 1.556 + else 1.557 + { 1.558 + for (; *languages; languages++) 1.559 + { 1.560 + unsigned int language_index; 1.561 + if (hb_ot_layout_script_find_language (face, 1.562 + table_tag, 1.563 + script_index, 1.564 + *languages, 1.565 + &language_index)) 1.566 + _hb_ot_layout_collect_lookups_features (face, 1.567 + table_tag, 1.568 + script_index, 1.569 + language_index, 1.570 + features, 1.571 + lookup_indexes); 1.572 + } 1.573 + } 1.574 +} 1.575 + 1.576 +void 1.577 +hb_ot_layout_collect_lookups (hb_face_t *face, 1.578 + hb_tag_t table_tag, 1.579 + const hb_tag_t *scripts, 1.580 + const hb_tag_t *languages, 1.581 + const hb_tag_t *features, 1.582 + hb_set_t *lookup_indexes /* OUT */) 1.583 +{ 1.584 + if (!scripts) 1.585 + { 1.586 + /* All scripts */ 1.587 + unsigned int count = hb_ot_layout_table_get_script_tags (face, 1.588 + table_tag, 1.589 + 0, NULL, NULL); 1.590 + for (unsigned int script_index = 0; script_index < count; script_index++) 1.591 + _hb_ot_layout_collect_lookups_languages (face, 1.592 + table_tag, 1.593 + script_index, 1.594 + languages, 1.595 + features, 1.596 + lookup_indexes); 1.597 + } 1.598 + else 1.599 + { 1.600 + for (; *scripts; scripts++) 1.601 + { 1.602 + unsigned int script_index; 1.603 + if (hb_ot_layout_table_find_script (face, 1.604 + table_tag, 1.605 + *scripts, 1.606 + &script_index)) 1.607 + _hb_ot_layout_collect_lookups_languages (face, 1.608 + table_tag, 1.609 + script_index, 1.610 + languages, 1.611 + features, 1.612 + lookup_indexes); 1.613 + } 1.614 + } 1.615 +} 1.616 + 1.617 +void 1.618 +hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, 1.619 + hb_tag_t table_tag, 1.620 + unsigned int lookup_index, 1.621 + hb_set_t *glyphs_before, /* OUT. May be NULL */ 1.622 + hb_set_t *glyphs_input, /* OUT. May be NULL */ 1.623 + hb_set_t *glyphs_after, /* OUT. May be NULL */ 1.624 + hb_set_t *glyphs_output /* OUT. May be NULL */) 1.625 +{ 1.626 + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return; 1.627 + 1.628 + OT::hb_collect_glyphs_context_t c (face, 1.629 + glyphs_before, 1.630 + glyphs_input, 1.631 + glyphs_after, 1.632 + glyphs_output); 1.633 + 1.634 + switch (table_tag) 1.635 + { 1.636 + case HB_OT_TAG_GSUB: 1.637 + { 1.638 + const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); 1.639 + l.collect_glyphs (&c); 1.640 + return; 1.641 + } 1.642 + case HB_OT_TAG_GPOS: 1.643 + { 1.644 + const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index); 1.645 + l.collect_glyphs (&c); 1.646 + return; 1.647 + } 1.648 + } 1.649 +} 1.650 + 1.651 + 1.652 +/* 1.653 + * OT::GSUB 1.654 + */ 1.655 + 1.656 +hb_bool_t 1.657 +hb_ot_layout_has_substitution (hb_face_t *face) 1.658 +{ 1.659 + return &_get_gsub (face) != &OT::Null(OT::GSUB); 1.660 +} 1.661 + 1.662 +hb_bool_t 1.663 +hb_ot_layout_lookup_would_substitute (hb_face_t *face, 1.664 + unsigned int lookup_index, 1.665 + const hb_codepoint_t *glyphs, 1.666 + unsigned int glyphs_length, 1.667 + hb_bool_t zero_context) 1.668 +{ 1.669 + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false; 1.670 + return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context); 1.671 +} 1.672 + 1.673 +hb_bool_t 1.674 +hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, 1.675 + unsigned int lookup_index, 1.676 + const hb_codepoint_t *glyphs, 1.677 + unsigned int glyphs_length, 1.678 + hb_bool_t zero_context) 1.679 +{ 1.680 + if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false; 1.681 + OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context); 1.682 + 1.683 + const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); 1.684 + 1.685 + return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index].digest); 1.686 +} 1.687 + 1.688 +void 1.689 +hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) 1.690 +{ 1.691 + OT::GSUB::substitute_start (font, buffer); 1.692 +} 1.693 + 1.694 +void 1.695 +hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer) 1.696 +{ 1.697 + OT::GSUB::substitute_finish (font, buffer); 1.698 +} 1.699 + 1.700 +void 1.701 +hb_ot_layout_lookup_substitute_closure (hb_face_t *face, 1.702 + unsigned int lookup_index, 1.703 + hb_set_t *glyphs) 1.704 +{ 1.705 + OT::hb_closure_context_t c (face, glyphs); 1.706 + 1.707 + const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index); 1.708 + 1.709 + l.closure (&c); 1.710 +} 1.711 + 1.712 +/* 1.713 + * OT::GPOS 1.714 + */ 1.715 + 1.716 +hb_bool_t 1.717 +hb_ot_layout_has_positioning (hb_face_t *face) 1.718 +{ 1.719 + return &_get_gpos (face) != &OT::Null(OT::GPOS); 1.720 +} 1.721 + 1.722 +void 1.723 +hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) 1.724 +{ 1.725 + OT::GPOS::position_start (font, buffer); 1.726 +} 1.727 + 1.728 +void 1.729 +hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer) 1.730 +{ 1.731 + OT::GPOS::position_finish (font, buffer); 1.732 +} 1.733 + 1.734 +hb_bool_t 1.735 +hb_ot_layout_get_size_params (hb_face_t *face, 1.736 + unsigned int *design_size, /* OUT. May be NULL */ 1.737 + unsigned int *subfamily_id, /* OUT. May be NULL */ 1.738 + unsigned int *subfamily_name_id, /* OUT. May be NULL */ 1.739 + unsigned int *range_start, /* OUT. May be NULL */ 1.740 + unsigned int *range_end /* OUT. May be NULL */) 1.741 +{ 1.742 + const OT::GPOS &gpos = _get_gpos (face); 1.743 + const hb_tag_t tag = HB_TAG ('s','i','z','e'); 1.744 + 1.745 + unsigned int num_features = gpos.get_feature_count (); 1.746 + for (unsigned int i = 0; i < num_features; i++) 1.747 + { 1.748 + if (tag == gpos.get_feature_tag (i)) 1.749 + { 1.750 + const OT::Feature &f = gpos.get_feature (i); 1.751 + const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag); 1.752 + 1.753 + if (params.designSize) 1.754 + { 1.755 +#define PARAM(a, A) if (a) *a = params.A 1.756 + PARAM (design_size, designSize); 1.757 + PARAM (subfamily_id, subfamilyID); 1.758 + PARAM (subfamily_name_id, subfamilyNameID); 1.759 + PARAM (range_start, rangeStart); 1.760 + PARAM (range_end, rangeEnd); 1.761 +#undef PARAM 1.762 + 1.763 + return true; 1.764 + } 1.765 + } 1.766 + } 1.767 + 1.768 +#define PARAM(a, A) if (a) *a = 0 1.769 + PARAM (design_size, designSize); 1.770 + PARAM (subfamily_id, subfamilyID); 1.771 + PARAM (subfamily_name_id, subfamilyNameID); 1.772 + PARAM (range_start, rangeStart); 1.773 + PARAM (range_end, rangeEnd); 1.774 +#undef PARAM 1.775 + 1.776 + return false; 1.777 +} 1.778 + 1.779 + 1.780 +/* 1.781 + * Parts of different types are implemented here such that they have direct 1.782 + * access to GSUB/GPOS lookups. 1.783 + */ 1.784 + 1.785 + 1.786 +struct GSUBProxy 1.787 +{ 1.788 + static const unsigned int table_index = 0; 1.789 + static const bool inplace = false; 1.790 + typedef OT::SubstLookup Lookup; 1.791 + 1.792 + GSUBProxy (hb_face_t *face) : 1.793 + table (*hb_ot_layout_from_face (face)->gsub), 1.794 + accels (hb_ot_layout_from_face (face)->gsub_accels) {} 1.795 + 1.796 + const OT::GSUB &table; 1.797 + const hb_ot_layout_lookup_accelerator_t *accels; 1.798 +}; 1.799 + 1.800 +struct GPOSProxy 1.801 +{ 1.802 + static const unsigned int table_index = 1; 1.803 + static const bool inplace = true; 1.804 + typedef OT::PosLookup Lookup; 1.805 + 1.806 + GPOSProxy (hb_face_t *face) : 1.807 + table (*hb_ot_layout_from_face (face)->gpos), 1.808 + accels (hb_ot_layout_from_face (face)->gpos_accels) {} 1.809 + 1.810 + const OT::GPOS &table; 1.811 + const hb_ot_layout_lookup_accelerator_t *accels; 1.812 +}; 1.813 + 1.814 + 1.815 +template <typename Lookup> 1.816 +static inline bool apply_once (OT::hb_apply_context_t *c, 1.817 + const Lookup &lookup) 1.818 +{ 1.819 + if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props)) 1.820 + return false; 1.821 + return lookup.dispatch (c); 1.822 +} 1.823 + 1.824 +template <typename Proxy> 1.825 +static inline bool 1.826 +apply_string (OT::hb_apply_context_t *c, 1.827 + const typename Proxy::Lookup &lookup, 1.828 + const hb_ot_layout_lookup_accelerator_t &accel) 1.829 +{ 1.830 + bool ret = false; 1.831 + hb_buffer_t *buffer = c->buffer; 1.832 + 1.833 + if (unlikely (!buffer->len || !c->lookup_mask)) 1.834 + return false; 1.835 + 1.836 + c->set_lookup (lookup); 1.837 + 1.838 + if (likely (!lookup.is_reverse ())) 1.839 + { 1.840 + /* in/out forward substitution/positioning */ 1.841 + if (Proxy::table_index == 0) 1.842 + buffer->clear_output (); 1.843 + buffer->idx = 0; 1.844 + 1.845 + while (buffer->idx < buffer->len) 1.846 + { 1.847 + if (accel.digest.may_have (buffer->cur().codepoint) && 1.848 + (buffer->cur().mask & c->lookup_mask) && 1.849 + apply_once (c, lookup)) 1.850 + ret = true; 1.851 + else 1.852 + buffer->next_glyph (); 1.853 + } 1.854 + if (ret) 1.855 + { 1.856 + if (!Proxy::inplace) 1.857 + buffer->swap_buffers (); 1.858 + else 1.859 + assert (!buffer->has_separate_output ()); 1.860 + } 1.861 + } 1.862 + else 1.863 + { 1.864 + /* in-place backward substitution/positioning */ 1.865 + if (Proxy::table_index == 0) 1.866 + buffer->remove_output (); 1.867 + buffer->idx = buffer->len - 1; 1.868 + do 1.869 + { 1.870 + if (accel.digest.may_have (buffer->cur().codepoint) && 1.871 + (buffer->cur().mask & c->lookup_mask) && 1.872 + apply_once (c, lookup)) 1.873 + ret = true; 1.874 + /* The reverse lookup doesn't "advance" cursor (for good reason). */ 1.875 + buffer->idx--; 1.876 + 1.877 + } 1.878 + while ((int) buffer->idx >= 0); 1.879 + } 1.880 + 1.881 + return ret; 1.882 +} 1.883 + 1.884 +template <typename Proxy> 1.885 +inline void hb_ot_map_t::apply (const Proxy &proxy, 1.886 + const hb_ot_shape_plan_t *plan, 1.887 + hb_font_t *font, 1.888 + hb_buffer_t *buffer) const 1.889 +{ 1.890 + const unsigned int table_index = proxy.table_index; 1.891 + unsigned int i = 0; 1.892 + OT::hb_apply_context_t c (table_index, font, buffer); 1.893 + c.set_recurse_func (Proxy::Lookup::apply_recurse_func); 1.894 + 1.895 + for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) { 1.896 + const stage_map_t *stage = &stages[table_index][stage_index]; 1.897 + for (; i < stage->last_lookup; i++) 1.898 + { 1.899 + unsigned int lookup_index = lookups[table_index][i].index; 1.900 + c.set_lookup_mask (lookups[table_index][i].mask); 1.901 + c.set_auto_zwj (lookups[table_index][i].auto_zwj); 1.902 + apply_string<Proxy> (&c, 1.903 + proxy.table.get_lookup (lookup_index), 1.904 + proxy.accels[lookup_index]); 1.905 + } 1.906 + 1.907 + if (stage->pause_func) 1.908 + { 1.909 + buffer->clear_output (); 1.910 + stage->pause_func (plan, font, buffer); 1.911 + } 1.912 + } 1.913 +} 1.914 + 1.915 +void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const 1.916 +{ 1.917 + GSUBProxy proxy (font->face); 1.918 + apply (proxy, plan, font, buffer); 1.919 +} 1.920 + 1.921 +void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const 1.922 +{ 1.923 + GPOSProxy proxy (font->face); 1.924 + apply (proxy, plan, font, buffer); 1.925 +} 1.926 + 1.927 +HB_INTERNAL void 1.928 +hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c, 1.929 + const OT::SubstLookup &lookup, 1.930 + const hb_ot_layout_lookup_accelerator_t &accel) 1.931 +{ 1.932 + apply_string<GSUBProxy> (c, lookup, accel); 1.933 +}