1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /* cairo - a vector graphics library with display and print output
3  *
4  * Copyright � 2008 Mozilla Corporation
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it either under the terms of the GNU Lesser General Public
8  * License version 2.1 as published by the Free Software Foundation
9  * (the "LGPL") or, at your option, under the terms of the Mozilla
10  * Public License Version 1.1 (the "MPL"). If you do not alter this
11  * notice, a recipient may use your version of this file under either
12  * the MPL or the LGPL.
13  *
14  * You should have received a copy of the LGPL along with this library
15  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17  * You should have received a copy of the MPL along with this library
18  * in the file COPYING-MPL-1.1
19  *
20  * The contents of this file are subject to the Mozilla Public License
21  * Version 1.1 (the "License"); you may not use this file except in
22  * compliance with the License. You may obtain a copy of the License at
23  * http://www.mozilla.org/MPL/
24  *
25  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27  * the specific language governing rights and limitations.
28  *
29  * The Original Code is the cairo graphics library.
30  *
31  * The Initial Developer of the Original Code is Mozilla Foundation.
32  *
33  * Contributor(s):
34  *	Vladimir Vukicevic <vladimir@mozilla.com>
35  */
36 
37 #include "cairoint.h"
38 
39 #include <dlfcn.h>
40 
41 #include "cairo-image-surface-private.h"
42 #include "cairo-quartz.h"
43 #include "cairo-quartz-private.h"
44 
45 #include "cairo-error-private.h"
46 
47 /**
48  * SECTION:cairo-quartz-fonts
49  * @Title: Quartz (CGFont) Fonts
50  * @Short_Description: Font support via CGFont on OS X
51  * @See_Also: #cairo_font_face_t
52  *
53  * The Quartz font backend is primarily used to render text on Apple
54  * MacOS X systems.  The CGFont API is used for the internal
55  * implementation of the font backend methods.
56  **/
57 
58 /**
59  * CAIRO_HAS_QUARTZ_FONT:
60  *
61  * Defined if the Quartz font backend is available.
62  * This macro can be used to conditionally compile backend-specific code.
63  *
64  * Since: 1.6
65  **/
66 
67 static CFDataRef (*CGFontCopyTableForTagPtr) (CGFontRef font, uint32_t tag) = NULL;
68 
69 /* CreateWithFontName exists in 10.5, but not in 10.4; CreateWithName isn't public in 10.4 */
70 static CGFontRef (*CGFontCreateWithFontNamePtr) (CFStringRef) = NULL;
71 static CGFontRef (*CGFontCreateWithNamePtr) (const char *) = NULL;
72 
73 /* These aren't public before 10.5, and some have different names in 10.4 */
74 static int (*CGFontGetUnitsPerEmPtr) (CGFontRef) = NULL;
75 static bool (*CGFontGetGlyphAdvancesPtr) (CGFontRef, const CGGlyph[], size_t, int[]) = NULL;
76 static bool (*CGFontGetGlyphBBoxesPtr) (CGFontRef, const CGGlyph[], size_t, CGRect[]) = NULL;
77 static CGRect (*CGFontGetFontBBoxPtr) (CGFontRef) = NULL;
78 
79 /* Not public, but present */
80 static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const CGGlyph[], size_t) = NULL;
81 static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
82 static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
83 
84 /* Not public in the least bit */
85 static CGPathRef (*CGFontGetGlyphPathPtr) (CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph) = NULL;
86 
87 /* CTFontCreateWithGraphicsFont is not available until 10.5 */
88 typedef const struct __CTFontDescriptor *CTFontDescriptorRef;
89 static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform*, CTFontDescriptorRef) = NULL;
90 static CGPathRef (*CTFontCreatePathForGlyphPtr) (CTFontRef, CGGlyph, CGAffineTransform *) = NULL;
91 static double (*CTFontGetAdvancesForGlyphsPtr) (CTFontRef, CTFontOrientation, const CGGlyph*, CGSize *, CFIndex) = NULL;
92 static CGRect (*CTFontGetBoundingRectsForGlyphsPtr) (CTFontRef, CTFontOrientation, const CGGlyph*, CGRect *, CFIndex) = NULL;
93 
94 /* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */
95 typedef struct {
96     int ascent;
97     int descent;
98     int leading;
99 } quartz_CGFontMetrics;
100 static quartz_CGFontMetrics* (*CGFontGetHMetricsPtr) (CGFontRef fontRef) = NULL;
101 static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL;
102 static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL;
103 static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL;
104 
105 /* Not public anymore in 64-bits nor in 10.7 */
106 static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL;
107 
108 static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
109 static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
110 
111 /* Defined in 10.11 */
112 #define CGGLYPH_MAX ((CGGlyph) 0xFFFE) /* kCGFontIndexMax */
113 #define CGGLYPH_INVALID ((CGGlyph) 0xFFFF) /* kCGFontIndexInvalid */
114 
115 static void
quartz_font_ensure_symbols(void)116 quartz_font_ensure_symbols(void)
117 {
118     if (_cairo_quartz_font_symbol_lookup_done)
119 	return;
120 
121     CGFontCopyTableForTagPtr = dlsym(RTLD_DEFAULT, "CGFontCopyTableForTag");
122 
123     /* Look for the 10.5 versions first */
124     CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBBoxes");
125     if (!CGFontGetGlyphBBoxesPtr)
126 	CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBoundingBoxes");
127 
128     CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnichars");
129     if (!CGFontGetGlyphsForUnicharsPtr)
130 	CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes");
131 
132     CGFontGetFontBBoxPtr = dlsym(RTLD_DEFAULT, "CGFontGetFontBBox");
133 
134     /* We just need one of these two */
135     CGFontCreateWithFontNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithFontName");
136     CGFontCreateWithNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithName");
137 
138     /* These have the same name in 10.4 and 10.5 */
139     CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm");
140     CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances");
141 
142     /*
143      * Some Tiger systems have a partial version of CoreText, which
144      * has incompatible signatures: CTFontCreateWithGraphicsFont
145      * accepts a double for the size argument even on i386 and all
146      * functions omit the CTFontOrientation arguments. Since the 10.4
147      * CoreText library does not provide the CTFontCreatePathForGlyph
148      * symbol, use it to determine whether to use CoreText at all.
149      */
150     CTFontCreatePathForGlyphPtr = dlsym(RTLD_DEFAULT, "CTFontCreatePathForGlyph");
151     if (CTFontCreatePathForGlyphPtr) {
152 	CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont");
153 	CTFontGetAdvancesForGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontGetAdvancesForGlyphs");
154 	CTFontGetBoundingRectsForGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontGetBoundingRectsForGlyphs");
155     } else {
156 	CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath");
157     }
158 
159     CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics");
160     CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent");
161     CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent");
162     CGFontGetLeadingPtr = dlsym(RTLD_DEFAULT, "CGFontGetLeading");
163 
164     CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
165     CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
166 
167     FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont");
168 
169     if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
170 	CGFontGetGlyphBBoxesPtr &&
171 	CGFontGetGlyphsForUnicharsPtr &&
172 	CGFontGetUnitsPerEmPtr &&
173 	CGFontGetGlyphAdvancesPtr &&
174 	((CTFontCreateWithGraphicsFontPtr && CTFontCreatePathForGlyphPtr) || CGFontGetGlyphPathPtr) &&
175 	(CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr)))
176 	_cairo_quartz_font_symbols_present = TRUE;
177 
178     _cairo_quartz_font_symbol_lookup_done = TRUE;
179 }
180 
181 typedef struct _cairo_quartz_font_face cairo_quartz_font_face_t;
182 typedef struct _cairo_quartz_scaled_font cairo_quartz_scaled_font_t;
183 
184 struct _cairo_quartz_scaled_font {
185     cairo_scaled_font_t base;
186 };
187 
188 struct _cairo_quartz_font_face {
189     cairo_font_face_t base;
190 
191     CGFontRef cgFont;
192     CTFontRef ctFont;
193 };
194 
195 /*
196  * font face backend
197  */
198 
199 static cairo_status_t
_cairo_quartz_font_face_create_for_toy(cairo_toy_font_face_t * toy_face,cairo_font_face_t ** font_face)200 _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t   *toy_face,
201 					cairo_font_face_t      **font_face)
202 {
203     const char *family;
204     char *full_name;
205     CFStringRef cgFontName = NULL;
206     CGFontRef cgFont = NULL;
207     int loop;
208 
209     quartz_font_ensure_symbols();
210     if (! _cairo_quartz_font_symbols_present)
211 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
212 
213     family = toy_face->family;
214     full_name = _cairo_malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc.
215     /* handle CSS-ish faces */
216     if (!strcmp(family, "serif") || !strcmp(family, "Times Roman"))
217 	family = "Times";
218     else if (!strcmp(family, "sans-serif") || !strcmp(family, "sans"))
219 	family = "Helvetica";
220     else if (!strcmp(family, "cursive"))
221 	family = "Apple Chancery";
222     else if (!strcmp(family, "fantasy"))
223 	family = "Papyrus";
224     else if (!strcmp(family, "monospace") || !strcmp(family, "mono"))
225 	family = "Courier";
226 
227     /* Try to build up the full name, e.g. "Helvetica Bold Oblique" first,
228      * then drop the bold, then drop the slant, then drop both.. finally
229      * just use "Helvetica".  And if Helvetica doesn't exist, give up.
230      */
231     for (loop = 0; loop < 5; loop++) {
232 	if (loop == 4)
233 	    family = "Helvetica";
234 
235 	strcpy (full_name, family);
236 
237 	if (loop < 3 && (loop & 1) == 0) {
238 	    if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD)
239 		strcat (full_name, " Bold");
240 	}
241 
242 	if (loop < 3 && (loop & 2) == 0) {
243 	    if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC)
244 		strcat (full_name, " Italic");
245 	    else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE)
246 		strcat (full_name, " Oblique");
247 	}
248 
249 	if (CGFontCreateWithFontNamePtr) {
250 	    cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII);
251 	    cgFont = CGFontCreateWithFontNamePtr (cgFontName);
252 	    CFRelease (cgFontName);
253 	} else {
254 	    cgFont = CGFontCreateWithNamePtr (full_name);
255 	}
256 
257 	if (cgFont)
258 	    break;
259     }
260 
261     if (!cgFont) {
262 	/* Give up */
263 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
264     }
265 
266     *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont);
267     CGFontRelease (cgFont);
268 
269     return CAIRO_STATUS_SUCCESS;
270 }
271 
272 static cairo_bool_t
_cairo_quartz_font_face_destroy(void * abstract_face)273 _cairo_quartz_font_face_destroy (void *abstract_face)
274 {
275     cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face;
276 
277     if (font_face->ctFont)
278 	CFRelease (font_face->ctFont);
279 
280     CGFontRelease (font_face->cgFont);
281     return TRUE;
282 }
283 
284 static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend;
285 
286 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)287 _cairo_quartz_font_face_scaled_font_create (void *abstract_face,
288 					    const cairo_matrix_t *font_matrix,
289 					    const cairo_matrix_t *ctm,
290 					    const cairo_font_options_t *options,
291 					    cairo_scaled_font_t **font_out)
292 {
293     cairo_quartz_font_face_t *font_face = abstract_face;
294     cairo_quartz_scaled_font_t *font = NULL;
295     cairo_status_t status;
296     cairo_font_extents_t fs_metrics;
297     double ems;
298     CGRect bbox;
299 
300     quartz_font_ensure_symbols();
301     if (!_cairo_quartz_font_symbols_present)
302 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
303 
304     font = _cairo_malloc (sizeof(cairo_quartz_scaled_font_t));
305     if (font == NULL)
306 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
307 
308     memset (font, 0, sizeof(cairo_quartz_scaled_font_t));
309 
310     status = _cairo_scaled_font_init (&font->base,
311 				      &font_face->base, font_matrix, ctm, options,
312 				      &_cairo_quartz_scaled_font_backend);
313     if (status)
314 	goto FINISH;
315 
316     ems = CGFontGetUnitsPerEmPtr (font_face->cgFont);
317 
318     /* initialize metrics */
319     if (CGFontGetFontBBoxPtr && CGFontGetAscentPtr) {
320 	fs_metrics.ascent = (CGFontGetAscentPtr (font_face->cgFont) / ems);
321 	fs_metrics.descent = - (CGFontGetDescentPtr (font_face->cgFont) / ems);
322 	fs_metrics.height = fs_metrics.ascent + fs_metrics.descent +
323 	    (CGFontGetLeadingPtr (font_face->cgFont) / ems);
324 
325 	bbox = CGFontGetFontBBoxPtr (font_face->cgFont);
326 	fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems;
327 	fs_metrics.max_y_advance = 0.0;
328     } else {
329 	CGGlyph wGlyph;
330 	UniChar u;
331 
332 	quartz_CGFontMetrics *m;
333 	m = CGFontGetHMetricsPtr (font_face->cgFont);
334 
335 	/* On OX 10.4, GetHMetricsPtr sometimes returns NULL for unknown reasons */
336 	if (!m) {
337 	    status = _cairo_error(CAIRO_STATUS_NULL_POINTER);
338 	    goto FINISH;
339 	}
340 
341 	fs_metrics.ascent = (m->ascent / ems);
342 	fs_metrics.descent = - (m->descent / ems);
343 	fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + (m->leading / ems);
344 
345 	/* We kind of have to guess here; W's big, right? */
346 	u = (UniChar) 'W';
347 	CGFontGetGlyphsForUnicharsPtr (font_face->cgFont, &u, &wGlyph, 1);
348 	if (wGlyph && CGFontGetGlyphBBoxesPtr (font_face->cgFont, &wGlyph, 1, &bbox)) {
349 	    fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems;
350 	    fs_metrics.max_y_advance = 0.0;
351 	} else {
352 	    fs_metrics.max_x_advance = 0.0;
353 	    fs_metrics.max_y_advance = 0.0;
354 	}
355     }
356 
357     status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics);
358 
359 FINISH:
360     if (status != CAIRO_STATUS_SUCCESS) {
361 	free (font);
362     } else {
363 	*font_out = (cairo_scaled_font_t*) font;
364     }
365 
366     return status;
367 }
368 
369 const cairo_font_face_backend_t _cairo_quartz_font_face_backend = {
370     CAIRO_FONT_TYPE_QUARTZ,
371     _cairo_quartz_font_face_create_for_toy,
372     _cairo_quartz_font_face_destroy,
373     _cairo_quartz_font_face_scaled_font_create
374 };
375 
376 /**
377  * cairo_quartz_font_face_create_for_cgfont:
378  * @font: a #CGFontRef obtained through a method external to cairo.
379  *
380  * Creates a new font for the Quartz font backend based on a
381  * #CGFontRef.  This font can then be used with
382  * cairo_set_font_face() or cairo_scaled_font_create().
383  *
384  * Return value: a newly created #cairo_font_face_t. Free with
385  *  cairo_font_face_destroy() when you are done using it.
386  *
387  * Since: 1.6
388  **/
389 cairo_font_face_t *
cairo_quartz_font_face_create_for_cgfont(CGFontRef font)390 cairo_quartz_font_face_create_for_cgfont (CGFontRef font)
391 {
392     cairo_quartz_font_face_t *font_face;
393 
394     quartz_font_ensure_symbols();
395 
396     font_face = _cairo_malloc (sizeof (cairo_quartz_font_face_t));
397     if (!font_face) {
398 	cairo_status_t ignore_status;
399 	ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
400 	return (cairo_font_face_t *)&_cairo_font_face_nil;
401     }
402 
403     font_face->cgFont = CGFontRetain (font);
404 
405     if (CTFontCreateWithGraphicsFontPtr)
406 	font_face->ctFont = CTFontCreateWithGraphicsFontPtr (font, 1.0, NULL, NULL);
407     else
408 	font_face->ctFont = NULL;
409 
410     _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
411 
412     return &font_face->base;
413 }
414 
415 /*
416  * scaled font backend
417  */
418 
419 static cairo_quartz_font_face_t *
_cairo_quartz_scaled_to_face(void * abstract_font)420 _cairo_quartz_scaled_to_face (void *abstract_font)
421 {
422     cairo_quartz_scaled_font_t *sfont = (cairo_quartz_scaled_font_t*) abstract_font;
423     cairo_font_face_t *font_face = sfont->base.font_face;
424     assert (font_face->backend->type == CAIRO_FONT_TYPE_QUARTZ);
425     return (cairo_quartz_font_face_t*) font_face;
426 }
427 
428 static void
_cairo_quartz_scaled_font_fini(void * abstract_font)429 _cairo_quartz_scaled_font_fini(void *abstract_font)
430 {
431 }
432 
433 static inline CGGlyph
_cairo_quartz_scaled_glyph_index(cairo_scaled_glyph_t * scaled_glyph)434 _cairo_quartz_scaled_glyph_index (cairo_scaled_glyph_t *scaled_glyph) {
435     unsigned long index = _cairo_scaled_glyph_index (scaled_glyph);
436     return index <= CGGLYPH_MAX ? index : CGGLYPH_INVALID;
437 }
438 
439 static cairo_int_status_t
_cairo_quartz_init_glyph_metrics(cairo_quartz_scaled_font_t * font,cairo_scaled_glyph_t * scaled_glyph)440 _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font,
441 				  cairo_scaled_glyph_t *scaled_glyph)
442 {
443     cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
444 
445     cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
446     cairo_text_extents_t extents = {0, 0, 0, 0, 0, 0};
447     CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
448     int advance;
449     CGRect bbox;
450     double xmin, ymin, xmax, ymax;
451 
452     if (unlikely (glyph == CGGLYPH_INVALID))
453 	goto FAIL;
454 
455     if (font_face->ctFont) {
456 	CGSize advanceSize;
457 	CTFontGetBoundingRectsForGlyphsPtr (font_face->ctFont,
458 					    kCTFontOrientationDefault,
459 					    &glyph, &bbox, 1);
460 
461 	CTFontGetAdvancesForGlyphsPtr (font_face->ctFont,
462 				       kCTFontOrientationDefault,
463 				       &glyph, &advanceSize, 1);
464 
465 	extents.x_advance = advanceSize.width;
466 	extents.y_advance = advanceSize.height;
467     } else if (CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) &&
468 	       CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) {
469 	double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
470 
471 	/* broken fonts like Al Bayan return incorrect bounds for some null
472 	 * characters,see https://bugzilla.mozilla.org/show_bug.cgi?id=534260 */
473 	if (unlikely (bbox.origin.x == -32767 &&
474 		      bbox.origin.y == -32767 &&
475 		      bbox.size.width == 65534 &&
476 		      bbox.size.height == 65534)) {
477 	    bbox.origin.x = bbox.origin.y = 0;
478 	    bbox.size.width = bbox.size.height = 0;
479 	}
480 
481 	bbox = CGRectMake (bbox.origin.x / emscale,
482 			   bbox.origin.y / emscale,
483 			   bbox.size.width / emscale,
484 			   bbox.size.height / emscale);
485 
486 	extents.x_advance = advance / emscale;
487 	extents.y_advance = 0.0;
488     } else {
489 	goto FAIL;
490     };
491 
492     /* Should we want to always integer-align glyph extents, we can do so in this way */
493 #if 0
494     {
495 	CGAffineTransform textMatrix;
496 	textMatrix = CGAffineTransformMake (font->base.scale.xx,
497 					    -font->base.scale.yx,
498 					    -font->base.scale.xy,
499 					    font->base.scale.yy,
500 					    0.0f, 0.0f);
501 
502 	bbox = CGRectApplyAffineTransform (bbox, textMatrix);
503 	bbox = CGRectIntegral (bbox);
504 	bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix));
505     }
506 #endif
507 
508     xmin = CGRectGetMinX(bbox);
509     ymin = CGRectGetMinY(bbox);
510     xmax = CGRectGetMaxX(bbox);
511     ymax = CGRectGetMaxY(bbox);
512 
513     extents.x_bearing = xmin;
514     extents.y_bearing = - ymax;
515     extents.width = xmax - xmin;
516     extents.height = ymax - ymin;
517 
518 #if 0
519     fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f\n\n", glyph,
520 	     extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance);
521 #endif
522 
523   FAIL:
524     _cairo_scaled_glyph_set_metrics (scaled_glyph,
525 				     &font->base,
526 				     &extents);
527 
528     return status;
529 }
530 
531 static void
_cairo_quartz_path_apply_func(void * info,const CGPathElement * el)532 _cairo_quartz_path_apply_func (void *info, const CGPathElement *el)
533 {
534     cairo_path_fixed_t *path = (cairo_path_fixed_t *) info;
535     cairo_status_t status;
536 
537     switch (el->type) {
538 	case kCGPathElementMoveToPoint:
539 	    status = _cairo_path_fixed_move_to (path,
540 						_cairo_fixed_from_double(el->points[0].x),
541 						_cairo_fixed_from_double(el->points[0].y));
542 	    assert(!status);
543 	    break;
544 	case kCGPathElementAddLineToPoint:
545 	    status = _cairo_path_fixed_line_to (path,
546 						_cairo_fixed_from_double(el->points[0].x),
547 						_cairo_fixed_from_double(el->points[0].y));
548 	    assert(!status);
549 	    break;
550 	case kCGPathElementAddQuadCurveToPoint: {
551 	    cairo_fixed_t fx, fy;
552 	    double x, y;
553 	    if (!_cairo_path_fixed_get_current_point (path, &fx, &fy))
554 		fx = fy = 0;
555 	    x = _cairo_fixed_to_double (fx);
556 	    y = _cairo_fixed_to_double (fy);
557 
558 	    status = _cairo_path_fixed_curve_to (path,
559 						 _cairo_fixed_from_double((x + el->points[0].x * 2.0) / 3.0),
560 						 _cairo_fixed_from_double((y + el->points[0].y * 2.0) / 3.0),
561 						 _cairo_fixed_from_double((el->points[0].x * 2.0 + el->points[1].x) / 3.0),
562 						 _cairo_fixed_from_double((el->points[0].y * 2.0 + el->points[1].y) / 3.0),
563 						 _cairo_fixed_from_double(el->points[1].x),
564 						 _cairo_fixed_from_double(el->points[1].y));
565 	}
566 	    assert(!status);
567 	    break;
568 	case kCGPathElementAddCurveToPoint:
569 	    status = _cairo_path_fixed_curve_to (path,
570 						 _cairo_fixed_from_double(el->points[0].x),
571 						 _cairo_fixed_from_double(el->points[0].y),
572 						 _cairo_fixed_from_double(el->points[1].x),
573 						 _cairo_fixed_from_double(el->points[1].y),
574 						 _cairo_fixed_from_double(el->points[2].x),
575 						 _cairo_fixed_from_double(el->points[2].y));
576 	    assert(!status);
577 	    break;
578 	case kCGPathElementCloseSubpath:
579 	    status = _cairo_path_fixed_close_path (path);
580 	    assert(!status);
581 	    break;
582     }
583 }
584 
585 static cairo_int_status_t
_cairo_quartz_init_glyph_path(cairo_quartz_scaled_font_t * font,cairo_scaled_glyph_t * scaled_glyph)586 _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font,
587 			       cairo_scaled_glyph_t *scaled_glyph)
588 {
589     cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
590     CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
591     CGAffineTransform textMatrix;
592     CGPathRef glyphPath;
593     cairo_path_fixed_t *path;
594 
595     if (unlikely (glyph == CGGLYPH_INVALID)) {
596 	_cairo_scaled_glyph_set_path (scaled_glyph, &font->base, _cairo_path_fixed_create());
597 	return CAIRO_STATUS_SUCCESS;
598     }
599 
600     /* scale(1,-1) * font->base.scale */
601     textMatrix = CGAffineTransformMake (font->base.scale.xx,
602 					font->base.scale.yx,
603 					-font->base.scale.xy,
604 					-font->base.scale.yy,
605 					0, 0);
606 
607     if (font_face->ctFont) {
608 	glyphPath = CTFontCreatePathForGlyphPtr (font_face->ctFont, glyph, &textMatrix);
609     } else {
610 	glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph);
611     }
612 
613     if (!glyphPath)
614 	return CAIRO_INT_STATUS_UNSUPPORTED;
615 
616     path = _cairo_path_fixed_create ();
617     if (!path) {
618 	CGPathRelease (glyphPath);
619 	return _cairo_error(CAIRO_STATUS_NO_MEMORY);
620     }
621 
622     CGPathApply (glyphPath, path, _cairo_quartz_path_apply_func);
623 
624     CGPathRelease (glyphPath);
625 
626     _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, path);
627 
628     return CAIRO_STATUS_SUCCESS;
629 }
630 
631 static cairo_int_status_t
_cairo_quartz_init_glyph_surface(cairo_quartz_scaled_font_t * font,cairo_scaled_glyph_t * scaled_glyph)632 _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font,
633 				  cairo_scaled_glyph_t *scaled_glyph)
634 {
635     cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
636 
637     cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
638 
639     cairo_image_surface_t *surface = NULL;
640 
641     CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
642 
643     int advance;
644     CGRect bbox;
645     double width, height;
646     double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
647 
648     CGContextRef cgContext = NULL;
649     CGAffineTransform textMatrix;
650     CGRect glyphRect, glyphRectInt;
651     CGPoint glyphOrigin;
652 
653     //fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface);
654 
655     /* Create blank 2x2 image if we don't have this character.
656      * Maybe we should draw a better missing-glyph slug or something,
657      * but this is ok for now.
658      */
659     if (unlikely (glyph == CGGLYPH_INVALID)) {
660 	surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, 2, 2);
661 	status = cairo_surface_status ((cairo_surface_t *) surface);
662 	if (status)
663 	    return status;
664 
665 	_cairo_scaled_glyph_set_surface (scaled_glyph,
666 					 &font->base,
667 					 surface);
668 	return CAIRO_STATUS_SUCCESS;
669     }
670 
671     if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
672 	!CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
673     {
674 	return CAIRO_INT_STATUS_UNSUPPORTED;
675     }
676 
677     /* scale(1,-1) * font->base.scale * scale(1,-1) */
678     textMatrix = CGAffineTransformMake (font->base.scale.xx,
679 					-font->base.scale.yx,
680 					-font->base.scale.xy,
681 					font->base.scale.yy,
682 					0, -0);
683     glyphRect = CGRectMake (bbox.origin.x / emscale,
684 			    bbox.origin.y / emscale,
685 			    bbox.size.width / emscale,
686 			    bbox.size.height / emscale);
687 
688     glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix);
689 
690     /* Round the rectangle outwards, so that we don't have to deal
691      * with non-integer-pixel origins or dimensions.
692      */
693     glyphRectInt = CGRectIntegral (glyphRect);
694 
695 #if 0
696     fprintf (stderr, "glyphRect[o]: %f %f %f %f\n",
697 	     glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);
698     fprintf (stderr, "glyphRectInt: %f %f %f %f\n",
699 	     glyphRectInt.origin.x, glyphRectInt.origin.y, glyphRectInt.size.width, glyphRectInt.size.height);
700 #endif
701 
702     glyphOrigin = glyphRectInt.origin;
703 
704     //textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm));
705 
706     width = glyphRectInt.size.width;
707     height = glyphRectInt.size.height;
708 
709     //fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);
710 
711     surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
712     if (surface->base.status)
713 	return surface->base.status;
714 
715     if (surface->width != 0 && surface->height != 0) {
716 	cgContext = CGBitmapContextCreate (surface->data,
717 					   surface->width,
718 					   surface->height,
719 					   8,
720 					   surface->stride,
721 					   NULL,
722 					   kCGImageAlphaOnly);
723 
724 	if (cgContext == NULL) {
725 	    cairo_surface_destroy (&surface->base);
726 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
727 	}
728 
729 	CGContextSetFont (cgContext, font_face->cgFont);
730 	CGContextSetFontSize (cgContext, 1.0);
731 	CGContextSetTextMatrix (cgContext, textMatrix);
732 
733 	switch (font->base.options.antialias) {
734 	case CAIRO_ANTIALIAS_SUBPIXEL:
735 	case CAIRO_ANTIALIAS_BEST:
736 	    CGContextSetShouldAntialias (cgContext, TRUE);
737 	    CGContextSetShouldSmoothFonts (cgContext, TRUE);
738 	    if (CGContextSetAllowsFontSmoothingPtr &&
739 		!CGContextGetAllowsFontSmoothingPtr (cgContext))
740 		CGContextSetAllowsFontSmoothingPtr (cgContext, TRUE);
741 	    break;
742 	case CAIRO_ANTIALIAS_NONE:
743 	    CGContextSetShouldAntialias (cgContext, FALSE);
744 	    break;
745 	case CAIRO_ANTIALIAS_GRAY:
746 	case CAIRO_ANTIALIAS_GOOD:
747 	case CAIRO_ANTIALIAS_FAST:
748 	    CGContextSetShouldAntialias (cgContext, TRUE);
749 	    CGContextSetShouldSmoothFonts (cgContext, FALSE);
750 	    break;
751 	case CAIRO_ANTIALIAS_DEFAULT:
752 	default:
753 	    /* Don't do anything */
754 	    break;
755 	}
756 
757 	CGContextSetAlpha (cgContext, 1.0);
758 	CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1);
759 
760 	CGContextRelease (cgContext);
761     }
762 
763     cairo_surface_set_device_offset (&surface->base,
764 				     - glyphOrigin.x,
765 				     height + glyphOrigin.y);
766 
767     _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface);
768 
769     return status;
770 }
771 
772 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)773 _cairo_quartz_scaled_glyph_init (void *abstract_font,
774 				 cairo_scaled_glyph_t *scaled_glyph,
775 				 cairo_scaled_glyph_info_t info)
776 {
777     cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t *) abstract_font;
778     cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
779 
780     if (!status && (info & CAIRO_SCALED_GLYPH_INFO_METRICS))
781 	status = _cairo_quartz_init_glyph_metrics (font, scaled_glyph);
782 
783     if (!status && (info & CAIRO_SCALED_GLYPH_INFO_PATH))
784 	status = _cairo_quartz_init_glyph_path (font, scaled_glyph);
785 
786     if (!status && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE))
787 	status = _cairo_quartz_init_glyph_surface (font, scaled_glyph);
788 
789     return status;
790 }
791 
792 static unsigned long
_cairo_quartz_ucs4_to_index(void * abstract_font,uint32_t ucs4)793 _cairo_quartz_ucs4_to_index (void *abstract_font,
794 			     uint32_t ucs4)
795 {
796     cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font;
797     cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font);
798     CGGlyph glyph[2];
799     UniChar utf16[2];
800 
801     int len = _cairo_ucs4_to_utf16 (ucs4, utf16);
802     CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, utf16, glyph, len);
803 
804     return glyph[0];
805 }
806 
807 static cairo_int_status_t
_cairo_quartz_load_truetype_table(void * abstract_font,unsigned long tag,long offset,unsigned char * buffer,unsigned long * length)808 _cairo_quartz_load_truetype_table (void	            *abstract_font,
809 				   unsigned long     tag,
810 				   long              offset,
811 				   unsigned char    *buffer,
812 				   unsigned long    *length)
813 {
814     cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face (abstract_font);
815     CFDataRef data = NULL;
816 
817     if (likely (CGFontCopyTableForTagPtr))
818 	data = CGFontCopyTableForTagPtr (font_face->cgFont, tag);
819 
820     if (!data)
821         return CAIRO_INT_STATUS_UNSUPPORTED;
822 
823     if (buffer == NULL) {
824 	*length = CFDataGetLength (data);
825 	CFRelease (data);
826 	return CAIRO_STATUS_SUCCESS;
827     }
828 
829     if (CFDataGetLength (data) < offset + (long) *length) {
830 	CFRelease (data);
831 	return CAIRO_INT_STATUS_UNSUPPORTED;
832     }
833 
834     CFDataGetBytes (data, CFRangeMake (offset, *length), buffer);
835     CFRelease (data);
836 
837     return CAIRO_STATUS_SUCCESS;
838 }
839 
840 static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = {
841     CAIRO_FONT_TYPE_QUARTZ,
842     _cairo_quartz_scaled_font_fini,
843     _cairo_quartz_scaled_glyph_init,
844     NULL, /* text_to_glyphs */
845     _cairo_quartz_ucs4_to_index,
846     _cairo_quartz_load_truetype_table,
847     NULL, /* map_glyphs_to_unicode */
848 };
849 
850 /*
851  * private methods that the quartz surface uses
852  */
853 
854 CGFontRef
_cairo_quartz_scaled_font_get_cg_font_ref(cairo_scaled_font_t * abstract_font)855 _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font)
856 {
857     cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
858 
859     return ffont->cgFont;
860 }
861 
862 CTFontRef
_cairo_quartz_scaled_font_get_ct_font_ref(cairo_scaled_font_t * abstract_font)863 _cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font)
864 {
865     cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
866 
867     return ffont->ctFont;
868 }
869 
870 /*
871  * compat with old ATSUI backend
872  */
873 
874 /**
875  * cairo_quartz_font_face_create_for_atsu_font_id:
876  * @font_id: an ATSUFontID for the font.
877  *
878  * Creates a new font for the Quartz font backend based on an
879  * #ATSUFontID. This font can then be used with
880  * cairo_set_font_face() or cairo_scaled_font_create().
881  *
882  * Return value: a newly created #cairo_font_face_t. Free with
883  *  cairo_font_face_destroy() when you are done using it.
884  *
885  * Since: 1.6
886  **/
887 cairo_font_face_t *
cairo_quartz_font_face_create_for_atsu_font_id(ATSUFontID font_id)888 cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id)
889 {
890     quartz_font_ensure_symbols();
891 
892     if (FMGetATSFontRefFromFontPtr != NULL) {
893 	ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id);
894 	CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont);
895 	cairo_font_face_t *ff;
896 
897 	ff = cairo_quartz_font_face_create_for_cgfont (cgFont);
898 
899 	CGFontRelease (cgFont);
900 
901 	return ff;
902     } else {
903 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
904 	return (cairo_font_face_t *)&_cairo_font_face_nil;
905     }
906 }
907 
908 /* This is the old name for the above function, exported for compat purposes */
909 cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id);
910 
911 cairo_font_face_t *
cairo_atsui_font_face_create_for_atsu_font_id(ATSUFontID font_id)912 cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id)
913 {
914     return cairo_quartz_font_face_create_for_atsu_font_id (font_id);
915 }
916