hb-graphite2.cc (12899B)
1 /* 2 * Copyright © 2011 Martin Hosken 3 * Copyright © 2011 SIL International 4 * Copyright © 2011,2012 Google, 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 * Google Author(s): Behdad Esfahbod 27 */ 28 29 #include "hb.hh" 30 31 #ifdef HAVE_GRAPHITE2 32 33 #include "hb-shaper-impl.hh" 34 35 #include "hb-graphite2.h" 36 37 #include <graphite2/Segment.h> 38 39 #include "hb-ot-layout.h" 40 41 42 /** 43 * SECTION:hb-graphite2 44 * @title: hb-graphite2 45 * @short_description: Graphite2 integration 46 * @include: hb-graphite2.h 47 * 48 * Functions for using HarfBuzz with fonts that include Graphite features. 49 * 50 * For Graphite features to work, you must be sure that HarfBuzz was compiled 51 * with the `graphite2` shaping engine enabled. Currently, the default is to 52 * not enable `graphite2` shaping. 53 **/ 54 55 56 /* 57 * shaper face data 58 */ 59 60 typedef struct hb_graphite2_tablelist_t 61 { 62 struct hb_graphite2_tablelist_t *next; 63 hb_blob_t *blob; 64 unsigned int tag; 65 } hb_graphite2_tablelist_t; 66 67 struct hb_graphite2_face_data_t 68 { 69 hb_face_t *face; 70 gr_face *grface; 71 hb_atomic_t<hb_graphite2_tablelist_t *> tlist; 72 }; 73 74 static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len) 75 { 76 hb_graphite2_face_data_t *face_data = (hb_graphite2_face_data_t *) data; 77 hb_graphite2_tablelist_t *tlist = face_data->tlist; 78 79 hb_blob_t *blob = nullptr; 80 81 for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next) 82 if (p->tag == tag) { 83 blob = p->blob; 84 break; 85 } 86 87 if (unlikely (!blob)) 88 { 89 blob = face_data->face->reference_table (tag); 90 91 hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) hb_calloc (1, sizeof (hb_graphite2_tablelist_t)); 92 if (unlikely (!p)) { 93 hb_blob_destroy (blob); 94 return nullptr; 95 } 96 p->blob = blob; 97 p->tag = tag; 98 99 retry: 100 hb_graphite2_tablelist_t *tlist = face_data->tlist; 101 p->next = tlist; 102 103 if (unlikely (!face_data->tlist.cmpexch (tlist, p))) 104 goto retry; 105 } 106 107 unsigned int tlen; 108 const char *d = hb_blob_get_data (blob, &tlen); 109 *len = tlen; 110 return d; 111 } 112 113 hb_graphite2_face_data_t * 114 _hb_graphite2_shaper_face_data_create (hb_face_t *face) 115 { 116 hb_blob_t *silf_blob = face->reference_table (HB_GRAPHITE2_TAG_SILF); 117 /* Umm, we just reference the table to check whether it exists. 118 * Maybe add better API for this? */ 119 if (!hb_blob_get_length (silf_blob)) 120 { 121 hb_blob_destroy (silf_blob); 122 return nullptr; 123 } 124 hb_blob_destroy (silf_blob); 125 126 hb_graphite2_face_data_t *data = (hb_graphite2_face_data_t *) hb_calloc (1, sizeof (hb_graphite2_face_data_t)); 127 if (unlikely (!data)) 128 return nullptr; 129 130 data->face = face; 131 const gr_face_ops ops = {sizeof(gr_face_ops), &hb_graphite2_get_table, NULL}; 132 data->grface = gr_make_face_with_ops (data, &ops, gr_face_preloadAll); 133 134 if (unlikely (!data->grface)) { 135 hb_free (data); 136 return nullptr; 137 } 138 139 return data; 140 } 141 142 void 143 _hb_graphite2_shaper_face_data_destroy (hb_graphite2_face_data_t *data) 144 { 145 hb_graphite2_tablelist_t *tlist = data->tlist; 146 147 while (tlist) 148 { 149 hb_graphite2_tablelist_t *old = tlist; 150 hb_blob_destroy (tlist->blob); 151 tlist = tlist->next; 152 hb_free (old); 153 } 154 155 gr_face_destroy (data->grface); 156 157 hb_free (data); 158 } 159 160 /** 161 * hb_graphite2_face_get_gr_face: (skip) 162 * @face: @hb_face_t to query 163 * 164 * Fetches the Graphite2 gr_face corresponding to the specified 165 * #hb_face_t face object. 166 * 167 * Return value: the gr_face found 168 * 169 * Since: 0.9.10 170 */ 171 gr_face * 172 hb_graphite2_face_get_gr_face (hb_face_t *face) 173 { 174 const hb_graphite2_face_data_t *data = face->data.graphite2; 175 return data ? data->grface : nullptr; 176 } 177 178 179 /* 180 * shaper font data 181 */ 182 183 struct hb_graphite2_font_data_t {}; 184 185 hb_graphite2_font_data_t * 186 _hb_graphite2_shaper_font_data_create (hb_font_t *font HB_UNUSED) 187 { 188 return (hb_graphite2_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; 189 } 190 191 void 192 _hb_graphite2_shaper_font_data_destroy (hb_graphite2_font_data_t *data HB_UNUSED) 193 { 194 } 195 196 #ifndef HB_DISABLE_DEPRECATED 197 /** 198 * hb_graphite2_font_get_gr_font: (skip) 199 * @font: An #hb_font_t 200 * 201 * Always returns `NULL`. Use hb_graphite2_face_get_gr_face() instead. 202 * 203 * Return value: (nullable): Graphite2 font associated with @font. 204 * 205 * Since: 0.9.10 206 * Deprecated: 1.4.2 207 */ 208 gr_font * 209 hb_graphite2_font_get_gr_font (hb_font_t *font HB_UNUSED) 210 { 211 return nullptr; 212 } 213 #endif 214 215 216 /* 217 * shaper 218 */ 219 220 struct hb_graphite2_cluster_t { 221 unsigned int base_char; 222 unsigned int num_chars; 223 unsigned int base_glyph; 224 unsigned int num_glyphs; 225 unsigned int cluster; 226 int advance; 227 }; 228 229 hb_bool_t 230 _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, 231 hb_font_t *font, 232 hb_buffer_t *buffer, 233 const hb_feature_t *features, 234 unsigned int num_features) 235 { 236 hb_face_t *face = font->face; 237 gr_face *grface = face->data.graphite2->grface; 238 239 const char *lang = hb_language_to_string (hb_buffer_get_language (buffer)); 240 const char *lang_end = lang ? strchr (lang, '-') : nullptr; 241 int lang_len = lang_end ? lang_end - lang : -1; 242 gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0); 243 244 for (unsigned int i = 0; i < num_features; i++) 245 { 246 const gr_feature_ref *fref = gr_face_find_fref (grface, features[i].tag); 247 if (fref) 248 gr_fref_set_feature_value (fref, features[i].value, feats); 249 } 250 251 hb_direction_t direction = buffer->props.direction; 252 hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script); 253 /* TODO vertical: 254 * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType 255 * Ogham fonts are supposed to be implemented BTT or not. Need to research that 256 * first. */ 257 if ((HB_DIRECTION_IS_HORIZONTAL (direction) && 258 direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) || 259 (HB_DIRECTION_IS_VERTICAL (direction) && 260 direction != HB_DIRECTION_TTB)) 261 { 262 hb_buffer_reverse_clusters (buffer); 263 direction = HB_DIRECTION_REVERSE (direction); 264 } 265 266 gr_segment *seg = nullptr; 267 const gr_slot *is; 268 unsigned int ci = 0, ic = 0; 269 int curradvx = 0, curradvy = 0; 270 271 unsigned int scratch_size; 272 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); 273 274 uint32_t *chars = (uint32_t *) scratch; 275 276 for (unsigned int i = 0; i < buffer->len; ++i) 277 chars[i] = buffer->info[i].codepoint; 278 279 seg = gr_make_seg (nullptr, grface, 280 HB_TAG_NONE, // https://github.com/harfbuzz/harfbuzz/issues/3439#issuecomment-1442650148 281 feats, 282 gr_utf32, chars, buffer->len, 283 2 | (direction == HB_DIRECTION_RTL ? 1 : 0)); 284 285 if (unlikely (!seg)) { 286 if (feats) gr_featureval_destroy (feats); 287 return false; 288 } 289 290 unsigned int glyph_count = gr_seg_n_slots (seg); 291 if (unlikely (!glyph_count)) { 292 if (feats) gr_featureval_destroy (feats); 293 gr_seg_destroy (seg); 294 buffer->len = 0; 295 return true; 296 } 297 298 (void) buffer->ensure (glyph_count); 299 scratch = buffer->get_scratch_buffer (&scratch_size); 300 while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) + 301 DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size) 302 { 303 if (unlikely (!buffer->ensure (buffer->allocated * 2))) 304 { 305 if (feats) gr_featureval_destroy (feats); 306 gr_seg_destroy (seg); 307 return false; 308 } 309 scratch = buffer->get_scratch_buffer (&scratch_size); 310 } 311 312 #define ALLOCATE_ARRAY(Type, name, len) \ 313 Type *name = (Type *) scratch; \ 314 do { \ 315 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ 316 assert (_consumed <= scratch_size); \ 317 scratch += _consumed; \ 318 scratch_size -= _consumed; \ 319 } while (0) 320 321 ALLOCATE_ARRAY (hb_graphite2_cluster_t, clusters, buffer->len); 322 ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count); 323 324 #undef ALLOCATE_ARRAY 325 326 hb_memset (clusters, 0, sizeof (clusters[0]) * buffer->len); 327 328 hb_codepoint_t *pg = gids; 329 clusters[0].cluster = buffer->info[0].cluster; 330 unsigned int upem = hb_face_get_upem (face); 331 float xscale = (float) font->x_scale / upem; 332 float yscale = (float) font->y_scale / upem; 333 yscale *= yscale / xscale; 334 unsigned int curradv = 0; 335 if (HB_DIRECTION_IS_BACKWARD (direction)) 336 { 337 curradv = gr_slot_origin_X(gr_seg_first_slot(seg)) * xscale; 338 clusters[0].advance = gr_seg_advance_X(seg) * xscale - curradv; 339 } 340 else 341 clusters[0].advance = 0; 342 for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++) 343 { 344 unsigned int before = gr_slot_before (is); 345 unsigned int after = gr_slot_after (is); 346 *pg = gr_slot_gid (is); 347 pg++; 348 while (clusters[ci].base_char > before && ci) 349 { 350 clusters[ci-1].num_chars += clusters[ci].num_chars; 351 clusters[ci-1].num_glyphs += clusters[ci].num_glyphs; 352 clusters[ci-1].advance += clusters[ci].advance; 353 ci--; 354 } 355 356 if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars) 357 { 358 hb_graphite2_cluster_t *c = clusters + ci + 1; 359 c->base_char = clusters[ci].base_char + clusters[ci].num_chars; 360 c->cluster = buffer->info[c->base_char].cluster; 361 c->num_chars = before - c->base_char; 362 c->base_glyph = ic; 363 c->num_glyphs = 0; 364 if (HB_DIRECTION_IS_BACKWARD (direction)) 365 { 366 c->advance = curradv - gr_slot_origin_X(is) * xscale; 367 curradv -= c->advance; 368 } 369 else 370 { 371 auto origin_X = gr_slot_origin_X (is) * xscale; 372 c->advance = 0; 373 clusters[ci].advance += origin_X - curradv; 374 curradv = origin_X; 375 } 376 ci++; 377 } 378 clusters[ci].num_glyphs++; 379 380 if (clusters[ci].base_char + clusters[ci].num_chars < after + 1) 381 clusters[ci].num_chars = after + 1 - clusters[ci].base_char; 382 } 383 384 if (HB_DIRECTION_IS_BACKWARD (direction)) 385 clusters[ci].advance += curradv; 386 else 387 clusters[ci].advance += gr_seg_advance_X(seg) * xscale - curradv; 388 ci++; 389 390 for (unsigned int i = 0; i < ci; ++i) 391 { 392 for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j) 393 { 394 hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j]; 395 info->codepoint = gids[clusters[i].base_glyph + j]; 396 info->cluster = clusters[i].cluster; 397 info->var1.i32 = clusters[i].advance; // all glyphs in the cluster get the same advance 398 } 399 } 400 buffer->len = glyph_count; 401 402 /* Positioning. */ 403 unsigned int currclus = UINT_MAX; 404 const hb_glyph_info_t *info = buffer->info; 405 hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, nullptr); 406 if (!HB_DIRECTION_IS_BACKWARD (direction)) 407 { 408 curradvx = 0; 409 for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is)) 410 { 411 pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx; 412 pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; 413 if (info->cluster != currclus) { 414 pPos->x_advance = info->var1.i32; 415 curradvx += pPos->x_advance; 416 currclus = info->cluster; 417 } else 418 pPos->x_advance = 0.; 419 420 pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; 421 curradvy += pPos->y_advance; 422 } 423 } 424 else 425 { 426 curradvx = gr_seg_advance_X(seg) * xscale; 427 for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is)) 428 { 429 if (info->cluster != currclus) 430 { 431 pPos->x_advance = info->var1.i32; 432 curradvx -= pPos->x_advance; 433 currclus = info->cluster; 434 } else 435 pPos->x_advance = 0.; 436 437 pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; 438 curradvy -= pPos->y_advance; 439 pPos->x_offset = gr_slot_origin_X (is) * xscale - info->var1.i32 - curradvx + pPos->x_advance; 440 pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; 441 } 442 hb_buffer_reverse_clusters (buffer); 443 } 444 445 if (feats) gr_featureval_destroy (feats); 446 gr_seg_destroy (seg); 447 448 buffer->clear_glyph_flags (); 449 buffer->unsafe_to_break (); 450 451 return true; 452 } 453 454 455 #endif