/* -*- 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 []) = NU
LL;
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;
/* 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;
static double (*CTFontGetAdvancesForGlyphsPtr) (CTFontRef, CTFontOrientation, const CGGlyph*, CGSize *, CFIndex) = NULL;
static CGRect (*CTFontGetBoundingRectsForGlyphsPtr) (CTFontRef, CTFontOrientation, const CGGlyph*, CGRect *, CFIndex) = 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;
#ifdef CAIRO_HAS_QUARTZ_ATSUFONTID
/* Not public anymore in 64-bits nor in 10.7 */
static ATSFontRef (*FMGetATSFontRefFromFontPtr) (ATSUFontID iFont) = NULL;
#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */
static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE ;
static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE ;
/* Defined in 10.11 */
#define CGGLYPH_MAX ((CGGlyph) 0 xFFFE) /* kCGFontIndexMax */
#define CGGLYPH_INVALID ((CGGlyph) 0 xFFFF) /* kCGFontIndexInvalid */
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" );
/*
* Some Tiger systems have a partial version of CoreText , which
* has incompatible signatures : CTFontCreateWithGraphicsFont
* accepts a double for the size argument even on i386 and all
* functions omit the CTFontOrientation arguments . Since the 10 . 4
* CoreText library does not provide the CTFontCreatePathForGlyph
* symbol , use it to determine whether to use CoreText at all .
*/
CTFontCreatePathForGlyphPtr = dlsym(RTLD_DEFAULT, "CTFontCreatePathForGlyph" );
if (CTFontCreatePathForGlyphPtr) {
CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont" );
CTFontGetAdvancesForGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontGetAdvancesForGlyphs" );
CTFontGetBoundingRectsForGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontGetBoundingRectsForGlyphs" );
} else {
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" );
#ifdef CAIRO_HAS_QUARTZ_ATSUFONTID
FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont" );
#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */
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;
}
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;
CTFontRef ctFont;
};
/*
* 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 = _cairo_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;
if (font_face->ctFont)
CFRelease (font_face->ctFont);
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 = _cairo_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 = 0 ;
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 = _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;
}
font_face->cgFont = CGFontRetain (font);
if (CTFontCreateWithGraphicsFontPtr)
font_face->ctFont = CTFontCreateWithGraphicsFontPtr (font, 1 .0 , NULL, NULL);
else
font_face->ctFont = NULL;
_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)
{
}
static inline CGGlyph
_cairo_quartz_scaled_glyph_index (cairo_scaled_glyph_t *scaled_glyph) {
unsigned long index = _cairo_scaled_glyph_index (scaled_glyph);
return index <= CGGLYPH_MAX ? index : CGGLYPH_INVALID;
}
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 xmin, ymin, xmax, ymax;
if (unlikely (glyph == CGGLYPH_INVALID))
goto FAIL;
if (font_face->ctFont) {
CGSize advanceSize;
CTFontGetBoundingRectsForGlyphsPtr (font_face->ctFont,
kCTFontOrientationDefault,
&glyph, &bbox, 1 );
CTFontGetAdvancesForGlyphsPtr (font_face->ctFont,
kCTFontOrientationDefault,
&glyph, &advanceSize, 1 );
extents.x_advance = advanceSize.width;
extents.y_advance = advanceSize.height;
} else if (CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1 , &advance) &&
CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1 , &bbox)) {
double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
/* 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);
extents.x_advance = advance / emscale;
extents.y_advance = 0 .0 ;
} else {
goto FAIL;
};
/* 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 .0 f, 0 .0 f);
bbox = CGRectApplyAffineTransform (bbox, textMatrix);
bbox = CGRectIntegral (bbox);
bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix));
}
#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;
#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 (unlikely (glyph == CGGLYPH_INVALID)) {
_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 );
if (font_face->ctFont) {
glyphPath = CTFontCreatePathForGlyphPtr (font_face->ctFont, glyph, &textMatrix);
} else {
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 (unlikely (glyph == CGGLYPH_INVALID)) {
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);
CGGlyph glyph[2 ];
UniChar utf16[2 ];
int len = _cairo_ucs4_to_utf16 (ucs4, utf16);
CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, utf16, glyph, len);
return glyph[0 ];
}
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;
}
CTFontRef
_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font)
{
cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
return ffont->ctFont;
}
/*
* compat with old ATSUI backend
*/
#ifdef CAIRO_HAS_QUARTZ_ATSUFONTID
/**
* 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);
}
#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */
Messung V0.5 in Prozent C=96 H=99 G=97
¤ Dauer der Verarbeitung: 0.13 Sekunden
¤
*© Formatika GbR, Deutschland