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 © 2002 University of Southern California
5  * Copyright © 2005,2008 Red Hat Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it either under the terms of the GNU Lesser General Public
9  * License version 2.1 as published by the Free Software Foundation
10  * (the "LGPL") or, at your option, under the terms of the Mozilla
11  * Public License Version 1.1 (the "MPL"). If you do not alter this
12  * notice, a recipient may use your version of this file under either
13  * the MPL or the LGPL.
14  *
15  * You should have received a copy of the LGPL along with this library
16  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
18  * You should have received a copy of the MPL along with this library
19  * in the file COPYING-MPL-1.1
20  *
21  * The contents of this file are subject to the Mozilla Public License
22  * Version 1.1 (the "License"); you may not use this file except in
23  * compliance with the License. You may obtain a copy of the License at
24  * http://www.mozilla.org/MPL/
25  *
26  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28  * the specific language governing rights and limitations.
29  *
30  * The Original Code is the cairo graphics library.
31  *
32  * The Initial Developer of the Original Code is University of Southern
33  * California.
34  *
35  * Contributor(s):
36  *	Carl D. Worth <cworth@cworth.org>
37  *      Graydon Hoare <graydon@redhat.com>
38  *      Owen Taylor <otaylor@redhat.com>
39  *      Behdad Esfahbod <behdad@behdad.org>
40  */
41 
42 #define _BSD_SOURCE /* for strdup() */
43 #include "cairoint.h"
44 #include "cairo-error-private.h"
45 
46 
47 static const cairo_font_face_t _cairo_font_face_null_pointer = {
48     { 0 },				/* hash_entry */
49     CAIRO_STATUS_NULL_POINTER,		/* status */
50     CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
51     { 0, 0, 0, NULL },			/* user_data */
52     NULL
53 };
54 
55 static const cairo_font_face_t _cairo_font_face_invalid_string = {
56     { 0 },				/* hash_entry */
57     CAIRO_STATUS_INVALID_STRING,	/* status */
58     CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
59     { 0, 0, 0, NULL },			/* user_data */
60     NULL
61 };
62 
63 static const cairo_font_face_t _cairo_font_face_invalid_slant = {
64     { 0 },				/* hash_entry */
65     CAIRO_STATUS_INVALID_SLANT,		/* status */
66     CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
67     { 0, 0, 0, NULL },			/* user_data */
68     NULL
69 };
70 
71 static const cairo_font_face_t _cairo_font_face_invalid_weight = {
72     { 0 },				/* hash_entry */
73     CAIRO_STATUS_INVALID_WEIGHT,	/* status */
74     CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
75     { 0, 0, 0, NULL },			/* user_data */
76     NULL
77 };
78 
79 
80 static const cairo_font_face_backend_t _cairo_toy_font_face_backend;
81 
82 static int
83 _cairo_toy_font_face_keys_equal (const void *key_a,
84 				 const void *key_b);
85 
86 /* We maintain a hash table from family/weight/slant =>
87  * #cairo_font_face_t for #cairo_toy_font_t. The primary purpose of
88  * this mapping is to provide unique #cairo_font_face_t values so that
89  * our cache and mapping from #cairo_font_face_t => #cairo_scaled_font_t
90  * works. Once the corresponding #cairo_font_face_t objects fall out of
91  * downstream caches, we don't need them in this hash table anymore.
92  *
93  * Modifications to this hash table are protected by
94  * _cairo_toy_font_face_mutex.
95  */
96 static cairo_hash_table_t *cairo_toy_font_face_hash_table = NULL;
97 
98 static cairo_hash_table_t *
_cairo_toy_font_face_hash_table_lock(void)99 _cairo_toy_font_face_hash_table_lock (void)
100 {
101     CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex);
102 
103     if (cairo_toy_font_face_hash_table == NULL)
104     {
105 	cairo_toy_font_face_hash_table =
106 	    _cairo_hash_table_create (_cairo_toy_font_face_keys_equal);
107 
108 	if (cairo_toy_font_face_hash_table == NULL) {
109 	    CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex);
110 	    return NULL;
111 	}
112     }
113 
114     return cairo_toy_font_face_hash_table;
115 }
116 
117 static void
_cairo_toy_font_face_hash_table_unlock(void)118 _cairo_toy_font_face_hash_table_unlock (void)
119 {
120     CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex);
121 }
122 
123 /**
124  * _cairo_toy_font_face_init_key:
125  *
126  * Initialize those portions of #cairo_toy_font_face_t needed to use
127  * it as a hash table key, including the hash code buried away in
128  * font_face->base.hash_entry. No memory allocation is performed here
129  * so that no fini call is needed. We do this to make it easier to use
130  * an automatic #cairo_toy_font_face_t variable as a key.
131  **/
132 static void
_cairo_toy_font_face_init_key(cairo_toy_font_face_t * key,const char * family,cairo_font_slant_t slant,cairo_font_weight_t weight)133 _cairo_toy_font_face_init_key (cairo_toy_font_face_t *key,
134 			       const char	     *family,
135 			       cairo_font_slant_t     slant,
136 			       cairo_font_weight_t    weight)
137 {
138     unsigned long hash;
139 
140     key->family = family;
141     key->owns_family = FALSE;
142 
143     key->slant = slant;
144     key->weight = weight;
145 
146     /* 1607 and 1451 are just a couple of arbitrary primes. */
147     hash = _cairo_hash_string (family);
148     hash += ((unsigned long) slant) * 1607;
149     hash += ((unsigned long) weight) * 1451;
150 
151     assert (hash != 0);
152     key->base.hash_entry.hash = hash;
153 }
154 
155 static cairo_status_t
_cairo_toy_font_face_create_impl_face(cairo_toy_font_face_t * font_face,cairo_font_face_t ** impl_font_face)156 _cairo_toy_font_face_create_impl_face (cairo_toy_font_face_t *font_face,
157 				       cairo_font_face_t **impl_font_face)
158 {
159     const cairo_font_face_backend_t * backend = CAIRO_FONT_FACE_BACKEND_DEFAULT;
160     cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
161 
162     if (unlikely (font_face->base.status))
163 	return font_face->base.status;
164 
165     if (backend->create_for_toy != NULL &&
166 	0 != strncmp (font_face->family, CAIRO_USER_FONT_FAMILY_DEFAULT,
167 		      strlen (CAIRO_USER_FONT_FAMILY_DEFAULT)))
168     {
169 	status = backend->create_for_toy (font_face, impl_font_face);
170     }
171 
172     if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
173 	backend = &_cairo_user_font_face_backend;
174 	status = backend->create_for_toy (font_face, impl_font_face);
175     }
176 
177     return status;
178 }
179 
180 static cairo_status_t
_cairo_toy_font_face_init(cairo_toy_font_face_t * font_face,const char * family,cairo_font_slant_t slant,cairo_font_weight_t weight)181 _cairo_toy_font_face_init (cairo_toy_font_face_t *font_face,
182 			   const char	         *family,
183 			   cairo_font_slant_t	  slant,
184 			   cairo_font_weight_t	  weight)
185 {
186     char *family_copy;
187     cairo_status_t status;
188 
189     family_copy = strdup (family);
190     if (unlikely (family_copy == NULL))
191 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
192 
193     _cairo_toy_font_face_init_key (font_face, family_copy, slant, weight);
194     font_face->owns_family = TRUE;
195 
196     _cairo_font_face_init (&font_face->base, &_cairo_toy_font_face_backend);
197 
198     status = _cairo_toy_font_face_create_impl_face (font_face,
199 						    &font_face->impl_face);
200     if (unlikely (status)) {
201 	free (family_copy);
202 	return status;
203     }
204 
205     return CAIRO_STATUS_SUCCESS;
206 }
207 
208 static void
_cairo_toy_font_face_fini(cairo_toy_font_face_t * font_face)209 _cairo_toy_font_face_fini (cairo_toy_font_face_t *font_face)
210 {
211     /* We assert here that we own font_face->family before casting
212      * away the const qualifer. */
213     assert (font_face->owns_family);
214     free ((char*) font_face->family);
215 
216     if (font_face->impl_face)
217 	cairo_font_face_destroy (font_face->impl_face);
218 }
219 
220 static int
_cairo_toy_font_face_keys_equal(const void * key_a,const void * key_b)221 _cairo_toy_font_face_keys_equal (const void *key_a,
222 				 const void *key_b)
223 {
224     const cairo_toy_font_face_t *face_a = key_a;
225     const cairo_toy_font_face_t *face_b = key_b;
226 
227     return (strcmp (face_a->family, face_b->family) == 0 &&
228 	    face_a->slant == face_b->slant &&
229 	    face_a->weight == face_b->weight);
230 }
231 
232 /**
233  * cairo_toy_font_face_create:
234  * @family: a font family name, encoded in UTF-8
235  * @slant: the slant for the font
236  * @weight: the weight for the font
237  *
238  * Creates a font face from a triplet of family, slant, and weight.
239  * These font faces are used in implementation of the the #cairo_t "toy"
240  * font API.
241  *
242  * If @family is the zero-length string "", the platform-specific default
243  * family is assumed.  The default family then can be queried using
244  * cairo_toy_font_face_get_family().
245  *
246  * The cairo_select_font_face() function uses this to create font faces.
247  * See that function for limitations and other details of toy font faces.
248  *
249  * Return value: a newly created #cairo_font_face_t. Free with
250  *  cairo_font_face_destroy() when you are done using it.
251  *
252  * Since: 1.8
253  **/
254 cairo_font_face_t *
cairo_toy_font_face_create(const char * family,cairo_font_slant_t slant,cairo_font_weight_t weight)255 cairo_toy_font_face_create (const char          *family,
256 			    cairo_font_slant_t   slant,
257 			    cairo_font_weight_t  weight)
258 {
259     cairo_status_t status;
260     cairo_toy_font_face_t key, *font_face;
261     cairo_hash_table_t *hash_table;
262 
263     if (family == NULL)
264 	return (cairo_font_face_t*) &_cairo_font_face_null_pointer;
265 
266     /* Make sure we've got valid UTF-8 for the family */
267     status = _cairo_utf8_to_ucs4 (family, -1, NULL, NULL);
268     if (unlikely (status)) {
269 	if (status == CAIRO_STATUS_INVALID_STRING)
270 	    return (cairo_font_face_t*) &_cairo_font_face_invalid_string;
271 
272 	return (cairo_font_face_t*) &_cairo_font_face_nil;
273     }
274 
275     switch (slant) {
276 	case CAIRO_FONT_SLANT_NORMAL:
277 	case CAIRO_FONT_SLANT_ITALIC:
278 	case CAIRO_FONT_SLANT_OBLIQUE:
279 	    break;
280 	default:
281 	    return (cairo_font_face_t*) &_cairo_font_face_invalid_slant;
282     }
283 
284     switch (weight) {
285 	case CAIRO_FONT_WEIGHT_NORMAL:
286 	case CAIRO_FONT_WEIGHT_BOLD:
287 	    break;
288 	default:
289 	    return (cairo_font_face_t*) &_cairo_font_face_invalid_weight;
290     }
291 
292     if (*family == '\0')
293 	family = CAIRO_FONT_FAMILY_DEFAULT;
294 
295     hash_table = _cairo_toy_font_face_hash_table_lock ();
296     if (unlikely (hash_table == NULL))
297 	goto UNWIND;
298 
299     _cairo_toy_font_face_init_key (&key, family, slant, weight);
300 
301     /* Return existing font_face if it exists in the hash table. */
302     font_face = _cairo_hash_table_lookup (hash_table,
303 					  &key.base.hash_entry);
304     if (font_face != NULL) {
305 	if (font_face->base.status == CAIRO_STATUS_SUCCESS) {
306 	    /* We increment the reference count here manually to avoid
307 	       double-locking. */
308 	    _cairo_reference_count_inc (&font_face->base.ref_count);
309 	    _cairo_toy_font_face_hash_table_unlock ();
310 	    return &font_face->base;
311 	}
312 
313 	/* remove the bad font from the hash table */
314 	_cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
315 	font_face->base.hash_entry.hash = 0;
316     }
317 
318     /* Otherwise create it and insert into hash table. */
319     font_face = malloc (sizeof (cairo_toy_font_face_t));
320     if (unlikely (font_face == NULL)) {
321 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
322 	goto UNWIND_HASH_TABLE_LOCK;
323     }
324 
325     status = _cairo_toy_font_face_init (font_face, family, slant, weight);
326     if (unlikely (status))
327 	goto UNWIND_FONT_FACE_MALLOC;
328 
329     assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash);
330     status = _cairo_hash_table_insert (hash_table, &font_face->base.hash_entry);
331     if (unlikely (status))
332 	goto UNWIND_FONT_FACE_INIT;
333 
334     _cairo_toy_font_face_hash_table_unlock ();
335 
336     return &font_face->base;
337 
338  UNWIND_FONT_FACE_INIT:
339     _cairo_toy_font_face_fini (font_face);
340  UNWIND_FONT_FACE_MALLOC:
341     free (font_face);
342  UNWIND_HASH_TABLE_LOCK:
343     _cairo_toy_font_face_hash_table_unlock ();
344  UNWIND:
345     return (cairo_font_face_t*) &_cairo_font_face_nil;
346 }
347 slim_hidden_def (cairo_toy_font_face_create);
348 
349 static void
_cairo_toy_font_face_destroy(void * abstract_face)350 _cairo_toy_font_face_destroy (void *abstract_face)
351 {
352     cairo_toy_font_face_t *font_face = abstract_face;
353     cairo_hash_table_t *hash_table;
354 
355     if (font_face == NULL ||
356 	    CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->base.ref_count))
357 	return;
358 
359     hash_table = _cairo_toy_font_face_hash_table_lock ();
360     /* All created objects must have been mapped in the hash table. */
361     assert (hash_table != NULL);
362 
363     if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) {
364 	/* somebody recreated the font whilst we waited for the lock */
365 	_cairo_toy_font_face_hash_table_unlock ();
366 	return;
367     }
368 
369     if (font_face->base.hash_entry.hash != 0)
370 	_cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
371 
372     _cairo_toy_font_face_hash_table_unlock ();
373 
374     _cairo_toy_font_face_fini (font_face);
375 }
376 
377 static cairo_status_t
_cairo_toy_font_face_scaled_font_create(void * abstract_font_face,const cairo_matrix_t * font_matrix,const cairo_matrix_t * ctm,const cairo_font_options_t * options,cairo_scaled_font_t ** scaled_font)378 _cairo_toy_font_face_scaled_font_create (void                *abstract_font_face,
379 					 const cairo_matrix_t       *font_matrix,
380 					 const cairo_matrix_t       *ctm,
381 					 const cairo_font_options_t *options,
382 					 cairo_scaled_font_t	   **scaled_font)
383 {
384     cairo_toy_font_face_t *font_face = (cairo_toy_font_face_t *) abstract_font_face;
385 
386     ASSERT_NOT_REACHED;
387 
388     return _cairo_font_face_set_error (&font_face->base, CAIRO_STATUS_FONT_TYPE_MISMATCH);
389 }
390 
391 static cairo_font_face_t *
_cairo_toy_font_face_get_implementation(void * abstract_font_face,const cairo_matrix_t * font_matrix,const cairo_matrix_t * ctm,const cairo_font_options_t * options)392 _cairo_toy_font_face_get_implementation (void                *abstract_font_face,
393 					 const cairo_matrix_t       *font_matrix,
394 					 const cairo_matrix_t       *ctm,
395 					 const cairo_font_options_t *options)
396 {
397     cairo_toy_font_face_t *font_face = abstract_font_face;
398 
399     if (font_face->impl_face) {
400 	cairo_font_face_t *impl = font_face->impl_face;
401 
402 	if (impl->backend->get_implementation != NULL) {
403 	    return impl->backend->get_implementation (impl,
404 						      font_matrix,
405 						      ctm,
406 						      options);
407 	}
408 
409 	return cairo_font_face_reference (impl);
410     }
411 
412     return abstract_font_face;
413 }
414 
415 static cairo_bool_t
_cairo_font_face_is_toy(cairo_font_face_t * font_face)416 _cairo_font_face_is_toy (cairo_font_face_t *font_face)
417 {
418     return font_face->backend == &_cairo_toy_font_face_backend;
419 }
420 
421 /**
422  * cairo_toy_font_face_get_family:
423  * @font_face: A toy font face
424  *
425  * Gets the familly name of a toy font.
426  *
427  * Return value: The family name.  This string is owned by the font face
428  * and remains valid as long as the font face is alive (referenced).
429  *
430  * Since: 1.8
431  **/
432 const char *
cairo_toy_font_face_get_family(cairo_font_face_t * font_face)433 cairo_toy_font_face_get_family (cairo_font_face_t *font_face)
434 {
435     cairo_toy_font_face_t *toy_font_face;
436 
437     if (font_face->status)
438 	return CAIRO_FONT_FAMILY_DEFAULT;
439 
440     toy_font_face = (cairo_toy_font_face_t *) font_face;
441     if (! _cairo_font_face_is_toy (font_face)) {
442 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
443 	    return CAIRO_FONT_FAMILY_DEFAULT;
444     }
445     assert (toy_font_face->owns_family);
446     return toy_font_face->family;
447 }
448 
449 /**
450  * cairo_toy_font_face_get_slant:
451  * @font_face: A toy font face
452  *
453  * Gets the slant a toy font.
454  *
455  * Return value: The slant value
456  *
457  * Since: 1.8
458  **/
459 cairo_font_slant_t
cairo_toy_font_face_get_slant(cairo_font_face_t * font_face)460 cairo_toy_font_face_get_slant (cairo_font_face_t *font_face)
461 {
462     cairo_toy_font_face_t *toy_font_face;
463 
464     if (font_face->status)
465 	return CAIRO_FONT_SLANT_DEFAULT;
466 
467     toy_font_face = (cairo_toy_font_face_t *) font_face;
468     if (! _cairo_font_face_is_toy (font_face)) {
469 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
470 	    return CAIRO_FONT_SLANT_DEFAULT;
471     }
472     return toy_font_face->slant;
473 }
474 slim_hidden_def (cairo_toy_font_face_get_slant);
475 
476 /**
477  * cairo_toy_font_face_get_weight:
478  * @font_face: A toy font face
479  *
480  * Gets the weight a toy font.
481  *
482  * Return value: The weight value
483  *
484  * Since: 1.8
485  **/
486 cairo_font_weight_t
cairo_toy_font_face_get_weight(cairo_font_face_t * font_face)487 cairo_toy_font_face_get_weight (cairo_font_face_t *font_face)
488 {
489     cairo_toy_font_face_t *toy_font_face;
490 
491     if (font_face->status)
492 	return CAIRO_FONT_WEIGHT_DEFAULT;
493 
494     toy_font_face = (cairo_toy_font_face_t *) font_face;
495     if (! _cairo_font_face_is_toy (font_face)) {
496 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
497 	    return CAIRO_FONT_WEIGHT_DEFAULT;
498     }
499     return toy_font_face->weight;
500 }
501 slim_hidden_def (cairo_toy_font_face_get_weight);
502 
503 static const cairo_font_face_backend_t _cairo_toy_font_face_backend = {
504     CAIRO_FONT_TYPE_TOY,
505     NULL,					/* create_for_toy */
506     _cairo_toy_font_face_destroy,
507     _cairo_toy_font_face_scaled_font_create,
508     _cairo_toy_font_face_get_implementation
509 };
510 
511 void
_cairo_toy_font_face_reset_static_data(void)512 _cairo_toy_font_face_reset_static_data (void)
513 {
514     cairo_hash_table_t *hash_table;
515 
516     /* We manually acquire the lock rather than calling
517      * cairo_toy_font_face_hash_table_lock simply to avoid
518      * creating the table only to destroy it again. */
519     CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex);
520     hash_table = cairo_toy_font_face_hash_table;
521     cairo_toy_font_face_hash_table = NULL;
522     CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex);
523 
524     if (hash_table != NULL)
525 	_cairo_hash_table_destroy (hash_table);
526 }
527