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