/* 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): * Kristian Høgsberg */ /* * Useful links: * http://partners.adobe.com/public/developer/en/font/T1_SPEC.PDF */ #define _BSD_SOURCE /* for snprintf(), strdup() */ #include "cairoint.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-output-stream-private.h" #include #define TYPE1_STACKSIZE 24 /* Defined in Type 1 Font Format */ typedef struct { int subset_index; double width; const char *encrypted_charstring; int encrypted_charstring_length; } glyph_data_t; typedef struct _cairo_type1_font_subset { cairo_scaled_font_subset_t *scaled_font_subset; struct { unsigned int font_id; char *base_font; unsigned int num_glyphs; double x_min, y_min, x_max, y_max; double ascent, descent; double units_per_em; const char *data; unsigned long header_size; unsigned long data_size; unsigned long trailer_size; } base; int num_glyphs; /* The glyphs and glyph_names arrays are indexed by the order of * the Charstrings in the font. This is not necessarily the same * order as the glyph index. The index_to_glyph_name() font backend * function is used to map the glyph index to the glyph order in * the Charstrings. */ glyph_data_t *glyphs; char **glyph_names; cairo_array_t glyphs_array; cairo_array_t glyph_names_array; int num_subrs; cairo_bool_t subset_subrs; struct { const char *subr_string; int subr_length; cairo_bool_t used; } *subrs; /* Indexed by subset_index this maps to the glyph order in the * glyph_names and glyphs arrays. Has font->num_golyphs * elements. */ int *subset_index_to_glyphs; cairo_output_stream_t *output; cairo_array_t contents; const char *rd, *nd, *np; char *type1_data; unsigned int type1_length; char *type1_end; char *header_segment; int header_segment_size; char *eexec_segment; int eexec_segment_size; cairo_bool_t eexec_segment_is_ascii; char *cleartext; char *cleartext_end; int header_size; unsigned short eexec_key; cairo_bool_t hex_encode; int hex_column; struct { int stack[TYPE1_STACKSIZE], sp, top_value; } build_stack; struct { int other_subr_args[TYPE1_STACKSIZE], num_other_subr_args, cur_other_subr_arg; } ps_stack; } cairo_type1_font_subset_t; static cairo_status_t _cairo_type1_font_subset_init (cairo_type1_font_subset_t *font, cairo_scaled_font_subset_t *scaled_font_subset, cairo_bool_t hex_encode) { memset (font, 0, sizeof (*font)); font->scaled_font_subset = scaled_font_subset; _cairo_array_init (&font->glyphs_array, sizeof (glyph_data_t)); _cairo_array_init (&font->glyph_names_array, sizeof (char *)); font->subset_index_to_glyphs = NULL; font->base.num_glyphs = 0; font->num_subrs = 0; font->subset_subrs = TRUE; font->subrs = NULL; font->hex_encode = hex_encode; font->num_glyphs = 0; _cairo_array_init (&font->contents, sizeof (char)); return CAIRO_STATUS_SUCCESS; } static void cairo_type1_font_subset_use_glyph (cairo_type1_font_subset_t *font, int glyph) { if (font->glyphs[glyph].subset_index >= 0) return; font->glyphs[glyph].subset_index = font->num_glyphs; font->subset_index_to_glyphs[font->num_glyphs] = glyph; font->num_glyphs++; } static cairo_bool_t is_ps_delimiter(int c) { static const char delimiters[] = "()[]{}<>/% \t\r\n"; return strchr (delimiters, c) != NULL; } static const char * find_token (const char *buffer, const char *end, const char *token) { int i, length; /* FIXME: find substring really must be find_token */ if (buffer == NULL) return NULL; length = strlen (token); for (i = 0; buffer + i < end - length + 1; i++) if (memcmp (buffer + i, token, length) == 0) if ((i == 0 || token[0] == '/' || is_ps_delimiter(buffer[i - 1])) && (buffer + i == end - length || is_ps_delimiter(buffer[i + length]))) return buffer + i; return NULL; } static cairo_status_t cairo_type1_font_subset_find_segments (cairo_type1_font_subset_t *font) { unsigned char *p; const char *eexec_token; int size, i; p = (unsigned char *) font->type1_data; font->type1_end = font->type1_data + font->type1_length; if (p[0] == 0x80 && p[1] == 0x01) { font->header_segment_size = p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); font->header_segment = (char *) p + 6; p += 6 + font->header_segment_size; font->eexec_segment_size = p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); font->eexec_segment = (char *) p + 6; font->eexec_segment_is_ascii = (p[1] == 1); p += 6 + font->eexec_segment_size; while (p < (unsigned char *) (font->type1_end) && p[1] != 0x03) { size = p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24); p += 6 + size; } font->type1_end = (char *) p; } else { eexec_token = find_token ((char *) p, font->type1_end, "eexec"); if (eexec_token == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; font->header_segment_size = eexec_token - (char *) p + strlen ("eexec\n"); font->header_segment = (char *) p; font->eexec_segment_size = font->type1_length - font->header_segment_size; font->eexec_segment = (char *) p + font->header_segment_size; font->eexec_segment_is_ascii = TRUE; for (i = 0; i < 4; i++) { if (!isxdigit(font->eexec_segment[i])) font->eexec_segment_is_ascii = FALSE; } } return CAIRO_STATUS_SUCCESS; } /* Search for the definition of key and erase it by overwriting with spaces. * This function is looks for definitions of the form: * * /key1 1234 def * /key2 [12 34 56] def * * ie a key defined as an integer or array of integers. * */ static void cairo_type1_font_erase_dict_key (cairo_type1_font_subset_t *font, const char *key) { const char *start, *p, *segment_end; segment_end = font->header_segment + font->header_segment_size; start = font->header_segment; do { start = find_token (start, segment_end, key); if (start) { p = start + strlen(key); /* skip integers or array of integers */ while (p < segment_end && (_cairo_isspace(*p) || _cairo_isdigit(*p) || *p == '[' || *p == ']')) { p++; } if (p + 3 < segment_end && memcmp(p, "def", 3) == 0) { /* erase definition of the key */ memset((char *) start, ' ', p + 3 - start); } start += strlen(key); } } while (start); } static cairo_status_t cairo_type1_font_subset_get_matrix (cairo_type1_font_subset_t *font, const char *name, double *a, double *b, double *c, double *d) { const char *start, *end, *segment_end; int ret; char *s; segment_end = font->header_segment + font->header_segment_size; start = find_token (font->header_segment, segment_end, name); if (start == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; end = find_token (start, segment_end, "def"); if (end == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; s = malloc (end - start + 1); if (unlikely (s == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); strncpy (s, start, end - start); s[end - start] = 0; start = strpbrk (s, "{["); if (!start) { free (s); return CAIRO_INT_STATUS_UNSUPPORTED; } start++; ret = 0; if (*start) ret = sscanf(start, "%lf %lf %lf %lf", a, b, c, d); free (s); if (ret != 4) return CAIRO_INT_STATUS_UNSUPPORTED; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_get_bbox (cairo_type1_font_subset_t *font) { cairo_status_t status; double x_min, y_min, x_max, y_max; double xx, yx, xy, yy; status = cairo_type1_font_subset_get_matrix (font, "/FontBBox", &x_min, &y_min, &x_max, &y_max); if (unlikely (status)) return status; status = cairo_type1_font_subset_get_matrix (font, "/FontMatrix", &xx, &yx, &xy, &yy); if (unlikely (status)) return status; if (yy == 0.0) return CAIRO_INT_STATUS_UNSUPPORTED; /* Freetype uses 1/yy to get units per EM */ font->base.units_per_em = 1.0/yy; font->base.x_min = x_min / font->base.units_per_em; font->base.y_min = y_min / font->base.units_per_em; font->base.x_max = x_max / font->base.units_per_em; font->base.y_max = y_max / font->base.units_per_em; font->base.ascent = font->base.y_max; font->base.descent = font->base.y_min; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_get_fontname (cairo_type1_font_subset_t *font) { const char *start, *end, *segment_end; char *s; segment_end = font->header_segment + font->header_segment_size; start = find_token (font->header_segment, segment_end, "/FontName"); if (start == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; start += strlen ("/FontName"); end = find_token (start, segment_end, "def"); if (end == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; s = malloc (end - start + 1); if (unlikely (s == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); strncpy (s, start, end - start); s[end - start] = 0; start = strchr (s, '/'); if (!start++ || !start) { free (s); return CAIRO_INT_STATUS_UNSUPPORTED; } font->base.base_font = strdup (start); free (s); if (unlikely (font->base.base_font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); s = font->base.base_font; while (*s && !is_ps_delimiter(*s)) s++; *s = 0; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_write_header (cairo_type1_font_subset_t *font, const char *name) { const char *start, *end, *segment_end; unsigned int i; /* FIXME: * This function assumes that /FontName always appears * before /Encoding. This appears to always be the case with Type1 * fonts. * * The more recently added code for removing the UniqueID and XUID * keys can not make any assumptions about the position of the * keys in the dictionary so it is implemented by overwriting the * key definition with spaces before we start copying the font to * the output. * * This code should be rewritten to not make any assumptions about * the order of dictionary keys. This will allow UniqueID to be * stripped out instead of leaving a bunch of spaces in the * output. */ cairo_type1_font_erase_dict_key (font, "/UniqueID"); cairo_type1_font_erase_dict_key (font, "/XUID"); segment_end = font->header_segment + font->header_segment_size; /* Type 1 fonts created by Fontforge have some PostScript code at * the start of the font that skips the font if the printer has a * cached copy of the font with the same unique id. This breaks * our subsetted font so we disable it by searching for the * PostScript operator "known" when used to check for the * "/UniqueID" dictionary key. We append " pop false " after it to * pop the result of this check off the stack and replace it with * "false" to make the PostScript code think "/UniqueID" does not * exist. */ end = font->header_segment; start = find_token (font->header_segment, segment_end, "/UniqueID"); if (start) { start += 9; while (start < segment_end && _cairo_isspace (*start)) start++; if (start + 5 < segment_end && memcmp(start, "known", 5) == 0) { _cairo_output_stream_write (font->output, font->header_segment, start + 5 - font->header_segment); _cairo_output_stream_printf (font->output, " pop false "); end = start + 5; } } start = find_token (end, segment_end, "/FontName"); if (start == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_output_stream_write (font->output, end, start - end); _cairo_output_stream_printf (font->output, "/FontName /%s def", name); end = find_token (start, segment_end, "def"); if (end == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; end += 3; start = find_token (end, segment_end, "/Encoding"); if (start == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_output_stream_write (font->output, end, start - end); _cairo_output_stream_printf (font->output, "/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]; int glyph_num = font->subset_index_to_glyphs[subset_glyph]; if (subset_glyph > 0) { _cairo_output_stream_printf (font->output, "dup %d /%s put\n", i, font->glyph_names[glyph_num]); } } } else { for (i = 1; i < font->base.num_glyphs; i++) { if (font->glyphs[i].subset_index < 0) continue; _cairo_output_stream_printf (font->output, "dup %d /%s put\n", font->glyphs[i].subset_index, font->glyph_names[i]); } } _cairo_output_stream_printf (font->output, "readonly def"); end = find_token (start, segment_end, "def"); if (end == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; end += 3; /* There are some buggy fonts that contain more than one /Encoding */ if (find_token (end, segment_end, "/Encoding")) return CAIRO_INT_STATUS_UNSUPPORTED; _cairo_output_stream_write (font->output, end, segment_end - end); return font->output->status; } static int hex_to_int (int ch) { if (ch <= '9') return ch - '0'; else if (ch <= 'F') return ch - 'A' + 10; else return ch - 'a' + 10; } static cairo_status_t cairo_type1_font_subset_write_encrypted (cairo_type1_font_subset_t *font, const char *data, unsigned int length) { const unsigned char *in, *end; int c, p; static const char hex_digits[16] = "0123456789abcdef"; char digits[3]; 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 font->output->status; } static cairo_status_t cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font) { unsigned short r = CAIRO_TYPE1_PRIVATE_DICT_KEY; unsigned char *in, *end; char *out; int c, p; int i; in = (unsigned char *) font->eexec_segment; end = (unsigned char *) in + font->eexec_segment_size; font->cleartext = malloc (font->eexec_segment_size + 1); if (unlikely (font->cleartext == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); out = font->cleartext; while (in < end) { if (font->eexec_segment_is_ascii) { c = *in++; if (_cairo_isspace (c)) continue; c = (hex_to_int (c) << 4) | hex_to_int (*in++); } else { c = *in++; } p = c ^ (r >> 8); r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; *out++ = p; } font->cleartext_end = out; /* Overwrite random bytes with spaces. * * The first 4 bytes of the cleartext are the random bytes * required by the encryption algorithm. When encrypting the * cleartext, the first ciphertext byte must not be a white space * character and the first 4 bytes must not be an ASCII Hex * character. Some fonts do not check that their randomly chosen * bytes results in ciphertext that complies with this * restriction. This may cause problems for some PDF consumers. By * replacing the random bytes with spaces, the first four bytes of * ciphertext will always be 0xf9, 0x83, 0xef, 0x00 which complies * with this restriction. Using spaces also means we don't have to * skip over the random bytes when parsing the cleartext. */ for (i = 0; i < 4 && i < font->eexec_segment_size; i++) font->cleartext[i] = ' '; /* Ensure strtol() can not scan past the end of the cleartext */ font->cleartext[font->eexec_segment_size] = 0; return CAIRO_STATUS_SUCCESS; } static const char * skip_token (const char *p, const char *end) { while (p < end && _cairo_isspace(*p)) p++; while (p < end && !_cairo_isspace(*p)) p++; if (p == end) return NULL; return p; } static void cairo_type1_font_subset_decrypt_charstring (const unsigned char *in, int size, unsigned char *out) { unsigned short r = CAIRO_TYPE1_CHARSTRING_KEY; int c, p, i; for (i = 0; i < size; i++) { c = *in++; p = c ^ (r >> 8); r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; *out++ = p; } } static const unsigned char * cairo_type1_font_subset_decode_integer (const unsigned char *p, int *integer) { if (*p <= 246) { *integer = *p++ - 139; } else if (*p <= 250) { *integer = (p[0] - 247) * 256 + p[1] + 108; p += 2; } else if (*p <= 254) { *integer = -(p[0] - 251) * 256 - p[1] - 108; p += 2; } else { *integer = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]; p += 5; } return p; } static cairo_status_t use_standard_encoding_glyph (cairo_type1_font_subset_t *font, int index) { const char *glyph_name; unsigned int i; if (index < 0 || index > 255) return CAIRO_STATUS_SUCCESS; glyph_name = _cairo_ps_standard_encoding_to_glyphname (index); if (glyph_name == NULL) return CAIRO_STATUS_SUCCESS; for (i = 0; i < font->base.num_glyphs; i++) { if (font->glyph_names[i] && strcmp (font->glyph_names[i], glyph_name) == 0) { cairo_type1_font_subset_use_glyph (font, i); return CAIRO_STATUS_SUCCESS; } } return CAIRO_INT_STATUS_UNSUPPORTED; } #define TYPE1_CHARSTRING_COMMAND_ESCAPE 0x0c #define TYPE1_CHARSTRING_COMMAND_SEAC 0x0c06 #define TYPE1_CHARSTRING_COMMAND_SBW 0x0c07 #define TYPE1_CHARSTRING_COMMAND_HSBW 0x0d #define TYPE1_CHARSTRING_COMMAND_CALLSUBR 0x0a #define TYPE1_CHARSTRING_COMMAND_CALLOTHERSUBR 0x0c10 #define TYPE1_CHARSTRING_COMMAND_POP 0x0c11 /* Get glyph width and look for seac operatorParse charstring */ static cairo_status_t cairo_type1_font_subset_parse_charstring (cairo_type1_font_subset_t *font, int glyph, const char *encrypted_charstring, int encrypted_charstring_length) { cairo_status_t status; unsigned char *charstring; const unsigned char *end; const unsigned char *p; cairo_bool_t last_op_was_integer; int command; int subr_num, i; charstring = malloc (encrypted_charstring_length); if (unlikely (charstring == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); cairo_type1_font_subset_decrypt_charstring ((const unsigned char *) encrypted_charstring, encrypted_charstring_length, charstring); end = charstring + encrypted_charstring_length; p = charstring + 4; last_op_was_integer = FALSE; while (p < end) { if (*p < 32) { command = *p++; switch (command) { case TYPE1_CHARSTRING_COMMAND_HSBW: font->glyphs[glyph].width = font->build_stack.stack[1]/font->base.units_per_em; font->build_stack.sp = 0; last_op_was_integer = FALSE; break; case TYPE1_CHARSTRING_COMMAND_CALLSUBR: if (font->subset_subrs && last_op_was_integer && font->build_stack.top_value >= 0 && font->build_stack.top_value < font->num_subrs) { subr_num = font->build_stack.top_value; font->subrs[subr_num].used = TRUE; last_op_was_integer = FALSE; status = cairo_type1_font_subset_parse_charstring (font, glyph, font->subrs[subr_num].subr_string, font->subrs[subr_num].subr_length); } else { font->subset_subrs = FALSE; } break; case TYPE1_CHARSTRING_COMMAND_ESCAPE: command = command << 8 | *p++; switch (command) { case TYPE1_CHARSTRING_COMMAND_SEAC: /* The seac command takes five integer arguments. The * last two are glyph indices into the PS standard * encoding give the names of the glyphs that this * glyph is composed from. All we need to do is to * make sure those glyphs are present in the subset * under their standard names. */ status = use_standard_encoding_glyph (font, font->build_stack.stack[3]); if (unlikely (status)) return status; status = use_standard_encoding_glyph (font, font->build_stack.stack[4]); if (unlikely (status)) return status; font->build_stack.sp = 0; last_op_was_integer = FALSE; break; case TYPE1_CHARSTRING_COMMAND_SBW: font->glyphs[glyph].width = font->build_stack.stack[2]/font->base.units_per_em; font->build_stack.sp = 0; last_op_was_integer = FALSE; break; case TYPE1_CHARSTRING_COMMAND_CALLOTHERSUBR: for (i = 0; i < font->build_stack.sp; i++) font->ps_stack.other_subr_args[i] = font->build_stack.stack[i]; font->ps_stack.num_other_subr_args = font->build_stack.sp; font->ps_stack.cur_other_subr_arg = 0; font->build_stack.sp = 0; last_op_was_integer = FALSE; break; case TYPE1_CHARSTRING_COMMAND_POP: if (font->ps_stack.num_other_subr_args > font->ps_stack.cur_other_subr_arg) { font->build_stack.top_value = font->ps_stack.other_subr_args[font->ps_stack.cur_other_subr_arg++]; last_op_was_integer = TRUE; } else { font->subset_subrs = FALSE; } break; default: font->build_stack.sp = 0; last_op_was_integer = FALSE; break; } break; default: font->build_stack.sp = 0; last_op_was_integer = FALSE; break; } } else { /* integer argument */ p = cairo_type1_font_subset_decode_integer (p, &font->build_stack.top_value); last_op_was_integer = TRUE; if (font->build_stack.sp < TYPE1_STACKSIZE) font->build_stack.stack[font->build_stack.sp++] = font->build_stack.top_value; } } free (charstring); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_build_subr_list (cairo_type1_font_subset_t *font, int subr_number, const char *encrypted_charstring, int encrypted_charstring_length) { font->subrs[subr_number].subr_string = encrypted_charstring; font->subrs[subr_number].subr_length = encrypted_charstring_length; return CAIRO_STATUS_SUCCESS; } static cairo_status_t write_used_subrs (cairo_type1_font_subset_t *font, int subr_number, const char *subr_string, int subr_string_length) { cairo_status_t status; char buffer[256]; int length; if (!font->subrs[subr_number].used) return CAIRO_STATUS_SUCCESS; length = snprintf (buffer, sizeof buffer, "dup %d %d %s ", subr_number, subr_string_length, font->rd); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); if (unlikely (status)) return status; status = cairo_type1_font_subset_write_encrypted (font, subr_string, subr_string_length); if (unlikely (status)) return status; length = snprintf (buffer, sizeof buffer, "%s\n", font->np); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } typedef cairo_status_t (*subr_func_t) (cairo_type1_font_subset_t *font, int subr_number, const char *subr_string, int subr_string_length); static cairo_status_t cairo_type1_font_for_each_subr (cairo_type1_font_subset_t *font, const char *array_start, const char *cleartext_end, subr_func_t func, const char **array_end) { const char *p, *subr_string; char *end; int subr_num, subr_length; cairo_status_t status; /* We're looking at "dup" at the start of the first subroutine. The subroutines * definitions are on the form: * * dup 5 23 RD <23 binary bytes> NP * * or alternatively using -| and |- instead of RD and ND. * The first number is the subroutine number. */ p = array_start; while (p + 3 < cleartext_end && strncmp (p, "dup", 3) == 0) { p = skip_token (p, cleartext_end); /* get subr number */ subr_num = strtol (p, &end, 10); if (p == end) return CAIRO_INT_STATUS_UNSUPPORTED; if (subr_num < 0 || subr_num >= font->num_subrs) return CAIRO_INT_STATUS_UNSUPPORTED; /* get subr length */ p = end; subr_length = strtol (p, &end, 10); if (p == end) return CAIRO_INT_STATUS_UNSUPPORTED; /* Skip past -| or RD to binary data. There is exactly one space * between the -| or RD token and the encrypted data, thus '+ 1'. */ subr_string = skip_token (end, cleartext_end) + 1; status = func (font, subr_num, subr_string, subr_length); if (unlikely (status)) return status; /* Skip binary data and | or NP token. */ p = skip_token (subr_string + subr_length, cleartext_end); while (p < cleartext_end && _cairo_isspace(*p)) p++; } *array_end = (char *) p; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_build_glyph_list (cairo_type1_font_subset_t *font, int glyph_number, const char *name, int name_length, const char *encrypted_charstring, int encrypted_charstring_length) { char *s; glyph_data_t glyph; cairo_status_t status; s = malloc (name_length + 1); if (unlikely (s == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); strncpy (s, name, name_length); s[name_length] = 0; status = _cairo_array_append (&font->glyph_names_array, &s); if (unlikely (status)) return status; glyph.subset_index = -1; glyph.width = 0; glyph.encrypted_charstring = encrypted_charstring; glyph.encrypted_charstring_length = encrypted_charstring_length; status = _cairo_array_append (&font->glyphs_array, &glyph); return status; } static cairo_status_t write_used_glyphs (cairo_type1_font_subset_t *font, int glyph_number, const char *name, int name_length, const char *charstring, int charstring_length) { cairo_status_t status; char buffer[256]; int length; if (font->glyphs[glyph_number].subset_index < 0) return CAIRO_STATUS_SUCCESS; length = snprintf (buffer, sizeof buffer, "/%.*s %d %s ", name_length, name, charstring_length, font->rd); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); if (unlikely (status)) return status; status = cairo_type1_font_subset_write_encrypted (font, charstring, charstring_length); if (unlikely (status)) return status; length = snprintf (buffer, sizeof buffer, "%s\n", font->nd); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } typedef cairo_status_t (*glyph_func_t) (cairo_type1_font_subset_t *font, int glyph_number, const char *name, int name_length, const char *charstring, int charstring_length); static cairo_status_t cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font, const char *dict_start, const char *dict_end, glyph_func_t func, const char **dict_out) { int charstring_length, name_length; const char *p, *charstring, *name; char *end; cairo_status_t status; int glyph_count; /* We're looking at '/' in the name of the first glyph. The glyph * definitions are on the form: * * /name 23 RD <23 binary bytes> ND * * or alternatively using -| and |- instead of RD and ND. * * We parse the glyph name and see if it is in the subset. If it * is, we call the specified callback with the glyph name and * glyph data, otherwise we just skip it. We need to parse * through a glyph definition; we can't just find the next '/', * since the binary data could contain a '/'. */ p = dict_start; glyph_count = 0; while (*p == '/') { name = p + 1; p = skip_token (p, dict_end); name_length = p - name; charstring_length = strtol (p, &end, 10); if (p == end) return CAIRO_INT_STATUS_UNSUPPORTED; /* Skip past -| or RD to binary data. There is exactly one space * between the -| or RD token and the encrypted data, thus '+ 1'. */ charstring = skip_token (end, dict_end) + 1; /* Skip binary data and |- or ND token. */ p = skip_token (charstring + charstring_length, dict_end); while (p < dict_end && _cairo_isspace(*p)) p++; /* In case any of the skip_token() calls above reached EOF, p will * be equal to dict_end. */ if (p == dict_end) return CAIRO_INT_STATUS_UNSUPPORTED; status = func (font, glyph_count++, name, name_length, charstring, charstring_length); if (unlikely (status)) return status; } *dict_out = p; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, const char *name) { cairo_status_t status; const char *p, *subrs, *charstrings, *array_start, *array_end, *dict_start, *dict_end; const char *closefile_token; char buffer[32], *subr_count_end, *glyph_count_end; int num_charstrings, length; const cairo_scaled_font_backend_t *backend; unsigned int i; /* The private dict holds hint information, common subroutines and * the actual glyph definitions (charstrings). * * What we do here is scan directly to the /Subrs token, which * marks the beginning of the subroutines. We then read in all the * subroutines then move on to the /CharString token, which marks * the beginning of the glyph definitions, and read in the chastrings. * * The charstrings are parsed to extracts glyph widths, work out * which subroutines are called, and too see if any extra glyphs * need to be included due to the use of the seac glyph combining * operator. * * Finally the private dict is copied to the subset font minus the * subroutines and charstrings not required. */ /* Find start of Subrs */ subrs = find_token (font->cleartext, font->cleartext_end, "/Subrs"); if (subrs == NULL) { font->subset_subrs = FALSE; p = font->cleartext; goto skip_subrs; } /* Scan past /Subrs and get the array size. */ p = subrs + strlen ("/Subrs"); font->num_subrs = strtol (p, &subr_count_end, 10); if (subr_count_end == p) return CAIRO_INT_STATUS_UNSUPPORTED; if (font->num_subrs <= 0) return CAIRO_INT_STATUS_UNSUPPORTED; font->subrs = calloc (font->num_subrs, sizeof (font->subrs[0])); if (unlikely (font->subrs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* look for "dup" which marks the beginning of the first subr */ array_start = find_token (subr_count_end, font->cleartext_end, "dup"); if (subrs == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; /* Read in the subroutines */ status = cairo_type1_font_for_each_subr (font, array_start, font->cleartext_end, cairo_type1_font_subset_build_subr_list, &array_end); if (unlikely(status)) return status; p = array_end; skip_subrs: /* Find start of CharStrings */ charstrings = find_token (p, font->cleartext_end, "/CharStrings"); if (charstrings == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; /* Scan past /CharStrings and the integer following it. */ p = charstrings + strlen ("/CharStrings"); num_charstrings = strtol (p, &glyph_count_end, 10); if (p == glyph_count_end) return CAIRO_INT_STATUS_UNSUPPORTED; /* Look for a '/' which marks the beginning of the first glyph * definition. */ for (p = glyph_count_end; p < font->cleartext_end; p++) if (*p == '/') break; if (p == font->cleartext_end) return CAIRO_INT_STATUS_UNSUPPORTED; dict_start = p; /* Now that we have the private dictionary broken down in * sections, do the first pass through the glyph definitions to * build a list of glyph names and charstrings. */ status = cairo_type1_font_subset_for_each_glyph (font, dict_start, font->cleartext_end, cairo_type1_font_subset_build_glyph_list, &dict_end); if (unlikely(status)) return status; font->glyphs = _cairo_array_index (&font->glyphs_array, 0); font->glyph_names = _cairo_array_index (&font->glyph_names_array, 0); font->base.num_glyphs = _cairo_array_num_elements (&font->glyphs_array); font->subset_index_to_glyphs = calloc (font->base.num_glyphs, sizeof font->subset_index_to_glyphs[0]); if (unlikely (font->subset_index_to_glyphs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); backend = font->scaled_font_subset->scaled_font->backend; if (!backend->index_to_glyph_name) return CAIRO_INT_STATUS_UNSUPPORTED; /* Find the glyph number corresponding to each glyph in the subset * and mark it as in use */ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { unsigned long index; status = backend->index_to_glyph_name (font->scaled_font_subset->scaled_font, font->glyph_names, font->base.num_glyphs, font->scaled_font_subset->glyphs[i], &index); if (unlikely(status)) return status; cairo_type1_font_subset_use_glyph (font, index); } /* Go through the charstring of each glyph in use, get the glyph * width and figure out which extra glyphs may be required by the * seac operator. Also subset the Subrs. */ for (i = 0; i < font->base.num_glyphs; i++) { if (font->glyphs[i].subset_index >= 0) { font->build_stack.sp = 0; font->ps_stack.num_other_subr_args = 0; status = cairo_type1_font_subset_parse_charstring (font, i, font->glyphs[i].encrypted_charstring, font->glyphs[i].encrypted_charstring_length); if (unlikely (status)) return status; } } closefile_token = find_token (dict_end, font->cleartext_end, "closefile"); if (closefile_token == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; /* We're ready to start outputting. First write the header, * i.e. the public part of the font dict.*/ status = cairo_type1_font_subset_write_header (font, name); if (unlikely (status)) return status; font->base.header_size = _cairo_output_stream_get_position (font->output); /* Start outputting the private dict */ if (font->subset_subrs) { /* First output everything up to the start of the Subrs array. */ status = cairo_type1_font_subset_write_encrypted (font, font->cleartext, array_start - font->cleartext); if (unlikely (status)) return status; /* Write out the subr definitions for each of the glyphs in * the subset. */ status = cairo_type1_font_for_each_subr (font, array_start, font->cleartext_end, write_used_subrs, &p); if (unlikely (status)) return status; } else { p = font->cleartext; } /* If subr subsetting, output everything from end of subrs to * start of /CharStrings token. If not subr subsetting, output * everything start of private dict to start of /CharStrings * token. */ status = cairo_type1_font_subset_write_encrypted (font, p, charstrings - p); if (unlikely (status)) return status; /* Write out new charstring count */ length = snprintf (buffer, sizeof buffer, "/CharStrings %d", font->num_glyphs); status = cairo_type1_font_subset_write_encrypted (font, buffer, length); if (unlikely (status)) return status; /* Write out text between the charstring count and the first * charstring definition */ status = cairo_type1_font_subset_write_encrypted (font, glyph_count_end, dict_start - glyph_count_end); if (unlikely (status)) return status; /* Write out the charstring definitions for each of the glyphs in * the subset. */ status = cairo_type1_font_subset_for_each_glyph (font, dict_start, font->cleartext_end, write_used_glyphs, &p); if (unlikely (status)) return status; /* Output what's left between the end of the glyph definitions and * the end of the private dict to the output. */ status = cairo_type1_font_subset_write_encrypted (font, p, closefile_token - p + strlen ("closefile") + 1); if (unlikely (status)) return status; if (font->hex_encode) _cairo_output_stream_write (font->output, "\n", 1); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_type1_font_subset_write_trailer(cairo_type1_font_subset_t *font) { const char *cleartomark_token; int i; static const char zeros[65] = "0000000000000000000000000000000000000000000000000000000000000000\n"; for (i = 0; i < 8; i++) _cairo_output_stream_write (font->output, zeros, sizeof zeros); cleartomark_token = find_token (font->type1_data, font->type1_end, "cleartomark"); if (cleartomark_token) { /* Some fonts have conditional save/restore around the entire * font dict, so we need to retain whatever postscript code * that may come after 'cleartomark'. */ _cairo_output_stream_write (font->output, cleartomark_token, font->type1_end - cleartomark_token); if (*(font->type1_end - 1) != '\n') _cairo_output_stream_printf (font->output, "\n"); } else if (!font->eexec_segment_is_ascii) { /* Fonts embedded in PDF may omit the fixed-content portion * that includes the 'cleartomark' operator. Type 1 in PDF is * always binary. */ _cairo_output_stream_printf (font->output, "cleartomark\n"); } else { return CAIRO_INT_STATUS_UNSUPPORTED; } /* some fonts do not have a newline at the end of the last line */ _cairo_output_stream_printf (font->output, "\n"); return CAIRO_STATUS_SUCCESS; } static cairo_status_t type1_font_write (void *closure, const unsigned char *data, unsigned int length) { cairo_type1_font_subset_t *font = closure; return _cairo_array_append_multiple (&font->contents, data, length); } static cairo_status_t cairo_type1_font_subset_write (cairo_type1_font_subset_t *font, const char *name) { cairo_status_t status; status = cairo_type1_font_subset_find_segments (font); if (unlikely (status)) return status; status = cairo_type1_font_subset_decrypt_eexec_segment (font); if (unlikely (status)) return status; /* Determine which glyph definition delimiters to use. */ if (find_token (font->cleartext, font->cleartext_end, "/-|") != NULL) { font->rd = "-|"; font->nd = "|-"; font->np = "|"; } else if (find_token (font->cleartext, font->cleartext_end, "/RD") != NULL) { font->rd = "RD"; font->nd = "ND"; font->np = "NP"; } else { /* Don't know *what* kind of font this is... */ return CAIRO_INT_STATUS_UNSUPPORTED; } font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY; font->hex_column = 0; status = cairo_type1_font_subset_get_bbox (font); if (unlikely (status)) return status; status = cairo_type1_font_subset_get_fontname (font); if (unlikely (status)) return status; status = cairo_type1_font_subset_write_private_dict (font, name); if (unlikely (status)) return status; font->base.data_size = _cairo_output_stream_get_position (font->output) - font->base.header_size; status = cairo_type1_font_subset_write_trailer (font); if (unlikely (status)) return status; font->base.trailer_size = _cairo_output_stream_get_position (font->output) - font->base.header_size - font->base.data_size; return CAIRO_STATUS_SUCCESS; } static cairo_bool_t check_fontdata_is_type1 (const unsigned char *data, long length) { /* Test for Type 1 Binary (PFB) */ if (length > 2 && data[0] == 0x80 && data[1] == 0x01) return TRUE; /* Test for Type 1 1 ASCII (PFA) */ if (length > 2 && data[0] == '%' && data[1] == '!') return TRUE; return FALSE; } static cairo_status_t cairo_type1_font_subset_generate (void *abstract_font, const char *name) { cairo_type1_font_subset_t *font = abstract_font; cairo_scaled_font_t *scaled_font; cairo_status_t status; unsigned long data_length; scaled_font = font->scaled_font_subset->scaled_font; if (!scaled_font->backend->load_type1_data) return CAIRO_INT_STATUS_UNSUPPORTED; status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &data_length); if (status) return CAIRO_INT_STATUS_UNSUPPORTED; font->type1_length = data_length; font->type1_data = malloc (font->type1_length); if (unlikely (font->type1_data == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = scaled_font->backend->load_type1_data (scaled_font, 0, (unsigned char *) font->type1_data, &data_length); if (unlikely (status)) return status; if (!check_fontdata_is_type1 ((unsigned char *)font->type1_data, data_length)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_array_grow_by (&font->contents, 4096); if (unlikely (status)) return status; font->output = _cairo_output_stream_create (type1_font_write, NULL, font); if (unlikely ((status = font->output->status))) return status; status = cairo_type1_font_subset_write (font, name); if (unlikely (status)) return status; font->base.data = _cairo_array_index (&font->contents, 0); return status; } static cairo_status_t _cairo_type1_font_subset_fini (cairo_type1_font_subset_t *font) { cairo_status_t status = CAIRO_STATUS_SUCCESS; unsigned int i; /* If the subset generation failed, some of the pointers below may * be NULL depending on at which point the error occurred. */ _cairo_array_fini (&font->contents); free (font->type1_data); for (i = 0; i < _cairo_array_num_elements (&font->glyph_names_array); i++) { char **s; s = _cairo_array_index (&font->glyph_names_array, i); free (*s); } _cairo_array_fini (&font->glyph_names_array); _cairo_array_fini (&font->glyphs_array); free (font->subrs); if (font->output != NULL) status = _cairo_output_stream_destroy (font->output); free (font->base.base_font); free (font->subset_index_to_glyphs); return status; } cairo_status_t _cairo_type1_subset_init (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_subset_t font; cairo_status_t status, status_ignored; unsigned long length; unsigned int i; char buf[30]; /* We need to use a fallback font generated from the synthesized outlines. */ if (scaled_font_subset->scaled_font->backend->is_synthetic && scaled_font_subset->scaled_font->backend->is_synthetic (scaled_font_subset->scaled_font)) return CAIRO_INT_STATUS_UNSUPPORTED; status = _cairo_type1_font_subset_init (&font, scaled_font_subset, hex_encode); if (unlikely (status)) return status; status = cairo_type1_font_subset_generate (&font, name); if (unlikely (status)) goto fail1; if (font.base.base_font) { type1_subset->base_font = strdup (font.base.base_font); } else { snprintf(buf, sizeof (buf), "CairoFont-%u-%u", scaled_font_subset->font_id, scaled_font_subset->subset_id); type1_subset->base_font = strdup (buf); } if (unlikely (type1_subset->base_font == NULL)) goto fail1; type1_subset->widths = calloc (sizeof (double), font.num_glyphs); if (unlikely (type1_subset->widths == NULL)) goto fail2; for (i = 0; i < font.base.num_glyphs; i++) { if (font.glyphs[i].subset_index < 0) continue; type1_subset->widths[font.glyphs[i].subset_index] = font.glyphs[i].width; } type1_subset->x_min = font.base.x_min; type1_subset->y_min = font.base.y_min; type1_subset->x_max = font.base.x_max; type1_subset->y_max = font.base.y_max; type1_subset->ascent = font.base.ascent; type1_subset->descent = font.base.descent; length = font.base.header_size + font.base.data_size + font.base.trailer_size; type1_subset->data = malloc (length); if (unlikely (type1_subset->data == NULL)) goto fail3; memcpy (type1_subset->data, _cairo_array_index (&font.contents, 0), length); type1_subset->header_length = font.base.header_size; type1_subset->data_length = font.base.data_size; type1_subset->trailer_length = font.base.trailer_size; return _cairo_type1_font_subset_fini (&font); fail3: free (type1_subset->widths); fail2: free (type1_subset->base_font); fail1: status_ignored = _cairo_type1_font_subset_fini (&font); return status; } void _cairo_type1_subset_fini (cairo_type1_subset_t *subset) { free (subset->base_font); free (subset->widths); free (subset->data); } cairo_bool_t _cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font) { cairo_status_t status; unsigned long length; unsigned char buf[64]; if (!scaled_font->backend->load_type1_data) return FALSE; status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &length); if (status) return FALSE; /* We only need a few bytes to test for Type 1 */ if (length > sizeof (buf)) length = sizeof (buf); status = scaled_font->backend->load_type1_data (scaled_font, 0, buf, &length); if (status) return FALSE; return check_fontdata_is_type1 (buf, length); } #endif /* CAIRO_HAS_FONT_SUBSET */