diff options
Diffstat (limited to 'src/hb-coretext.cc')
-rw-r--r-- | src/hb-coretext.cc | 236 |
1 files changed, 155 insertions, 81 deletions
diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc index 4a45175..e64d265 100644 --- a/src/hb-coretext.cc +++ b/src/hb-coretext.cc @@ -27,7 +27,6 @@ */ #define HB_SHAPER coretext -#define hb_coretext_shaper_face_data_t CGFont #include "hb-shaper-impl-private.hh" #include "hb-coretext.h" @@ -78,6 +77,29 @@ HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font) * shaper face data */ +static CTFontDescriptorRef +get_last_resort_font_desc (void) +{ + // TODO Handle allocation failures? + CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0); + CFArrayRef cascade_list = CFArrayCreate (kCFAllocatorDefault, + (const void **) &last_resort, + 1, + &kCFTypeArrayCallBacks); + CFRelease (last_resort); + CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontCascadeListAttribute, + (const void **) &cascade_list, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (cascade_list); + + CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); + CFRelease (attributes); + return font_desc; +} + static void release_data (void *info, const void *data, size_t size) { @@ -87,14 +109,13 @@ release_data (void *info, const void *data, size_t size) hb_blob_destroy ((hb_blob_t *) info); } -hb_coretext_shaper_face_data_t * -_hb_coretext_shaper_face_data_create (hb_face_t *face) +static CGFontRef +create_cg_font (hb_face_t *face) { - hb_coretext_shaper_face_data_t *data = NULL; - + CGFontRef cg_font = NULL; if (face->destroy == (hb_destroy_func_t) CGFontRelease) { - data = CGFontRetain ((CGFontRef) face->user_data); + cg_font = CGFontRetain ((CGFontRef) face->user_data); } else { @@ -107,13 +128,76 @@ _hb_coretext_shaper_face_data_create (hb_face_t *face) CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data); if (likely (provider)) { - data = CGFontCreateWithDataProvider (provider); + cg_font = CGFontCreateWithDataProvider (provider); + if (unlikely (!cg_font)) + DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); CGDataProviderRelease (provider); } } + return cg_font; +} + +static CTFontRef +create_ct_font (CGFontRef cg_font, CGFloat font_size) +{ + CTFontRef ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, NULL, NULL); + if (unlikely (!ct_font)) { + DEBUG_MSG (CORETEXT, cg_font, "Font CTFontCreateWithGraphicsFont() failed"); + return NULL; + } + + /* Create font copy with cascade list that has LastResort first; this speeds up CoreText + * font fallback which we don't need anyway. */ + { + CTFontDescriptorRef last_resort_font_desc = get_last_resort_font_desc (); + CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, NULL, last_resort_font_desc); + CFRelease (last_resort_font_desc); + if (new_ct_font) + { + CFRelease (ct_font); + ct_font = new_ct_font; + } + else + DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed"); + } - if (unlikely (!data)) { - DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); + return ct_font; +} + +struct hb_coretext_shaper_face_data_t { + CGFontRef cg_font; + CTFontRef ct_font; +}; + +hb_coretext_shaper_face_data_t * +_hb_coretext_shaper_face_data_create (hb_face_t *face) +{ + hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) calloc (1, sizeof (hb_coretext_shaper_face_data_t)); + if (unlikely (!data)) + return NULL; + + data->cg_font = create_cg_font (face); + if (unlikely (!data->cg_font)) + { + DEBUG_MSG (CORETEXT, face, "CGFont creation failed.."); + free (data); + return NULL; + } + + /* We use 36pt size instead of UPEM, because CoreText implements the 'trak' table, + * which can make the font too tight at large sizes. 36pt should be a good semi-neutral + * size. + * + * Since we always create CTFont at a fixed size, our CTFont lives in face_data + * instead of font_data. Which is good, because when people change scale on + * hb_font_t, we won't need to update our CTFont. */ + data->ct_font = create_ct_font (data->cg_font, 36.); + if (unlikely (!data->ct_font)) + { + DEBUG_MSG (CORETEXT, face, "CTFont creation failed."); + CFRelease (data->cg_font); + free (data); + return NULL; } return data; @@ -122,15 +206,20 @@ _hb_coretext_shaper_face_data_create (hb_face_t *face) void _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) { - CFRelease (data); + CFRelease (data->ct_font); + CFRelease (data->cg_font); + free (data); } +/* + * Since: 0.9.10 + */ CGFontRef hb_coretext_face_get_cg_font (hb_face_t *face) { if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL; hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); - return face_data; + return face_data->cg_font; } @@ -138,48 +227,17 @@ hb_coretext_face_get_cg_font (hb_face_t *face) * shaper font data */ -struct hb_coretext_shaper_font_data_t { - CTFontRef ct_font; - CGFloat x_mult, y_mult; /* From CT space to HB space. */ -}; +struct hb_coretext_shaper_font_data_t {}; hb_coretext_shaper_font_data_t * -_hb_coretext_shaper_font_data_create (hb_font_t *font) +_hb_coretext_shaper_font_data_create (hb_font_t *font HB_UNUSED) { - if (unlikely (!hb_coretext_shaper_face_data_ensure (font->face))) return NULL; - - hb_coretext_shaper_font_data_t *data = (hb_coretext_shaper_font_data_t *) calloc (1, sizeof (hb_coretext_shaper_font_data_t)); - if (unlikely (!data)) - return NULL; - - hb_face_t *face = font->face; - hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); - - /* Choose a CoreText font size and calculate multipliers to convert to HarfBuzz space. */ - CGFloat font_size = 36.; /* Default... */ - /* No idea if the following is even a good idea. */ - if (font->y_ppem) - font_size = font->y_ppem; - - if (font_size < 0) - font_size = -font_size; - data->x_mult = (CGFloat) font->x_scale / font_size; - data->y_mult = (CGFloat) font->y_scale / font_size; - data->ct_font = CTFontCreateWithGraphicsFont (face_data, font_size, NULL, NULL); - if (unlikely (!data->ct_font)) { - DEBUG_MSG (CORETEXT, font, "Font CTFontCreateWithGraphicsFont() failed"); - free (data); - return NULL; - } - - return data; + return (hb_coretext_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; } void _hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data) { - CFRelease (data->ct_font); - free (data); } @@ -205,9 +263,10 @@ _hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_ CTFontRef hb_coretext_font_get_ct_font (hb_font_t *font) { - if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return NULL; - hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); - return font_data->ct_font; + hb_face_t *face = font->face; + if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL; + hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + return face_data->ct_font; } @@ -440,7 +499,10 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, { hb_face_t *face = font->face; hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); - hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); + + CGFloat ct_font_size = CTFontGetSize (face_data->ct_font); + CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size; + CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size; /* Attach marks to their bases, to match the 'ot' shaper. * Adapted from hb-ot-shape:hb_form_clusters(). @@ -449,6 +511,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will * continue pointing to B2 even though B2 was merged into B1's * cluster... */ + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) { hb_unicode_funcs_t *unicode = buffer->unicode; unsigned int count = buffer->len; @@ -571,7 +634,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); CFRelease (attributes); - range->font = CTFontCreateCopyWithAttributes (font_data->ct_font, 0.0, NULL, font_desc); + range->font = CTFontCreateCopyWithAttributes (face_data->ct_font, 0.0, NULL, font_desc); CFRelease (font_desc); } else @@ -689,7 +752,6 @@ resize_and_retry: scratch += old_scratch_used; scratch_size -= old_scratch_used; } -retry: { string_ref = CFStringCreateWithCharactersNoCopy (NULL, pchars, chars_len, @@ -729,7 +791,7 @@ retry: CFRelease (lang); } CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), - kCTFontAttributeName, font_data->ct_font); + kCTFontAttributeName, face_data->ct_font); if (num_features) { @@ -788,6 +850,17 @@ retry: buffer->len = 0; uint32_t status_and = ~0, status_or = 0; double advances_so_far = 0; + /* For right-to-left runs, CoreText returns the glyphs positioned such that + * any trailing whitespace is to the left of (0,0). Adjust coordinate system + * to fix for that. Test with any RTL string with trailing spaces. + * https://code.google.com/p/chromium/issues/detail?id=469028 + */ + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + { + advances_so_far -= CTLineGetTrailingWhitespaceWidth (line); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + advances_so_far = -advances_so_far; + } const CFRange range_all = CFRangeMake (0, 0); @@ -811,7 +884,7 @@ retry: */ CFDictionaryRef attributes = CTRunGetAttributes (run); CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName)); - if (!CFEqual (run_ct_font, font_data->ct_font)) + if (!CFEqual (run_ct_font, face_data->ct_font)) { /* The run doesn't use our main font instance. We have to figure out * whether font fallback happened, or this is just CoreText giving us @@ -831,15 +904,11 @@ retry: * backend. * * However, even that wouldn't work if we were passed in the CGFont to - * begin with. - * - * Webkit uses a slightly different approach: it installs LastResort - * as fallback chain, and then checks PS name of used font against - * LastResort. That one is safe for any font except for LastResort, - * as opposed to ours, which can fail if we are using any uninstalled - * font that has the same name as an installed font. + * construct a hb_face to begin with. * * See: http://github.com/behdad/harfbuzz/pull/36 + * + * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098 */ bool matched = false; for (unsigned int i = 0; i < range_records.len; i++) @@ -853,13 +922,13 @@ retry: CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0); if (run_cg_font) { - matched = CFEqual (run_cg_font, face_data); + matched = CFEqual (run_cg_font, face_data->cg_font); CFRelease (run_cg_font); } } if (!matched) { - CFStringRef font_ps_name = CTFontCopyName (font_data->ct_font, kCTFontPostScriptNameKey); + CFStringRef font_ps_name = CTFontCopyName (face_data->ct_font, kCTFontPostScriptNameKey); CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey); CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0); CFRelease (run_ps_name); @@ -904,8 +973,8 @@ retry: info->cluster = log_clusters[j]; info->mask = advance; - info->var1.u32 = x_offset; - info->var2.u32 = y_offset; + info->var1.i32 = x_offset; + info->var2.i32 = y_offset; info++; buffer->len++; @@ -979,7 +1048,6 @@ retry: positions = position_buf; } hb_glyph_info_t *info = run_info; - CGFloat x_mult = font_data->x_mult, y_mult = font_data->y_mult; if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) { hb_position_t x_offset = (positions[0].x - advances_so_far) * x_mult; @@ -991,8 +1059,8 @@ retry: else /* last glyph */ advance = run_advance - (positions[j].x - positions[0].x); info->mask = advance * x_mult; - info->var1.u32 = x_offset; - info->var2.u32 = positions[j].y * y_mult; + info->var1.i32 = x_offset; + info->var2.i32 = positions[j].y * y_mult; info++; } } @@ -1007,8 +1075,8 @@ retry: else /* last glyph */ advance = run_advance - (positions[j].y - positions[0].y); info->mask = advance * y_mult; - info->var1.u32 = positions[j].x * x_mult; - info->var2.u32 = y_offset; + info->var1.i32 = positions[j].x * x_mult; + info->var2.i32 = y_offset; info++; } } @@ -1023,10 +1091,20 @@ retry: buffer->len += num_glyphs; } - /* Make sure all runs had the expected direction. */ - bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); - assert (bool (status_and & kCTRunStatusRightToLeft) == backward); - assert (bool (status_or & kCTRunStatusRightToLeft) == backward); + /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel, + * or if it does, it doesn't resepct it. So we get runs with wrong + * directions. As such, disable the assert... It wouldn't crash, but + * cursoring will be off... + * + * http://crbug.com/419769 + */ + if (0) + { + /* Make sure all runs had the expected direction. */ + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + assert (bool (status_and & kCTRunStatusRightToLeft) == backward); + assert (bool (status_or & kCTRunStatusRightToLeft) == backward); + } buffer->clear_positions (); @@ -1037,16 +1115,16 @@ retry: for (unsigned int i = 0; i < count; i++) { pos->x_advance = info->mask; - pos->x_offset = info->var1.u32; - pos->y_offset = info->var2.u32; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; info++, pos++; } else for (unsigned int i = 0; i < count; i++) { pos->y_advance = info->mask; - pos->x_offset = info->var1.u32; - pos->y_offset = info->var2.u32; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; info++, pos++; } @@ -1104,10 +1182,6 @@ fail: * AAT shaper */ -HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat, face) -HB_SHAPER_DATA_ENSURE_DECLARE(coretext_aat, font) - - /* * shaper face data */ |