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 #define HB_SHAPER graphite2 30 #include "hb-shaper-impl-private.hh" 31 32 #include "hb-graphite2.h" 33 34 #include <graphite2/Segment.h> 35 36 37 HB_SHAPER_DATA_ENSURE_DEFINE(graphite2, face) 38 HB_SHAPER_DATA_ENSURE_DEFINE(graphite2, font) 39 40 41 /* 42 * shaper face data 43 */ 44 45 typedef struct hb_graphite2_tablelist_t { 46 struct hb_graphite2_tablelist_t *next; 47 hb_blob_t *blob; 48 unsigned int tag; 49 } hb_graphite2_tablelist_t; 50 51 struct hb_graphite2_shaper_face_data_t { 52 hb_face_t *face; 53 gr_face *grface; 54 hb_graphite2_tablelist_t *tlist; 55 }; 56 57 static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len) 58 { 59 hb_graphite2_shaper_face_data_t *face_data = (hb_graphite2_shaper_face_data_t *) data; 60 hb_graphite2_tablelist_t *tlist = face_data->tlist; 61 62 hb_blob_t *blob = nullptr; 63 64 for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next) 65 if (p->tag == tag) { 66 blob = p->blob; 67 break; 68 } 69 70 if (unlikely (!blob)) 71 { 72 blob = face_data->face->reference_table (tag); 73 74 hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) calloc (1, sizeof (hb_graphite2_tablelist_t)); 75 if (unlikely (!p)) { 76 hb_blob_destroy (blob); 77 return nullptr; 78 } 79 p->blob = blob; 80 p->tag = tag; 81 82 /* TODO Not thread-safe, but fairly harmless. 83 * We can do the double-chcked pointer cmpexch thing here. */ 84 p->next = face_data->tlist; 85 face_data->tlist = p; 86 } 87 88 unsigned int tlen; 89 const char *d = hb_blob_get_data (blob, &tlen); 90 *len = tlen; 91 return d; 92 } 93 94 hb_graphite2_shaper_face_data_t * 95 _hb_graphite2_shaper_face_data_create (hb_face_t *face) 96 { 97 hb_blob_t *silf_blob = face->reference_table (HB_GRAPHITE2_TAG_SILF); 98 /* Umm, we just reference the table to check whether it exists. 99 * Maybe add better API for this? */ 100 if (!hb_blob_get_length (silf_blob)) 101 { 102 hb_blob_destroy (silf_blob); 103 return nullptr; 104 } 105 hb_blob_destroy (silf_blob); 106 107 hb_graphite2_shaper_face_data_t *data = (hb_graphite2_shaper_face_data_t *) calloc (1, sizeof (hb_graphite2_shaper_face_data_t)); 108 if (unlikely (!data)) 109 return nullptr; 110 111 data->face = face; 112 data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll); 113 114 if (unlikely (!data->grface)) { 115 free (data); 116 return nullptr; 117 } 118 119 return data; 120 } 121 122 void 123 _hb_graphite2_shaper_face_data_destroy (hb_graphite2_shaper_face_data_t *data) 124 { 125 hb_graphite2_tablelist_t *tlist = data->tlist; 126 127 while (tlist) 128 { 129 hb_graphite2_tablelist_t *old = tlist; 130 hb_blob_destroy (tlist->blob); 131 tlist = tlist->next; 132 free (old); 133 } 134 135 gr_face_destroy (data->grface); 136 137 free (data); 138 } 139 140 /* 141 * Since: 0.9.10 142 */ 143 gr_face * 144 hb_graphite2_face_get_gr_face (hb_face_t *face) 145 { 146 if (unlikely (!hb_graphite2_shaper_face_data_ensure (face))) return nullptr; 147 return HB_SHAPER_DATA_GET (face)->grface; 148 } 149 150 151 /* 152 * shaper font data 153 */ 154 155 struct hb_graphite2_shaper_font_data_t {}; 156 157 hb_graphite2_shaper_font_data_t * 158 _hb_graphite2_shaper_font_data_create (hb_font_t *font HB_UNUSED) 159 { 160 return (hb_graphite2_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; 161 } 162 163 void 164 _hb_graphite2_shaper_font_data_destroy (hb_graphite2_shaper_font_data_t *data HB_UNUSED) 165 { 166 } 167 168 /* 169 * Since: 0.9.10 170 */ 171 gr_font * 172 hb_graphite2_font_get_gr_font (hb_font_t *font) 173 { 174 return nullptr; 175 } 176 177 178 /* 179 * shaper shape_plan data 180 */ 181 182 struct hb_graphite2_shaper_shape_plan_data_t {}; 183 184 hb_graphite2_shaper_shape_plan_data_t * 185 _hb_graphite2_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, 186 const hb_feature_t *user_features HB_UNUSED, 187 unsigned int num_user_features HB_UNUSED, 188 const int *coords HB_UNUSED, 189 unsigned int num_coords HB_UNUSED) 190 { 191 return (hb_graphite2_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; 192 } 193 194 void 195 _hb_graphite2_shaper_shape_plan_data_destroy (hb_graphite2_shaper_shape_plan_data_t *data HB_UNUSED) 196 { 197 } 198 199 200 /* 201 * shaper 202 */ 203 204 struct hb_graphite2_cluster_t { 205 unsigned int base_char; 206 unsigned int num_chars; 207 unsigned int base_glyph; 208 unsigned int num_glyphs; 209 unsigned int cluster; 210 float advance; 211 }; 212 213 hb_bool_t 214 _hb_graphite2_shape (hb_shape_plan_t *shape_plan, 215 hb_font_t *font, 216 hb_buffer_t *buffer, 217 const hb_feature_t *features, 218 unsigned int num_features) 219 { 220 hb_face_t *face = font->face; 221 gr_face *grface = HB_SHAPER_DATA_GET (face)->grface; 222 223 const char *lang = hb_language_to_string (hb_buffer_get_language (buffer)); 224 const char *lang_end = lang ? strchr (lang, '-') : nullptr; 225 int lang_len = lang_end ? lang_end - lang : -1; 226 gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0); 227 228 for (unsigned int i = 0; i < num_features; i++) 229 { 230 const gr_feature_ref *fref = gr_face_find_fref (grface, features[i].tag); 231 if (fref) 232 gr_fref_set_feature_value (fref, features[i].value, feats); 233 } 234 235 gr_segment *seg = nullptr; 236 const gr_slot *is; 237 unsigned int ci = 0, ic = 0; 238 float curradvx = 0., curradvy = 0.; 239 240 unsigned int scratch_size; 241 hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); 242 243 uint32_t *chars = (uint32_t *) scratch; 244 245 for (unsigned int i = 0; i < buffer->len; ++i) 246 chars[i] = buffer->info[i].codepoint; 247 248 /* TODO ensure_native_direction. */ 249 250 hb_tag_t script_tag[2]; 251 hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]); 252 253 seg = gr_make_seg (nullptr, grface, 254 script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1], 255 feats, 256 gr_utf32, chars, buffer->len, 257 2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0)); 258 259 if (unlikely (!seg)) { 260 if (feats) gr_featureval_destroy (feats); 261 return false; 262 } 263 264 unsigned int glyph_count = gr_seg_n_slots (seg); 265 if (unlikely (!glyph_count)) { 266 if (feats) gr_featureval_destroy (feats); 267 gr_seg_destroy (seg); 268 buffer->len = 0; 269 return true; 270 } 271 272 buffer->ensure (glyph_count); 273 scratch = buffer->get_scratch_buffer (&scratch_size); 274 while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) + 275 DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size) 276 { 277 if (unlikely (!buffer->ensure (buffer->allocated * 2))) 278 { 279 if (feats) gr_featureval_destroy (feats); 280 gr_seg_destroy (seg); 281 return false; 282 } 283 scratch = buffer->get_scratch_buffer (&scratch_size); 284 } 285 286 #define ALLOCATE_ARRAY(Type, name, len) \ 287 Type *name = (Type *) scratch; \ 288 { \ 289 unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ 290 assert (_consumed <= scratch_size); \ 291 scratch += _consumed; \ 292 scratch_size -= _consumed; \ 293 } 294 295 ALLOCATE_ARRAY (hb_graphite2_cluster_t, clusters, buffer->len); 296 ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count); 297 298 #undef ALLOCATE_ARRAY 299 300 memset (clusters, 0, sizeof (clusters[0]) * buffer->len); 301 302 hb_codepoint_t *pg = gids; 303 clusters[0].cluster = buffer->info[0].cluster; 304 float curradv = 0.; 305 if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) 306 { 307 curradv = gr_slot_origin_X(gr_seg_first_slot(seg)); 308 clusters[0].advance = gr_seg_advance_X(seg) - curradv; 309 } 310 else 311 clusters[0].advance = 0; 312 for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++) 313 { 314 unsigned int before = gr_slot_before (is); 315 unsigned int after = gr_slot_after (is); 316 *pg = gr_slot_gid (is); 317 pg++; 318 while (clusters[ci].base_char > before && ci) 319 { 320 clusters[ci-1].num_chars += clusters[ci].num_chars; 321 clusters[ci-1].num_glyphs += clusters[ci].num_glyphs; 322 clusters[ci-1].advance += clusters[ci].advance; 323 ci--; 324 } 325 326 if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars) 327 { 328 hb_graphite2_cluster_t *c = clusters + ci + 1; 329 c->base_char = clusters[ci].base_char + clusters[ci].num_chars; 330 c->cluster = buffer->info[c->base_char].cluster; 331 c->num_chars = before - c->base_char; 332 c->base_glyph = ic; 333 c->num_glyphs = 0; 334 if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) 335 c->advance = curradv - gr_slot_origin_X(is); 336 else 337 { 338 c->advance = 0; 339 clusters[ci].advance += gr_slot_origin_X(is) - curradv; 340 } 341 ci++; 342 curradv = gr_slot_origin_X(is); 343 } 344 clusters[ci].num_glyphs++; 345 346 if (clusters[ci].base_char + clusters[ci].num_chars < after + 1) 347 clusters[ci].num_chars = after + 1 - clusters[ci].base_char; 348 } 349 350 if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) 351 clusters[ci].advance += curradv; 352 else 353 clusters[ci].advance += gr_seg_advance_X(seg) - curradv; 354 ci++; 355 356 for (unsigned int i = 0; i < ci; ++i) 357 { 358 for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j) 359 { 360 hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j]; 361 info->codepoint = gids[clusters[i].base_glyph + j]; 362 info->cluster = clusters[i].cluster; 363 info->mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK; 364 info->var1.i32 = clusters[i].advance; // all glyphs in the cluster get the same advance 365 } 366 } 367 buffer->len = glyph_count; 368 369 unsigned int upem = hb_face_get_upem (face); 370 float xscale = (float) font->x_scale / upem; 371 float yscale = (float) font->y_scale / upem; 372 yscale *= yscale / xscale; 373 /* Positioning. */ 374 unsigned int currclus = (unsigned int) -1; 375 const hb_glyph_info_t *info = buffer->info; 376 hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, nullptr); 377 if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) 378 { 379 curradvx = 0; 380 for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is)) 381 { 382 pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx; 383 pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; 384 if (info->cluster != currclus) { 385 pPos->x_advance = info->var1.i32 * xscale; 386 curradvx += pPos->x_advance; 387 currclus = info->cluster; 388 } else 389 pPos->x_advance = 0.; 390 391 pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; 392 curradvy += pPos->y_advance; 393 } 394 } 395 else 396 { 397 curradvx = gr_seg_advance_X(seg) * xscale; 398 for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is)) 399 { 400 if (info->cluster != currclus) 401 { 402 pPos->x_advance = info->var1.i32 * xscale; 403 curradvx -= pPos->x_advance; 404 currclus = info->cluster; 405 } else 406 pPos->x_advance = 0.; 407 408 pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; 409 curradvy -= pPos->y_advance; 410 pPos->x_offset = (gr_slot_origin_X (is) - info->var1.i32) * xscale - curradvx + pPos->x_advance; 411 pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; 412 } 413 hb_buffer_reverse_clusters (buffer); 414 } 415 416 if (feats) gr_featureval_destroy (feats); 417 gr_seg_destroy (seg); 418 419 buffer->unsafe_to_break_all (); 420 421 return true; 422 } 423