From 0048f0f8035549f024a6a8571afba84742cb3dac Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 17 Apr 2022 10:47:10 -0700 Subject: [PATCH 1/7] [quartz] Fix some compiler warnings. --- src/cairo-quartz-surface.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index 5681918c4..07484efb6 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -1472,7 +1472,7 @@ _cairo_quartz_surface_map_to_image (void *abstract_surface, cairo_surface_t *return_surface = NULL; unsigned int stride, bitinfo, bpp, color_comps; CGColorSpaceRef colorspace; - void *imageData; + unsigned char *imageData; cairo_format_t format; if (IS_EMPTY (surface)) @@ -2632,7 +2632,7 @@ quartz_image_to_png (CGImageRef image, const char *dest) memset (pathbuf, 0, sizeof (pathbuf)); dest = dest ? dest : image_name; - snprintf (pathbuf, sizeof (pathbuf), "%s/Desktop/%s%d.png",getenv ("HOME"), dest, sctr++, ext); + snprintf (pathbuf, sizeof (pathbuf), "%s/Desktop/%s%d.png",getenv ("HOME"), dest, sctr++); path = CFStringCreateWithCString (NULL, pathbuf, kCFStringEncodingUTF8); url = CFURLCreateWithFileSystemPath (NULL, path, kCFURLPOSIXPathStyle, FALSE); image_dest = CGImageDestinationCreateWithURL (url, png_utti, 1, NULL); @@ -2647,7 +2647,6 @@ quartz_image_to_png (CGImageRef image, const char *dest) void quartz_surface_to_png (cairo_quartz_surface_t *nq, const char *dest) { - static int sctr = 0; CGImageRef image; if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) { From 77a8d0f9e4f730e90af6367b8569de62cc0f3c9e Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 7 Apr 2022 16:51:16 -0700 Subject: [PATCH 2/7] [quartz] Require at least Mac OS X 10.7 Lion Allows removal of most conditional compilation and dlsym lookups. --- configure.ac | 7 +- meson.build | 20 ++-- src/cairo-quartz-font.c | 189 +++++++------------------------------ src/cairo-quartz-surface.c | 177 ++++------------------------------ 4 files changed, 66 insertions(+), 327 deletions(-) diff --git a/configure.ac b/configure.ac index 209df4fb8..c75648779 100644 --- a/configure.ac +++ b/configure.ac @@ -193,11 +193,8 @@ dnl =========================================================================== CAIRO_ENABLE_SURFACE_BACKEND(quartz, Quartz, auto, [ dnl There is no pkgconfig for quartz; lets do a header check AC_CHECK_HEADER(ApplicationServices/ApplicationServices.h, , [use_quartz="no (requires ApplicationServices framework)"]) - if test "x$use_quartz" != "xyes" ; then - dnl check for CoreGraphics as a separate framework - AC_CHECK_HEADER(CoreGraphics/CoreGraphics.h, , [use_quartz="no (requires CoreGraphics framework)"]) - quartz_LIBS="-Xlinker -framework -Xlinker CoreGraphics" - else + AC_CHECK_FUNC([CTFontDrawGlyphs],,[use_quartz_fonts="no (requires Mac OS X 10.7 or later)"]) + if test "x$use_quartz" = "xyes" ; then quartz_LIBS="-Xlinker -framework -Xlinker ApplicationServices" fi ]) diff --git a/meson.build b/meson.build index 8c07f02e9..aa6506332 100644 --- a/meson.build +++ b/meson.build @@ -455,7 +455,6 @@ if host_machine.system() == 'darwin' and not get_option('quartz').disabled() deps += [quartz_deps] feature_conf.set('CAIRO_HAS_QUARTZ_SURFACE', 1) - feature_conf.set('CAIRO_HAS_QUARTZ_FONT', 1) feature_conf.set('CAIRO_HAS_QUARTZ_IMAGE_SURFACE', 1) built_features += [ @@ -468,13 +467,18 @@ if host_machine.system() == 'darwin' and not get_option('quartz').disabled() 'name': 'cairo-quartz-image', 'description': 'Quartz Image surface backend', 'deps': quartz_deps, - }, - { - 'name': 'cairo-quartz-font', - 'description': 'Quartz font backend', - 'deps': quartz_deps, - }, - ] + }] + compiler = meson.get_compiler('c') + if compiler.has_function('CTFontDrawGlyphs', prefix: '#include ', + dependencies: quartz_deps) + built_features += [ + { + 'name': 'cairo-quartz-font', + 'description': 'Quartz font backend', + 'deps': quartz_deps, + }] + feature_conf.set('CAIRO_HAS_QUARTZ_FONT', 1) + endif endif endif diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c index 77a9d6a55..a9480a736 100644 --- a/src/cairo-quartz-font.c +++ b/src/cairo-quartz-font.c @@ -64,47 +64,11 @@ * Since: 1.6 **/ -static CFDataRef (*CGFontCopyTableForTagPtr) (CGFontRef font, uint32_t tag) = NULL; - -/* CreateWithFontName exists in 10.5, but not in 10.4; CreateWithName isn't public in 10.4 */ -static CGFontRef (*CGFontCreateWithFontNamePtr) (CFStringRef) = NULL; -static CGFontRef (*CGFontCreateWithNamePtr) (const char *) = NULL; - -/* These aren't public before 10.5, and some have different names in 10.4 */ -static int (*CGFontGetUnitsPerEmPtr) (CGFontRef) = NULL; -static bool (*CGFontGetGlyphAdvancesPtr) (CGFontRef, const CGGlyph[], size_t, int[]) = NULL; -static bool (*CGFontGetGlyphBBoxesPtr) (CGFontRef, const CGGlyph[], size_t, CGRect[]) = NULL; -static CGRect (*CGFontGetFontBBoxPtr) (CGFontRef) = NULL; - -/* Not public, but present */ -static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const CGGlyph[], size_t) = NULL; -static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; +/* These are private functions */ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; - -/* Not public in the least bit */ -static CGPathRef (*CGFontGetGlyphPathPtr) (CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph) = NULL; - -/* CTFontCreateWithGraphicsFont is not available until 10.5 */ -typedef const struct __CTFontDescriptor *CTFontDescriptorRef; -static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform*, CTFontDescriptorRef) = NULL; -static CGPathRef (*CTFontCreatePathForGlyphPtr) (CTFontRef, CGGlyph, CGAffineTransform *) = NULL; - -/* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */ -typedef struct { - int ascent; - int descent; - int leading; -} quartz_CGFontMetrics; -static quartz_CGFontMetrics* (*CGFontGetHMetricsPtr) (CGFontRef fontRef) = NULL; -static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL; -static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL; -static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL; - -/* Not public anymore in 64-bits nor in 10.7 */ static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL; static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE; -static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE; /* Defined in 10.11 */ #define CGGLYPH_MAX ((CGGlyph) 0xFFFE) /* kCGFontIndexMax */ @@ -116,51 +80,11 @@ quartz_font_ensure_symbols(void) if (_cairo_quartz_font_symbol_lookup_done) return; - CGFontCopyTableForTagPtr = dlsym(RTLD_DEFAULT, "CGFontCopyTableForTag"); - - /* Look for the 10.5 versions first */ - CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBBoxes"); - if (!CGFontGetGlyphBBoxesPtr) - CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBoundingBoxes"); - - CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnichars"); - if (!CGFontGetGlyphsForUnicharsPtr) - CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes"); - - CGFontGetFontBBoxPtr = dlsym(RTLD_DEFAULT, "CGFontGetFontBBox"); - - /* We just need one of these two */ - CGFontCreateWithFontNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithFontName"); - CGFontCreateWithNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithName"); - - /* These have the same name in 10.4 and 10.5 */ - CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm"); - CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances"); - - CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont"); - CTFontCreatePathForGlyphPtr = dlsym(RTLD_DEFAULT, "CTFontCreatePathForGlyph"); - if (!CTFontCreateWithGraphicsFontPtr || !CTFontCreatePathForGlyphPtr) - CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath"); - - CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics"); - CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent"); - CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent"); - CGFontGetLeadingPtr = dlsym(RTLD_DEFAULT, "CGFontGetLeading"); - - CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); - CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); + CGContextGetAllowsFontSmoothingPtr = + dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont"); - if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) && - CGFontGetGlyphBBoxesPtr && - CGFontGetGlyphsForUnicharsPtr && - CGFontGetUnitsPerEmPtr && - CGFontGetGlyphAdvancesPtr && - ((CTFontCreateWithGraphicsFontPtr && CTFontCreatePathForGlyphPtr) || CGFontGetGlyphPathPtr) && - (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr))) - _cairo_quartz_font_symbols_present = TRUE; - _cairo_quartz_font_symbol_lookup_done = TRUE; } @@ -191,10 +115,6 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, CGFontRef cgFont = NULL; int loop; - quartz_font_ensure_symbols(); - if (! _cairo_quartz_font_symbols_present) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - family = toy_face->family; full_name = _cairo_malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc. /* handle CSS-ish faces */ @@ -231,13 +151,9 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, strcat (full_name, " Oblique"); } - if (CGFontCreateWithFontNamePtr) { - cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); - cgFont = CGFontCreateWithFontNamePtr (cgFontName); - CFRelease (cgFontName); - } else { - cgFont = CGFontCreateWithNamePtr (full_name); - } + cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); + cgFont = CGFontCreateWithFontName (cgFontName); + CFRelease (cgFontName); if (cgFont) break; @@ -279,10 +195,6 @@ _cairo_quartz_font_face_scaled_font_create (void *abstract_face, double ems; CGRect bbox; - quartz_font_ensure_symbols(); - if (!_cairo_quartz_font_symbols_present) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - font = _cairo_malloc (sizeof(cairo_quartz_scaled_font_t)); if (font == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -295,46 +207,17 @@ _cairo_quartz_font_face_scaled_font_create (void *abstract_face, if (status) goto FINISH; - ems = CGFontGetUnitsPerEmPtr (font_face->cgFont); + ems = CGFontGetUnitsPerEm (font_face->cgFont); /* initialize metrics */ - if (CGFontGetFontBBoxPtr && CGFontGetAscentPtr) { - fs_metrics.ascent = (CGFontGetAscentPtr (font_face->cgFont) / ems); - fs_metrics.descent = - (CGFontGetDescentPtr (font_face->cgFont) / ems); - fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + - (CGFontGetLeadingPtr (font_face->cgFont) / ems); + fs_metrics.ascent = (CGFontGetAscent (font_face->cgFont) / ems); + fs_metrics.descent = - (CGFontGetDescent (font_face->cgFont) / ems); + fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + + (CGFontGetLeading (font_face->cgFont) / ems); - bbox = CGFontGetFontBBoxPtr (font_face->cgFont); - fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; - fs_metrics.max_y_advance = 0.0; - } else { - CGGlyph wGlyph; - UniChar u; - - quartz_CGFontMetrics *m; - m = CGFontGetHMetricsPtr (font_face->cgFont); - - /* On OX 10.4, GetHMetricsPtr sometimes returns NULL for unknown reasons */ - if (!m) { - status = _cairo_error(CAIRO_STATUS_NULL_POINTER); - goto FINISH; - } - - fs_metrics.ascent = (m->ascent / ems); - fs_metrics.descent = - (m->descent / ems); - fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + (m->leading / ems); - - /* We kind of have to guess here; W's big, right? */ - u = (UniChar) 'W'; - CGFontGetGlyphsForUnicharsPtr (font_face->cgFont, &u, &wGlyph, 1); - if (wGlyph && CGFontGetGlyphBBoxesPtr (font_face->cgFont, &wGlyph, 1, &bbox)) { - fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; - fs_metrics.max_y_advance = 0.0; - } else { - fs_metrics.max_x_advance = 0.0; - fs_metrics.max_y_advance = 0.0; - } - } + bbox = CGFontGetFontBBox (font_face->cgFont); + fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; + fs_metrics.max_y_advance = 0.0; status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics); @@ -371,11 +254,9 @@ const cairo_font_face_backend_t _cairo_quartz_font_face_backend = { cairo_font_face_t * cairo_quartz_font_face_create_for_cgfont (CGFontRef font) { - cairo_quartz_font_face_t *font_face; + cairo_quartz_font_face_t *font_face = + _cairo_malloc (sizeof (cairo_quartz_font_face_t)); - quartz_font_ensure_symbols(); - - font_face = _cairo_malloc (sizeof (cairo_quartz_font_face_t)); if (!font_face) { cairo_status_t ignore_status; ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -424,14 +305,14 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); int advance; CGRect bbox; - double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont); + double emscale = CGFontGetUnitsPerEm (font_face->cgFont); double xmin, ymin, xmax, ymax; if (unlikely (glyph == CGGLYPH_INVALID)) goto FAIL; - if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) || - !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) + if (!CGFontGetGlyphAdvances (font_face->cgFont, &glyph, 1, &advance) || + !CGFontGetGlyphBBoxes (font_face->cgFont, &glyph, 1, &bbox)) goto FAIL; /* broken fonts like Al Bayan return incorrect bounds for some null characters, @@ -558,6 +439,7 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); CGAffineTransform textMatrix; + CTFontRef ctFont; CGPathRef glyphPath; cairo_path_fixed_t *path; @@ -573,13 +455,9 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, -font->base.scale.yy, 0, 0); - if (CTFontCreateWithGraphicsFontPtr && CTFontCreatePathForGlyphPtr) { - CTFontRef ctFont = CTFontCreateWithGraphicsFontPtr (font_face->cgFont, 1.0, NULL, NULL); - glyphPath = CTFontCreatePathForGlyphPtr (ctFont, glyph, &textMatrix); - CFRelease (ctFont); - } else { - glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph); - } + ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, 1.0, NULL, NULL); + glyphPath = CTFontCreatePathForGlyph (ctFont, glyph, &textMatrix); + CFRelease (ctFont); if (!glyphPath) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -614,7 +492,7 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, int advance; CGRect bbox; double width, height; - double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont); + double emscale = CGFontGetUnitsPerEm (font_face->cgFont); CGContextRef cgContext = NULL; CGAffineTransform textMatrix; @@ -639,8 +517,8 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, return CAIRO_STATUS_SUCCESS; } - if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) || - !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) + if (!CGFontGetGlyphAdvances (font_face->cgFont, &glyph, 1, &advance) || + !CGFontGetGlyphBBoxes (font_face->cgFont, &glyph, 1, &bbox)) { return CAIRO_INT_STATUS_UNSUPPORTED; } @@ -706,9 +584,10 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, case CAIRO_ANTIALIAS_BEST: CGContextSetShouldAntialias (cgContext, TRUE); CGContextSetShouldSmoothFonts (cgContext, TRUE); - if (CGContextSetAllowsFontSmoothingPtr && + quartz_font_ensure_symbols (); + if (CGContextGetAllowsFontSmoothingPtr && !CGContextGetAllowsFontSmoothingPtr (cgContext)) - CGContextSetAllowsFontSmoothingPtr (cgContext, TRUE); + CGContextSetAllowsFontSmoothing (cgContext, TRUE); break; case CAIRO_ANTIALIAS_NONE: CGContextSetShouldAntialias (cgContext, FALSE); @@ -767,12 +646,13 @@ _cairo_quartz_ucs4_to_index (void *abstract_font, { cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font); + CTFontRef ctFont; CGGlyph glyph[2]; UniChar utf16[2]; int len = _cairo_ucs4_to_utf16 (ucs4, utf16); - CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, utf16, glyph, len); - + ctFont = CTFontCreateWithGraphicsFont(ffont->cgFont, 10.0, NULL, NULL); + CTFontGetGlyphsForCharacters (ctFont, utf16, glyph, len); return glyph[0]; } @@ -784,10 +664,7 @@ _cairo_quartz_load_truetype_table (void *abstract_font, unsigned long *length) { cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face (abstract_font); - CFDataRef data = NULL; - - if (likely (CGFontCopyTableForTagPtr)) - data = CGFontCopyTableForTagPtr (font_face->cgFont, tag); + CFDataRef data = CGFontCopyTableForTag (font_face->cgFont, tag); if (!data) return CAIRO_INT_STATUS_UNSUPPORTED; diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index 07484efb6..bc6968c06 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -85,46 +85,22 @@ * Since: 1.6 **/ -#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 -/* This method is private, but it exists. Its params are are exposed - * as args to the NS* method, but not as CG. - */ -enum PrivateCGCompositeMode { - kPrivateCGCompositeClear = 0, - kPrivateCGCompositeCopy = 1, - kPrivateCGCompositeSourceOver = 2, - kPrivateCGCompositeSourceIn = 3, - kPrivateCGCompositeSourceOut = 4, - kPrivateCGCompositeSourceAtop = 5, - kPrivateCGCompositeDestinationOver = 6, - kPrivateCGCompositeDestinationIn = 7, - kPrivateCGCompositeDestinationOut = 8, - kPrivateCGCompositeDestinationAtop = 9, - kPrivateCGCompositeXOR = 10, - kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s))) - kPrivateCGCompositePlusLighter = 12, // (min (1, s + d)) -}; -typedef enum PrivateCGCompositeMode PrivateCGCompositeMode; -CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode); -#endif - -/* Some of these are present in earlier versions of the OS than where - * they are public; other are not public at all - */ - -/* public since 10.6 */ -static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; -static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; - -/* not yet public */ -static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; -static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; - -static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; - /* - * Utility functions + * macOS Private functions */ +static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; +static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; +static void +quartz_ensure_symbols() +{ + static cairo_bool_t symbol_lookup_done = FALSE; + if (!symbol_lookup_done) { + CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType"); + CGContextGetAllowsFontSmoothingPtr = + dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); + symbol_lookup_done = TRUE; + } +} #ifdef QUARTZ_DEBUG static void quartz_surface_to_png (cairo_quartz_surface_t *nq, const char *dest); @@ -152,20 +128,6 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, unsigned int width, unsigned int height); -/* Load all extra symbols */ -static void quartz_ensure_symbols (void) -{ - if (likely (_cairo_quartz_symbol_lookup_done)) - return; - - CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType"); - CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath"); - CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); - CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); - - _cairo_quartz_symbol_lookup_done = TRUE; -} - CGImageRef CairoQuartzCreateCGImage (cairo_format_t format, unsigned int width, @@ -270,6 +232,7 @@ _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) if (unlikely (cgc == NULL)) return FALSE; + quartz_ensure_symbols (); if (likely (CGContextGetTypePtr)) { /* 4 is the type value of a bitmap context */ return CGContextGetTypePtr (cgc) == 4; @@ -377,58 +340,6 @@ _cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path, * Misc helpers/callbacks */ -#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 -static PrivateCGCompositeMode -_cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - return kPrivateCGCompositeClear; - case CAIRO_OPERATOR_SOURCE: - return kPrivateCGCompositeCopy; - case CAIRO_OPERATOR_OVER: - return kPrivateCGCompositeSourceOver; - case CAIRO_OPERATOR_IN: - return kPrivateCGCompositeSourceIn; - case CAIRO_OPERATOR_OUT: - return kPrivateCGCompositeSourceOut; - case CAIRO_OPERATOR_ATOP: - return kPrivateCGCompositeSourceAtop; - case CAIRO_OPERATOR_DEST_OVER: - return kPrivateCGCompositeDestinationOver; - case CAIRO_OPERATOR_DEST_IN: - return kPrivateCGCompositeDestinationIn; - case CAIRO_OPERATOR_DEST_OUT: - return kPrivateCGCompositeDestinationOut; - case CAIRO_OPERATOR_DEST_ATOP: - return kPrivateCGCompositeDestinationAtop; - case CAIRO_OPERATOR_XOR: - return kPrivateCGCompositeXOR; - case CAIRO_OPERATOR_ADD: - return kPrivateCGCompositePlusLighter; - - case CAIRO_OPERATOR_DEST: - case CAIRO_OPERATOR_SATURATE: - case CAIRO_OPERATOR_MULTIPLY: - case CAIRO_OPERATOR_SCREEN: - case CAIRO_OPERATOR_OVERLAY: - case CAIRO_OPERATOR_DARKEN: - case CAIRO_OPERATOR_LIGHTEN: - case CAIRO_OPERATOR_COLOR_DODGE: - case CAIRO_OPERATOR_COLOR_BURN: - case CAIRO_OPERATOR_HARD_LIGHT: - case CAIRO_OPERATOR_SOFT_LIGHT: - case CAIRO_OPERATOR_DIFFERENCE: - case CAIRO_OPERATOR_EXCLUSION: - case CAIRO_OPERATOR_HSL_HUE: - case CAIRO_OPERATOR_HSL_SATURATION: - case CAIRO_OPERATOR_HSL_COLOR: - case CAIRO_OPERATOR_HSL_LUMINOSITY: - default: - ASSERT_NOT_REACHED; - } -} -#endif static CGBlendMode _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op) @@ -465,7 +376,6 @@ _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op) case CAIRO_OPERATOR_HSL_LUMINOSITY: return kCGBlendModeLuminosity; -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 case CAIRO_OPERATOR_CLEAR: return kCGBlendModeClear; case CAIRO_OPERATOR_SOURCE: @@ -490,21 +400,6 @@ _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op) return kCGBlendModeXOR; case CAIRO_OPERATOR_ADD: return kCGBlendModePlusLighter; -#else - case CAIRO_OPERATOR_CLEAR: - case CAIRO_OPERATOR_SOURCE: - case CAIRO_OPERATOR_OVER: - case CAIRO_OPERATOR_IN: - case CAIRO_OPERATOR_OUT: - case CAIRO_OPERATOR_ATOP: - case CAIRO_OPERATOR_DEST_OVER: - case CAIRO_OPERATOR_DEST_IN: - case CAIRO_OPERATOR_DEST_OUT: - case CAIRO_OPERATOR_DEST_ATOP: - case CAIRO_OPERATOR_XOR: - case CAIRO_OPERATOR_ADD: -#endif - case CAIRO_OPERATOR_DEST: case CAIRO_OPERATOR_SATURATE: default: @@ -534,16 +429,6 @@ _cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op) return CAIRO_INT_STATUS_UNSUPPORTED; } -#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 - if (op <= CAIRO_OPERATOR_ADD) { - PrivateCGCompositeMode compmode; - - compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op); - CGContextSetCompositeOperation (context, compmode); - return CAIRO_STATUS_SUCCESS; - } -#endif - blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op); CGContextSetBlendMode (context, blendmode); return CAIRO_STATUS_SUCCESS; @@ -1558,13 +1443,6 @@ _cairo_quartz_surface_finish (void *abstract_surface) surface->cgContext = NULL; -#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600 - if (surface->imageData) { - free (surface->imageData); - surface->imageData = NULL; - } -#endif - if (surface->cgLayer) { CGLayerRelease (surface->cgLayer); @@ -2054,11 +1932,12 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, case CAIRO_ANTIALIAS_BEST: CGContextSetShouldAntialias (state.cgMaskContext, TRUE); CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE); - if (CGContextSetAllowsFontSmoothingPtr && + quartz_ensure_symbols(); + if (CGContextGetAllowsFontSmoothingPtr && !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext)) { didForceFontSmoothing = TRUE; - CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE); + CGContextSetAllowsFontSmoothing (state.cgMaskContext, TRUE); } break; case CAIRO_ANTIALIAS_NONE: @@ -2135,7 +2014,7 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, BAIL: if (didForceFontSmoothing) - CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE); + CGContextSetAllowsFontSmoothing (state.cgMaskContext, FALSE); _cairo_quartz_teardown_state (&state, extents); @@ -2319,8 +2198,6 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, { cairo_quartz_surface_t *surface; - quartz_ensure_symbols (); - /* Init the base surface */ surface = _cairo_malloc (sizeof (cairo_quartz_surface_t)); if (unlikely (surface == NULL)) @@ -2342,9 +2219,6 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext, surface->extents.width = width; surface->extents.height = height; surface->virtual_extents = surface->extents; -#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600 - surface->imageData = NULL; -#endif if (IS_EMPTY (surface)) { surface->cgContext = NULL; @@ -2473,16 +2347,6 @@ cairo_quartz_surface_create (cairo_format_t format, * so we don't have to anything special on allocation. */ stride = (stride + 15) & ~15; -#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600 - imageData = _cairo_malloc_ab (height, stride); - if (unlikely (!imageData)) { - CGColorSpaceRelease (cgColorspace); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - /* zero the memory to match the image surface behavior */ - memset (imageData, 0, height * stride); -#endif /* For newer macOS versions let Core Graphics manage the buffer. */ cgc = CGBitmapContextCreate (imageData, width, height, @@ -2515,9 +2379,6 @@ cairo_quartz_surface_create (cairo_format_t format, return &surf->base; } -#if MAC_OS_X_VERSION_MIN_REQUIRED < 10600 - surf->imageData = imageData; -#endif surf->base.is_clear = TRUE; return &surf->base; From cf351a8a0a5c0be291061f58db9853a95b9044d3 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 9 Apr 2022 11:16:11 -0700 Subject: [PATCH 3/7] Reimplement cairo-quartz-font with Core Text. --- src/cairo-quartz-font.c | 96 +++++++++++++++++++++++--------------- src/cairo-quartz-private.h | 4 ++ 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c index a9480a736..3a702ca0e 100644 --- a/src/cairo-quartz-font.c +++ b/src/cairo-quartz-font.c @@ -47,12 +47,9 @@ /** * SECTION:cairo-quartz-fonts * @Title: Quartz (CGFont) Fonts - * @Short_Description: Font support via CGFont on OS X + * @Short_Description: Font support via Core Text on Apple operating systems. * @See_Also: #cairo_font_face_t * - * The Quartz font backend is primarily used to render text on Apple - * MacOS X systems. The CGFont API is used for the internal - * implementation of the font backend methods. **/ /** @@ -99,6 +96,8 @@ struct _cairo_quartz_font_face { cairo_font_face_t base; CGFontRef cgFont; + CTFontRef ctFont; + double ctFont_scale; }; /* @@ -111,8 +110,8 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, { const char *family; char *full_name; - CFStringRef cgFontName = NULL; - CGFontRef cgFont = NULL; + CFStringRef FontName = NULL; + CTFontRef ctFont = NULL; int loop; family = toy_face->family; @@ -141,31 +140,31 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, if (loop < 3 && (loop & 1) == 0) { if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD) - strcat (full_name, " Bold"); + strcat (full_name, "-Bold"); } if (loop < 3 && (loop & 2) == 0) { if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC) - strcat (full_name, " Italic"); + strcat (full_name, "-Italic"); else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE) - strcat (full_name, " Oblique"); + strcat (full_name, "-Oblique"); } - cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); - cgFont = CGFontCreateWithFontName (cgFontName); - CFRelease (cgFontName); + FontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); + ctFont = CTFontCreateWithName (FontName, 1.0, NULL); + CFRelease (FontName); - if (cgFont) + if (ctFont) break; } - if (!cgFont) { + if (!ctFont) { /* Give up */ return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont); - CGFontRelease (cgFont); + *font_face = _cairo_quartz_font_face_create_for_ctfont (ctFont); + CFRelease (ctFont); return CAIRO_STATUS_SUCCESS; } @@ -176,6 +175,7 @@ _cairo_quartz_font_face_destroy (void *abstract_face) cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face; CGFontRelease (font_face->cgFont); + CFRelease (font_face->ctFont); return TRUE; } @@ -238,6 +238,23 @@ const cairo_font_face_backend_t _cairo_quartz_font_face_backend = { _cairo_quartz_font_face_scaled_font_create }; +static inline cairo_quartz_font_face_t* +_cairo_quartz_font_face_create () +{ + cairo_quartz_font_face_t *font_face = + _cairo_malloc (sizeof (cairo_quartz_font_face_t)); + + if (!font_face) { + cairo_status_t ignore_status; + ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_quartz_font_face_t *)&_cairo_font_face_nil; + } + + _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend); + + return font_face; +} + /** * cairo_quartz_font_face_create_for_cgfont: * @font: a #CGFontRef obtained through a method external to cairo. @@ -254,18 +271,30 @@ const cairo_font_face_backend_t _cairo_quartz_font_face_backend = { cairo_font_face_t * cairo_quartz_font_face_create_for_cgfont (CGFontRef font) { - cairo_quartz_font_face_t *font_face = - _cairo_malloc (sizeof (cairo_quartz_font_face_t)); + cairo_quartz_font_face_t* font_face = _cairo_quartz_font_face_create (); + double font_scale = 1.0; /* 1.0 produces glyphs of the right size to pass tests. */ - if (!font_face) { - cairo_status_t ignore_status; - ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - return (cairo_font_face_t *)&_cairo_font_face_nil; - } + if (cairo_font_face_status (&font_face->base)) + return &font_face->base; font_face->cgFont = CGFontRetain (font); + font_face->ctFont = CTFontCreateWithGraphicsFont (font, font_scale, NULL, NULL); + font_face->ctFont_scale = CTFontGetUnitsPerEm(font_face->ctFont); - _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend); + return &font_face->base; +} + +cairo_font_face_t * +_cairo_quartz_font_face_create_for_ctfont (CTFontRef font) +{ + cairo_quartz_font_face_t* font_face = _cairo_quartz_font_face_create (); + + if (cairo_font_face_status (&font_face->base)) + return &font_face->base; + + font_face->ctFont = CFRetain (font); + font_face->cgFont = CTFontCopyGraphicsFont (font, NULL); + font_face->ctFont_scale = CTFontGetUnitsPerEm(font_face->ctFont); return &font_face->base; } @@ -439,7 +468,6 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); CGAffineTransform textMatrix; - CTFontRef ctFont; CGPathRef glyphPath; cairo_path_fixed_t *path; @@ -455,9 +483,7 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, -font->base.scale.yy, 0, 0); - ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, 1.0, NULL, NULL); - glyphPath = CTFontCreatePathForGlyph (ctFont, glyph, &textMatrix); - CFRelease (ctFont); + glyphPath = CTFontCreatePathForGlyph (font_face->ctFont, glyph, &textMatrix); if (!glyphPath) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -517,11 +543,7 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, return CAIRO_STATUS_SUCCESS; } - if (!CGFontGetGlyphAdvances (font_face->cgFont, &glyph, 1, &advance) || - !CGFontGetGlyphBBoxes (font_face->cgFont, &glyph, 1, &bbox)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } + CTFontGetBoundingRectsForGlyphs (font_face->ctFont, FONT_ORIENTATION_HORIZONTAL, &glyph, &bbox, 1); /* scale(1,-1) * font->base.scale * scale(1,-1) */ textMatrix = CGAffineTransformMake (font->base.scale.xx, @@ -646,13 +668,11 @@ _cairo_quartz_ucs4_to_index (void *abstract_font, { cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font); - CTFontRef ctFont; CGGlyph glyph[2]; UniChar utf16[2]; int len = _cairo_ucs4_to_utf16 (ucs4, utf16); - ctFont = CTFontCreateWithGraphicsFont(ffont->cgFont, 10.0, NULL, NULL); - CTFontGetGlyphsForCharacters (ctFont, utf16, glyph, len); + CTFontGetGlyphsForCharacters (ffont->ctFont, utf16, glyph, len); return glyph[0]; } @@ -693,8 +713,8 @@ static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = { NULL, /* text_to_glyphs */ _cairo_quartz_ucs4_to_index, _cairo_quartz_load_truetype_table, - NULL, /* map_glyphs_to_unicode */ -}; + NULL, /*index_to_ucs4*/ +}; /* is_synthetic, index_to_glyph_name, load_type1_data, has_color_glyphs */ /* * private methods that the quartz surface uses diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h index 609a9052c..e3600d5fc 100644 --- a/src/cairo-quartz-private.h +++ b/src/cairo-quartz-private.h @@ -105,6 +105,10 @@ CairoQuartzCreateCGImage (cairo_format_t format, cairo_private CGFontRef _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont); +cairo_private CTFontRef +_cairo_quartz_scaled_font_get_ct_font (cairo_scaled_font_t *sfont); +cairo_private cairo_font_face_t* +_cairo_quartz_font_face_create_for_ctfont (CTFontRef ctFont); #else From c6dc5df61264be1bec2e622355b97c6c45d236fc Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 21 Apr 2022 17:19:15 -0700 Subject: [PATCH 4/7] [quartz] Convert font handling from CGFont to CTFont. --- src/cairo-quartz-font.c | 301 +++++++++--------- src/cairo-quartz-private.h | 2 + src/cairo-quartz-surface.c | 83 ++--- .../overlapping-glyphs.quartz.argb32.ref.png | Bin 2766 -> 2687 bytes .../overlapping-glyphs.quartz.rgb24.ref.png | Bin 1667 -> 1630 bytes 5 files changed, 184 insertions(+), 202 deletions(-) diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c index 3a702ca0e..9e667e0d6 100644 --- a/src/cairo-quartz-font.c +++ b/src/cairo-quartz-font.c @@ -44,6 +44,7 @@ #include "cairo-error-private.h" +//#define DEBUG /* Uncomment this to get debug messages on the console. */ /** * SECTION:cairo-quartz-fonts * @Title: Quartz (CGFont) Fonts @@ -66,11 +67,19 @@ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL; static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE; +/* Cairo's transformations assume a unit-scaled font. */ +static const CGFloat font_scale = 1.0; /* Defined in 10.11 */ #define CGGLYPH_MAX ((CGGlyph) 0xFFFE) /* kCGFontIndexMax */ #define CGGLYPH_INVALID ((CGGlyph) 0xFFFF) /* kCGFontIndexInvalid */ +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 +#define FONT_ORIENTATION_HORIZONTAL kCTFontHorizontalOrientation +#else +#define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal +#endif + static void quartz_font_ensure_symbols(void) { @@ -90,14 +99,13 @@ typedef struct _cairo_quartz_scaled_font cairo_quartz_scaled_font_t; struct _cairo_quartz_scaled_font { cairo_scaled_font_t base; + CTFontRef ctFont; }; struct _cairo_quartz_font_face { cairo_font_face_t base; CGFontRef cgFont; - CTFontRef ctFont; - double ctFont_scale; }; /* @@ -111,7 +119,7 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, const char *family; char *full_name; CFStringRef FontName = NULL; - CTFontRef ctFont = NULL; + CGFontRef cgFont = NULL; int loop; family = toy_face->family; @@ -151,20 +159,20 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, } FontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); - ctFont = CTFontCreateWithName (FontName, 1.0, NULL); + cgFont = CGFontCreateWithFontName (FontName); CFRelease (FontName); - if (ctFont) + if (cgFont) break; } - if (!ctFont) { + if (!cgFont) { /* Give up */ return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - *font_face = _cairo_quartz_font_face_create_for_ctfont (ctFont); - CFRelease (ctFont); + *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont); + CFRelease (cgFont); return CAIRO_STATUS_SUCCESS; } @@ -175,12 +183,56 @@ _cairo_quartz_font_face_destroy (void *abstract_face) cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face; CGFontRelease (font_face->cgFont); - CFRelease (font_face->ctFont); return TRUE; } static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend; +#ifdef DEBUG +static void +_cairo_quartz_debug_font_characteristics (cairo_quartz_scaled_font_t *font) +{ + CGRect ct_bbox = CTFontGetBoundingBox (font->ctFont); + CGFloat ct_ascent = CTFontGetAscent (font->ctFont); + CGFloat ct_descent = CTFontGetDescent (font->ctFont); + CGFloat ct_leading = CTFontGetLeading (font->ctFont); + CGFloat ct_capheight = CTFontGetCapHeight (font->ctFont); + CGFloat ct_xheight = CTFontGetXHeight (font->ctFont); + char chars[] = "ymMW"; + CGGlyph glyphs[4]; + UniChar *utf16 = NULL; + CGSize ct_advances[4]; + CGRect ct_gbbox[4], ct_gobox[4], ct_rbbox, ct_robox; + double ct_radvance; + int converted; + cairo_status_t rv; + + rv = _cairo_utf8_to_utf16 (chars, 4, &utf16, &converted); + if (rv) return; + CTFontGetGlyphsForCharacters (font->ctFont, utf16, glyphs, 4); + free (utf16); + ct_rbbox = CTFontGetBoundingRectsForGlyphs (font->ctFont, FONT_ORIENTATION_HORIZONTAL, glyphs, ct_gbbox, 4); + ct_robox = CTFontGetOpticalBoundsForGlyphs (font->ctFont, glyphs, ct_gobox, 4, 0); + ct_radvance = CTFontGetAdvancesForGlyphs (font->ctFont, FONT_ORIENTATION_HORIZONTAL, glyphs, ct_advances, 4); + + fprintf (stderr, "\nCTFont Bounding Box: %f %f %f %f\nAscent %f Descent %f Leading %f Cap Height %f X-Height %f\n", + ct_bbox.origin.x, ct_bbox.origin.y, ct_bbox.size.width, ct_bbox.size.height, ct_ascent, ct_descent, + ct_leading, ct_capheight, ct_xheight); + fprintf (stderr, "CTFont string\n\t bounding box %f %f %f %f advance %f\n\toptical box %f %f %f %f\n\n", + ct_rbbox.origin.x, ct_rbbox.origin.y, ct_rbbox.size.width, ct_rbbox.size.height, ct_radvance, + ct_robox.origin.x, ct_robox.origin.y, ct_robox.size.width, ct_robox.size.height); + for (int i = 0; i < 4; ++i) + { + fprintf (stderr, "Character %c\n", chars[i]); + fprintf (stderr, "\tbox %f %f %f %f\n\toptical %f %f %f %f advance %f %f\n", + ct_gbbox[i].origin.x, ct_gbbox[i].origin.y, ct_gbbox[i].size.width, ct_gbbox[i].size.height, + ct_advances[i].width, ct_advances[i].height, + ct_gobox[i].origin.x, ct_gobox[i].origin.y, ct_gobox[i].size.width, ct_gobox[i].size.height); + } + fprintf (stderr, "\n"); +} +#endif + static cairo_status_t _cairo_quartz_font_face_scaled_font_create (void *abstract_face, const cairo_matrix_t *font_matrix, @@ -192,7 +244,7 @@ _cairo_quartz_font_face_scaled_font_create (void *abstract_face, cairo_quartz_scaled_font_t *font = NULL; cairo_status_t status; cairo_font_extents_t fs_metrics; - double ems; + CTFontRef ctFont; CGRect bbox; font = _cairo_malloc (sizeof(cairo_quartz_scaled_font_t)); @@ -207,19 +259,29 @@ _cairo_quartz_font_face_scaled_font_create (void *abstract_face, if (status) goto FINISH; - ems = CGFontGetUnitsPerEm (font_face->cgFont); - + ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, font_scale, NULL, NULL); /* initialize metrics */ - fs_metrics.ascent = (CGFontGetAscent (font_face->cgFont) / ems); - fs_metrics.descent = - (CGFontGetDescent (font_face->cgFont) / ems); + fs_metrics.ascent = CTFontGetAscent (ctFont); + fs_metrics.descent = CTFontGetDescent (ctFont); fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + - (CGFontGetLeading (font_face->cgFont) / ems); + CTFontGetLeading (ctFont); - bbox = CGFontGetFontBBox (font_face->cgFont); - fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; + bbox = CTFontGetBoundingBox (ctFont); + fs_metrics.max_x_advance = CGRectGetMaxX(bbox); fs_metrics.max_y_advance = 0.0; - + font->ctFont = CFRetain (ctFont); status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics); +#ifdef DEBUG + { + CFStringRef fontFullName = CTFontCopyFullName (ctFont); + const char* font_full_name = CFStringGetCStringPtr(fontFullName, kCFStringEncodingUTF8); + + fprintf (stderr, "Create scaled font %s with scale %f ascent %f, descent %f, height %f, x-advance %f\n", + font_full_name, fs_metrics.ascent, fs_metrics.descent, fs_metrics.height, + fs_metrics.max_x_advance); + _cairo_quartz_debug_font_characteristics (font); + } +#endif FINISH: if (status != CAIRO_STATUS_SUCCESS) { @@ -272,29 +334,11 @@ cairo_font_face_t * cairo_quartz_font_face_create_for_cgfont (CGFontRef font) { cairo_quartz_font_face_t* font_face = _cairo_quartz_font_face_create (); - double font_scale = 1.0; /* 1.0 produces glyphs of the right size to pass tests. */ if (cairo_font_face_status (&font_face->base)) return &font_face->base; font_face->cgFont = CGFontRetain (font); - font_face->ctFont = CTFontCreateWithGraphicsFont (font, font_scale, NULL, NULL); - font_face->ctFont_scale = CTFontGetUnitsPerEm(font_face->ctFont); - - return &font_face->base; -} - -cairo_font_face_t * -_cairo_quartz_font_face_create_for_ctfont (CTFontRef font) -{ - cairo_quartz_font_face_t* font_face = _cairo_quartz_font_face_create (); - - if (cairo_font_face_status (&font_face->base)) - return &font_face->base; - - font_face->ctFont = CFRetain (font); - font_face->cgFont = CTFontCopyGraphicsFont (font, NULL); - font_face->ctFont_scale = CTFontGetUnitsPerEm(font_face->ctFont); return &font_face->base; } @@ -315,6 +359,8 @@ _cairo_quartz_scaled_to_face (void *abstract_font) static void _cairo_quartz_scaled_font_fini(void *abstract_font) { + cairo_quartz_scaled_font_t* font = (cairo_quartz_scaled_font_t*)abstract_font; + CFRelease (font->ctFont); } static inline CGGlyph @@ -329,21 +375,17 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); cairo_text_extents_t extents = {0, 0, 0, 0, 0, 0}; CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); - int advance; + CGSize advance; CGRect bbox; - double emscale = CGFontGetUnitsPerEm (font_face->cgFont); double xmin, ymin, xmax, ymax; if (unlikely (glyph == CGGLYPH_INVALID)) goto FAIL; - if (!CGFontGetGlyphAdvances (font_face->cgFont, &glyph, 1, &advance) || - !CGFontGetGlyphBBoxes (font_face->cgFont, &glyph, 1, &bbox)) - goto FAIL; - + CTFontGetAdvancesForGlyphs (font->ctFont, FONT_ORIENTATION_HORIZONTAL, &glyph, &advance, 1); + CTFontGetBoundingRectsForGlyphs (font->ctFont, FONT_ORIENTATION_HORIZONTAL, &glyph, &bbox, 1); /* broken fonts like Al Bayan return incorrect bounds for some null characters, see https://bugzilla.mozilla.org/show_bug.cgi?id=534260 */ if (unlikely (bbox.origin.x == -32767 && @@ -354,31 +396,9 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, bbox.size.width = bbox.size.height = 0; } - bbox = CGRectMake (bbox.origin.x / emscale, - bbox.origin.y / emscale, - bbox.size.width / emscale, - bbox.size.height / emscale); - - /* Should we want to always integer-align glyph extents, we can do so in this way */ -#if 0 - { - CGAffineTransform textMatrix; - textMatrix = CGAffineTransformMake (font->base.scale.xx, - -font->base.scale.yx, - -font->base.scale.xy, - font->base.scale.yy, - 0.0f, 0.0f); - - bbox = CGRectApplyAffineTransform (bbox, textMatrix); - bbox = CGRectIntegral (bbox); - bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix)); - } -#endif - -#if 0 - fprintf (stderr, "[0x%04x] bbox: %f %f %f %f\n", glyph, - bbox.origin.x / emscale, bbox.origin.y / emscale, - bbox.size.width / emscale, bbox.size.height / emscale); +#ifdef DEBUG + fprintf (stderr, "[0x%04x] bbox: x %f y %f width %f height %f\n", glyph, + bbox.origin.x, bbox.origin.y, bbox.size.width, bbox.size.height); #endif xmin = CGRectGetMinX(bbox); @@ -391,12 +411,12 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, extents.width = xmax - xmin; extents.height = ymax - ymin; - extents.x_advance = (double) advance / emscale; - extents.y_advance = 0.0; + extents.x_advance = advance.width; + extents.y_advance = advance.height; -#if 0 - fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f\n\n", glyph, - extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance); +#ifdef DEBUG + fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f %f\n\n", glyph, + extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance, extents.y_advance); #endif FAIL: @@ -452,7 +472,7 @@ _cairo_quartz_path_apply_func (void *info, const CGPathElement *el) _cairo_fixed_from_double(el->points[1].y), _cairo_fixed_from_double(el->points[2].x), _cairo_fixed_from_double(el->points[2].y)); - assert(!status); + assert(!status); break; case kCGPathElementCloseSubpath: status = _cairo_path_fixed_close_path (path); @@ -465,7 +485,6 @@ static cairo_int_status_t _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, cairo_scaled_glyph_t *scaled_glyph) { - cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); CGAffineTransform textMatrix; CGPathRef glyphPath; @@ -483,7 +502,7 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, -font->base.scale.yy, 0, 0); - glyphPath = CTFontCreatePathForGlyph (font_face->ctFont, glyph, &textMatrix); + glyphPath = CTFontCreatePathForGlyph (font->ctFont, glyph, &textMatrix); if (!glyphPath) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -508,25 +527,25 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, cairo_scaled_glyph_t *scaled_glyph) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - - cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); - cairo_image_surface_t *surface = NULL; CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); - - int advance; - CGRect bbox; + cairo_text_extents_t metrics = scaled_glyph->fs_metrics; + CGRect bbox = CGRectMake (metrics.x_bearing, -(metrics.y_bearing + metrics.height), + metrics.width, metrics.height); double width, height; - double emscale = CGFontGetUnitsPerEm (font_face->cgFont); - CGContextRef cgContext = NULL; CGAffineTransform textMatrix; CGRect glyphRect, glyphRectInt; CGPoint glyphOrigin; - //fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface); - +#ifdef DEBUG + fprintf (stderr, "[0x%04x] bearing: %f %f width %f height %f advances %f %f\n", + glyph, metrics.x_bearing, metrics.y_bearing, metrics.width, metrics.height, + metrics.x_advance, metrics.y_advance); + fprintf (stderr, "[0x%04x] bounds: origin %f %f, size %f %f\n", glyph, bbox.origin.x, + bbox.origin.y, bbox.size.width, bbox.size.height); +#endif /* Create blank 2x2 image if we don't have this character. * Maybe we should draw a better missing-glyph slug or something, * but this is ok for now. @@ -543,27 +562,20 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, return CAIRO_STATUS_SUCCESS; } - CTFontGetBoundingRectsForGlyphs (font_face->ctFont, FONT_ORIENTATION_HORIZONTAL, &glyph, &bbox, 1); - /* scale(1,-1) * font->base.scale * scale(1,-1) */ textMatrix = CGAffineTransformMake (font->base.scale.xx, -font->base.scale.yx, -font->base.scale.xy, font->base.scale.yy, - 0, -0); - glyphRect = CGRectMake (bbox.origin.x / emscale, - bbox.origin.y / emscale, - bbox.size.width / emscale, - bbox.size.height / emscale); - - glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix); + 0, 0); + glyphRect = CGRectApplyAffineTransform (bbox, textMatrix); /* Round the rectangle outwards, so that we don't have to deal * with non-integer-pixel origins or dimensions. */ glyphRectInt = CGRectIntegral (glyphRect); -#if 0 +#ifdef DEBUG fprintf (stderr, "glyphRect[o]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height); fprintf (stderr, "glyphRectInt: %f %f %f %f\n", @@ -572,63 +584,32 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, glyphOrigin = glyphRectInt.origin; - //textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm)); - width = glyphRectInt.size.width; height = glyphRectInt.size.height; - //fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height); - surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); if (surface->base.status) return surface->base.status; if (surface->width != 0 && surface->height != 0) { - cgContext = CGBitmapContextCreate (surface->data, - surface->width, - surface->height, - 8, - surface->stride, - NULL, - kCGImageAlphaOnly); + CGContextRef cgContext = CGBitmapContextCreate (surface->data, + surface->width, + surface->height, + 8, + surface->stride, + NULL, + kCGImageAlphaOnly); if (cgContext == NULL) { cairo_surface_destroy (&surface->base); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - CGContextSetFont (cgContext, font_face->cgFont); - CGContextSetFontSize (cgContext, 1.0); - CGContextSetTextMatrix (cgContext, textMatrix); - - switch (font->base.options.antialias) { - case CAIRO_ANTIALIAS_SUBPIXEL: - case CAIRO_ANTIALIAS_BEST: - CGContextSetShouldAntialias (cgContext, TRUE); - CGContextSetShouldSmoothFonts (cgContext, TRUE); - quartz_font_ensure_symbols (); - if (CGContextGetAllowsFontSmoothingPtr && - !CGContextGetAllowsFontSmoothingPtr (cgContext)) - CGContextSetAllowsFontSmoothing (cgContext, TRUE); - break; - case CAIRO_ANTIALIAS_NONE: - CGContextSetShouldAntialias (cgContext, FALSE); - break; - case CAIRO_ANTIALIAS_GRAY: - case CAIRO_ANTIALIAS_GOOD: - case CAIRO_ANTIALIAS_FAST: - CGContextSetShouldAntialias (cgContext, TRUE); - CGContextSetShouldSmoothFonts (cgContext, FALSE); - break; - case CAIRO_ANTIALIAS_DEFAULT: - default: - /* Don't do anything */ - break; - } - + _cairo_quartz_set_antialiasing (cgContext, font->base.options.antialias); CGContextSetAlpha (cgContext, 1.0); - CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1); - + CGContextTranslateCTM (cgContext, -glyphOrigin.x, -glyphOrigin.y); + CGContextConcatCTM (cgContext, textMatrix); + CTFontDrawGlyphs (font->ctFont, &glyph, &CGPointZero, 1, cgContext); CGContextRelease (cgContext); } @@ -667,12 +648,11 @@ _cairo_quartz_ucs4_to_index (void *abstract_font, uint32_t ucs4) { cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; - cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font); CGGlyph glyph[2]; UniChar utf16[2]; int len = _cairo_ucs4_to_utf16 (ucs4, utf16); - CTFontGetGlyphsForCharacters (ffont->ctFont, utf16, glyph, len); + CTFontGetGlyphsForCharacters (font->ctFont, utf16, glyph, len); return glyph[0]; } @@ -683,8 +663,8 @@ _cairo_quartz_load_truetype_table (void *abstract_font, unsigned char *buffer, unsigned long *length) { - cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face (abstract_font); - CFDataRef data = CGFontCopyTableForTag (font_face->cgFont, tag); + cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; + CFDataRef data = CTFontCopyTable (font->ctFont, tag, kCTFontTableOptionNoOptions); if (!data) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -728,6 +708,43 @@ _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font) return ffont->cgFont; } +CTFontRef +_cairo_quartz_scaled_font_get_ct_font (cairo_scaled_font_t *abstract_font) +{ + cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; + + return font->ctFont; +} + + void + _cairo_quartz_set_antialiasing (CGContextRef cgContext, cairo_antialias_t antialias) +{ + switch (antialias) { + case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_BEST: + CGContextSetShouldAntialias (cgContext, TRUE); + CGContextSetShouldSmoothFonts (cgContext, TRUE); + quartz_font_ensure_symbols (); + if (CGContextGetAllowsFontSmoothingPtr && + !CGContextGetAllowsFontSmoothingPtr (cgContext)) + CGContextSetAllowsFontSmoothing (cgContext, TRUE); + break; + case CAIRO_ANTIALIAS_NONE: + CGContextSetShouldAntialias (cgContext, FALSE); + break; + case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_GOOD: + case CAIRO_ANTIALIAS_FAST: + CGContextSetShouldAntialias (cgContext, TRUE); + CGContextSetShouldSmoothFonts (cgContext, FALSE); + break; + case CAIRO_ANTIALIAS_DEFAULT: + default: + /* Don't do anything */ + break; + } + +} /* * compat with old ATSUI backend */ diff --git a/src/cairo-quartz-private.h b/src/cairo-quartz-private.h index e3600d5fc..f142f8471 100644 --- a/src/cairo-quartz-private.h +++ b/src/cairo-quartz-private.h @@ -109,6 +109,8 @@ cairo_private CTFontRef _cairo_quartz_scaled_font_get_ct_font (cairo_scaled_font_t *sfont); cairo_private cairo_font_face_t* _cairo_quartz_font_face_create_for_ctfont (CTFontRef ctFont); +cairo_private void +_cairo_quartz_set_antialiasing (CGContextRef context, cairo_antialias_t antialias); #else diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index bc6968c06..ae6546029 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -65,6 +65,12 @@ #endif #define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0) +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 +#define FONT_ORIENTATION_HORIZONTAL kCTFontHorizontalOrientation +#else +#define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal +#endif + /** * SECTION:cairo-quartz @@ -1894,17 +1900,15 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, cairo_bool_t overlap) { CGAffineTransform textTransform, invTextTransform; - CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; - CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; + CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGGlyph)]; + CGPoint cg_positions_static[CAIRO_STACK_ARRAY_LENGTH (CGPoint)]; CGGlyph *cg_glyphs = &glyphs_static[0]; - CGSize *cg_advances = &cg_advances_static[0]; - COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize)); + CGPoint *cg_positions = &cg_positions_static[0]; cairo_quartz_drawing_state_t state; cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED; - cairo_quartz_float_t xprev, yprev; - int i; - CGFontRef cgfref = NULL; + CGPoint origin; + CTFontRef ctFont = NULL; cairo_bool_t didForceFontSmoothing = FALSE; @@ -1923,45 +1927,17 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, } /* this doesn't addref */ - cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); - CGContextSetFont (state.cgMaskContext, cgfref); - CGContextSetFontSize (state.cgMaskContext, 1.0); - - switch (scaled_font->options.antialias) { - case CAIRO_ANTIALIAS_SUBPIXEL: - case CAIRO_ANTIALIAS_BEST: - CGContextSetShouldAntialias (state.cgMaskContext, TRUE); - CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE); - quartz_ensure_symbols(); - if (CGContextGetAllowsFontSmoothingPtr && - !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext)) - { - didForceFontSmoothing = TRUE; - CGContextSetAllowsFontSmoothing (state.cgMaskContext, TRUE); - } - break; - case CAIRO_ANTIALIAS_NONE: - CGContextSetShouldAntialias (state.cgMaskContext, FALSE); - break; - case CAIRO_ANTIALIAS_GRAY: - case CAIRO_ANTIALIAS_GOOD: - case CAIRO_ANTIALIAS_FAST: - CGContextSetShouldAntialias (state.cgMaskContext, TRUE); - CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE); - break; - case CAIRO_ANTIALIAS_DEFAULT: - /* Don't do anything */ - break; - } + ctFont = _cairo_quartz_scaled_font_get_ct_font (scaled_font); + _cairo_quartz_set_antialiasing (state.cgMaskContext, scaled_font->options.antialias); if (num_glyphs > ARRAY_LENGTH (glyphs_static)) { - cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize)); + cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGPoint)); if (unlikely (cg_glyphs == NULL)) { rv = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto BAIL; } - cg_advances = (CGSize*) (cg_glyphs + num_glyphs); + cg_positions = (CGPoint*) (cg_glyphs + num_glyphs); } /* scale(1,-1) * scaled_font->scale */ @@ -1978,36 +1954,23 @@ _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, -scaled_font->scale_inverse.yy, 0.0, 0.0); - CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0); - CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity); - /* Convert our glyph positions to glyph advances. We need n-1 advances, - * since the advance at index 0 is applied after glyph 0. */ - xprev = glyphs[0].x; - yprev = glyphs[0].y; - - cg_glyphs[0] = glyphs[0].index; - - for (i = 1; i < num_glyphs; i++) { - cairo_quartz_float_t xf = glyphs[i].x; - cairo_quartz_float_t yf = glyphs[i].y; + origin = CGPointMake (glyphs[0].x, glyphs[0].y); + for (int i = 0; i < num_glyphs; ++i) + { cg_glyphs[i] = glyphs[i].index; - cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform); - xprev = xf; - yprev = yf; + cg_positions[i] = CGPointMake (glyphs[i].x - origin.x, glyphs[i].y - origin.y); + cg_positions[i] = CGPointApplyAffineTransform (cg_positions[i], invTextTransform); } /* Translate to the first glyph's position before drawing */ - CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y); + CGContextTranslateCTM (state.cgMaskContext, origin.x, origin.y); CGContextConcatCTM (state.cgMaskContext, textTransform); - CGContextShowGlyphsWithAdvances (state.cgMaskContext, - cg_glyphs, - cg_advances, - num_glyphs); + CTFontDrawGlyphs (ctFont, cg_glyphs, cg_positions, num_glyphs, state.cgMaskContext); CGContextConcatCTM (state.cgMaskContext, invTextTransform); - CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y); + CGContextTranslateCTM (state.cgMaskContext, -origin.x, -origin.y); if (state.action != DO_DIRECT) _cairo_quartz_draw_source (&state, extents->op); diff --git a/test/reference/overlapping-glyphs.quartz.argb32.ref.png b/test/reference/overlapping-glyphs.quartz.argb32.ref.png index 2bbbb3944be27ef65d44bb6e13f83a05af58813a..44200d59ee7b64efbc86a674934ea6b3875fae19 100644 GIT binary patch delta 2660 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 delta 1632 zcmV-m2A}!f41*1jHh(xtL_t(&f$f>gZxUM^$IlD{oerhcVNlwV@~B231~qL`p{A|1 z*7y6hu#G0VFm7EM6a5>yG$vj6SeV#`*rcfS(bz^%Y@`HgAi)t426?qm7zUWRi@6dP z_ZCSyw)ck5>f}7g@B7V}bAIOs#Bm&0e?Snl)*<`@@LC7(Uw@)zRu%lNS-D&;m&*YF zXqsMLUjB6`5{X125x)KT^JjkA{}QC)45!oi=FJ<9L z10s>g?RIkz6L-mAMo$qy|dfx=g*%fNz&nP^!N8C5{aIk zo@_QdJw4sq+kY#NFYEJwUukC*i^bQkU!OdAQmIsqj*gx_eR}KGt^5!KLGgHeWMl-x zFc^l7Mq^)JpUq}_^yrbnV35gVe!t&jGKs}vtJSL0>2MrhTwFv^6vy$sd-ocR#y_HN zm09V`ipS$LO}DqV$K&yQKCjp7%R>+ZSuBlS!}FtJP}n+_`i6_HBhik6DN)zKdx4*%S#p(7GM}29Df`H0Pq)OLqo&0Yu7Xyjb5+kFOyg- zCY4Hs6eScZnS~%oZ*T9jXV0S1DBqZ$pMUl06#yWePO~fv0HA5QY~sg{A7ioDkt0XS zcNtC75CmZurc^4&$HxHx(P%Uf2yEH1MOazFP$AFSy?Zx;AP*ltgkd-s4BotXbMxlS z9)FJ~l}c^cuwi0ig5$W28#l(|aR7kB;V2Xe4<0;_%jF)Ar=z3e;>C+@w|it{BpeQ> zQmJ4t$S_QIcXxYx`yWxa%2dd+){6C+XVu<@RZugl3TkFmLCvfxsF_s-l}j`LfW^f{ zk|Y_1X>4p&g^hZRsBG5DmoFU- zheo6E`Ftpf=5o2t&Q7=6ZM9mjT)EQF&`>B87>3~|(==Twl?0A?&8U!Pxm+%r&35(b z)#>T!WHLE4G!zPj)M~XzBqB+&R4QQ@*4WsHAc#aF$>;OMVzIndEEdBsOi>hqAb-VT zQ7)HfG8q8CrcIkFrcvuN&#JJq91cfUR~Lq1E|=@_<;w_yXfzrCKsK8l8yiz76dcEq zB-z>7dFap~yWRfw?c1iNCKN@(;jl)d;or{$L2w)w3WYEXYiep@7-o2QST2|U5rL~o z1+yrM>g((K@#9CSRBCBy;X8A=Tz|1xymaZ3OeRARWNK>a>({RUfS#TnkH<4OIGD*~ zo;-OHiA1hnzs_&E$B!Q?6bhfuH#9W#;>C-}$;k^BE(k7ewW(kh%d#*G^KWJt{`=~j zoSY;{GM!GhwY5p5Qi`H}{R@L6NvG3kFc?r2#V{Ts{JI3#|J$v?Owc0|VP+l@KGjrz58G;~C z6s2kU-o1MmhOsPbHk(ltEfflTol7`L!coC2lgae{{rkGQI=x;$J39-07H0y1002O# zRH{@e48!=z*=#nG$qWn(=znxNz9$-uqA1$c)kP3QJRau{_)sWB5QKo51f#-dXt7vG zlDvEOu3oRV+wI9@lA@^n`}gxjbc&*!PUnXYANa{ut2L9!EG;d~&CNw35rQBT3Pn1d zo}Ha-X=!=>{5i*QUa!~fb{{@`Sa4+m@LMICg@uJcAW%+9S(e3d99hG#P$(pm$y6#; zB9X9IET2AoN~hE1RjsY9I-M>Y4$sZaK@im3+#Cvp002g#vAp&EHLP`+I@Ib}RZx2y eRzc0ID) Date: Tue, 12 Apr 2022 16:44:05 -0700 Subject: [PATCH 5/7] [quartz] Implement color font support. --- src/cairo-quartz-font.c | 61 ++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c index 9e667e0d6..000a0e237 100644 --- a/src/cairo-quartz-font.c +++ b/src/cairo-quartz-font.c @@ -76,8 +76,10 @@ static const CGFloat font_scale = 1.0; #if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 #define FONT_ORIENTATION_HORIZONTAL kCTFontHorizontalOrientation +#define FONT_COLOR_GLYPHS kCTFontTraitColorGlyphs #else #define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal +#define FONT_COLOR_GLYPHS kCTFontColorGlyphsTrait #endif static void @@ -410,7 +412,6 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, extents.y_bearing = - ymax; extents.width = xmax - xmin; extents.height = ymax - ymin; - extents.x_advance = advance.width; extents.y_advance = advance.height; @@ -522,9 +523,19 @@ _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, return CAIRO_STATUS_SUCCESS; } +static cairo_bool_t +_cairo_quartz_font_has_color_glyphs (void *abstract_font) +{ + cairo_quartz_scaled_font_t *face = (cairo_quartz_scaled_font_t*)abstract_font; + CTFontSymbolicTraits traits = CTFontGetSymbolicTraits (face->ctFont); + return traits & FONT_COLOR_GLYPHS; +} + static cairo_int_status_t _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, - cairo_scaled_glyph_t *scaled_glyph) + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info, + const cairo_color_t *fg_color) { cairo_int_status_t status = CAIRO_STATUS_SUCCESS; cairo_image_surface_t *surface = NULL; @@ -538,6 +549,8 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, CGAffineTransform textMatrix; CGRect glyphRect, glyphRectInt; CGPoint glyphOrigin; + cairo_bool_t is_color = info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE; + cairo_format_t format = is_color ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_A8; #ifdef DEBUG fprintf (stderr, "[0x%04x] bearing: %f %f width %f height %f advances %f %f\n", @@ -562,6 +575,19 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, return CAIRO_STATUS_SUCCESS; } +/* Note: Certain opentype color fonts have the ability to provide a + * mixture of color and not-color glyphs. The Core Text API doesn't + * expose a way to query individual glyphs and at the level that that + * API is written it's not supposed to matter. The following code will + * cheerfully render any glyph requested onto the image surface. If + * the font is capable of color and + * COLOR_SCALED_GLYPH_INFO_COLOR_SURFACE is set then you get back a + * CAIRO_FORMAT_ARGB32 surface. If a foreground color is provided then + * the glyph will be drawn in that color, otherwise it will be black. + */ + if (unlikely (is_color && ! _cairo_quartz_font_has_color_glyphs (font))) + return CAIRO_INT_STATUS_UNSUPPORTED; + /* scale(1,-1) * font->base.scale * scale(1,-1) */ textMatrix = CGAffineTransformMake (font->base.scale.xx, -font->base.scale.yx, @@ -587,37 +613,47 @@ _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, width = glyphRectInt.size.width; height = glyphRectInt.size.height; - surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); + surface = (cairo_image_surface_t*) cairo_image_surface_create (format, width, height); if (surface->base.status) return surface->base.status; if (surface->width != 0 && surface->height != 0) { + CGColorSpaceRef colorspace = is_color ? CGColorSpaceCreateDeviceRGB () : NULL; + CGBitmapInfo bitinfo = is_color ? kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst : kCGImageAlphaOnly; + CGContextRef cgContext = CGBitmapContextCreate (surface->data, surface->width, surface->height, 8, surface->stride, - NULL, - kCGImageAlphaOnly); + colorspace, + bitinfo); if (cgContext == NULL) { cairo_surface_destroy (&surface->base); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } + if (fg_color) + CGContextSetRGBFillColor (cgContext, fg_color->red, fg_color->green, fg_color->blue, fg_color->alpha); _cairo_quartz_set_antialiasing (cgContext, font->base.options.antialias); CGContextSetAlpha (cgContext, 1.0); CGContextTranslateCTM (cgContext, -glyphOrigin.x, -glyphOrigin.y); CGContextConcatCTM (cgContext, textMatrix); CTFontDrawGlyphs (font->ctFont, &glyph, &CGPointZero, 1, cgContext); CGContextRelease (cgContext); + CGColorSpaceRelease (colorspace); } cairo_surface_set_device_offset (&surface->base, - glyphOrigin.x, height + glyphOrigin.y); + cairo_surface_mark_dirty (&surface->base); - _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface); + if (is_color) + _cairo_scaled_glyph_set_color_surface (scaled_glyph, &font->base, surface, fg_color != NULL); + else + _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface); return status; } @@ -637,8 +673,10 @@ _cairo_quartz_scaled_glyph_init (void *abstract_font, if (!status && (info & CAIRO_SCALED_GLYPH_INFO_PATH)) status = _cairo_quartz_init_glyph_path (font, scaled_glyph); - if (!status && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE)) - status = _cairo_quartz_init_glyph_surface (font, scaled_glyph); + if (!status && (info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | + CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE ))) + status = _cairo_quartz_init_glyph_surface (font, scaled_glyph, + info, foreground_color); return status; } @@ -694,8 +732,11 @@ static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = { _cairo_quartz_ucs4_to_index, _cairo_quartz_load_truetype_table, NULL, /*index_to_ucs4*/ -}; /* is_synthetic, index_to_glyph_name, load_type1_data, has_color_glyphs */ - + NULL, /* is_synthetic */ + NULL, /* index_to_glyph_name */ + NULL, /* load_type1_data */ + _cairo_quartz_font_has_color_glyphs +}; /* * private methods that the quartz surface uses */ From 2784159d0e57b409477cfcb25ddcce3227379a6f Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sat, 23 Apr 2022 14:36:15 -0700 Subject: [PATCH 6/7] [quartz] Adjust x_extents metric. CoreText uses different advances depending on the font size, with very small point sizes sometimes getting advances that are smaller than the glyph's width. This is manifested in the Apple Color Emoji font with the Emoji glyphs having a width of 1.25 and an advance width of 1.0. That results in overlapping emoji when they're in a string. The small spacing difference also affects 3 tests so updated reference images are included in this commit. # Please enter the commit message for your changes. Lines starting --- src/cairo-quartz-font.c | 7 ++++++- test/reference/inverse-text.quartz.ref.png | Bin 2565 -> 2602 bytes .../pdf-operators-text.quartz.ref.png | Bin 0 -> 8226 bytes .../reference/select-font-face.quartz.ref.png | Bin 2645 -> 2691 bytes 4 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 test/reference/pdf-operators-text.quartz.ref.png diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c index 000a0e237..641a2dfc7 100644 --- a/src/cairo-quartz-font.c +++ b/src/cairo-quartz-font.c @@ -412,7 +412,12 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, extents.y_bearing = - ymax; extents.width = xmax - xmin; extents.height = ymax - ymin; - extents.x_advance = advance.width; +/* At the necessary 1.0pt ctFont size some glyphs get a reduced + * advance that causes overlaps when scaled up. We can avoid that by + * using the width instead if it's wider. Since cairo doesn't support + * vertical font layout we don't do the same for y_advance. + */ + extents.x_advance = MAX(extents.width, advance.width); extents.y_advance = advance.height; #ifdef DEBUG diff --git a/test/reference/inverse-text.quartz.ref.png b/test/reference/inverse-text.quartz.ref.png index df0ea57eaa5ef7926ea25a5032897e63c08f5fd0..f57f45035d73c12a1f630a8eab3783a33e58db79 100644 GIT binary patch delta 2575 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 Z)9Ri-X*!#|3W0~)Nbef9>hkS~{{fnq6wCks delta 2537 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 Lmrct|ZbtndCQu1z From cfb3835f57abd05b5d862904dd1d4300ae990a8a Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 26 Apr 2022 12:44:54 -0700 Subject: [PATCH 7/7] [quartz] Add wiggle test for quartz emoji. --- test/Makefile.am | 2 +- test/Makefile.sources | 2 + test/meson.build | 1 + test/quartz-color-font.c | 54 ++++++++++++++++++ .../quartz-color-font.quartz.rgb24.ref.png | Bin 0 -> 2050 bytes test/reference/quartz-color-font.ref.png | Bin 0 -> 2442 bytes 6 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 test/quartz-color-font.c create mode 100644 test/reference/quartz-color-font.quartz.rgb24.ref.png create mode 100644 test/reference/quartz-color-font.ref.png diff --git a/test/Makefile.am b/test/Makefile.am index 8e271fe9b..a74d71a7c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -27,7 +27,7 @@ endif # Need to add quartz-surface-source if CAIRO_HAS_QUARTZ_SURFACE -test_sources += $(quartz_surface_test_sources) +test_sources += "$(quartz_surface_test_sources) $(quartz_color_font_test_sources)" endif if CAIRO_HAS_PDF_SURFACE diff --git a/test/Makefile.sources b/test/Makefile.sources index c180289ab..330aa9da1 100644 --- a/test/Makefile.sources +++ b/test/Makefile.sources @@ -434,6 +434,8 @@ egl_surface_test_sources = \ quartz_surface_test_sources = quartz-surface-source.c +quartz_color_font_test_sources = quartz-color-font.c + pdf_surface_test_sources = \ pdf-features.c \ pdf-mime-data.c \ diff --git a/test/meson.build b/test/meson.build index c0be0e086..21a0588e7 100644 --- a/test/meson.build +++ b/test/meson.build @@ -438,6 +438,7 @@ test_egl_sources = [ test_quartz_sources = [ 'quartz-surface-source.c', + 'quartz-color-font.c', ] test_pdf_sources = [ diff --git a/test/quartz-color-font.c b/test/quartz-color-font.c new file mode 100644 index 000000000..7dae214f6 --- /dev/null +++ b/test/quartz-color-font.c @@ -0,0 +1,54 @@ +/* + * Copyright © 2022 John Ralls + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include "cairo-test.h" +#include + +#define WIDTH 100 +#define HEIGHT 50 + +#define FONT "Apple Color Emoji" + +static const char smiley_face_utf8[] = { 0xf0, 0x9f, 0x99, 0x82, 0x00 }; /* U+1F642 */ + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + cairo_select_font_face(cr, FONT, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size (cr, HEIGHT/2); + cairo_move_to (cr, width/5, 2*height/3); + + cairo_show_text(cr, smiley_face_utf8); + cairo_show_text(cr, smiley_face_utf8); + + return CAIRO_TEST_SUCCESS; +} + +CAIRO_TEST (quartz_color_font, + "Test color font", + "quartz, font", /* keywords */ + NULL, /* requirements */ + WIDTH, HEIGHT, + NULL, draw) diff --git a/test/reference/quartz-color-font.quartz.rgb24.ref.png b/test/reference/quartz-color-font.quartz.rgb24.ref.png new file mode 100644 index 0000000000000000000000000000000000000000..00a0c4499e315c28978b8a1be3e0a32b2c4d59cd GIT binary patch literal 2050 zcmV+d2>thoP)q}MDiUm@OTD1~VUkXAc2r6hngb*GvBqcbp<9Nmsd*(jQ z>-X!zK|+NRzsA|jN29x%`J8*c_uez-{LTgX^y$;5PoF-0`t$%R;H|wi_P%L62ddxsdKka!F!p~5^?MQD3XB6W;DHwWIi#=R%5!U{=b!kq{PIM8ZrS;8 z{XX?nz8Q8itWu=zzqS0MNA(4bIUhMzDe}X6&oaxI(}#6*WahvhmPRL<7j*hh`=pK8`6cOkS?SHxe8ftxE|3B#cop14)Iejl(rA8lbm7$d$+;{3abLj1EFwTsz#+1X;_|yoXJ2}+RI=4kfHO!2x@)P!Ekw}IMW zSO-T-v->Tg5^M(Bh0EYmxK8I%v%SzB8$k1Xt9@|=NMSobx3jd;I@jDci1x*+YYQ#d z4txsNh3&$2pp(Pn?v{Ne>b-m$>}a_|gz3?!oETw{%b*mLg4S>b8)vw%`a(Nwls$VV-yS=iIxIBJ*_Fhm-kSiQ!4(Jsj`EHyp6&W> zK%;~t0mXm@MTipAN(i|0PUq^16V~v6Gw_)=-ems$R_VRvLV7RX20NXUI+lsy`aP4G zswQy+r5F@J$0q1E-b`P6rHRbdA{ECPiV(!DyM?psFaBd?ahVTHHtGq%6|n1UO`e6k zl|Ox^cH+%!W0Kud`8L?;0PlmGc*lNgt~ymWaiXirC|Luzn$&0KMo*oNPQI}E;`8l# zRh7%iI_`G4U_E(o@Yv^uhU-Alh3&94h2gwoo9*&n-U^;&59Hfm7pe|v5hjL`&yH9m zT16^SLhMO=y+o5YS8tvgtko+Hkw;vrmT#IGJa+f!++1zE;a}fdn(@&yAu`*F0IMB$L*s~!lc0< zu!sl(!v{DAXW^VU&z`*x&T(Eyk=bt3uC>Ci{_X#CDrBEjYwD?dOYGW)g$p>}l%+0a z2BpRvSeOu)0)ttChY#X|c+bv>v0{plt=X*U7nb>FFZn;d7S{6{Bi}r`78U>-aK0(8 zElM@P2#FXDE(FiPi}T_gTNi8*Od7H^*ID)LRycXy|M)q7a-LIjBjuZ9_qXpAp;5-M zX?pNBeb)iCuTDu!N)r)8z(Ks1<*uB$%+Jh+m;NK`IX3@q-c$K?>D9XqiV$n;AHdN` zni{5&DwSh0UfP)~T$Q&LWoZr8Z?h*om46C53TrckA)^0nLpJfEORwd9`t<43r%#_g gefsq2)8`}n7tNuqnFtOdl>h($07*qoM6N<$g139@-~a#s literal 0 HcmV?d00001 diff --git a/test/reference/quartz-color-font.ref.png b/test/reference/quartz-color-font.ref.png new file mode 100644 index 0000000000000000000000000000000000000000..e52674ba0ac40fe8487626c068d07cdc45bf7b1d GIT binary patch literal 2442 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 IM6N<$g2srIS^xk5 literal 0 HcmV?d00001