OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright © 2011 Martin Hosken |
| 3 * Copyright © 2011 SIL International |
| 4 * Copyright © 2011 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-private.hh" |
| 30 |
| 31 #include "hb-graphite2.h" |
| 32 |
| 33 #include "hb-buffer-private.hh" |
| 34 #include "hb-font-private.hh" |
| 35 #include "hb-ot-tag.h" |
| 36 |
| 37 #include <graphite2/Font.h> |
| 38 #include <graphite2/Segment.h> |
| 39 |
| 40 |
| 41 struct hb_gr_cluster_t { |
| 42 unsigned int base_char; |
| 43 unsigned int num_chars; |
| 44 unsigned int base_glyph; |
| 45 unsigned int num_glyphs; |
| 46 }; |
| 47 |
| 48 |
| 49 typedef struct hb_gr_tablelist_t { |
| 50 hb_blob_t *blob; |
| 51 struct hb_gr_tablelist_t *next; |
| 52 unsigned int tag; |
| 53 } hb_gr_tablelist_t; |
| 54 |
| 55 static struct hb_gr_face_data_t { |
| 56 hb_face_t *face; |
| 57 gr_face *grface; |
| 58 hb_gr_tablelist_t *tlist; |
| 59 } _hb_gr_face_data_nil = {NULL, NULL}; |
| 60 |
| 61 static struct hb_gr_font_data_t { |
| 62 gr_font *grfont; |
| 63 gr_face *grface; |
| 64 } _hb_gr_font_data_nil = {NULL, NULL}; |
| 65 |
| 66 |
| 67 static const void *hb_gr_get_table (const void *data, unsigned int tag, size_t *
len) |
| 68 { |
| 69 hb_gr_tablelist_t *pl = NULL, *p; |
| 70 hb_gr_face_data_t *face = (hb_gr_face_data_t *) data; |
| 71 hb_gr_tablelist_t *tlist = face->tlist; |
| 72 |
| 73 for (p = tlist; p; p = p->next) |
| 74 if (p->tag == tag ) { |
| 75 unsigned int tlen; |
| 76 const char *d = hb_blob_get_data (p->blob, &tlen); |
| 77 *len = tlen; |
| 78 return d; |
| 79 } else |
| 80 pl = p; |
| 81 |
| 82 if (!face->face) |
| 83 return NULL; |
| 84 hb_blob_t *blob = hb_face_reference_table (face->face, tag); |
| 85 |
| 86 if (!pl || pl->blob) |
| 87 { |
| 88 p = (hb_gr_tablelist_t *) malloc (sizeof (hb_gr_tablelist_t)); |
| 89 if (!p) { |
| 90 hb_blob_destroy (blob); |
| 91 return NULL; |
| 92 } |
| 93 p->next = NULL; |
| 94 if (pl) |
| 95 pl->next = p; |
| 96 else |
| 97 face->tlist = p; |
| 98 pl = p; |
| 99 } |
| 100 pl->blob = blob; |
| 101 pl->tag = tag; |
| 102 |
| 103 unsigned int tlen; |
| 104 const char *d = hb_blob_get_data (blob, &tlen); |
| 105 *len = tlen; |
| 106 return d; |
| 107 } |
| 108 |
| 109 static float hb_gr_get_advance (const void *hb_font, unsigned short gid) |
| 110 { |
| 111 return hb_font_get_glyph_h_advance ((hb_font_t *) hb_font, gid); |
| 112 } |
| 113 |
| 114 static void _hb_gr_face_data_destroy (void *data) |
| 115 { |
| 116 hb_gr_face_data_t *f = (hb_gr_face_data_t *) data; |
| 117 hb_gr_tablelist_t *tlist = f->tlist; |
| 118 while (tlist) |
| 119 { |
| 120 hb_gr_tablelist_t *old = tlist; |
| 121 hb_blob_destroy (tlist->blob); |
| 122 tlist = tlist->next; |
| 123 free (old); |
| 124 } |
| 125 gr_face_destroy (f->grface); |
| 126 } |
| 127 |
| 128 static void _hb_gr_font_data_destroy (void *data) |
| 129 { |
| 130 hb_gr_font_data_t *f = (hb_gr_font_data_t *) data; |
| 131 |
| 132 gr_font_destroy (f->grfont); |
| 133 } |
| 134 |
| 135 static hb_user_data_key_t hb_gr_data_key; |
| 136 |
| 137 static hb_gr_face_data_t * |
| 138 _hb_gr_face_get_data (hb_face_t *face) |
| 139 { |
| 140 hb_gr_face_data_t *data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &
hb_gr_data_key); |
| 141 if (likely (data)) return data; |
| 142 |
| 143 data = (hb_gr_face_data_t *) calloc (1, sizeof (hb_gr_face_data_t)); |
| 144 if (unlikely (!data)) |
| 145 return &_hb_gr_face_data_nil; |
| 146 |
| 147 |
| 148 hb_blob_t *silf_blob = hb_face_reference_table (face, HB_GRAPHITE_TAG_Silf); |
| 149 if (!hb_blob_get_length (silf_blob)) |
| 150 { |
| 151 hb_blob_destroy (silf_blob); |
| 152 return &_hb_gr_face_data_nil; |
| 153 } |
| 154 |
| 155 data->face = face; |
| 156 data->grface = gr_make_face (data, &hb_gr_get_table, gr_face_default); |
| 157 |
| 158 |
| 159 if (unlikely (!hb_face_set_user_data (face, &hb_gr_data_key, data, |
| 160 (hb_destroy_func_t) _hb_gr_face_data_des
troy, |
| 161 FALSE))) |
| 162 { |
| 163 _hb_gr_face_data_destroy (data); |
| 164 data = (hb_gr_face_data_t *) hb_face_get_user_data (face, &hb_gr_data_key); |
| 165 if (data) |
| 166 return data; |
| 167 else |
| 168 return &_hb_gr_face_data_nil; |
| 169 } |
| 170 |
| 171 return data; |
| 172 } |
| 173 |
| 174 static hb_gr_font_data_t * |
| 175 _hb_gr_font_get_data (hb_font_t *font) |
| 176 { |
| 177 hb_gr_font_data_t *data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &
hb_gr_data_key); |
| 178 if (likely (data)) return data; |
| 179 |
| 180 data = (hb_gr_font_data_t *) calloc (1, sizeof (hb_gr_font_data_t)); |
| 181 if (unlikely (!data)) |
| 182 return &_hb_gr_font_data_nil; |
| 183 |
| 184 |
| 185 hb_blob_t *silf_blob = hb_face_reference_table (font->face, HB_GRAPHITE_TAG_Si
lf); |
| 186 if (!hb_blob_get_length (silf_blob)) |
| 187 { |
| 188 hb_blob_destroy (silf_blob); |
| 189 return &_hb_gr_font_data_nil; |
| 190 } |
| 191 |
| 192 data->grface = _hb_gr_face_get_data (font->face)->grface; |
| 193 int scale; |
| 194 hb_font_get_scale (font, &scale, NULL); |
| 195 data->grfont = gr_make_font_with_advance_fn (scale, font, &hb_gr_get_advance,
data->grface); |
| 196 |
| 197 |
| 198 if (unlikely (!hb_font_set_user_data (font, &hb_gr_data_key, data, |
| 199 (hb_destroy_func_t) _hb_gr_font_data_des
troy, |
| 200 FALSE))) |
| 201 { |
| 202 _hb_gr_font_data_destroy (data); |
| 203 data = (hb_gr_font_data_t *) hb_font_get_user_data (font, &hb_gr_data_key); |
| 204 if (data) |
| 205 return data; |
| 206 else |
| 207 return &_hb_gr_font_data_nil; |
| 208 } |
| 209 |
| 210 return data; |
| 211 } |
| 212 |
| 213 |
| 214 hb_bool_t |
| 215 hb_graphite_shape (hb_font_t *font, |
| 216 hb_buffer_t *buffer, |
| 217 const hb_feature_t *features, |
| 218 unsigned int num_features, |
| 219 const char * const *shaper_options) |
| 220 { |
| 221 |
| 222 buffer->guess_properties (); |
| 223 |
| 224 hb_gr_font_data_t *data = _hb_gr_font_get_data (font); |
| 225 if (!data->grface) return FALSE; |
| 226 |
| 227 unsigned int charlen; |
| 228 hb_glyph_info_t *bufferi = hb_buffer_get_glyph_infos (buffer, &charlen); |
| 229 |
| 230 int success = 0; |
| 231 |
| 232 if (!charlen) return TRUE; |
| 233 |
| 234 const char *lang = hb_language_to_string (hb_buffer_get_language (buffer)); |
| 235 const char *lang_end = strchr (lang, '-'); |
| 236 int lang_len = lang_end ? lang_end - lang : -1; |
| 237 gr_feature_val *feats = gr_face_featureval_for_lang (data->grface, lang ? hb_t
ag_from_string (lang, lang_len) : 0); |
| 238 |
| 239 while (num_features--) |
| 240 { |
| 241 const gr_feature_ref *fref = gr_face_find_fref (data->grface, features->tag)
; |
| 242 if (fref) |
| 243 gr_fref_set_feature_value (fref, features->value, feats); |
| 244 features++; |
| 245 } |
| 246 |
| 247 unsigned short *gids = NULL; |
| 248 hb_gr_cluster_t *clusters = NULL; |
| 249 gr_segment *seg = NULL; |
| 250 uint32_t *text = NULL; |
| 251 unsigned short *pg; |
| 252 const gr_slot *is; |
| 253 unsigned int ci = 0, ic = 0; |
| 254 float curradvx = 0., curradvy = 0.; |
| 255 unsigned int glyphlen = 0; |
| 256 unsigned int *p; |
| 257 |
| 258 text = (uint32_t *) malloc ((charlen + 1) * sizeof (uint32_t)); |
| 259 if (!text) goto dieout; |
| 260 |
| 261 p = text; |
| 262 for (unsigned int i = 0; i < charlen; ++i) |
| 263 *p++ = bufferi++->codepoint; |
| 264 *p = 0; |
| 265 |
| 266 hb_tag_t script_tag[2]; |
| 267 hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script
_tag[1]); |
| 268 |
| 269 seg = gr_make_seg (data->grfont, data->grface, |
| 270 script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1
], |
| 271 feats, |
| 272 gr_utf32, text, charlen, |
| 273 2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ?
1 : 0)); |
| 274 if (!seg) goto dieout; |
| 275 |
| 276 glyphlen = gr_seg_n_slots (seg); |
| 277 clusters = (hb_gr_cluster_t *) calloc (charlen, sizeof (hb_gr_cluster_t)); |
| 278 if (!glyphlen || !clusters) goto dieout; |
| 279 |
| 280 gids = (uint16_t *) malloc (glyphlen * sizeof (uint16_t)); |
| 281 if (!gids) goto dieout; |
| 282 |
| 283 pg = gids; |
| 284 for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (i
s), ic++) |
| 285 { |
| 286 unsigned int before = gr_slot_before (is); |
| 287 unsigned int after = gr_slot_after (is); |
| 288 *pg = gr_slot_gid (is); |
| 289 pg++; |
| 290 while (clusters[ci].base_char > before && ci) |
| 291 { |
| 292 clusters[ci-1].num_chars += clusters[ci].num_chars; |
| 293 clusters[ci-1].num_glyphs += clusters[ci].num_glyphs; |
| 294 ci--; |
| 295 } |
| 296 |
| 297 if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= cl
usters[ci].base_char + clusters[ci].num_chars) |
| 298 { |
| 299 hb_gr_cluster_t *c = clusters + ci + 1; |
| 300 c->base_char = clusters[ci].base_char + clusters[ci].num_chars; |
| 301 c->num_chars = before - c->base_char; |
| 302 c->base_glyph = ic; |
| 303 c->num_glyphs = 0; |
| 304 ci++; |
| 305 } |
| 306 clusters[ci].num_glyphs++; |
| 307 |
| 308 if (clusters[ci].base_char + clusters[ci].num_chars < after + 1) |
| 309 clusters[ci].num_chars = after + 1 - clusters[ci].base_char; |
| 310 } |
| 311 ci++; |
| 312 |
| 313 buffer->clear_output (); |
| 314 for (unsigned int i = 0; i < ci; ++i) |
| 315 buffer->replace_glyphs (clusters[i].num_chars, clusters[i].num_glyphs, gids
+ clusters[i].base_glyph); |
| 316 buffer->swap_buffers (); |
| 317 |
| 318 hb_glyph_position_t *pPos; |
| 319 for (pPos = hb_buffer_get_glyph_positions (buffer, NULL), is = gr_seg_first_sl
ot (seg); |
| 320 is; pPos++, is = gr_slot_next_in_segment (is)) |
| 321 { |
| 322 pPos->x_offset = gr_slot_origin_X(is) - curradvx; |
| 323 pPos->y_offset = gr_slot_origin_Y(is) - curradvy; |
| 324 pPos->x_advance = gr_slot_advance_X(is, data->grface, data->grfont); |
| 325 pPos->y_advance = gr_slot_advance_Y(is, data->grface, data->grfont); |
| 326 // if (pPos->x_advance < 0 && gr_slot_attached_to(is)) |
| 327 // pPos->x_advance = 0; |
| 328 curradvx += pPos->x_advance; |
| 329 curradvy += pPos->y_advance; |
| 330 } |
| 331 pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx; |
| 332 |
| 333 /* TODO(behdad): |
| 334 * This shaper is badly broken with RTL text. It returns glyphs |
| 335 * in the logical order! |
| 336 */ |
| 337 // if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) |
| 338 // hb_buffer_reverse (buffer); |
| 339 |
| 340 success = 1; |
| 341 |
| 342 dieout: |
| 343 if (gids) free (gids); |
| 344 if (clusters) free (clusters); |
| 345 if (seg) gr_seg_destroy (seg); |
| 346 if (text) free (text); |
| 347 return success; |
| 348 } |
OLD | NEW |