Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright © 2012 Mozilla Foundation. |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 3 | * Copyright © 2012 Google, Inc. |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 4 | * |
| 5 | * This is part of HarfBuzz, a text shaping library. |
| 6 | * |
| 7 | * Permission is hereby granted, without written agreement and without |
| 8 | * license or royalty fees, to use, copy, modify, and distribute this |
| 9 | * software and its documentation for any purpose, provided that the |
| 10 | * above copyright notice and the following two paragraphs appear in |
| 11 | * all copies of this software. |
| 12 | * |
| 13 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
| 14 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
| 15 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
| 16 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
| 17 | * DAMAGE. |
| 18 | * |
| 19 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
| 20 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
| 21 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
| 22 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
| 23 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
| 24 | * |
| 25 | * Mozilla Author(s): Jonathan Kew |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 26 | * Google Author(s): Behdad Esfahbod |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 27 | */ |
| 28 | |
Behdad Esfahbod | 027857d | 2012-07-26 17:34:25 -0400 | [diff] [blame] | 29 | #define HB_SHAPER coretext |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 30 | #include "hb-shaper-impl-private.hh" |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 31 | |
| 32 | #define GlyphID GlyphID_mac |
| 33 | #include <ApplicationServices/ApplicationServices.h> |
| 34 | #undef GlyphID |
| 35 | |
| 36 | #include "hb-coretext.h" |
| 37 | |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 38 | |
| 39 | #ifndef HB_DEBUG_CORETEXT |
| 40 | #define HB_DEBUG_CORETEXT (HB_DEBUG+0) |
| 41 | #endif |
| 42 | |
| 43 | |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 44 | HB_SHAPER_DATA_ENSURE_DECLARE(coretext, face) |
| 45 | HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font) |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 46 | |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 47 | |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 48 | /* |
| 49 | * shaper face data |
| 50 | */ |
| 51 | |
| 52 | struct hb_coretext_shaper_face_data_t { |
| 53 | CGFontRef cg_font; |
| 54 | }; |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 55 | |
| 56 | static void |
| 57 | release_data (void *info, const void *data, size_t size) |
| 58 | { |
| 59 | assert (hb_blob_get_length ((hb_blob_t *) info) == size && |
| 60 | hb_blob_get_data ((hb_blob_t *) info, NULL) == data); |
| 61 | |
| 62 | hb_blob_destroy ((hb_blob_t *) info); |
| 63 | } |
| 64 | |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 65 | hb_coretext_shaper_face_data_t * |
| 66 | _hb_coretext_shaper_face_data_create (hb_face_t *face) |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 67 | { |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 68 | hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) calloc (1, sizeof (hb_coretext_shaper_face_data_t)); |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 69 | if (unlikely (!data)) |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 70 | return NULL; |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 71 | |
| 72 | hb_blob_t *blob = hb_face_reference_blob (face); |
| 73 | unsigned int blob_length; |
| 74 | const char *blob_data = hb_blob_get_data (blob, &blob_length); |
| 75 | if (unlikely (!blob_length)) |
| 76 | DEBUG_MSG (CORETEXT, face, "Face has empty blob"); |
| 77 | |
| 78 | CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data); |
| 79 | data->cg_font = CGFontCreateWithDataProvider (provider); |
| 80 | CGDataProviderRelease (provider); |
| 81 | |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 82 | if (unlikely (!data->cg_font)) { |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 83 | DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 84 | free (data); |
| 85 | return NULL; |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 86 | } |
| 87 | |
| 88 | return data; |
| 89 | } |
| 90 | |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 91 | void |
| 92 | _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 93 | { |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 94 | CFRelease (data->cg_font); |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 95 | free (data); |
| 96 | } |
| 97 | |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 98 | |
| 99 | /* |
| 100 | * shaper font data |
| 101 | */ |
| 102 | |
| 103 | struct hb_coretext_shaper_font_data_t { |
| 104 | CTFontRef ct_font; |
| 105 | }; |
| 106 | |
| 107 | hb_coretext_shaper_font_data_t * |
| 108 | _hb_coretext_shaper_font_data_create (hb_font_t *font) |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 109 | { |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 110 | if (unlikely (!hb_coretext_shaper_face_data_ensure (font->face))) return NULL; |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 111 | |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 112 | hb_coretext_shaper_font_data_t *data = (hb_coretext_shaper_font_data_t *) calloc (1, sizeof (hb_coretext_shaper_font_data_t)); |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 113 | if (unlikely (!data)) |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 114 | return NULL; |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 115 | |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 116 | hb_face_t *face = font->face; |
| 117 | hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 118 | |
| 119 | data->ct_font = CTFontCreateWithGraphicsFont (face_data->cg_font, font->y_scale, NULL, NULL); |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 120 | if (unlikely (!data->ct_font)) { |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 121 | DEBUG_MSG (CORETEXT, font, "Font CTFontCreateWithGraphicsFont() failed"); |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 122 | free (data); |
| 123 | return NULL; |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 124 | } |
| 125 | |
| 126 | return data; |
| 127 | } |
| 128 | |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 129 | void |
| 130 | _hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data) |
| 131 | { |
| 132 | CFRelease (data->ct_font); |
| 133 | free (data); |
| 134 | } |
| 135 | |
| 136 | |
| 137 | /* |
| 138 | * shaper shape_plan data |
| 139 | */ |
| 140 | |
| 141 | struct hb_coretext_shaper_shape_plan_data_t {}; |
| 142 | |
| 143 | hb_coretext_shaper_shape_plan_data_t * |
| 144 | _hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan, |
| 145 | const hb_feature_t *user_features, |
| 146 | unsigned int num_user_features) |
| 147 | { |
| 148 | return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; |
| 149 | } |
| 150 | |
| 151 | void |
| 152 | _hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_t *data) |
| 153 | { |
| 154 | } |
| 155 | |
| 156 | |
| 157 | /* |
| 158 | * shaper |
| 159 | */ |
| 160 | |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 161 | CTFontRef |
| 162 | hb_coretext_font_get_ct_font (hb_font_t *font) |
| 163 | { |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 164 | if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return 0; |
| 165 | hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 166 | return font_data->ct_font; |
| 167 | } |
| 168 | |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 169 | hb_bool_t |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 170 | _hb_coretext_shape (hb_shape_plan_t *shape_plan, |
| 171 | hb_font_t *font, |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 172 | hb_buffer_t *buffer, |
| 173 | const hb_feature_t *features, |
| 174 | unsigned int num_features) |
| 175 | { |
Behdad Esfahbod | 301168d | 2012-07-30 17:48:04 -0400 | [diff] [blame^] | 176 | hb_face_t *face = font->face; |
| 177 | hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); |
| 178 | hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 179 | |
| 180 | #define FAIL(...) \ |
| 181 | HB_STMT_START { \ |
| 182 | DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \ |
| 183 | return false; \ |
| 184 | } HB_STMT_END; |
| 185 | |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 186 | unsigned int scratch_size; |
| 187 | char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size); |
| 188 | |
| 189 | #define utf16_index() var1.u32 |
| 190 | |
| 191 | UniChar *pchars = (UniChar *) scratch; |
| 192 | unsigned int chars_len = 0; |
| 193 | for (unsigned int i = 0; i < buffer->len; i++) { |
| 194 | hb_codepoint_t c = buffer->info[i].codepoint; |
| 195 | buffer->info[i].utf16_index() = chars_len; |
| 196 | if (likely (c < 0x10000)) |
| 197 | pchars[chars_len++] = c; |
| 198 | else if (unlikely (c >= 0x110000)) |
| 199 | pchars[chars_len++] = 0xFFFD; |
| 200 | else { |
| 201 | pchars[chars_len++] = 0xD800 + ((c - 0x10000) >> 10); |
| 202 | pchars[chars_len++] = 0xDC00 + ((c - 0x10000) & ((1 << 10) - 1)); |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | #undef utf16_index |
| 207 | |
| 208 | CFStringRef string_ref = CFStringCreateWithCharactersNoCopy (kCFAllocatorDefault, |
| 209 | pchars, chars_len, |
| 210 | kCFAllocatorNull); |
| 211 | |
| 212 | CFDictionaryRef attrs = CFDictionaryCreate (kCFAllocatorDefault, |
| 213 | (const void**) &kCTFontAttributeName, |
| 214 | (const void**) &font_data->ct_font, |
| 215 | 1, // count of attributes |
| 216 | &kCFTypeDictionaryKeyCallBacks, |
| 217 | &kCFTypeDictionaryValueCallBacks); |
| 218 | |
| 219 | // TODO: support features |
| 220 | |
| 221 | // Now we can create an attributed string |
| 222 | CFAttributedStringRef attr_string = CFAttributedStringCreate (kCFAllocatorDefault, string_ref, attrs); |
| 223 | CFRelease (string_ref); |
| 224 | CFRelease (attrs); |
| 225 | |
| 226 | // Create the CoreText line from our string, then we're done with it |
| 227 | CTLineRef line = CTLineCreateWithAttributedString (attr_string); |
| 228 | CFRelease (attr_string); |
| 229 | |
| 230 | // and finally retrieve the glyph data and store into the gfxTextRun |
| 231 | CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); |
| 232 | unsigned int num_runs = CFArrayGetCount (glyph_runs); |
| 233 | |
| 234 | // Iterate through the glyph runs. |
| 235 | bool success = true; |
| 236 | buffer->len = 0; |
| 237 | |
| 238 | const CFRange range_all = CFRangeMake (0, 0); |
| 239 | |
| 240 | for (unsigned int i = 0; i < num_runs; i++) { |
| 241 | CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (glyph_runs, i); |
| 242 | |
| 243 | unsigned int num_glyphs = CTRunGetGlyphCount (run); |
| 244 | if (num_glyphs == 0) |
| 245 | continue; |
| 246 | |
| 247 | buffer->ensure (buffer->len + num_glyphs); |
| 248 | |
| 249 | // retrieve the laid-out glyph data from the CTRun |
| 250 | |
| 251 | // Testing indicates that CTRunGetGlyphsPtr (almost?) always succeeds, |
| 252 | // and so copying data to our own buffer with CTRunGetGlyphs will be |
| 253 | // extremely rare. |
| 254 | |
| 255 | unsigned int scratch_size; |
| 256 | char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size); |
| 257 | |
| 258 | #define ALLOCATE_ARRAY(Type, name, len) \ |
| 259 | Type *name = (Type *) scratch; \ |
Behdad Esfahbod | 441d3bb | 2012-07-26 12:01:12 -0400 | [diff] [blame] | 260 | scratch += (len) * sizeof ((name)[0]); \ |
| 261 | scratch_size -= (len) * sizeof ((name)[0]); |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 262 | |
| 263 | const CGGlyph* glyphs = CTRunGetGlyphsPtr (run); |
| 264 | if (!glyphs) { |
| 265 | ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs); |
| 266 | CTRunGetGlyphs (run, range_all, glyph_buf); |
| 267 | glyphs = glyph_buf; |
| 268 | } |
| 269 | |
| 270 | const CGPoint* positions = CTRunGetPositionsPtr (run); |
| 271 | if (!positions) { |
| 272 | ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs); |
| 273 | CTRunGetPositions (run, range_all, position_buf); |
| 274 | positions = position_buf; |
| 275 | } |
| 276 | |
| 277 | const CFIndex* string_indices = CTRunGetStringIndicesPtr (run); |
| 278 | if (!string_indices) { |
| 279 | ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs); |
| 280 | CTRunGetStringIndices (run, range_all, index_buf); |
| 281 | string_indices = index_buf; |
| 282 | } |
| 283 | |
| 284 | #undef ALLOCATE_ARRAY |
| 285 | |
| 286 | double run_width = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL); |
| 287 | |
| 288 | for (unsigned int j = 0; j < num_glyphs; j++) { |
| 289 | double advance = (j + 1 < num_glyphs ? positions[j + 1].x : positions[0].x + run_width) - positions[j].x; |
| 290 | |
| 291 | hb_glyph_info_t *info = &buffer->info[buffer->len]; |
| 292 | hb_glyph_position_t *pos = &buffer->pos[buffer->len]; |
| 293 | |
| 294 | info->codepoint = glyphs[j]; |
| 295 | info->cluster = string_indices[j]; |
| 296 | |
| 297 | // currently, we do all x-positioning by setting the advance, we never use x-offset |
| 298 | info->mask = advance; |
| 299 | info->var1.u32 = 0; |
| 300 | info->var2.u32 = positions[j].y; |
| 301 | |
| 302 | buffer->len++; |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | buffer->clear_positions (); |
| 307 | |
| 308 | unsigned int count = buffer->len; |
| 309 | for (unsigned int i = 0; i < count; ++i) { |
| 310 | hb_glyph_info_t *info = &buffer->info[i]; |
| 311 | hb_glyph_position_t *pos = &buffer->pos[i]; |
| 312 | |
| 313 | /* TODO vertical */ |
| 314 | pos->x_advance = info->mask; |
| 315 | pos->x_offset = info->var1.u32; |
| 316 | pos->y_offset = info->var2.u32; |
| 317 | } |
| 318 | |
Jonathan Kew | ac2085d | 2012-07-26 15:58:45 -0400 | [diff] [blame] | 319 | // Fix up clusters so that we never return out-of-order indices; |
| 320 | // if core text has reordered glyphs, we'll merge them to the |
| 321 | // beginning of the reordered cluster. |
| 322 | // This does *not* mean we'll form the same clusters as Uniscribe |
| 323 | // or the native OT backend, only that the cluster indices will be |
| 324 | // non-decreasing in the output buffer. |
| 325 | if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) { |
| 326 | unsigned int prev_cluster = 0; |
| 327 | for (unsigned int i = 0; i < count; i++) { |
| 328 | unsigned int curr_cluster = buffer->info[i].cluster; |
| 329 | if (curr_cluster < prev_cluster) { |
| 330 | for (unsigned int j = i; j > 0; j--) { |
| 331 | if (buffer->info[j - 1].cluster > curr_cluster) |
| 332 | buffer->info[j - 1].cluster = curr_cluster; |
| 333 | else |
| 334 | break; |
| 335 | } |
| 336 | } |
| 337 | prev_cluster = curr_cluster; |
| 338 | } |
| 339 | } else { |
| 340 | // For RTL runs, we make them non-increasing instead. |
| 341 | unsigned int prev_cluster = (unsigned int)-1; |
| 342 | for (unsigned int i = 0; i < count; i++) { |
| 343 | unsigned int curr_cluster = buffer->info[i].cluster; |
| 344 | if (curr_cluster > prev_cluster) { |
| 345 | for (unsigned int j = i; j > 0; j--) { |
| 346 | if (buffer->info[j - 1].cluster < curr_cluster) |
| 347 | buffer->info[j - 1].cluster = curr_cluster; |
| 348 | else |
| 349 | break; |
| 350 | } |
| 351 | } |
| 352 | prev_cluster = curr_cluster; |
| 353 | } |
| 354 | } |
| 355 | |
Jonathan Kew | aa6d849 | 2012-07-24 15:52:32 -0400 | [diff] [blame] | 356 | return true; |
| 357 | } |