summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Johnson <ajohnson@redneon.com>2022-04-27 00:04:43 +0000
committerAdrian Johnson <ajohnson@redneon.com>2022-04-27 00:04:43 +0000
commit455a4cca5472a309d036b6393f1db0dc4403d303 (patch)
treeeadac9e15f0bcb4688020670a74e89655df96ef3
parentf07d539c07488edccd0bea241073572bc20c8ed1 (diff)
parentcfb3835f57abd05b5d862904dd1d4300ae990a8a (diff)
Merge branch 'quartz-core-text' into 'master'
quartz: support rendering colored bitmap fonts See merge request cairo/cairo!289
-rw-r--r--configure.ac7
-rw-r--r--meson.build20
-rw-r--r--src/cairo-quartz-font.c520
-rw-r--r--src/cairo-quartz-private.h6
-rw-r--r--src/cairo-quartz-surface.c259
-rw-r--r--test/Makefile.am2
-rw-r--r--test/Makefile.sources2
-rw-r--r--test/meson.build1
-rw-r--r--test/quartz-color-font.c54
-rw-r--r--test/reference/inverse-text.quartz.ref.pngbin2565 -> 2602 bytes
-rw-r--r--test/reference/overlapping-glyphs.quartz.argb32.ref.pngbin2766 -> 2687 bytes
-rw-r--r--test/reference/overlapping-glyphs.quartz.rgb24.ref.pngbin1667 -> 1630 bytes
-rw-r--r--test/reference/pdf-operators-text.quartz.ref.pngbin0 -> 8226 bytes
-rw-r--r--test/reference/quartz-color-font.quartz.rgb24.ref.pngbin0 -> 2050 bytes
-rw-r--r--test/reference/quartz-color-font.ref.pngbin0 -> 2442 bytes
-rw-r--r--test/reference/select-font-face.quartz.ref.pngbin2645 -> 2691 bytes
16 files changed, 359 insertions, 512 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 <ApplicationServices/ApplicationServices.h>',
+ 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..641a2dfc7 100644
--- a/src/cairo-quartz-font.c
+++ b/src/cairo-quartz-font.c
@@ -44,15 +44,13 @@
#include "cairo-error-private.h"
+//#define DEBUG /* Uncomment this to get debug messages on the console. */
/**
* 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.
**/
/**
@@ -64,103 +62,37 @@
* 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;
+/* 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
+#define FONT_COLOR_GLYPHS kCTFontTraitColorGlyphs
+#else
+#define FONT_ORIENTATION_HORIZONTAL kCTFontOrientationHorizontal
+#define FONT_COLOR_GLYPHS kCTFontColorGlyphsTrait
+#endif
+
static void
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;
}
@@ -169,6 +101,7 @@ 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 {
@@ -187,14 +120,10 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
{
const char *family;
char *full_name;
- CFStringRef cgFontName = NULL;
+ CFStringRef FontName = NULL;
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 */
@@ -221,23 +150,19 @@ _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");
}
- if (CGFontCreateWithFontNamePtr) {
- cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII);
- cgFont = CGFontCreateWithFontNamePtr (cgFontName);
- CFRelease (cgFontName);
- } else {
- cgFont = CGFontCreateWithNamePtr (full_name);
- }
+ FontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII);
+ cgFont = CGFontCreateWithFontName (FontName);
+ CFRelease (FontName);
if (cgFont)
break;
@@ -249,7 +174,7 @@ _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
}
*font_face = cairo_quartz_font_face_create_for_cgfont (cgFont);
- CGFontRelease (cgFont);
+ CFRelease (cgFont);
return CAIRO_STATUS_SUCCESS;
}
@@ -265,6 +190,51 @@ _cairo_quartz_font_face_destroy (void *abstract_face)
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,
@@ -276,13 +246,9 @@ _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;
- 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,48 +261,29 @@ _cairo_quartz_font_face_scaled_font_create (void *abstract_face,
if (status)
goto FINISH;
- ems = CGFontGetUnitsPerEmPtr (font_face->cgFont);
-
+ ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, font_scale, NULL, NULL);
/* 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);
-
- 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 = CTFontGetAscent (ctFont);
+ fs_metrics.descent = CTFontGetDescent (ctFont);
+ fs_metrics.height = fs_metrics.ascent + fs_metrics.descent +
+ CTFontGetLeading (ctFont);
+
+ 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);
- 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;
- }
+ 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);
}
-
- status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics);
+#endif
FINISH:
if (status != CAIRO_STATUS_SUCCESS) {
@@ -355,6 +302,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.
@@ -371,21 +335,13 @@ 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_quartz_font_face_create ();
- 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);
- 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);
- _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
-
return &font_face->base;
}
@@ -405,6 +361,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
@@ -419,21 +377,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 = CGFontGetUnitsPerEmPtr (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))
- 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 &&
@@ -444,31 +398,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);
@@ -480,13 +412,17 @@ _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font,
extents.y_bearing = - ymax;
extents.width = xmax - xmin;
extents.height = ymax - ymin;
+/* 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;
- extents.x_advance = (double) advance / emscale;
- extents.y_advance = 0.0;
-
-#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:
@@ -542,7 +478,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);
@@ -555,7 +491,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;
@@ -573,13 +508,7 @@ _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);
- }
+ glyphPath = CTFontCreatePathForGlyph (font->ctFont, glyph, &textMatrix);
if (!glyphPath)
return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -599,30 +528,42 @@ _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_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 = CGFontGetUnitsPerEmPtr (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);
-
+ 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",
+ 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.
@@ -639,31 +580,33 @@ _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))
- {
+/* 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,
-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",
@@ -672,70 +615,50 @@ _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);
+ 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) {
- cgContext = CGBitmapContextCreate (surface->data,
- surface->width,
- surface->height,
- 8,
- surface->stride,
- NULL,
- kCGImageAlphaOnly);
+ 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,
+ colorspace,
+ bitinfo);
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);
- if (CGContextSetAllowsFontSmoothingPtr &&
- !CGContextGetAllowsFontSmoothingPtr (cgContext))
- CGContextSetAllowsFontSmoothingPtr (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;
- }
-
+ 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);
- 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);
+ 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;
}
@@ -755,8 +678,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;
}
@@ -766,13 +691,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);
- CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, utf16, glyph, len);
-
+ CTFontGetGlyphsForCharacters (font->ctFont, utf16, glyph, len);
return glyph[0];
}
@@ -783,11 +706,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 = NULL;
-
- if (likely (CGFontCopyTableForTagPtr))
- data = CGFontCopyTableForTagPtr (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;
@@ -816,9 +736,12 @@ 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*/
+ 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
*/
@@ -831,6 +754,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 609a9052c..f142f8471 100644
--- a/src/cairo-quartz-private.h
+++ b/src/cairo-quartz-private.h
@@ -105,6 +105,12 @@ 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);
+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 fa6d9b1c9..97ddf2c28 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
@@ -85,46 +91,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 +134,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 +238,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 +346,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 +382,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 +406,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 +435,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;
@@ -1472,7 +1363,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))
@@ -1558,13 +1449,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);
@@ -2016,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;
@@ -2045,44 +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);
- if (CGContextSetAllowsFontSmoothingPtr &&
- !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext))
- {
- didForceFontSmoothing = TRUE;
- CGContextSetAllowsFontSmoothingPtr (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 */
@@ -2099,43 +1954,30 @@ _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);
BAIL:
if (didForceFontSmoothing)
- CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
+ CGContextSetAllowsFontSmoothing (state.cgMaskContext, FALSE);
_cairo_quartz_teardown_state (&state, extents);
@@ -2319,8 +2161,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 +2182,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 +2310,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 +2342,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;
@@ -2633,7 +2457,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);
@@ -2648,7 +2472,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) {
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 <jralls@ceridwen.us>
+ *
+ * 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 <cairo-quartz.h>
+
+#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/inverse-text.quartz.ref.png b/test/reference/inverse-text.quartz.ref.png
index df0ea57ea..f57f45035 100644
--- a/test/reference/inverse-text.quartz.ref.png
+++ b/test/reference/inverse-text.quartz.ref.png
Binary files differ
diff --git a/test/reference/overlapping-glyphs.quartz.argb32.ref.png b/test/reference/overlapping-glyphs.quartz.argb32.ref.png
index 2bbbb3944..44200d59e 100644
--- a/test/reference/overlapping-glyphs.quartz.argb32.ref.png
+++ b/test/reference/overlapping-glyphs.quartz.argb32.ref.png
Binary files differ
diff --git a/test/reference/overlapping-glyphs.quartz.rgb24.ref.png b/test/reference/overlapping-glyphs.quartz.rgb24.ref.png
index a3961f8bf..d236e19a5 100644
--- a/test/reference/overlapping-glyphs.quartz.rgb24.ref.png
+++ b/test/reference/overlapping-glyphs.quartz.rgb24.ref.png
Binary files differ
diff --git a/test/reference/pdf-operators-text.quartz.ref.png b/test/reference/pdf-operators-text.quartz.ref.png
new file mode 100644
index 000000000..ecfa7cc5d
--- /dev/null
+++ b/test/reference/pdf-operators-text.quartz.ref.png
Binary files differ
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 000000000..00a0c4499
--- /dev/null
+++ b/test/reference/quartz-color-font.quartz.rgb24.ref.png
Binary files differ
diff --git a/test/reference/quartz-color-font.ref.png b/test/reference/quartz-color-font.ref.png
new file mode 100644
index 000000000..e52674ba0
--- /dev/null
+++ b/test/reference/quartz-color-font.ref.png
Binary files differ
diff --git a/test/reference/select-font-face.quartz.ref.png b/test/reference/select-font-face.quartz.ref.png
index 69fd2e239..1100fe28c 100644
--- a/test/reference/select-font-face.quartz.ref.png
+++ b/test/reference/select-font-face.quartz.ref.png
Binary files differ