diff --git a/ROADMAP b/ROADMAP index 1b1204fbe..50dbb680c 100644 --- a/ROADMAP +++ b/ROADMAP @@ -71,6 +71,7 @@ We don't expect to release without these being complete. cairo_set_line_width should immediately use CTM _transform_glyph_bitmap http://lists.freedesktop.org/archives/cairo/2005-October/005564.html 6759 fontconfig option AntiAlias doesn't work in cairo 1.1.2 + PS/PDF subsetting code needs bitmapped-glyph support Fix memory leaks 1. Ensure 'make check-valgrind' passes with no leaks diff --git a/src/Makefile.am b/src/Makefile.am index 118da2d67..6390d3290 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ if CAIRO_HAS_PS_SURFACE libcairo_ps_headers = cairo-ps.h -libcairo_ps_sources = cairo-ps-surface.c cairo-ps-font.c cairo-ps-font-private.h +libcairo_ps_sources = cairo-ps-surface.c cairo-scaled-font-subsets.c cairo-scaled-font-subsets-private.h libcairo_font_subset_sources = cairo-font-subset.c cairo-font-subset-private.h endif diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index e94ebe20b..eea89be02 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -39,8 +39,7 @@ #include "cairoint.h" #include "cairo-ps.h" -#include "cairo-ps-font-private.h" -#include "cairo-font-subset-private.h" +#include "cairo-scaled-font-subsets-private.h" #include "cairo-paginated-surface-private.h" #include "cairo-meta-surface-private.h" #include "cairo-ft-private.h" @@ -75,7 +74,7 @@ typedef struct cairo_ps_surface { cairo_paginated_mode_t paginated_mode; - cairo_hash_table_t *fonts; + cairo_scaled_font_subsets_t *font_subsets; unsigned int max_font; cairo_array_t dsc_header_comments; @@ -86,7 +85,8 @@ typedef struct cairo_ps_surface { } cairo_ps_surface_t; -#define PS_SURFACE_DPI_DEFAULT 300.0 +#define PS_SURFACE_DPI_DEFAULT 300.0 +#define PS_SURFACE_MAX_GLYPHS_PER_FONT 256 static cairo_status_t _cairo_ps_surface_path_move_to (void *closure, cairo_point_t *point) @@ -213,51 +213,19 @@ _cairo_ps_surface_emit_header (cairo_ps_surface_t *surface) } static cairo_status_t -_cairo_ps_surface_find_ps_font (cairo_ps_surface_t *surface, - cairo_scaled_font_t *scaled_font, - cairo_ps_font_t **result) -{ - cairo_ps_font_t key; - cairo_ps_font_t *ps_font; - cairo_status_t status; - - _cairo_ps_font_key_init (&key, scaled_font); - if (!_cairo_hash_table_lookup (surface->fonts, &key.base, - (cairo_hash_entry_t **) &ps_font)) - { - ps_font = _cairo_ps_font_create (scaled_font, surface->max_font++); - if (!ps_font) - return CAIRO_STATUS_NO_MEMORY; - status = _cairo_hash_table_insert (surface->fonts, - &ps_font->base); - if (status) - return status; - } - *result = ps_font; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_ps_surface_destroy_ps_font (cairo_ps_surface_t *surface, - cairo_ps_font_t *ps_font) -{ - _cairo_hash_table_remove (surface->fonts, &ps_font->base); - _cairo_ps_font_destroy (ps_font); -} - -static cairo_status_t -_cairo_ps_surface_emit_glyph (cairo_ps_surface_t *surface, - cairo_ps_font_t *ps_font, - cairo_ps_glyph_t *ps_glyph) +_cairo_ps_surface_emit_glyph (cairo_ps_surface_t *surface, + cairo_scaled_font_t *scaled_font, + unsigned long scaled_font_glyph_index, + unsigned int subset_glyph_index) { cairo_scaled_glyph_t *scaled_glyph; cairo_status_t status; - + _cairo_output_stream_printf (surface->final_stream, - "\t\t{ %% %d\n", ps_glyph->output_glyph); - status = _cairo_scaled_glyph_lookup (ps_font->scaled_font, - ps_glyph->base.hash, + "\t\t{ %% %d\n", subset_glyph_index); + + status = _cairo_scaled_glyph_lookup (scaled_font, + scaled_font_glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS| CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); @@ -265,8 +233,8 @@ _cairo_ps_surface_emit_glyph (cairo_ps_surface_t *surface, * If that fails, try again but ask for an image instead */ if (status) - status = _cairo_scaled_glyph_lookup (ps_font->scaled_font, - ps_glyph->base.hash, + status = _cairo_scaled_glyph_lookup (scaled_font, + scaled_font_glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS| CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); @@ -274,6 +242,10 @@ _cairo_ps_surface_emit_glyph (cairo_ps_surface_t *surface, _cairo_output_stream_printf (surface->final_stream, "\t\t}\n"); return status; } + + /* XXX: Need to actually use the image not the path if that's all + * we could get... */ + _cairo_output_stream_printf (surface->final_stream, "%f %f %f %f 0 0 setcachedevice\n", _cairo_fixed_to_double (scaled_glyph->bbox.p1.x), @@ -297,70 +269,57 @@ _cairo_ps_surface_emit_glyph (cairo_ps_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -static void -_cairo_ps_surface_emit_font (void *entry, void *closure) +static cairo_status_t +_cairo_ps_surface_emit_font_subset (cairo_scaled_font_subset_t *font_subset, + void *closure) { - cairo_ps_font_t *ps_font = entry; cairo_ps_surface_t *surface = closure; - cairo_ps_font_glyph_select_t glyph_select; - cairo_ps_glyph_t *ps_glyphs[256], *ps_glyph; - int glyph, numglyph; - int subfont, nsubfont; + int i; _cairo_output_stream_printf (surface->final_stream, - "%% _cairo_ps_surface_emit_font\n"); - nsubfont = (ps_font->max_glyph >> 8) + 1; - for (subfont = 0; subfont < nsubfont; subfont++) { - _cairo_output_stream_printf (surface->final_stream, - "/CairoFont-%d-%d <<\n", - ps_font->output_font, - subfont); - memset (ps_glyphs, '\0', sizeof (ps_glyphs)); - glyph_select.glyphs = ps_glyphs; - glyph_select.numglyph = 0; - glyph_select.subfont = subfont; - _cairo_hash_table_foreach (ps_font->glyphs, - _cairo_ps_font_select_glyphs, - &glyph_select); - _cairo_output_stream_printf (surface->final_stream, - "\t/FontType\t3\n" - "\t/FontMatrix\t[1 0 0 1 0 0]\n" - "\t/Encoding\t[0]\n" - "\t/FontBBox\t[0 0 10 10]\n" - "\t/Glyphs [\n"); - numglyph = glyph_select.numglyph; - for (glyph = 0; glyph < numglyph; glyph++) { - ps_glyph = ps_glyphs[glyph]; - if (ps_glyph) { - _cairo_ps_surface_emit_glyph (surface, - ps_font, - ps_glyph); - } else { - _cairo_output_stream_printf (surface->final_stream, - "\t\t{ } %% %d\n", glyph); - } - _cairo_ps_font_destroy_glyph (ps_font, ps_glyph); - } - _cairo_output_stream_printf (surface->final_stream, - "\t]\n" - "\t/BuildChar {\n" - "\t\texch /Glyphs get\n" - "\t\texch get exec\n" - "\t}\n" - ">> definefont pop\n"); + "%% _cairo_ps_surface_emit_font_subset\n"); + + _cairo_output_stream_printf (surface->final_stream, + "/CairoFont-%d-%d <<\n", + font_subset->font_id, + font_subset->subset_id); + + _cairo_output_stream_printf (surface->final_stream, + "\t/FontType\t3\n" + "\t/FontMatrix\t[1 0 0 1 0 0]\n" + "\t/Encoding\t[0]\n" + "\t/FontBBox\t[0 0 10 10]\n" + "\t/Glyphs [\n"); + + for (i = 0; i < font_subset->num_glyphs; i++) { + _cairo_ps_surface_emit_glyph (surface, + font_subset->scaled_font, + font_subset->glyphs[i], i); } - _cairo_ps_surface_destroy_ps_font (surface, ps_font); + + _cairo_output_stream_printf (surface->final_stream, + "\t]\n" + "\t/BuildChar {\n" + "\t\texch /Glyphs get\n" + "\t\texch get exec\n" + "\t}\n" + ">> definefont pop\n"); + + return _cairo_output_stream_get_status (surface->final_stream); } static void -_cairo_ps_surface_emit_fonts (cairo_ps_surface_t *surface) +_cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface) { - _cairo_hash_table_foreach (surface->fonts, - _cairo_ps_surface_emit_font, - surface); - _cairo_hash_table_destroy (surface->fonts); - surface->fonts = NULL; + _cairo_output_stream_printf (surface->final_stream, + "%% _cairo_ps_surface_emit_font_subsets\n"); + + _cairo_scaled_font_subsets_foreach (surface->font_subsets, + _cairo_ps_surface_emit_font_subset, + surface); + _cairo_scaled_font_subsets_destroy (surface->font_subsets); + surface->font_subsets = NULL; } static void @@ -388,12 +347,12 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, double height) { cairo_status_t status; - cairo_ps_surface_t *surface; + cairo_ps_surface_t *surface = NULL; surface = malloc (sizeof (cairo_ps_surface_t)); if (surface == NULL) { - _cairo_error (CAIRO_STATUS_NO_MEMORY); - return (cairo_surface_t*) &_cairo_surface_nil; + status = CAIRO_STATUS_NO_MEMORY; + goto CLEANUP; } _cairo_surface_init (&surface->base, &cairo_ps_surface_backend); @@ -401,23 +360,18 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, surface->final_stream = stream; surface->tmpfile = tmpfile (); + if (surface->tmpfile == NULL) + goto CLEANUP_SURFACE; + surface->stream = _cairo_output_stream_create_for_file (surface->tmpfile); status = _cairo_output_stream_get_status (surface->stream); - if (status) { - fclose (surface->tmpfile); - free (surface); - _cairo_error (status); - return (cairo_surface_t*) &_cairo_surface_nil; - } + if (status) + goto CLEANUP_TMPFILE; + + surface->font_subsets = _cairo_scaled_font_subsets_create (PS_SURFACE_MAX_GLYPHS_PER_FONT); + if (! surface->font_subsets) + goto CLEANUP_OUTPUT_STREAM; - surface->fonts = _cairo_hash_table_create (_cairo_ps_font_equal); - if (!surface->fonts) { - _cairo_output_stream_destroy (surface->stream); - fclose (surface->tmpfile); - free (surface); - _cairo_error (CAIRO_STATUS_NO_MEMORY); - return (cairo_surface_t*) &_cairo_surface_nil; - } surface->max_font = 0; surface->width = width; @@ -440,6 +394,17 @@ _cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, CAIRO_CONTENT_COLOR_ALPHA, width, height, &cairo_ps_surface_paginated_backend); + + + CLEANUP_OUTPUT_STREAM: + _cairo_output_stream_destroy (surface->stream); + CLEANUP_TMPFILE: + fclose (surface->tmpfile); + CLEANUP_SURFACE: + free (surface); + CLEANUP: + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return (cairo_surface_t*) &_cairo_surface_nil; } /** @@ -955,7 +920,7 @@ _cairo_ps_surface_finish (void *abstract_surface) _cairo_ps_surface_emit_header (surface); - _cairo_ps_surface_emit_fonts (surface); + _cairo_ps_surface_emit_font_subsets (surface); _cairo_ps_surface_emit_body (surface); @@ -1773,9 +1738,8 @@ _cairo_ps_surface_show_glyphs (void *abstract_surface, cairo_int_status_t status; cairo_path_fixed_t *path; int i; - int cur_subfont = -1, subfont; - cairo_ps_font_t *ps_font; - cairo_ps_glyph_t *ps_glyph; + int current_sub_font_id = -1; + unsigned int font_id, sub_font_id, sub_font_glyph_index; if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) return _analyze_operation (surface, op, source); @@ -1784,34 +1748,30 @@ _cairo_ps_surface_show_glyphs (void *abstract_surface, _cairo_output_stream_printf (stream, "%% _cairo_ps_surface_show_glyphs\n"); - status = _cairo_ps_surface_find_ps_font (surface, scaled_font, &ps_font); - if (status) - goto fallback; if (num_glyphs) emit_pattern (surface, source); for (i = 0; i < num_glyphs; i++) { - status = _cairo_ps_font_find_glyph (ps_font, scaled_font, - glyphs[i].index, &ps_glyph); + status = _cairo_scaled_font_subsets_map_glyph (surface->font_subsets, + scaled_font, glyphs[i].index, + &font_id, &sub_font_id, &sub_font_glyph_index); if (status) { glyphs += i; num_glyphs -= i; goto fallback; } - subfont = ps_glyph->output_glyph >> 8; - if (subfont != cur_subfont) { + if (sub_font_id != current_sub_font_id) { _cairo_output_stream_printf (surface->stream, "/CairoFont-%d-%d 1 selectfont\n", - ps_font->output_font, - subfont); - cur_subfont = subfont; + font_id, sub_font_id); + current_sub_font_id = sub_font_id; } _cairo_output_stream_printf (surface->stream, "%f %f M <%c%c> S\n", glyphs[i].x, glyphs[i].y, - hex_digit (ps_glyph->output_glyph >> 4), - hex_digit (ps_glyph->output_glyph)); + hex_digit (sub_font_glyph_index >> 4), + hex_digit (sub_font_glyph_index)); } return CAIRO_STATUS_SUCCESS; diff --git a/src/cairo-scaled-font-subsets-private.h b/src/cairo-scaled-font-subsets-private.h index 428217937..2c5c0dc84 100644 --- a/src/cairo-scaled-font-subsets-private.h +++ b/src/cairo-scaled-font-subsets-private.h @@ -1,8 +1,5 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2003 University of Southern California - * Copyright © 2005 Red Hat, Inc - * Copyright © 2006 Keith Packard * Copyright © 2006 Red Hat, Inc * * This library is free software; you can redistribute it and/or @@ -35,67 +32,48 @@ * * Contributor(s): * Carl D. Worth - * Kristian Høgsberg - * Keith Packard */ -/* - * Type1 and Type3 PS fonts can hold only 256 glyphs. - * - * XXX Work around this by placing each set of 256 glyphs in a separate - * font. No separate data structure is kept for this; the font name is - * generated from all but the low 8 bits of the output glyph id. - */ - -#ifndef CAIRO_PS_FONT_PRIVATE_H -#define CAIRO_PS_FONT_PRIVATE_H +#ifndef CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H +#define CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H #include "cairoint.h" -typedef struct cairo_ps_glyph { - cairo_hash_entry_t base; /* font glyph index */ - unsigned int output_glyph; /* PS sub-font glyph index */ -} cairo_ps_glyph_t; +typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t; -typedef struct cairo_ps_font { - cairo_hash_entry_t base; - cairo_scaled_font_t *scaled_font; - unsigned int output_font; - cairo_hash_table_t *glyphs; - unsigned int max_glyph; -} cairo_ps_font_t; +typedef struct _cairo_scaled_font_subset { + cairo_scaled_font_t *scaled_font; + unsigned int font_id; + unsigned int subset_id; -typedef struct _cairo_ps_font_glyph_select { - cairo_ps_glyph_t **glyphs; - int subfont; - int numglyph; -} cairo_ps_font_glyph_select_t; + /* Index of glyphs array is subset_glyph_index. + * Value of glyphs array is scaled_font_glyph_index. + */ + unsigned long *glyphs; + int num_glyphs; +} cairo_scaled_font_subset_t; -cairo_private cairo_ps_font_t * -_cairo_ps_font_create (cairo_scaled_font_t *scaled_font, - unsigned int id); +cairo_private cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create (int max_glyphs_per_subset); cairo_private void -_cairo_ps_font_destroy (cairo_ps_font_t *ps_font); - -cairo_private void -_cairo_ps_font_key_init (cairo_ps_font_t *ps_font, - cairo_scaled_font_t *scaled_font); - -cairo_private void -_cairo_ps_font_select_glyphs (void *entry, void *closure); - -cairo_private void -_cairo_ps_font_destroy_glyph (cairo_ps_font_t *ps_font, - cairo_ps_glyph_t *ps_glyph); - -cairo_private cairo_bool_t -_cairo_ps_font_equal (const void *key_a, const void *key_b); +_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *font_subsets); cairo_private cairo_status_t -_cairo_ps_font_find_glyph (cairo_ps_font_t *font, - cairo_scaled_font_t *scaled_font, - unsigned long index, - cairo_ps_glyph_t **result); +_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_t *scaled_font, + unsigned long scaled_font_glyph_index, + unsigned int *font_id, + unsigned int *subset_id, + unsigned int *subset_glyph_index); -#endif /* CAIRO_PS_FONT_PRIVATE_H */ +typedef cairo_status_t +(*cairo_scaled_font_subset_callback_func_t) (cairo_scaled_font_subset_t *font_subset, + void *closure); + +cairo_private cairo_status_t +_cairo_scaled_font_subsets_foreach (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure); + +#endif /* CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H */ diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c index 353af5cf9..12eda92ec 100644 --- a/src/cairo-scaled-font-subsets.c +++ b/src/cairo-scaled-font-subsets.c @@ -39,143 +39,345 @@ * Keith Packard */ + #include "cairoint.h" -#include "cairo-ps-font-private.h" +#include "cairo-scaled-font-subsets-private.h" + +struct _cairo_scaled_font_subsets { + int max_glyphs_per_subset_limit; + int max_glyphs_per_subset_used; + int num_sub_fonts; + + cairo_hash_table_t *sub_fonts; +}; + +typedef struct _cairo_sub_font { + cairo_hash_entry_t base; + + cairo_scaled_font_subsets_t *parent; + cairo_scaled_font_t *scaled_font; + unsigned int font_id; + + int current_subset; + int num_glyphs_in_current_subset; + int max_glyphs_per_subset; + + cairo_hash_table_t *sub_font_glyphs; +} cairo_sub_font_t; + +typedef struct _cairo_sub_font_glyph { + cairo_hash_entry_t base; + + unsigned int subset_id; + unsigned int subset_glyph_index; +} cairo_sub_font_glyph_t; + +typedef struct _cairo_sub_font_collection { + unsigned long *glyphs; /* scaled_font_glyph_index */ + int glyphs_size; + int max_glyph; + int num_glyphs; + + unsigned int subset_id; + + cairo_scaled_font_subset_callback_func_t font_subset_callback; + void *font_subset_callback_closure; +} cairo_sub_font_collection_t; + +static void +_cairo_sub_font_glyph_init_key (cairo_sub_font_glyph_t *sub_font_glyph, + unsigned long scaled_font_glyph_index) +{ + sub_font_glyph->base.hash = scaled_font_glyph_index; +} static cairo_bool_t -_cairo_ps_glyph_equal (const void *key_a, const void *key_b) +_cairo_sub_font_glyphs_equal (const void *key_a, const void *key_b) { - const cairo_ps_glyph_t *ps_glyph_a = key_a; - const cairo_ps_glyph_t *ps_glyph_b = key_b; + const cairo_sub_font_glyph_t *sub_font_glyph_a = key_a; + const cairo_sub_font_glyph_t *sub_font_glyph_b = key_b; - return ps_glyph_a->base.hash == ps_glyph_b->base.hash; + return sub_font_glyph_a->base.hash == sub_font_glyph_b->base.hash; } -static void -_cairo_ps_glyph_key_init (cairo_ps_glyph_t *ps_glyph, - unsigned long index) +static cairo_sub_font_glyph_t * +_cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index, + unsigned int subset_id, + unsigned int subset_glyph_index) { - ps_glyph->base.hash = index; -} + cairo_sub_font_glyph_t *sub_font_glyph; -static cairo_ps_glyph_t * -_cairo_ps_glyph_create (cairo_ps_font_t *ps_font, - unsigned long index) -{ - cairo_ps_glyph_t *ps_glyph = malloc (sizeof (cairo_ps_glyph_t)); - - if (!ps_glyph) + sub_font_glyph = malloc (sizeof (cairo_sub_font_glyph_t)); + if (sub_font_glyph == NULL) return NULL; - _cairo_ps_glyph_key_init (ps_glyph, index); - ps_glyph->output_glyph = ps_font->max_glyph++; - return ps_glyph; + + _cairo_sub_font_glyph_init_key (sub_font_glyph, scaled_font_glyph_index); + sub_font_glyph->subset_id = subset_id; + sub_font_glyph->subset_glyph_index = subset_glyph_index; + + return sub_font_glyph; } static void -_cairo_ps_glyph_destroy (cairo_ps_glyph_t *ps_glyph) +_cairo_sub_font_glyph_destroy (cairo_sub_font_glyph_t *sub_font_glyph) { - free (ps_glyph); + free (sub_font_glyph); } -cairo_status_t -_cairo_ps_font_find_glyph (cairo_ps_font_t *font, - cairo_scaled_font_t *scaled_font, - unsigned long index, - cairo_ps_glyph_t **result) +static void +_cairo_sub_font_glyph_pluck (void *entry, void *closure) { - cairo_ps_glyph_t key; - cairo_ps_glyph_t *ps_glyph; - cairo_status_t status; + cairo_sub_font_glyph_t *sub_font_glyph = entry; + cairo_hash_table_t *sub_font_glyphs = closure; - _cairo_ps_glyph_key_init (&key, index); - if (! _cairo_hash_table_lookup (font->glyphs, - &key.base, - (cairo_hash_entry_t **) &ps_glyph)) { - ps_glyph = _cairo_ps_glyph_create (font, index); - if (!ps_glyph) + _cairo_hash_table_remove (sub_font_glyphs, &sub_font_glyph->base); + _cairo_sub_font_glyph_destroy (sub_font_glyph); +} + +static void +_cairo_sub_font_glyph_collect (void *entry, void *closure) +{ + cairo_sub_font_glyph_t *sub_font_glyph = entry; + cairo_sub_font_collection_t *collection = closure; + unsigned long scaled_font_glyph_index; + unsigned int subset_glyph_index; + + if (sub_font_glyph->subset_id != collection->subset_id) + return; + + scaled_font_glyph_index = sub_font_glyph->base.hash; + subset_glyph_index = sub_font_glyph->subset_glyph_index; + + /* Ensure we don't exceed the allocated bounds. */ + assert (subset_glyph_index < collection->glyphs_size); + + collection->glyphs[subset_glyph_index] = scaled_font_glyph_index; + if (subset_glyph_index > collection->max_glyph) + collection->max_glyph = subset_glyph_index; + + collection->num_glyphs++; +} + +static cairo_bool_t +_cairo_sub_fonts_equal (const void *key_a, const void *key_b) +{ + const cairo_sub_font_t *sub_font_a = key_a; + const cairo_sub_font_t *sub_font_b = key_b; + + return sub_font_a->scaled_font == sub_font_b->scaled_font; +} + +static void +_cairo_sub_font_init_key (cairo_sub_font_t *sub_font, + cairo_scaled_font_t *scaled_font) +{ + sub_font->base.hash = (unsigned long) scaled_font; + sub_font->scaled_font = scaled_font; +} + +static cairo_sub_font_t * +_cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, + cairo_scaled_font_t *scaled_font, + unsigned int font_id, + int max_glyphs_per_subset) +{ + cairo_sub_font_t *sub_font; + + sub_font = malloc (sizeof (cairo_sub_font_t)); + if (sub_font == NULL) + return NULL; + + _cairo_sub_font_init_key (sub_font, scaled_font); + + sub_font->parent = parent; + sub_font->scaled_font = cairo_scaled_font_reference (scaled_font); + sub_font->font_id = font_id; + + sub_font->current_subset = 0; + sub_font->num_glyphs_in_current_subset = 0; + sub_font->max_glyphs_per_subset = max_glyphs_per_subset; + + sub_font->sub_font_glyphs = _cairo_hash_table_create (_cairo_sub_font_glyphs_equal); + if (! sub_font->sub_font_glyphs) { + free (sub_font); + return NULL; + } + + return sub_font; +} + +static void +_cairo_sub_font_destroy (cairo_sub_font_t *sub_font) +{ + _cairo_hash_table_foreach (sub_font->sub_font_glyphs, + _cairo_sub_font_glyph_pluck, + sub_font->sub_font_glyphs); + _cairo_hash_table_destroy (sub_font->sub_font_glyphs); + cairo_scaled_font_destroy (sub_font->scaled_font); + free (sub_font); +} + +static void +_cairo_sub_font_pluck (void *entry, void *closure) +{ + cairo_sub_font_t *sub_font = entry; + cairo_hash_table_t *sub_fonts = closure; + + _cairo_hash_table_remove (sub_fonts, &sub_font->base); + _cairo_sub_font_destroy (sub_font); +} + +static cairo_status_t +_cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, + unsigned long scaled_font_glyph_index, + unsigned int *subset_id, + unsigned int *subset_glyph_index) +{ + cairo_sub_font_glyph_t key, *sub_font_glyph; + cairo_status_t status; + + _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index); + if (! _cairo_hash_table_lookup (sub_font->sub_font_glyphs, &key.base, + (cairo_hash_entry_t **) &sub_font_glyph)) + { + if (sub_font->max_glyphs_per_subset && + sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset) + { + sub_font->current_subset++; + sub_font->num_glyphs_in_current_subset = 0; + } + + sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index, + sub_font->current_subset, + sub_font->num_glyphs_in_current_subset++); + if (sub_font_glyph == NULL) return CAIRO_STATUS_NO_MEMORY; - status = _cairo_hash_table_insert (font->glyphs, &ps_glyph->base); + + if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_subset_used) + sub_font->parent->max_glyphs_per_subset_used = sub_font->num_glyphs_in_current_subset; + + status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base); if (status) return status; } - *result = ps_glyph; + + *subset_id = sub_font_glyph->subset_id; + *subset_glyph_index = sub_font_glyph->subset_glyph_index; + return CAIRO_STATUS_SUCCESS; } -cairo_bool_t -_cairo_ps_font_equal (const void *key_a, const void *key_b) -{ - const cairo_ps_font_t *ps_font_a = key_a; - const cairo_ps_font_t *ps_font_b = key_b; - - return ps_font_a->scaled_font == ps_font_b->scaled_font; -} - -void -_cairo_ps_font_key_init (cairo_ps_font_t *ps_font, - cairo_scaled_font_t *scaled_font) -{ - ps_font->base.hash = (unsigned long) scaled_font; - ps_font->scaled_font = scaled_font; -} - -cairo_ps_font_t * -_cairo_ps_font_create (cairo_scaled_font_t *scaled_font, - unsigned int id) -{ - cairo_ps_font_t *ps_font = malloc (sizeof (cairo_ps_font_t)); - if (!ps_font) - return NULL; - _cairo_ps_font_key_init (ps_font, scaled_font); - ps_font->glyphs = _cairo_hash_table_create (_cairo_ps_glyph_equal); - if (!ps_font->glyphs) { - free (ps_font); - return NULL; - } - ps_font->max_glyph = 0; - ps_font->output_font = id; - cairo_scaled_font_reference (ps_font->scaled_font); - return ps_font; -} - -void -_cairo_ps_font_destroy_glyph (cairo_ps_font_t *ps_font, - cairo_ps_glyph_t *ps_glyph) -{ - _cairo_hash_table_remove (ps_font->glyphs, &ps_glyph->base); - _cairo_ps_glyph_destroy (ps_glyph); -} - static void -_cairo_ps_font_destroy_glyph_callback (void *entry, void *closure) +_cairo_sub_font_collect (void *entry, void *closure) { - cairo_ps_glyph_t *ps_glyph = entry; - cairo_ps_font_t *ps_font = closure; + cairo_sub_font_t *sub_font = entry; + cairo_sub_font_collection_t *collection = closure; + cairo_scaled_font_subset_t subset; + int i; - _cairo_ps_font_destroy_glyph (ps_font, ps_glyph); -} + for (i = 0; i <= sub_font->current_subset; i++) { + collection->subset_id = i; -void -_cairo_ps_font_destroy (cairo_ps_font_t *ps_font) -{ - _cairo_hash_table_foreach (ps_font->glyphs, - _cairo_ps_font_destroy_glyph_callback, - ps_font); - _cairo_hash_table_destroy (ps_font->glyphs); - cairo_scaled_font_destroy (ps_font->scaled_font); - free (ps_font); -} + collection->num_glyphs = 0; + collection->max_glyph = 0; -void -_cairo_ps_font_select_glyphs (void *entry, void *closure) -{ - cairo_ps_glyph_t *ps_glyph = entry; - cairo_ps_font_glyph_select_t *ps_glyph_select = closure; + _cairo_hash_table_foreach (sub_font->sub_font_glyphs, + _cairo_sub_font_glyph_collect, collection); - if (ps_glyph->output_glyph >> 8 == ps_glyph_select->subfont) { - unsigned long sub_glyph = ps_glyph->output_glyph & 0xff; - ps_glyph_select->glyphs[sub_glyph] = ps_glyph; - if (sub_glyph >= ps_glyph_select->numglyph) - ps_glyph_select->numglyph = sub_glyph + 1; + /* Ensure the resulting array has no uninitialized holes */ + assert (collection->num_glyphs == collection->max_glyph + 1); + + subset.scaled_font = sub_font->scaled_font; + subset.font_id = sub_font->font_id; + subset.subset_id = i; + subset.glyphs = collection->glyphs; + subset.num_glyphs = collection->num_glyphs; + + (collection->font_subset_callback) (&subset, + collection->font_subset_callback_closure); } } + +cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create (int max_glyphs_per_subset) +{ + cairo_scaled_font_subsets_t *subsets; + + subsets = malloc (sizeof (cairo_scaled_font_subsets_t)); + if (subsets == NULL) + return NULL; + + subsets->max_glyphs_per_subset_limit = max_glyphs_per_subset; + subsets->max_glyphs_per_subset_used = 0; + subsets->num_sub_fonts = 0; + + subsets->sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal); + if (! subsets->sub_fonts) { + free (subsets); + return NULL; + } + + return subsets; +} + +void +_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *subsets) +{ + _cairo_hash_table_foreach (subsets->sub_fonts, _cairo_sub_font_pluck, subsets->sub_fonts); + _cairo_hash_table_destroy (subsets->sub_fonts); + free (subsets); +} + +cairo_private cairo_status_t +_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, + cairo_scaled_font_t *scaled_font, + unsigned long scaled_font_glyph_index, + unsigned int *font_id, + unsigned int *subset_id, + unsigned int *subset_glyph_index) +{ + cairo_sub_font_t key, *sub_font; + cairo_status_t status; + + _cairo_sub_font_init_key (&key, scaled_font); + if (! _cairo_hash_table_lookup (subsets->sub_fonts, &key.base, + (cairo_hash_entry_t **) &sub_font)) + { + sub_font = _cairo_sub_font_create (subsets, scaled_font, + subsets->num_sub_fonts++, + subsets->max_glyphs_per_subset_limit); + if (sub_font == NULL) + return CAIRO_STATUS_NO_MEMORY; + + status = _cairo_hash_table_insert (subsets->sub_fonts, + &sub_font->base); + if (status) + return status; + } + + *font_id = sub_font->font_id; + + return _cairo_sub_font_map_glyph (sub_font, scaled_font_glyph_index, + subset_id, subset_glyph_index); +} + +cairo_private cairo_status_t +_cairo_scaled_font_subsets_foreach (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure) +{ + cairo_sub_font_collection_t collection; + + collection.glyphs_size = font_subsets->max_glyphs_per_subset_used; + collection.glyphs = malloc (collection.glyphs_size * sizeof(unsigned long)); + if (collection.glyphs == NULL) + return CAIRO_STATUS_NO_MEMORY; + + collection.font_subset_callback = font_subset_callback; + collection.font_subset_callback_closure = closure; + + _cairo_hash_table_foreach (font_subsets->sub_fonts, + _cairo_sub_font_collect, &collection); + + return CAIRO_STATUS_SUCCESS; +}