Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3959 | Serge | 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 |
||
35 | */ |
||
36 | |||
37 | #include "cairoint.h" |
||
38 | |||
39 | #include |
||
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 | /* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */ |
||
88 | typedef struct { |
||
89 | int ascent; |
||
90 | int descent; |
||
91 | int leading; |
||
92 | } quartz_CGFontMetrics; |
||
93 | static quartz_CGFontMetrics* (*CGFontGetHMetricsPtr) (CGFontRef fontRef) = NULL; |
||
94 | static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL; |
||
95 | static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL; |
||
96 | static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL; |
||
97 | |||
98 | /* Not public anymore in 64-bits nor in 10.7 */ |
||
99 | static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL; |
||
100 | |||
101 | static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE; |
||
102 | static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE; |
||
103 | |||
104 | static void |
||
105 | quartz_font_ensure_symbols(void) |
||
106 | { |
||
107 | if (_cairo_quartz_font_symbol_lookup_done) |
||
108 | return; |
||
109 | |||
110 | CGFontCopyTableForTagPtr = dlsym(RTLD_DEFAULT, "CGFontCopyTableForTag"); |
||
111 | |||
112 | /* Look for the 10.5 versions first */ |
||
113 | CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBBoxes"); |
||
114 | if (!CGFontGetGlyphBBoxesPtr) |
||
115 | CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBoundingBoxes"); |
||
116 | |||
117 | CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnichars"); |
||
118 | if (!CGFontGetGlyphsForUnicharsPtr) |
||
119 | CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes"); |
||
120 | |||
121 | CGFontGetFontBBoxPtr = dlsym(RTLD_DEFAULT, "CGFontGetFontBBox"); |
||
122 | |||
123 | /* We just need one of these two */ |
||
124 | CGFontCreateWithFontNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithFontName"); |
||
125 | CGFontCreateWithNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithName"); |
||
126 | |||
127 | /* These have the same name in 10.4 and 10.5 */ |
||
128 | CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm"); |
||
129 | CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances"); |
||
130 | CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath"); |
||
131 | |||
132 | CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics"); |
||
133 | CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent"); |
||
134 | CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent"); |
||
135 | CGFontGetLeadingPtr = dlsym(RTLD_DEFAULT, "CGFontGetLeading"); |
||
136 | |||
137 | CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); |
||
138 | CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); |
||
139 | |||
140 | FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont"); |
||
141 | |||
142 | if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) && |
||
143 | CGFontGetGlyphBBoxesPtr && |
||
144 | CGFontGetGlyphsForUnicharsPtr && |
||
145 | CGFontGetUnitsPerEmPtr && |
||
146 | CGFontGetGlyphAdvancesPtr && |
||
147 | CGFontGetGlyphPathPtr && |
||
148 | (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr))) |
||
149 | _cairo_quartz_font_symbols_present = TRUE; |
||
150 | |||
151 | _cairo_quartz_font_symbol_lookup_done = TRUE; |
||
152 | } |
||
153 | |||
154 | typedef struct _cairo_quartz_font_face cairo_quartz_font_face_t; |
||
155 | typedef struct _cairo_quartz_scaled_font cairo_quartz_scaled_font_t; |
||
156 | |||
157 | struct _cairo_quartz_scaled_font { |
||
158 | cairo_scaled_font_t base; |
||
159 | }; |
||
160 | |||
161 | struct _cairo_quartz_font_face { |
||
162 | cairo_font_face_t base; |
||
163 | |||
164 | CGFontRef cgFont; |
||
165 | }; |
||
166 | |||
167 | /* |
||
168 | * font face backend |
||
169 | */ |
||
170 | |||
171 | static cairo_status_t |
||
172 | _cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, |
||
173 | cairo_font_face_t **font_face) |
||
174 | { |
||
175 | const char *family; |
||
176 | char *full_name; |
||
177 | CFStringRef cgFontName = NULL; |
||
178 | CGFontRef cgFont = NULL; |
||
179 | int loop; |
||
180 | |||
181 | quartz_font_ensure_symbols(); |
||
182 | if (! _cairo_quartz_font_symbols_present) |
||
183 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
184 | |||
185 | family = toy_face->family; |
||
186 | full_name = malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc. |
||
187 | /* handle CSS-ish faces */ |
||
188 | if (!strcmp(family, "serif") || !strcmp(family, "Times Roman")) |
||
189 | family = "Times"; |
||
190 | else if (!strcmp(family, "sans-serif") || !strcmp(family, "sans")) |
||
191 | family = "Helvetica"; |
||
192 | else if (!strcmp(family, "cursive")) |
||
193 | family = "Apple Chancery"; |
||
194 | else if (!strcmp(family, "fantasy")) |
||
195 | family = "Papyrus"; |
||
196 | else if (!strcmp(family, "monospace") || !strcmp(family, "mono")) |
||
197 | family = "Courier"; |
||
198 | |||
199 | /* Try to build up the full name, e.g. "Helvetica Bold Oblique" first, |
||
200 | * then drop the bold, then drop the slant, then drop both.. finally |
||
201 | * just use "Helvetica". And if Helvetica doesn't exist, give up. |
||
202 | */ |
||
203 | for (loop = 0; loop < 5; loop++) { |
||
204 | if (loop == 4) |
||
205 | family = "Helvetica"; |
||
206 | |||
207 | strcpy (full_name, family); |
||
208 | |||
209 | if (loop < 3 && (loop & 1) == 0) { |
||
210 | if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD) |
||
211 | strcat (full_name, " Bold"); |
||
212 | } |
||
213 | |||
214 | if (loop < 3 && (loop & 2) == 0) { |
||
215 | if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC) |
||
216 | strcat (full_name, " Italic"); |
||
217 | else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE) |
||
218 | strcat (full_name, " Oblique"); |
||
219 | } |
||
220 | |||
221 | if (CGFontCreateWithFontNamePtr) { |
||
222 | cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); |
||
223 | cgFont = CGFontCreateWithFontNamePtr (cgFontName); |
||
224 | CFRelease (cgFontName); |
||
225 | } else { |
||
226 | cgFont = CGFontCreateWithNamePtr (full_name); |
||
227 | } |
||
228 | |||
229 | if (cgFont) |
||
230 | break; |
||
231 | } |
||
232 | |||
233 | if (!cgFont) { |
||
234 | /* Give up */ |
||
235 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
236 | } |
||
237 | |||
238 | *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont); |
||
239 | CGFontRelease (cgFont); |
||
240 | |||
241 | return CAIRO_STATUS_SUCCESS; |
||
242 | } |
||
243 | |||
244 | static void |
||
245 | _cairo_quartz_font_face_destroy (void *abstract_face) |
||
246 | { |
||
247 | cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face; |
||
248 | |||
249 | CGFontRelease (font_face->cgFont); |
||
250 | } |
||
251 | |||
252 | static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend; |
||
253 | |||
254 | static cairo_status_t |
||
255 | _cairo_quartz_font_face_scaled_font_create (void *abstract_face, |
||
256 | const cairo_matrix_t *font_matrix, |
||
257 | const cairo_matrix_t *ctm, |
||
258 | const cairo_font_options_t *options, |
||
259 | cairo_scaled_font_t **font_out) |
||
260 | { |
||
261 | cairo_quartz_font_face_t *font_face = abstract_face; |
||
262 | cairo_quartz_scaled_font_t *font = NULL; |
||
263 | cairo_status_t status; |
||
264 | cairo_font_extents_t fs_metrics; |
||
265 | double ems; |
||
266 | CGRect bbox; |
||
267 | |||
268 | quartz_font_ensure_symbols(); |
||
269 | if (!_cairo_quartz_font_symbols_present) |
||
270 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
271 | |||
272 | font = malloc(sizeof(cairo_quartz_scaled_font_t)); |
||
273 | if (font == NULL) |
||
274 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
275 | |||
276 | memset (font, 0, sizeof(cairo_quartz_scaled_font_t)); |
||
277 | |||
278 | status = _cairo_scaled_font_init (&font->base, |
||
279 | &font_face->base, font_matrix, ctm, options, |
||
280 | &_cairo_quartz_scaled_font_backend); |
||
281 | if (status) |
||
282 | goto FINISH; |
||
283 | |||
284 | ems = CGFontGetUnitsPerEmPtr (font_face->cgFont); |
||
285 | |||
286 | /* initialize metrics */ |
||
287 | if (CGFontGetFontBBoxPtr && CGFontGetAscentPtr) { |
||
288 | fs_metrics.ascent = (CGFontGetAscentPtr (font_face->cgFont) / ems); |
||
289 | fs_metrics.descent = - (CGFontGetDescentPtr (font_face->cgFont) / ems); |
||
290 | fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + |
||
291 | (CGFontGetLeadingPtr (font_face->cgFont) / ems); |
||
292 | |||
293 | bbox = CGFontGetFontBBoxPtr (font_face->cgFont); |
||
294 | fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; |
||
295 | fs_metrics.max_y_advance = 0.0; |
||
296 | } else { |
||
297 | CGGlyph wGlyph; |
||
298 | UniChar u; |
||
299 | |||
300 | quartz_CGFontMetrics *m; |
||
301 | m = CGFontGetHMetricsPtr (font_face->cgFont); |
||
302 | |||
303 | /* On OX 10.4, GetHMetricsPtr sometimes returns NULL for unknown reasons */ |
||
304 | if (!m) { |
||
305 | status = _cairo_error(CAIRO_STATUS_NULL_POINTER); |
||
306 | goto FINISH; |
||
307 | } |
||
308 | |||
309 | fs_metrics.ascent = (m->ascent / ems); |
||
310 | fs_metrics.descent = - (m->descent / ems); |
||
311 | fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + (m->leading / ems); |
||
312 | |||
313 | /* We kind of have to guess here; W's big, right? */ |
||
314 | u = (UniChar) 'W'; |
||
315 | CGFontGetGlyphsForUnicharsPtr (font_face->cgFont, &u, &wGlyph, 1); |
||
316 | if (wGlyph && CGFontGetGlyphBBoxesPtr (font_face->cgFont, &wGlyph, 1, &bbox)) { |
||
317 | fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; |
||
318 | fs_metrics.max_y_advance = 0.0; |
||
319 | } else { |
||
320 | fs_metrics.max_x_advance = 0.0; |
||
321 | fs_metrics.max_y_advance = 0.0; |
||
322 | } |
||
323 | } |
||
324 | |||
325 | status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics); |
||
326 | |||
327 | FINISH: |
||
328 | if (status != CAIRO_STATUS_SUCCESS) { |
||
329 | free (font); |
||
330 | } else { |
||
331 | *font_out = (cairo_scaled_font_t*) font; |
||
332 | } |
||
333 | |||
334 | return status; |
||
335 | } |
||
336 | |||
337 | const cairo_font_face_backend_t _cairo_quartz_font_face_backend = { |
||
338 | CAIRO_FONT_TYPE_QUARTZ, |
||
339 | _cairo_quartz_font_face_create_for_toy, |
||
340 | _cairo_quartz_font_face_destroy, |
||
341 | _cairo_quartz_font_face_scaled_font_create |
||
342 | }; |
||
343 | |||
344 | /** |
||
345 | * cairo_quartz_font_face_create_for_cgfont: |
||
346 | * @font: a #CGFontRef obtained through a method external to cairo. |
||
347 | * |
||
348 | * Creates a new font for the Quartz font backend based on a |
||
349 | * #CGFontRef. This font can then be used with |
||
350 | * cairo_set_font_face() or cairo_scaled_font_create(). |
||
351 | * |
||
352 | * Return value: a newly created #cairo_font_face_t. Free with |
||
353 | * cairo_font_face_destroy() when you are done using it. |
||
354 | * |
||
355 | * Since: 1.6 |
||
356 | **/ |
||
357 | cairo_font_face_t * |
||
358 | cairo_quartz_font_face_create_for_cgfont (CGFontRef font) |
||
359 | { |
||
360 | cairo_quartz_font_face_t *font_face; |
||
361 | |||
362 | quartz_font_ensure_symbols(); |
||
363 | |||
364 | font_face = malloc (sizeof (cairo_quartz_font_face_t)); |
||
365 | if (!font_face) { |
||
366 | cairo_status_t ignore_status; |
||
367 | ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
368 | return (cairo_font_face_t *)&_cairo_font_face_nil; |
||
369 | } |
||
370 | |||
371 | font_face->cgFont = CGFontRetain (font); |
||
372 | |||
373 | _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend); |
||
374 | |||
375 | return &font_face->base; |
||
376 | } |
||
377 | |||
378 | /* |
||
379 | * scaled font backend |
||
380 | */ |
||
381 | |||
382 | static cairo_quartz_font_face_t * |
||
383 | _cairo_quartz_scaled_to_face (void *abstract_font) |
||
384 | { |
||
385 | cairo_quartz_scaled_font_t *sfont = (cairo_quartz_scaled_font_t*) abstract_font; |
||
386 | cairo_font_face_t *font_face = sfont->base.font_face; |
||
387 | assert (font_face->backend->type == CAIRO_FONT_TYPE_QUARTZ); |
||
388 | return (cairo_quartz_font_face_t*) font_face; |
||
389 | } |
||
390 | |||
391 | static void |
||
392 | _cairo_quartz_scaled_font_fini(void *abstract_font) |
||
393 | { |
||
394 | } |
||
395 | |||
396 | #define INVALID_GLYPH 0x00 |
||
397 | |||
398 | static inline CGGlyph |
||
399 | _cairo_quartz_scaled_glyph_index (cairo_scaled_glyph_t *scaled_glyph) { |
||
400 | unsigned long index = _cairo_scaled_glyph_index (scaled_glyph); |
||
401 | if (index > 0xffff) |
||
402 | return INVALID_GLYPH; |
||
403 | return (CGGlyph) index; |
||
404 | } |
||
405 | |||
406 | static cairo_int_status_t |
||
407 | _cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, |
||
408 | cairo_scaled_glyph_t *scaled_glyph) |
||
409 | { |
||
410 | cairo_int_status_t status = CAIRO_STATUS_SUCCESS; |
||
411 | |||
412 | cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); |
||
413 | cairo_text_extents_t extents = {0, 0, 0, 0, 0, 0}; |
||
414 | CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); |
||
415 | int advance; |
||
416 | CGRect bbox; |
||
417 | double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont); |
||
418 | double xmin, ymin, xmax, ymax; |
||
419 | |||
420 | if (glyph == INVALID_GLYPH) |
||
421 | goto FAIL; |
||
422 | |||
423 | if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) || |
||
424 | !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) |
||
425 | goto FAIL; |
||
426 | |||
427 | /* broken fonts like Al Bayan return incorrect bounds for some null characters, |
||
428 | see https://bugzilla.mozilla.org/show_bug.cgi?id=534260 */ |
||
429 | if (unlikely (bbox.origin.x == -32767 && |
||
430 | bbox.origin.y == -32767 && |
||
431 | bbox.size.width == 65534 && |
||
432 | bbox.size.height == 65534)) { |
||
433 | bbox.origin.x = bbox.origin.y = 0; |
||
434 | bbox.size.width = bbox.size.height = 0; |
||
435 | } |
||
436 | |||
437 | bbox = CGRectMake (bbox.origin.x / emscale, |
||
438 | bbox.origin.y / emscale, |
||
439 | bbox.size.width / emscale, |
||
440 | bbox.size.height / emscale); |
||
441 | |||
442 | /* Should we want to always integer-align glyph extents, we can do so in this way */ |
||
443 | #if 0 |
||
444 | { |
||
445 | CGAffineTransform textMatrix; |
||
446 | textMatrix = CGAffineTransformMake (font->base.scale.xx, |
||
447 | -font->base.scale.yx, |
||
448 | -font->base.scale.xy, |
||
449 | font->base.scale.yy, |
||
450 | 0.0f, 0.0f); |
||
451 | |||
452 | bbox = CGRectApplyAffineTransform (bbox, textMatrix); |
||
453 | bbox = CGRectIntegral (bbox); |
||
454 | bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix)); |
||
455 | } |
||
456 | #endif |
||
457 | |||
458 | #if 0 |
||
459 | fprintf (stderr, "[0x%04x] bbox: %f %f %f %f\n", glyph, |
||
460 | bbox.origin.x / emscale, bbox.origin.y / emscale, |
||
461 | bbox.size.width / emscale, bbox.size.height / emscale); |
||
462 | #endif |
||
463 | |||
464 | xmin = CGRectGetMinX(bbox); |
||
465 | ymin = CGRectGetMinY(bbox); |
||
466 | xmax = CGRectGetMaxX(bbox); |
||
467 | ymax = CGRectGetMaxY(bbox); |
||
468 | |||
469 | extents.x_bearing = xmin; |
||
470 | extents.y_bearing = - ymax; |
||
471 | extents.width = xmax - xmin; |
||
472 | extents.height = ymax - ymin; |
||
473 | |||
474 | extents.x_advance = (double) advance / emscale; |
||
475 | extents.y_advance = 0.0; |
||
476 | |||
477 | #if 0 |
||
478 | fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f\n\n", glyph, |
||
479 | extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance); |
||
480 | #endif |
||
481 | |||
482 | FAIL: |
||
483 | _cairo_scaled_glyph_set_metrics (scaled_glyph, |
||
484 | &font->base, |
||
485 | &extents); |
||
486 | |||
487 | return status; |
||
488 | } |
||
489 | |||
490 | static void |
||
491 | _cairo_quartz_path_apply_func (void *info, const CGPathElement *el) |
||
492 | { |
||
493 | cairo_path_fixed_t *path = (cairo_path_fixed_t *) info; |
||
494 | cairo_status_t status; |
||
495 | |||
496 | switch (el->type) { |
||
497 | case kCGPathElementMoveToPoint: |
||
498 | status = _cairo_path_fixed_move_to (path, |
||
499 | _cairo_fixed_from_double(el->points[0].x), |
||
500 | _cairo_fixed_from_double(el->points[0].y)); |
||
501 | assert(!status); |
||
502 | break; |
||
503 | case kCGPathElementAddLineToPoint: |
||
504 | status = _cairo_path_fixed_line_to (path, |
||
505 | _cairo_fixed_from_double(el->points[0].x), |
||
506 | _cairo_fixed_from_double(el->points[0].y)); |
||
507 | assert(!status); |
||
508 | break; |
||
509 | case kCGPathElementAddQuadCurveToPoint: { |
||
510 | cairo_fixed_t fx, fy; |
||
511 | double x, y; |
||
512 | if (!_cairo_path_fixed_get_current_point (path, &fx, &fy)) |
||
513 | fx = fy = 0; |
||
514 | x = _cairo_fixed_to_double (fx); |
||
515 | y = _cairo_fixed_to_double (fy); |
||
516 | |||
517 | status = _cairo_path_fixed_curve_to (path, |
||
518 | _cairo_fixed_from_double((x + el->points[0].x * 2.0) / 3.0), |
||
519 | _cairo_fixed_from_double((y + el->points[0].y * 2.0) / 3.0), |
||
520 | _cairo_fixed_from_double((el->points[0].x * 2.0 + el->points[1].x) / 3.0), |
||
521 | _cairo_fixed_from_double((el->points[0].y * 2.0 + el->points[1].y) / 3.0), |
||
522 | _cairo_fixed_from_double(el->points[1].x), |
||
523 | _cairo_fixed_from_double(el->points[1].y)); |
||
524 | } |
||
525 | assert(!status); |
||
526 | break; |
||
527 | case kCGPathElementAddCurveToPoint: |
||
528 | status = _cairo_path_fixed_curve_to (path, |
||
529 | _cairo_fixed_from_double(el->points[0].x), |
||
530 | _cairo_fixed_from_double(el->points[0].y), |
||
531 | _cairo_fixed_from_double(el->points[1].x), |
||
532 | _cairo_fixed_from_double(el->points[1].y), |
||
533 | _cairo_fixed_from_double(el->points[2].x), |
||
534 | _cairo_fixed_from_double(el->points[2].y)); |
||
535 | assert(!status); |
||
536 | break; |
||
537 | case kCGPathElementCloseSubpath: |
||
538 | status = _cairo_path_fixed_close_path (path); |
||
539 | assert(!status); |
||
540 | break; |
||
541 | } |
||
542 | } |
||
543 | |||
544 | static cairo_int_status_t |
||
545 | _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, |
||
546 | cairo_scaled_glyph_t *scaled_glyph) |
||
547 | { |
||
548 | cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); |
||
549 | CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); |
||
550 | CGAffineTransform textMatrix; |
||
551 | CGPathRef glyphPath; |
||
552 | cairo_path_fixed_t *path; |
||
553 | |||
554 | if (glyph == INVALID_GLYPH) { |
||
555 | _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, _cairo_path_fixed_create()); |
||
556 | return CAIRO_STATUS_SUCCESS; |
||
557 | } |
||
558 | |||
559 | /* scale(1,-1) * font->base.scale */ |
||
560 | textMatrix = CGAffineTransformMake (font->base.scale.xx, |
||
561 | font->base.scale.yx, |
||
562 | -font->base.scale.xy, |
||
563 | -font->base.scale.yy, |
||
564 | 0, 0); |
||
565 | |||
566 | glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph); |
||
567 | if (!glyphPath) |
||
568 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
569 | |||
570 | path = _cairo_path_fixed_create (); |
||
571 | if (!path) { |
||
572 | CGPathRelease (glyphPath); |
||
573 | return _cairo_error(CAIRO_STATUS_NO_MEMORY); |
||
574 | } |
||
575 | |||
576 | CGPathApply (glyphPath, path, _cairo_quartz_path_apply_func); |
||
577 | |||
578 | CGPathRelease (glyphPath); |
||
579 | |||
580 | _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, path); |
||
581 | |||
582 | return CAIRO_STATUS_SUCCESS; |
||
583 | } |
||
584 | |||
585 | static cairo_int_status_t |
||
586 | _cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, |
||
587 | cairo_scaled_glyph_t *scaled_glyph) |
||
588 | { |
||
589 | cairo_int_status_t status = CAIRO_STATUS_SUCCESS; |
||
590 | |||
591 | cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); |
||
592 | |||
593 | cairo_image_surface_t *surface = NULL; |
||
594 | |||
595 | CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); |
||
596 | |||
597 | int advance; |
||
598 | CGRect bbox; |
||
599 | double width, height; |
||
600 | double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont); |
||
601 | |||
602 | CGContextRef cgContext = NULL; |
||
603 | CGAffineTransform textMatrix; |
||
604 | CGRect glyphRect, glyphRectInt; |
||
605 | CGPoint glyphOrigin; |
||
606 | |||
607 | //fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface); |
||
608 | |||
609 | /* Create blank 2x2 image if we don't have this character. |
||
610 | * Maybe we should draw a better missing-glyph slug or something, |
||
611 | * but this is ok for now. |
||
612 | */ |
||
613 | if (glyph == INVALID_GLYPH) { |
||
614 | surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, 2, 2); |
||
615 | status = cairo_surface_status ((cairo_surface_t *) surface); |
||
616 | if (status) |
||
617 | return status; |
||
618 | |||
619 | _cairo_scaled_glyph_set_surface (scaled_glyph, |
||
620 | &font->base, |
||
621 | surface); |
||
622 | return CAIRO_STATUS_SUCCESS; |
||
623 | } |
||
624 | |||
625 | if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) || |
||
626 | !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) |
||
627 | { |
||
628 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
629 | } |
||
630 | |||
631 | /* scale(1,-1) * font->base.scale * scale(1,-1) */ |
||
632 | textMatrix = CGAffineTransformMake (font->base.scale.xx, |
||
633 | -font->base.scale.yx, |
||
634 | -font->base.scale.xy, |
||
635 | font->base.scale.yy, |
||
636 | 0, -0); |
||
637 | glyphRect = CGRectMake (bbox.origin.x / emscale, |
||
638 | bbox.origin.y / emscale, |
||
639 | bbox.size.width / emscale, |
||
640 | bbox.size.height / emscale); |
||
641 | |||
642 | glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix); |
||
643 | |||
644 | /* Round the rectangle outwards, so that we don't have to deal |
||
645 | * with non-integer-pixel origins or dimensions. |
||
646 | */ |
||
647 | glyphRectInt = CGRectIntegral (glyphRect); |
||
648 | |||
649 | #if 0 |
||
650 | fprintf (stderr, "glyphRect[o]: %f %f %f %f\n", |
||
651 | glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height); |
||
652 | fprintf (stderr, "glyphRectInt: %f %f %f %f\n", |
||
653 | glyphRectInt.origin.x, glyphRectInt.origin.y, glyphRectInt.size.width, glyphRectInt.size.height); |
||
654 | #endif |
||
655 | |||
656 | glyphOrigin = glyphRectInt.origin; |
||
657 | |||
658 | //textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm)); |
||
659 | |||
660 | width = glyphRectInt.size.width; |
||
661 | height = glyphRectInt.size.height; |
||
662 | |||
663 | //fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height); |
||
664 | |||
665 | surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); |
||
666 | if (surface->base.status) |
||
667 | return surface->base.status; |
||
668 | |||
669 | if (surface->width != 0 && surface->height != 0) { |
||
670 | cgContext = CGBitmapContextCreate (surface->data, |
||
671 | surface->width, |
||
672 | surface->height, |
||
673 | 8, |
||
674 | surface->stride, |
||
675 | NULL, |
||
676 | kCGImageAlphaOnly); |
||
677 | |||
678 | if (cgContext == NULL) { |
||
679 | cairo_surface_destroy (&surface->base); |
||
680 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
681 | } |
||
682 | |||
683 | CGContextSetFont (cgContext, font_face->cgFont); |
||
684 | CGContextSetFontSize (cgContext, 1.0); |
||
685 | CGContextSetTextMatrix (cgContext, textMatrix); |
||
686 | |||
687 | switch (font->base.options.antialias) { |
||
688 | case CAIRO_ANTIALIAS_SUBPIXEL: |
||
689 | case CAIRO_ANTIALIAS_BEST: |
||
690 | CGContextSetShouldAntialias (cgContext, TRUE); |
||
691 | CGContextSetShouldSmoothFonts (cgContext, TRUE); |
||
692 | if (CGContextSetAllowsFontSmoothingPtr && |
||
693 | !CGContextGetAllowsFontSmoothingPtr (cgContext)) |
||
694 | CGContextSetAllowsFontSmoothingPtr (cgContext, TRUE); |
||
695 | break; |
||
696 | case CAIRO_ANTIALIAS_NONE: |
||
697 | CGContextSetShouldAntialias (cgContext, FALSE); |
||
698 | break; |
||
699 | case CAIRO_ANTIALIAS_GRAY: |
||
700 | case CAIRO_ANTIALIAS_GOOD: |
||
701 | case CAIRO_ANTIALIAS_FAST: |
||
702 | CGContextSetShouldAntialias (cgContext, TRUE); |
||
703 | CGContextSetShouldSmoothFonts (cgContext, FALSE); |
||
704 | break; |
||
705 | case CAIRO_ANTIALIAS_DEFAULT: |
||
706 | default: |
||
707 | /* Don't do anything */ |
||
708 | break; |
||
709 | } |
||
710 | |||
711 | CGContextSetAlpha (cgContext, 1.0); |
||
712 | CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1); |
||
713 | |||
714 | CGContextRelease (cgContext); |
||
715 | } |
||
716 | |||
717 | cairo_surface_set_device_offset (&surface->base, |
||
718 | - glyphOrigin.x, |
||
719 | height + glyphOrigin.y); |
||
720 | |||
721 | _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface); |
||
722 | |||
723 | return status; |
||
724 | } |
||
725 | |||
726 | static cairo_int_status_t |
||
727 | _cairo_quartz_scaled_glyph_init (void *abstract_font, |
||
728 | cairo_scaled_glyph_t *scaled_glyph, |
||
729 | cairo_scaled_glyph_info_t info) |
||
730 | { |
||
731 | cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t *) abstract_font; |
||
732 | cairo_int_status_t status = CAIRO_STATUS_SUCCESS; |
||
733 | |||
734 | if (!status && (info & CAIRO_SCALED_GLYPH_INFO_METRICS)) |
||
735 | status = _cairo_quartz_init_glyph_metrics (font, scaled_glyph); |
||
736 | |||
737 | if (!status && (info & CAIRO_SCALED_GLYPH_INFO_PATH)) |
||
738 | status = _cairo_quartz_init_glyph_path (font, scaled_glyph); |
||
739 | |||
740 | if (!status && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE)) |
||
741 | status = _cairo_quartz_init_glyph_surface (font, scaled_glyph); |
||
742 | |||
743 | return status; |
||
744 | } |
||
745 | |||
746 | static unsigned long |
||
747 | _cairo_quartz_ucs4_to_index (void *abstract_font, |
||
748 | uint32_t ucs4) |
||
749 | { |
||
750 | cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font; |
||
751 | cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font); |
||
752 | UniChar u = (UniChar) ucs4; |
||
753 | CGGlyph glyph; |
||
754 | |||
755 | CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, &u, &glyph, 1); |
||
756 | |||
757 | return glyph; |
||
758 | } |
||
759 | |||
760 | static cairo_int_status_t |
||
761 | _cairo_quartz_load_truetype_table (void *abstract_font, |
||
762 | unsigned long tag, |
||
763 | long offset, |
||
764 | unsigned char *buffer, |
||
765 | unsigned long *length) |
||
766 | { |
||
767 | cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face (abstract_font); |
||
768 | CFDataRef data = NULL; |
||
769 | |||
770 | if (likely (CGFontCopyTableForTagPtr)) |
||
771 | data = CGFontCopyTableForTagPtr (font_face->cgFont, tag); |
||
772 | |||
773 | if (!data) |
||
774 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
775 | |||
776 | if (buffer == NULL) { |
||
777 | *length = CFDataGetLength (data); |
||
778 | CFRelease (data); |
||
779 | return CAIRO_STATUS_SUCCESS; |
||
780 | } |
||
781 | |||
782 | if (CFDataGetLength (data) < offset + (long) *length) { |
||
783 | CFRelease (data); |
||
784 | return CAIRO_INT_STATUS_UNSUPPORTED; |
||
785 | } |
||
786 | |||
787 | CFDataGetBytes (data, CFRangeMake (offset, *length), buffer); |
||
788 | CFRelease (data); |
||
789 | |||
790 | return CAIRO_STATUS_SUCCESS; |
||
791 | } |
||
792 | |||
793 | static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = { |
||
794 | CAIRO_FONT_TYPE_QUARTZ, |
||
795 | _cairo_quartz_scaled_font_fini, |
||
796 | _cairo_quartz_scaled_glyph_init, |
||
797 | NULL, /* text_to_glyphs */ |
||
798 | _cairo_quartz_ucs4_to_index, |
||
799 | _cairo_quartz_load_truetype_table, |
||
800 | NULL, /* map_glyphs_to_unicode */ |
||
801 | }; |
||
802 | |||
803 | /* |
||
804 | * private methods that the quartz surface uses |
||
805 | */ |
||
806 | |||
807 | CGFontRef |
||
808 | _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font) |
||
809 | { |
||
810 | cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font); |
||
811 | |||
812 | return ffont->cgFont; |
||
813 | } |
||
814 | |||
815 | /* |
||
816 | * compat with old ATSUI backend |
||
817 | */ |
||
818 | |||
819 | /** |
||
820 | * cairo_quartz_font_face_create_for_atsu_font_id: |
||
821 | * @font_id: an ATSUFontID for the font. |
||
822 | * |
||
823 | * Creates a new font for the Quartz font backend based on an |
||
824 | * #ATSUFontID. This font can then be used with |
||
825 | * cairo_set_font_face() or cairo_scaled_font_create(). |
||
826 | * |
||
827 | * Return value: a newly created #cairo_font_face_t. Free with |
||
828 | * cairo_font_face_destroy() when you are done using it. |
||
829 | * |
||
830 | * Since: 1.6 |
||
831 | **/ |
||
832 | cairo_font_face_t * |
||
833 | cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id) |
||
834 | { |
||
835 | quartz_font_ensure_symbols(); |
||
836 | |||
837 | if (FMGetATSFontRefFromFontPtr != NULL) { |
||
838 | ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id); |
||
839 | CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont); |
||
840 | cairo_font_face_t *ff; |
||
841 | |||
842 | ff = cairo_quartz_font_face_create_for_cgfont (cgFont); |
||
843 | |||
844 | CGFontRelease (cgFont); |
||
845 | |||
846 | return ff; |
||
847 | } else { |
||
848 | _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); |
||
849 | return (cairo_font_face_t *)&_cairo_font_face_nil; |
||
850 | } |
||
851 | } |
||
852 | |||
853 | /* This is the old name for the above function, exported for compat purposes */ |
||
854 | cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id); |
||
855 | |||
856 | cairo_font_face_t * |
||
857 | cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id) |
||
858 | { |
||
859 | return cairo_quartz_font_face_create_for_atsu_font_id (font_id); |
||
860 | }>>>> |