Changes.
[cairo/gpu.git] / src / cairo-scaled-font-subsets.c
blob64c9d9aadb4643e77cdeed91aae85e1b056f0cc9
1 /* cairo - a vector graphics library with display and print output
3 * Copyright © 2003 University of Southern California
4 * Copyright © 2005 Red Hat, Inc
5 * Copyright © 2006 Keith Packard
6 * Copyright © 2006 Red Hat, Inc
8 * This library is free software; you can redistribute it and/or
9 * modify it either under the terms of the GNU Lesser General Public
10 * License version 2.1 as published by the Free Software Foundation
11 * (the "LGPL") or, at your option, under the terms of the Mozilla
12 * Public License Version 1.1 (the "MPL"). If you do not alter this
13 * notice, a recipient may use your version of this file under either
14 * the MPL or the LGPL.
16 * You should have received a copy of the LGPL along with this library
17 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 * You should have received a copy of the MPL along with this library
20 * in the file COPYING-MPL-1.1
22 * The contents of this file are subject to the Mozilla Public License
23 * Version 1.1 (the "License"); you may not use this file except in
24 * compliance with the License. You may obtain a copy of the License at
25 * http://www.mozilla.org/MPL/
27 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
28 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
29 * the specific language governing rights and limitations.
31 * The Original Code is the cairo graphics library.
33 * The Initial Developer of the Original Code is University of Southern
34 * California.
36 * Contributor(s):
37 * Carl D. Worth <cworth@cworth.org>
38 * Kristian Høgsberg <krh@redhat.com>
39 * Keith Packard <keithp@keithp.com>
40 * Adrian Johnson <ajohnson@redneon.com>
43 #define _BSD_SOURCE /* for snprintf(), strdup() */
44 #include "cairoint.h"
46 #if CAIRO_HAS_FONT_SUBSET
48 #include "cairo-scaled-font-subsets-private.h"
49 #include "cairo-user-font-private.h"
51 #define MAX_GLYPHS_PER_SIMPLE_FONT 256
52 #define MAX_GLYPHS_PER_COMPOSITE_FONT 65536
54 typedef enum {
55 CAIRO_SUBSETS_SCALED,
56 CAIRO_SUBSETS_SIMPLE,
57 CAIRO_SUBSETS_COMPOSITE
58 } cairo_subsets_type_t;
60 typedef enum {
61 CAIRO_SUBSETS_FOREACH_UNSCALED,
62 CAIRO_SUBSETS_FOREACH_SCALED,
63 CAIRO_SUBSETS_FOREACH_USER
64 } cairo_subsets_foreach_type_t;
66 typedef struct _cairo_sub_font {
67 cairo_hash_entry_t base;
69 cairo_bool_t is_scaled;
70 cairo_bool_t is_composite;
71 cairo_bool_t is_user;
72 cairo_scaled_font_subsets_t *parent;
73 cairo_scaled_font_t *scaled_font;
74 unsigned int font_id;
76 int current_subset;
77 int num_glyphs_in_current_subset;
78 int max_glyphs_per_subset;
80 cairo_hash_table_t *sub_font_glyphs;
81 struct _cairo_sub_font *next;
82 } cairo_sub_font_t;
84 struct _cairo_scaled_font_subsets {
85 cairo_subsets_type_t type;
87 int max_glyphs_per_unscaled_subset_used;
88 cairo_hash_table_t *unscaled_sub_fonts;
89 cairo_sub_font_t *unscaled_sub_fonts_list;
90 cairo_sub_font_t *unscaled_sub_fonts_list_end;
92 int max_glyphs_per_scaled_subset_used;
93 cairo_hash_table_t *scaled_sub_fonts;
94 cairo_sub_font_t *scaled_sub_fonts_list;
95 cairo_sub_font_t *scaled_sub_fonts_list_end;
97 int num_sub_fonts;
100 typedef struct _cairo_sub_font_glyph {
101 cairo_hash_entry_t base;
103 unsigned int subset_id;
104 unsigned int subset_glyph_index;
105 double x_advance;
106 double y_advance;
108 cairo_bool_t is_mapped;
109 uint32_t unicode;
110 char *utf8;
111 int utf8_len;
112 } cairo_sub_font_glyph_t;
114 typedef struct _cairo_sub_font_collection {
115 unsigned long *glyphs; /* scaled_font_glyph_index */
116 char **utf8;
117 unsigned int glyphs_size;
118 unsigned int max_glyph;
119 unsigned int num_glyphs;
121 unsigned int subset_id;
123 cairo_status_t status;
124 cairo_scaled_font_subset_callback_func_t font_subset_callback;
125 void *font_subset_callback_closure;
126 } cairo_sub_font_collection_t;
128 typedef struct _cairo_string_entry {
129 cairo_hash_entry_t base;
130 char *string;
131 } cairo_string_entry_t;
133 static cairo_status_t
134 _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font,
135 unsigned long scaled_font_glyph_index,
136 const char * utf8,
137 int utf8_len,
138 cairo_scaled_font_subsets_glyph_t *subset_glyph);
140 static void
141 _cairo_sub_font_glyph_init_key (cairo_sub_font_glyph_t *sub_font_glyph,
142 unsigned long scaled_font_glyph_index)
144 sub_font_glyph->base.hash = scaled_font_glyph_index;
147 static cairo_bool_t
148 _cairo_sub_font_glyphs_equal (const void *key_a, const void *key_b)
150 const cairo_sub_font_glyph_t *sub_font_glyph_a = key_a;
151 const cairo_sub_font_glyph_t *sub_font_glyph_b = key_b;
153 return sub_font_glyph_a->base.hash == sub_font_glyph_b->base.hash;
156 static cairo_sub_font_glyph_t *
157 _cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index,
158 unsigned int subset_id,
159 unsigned int subset_glyph_index,
160 double x_advance,
161 double y_advance)
163 cairo_sub_font_glyph_t *sub_font_glyph;
165 sub_font_glyph = malloc (sizeof (cairo_sub_font_glyph_t));
166 if (unlikely (sub_font_glyph == NULL)) {
167 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
168 return NULL;
171 _cairo_sub_font_glyph_init_key (sub_font_glyph, scaled_font_glyph_index);
172 sub_font_glyph->subset_id = subset_id;
173 sub_font_glyph->subset_glyph_index = subset_glyph_index;
174 sub_font_glyph->x_advance = x_advance;
175 sub_font_glyph->y_advance = y_advance;
176 sub_font_glyph->is_mapped = FALSE;
177 sub_font_glyph->unicode = -1;
178 sub_font_glyph->utf8 = NULL;
179 sub_font_glyph->utf8_len = 0;
181 return sub_font_glyph;
184 static void
185 _cairo_sub_font_glyph_destroy (cairo_sub_font_glyph_t *sub_font_glyph)
187 if (sub_font_glyph->utf8 != NULL)
188 free (sub_font_glyph->utf8);
190 free (sub_font_glyph);
193 static void
194 _cairo_sub_font_glyph_pluck (void *entry, void *closure)
196 cairo_sub_font_glyph_t *sub_font_glyph = entry;
197 cairo_hash_table_t *sub_font_glyphs = closure;
199 _cairo_hash_table_remove (sub_font_glyphs, &sub_font_glyph->base);
200 _cairo_sub_font_glyph_destroy (sub_font_glyph);
203 static void
204 _cairo_sub_font_glyph_collect (void *entry, void *closure)
206 cairo_sub_font_glyph_t *sub_font_glyph = entry;
207 cairo_sub_font_collection_t *collection = closure;
208 unsigned long scaled_font_glyph_index;
209 unsigned int subset_glyph_index;
211 if (sub_font_glyph->subset_id != collection->subset_id)
212 return;
214 scaled_font_glyph_index = sub_font_glyph->base.hash;
215 subset_glyph_index = sub_font_glyph->subset_glyph_index;
217 /* Ensure we don't exceed the allocated bounds. */
218 assert (subset_glyph_index < collection->glyphs_size);
220 collection->glyphs[subset_glyph_index] = scaled_font_glyph_index;
221 collection->utf8[subset_glyph_index] = sub_font_glyph->utf8;
222 if (subset_glyph_index > collection->max_glyph)
223 collection->max_glyph = subset_glyph_index;
225 collection->num_glyphs++;
228 static cairo_bool_t
229 _cairo_sub_fonts_equal (const void *key_a, const void *key_b)
231 const cairo_sub_font_t *sub_font_a = key_a;
232 const cairo_sub_font_t *sub_font_b = key_b;
233 cairo_scaled_font_t *a = sub_font_a->scaled_font;
234 cairo_scaled_font_t *b = sub_font_b->scaled_font;
236 if (sub_font_a->is_scaled)
237 return a == b;
238 else
239 return a->font_face == b->font_face || a->original_font_face == b->original_font_face;
242 static void
243 _cairo_sub_font_init_key (cairo_sub_font_t *sub_font,
244 cairo_scaled_font_t *scaled_font)
246 if (sub_font->is_scaled)
248 sub_font->base.hash = (unsigned long) scaled_font;
249 sub_font->scaled_font = scaled_font;
251 else
253 sub_font->base.hash = (unsigned long) scaled_font->font_face;
254 sub_font->scaled_font = scaled_font;
258 static cairo_status_t
259 _cairo_sub_font_create (cairo_scaled_font_subsets_t *parent,
260 cairo_scaled_font_t *scaled_font,
261 unsigned int font_id,
262 int max_glyphs_per_subset,
263 cairo_bool_t is_scaled,
264 cairo_bool_t is_composite,
265 cairo_sub_font_t **sub_font_out)
267 cairo_sub_font_t *sub_font;
268 cairo_status_t status;
269 cairo_scaled_font_subsets_glyph_t subset_glyph;
271 sub_font = malloc (sizeof (cairo_sub_font_t));
272 if (unlikely (sub_font == NULL))
273 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
275 sub_font->is_scaled = is_scaled;
276 sub_font->is_composite = is_composite;
277 sub_font->is_user = _cairo_font_face_is_user (scaled_font->font_face);
278 _cairo_sub_font_init_key (sub_font, scaled_font);
280 sub_font->parent = parent;
281 sub_font->scaled_font = scaled_font;
282 sub_font->font_id = font_id;
284 sub_font->current_subset = 0;
285 sub_font->num_glyphs_in_current_subset = 0;
286 sub_font->max_glyphs_per_subset = max_glyphs_per_subset;
288 sub_font->sub_font_glyphs = _cairo_hash_table_create (_cairo_sub_font_glyphs_equal);
289 if (unlikely (sub_font->sub_font_glyphs == NULL)) {
290 free (sub_font);
291 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
293 sub_font->next = NULL;
295 /* Reserve first glyph in subset for the .notdef glyph except for
296 * Type 3 fonts */
297 if (! _cairo_font_face_is_user (scaled_font->font_face)) {
298 status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &subset_glyph);
299 if (unlikely (status)) {
300 _cairo_hash_table_destroy (sub_font->sub_font_glyphs);
301 free (sub_font);
302 return status;
306 *sub_font_out = sub_font;
307 return CAIRO_STATUS_SUCCESS;
310 static void
311 _cairo_sub_font_destroy (cairo_sub_font_t *sub_font)
313 _cairo_hash_table_foreach (sub_font->sub_font_glyphs,
314 _cairo_sub_font_glyph_pluck,
315 sub_font->sub_font_glyphs);
316 _cairo_hash_table_destroy (sub_font->sub_font_glyphs);
317 cairo_scaled_font_destroy (sub_font->scaled_font);
318 free (sub_font);
321 static void
322 _cairo_sub_font_pluck (void *entry, void *closure)
324 cairo_sub_font_t *sub_font = entry;
325 cairo_hash_table_t *sub_fonts = closure;
327 _cairo_hash_table_remove (sub_fonts, &sub_font->base);
328 _cairo_sub_font_destroy (sub_font);
331 static cairo_status_t
332 _cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
333 cairo_scaled_font_t *scaled_font,
334 unsigned long scaled_font_glyph_index)
336 uint32_t unicode;
337 char buf[8];
338 int len;
339 cairo_status_t status;
341 /* Do a reverse lookup on the glyph index. unicode is -1 if the
342 * index could not be mapped to a unicode character. */
343 unicode = -1;
344 status = _cairo_truetype_index_to_ucs4 (scaled_font,
345 scaled_font_glyph_index,
346 &unicode);
347 if (_cairo_status_is_error (status))
348 return status;
350 if (unicode == (uint32_t)-1 && scaled_font->backend->index_to_ucs4) {
351 status = scaled_font->backend->index_to_ucs4 (scaled_font,
352 scaled_font_glyph_index,
353 &unicode);
354 if (unlikely (status))
355 return status;
358 sub_font_glyph->unicode = unicode;
359 sub_font_glyph->utf8 = NULL;
360 sub_font_glyph->utf8_len = 0;
361 if (unicode != (uint32_t) -1) {
362 len = _cairo_ucs4_to_utf8 (unicode, buf);
363 if (len > 0) {
364 sub_font_glyph->utf8 = malloc (len + 1);
365 if (unlikely (sub_font_glyph->utf8 == NULL))
366 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
368 memcpy (sub_font_glyph->utf8, buf, len);
369 sub_font_glyph->utf8[len] = 0;
370 sub_font_glyph->utf8_len = len;
374 return CAIRO_STATUS_SUCCESS;
377 static cairo_status_t
378 _cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
379 const char *utf8,
380 int utf8_len,
381 cairo_bool_t *is_mapped)
383 *is_mapped = FALSE;
385 if (utf8_len < 0)
386 return CAIRO_STATUS_SUCCESS;
388 if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0')
389 utf8_len--;
391 if (utf8 != NULL && utf8_len != 0) {
392 if (sub_font_glyph->utf8 != NULL) {
393 if (utf8_len == sub_font_glyph->utf8_len &&
394 memcmp (utf8, sub_font_glyph->utf8, utf8_len) == 0)
396 /* Requested utf8 mapping matches the existing mapping */
397 *is_mapped = TRUE;
399 } else {
400 /* No existing mapping. Use the requested mapping */
401 sub_font_glyph->utf8 = malloc (utf8_len + 1);
402 if (unlikely (sub_font_glyph->utf8 == NULL))
403 return CAIRO_STATUS_NO_MEMORY;
405 memcpy (sub_font_glyph->utf8, utf8, utf8_len);
406 sub_font_glyph->utf8[utf8_len] = 0;
407 sub_font_glyph->utf8_len = utf8_len;
408 *is_mapped = TRUE;
412 return CAIRO_STATUS_SUCCESS;
415 static cairo_int_status_t
416 _cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font,
417 unsigned long scaled_font_glyph_index,
418 const char *utf8,
419 int utf8_len,
420 cairo_scaled_font_subsets_glyph_t *subset_glyph)
422 cairo_sub_font_glyph_t key, *sub_font_glyph;
423 cairo_int_status_t status;
425 _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index);
426 sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs,
427 &key.base);
428 if (sub_font_glyph != NULL) {
429 subset_glyph->font_id = sub_font->font_id;
430 subset_glyph->subset_id = sub_font_glyph->subset_id;
431 subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index;
432 subset_glyph->is_scaled = sub_font->is_scaled;
433 subset_glyph->is_composite = sub_font->is_composite;
434 subset_glyph->x_advance = sub_font_glyph->x_advance;
435 subset_glyph->y_advance = sub_font_glyph->y_advance;
436 status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph,
437 utf8, utf8_len,
438 &subset_glyph->utf8_is_mapped);
439 subset_glyph->unicode = sub_font_glyph->unicode;
441 return status;
444 return CAIRO_INT_STATUS_UNSUPPORTED;
447 static cairo_status_t
448 _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font,
449 unsigned long scaled_font_glyph_index,
450 const char *utf8,
451 int utf8_len,
452 cairo_scaled_font_subsets_glyph_t *subset_glyph)
454 cairo_sub_font_glyph_t key, *sub_font_glyph;
455 cairo_status_t status;
457 _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index);
458 sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs,
459 &key.base);
460 if (sub_font_glyph == NULL) {
461 cairo_scaled_glyph_t *scaled_glyph;
463 if (sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset)
465 cairo_scaled_font_subsets_glyph_t tmp_subset_glyph;
467 sub_font->current_subset++;
468 sub_font->num_glyphs_in_current_subset = 0;
470 /* Reserve first glyph in subset for the .notdef glyph
471 * except for Type 3 fonts */
472 if (! _cairo_font_face_is_user (sub_font->scaled_font->font_face)) {
473 status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &tmp_subset_glyph);
474 if (unlikely (status))
475 return status;
479 _cairo_scaled_font_freeze_cache (sub_font->scaled_font);
480 status = _cairo_scaled_glyph_lookup (sub_font->scaled_font,
481 scaled_font_glyph_index,
482 CAIRO_SCALED_GLYPH_INFO_METRICS,
483 &scaled_glyph);
484 assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
485 if (unlikely (status)) {
486 _cairo_scaled_font_thaw_cache (sub_font->scaled_font);
487 return status;
490 sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index,
491 sub_font->current_subset,
492 sub_font->num_glyphs_in_current_subset,
493 scaled_glyph->metrics.x_advance,
494 scaled_glyph->metrics.y_advance);
495 _cairo_scaled_font_thaw_cache (sub_font->scaled_font);
497 if (unlikely (sub_font_glyph == NULL))
498 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
500 status = _cairo_sub_font_glyph_lookup_unicode (sub_font_glyph,
501 sub_font->scaled_font,
502 scaled_font_glyph_index);
503 if (unlikely (status)) {
504 _cairo_sub_font_glyph_destroy (sub_font_glyph);
505 return status;
508 status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base);
509 if (unlikely (status)) {
510 _cairo_sub_font_glyph_destroy (sub_font_glyph);
511 return status;
514 sub_font->num_glyphs_in_current_subset++;
516 if (sub_font->is_scaled) {
517 if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_scaled_subset_used)
518 sub_font->parent->max_glyphs_per_scaled_subset_used = sub_font->num_glyphs_in_current_subset;
519 } else {
520 if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_unscaled_subset_used)
521 sub_font->parent->max_glyphs_per_unscaled_subset_used = sub_font->num_glyphs_in_current_subset;
525 subset_glyph->font_id = sub_font->font_id;
526 subset_glyph->subset_id = sub_font_glyph->subset_id;
527 subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index;
528 subset_glyph->is_scaled = sub_font->is_scaled;
529 subset_glyph->is_composite = sub_font->is_composite;
530 subset_glyph->x_advance = sub_font_glyph->x_advance;
531 subset_glyph->y_advance = sub_font_glyph->y_advance;
532 status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph,
533 utf8, utf8_len,
534 &subset_glyph->utf8_is_mapped);
535 subset_glyph->unicode = sub_font_glyph->unicode;
537 return status;
540 static void
541 _cairo_sub_font_collect (void *entry, void *closure)
543 cairo_sub_font_t *sub_font = entry;
544 cairo_sub_font_collection_t *collection = closure;
545 cairo_scaled_font_subset_t subset;
546 int i;
547 unsigned int j;
549 if (collection->status)
550 return;
552 collection->status = sub_font->scaled_font->status;
553 if (collection->status)
554 return;
556 for (i = 0; i <= sub_font->current_subset; i++) {
557 collection->subset_id = i;
558 collection->num_glyphs = 0;
559 collection->max_glyph = 0;
561 _cairo_hash_table_foreach (sub_font->sub_font_glyphs,
562 _cairo_sub_font_glyph_collect, collection);
563 if (collection->status)
564 break;
565 if (collection->num_glyphs == 0)
566 continue;
568 /* Ensure the resulting array has no uninitialized holes */
569 assert (collection->num_glyphs == collection->max_glyph + 1);
571 subset.scaled_font = sub_font->scaled_font;
572 subset.is_composite = sub_font->is_composite;
573 subset.font_id = sub_font->font_id;
574 subset.subset_id = i;
575 subset.glyphs = collection->glyphs;
576 subset.utf8 = collection->utf8;
577 subset.num_glyphs = collection->num_glyphs;
578 subset.glyph_names = NULL;
579 /* No need to check for out of memory here. If to_unicode is NULL, the PDF
580 * surface does not emit an ToUnicode stream */
581 subset.to_unicode = _cairo_malloc_ab (collection->num_glyphs, sizeof (unsigned long));
582 if (subset.to_unicode) {
583 for (j = 0; j < collection->num_glyphs; j++) {
584 /* default unicode character required when mapping fails */
585 subset.to_unicode[j] = 0xfffd;
588 collection->status = (collection->font_subset_callback) (&subset,
589 collection->font_subset_callback_closure);
591 if (subset.to_unicode != NULL)
592 free (subset.to_unicode);
594 if (subset.glyph_names != NULL) {
595 for (j = 0; j < collection->num_glyphs; j++)
596 free (subset.glyph_names[j]);
597 free (subset.glyph_names);
600 if (collection->status)
601 break;
605 static cairo_scaled_font_subsets_t *
606 _cairo_scaled_font_subsets_create_internal (cairo_subsets_type_t type)
608 cairo_scaled_font_subsets_t *subsets;
610 subsets = malloc (sizeof (cairo_scaled_font_subsets_t));
611 if (unlikely (subsets == NULL)) {
612 _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
613 return NULL;
616 subsets->type = type;
617 subsets->max_glyphs_per_unscaled_subset_used = 0;
618 subsets->max_glyphs_per_scaled_subset_used = 0;
619 subsets->num_sub_fonts = 0;
621 subsets->unscaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal);
622 if (! subsets->unscaled_sub_fonts) {
623 free (subsets);
624 return NULL;
626 subsets->unscaled_sub_fonts_list = NULL;
627 subsets->unscaled_sub_fonts_list_end = NULL;
629 subsets->scaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal);
630 if (! subsets->scaled_sub_fonts) {
631 _cairo_hash_table_destroy (subsets->unscaled_sub_fonts);
632 free (subsets);
633 return NULL;
635 subsets->scaled_sub_fonts_list = NULL;
636 subsets->scaled_sub_fonts_list_end = NULL;
638 return subsets;
641 cairo_scaled_font_subsets_t *
642 _cairo_scaled_font_subsets_create_scaled (void)
644 return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SCALED);
647 cairo_scaled_font_subsets_t *
648 _cairo_scaled_font_subsets_create_simple (void)
650 return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SIMPLE);
653 cairo_scaled_font_subsets_t *
654 _cairo_scaled_font_subsets_create_composite (void)
656 return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_COMPOSITE);
659 void
660 _cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *subsets)
662 _cairo_hash_table_foreach (subsets->scaled_sub_fonts, _cairo_sub_font_pluck, subsets->scaled_sub_fonts);
663 _cairo_hash_table_destroy (subsets->scaled_sub_fonts);
665 _cairo_hash_table_foreach (subsets->unscaled_sub_fonts, _cairo_sub_font_pluck, subsets->unscaled_sub_fonts);
666 _cairo_hash_table_destroy (subsets->unscaled_sub_fonts);
668 free (subsets);
671 cairo_status_t
672 _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets,
673 cairo_scaled_font_t *scaled_font,
674 unsigned long scaled_font_glyph_index,
675 const char * utf8,
676 int utf8_len,
677 cairo_scaled_font_subsets_glyph_t *subset_glyph)
679 cairo_sub_font_t key, *sub_font;
680 cairo_scaled_glyph_t *scaled_glyph;
681 cairo_font_face_t *font_face;
682 cairo_matrix_t identity;
683 cairo_font_options_t font_options;
684 cairo_scaled_font_t *unscaled_font;
685 cairo_status_t status;
686 int max_glyphs;
687 cairo_bool_t type1_font;
689 /* Lookup glyph in unscaled subsets */
690 if (subsets->type != CAIRO_SUBSETS_SCALED) {
691 key.is_scaled = FALSE;
692 _cairo_sub_font_init_key (&key, scaled_font);
693 sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts,
694 &key.base);
695 if (sub_font != NULL) {
696 status = _cairo_sub_font_lookup_glyph (sub_font,
697 scaled_font_glyph_index,
698 utf8, utf8_len,
699 subset_glyph);
700 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
701 return status;
705 /* Lookup glyph in scaled subsets */
706 key.is_scaled = TRUE;
707 _cairo_sub_font_init_key (&key, scaled_font);
708 sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts,
709 &key.base);
710 if (sub_font != NULL) {
711 status = _cairo_sub_font_lookup_glyph (sub_font,
712 scaled_font_glyph_index,
713 utf8, utf8_len,
714 subset_glyph);
715 if (status != CAIRO_INT_STATUS_UNSUPPORTED)
716 return status;
719 /* Glyph not found. Determine whether the glyph is outline or
720 * bitmap and add to the appropriate subset.
722 * glyph_index 0 (the .notdef glyph) is a special case. Some fonts
723 * will return CAIRO_INT_STATUS_UNSUPPORTED when doing a
724 * _scaled_glyph_lookup(_GLYPH_INFO_PATH). Type1-fallback creates
725 * empty glyphs in this case so we can put the glyph in a unscaled
726 * subset. */
727 if (scaled_font_glyph_index == 0 ||
728 _cairo_font_face_is_user (scaled_font->font_face)) {
729 status = CAIRO_STATUS_SUCCESS;
730 } else {
731 _cairo_scaled_font_freeze_cache (scaled_font);
732 status = _cairo_scaled_glyph_lookup (scaled_font,
733 scaled_font_glyph_index,
734 CAIRO_SCALED_GLYPH_INFO_PATH,
735 &scaled_glyph);
736 _cairo_scaled_font_thaw_cache (scaled_font);
738 if (_cairo_status_is_error (status))
739 return status;
741 if (status == CAIRO_STATUS_SUCCESS &&
742 subsets->type != CAIRO_SUBSETS_SCALED &&
743 ! _cairo_font_face_is_user (scaled_font->font_face))
745 /* Path available. Add to unscaled subset. */
746 key.is_scaled = FALSE;
747 _cairo_sub_font_init_key (&key, scaled_font);
748 sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts,
749 &key.base);
750 if (sub_font == NULL) {
751 font_face = cairo_scaled_font_get_font_face (scaled_font);
752 cairo_matrix_init_identity (&identity);
753 _cairo_font_options_init_default (&font_options);
754 cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE);
755 cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF);
756 unscaled_font = cairo_scaled_font_create (font_face,
757 &identity,
758 &identity,
759 &font_options);
760 if (unlikely (unscaled_font->status))
761 return unscaled_font->status;
763 subset_glyph->is_scaled = FALSE;
764 type1_font = FALSE;
765 #if CAIRO_HAS_FT_FONT
766 type1_font = _cairo_type1_scaled_font_is_type1 (unscaled_font);
767 #endif
768 if (subsets->type == CAIRO_SUBSETS_COMPOSITE && !type1_font) {
769 max_glyphs = MAX_GLYPHS_PER_COMPOSITE_FONT;
770 subset_glyph->is_composite = TRUE;
771 } else {
772 max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT;
773 subset_glyph->is_composite = FALSE;
776 status = _cairo_sub_font_create (subsets,
777 unscaled_font,
778 subsets->num_sub_fonts,
779 max_glyphs,
780 subset_glyph->is_scaled,
781 subset_glyph->is_composite,
782 &sub_font);
784 if (unlikely (status)) {
785 cairo_scaled_font_destroy (unscaled_font);
786 return status;
789 status = _cairo_hash_table_insert (subsets->unscaled_sub_fonts,
790 &sub_font->base);
792 if (unlikely (status)) {
793 _cairo_sub_font_destroy (sub_font);
794 return status;
796 if (!subsets->unscaled_sub_fonts_list)
797 subsets->unscaled_sub_fonts_list = sub_font;
798 else
799 subsets->unscaled_sub_fonts_list_end->next = sub_font;
800 subsets->unscaled_sub_fonts_list_end = sub_font;
801 subsets->num_sub_fonts++;
803 } else {
804 /* No path available. Add to scaled subset. */
805 key.is_scaled = TRUE;
806 _cairo_sub_font_init_key (&key, scaled_font);
807 sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts,
808 &key.base);
809 if (sub_font == NULL) {
810 subset_glyph->is_scaled = TRUE;
811 subset_glyph->is_composite = FALSE;
812 if (subsets->type == CAIRO_SUBSETS_SCALED)
813 max_glyphs = INT_MAX;
814 else
815 max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT;
817 status = _cairo_sub_font_create (subsets,
818 cairo_scaled_font_reference (scaled_font),
819 subsets->num_sub_fonts,
820 max_glyphs,
821 subset_glyph->is_scaled,
822 subset_glyph->is_composite,
823 &sub_font);
824 if (unlikely (status)) {
825 cairo_scaled_font_destroy (scaled_font);
826 return status;
829 status = _cairo_hash_table_insert (subsets->scaled_sub_fonts,
830 &sub_font->base);
831 if (unlikely (status)) {
832 _cairo_sub_font_destroy (sub_font);
833 return status;
835 if (!subsets->scaled_sub_fonts_list)
836 subsets->scaled_sub_fonts_list = sub_font;
837 else
838 subsets->scaled_sub_fonts_list_end->next = sub_font;
839 subsets->scaled_sub_fonts_list_end = sub_font;
840 subsets->num_sub_fonts++;
844 return _cairo_sub_font_map_glyph (sub_font,
845 scaled_font_glyph_index,
846 utf8, utf8_len,
847 subset_glyph);
850 static cairo_status_t
851 _cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t *font_subsets,
852 cairo_scaled_font_subset_callback_func_t font_subset_callback,
853 void *closure,
854 cairo_subsets_foreach_type_t type)
856 cairo_sub_font_collection_t collection;
857 cairo_sub_font_t *sub_font;
858 cairo_bool_t is_scaled, is_user;
860 is_scaled = FALSE;
861 is_user = FALSE;
863 if (type == CAIRO_SUBSETS_FOREACH_USER)
864 is_user = TRUE;
866 if (type == CAIRO_SUBSETS_FOREACH_SCALED ||
867 type == CAIRO_SUBSETS_FOREACH_USER)
869 is_scaled = TRUE;
872 if (is_scaled)
873 collection.glyphs_size = font_subsets->max_glyphs_per_scaled_subset_used;
874 else
875 collection.glyphs_size = font_subsets->max_glyphs_per_unscaled_subset_used;
877 if (! collection.glyphs_size)
878 return CAIRO_STATUS_SUCCESS;
880 collection.glyphs = _cairo_malloc_ab (collection.glyphs_size, sizeof(unsigned long));
881 collection.utf8 = _cairo_malloc_ab (collection.glyphs_size, sizeof(char *));
882 if (unlikely (collection.glyphs == NULL || collection.utf8 == NULL)) {
883 if (collection.glyphs != NULL)
884 free (collection.glyphs);
885 if (collection.utf8 != NULL)
886 free (collection.utf8);
888 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
891 collection.font_subset_callback = font_subset_callback;
892 collection.font_subset_callback_closure = closure;
893 collection.status = CAIRO_STATUS_SUCCESS;
895 if (is_scaled)
896 sub_font = font_subsets->scaled_sub_fonts_list;
897 else
898 sub_font = font_subsets->unscaled_sub_fonts_list;
900 while (sub_font) {
901 if (sub_font->is_user == is_user)
902 _cairo_sub_font_collect (sub_font, &collection);
904 sub_font = sub_font->next;
906 free (collection.utf8);
907 free (collection.glyphs);
909 return collection.status;
912 cairo_status_t
913 _cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t *font_subsets,
914 cairo_scaled_font_subset_callback_func_t font_subset_callback,
915 void *closure)
917 return _cairo_scaled_font_subsets_foreach_internal (font_subsets,
918 font_subset_callback,
919 closure,
920 CAIRO_SUBSETS_FOREACH_SCALED);
923 cairo_status_t
924 _cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *font_subsets,
925 cairo_scaled_font_subset_callback_func_t font_subset_callback,
926 void *closure)
928 return _cairo_scaled_font_subsets_foreach_internal (font_subsets,
929 font_subset_callback,
930 closure,
931 CAIRO_SUBSETS_FOREACH_UNSCALED);
934 cairo_status_t
935 _cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets,
936 cairo_scaled_font_subset_callback_func_t font_subset_callback,
937 void *closure)
939 return _cairo_scaled_font_subsets_foreach_internal (font_subsets,
940 font_subset_callback,
941 closure,
942 CAIRO_SUBSETS_FOREACH_USER);
945 static cairo_bool_t
946 _cairo_string_equal (const void *key_a, const void *key_b)
948 const cairo_string_entry_t *a = key_a;
949 const cairo_string_entry_t *b = key_b;
951 if (strcmp (a->string, b->string) == 0)
952 return TRUE;
953 else
954 return FALSE;
957 static void
958 _cairo_string_init_key (cairo_string_entry_t *key, char *s)
960 unsigned long sum = 0;
961 unsigned int i;
963 for (i = 0; i < strlen(s); i++)
964 sum += s[i];
965 key->base.hash = sum;
966 key->string = s;
969 static cairo_status_t
970 create_string_entry (char *s, cairo_string_entry_t **entry)
972 *entry = malloc (sizeof (cairo_string_entry_t));
973 if (unlikely (*entry == NULL))
974 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
976 _cairo_string_init_key (*entry, s);
978 return CAIRO_STATUS_SUCCESS;
981 static void
982 _pluck_entry (void *entry, void *closure)
984 _cairo_hash_table_remove (closure, entry);
985 free (entry);
988 cairo_int_status_t
989 _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset)
991 unsigned int i;
992 cairo_hash_table_t *names;
993 cairo_string_entry_t key, *entry;
994 char buf[30];
995 char *utf8;
996 uint16_t *utf16;
997 int utf16_len;
998 cairo_status_t status = CAIRO_STATUS_SUCCESS;
1000 names = _cairo_hash_table_create (_cairo_string_equal);
1001 if (unlikely (names == NULL))
1002 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1004 subset->glyph_names = calloc (subset->num_glyphs, sizeof (char *));
1005 if (unlikely (subset->glyph_names == NULL)) {
1006 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1007 goto CLEANUP_HASH;
1010 i = 0;
1011 if (! _cairo_font_face_is_user (subset->scaled_font->font_face)) {
1012 subset->glyph_names[0] = strdup (".notdef");
1013 if (unlikely (subset->glyph_names[0] == NULL)) {
1014 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1015 goto CLEANUP_HASH;
1018 status = create_string_entry (subset->glyph_names[0], &entry);
1019 if (unlikely (status))
1020 goto CLEANUP_HASH;
1022 status = _cairo_hash_table_insert (names, &entry->base);
1023 if (unlikely (status)) {
1024 free (entry);
1025 goto CLEANUP_HASH;
1027 i++;
1030 for (; i < subset->num_glyphs; i++) {
1031 utf8 = subset->utf8[i];
1032 utf16 = NULL;
1033 utf16_len = 0;
1034 if (utf8 && *utf8) {
1035 status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
1036 if (unlikely (status))
1037 goto CLEANUP_HASH;
1040 if (utf16_len == 1) {
1041 snprintf (buf, sizeof (buf), "uni%04X", (int) utf16[0]);
1042 _cairo_string_init_key (&key, buf);
1043 entry = _cairo_hash_table_lookup (names, &key.base);
1044 if (entry != NULL)
1045 snprintf (buf, sizeof (buf), "g%d", i);
1046 } else {
1047 snprintf (buf, sizeof (buf), "g%d", i);
1049 if (utf16)
1050 free (utf16);
1052 subset->glyph_names[i] = strdup (buf);
1053 if (unlikely (subset->glyph_names[i] == NULL)) {
1054 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1055 goto CLEANUP_HASH;
1058 status = create_string_entry (subset->glyph_names[i], &entry);
1059 if (unlikely (status))
1060 goto CLEANUP_HASH;
1062 status = _cairo_hash_table_insert (names, &entry->base);
1063 if (unlikely (status)) {
1064 free (entry);
1065 goto CLEANUP_HASH;
1069 CLEANUP_HASH:
1070 _cairo_hash_table_foreach (names, _pluck_entry, names);
1071 _cairo_hash_table_destroy (names);
1073 if (likely (status == CAIRO_STATUS_SUCCESS))
1074 return CAIRO_STATUS_SUCCESS;
1076 if (subset->glyph_names != NULL) {
1077 for (i = 0; i < subset->num_glyphs; i++) {
1078 if (subset->glyph_names[i] != NULL)
1079 free (subset->glyph_names[i]);
1082 free (subset->glyph_names);
1083 subset->glyph_names = NULL;
1086 return status;
1089 #endif /* CAIRO_HAS_FONT_SUBSET */