754f9336f0
git-svn-id: svn://kolibrios.org@4349 a494cfbc-eb01-0410-851d-a64ba20cac60
861 lines
27 KiB
C
861 lines
27 KiB
C
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
|
||
/* cairo - a vector graphics library with display and print output
|
||
*
|
||
* Copyright <20> 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);
|
||
}
|