summaryrefslogtreecommitdiff
path: root/src/cairo-quartz-font.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-quartz-font.c')
-rw-r--r--src/cairo-quartz-font.c859
1 files changed, 859 insertions, 0 deletions
diff --git a/src/cairo-quartz-font.c b/src/cairo-quartz-font.c
new file mode 100644
index 000000000..02f34267b
--- /dev/null
+++ b/src/cairo-quartz-font.c
@@ -0,0 +1,859 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright � 2008 Mozilla Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ *
+ * Contributor(s):
+ * Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include <dlfcn.h>
+
+#include "cairo-image-surface-private.h"
+#include "cairo-quartz.h"
+#include "cairo-quartz-private.h"
+
+#include "cairo-error-private.h"
+
+/**
+ * SECTION:cairo-quartz-fonts
+ * @Title: Quartz (CGFont) Fonts
+ * @Short_Description: Font support via CGFont on OS X
+ * @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.
+ **/
+
+/**
+ * CAIRO_HAS_QUARTZ_FONT:
+ *
+ * Defined if the Quartz font backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ *
+ * 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;
+static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = 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;
+
+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");
+
+ 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");
+
+ FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont");
+
+ if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
+ CGFontGetGlyphBBoxesPtr &&
+ CGFontGetGlyphsForUnicharsPtr &&
+ CGFontGetUnitsPerEmPtr &&
+ CGFontGetGlyphAdvancesPtr &&
+ (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr)))
+ _cairo_quartz_font_symbols_present = TRUE;
+
+ _cairo_quartz_font_symbol_lookup_done = TRUE;
+}
+
+typedef struct _cairo_quartz_font_face cairo_quartz_font_face_t;
+typedef struct _cairo_quartz_scaled_font cairo_quartz_scaled_font_t;
+
+struct _cairo_quartz_scaled_font {
+ cairo_scaled_font_t base;
+};
+
+struct _cairo_quartz_font_face {
+ cairo_font_face_t base;
+
+ CGFontRef cgFont;
+};
+
+/*
+ * font face backend
+ */
+
+static cairo_status_t
+_cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
+ cairo_font_face_t **font_face)
+{
+ const char *family;
+ char *full_name;
+ CFStringRef cgFontName = 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 = malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc.
+ /* handle CSS-ish faces */
+ if (!strcmp(family, "serif") || !strcmp(family, "Times Roman"))
+ family = "Times";
+ else if (!strcmp(family, "sans-serif") || !strcmp(family, "sans"))
+ family = "Helvetica";
+ else if (!strcmp(family, "cursive"))
+ family = "Apple Chancery";
+ else if (!strcmp(family, "fantasy"))
+ family = "Papyrus";
+ else if (!strcmp(family, "monospace") || !strcmp(family, "mono"))
+ family = "Courier";
+
+ /* Try to build up the full name, e.g. "Helvetica Bold Oblique" first,
+ * then drop the bold, then drop the slant, then drop both.. finally
+ * just use "Helvetica". And if Helvetica doesn't exist, give up.
+ */
+ for (loop = 0; loop < 5; loop++) {
+ if (loop == 4)
+ family = "Helvetica";
+
+ strcpy (full_name, family);
+
+ if (loop < 3 && (loop & 1) == 0) {
+ if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD)
+ strcat (full_name, " Bold");
+ }
+
+ if (loop < 3 && (loop & 2) == 0) {
+ if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC)
+ strcat (full_name, " Italic");
+ else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE)
+ strcat (full_name, " Oblique");
+ }
+
+ if (CGFontCreateWithFontNamePtr) {
+ cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII);
+ cgFont = CGFontCreateWithFontNamePtr (cgFontName);
+ CFRelease (cgFontName);
+ } else {
+ cgFont = CGFontCreateWithNamePtr (full_name);
+ }
+
+ if (cgFont)
+ break;
+ }
+
+ if (!cgFont) {
+ /* Give up */
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont);
+ CGFontRelease (cgFont);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_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);
+ return TRUE;
+}
+
+static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend;
+
+static cairo_status_t
+_cairo_quartz_font_face_scaled_font_create (void *abstract_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options,
+ cairo_scaled_font_t **font_out)
+{
+ cairo_quartz_font_face_t *font_face = abstract_face;
+ cairo_quartz_scaled_font_t *font = NULL;
+ cairo_status_t status;
+ cairo_font_extents_t fs_metrics;
+ double ems;
+ CGRect bbox;
+
+ quartz_font_ensure_symbols();
+ if (!_cairo_quartz_font_symbols_present)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font = malloc(sizeof(cairo_quartz_scaled_font_t));
+ if (font == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memset (font, 0, sizeof(cairo_quartz_scaled_font_t));
+
+ status = _cairo_scaled_font_init (&font->base,
+ &font_face->base, font_matrix, ctm, options,
+ &_cairo_quartz_scaled_font_backend);
+ if (status)
+ goto FINISH;
+
+ ems = CGFontGetUnitsPerEmPtr (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);
+
+ 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;
+ }
+ }
+
+ status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics);
+
+FINISH:
+ if (status != CAIRO_STATUS_SUCCESS) {
+ free (font);
+ } else {
+ *font_out = (cairo_scaled_font_t*) font;
+ }
+
+ return status;
+}
+
+const cairo_font_face_backend_t _cairo_quartz_font_face_backend = {
+ CAIRO_FONT_TYPE_QUARTZ,
+ _cairo_quartz_font_face_create_for_toy,
+ _cairo_quartz_font_face_destroy,
+ _cairo_quartz_font_face_scaled_font_create
+};
+
+/**
+ * cairo_quartz_font_face_create_for_cgfont:
+ * @font: a #CGFontRef obtained through a method external to cairo.
+ *
+ * Creates a new font for the Quartz font backend based on a
+ * #CGFontRef. This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create().
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ * cairo_font_face_destroy() when you are done using it.
+ *
+ * Since: 1.6
+ **/
+cairo_font_face_t *
+cairo_quartz_font_face_create_for_cgfont (CGFontRef font)
+{
+ cairo_quartz_font_face_t *font_face;
+
+ quartz_font_ensure_symbols();
+
+ font_face = 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;
+ }
+
+ font_face->cgFont = CGFontRetain (font);
+
+ _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
+
+ return &font_face->base;
+}
+
+/*
+ * scaled font backend
+ */
+
+static cairo_quartz_font_face_t *
+_cairo_quartz_scaled_to_face (void *abstract_font)
+{
+ cairo_quartz_scaled_font_t *sfont = (cairo_quartz_scaled_font_t*) abstract_font;
+ cairo_font_face_t *font_face = sfont->base.font_face;
+ assert (font_face->backend->type == CAIRO_FONT_TYPE_QUARTZ);
+ return (cairo_quartz_font_face_t*) font_face;
+}
+
+static void
+_cairo_quartz_scaled_font_fini(void *abstract_font)
+{
+}
+
+#define INVALID_GLYPH 0x00
+
+static inline CGGlyph
+_cairo_quartz_scaled_glyph_index (cairo_scaled_glyph_t *scaled_glyph) {
+ unsigned long index = _cairo_scaled_glyph_index (scaled_glyph);
+ if (index > 0xffff)
+ return INVALID_GLYPH;
+ return (CGGlyph) index;
+}
+
+static cairo_int_status_t
+_cairo_quartz_init_glyph_metrics (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_text_extents_t extents = {0, 0, 0, 0, 0, 0};
+ CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
+ int advance;
+ CGRect bbox;
+ double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
+ double xmin, ymin, xmax, ymax;
+
+ if (glyph == INVALID_GLYPH)
+ goto FAIL;
+
+ if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
+ !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
+ goto FAIL;
+
+ /* 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 &&
+ bbox.origin.y == -32767 &&
+ bbox.size.width == 65534 &&
+ bbox.size.height == 65534)) {
+ bbox.origin.x = bbox.origin.y = 0;
+ 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);
+#endif
+
+ xmin = CGRectGetMinX(bbox);
+ ymin = CGRectGetMinY(bbox);
+ xmax = CGRectGetMaxX(bbox);
+ ymax = CGRectGetMaxY(bbox);
+
+ extents.x_bearing = xmin;
+ extents.y_bearing = - ymax;
+ extents.width = xmax - xmin;
+ extents.height = ymax - ymin;
+
+ 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);
+#endif
+
+ FAIL:
+ _cairo_scaled_glyph_set_metrics (scaled_glyph,
+ &font->base,
+ &extents);
+
+ return status;
+}
+
+static void
+_cairo_quartz_path_apply_func (void *info, const CGPathElement *el)
+{
+ cairo_path_fixed_t *path = (cairo_path_fixed_t *) info;
+ cairo_status_t status;
+
+ switch (el->type) {
+ case kCGPathElementMoveToPoint:
+ status = _cairo_path_fixed_move_to (path,
+ _cairo_fixed_from_double(el->points[0].x),
+ _cairo_fixed_from_double(el->points[0].y));
+ assert(!status);
+ break;
+ case kCGPathElementAddLineToPoint:
+ status = _cairo_path_fixed_line_to (path,
+ _cairo_fixed_from_double(el->points[0].x),
+ _cairo_fixed_from_double(el->points[0].y));
+ assert(!status);
+ break;
+ case kCGPathElementAddQuadCurveToPoint: {
+ cairo_fixed_t fx, fy;
+ double x, y;
+ if (!_cairo_path_fixed_get_current_point (path, &fx, &fy))
+ fx = fy = 0;
+ x = _cairo_fixed_to_double (fx);
+ y = _cairo_fixed_to_double (fy);
+
+ status = _cairo_path_fixed_curve_to (path,
+ _cairo_fixed_from_double((x + el->points[0].x * 2.0) / 3.0),
+ _cairo_fixed_from_double((y + el->points[0].y * 2.0) / 3.0),
+ _cairo_fixed_from_double((el->points[0].x * 2.0 + el->points[1].x) / 3.0),
+ _cairo_fixed_from_double((el->points[0].y * 2.0 + el->points[1].y) / 3.0),
+ _cairo_fixed_from_double(el->points[1].x),
+ _cairo_fixed_from_double(el->points[1].y));
+ }
+ assert(!status);
+ break;
+ case kCGPathElementAddCurveToPoint:
+ status = _cairo_path_fixed_curve_to (path,
+ _cairo_fixed_from_double(el->points[0].x),
+ _cairo_fixed_from_double(el->points[0].y),
+ _cairo_fixed_from_double(el->points[1].x),
+ _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);
+ break;
+ case kCGPathElementCloseSubpath:
+ status = _cairo_path_fixed_close_path (path);
+ assert(!status);
+ break;
+ }
+}
+
+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;
+ CTFontRef ctFont;
+ cairo_path_fixed_t *path;
+
+ if (glyph == INVALID_GLYPH) {
+ _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, _cairo_path_fixed_create());
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* scale(1,-1) * font->base.scale */
+ textMatrix = CGAffineTransformMake (font->base.scale.xx,
+ font->base.scale.yx,
+ -font->base.scale.xy,
+ -font->base.scale.yy,
+ 0, 0);
+
+ ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, 0.0, NULL, NULL);
+ glyphPath = CTFontCreatePathForGlyph (ctFont, glyph, &textMatrix);
+ CFRelease (ctFont);
+ if (!glyphPath)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ path = _cairo_path_fixed_create ();
+ if (!path) {
+ CGPathRelease (glyphPath);
+ return _cairo_error(CAIRO_STATUS_NO_MEMORY);
+ }
+
+ CGPathApply (glyphPath, path, _cairo_quartz_path_apply_func);
+
+ CGPathRelease (glyphPath);
+
+ _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, path);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_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;
+ 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);
+
+ /* 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.
+ */
+ if (glyph == INVALID_GLYPH) {
+ surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, 2, 2);
+ status = cairo_surface_status ((cairo_surface_t *) surface);
+ if (status)
+ return status;
+
+ _cairo_scaled_glyph_set_surface (scaled_glyph,
+ &font->base,
+ surface);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
+ !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
+ {
+ 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);
+
+ /* Round the rectangle outwards, so that we don't have to deal
+ * with non-integer-pixel origins or dimensions.
+ */
+ glyphRectInt = CGRectIntegral (glyphRect);
+
+#if 0
+ 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",
+ glyphRectInt.origin.x, glyphRectInt.origin.y, glyphRectInt.size.width, glyphRectInt.size.height);
+#endif
+
+ 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);
+
+ 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;
+ }
+
+ CGContextSetAlpha (cgContext, 1.0);
+ CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1);
+
+ CGContextRelease (cgContext);
+ }
+
+ cairo_surface_set_device_offset (&surface->base,
+ - glyphOrigin.x,
+ height + glyphOrigin.y);
+
+ _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_quartz_scaled_glyph_init (void *abstract_font,
+ cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_glyph_info_t info)
+{
+ cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t *) abstract_font;
+ cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (!status && (info & CAIRO_SCALED_GLYPH_INFO_METRICS))
+ status = _cairo_quartz_init_glyph_metrics (font, scaled_glyph);
+
+ 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);
+
+ return status;
+}
+
+static unsigned long
+_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);
+ UniChar u = (UniChar) ucs4;
+ CGGlyph glyph;
+
+ CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, &u, &glyph, 1);
+
+ return glyph;
+}
+
+static cairo_int_status_t
+_cairo_quartz_load_truetype_table (void *abstract_font,
+ unsigned long tag,
+ long offset,
+ 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);
+
+ if (!data)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (buffer == NULL) {
+ *length = CFDataGetLength (data);
+ CFRelease (data);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (CFDataGetLength (data) < offset + (long) *length) {
+ CFRelease (data);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ CFDataGetBytes (data, CFRangeMake (offset, *length), buffer);
+ CFRelease (data);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = {
+ CAIRO_FONT_TYPE_QUARTZ,
+ _cairo_quartz_scaled_font_fini,
+ _cairo_quartz_scaled_glyph_init,
+ NULL, /* text_to_glyphs */
+ _cairo_quartz_ucs4_to_index,
+ _cairo_quartz_load_truetype_table,
+ NULL, /* map_glyphs_to_unicode */
+};
+
+/*
+ * private methods that the quartz surface uses
+ */
+
+CGFontRef
+_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font)
+{
+ cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
+
+ return ffont->cgFont;
+}
+
+/*
+ * compat with old ATSUI backend
+ */
+
+/**
+ * cairo_quartz_font_face_create_for_atsu_font_id:
+ * @font_id: an ATSUFontID for the font.
+ *
+ * Creates a new font for the Quartz font backend based on an
+ * #ATSUFontID. This font can then be used with
+ * cairo_set_font_face() or cairo_scaled_font_create().
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ * cairo_font_face_destroy() when you are done using it.
+ *
+ * Since: 1.6
+ **/
+cairo_font_face_t *
+cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id)
+{
+ quartz_font_ensure_symbols();
+
+ if (FMGetATSFontRefFromFontPtr != NULL) {
+ ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id);
+ CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont);
+ cairo_font_face_t *ff;
+
+ ff = cairo_quartz_font_face_create_for_cgfont (cgFont);
+
+ CGFontRelease (cgFont);
+
+ return ff;
+ } else {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_font_face_t *)&_cairo_font_face_nil;
+ }
+}
+
+/* This is the old name for the above function, exported for compat purposes */
+cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id);
+
+cairo_font_face_t *
+cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id)
+{
+ return cairo_quartz_font_face_create_for_atsu_font_id (font_id);
+}