0,0 → 1,860 |
/* -*- 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; |
|
/* Not public in the least bit */ |
static CGPathRef (*CGFontGetGlyphPathPtr) (CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph) = 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"); |
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"); |
|
FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont"); |
|
if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) && |
CGFontGetGlyphBBoxesPtr && |
CGFontGetGlyphsForUnicharsPtr && |
CGFontGetUnitsPerEmPtr && |
CGFontGetGlyphAdvancesPtr && |
CGFontGetGlyphPathPtr && |
(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 void |
_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); |
} |
|
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; |
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); |
|
glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph); |
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); |
} |