/* cairo - a vector graphics library with display and print output * * Copyright © 2006 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Adrian Johnson */ #define _BSD_SOURCE /* for snprintf(), strdup() */ #include "cairoint.h" #include "cairo-array-private.h" #include "cairo-error-private.h" #if CAIRO_HAS_FONT_SUBSET #include "cairo-type1-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-path-fixed-private.h" #include "cairo-output-stream-private.h" typedef enum { CAIRO_CHARSTRING_TYPE1, CAIRO_CHARSTRING_TYPE2 } cairo_charstring_type_t; typedef struct _cairo_type1_font { int *widths; cairo_scaled_font_subset_t *scaled_font_subset; cairo_scaled_font_t *type1_scaled_font; cairo_array_t contents; double x_min, y_min, x_max, y_max; const char *data; unsigned long header_size; unsigned long data_size; unsigned long trailer_size; int bbox_position; int bbox_max_chars; cairo_output_stream_t *output; unsigned short eexec_key; cairo_bool_t hex_encode; int hex_column; } cairo_type1_font_t; static cairo_status_t cairo_type1_font_create (cairo_scaled_font_subset_t *scaled_font_subset, cairo_type1_font_t **subset_return, cairo_bool_t hex_encode) { cairo_type1_font_t *font; cairo_font_face_t *font_face; cairo_matrix_t font_matrix; cairo_matrix_t ctm; cairo_font_options_t font_options; cairo_status_t status; font = calloc (1, sizeof (cairo_type1_font_t)); if (unlikely (font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->widths = calloc (scaled_font_subset->num_glyphs, sizeof (int)); if (unlikely (font->widths == NULL)) { free (font); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } font->scaled_font_subset = scaled_font_subset; font->hex_encode = hex_encode; font_face = cairo_scaled_font_get_font_face (scaled_font_subset->scaled_font); cairo_matrix_init_scale (&font_matrix, 1000, -1000); cairo_matrix_init_identity (&ctm); _cairo_font_options_init_default (&font_options); cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF); font->type1_scaled_font = cairo_scaled_font_create (font_face, &font_matrix, &ctm, &font_options); status = font->type1_scaled_font->status; if (unlikely (status)) goto fail; _cairo_array_init (&font->contents, sizeof (unsigned char)); font->output = NULL; *subset_return = font; return CAIRO_STATUS_SUCCESS; fail: free (font->widths); free (font); return status; } /* Charstring commands. If the high byte is 0 the command is encoded * with a single byte. */ #define CHARSTRING_sbw 0x0c07 #define CHARSTRING_rmoveto 0x0015 #define CHARSTRING_rlineto 0x0005 #define CHARSTRING_rcurveto 0x0008 #define CHARSTRING_closepath 0x0009 #define CHARSTRING_endchar 0x000e /* Before calling this function, the caller must allocate sufficient * space in data (see _cairo_array_grow_by). The maximum number of * bytes that will be used is 2. */ static void charstring_encode_command (cairo_array_t *data, int command) { cairo_status_t status; unsigned int orig_size; unsigned char buf[5]; unsigned char *p = buf; if (command & 0xff00) *p++ = command >> 8; *p++ = command & 0x00ff; /* Ensure the array doesn't grow, which allows this function to * have no possibility of failure. */ orig_size = _cairo_array_size (data); status = _cairo_array_append_multiple (data, buf, p - buf); assert (status == CAIRO_STATUS_SUCCESS); assert (_cairo_array_size (data) == orig_size); } /* Before calling this function, the caller must allocate sufficient * space in data (see _cairo_array_grow_by). The maximum number of * bytes that will be used is 5. */ static void charstring_encode_integer (cairo_array_t *data, int i, cairo_charstring_type_t type) { cairo_status_t status; unsigned int orig_size; unsigned char buf[10]; unsigned char *p = buf; if (i >= -107 && i <= 107) { *p++ = i + 139; } else if (i >= 108 && i <= 1131) { i -= 108; *p++ = (i >> 8)+ 247; *p++ = i & 0xff; } else if (i >= -1131 && i <= -108) { i = -i - 108; *p++ = (i >> 8)+ 251; *p++ = i & 0xff; } else { if (type == CAIRO_CHARSTRING_TYPE1) { *p++ = 0xff; *p++ = i >> 24; *p++ = (i >> 16) & 0xff; *p++ = (i >> 8) & 0xff; *p++ = i & 0xff; } else { *p++ = 0xff; *p++ = (i >> 8) & 0xff; *p++ = i & 0xff; *p++ = 0; *p++ = 0; } } /* Ensure the array doesn't grow, which allows this function to * have no possibility of failure. */ orig_size = _cairo_array_size (data); status = _cairo_array_append_multiple (data, buf, p - buf); assert (status == CAIRO_STATUS_SUCCESS); assert (_cairo_array_size (data) == orig_size); } typedef struct _ps_path_info { cairo_array_t *data; int current_x, current_y; cairo_charstring_type_t type; } t1_path_info_t; static cairo_status_t _charstring_move_to (void *closure, const cairo_point_t *point) { t1_path_info_t *path_info = (t1_path_info_t *) closure; int dx, dy; cairo_status_t status; status = _cairo_array_grow_by (path_info->data, 12); if (unlikely (status)) return status; dx = _cairo_fixed_integer_part (point->x) - path_info->current_x; dy = _cairo_fixed_integer_part (point->y) - path_info->current_y; charstring_encode_integer (path_info->data, dx, path_info->type); charstring_encode_integer (path_info->data, dy, path_info->type); path_info->current_x += dx; path_info->current_y += dy; charstring_encode_command (path_info->data, CHARSTRING_rmoveto); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _charstring_line_to (void *closure, const cairo_point_t *point) { t1_path_info_t *path_info = (t1_path_info_t *) closure; int dx, dy; cairo_status_t status; status = _cairo_array_grow_by (path_info->data, 12); if (unlikely (status)) return status; dx = _cairo_fixed_integer_part (point->x) - path_info->current_x; dy = _cairo_fixed_integer_part (point->y) - path_info->current_y; charstring_encode_integer (path_info->data, dx, path_info->type); charstring_encode_integer (path_info->data, dy, path_info->type); path_info->current_x += dx; path_info->current_y += dy; charstring_encode_command (path_info->data, CHARSTRING_rlineto); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _charstring_curve_to (void *closure, const cairo_point_t *point1, const cairo_point_t *point2, const cairo_point_t *point3) { t1_path_info_t *path_info = (t1_path_info_t *) closure; int dx1, dy1, dx2, dy2, dx3, dy3; cairo_status_t status; status = _cairo_array_grow_by (path_info->data, 32); if (unlikely (status)) return status; dx1 = _cairo_fixed_integer_part (point1->x) - path_info->current_x; dy1 = _cairo_fixed_integer_part (point1->y) - path_info->current_y; dx2 = _cairo_fixed_integer_part (point2->x) - path_info->current_x - dx1; dy2 = _cairo_fixed_integer_part (point2->y) - path_info->current_y - dy1; dx3 = _cairo_fixed_integer_part (point3->x) - path_info->current_x - dx1 - dx2; dy3 = _cairo_fixed_integer_part (point3->y) - path_info->current_y - dy1 - dy2; charstring_encode_integer (path_info->data, dx1, path_info->type); charstring_encode_integer (path_info->data, dy1, path_info->type); charstring_encode_integer (path_info->data, dx2, path_info->type); charstring_encode_integer (path_info->data, dy2, path_info->type); charstring_encode_integer (path_info->data, dx3, path_info->type); charstring_encode_integer (path_info->data, dy3, path_info->type); path_info->current_x += dx1 + dx2 + dx3; path_info->current_y += dy1 + dy2 + dy3; charstring_encode_command (path_info->data, CHARSTRING_rcurveto); return CAIRO_STATUS_SUCCESS; } static cairo_status_t _charstring_close_path (void *closure) { cairo_status_t status; t1_path_info_t *path_info = (t1_path_info_t *) closure; if (path_info->type == CAIRO_CHARSTRING_TYPE2) return CAIRO_STATUS_SUCCESS; status = _cairo_array_grow_by (path_info->data, 2); if (unlikely (status)) return status; charstring_encode_command (path_info->data, CHARSTRING_closepath); return CAIRO_STATUS_SUCCESS; } static void charstring_encrypt (cairo_array_t *data) { unsigned char *d, *end; uint16_t c, p, r; r = CAIRO_TYPE1_CHARSTRING_KEY; d = (unsigned char *) _cairo_array_index (data, 0); if (d == NULL) return; end = d + _cairo_array_num_elements (data); while (d < end) { p = *d; c = p ^ (r >> 8); r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; *d++ = c; } } static cairo_int_status_t cairo_type1_font_create_charstring (cairo_type1_font_t *font, int subset_index, int glyph_index, cairo_charstring_type_t type, cairo_array_t *data) { cairo_int_status_t status; cairo_scaled_glyph_t *scaled_glyph; t1_path_info_t path_info; cairo_text_extents_t *metrics; cairo_bool_t emit_path = TRUE; /* This call may return CAIRO_INT_STATUS_UNSUPPORTED for bitmap fonts. */ status = _cairo_scaled_glyph_lookup (font->type1_scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS| CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); /* It is ok for the .notdef glyph to not have a path available. We * just need the metrics to emit an empty glyph. */ if (glyph_index == 0 && status == CAIRO_INT_STATUS_UNSUPPORTED) { emit_path = FALSE; status = _cairo_scaled_glyph_lookup (font->type1_scaled_font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS, &scaled_glyph); } if (unlikely (status)) return status; metrics = &scaled_glyph->metrics; if (subset_index == 0) { font->x_min = metrics->x_bearing; font->y_min = metrics->y_bearing; font->x_max = metrics->x_bearing + metrics->width; font->y_max = metrics->y_bearing + metrics->height; } else { if (metrics->x_bearing < font->x_min) font->x_min = metrics->x_bearing; if (metrics->y_bearing < font->y_min) font->y_min = metrics->y_bearing; if (metrics->x_bearing + metrics->width > font->x_max) font->x_max = metrics->x_bearing + metrics->width; if (metrics->y_bearing + metrics->height > font->y_max) font->y_max = metrics->y_bearing + metrics->height; } font->widths[subset_index] = metrics->x_advance; status = _cairo_array_grow_by (data, 30); if (unlikely (status)) return status; if (type == CAIRO_CHARSTRING_TYPE1) { charstring_encode_integer (data, (int) scaled_glyph->metrics.x_bearing, type); charstring_encode_integer (data, (int) scaled_glyph->metrics.y_bearing, type); charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type); charstring_encode_integer (data, (int) scaled_glyph->metrics.y_advance, type); charstring_encode_command (data, CHARSTRING_sbw); path_info.current_x = (int) scaled_glyph->metrics.x_bearing; path_info.current_y = (int) scaled_glyph->metrics.y_bearing; } else { charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type); path_info.current_x = 0; path_info.current_y = 0; } path_info.data = data; path_info.type = type; if (emit_path) { status = _cairo_path_fixed_interpret (scaled_glyph->path, _charstring_move_to, _charstring_line_to, _charstring_curve_to, _charstring_close_path, &path_info); if (unlikely (status)) return status; } status = _cairo_array_grow_by (data, 1); if (unlikely (status)) return status; charstring_encode_command (path_info.data, CHARSTRING_endchar); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_type1_font_write_charstrings (cairo_type1_font_t *font, cairo_output_stream_t *encrypted_output) { cairo_status_t status; unsigned char zeros[] = { 0, 0, 0, 0 }; cairo_array_t data; unsigned int i; int length; _cairo_array_init (&data, sizeof (unsigned char)); status = _cairo_array_grow_by (&data, 1024); if (unlikely (status)) goto fail; _cairo_output_stream_printf (encrypted_output, "2 index /CharStrings %d dict dup begin\n", font->scaled_font_subset->num_glyphs + 1); _cairo_scaled_font_freeze_cache (font->type1_scaled_font); for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { _cairo_array_truncate (&data, 0); /* four "random" bytes required by encryption algorithm */ status = _cairo_array_append_multiple (&data, zeros, 4); if (unlikely (status)) break; status = cairo_type1_font_create_charstring (font, i, font->scaled_font_subset->glyphs[i], CAIRO_CHARSTRING_TYPE1, &data); if (unlikely (status)) break; charstring_encrypt (&data); length = _cairo_array_num_elements (&data); if (font->scaled_font_subset->glyph_names != NULL) { _cairo_output_stream_printf (encrypted_output, "/%s %d RD ", font->scaled_font_subset->glyph_names[i], length); } else if (i == 0) { _cairo_output_stream_printf (encrypted_output, "/.notdef %d RD ", length); } else { _cairo_output_stream_printf (encrypted_output, "/g%d %d RD ", i, length); } _cairo_output_stream_write (encrypted_output, _cairo_array_index (&data, 0), length); _cairo_output_stream_printf (encrypted_output, " ND\n"); } _cairo_scaled_font_thaw_cache (font->type1_scaled_font); fail: _cairo_array_fini (&data); return status; } static void cairo_type1_font_write_header (cairo_type1_font_t *font, const char *name) { unsigned int i; const char spaces[50] = " "; _cairo_output_stream_printf (font->output, "%%!FontType1-1.1 %s 1.0\n" "11 dict begin\n" "/FontName /%s def\n" "/PaintType 0 def\n" "/FontType 1 def\n" "/FontMatrix [0.001 0 0 0.001 0 0] readonly def\n", name, name); /* We don't know the bbox values until after the charstrings have * been generated. Reserve some space and fill in the bbox * later. */ /* Worst case for four signed ints with spaces between each number */ font->bbox_max_chars = 50; _cairo_output_stream_printf (font->output, "/FontBBox {"); font->bbox_position = _cairo_output_stream_get_position (font->output); _cairo_output_stream_write (font->output, spaces, font->bbox_max_chars); _cairo_output_stream_printf (font->output, "} readonly def\n" "/Encoding 256 array\n" "0 1 255 {1 index exch /.notdef put} for\n"); if (font->scaled_font_subset->is_latin) { for (i = 1; i < 256; i++) { int subset_glyph = font->scaled_font_subset->latin_to_subset_glyph_index[i]; if (subset_glyph > 0) { if (font->scaled_font_subset->glyph_names != NULL) { _cairo_output_stream_printf (font->output, "dup %d /%s put\n", i, font->scaled_font_subset->glyph_names[subset_glyph]); } else { _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, subset_glyph); } } } } else { for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { if (font->scaled_font_subset->glyph_names != NULL) { _cairo_output_stream_printf (font->output, "dup %d /%s put\n", i, font->scaled_font_subset->glyph_names[i]); } else { _cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i); } } } _cairo_output_stream_printf (font->output, "readonly def\n" "currentdict end\n" "currentfile eexec\n"); } static cairo_status_t cairo_type1_write_stream_encrypted (void *closure, const unsigned char *data, unsigned int length) { const unsigned char *in, *end; uint16_t c, p; static const char hex_digits[16] = "0123456789abcdef"; char digits[3]; cairo_type1_font_t *font = closure; in = (const unsigned char *) data; end = (const unsigned char *) data + length; while (in < end) { p = *in++; c = p ^ (font->eexec_key >> 8); font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; if (font->hex_encode) { digits[0] = hex_digits[c >> 4]; digits[1] = hex_digits[c & 0x0f]; digits[2] = '\n'; font->hex_column += 2; if (font->hex_column == 78) { _cairo_output_stream_write (font->output, digits, 3); font->hex_column = 0; } else { _cairo_output_stream_write (font->output, digits, 2); } } else { digits[0] = c; _cairo_output_stream_write (font->output, digits, 1); } } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_type1_font_write_private_dict (cairo_type1_font_t *font, const char *name) { cairo_int_status_t status; cairo_status_t status2; cairo_output_stream_t *encrypted_output; font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY; font->hex_column = 0; encrypted_output = _cairo_output_stream_create ( cairo_type1_write_stream_encrypted, NULL, font); if (_cairo_output_stream_get_status (encrypted_output)) return _cairo_output_stream_destroy (encrypted_output); /* Note: the first four spaces at the start of this private dict * are the four "random" bytes of plaintext required by the * encryption algorithm */ _cairo_output_stream_printf (encrypted_output, " dup /Private 9 dict dup begin\n" "/RD {string currentfile exch readstring pop}" " bind executeonly def\n" "/ND {noaccess def} executeonly def\n" "/NP {noaccess put} executeonly def\n" "/BlueValues [] def\n" "/MinFeature {16 16} def\n" "/lenIV 4 def\n" "/password 5839 def\n"); status = cairo_type1_font_write_charstrings (font, encrypted_output); if (unlikely (status)) goto fail; _cairo_output_stream_printf (encrypted_output, "end\n" "end\n" "readonly put\n" "noaccess put\n" "dup /FontName get exch definefont pop\n" "mark currentfile closefile\n"); fail: status2 = _cairo_output_stream_destroy (encrypted_output); if (status == CAIRO_INT_STATUS_SUCCESS) status = status2; return status; } static void cairo_type1_font_write_trailer(cairo_type1_font_t *font) { int i; static const char zeros[65] = "0000000000000000000000000000000000000000000000000000000000000000\n"; for (i = 0; i < 8; i++) _cairo_output_stream_write (font->output, zeros, sizeof zeros); _cairo_output_stream_printf (font->output, "cleartomark\n"); } static cairo_status_t cairo_type1_write_stream (void *closure, const unsigned char *data, unsigned int length) { cairo_type1_font_t *font = closure; return _cairo_array_append_multiple (&font->contents, data, length); } static cairo_int_status_t cairo_type1_font_write (cairo_type1_font_t *font, const char *name) { cairo_int_status_t status; cairo_type1_font_write_header (font, name); font->header_size = _cairo_output_stream_get_position (font->output); status = cairo_type1_font_write_private_dict (font, name); if (unlikely (status)) return status; font->data_size = _cairo_output_stream_get_position (font->output) - font->header_size; cairo_type1_font_write_trailer (font); font->trailer_size = _cairo_output_stream_get_position (font->output) - font->header_size - font->data_size; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_type1_font_generate (cairo_type1_font_t *font, const char *name) { cairo_int_status_t status; status = _cairo_array_grow_by (&font->contents, 4096); if (unlikely (status)) return status; font->output = _cairo_output_stream_create (cairo_type1_write_stream, NULL, font); if (_cairo_output_stream_get_status (font->output)) return _cairo_output_stream_destroy (font->output); status = cairo_type1_font_write (font, name); if (unlikely (status)) return status; font->data = _cairo_array_index (&font->contents, 0); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_destroy (cairo_type1_font_t *font) { cairo_status_t status = CAIRO_STATUS_SUCCESS; free (font->widths); cairo_scaled_font_destroy (font->type1_scaled_font); _cairo_array_fini (&font->contents); if (font->output) status = _cairo_output_stream_destroy (font->output); free (font); return status; } static cairo_status_t _cairo_type1_fallback_init_internal (cairo_type1_subset_t *type1_subset, const char *name, cairo_scaled_font_subset_t *scaled_font_subset, cairo_bool_t hex_encode) { cairo_type1_font_t *font; cairo_status_t status; unsigned long length; unsigned int i, len; status = cairo_type1_font_create (scaled_font_subset, &font, hex_encode); if (unlikely (status)) return status; status = cairo_type1_font_generate (font, name); if (unlikely (status)) goto fail1; type1_subset->base_font = strdup (name); if (unlikely (type1_subset->base_font == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } type1_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); if (unlikely (type1_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) type1_subset->widths[i] = (double)font->widths[i]/1000; type1_subset->x_min = (double)font->x_min/1000; type1_subset->y_min = (double)font->y_min/1000; type1_subset->x_max = (double)font->x_max/1000; type1_subset->y_max = (double)font->y_max/1000; type1_subset->ascent = (double)font->y_max/1000; type1_subset->descent = (double)font->y_min/1000; length = font->header_size + font->data_size + font->trailer_size; type1_subset->data = malloc (length); if (unlikely (type1_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } memcpy (type1_subset->data, _cairo_array_index (&font->contents, 0), length); len = snprintf(type1_subset->data + font->bbox_position, font->bbox_max_chars, "%d %d %d %d", (int)font->x_min, (int)font->y_min, (int)font->x_max, (int)font->y_max); type1_subset->data[font->bbox_position + len] = ' '; type1_subset->header_length = font->header_size; type1_subset->data_length = font->data_size; type1_subset->trailer_length = font->trailer_size; return cairo_type1_font_destroy (font); fail3: free (type1_subset->widths); fail2: free (type1_subset->base_font); fail1: /* status is already set, ignore further errors */ cairo_type1_font_destroy (font); return status; } cairo_status_t _cairo_type1_fallback_init_binary (cairo_type1_subset_t *type1_subset, const char *name, cairo_scaled_font_subset_t *scaled_font_subset) { return _cairo_type1_fallback_init_internal (type1_subset, name, scaled_font_subset, FALSE); } cairo_status_t _cairo_type1_fallback_init_hex (cairo_type1_subset_t *type1_subset, const char *name, cairo_scaled_font_subset_t *scaled_font_subset) { return _cairo_type1_fallback_init_internal (type1_subset, name, scaled_font_subset, TRUE); } void _cairo_type1_fallback_fini (cairo_type1_subset_t *subset) { free (subset->base_font); free (subset->widths); free (subset->data); } cairo_status_t _cairo_type2_charstrings_init (cairo_type2_charstrings_t *type2_subset, cairo_scaled_font_subset_t *scaled_font_subset) { cairo_type1_font_t *font; cairo_status_t status; unsigned int i; cairo_array_t charstring; status = cairo_type1_font_create (scaled_font_subset, &font, FALSE); if (unlikely (status)) return status; _cairo_array_init (&type2_subset->charstrings, sizeof (cairo_array_t)); type2_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs); if (unlikely (type2_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } _cairo_scaled_font_freeze_cache (font->type1_scaled_font); for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { _cairo_array_init (&charstring, sizeof (unsigned char)); status = _cairo_array_grow_by (&charstring, 32); if (unlikely (status)) goto fail2; status = cairo_type1_font_create_charstring (font, i, font->scaled_font_subset->glyphs[i], CAIRO_CHARSTRING_TYPE2, &charstring); if (unlikely (status)) goto fail2; status = _cairo_array_append (&type2_subset->charstrings, &charstring); if (unlikely (status)) goto fail2; } _cairo_scaled_font_thaw_cache (font->type1_scaled_font); for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) type2_subset->widths[i] = font->widths[i]; type2_subset->x_min = (int) font->x_min; type2_subset->y_min = (int) font->y_min; type2_subset->x_max = (int) font->x_max; type2_subset->y_max = (int) font->y_max; type2_subset->ascent = (int) font->y_max; type2_subset->descent = (int) font->y_min; return cairo_type1_font_destroy (font); fail2: _cairo_scaled_font_thaw_cache (font->type1_scaled_font); _cairo_array_fini (&charstring); _cairo_type2_charstrings_fini (type2_subset); fail1: cairo_type1_font_destroy (font); return status; } void _cairo_type2_charstrings_fini (cairo_type2_charstrings_t *type2_subset) { unsigned int i, num_charstrings; cairo_array_t *charstring; num_charstrings = _cairo_array_num_elements (&type2_subset->charstrings); for (i = 0; i < num_charstrings; i++) { charstring = _cairo_array_index (&type2_subset->charstrings, i); if (charstring) _cairo_array_fini (charstring); } _cairo_array_fini (&type2_subset->charstrings); free (type2_subset->widths); } #endif /* CAIRO_HAS_FONT_SUBSET */