From f6043b06d658d307b9e3ac36f14d049f0a8664bf Mon Sep 17 00:00:00 2001 From: Adrian Johnson Date: Thu, 21 Jul 2011 21:17:18 +0930 Subject: [PATCH] Add support for subsetting bare CFF fonts This avoids fallback when using poppler cairo for printing PDFs with CFF fonts. The current CFF subsetting only works with Opentype/CFF fonts. CFF fonts inside PDF files are usually embedded as a bare CFF font without the Opentype wrapper. Making the CFF subset work with bare CFF fonts requires doing a bit of extra work to extract the fontname, font bbox, and glyph widths from the CFF data instead of using the Opentype tables. --- src/cairo-cff-subset.c | 583 +++++++++++++++++++++++++++++++++-------- src/cairo-ft-font.c | 12 +- 2 files changed, 481 insertions(+), 114 deletions(-) diff --git a/src/cairo-cff-subset.c b/src/cairo-cff-subset.c index 02f2cba61..918a0b290 100644 --- a/src/cairo-cff-subset.c +++ b/src/cairo-cff-subset.c @@ -57,14 +57,17 @@ #define CHARSET_OP 0x000f #define CHARSTRINGS_OP 0x0011 #define COPYRIGHT_OP 0x0c00 +#define DEFAULTWIDTH_OP 0x0014 #define ENCODING_OP 0x0010 #define FAMILYNAME_OP 0x0003 #define FDARRAY_OP 0x0c24 #define FDSELECT_OP 0x0c25 #define FONTBBOX_OP 0x0005 +#define FONTMATRIX_OP 0x0c07 #define FONTNAME_OP 0x0c26 #define FULLNAME_OP 0x0002 #define LOCAL_SUB_OP 0x0013 +#define NOMINALWIDTH_OP 0x0015 #define NOTICE_OP 0x0001 #define POSTSCRIPT_OP 0x0c15 #define PRIVATE_OP 0x0012 @@ -90,6 +93,11 @@ #define TYPE2_vstemhm 0x0017 #define TYPE2_callgsubr 0x001d +#define TYPE2_rmoveto 0x0015 +#define TYPE2_hmoveto 0x0016 +#define TYPE2_vmoveto 0x0004 + + #define MAX_SUBROUTINE_NESTING 10 /* From Type2 Charstring spec */ @@ -136,9 +144,12 @@ typedef struct _cairo_cff_font { cairo_array_t local_sub_index; int num_glyphs; cairo_bool_t is_cid; + cairo_bool_t is_opentype; int units_per_em; int global_sub_bias; int local_sub_bias; + int default_width; + int nominal_width; /* CID Font Data */ int *fdselect; @@ -147,6 +158,8 @@ typedef struct _cairo_cff_font { cairo_hash_table_t **fd_private_dict; cairo_array_t *fd_local_sub_index; int *fd_local_sub_bias; + int *fd_default_width; + int *fd_nominal_width; /* Subsetted Font Data */ char *subset_font_name; @@ -175,6 +188,10 @@ typedef struct _cairo_cff_font { int type2_num_hints; int type2_hintmask_bytes; int type2_nesting_level; + cairo_bool_t type2_seen_first_int; + cairo_bool_t type2_find_width; + cairo_bool_t type2_found_width; + int type2_width; } cairo_cff_font_t; @@ -238,6 +255,76 @@ decode_integer (unsigned char *p, int *integer) return p; } +static char * +decode_nibble (int n, char *buf) +{ + switch (n) + { + case 0xa: + *buf++ = '.'; + break; + case 0xb: + *buf++ = 'E'; + break; + case 0xc: + *buf++ = 'E'; + *buf++ = '-'; + break; + case 0xd: + *buf++ = '-'; + break; + case 0xe: + *buf++ = '-'; + break; + case 0xf: + break; + default: + *buf++ = '0' + n; + break; + } + + return buf; +} + +static unsigned char * +decode_real (unsigned char *p, double *real) +{ + int n; + char buffer[100]; + char *buf = buffer; + char *buf_end = buffer + sizeof (buf); + + p++; + while (buf + 2 < buf_end) { + n = *p >> 4; + buf = decode_nibble (n, buf); + n = *p & 0x0f; + buf = decode_nibble (n, buf); + if ((*p & 0x0f) == 0x0f) + break; + p++; + }; + *buf = 0; + + if (sscanf(buffer, "%lf", real) != 1) + *real = 0.0; + + return p; +} + +static unsigned char * +decode_number (unsigned char *p, double *number) +{ + if (*p == 30) { + p = decode_real (p, number); + } else { + int i; + p = decode_integer (p, &i); + *number = i; + } + return p; +} + static unsigned char * decode_operator (unsigned char *p, unsigned short *operator) { @@ -742,6 +829,7 @@ cairo_cff_font_read_header (cairo_cff_font_t *font) if (font->data_length < sizeof (cff_header_t)) return CAIRO_INT_STATUS_UNSUPPORTED; + font->header = (cff_header_t *) font->data; font->current_ptr = font->data + font->header->header_size; @@ -753,11 +841,19 @@ cairo_cff_font_read_name (cairo_cff_font_t *font) { cairo_array_t index; cairo_int_status_t status; + cff_index_element_t *element; - /* The original font name is not used in the subset. Read the name - * index to skip over it. */ cff_index_init (&index); status = cff_index_read (&index, &font->current_ptr, font->data_end); + if (!font->is_opentype) { + element = _cairo_array_index (&index, 0); + font->ps_name = malloc (element->length + 1); + if (unlikely (font->ps_name == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (font->ps_name, element->data, element->length); + font->ps_name[element->length] = 0; + } cff_index_fini (&index); return status; @@ -769,6 +865,8 @@ cairo_cff_font_read_private_dict (cairo_cff_font_t *font, cairo_array_t *local_sub_index, int *local_sub_bias, cairo_bool_t **local_subs_used, + int *default_width, + int *nominal_width, unsigned char *ptr, int size) { @@ -798,9 +896,18 @@ cairo_cff_font_read_private_dict (cairo_cff_font_t *font, status = cff_dict_set_operands (private_dict, LOCAL_SUB_OP, buf, end_buf - buf); if (unlikely (status)) return status; - } + *default_width = 0; + operand = cff_dict_get_operands (private_dict, DEFAULTWIDTH_OP, &i); + if (operand) + decode_integer (operand, default_width); + + *nominal_width = 0; + operand = cff_dict_get_operands (private_dict, NOMINALWIDTH_OP, &i); + if (operand) + decode_integer (operand, nominal_width); + num_subs = _cairo_array_num_elements (local_sub_index); *local_subs_used = calloc (num_subs, sizeof (cairo_bool_t)); if (unlikely (*local_subs_used == NULL)) @@ -899,6 +1006,18 @@ cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) goto fail; } + font->fd_default_width = calloc (sizeof (int), font->num_fontdicts); + if (unlikely (font->fd_default_width == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + font->fd_nominal_width = calloc (sizeof (int), font->num_fontdicts); + if (unlikely (font->fd_nominal_width == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + for (i = 0; i < font->num_fontdicts; i++) { status = cff_dict_init (&font->fd_dict[i]); if (unlikely (status)) @@ -926,6 +1045,8 @@ cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) &font->fd_local_sub_index[i], &font->fd_local_sub_bias[i], &font->fd_local_subs_used[i], + &font->fd_default_width[i], + &font->fd_nominal_width[i], font->data + offset, size); if (unlikely (status)) @@ -948,6 +1069,58 @@ fail: return status; } +static void +cairo_cff_font_read_font_metrics (cairo_cff_font_t *font, cairo_hash_table_t *top_dict) +{ + unsigned char *p; + unsigned char *end; + int size; + double x_min, y_min, x_max, y_max; + double xx, yx, xy, yy; + + font->x_min = 0.0; + font->y_min = 0.0; + font->x_max = 0.0; + font->y_max = 0.0; + p = cff_dict_get_operands (font->top_dict, FONTBBOX_OP, &size); + if (p) { + end = p + size; + if (p < end) + p = decode_number (p, &x_min); + if (p < end) + p = decode_number (p, &y_min); + if (p < end) + p = decode_number (p, &x_max); + if (p < end) + p = decode_number (p, &y_max); + } + font->x_min = floor (x_min); + font->y_min = floor (y_min); + font->x_max = floor (x_max); + font->y_max = floor (y_max); + font->ascent = font->y_max; + font->descent = font->y_min; + + xx = 0.001; + yx = 0.0; + xy = 0.0; + yy = 0.001; + p = cff_dict_get_operands (font->top_dict, FONTMATRIX_OP, &size); + if (p) { + end = p + size; + if (p < end) + p = decode_number (p, &xx); + if (p < end) + p = decode_number (p, &yx); + if (p < end) + p = decode_number (p, &xy); + if (p < end) + p = decode_number (p, &yy); + } + /* Freetype uses 1/yy to get units per EM */ + font->units_per_em = _cairo_round(1.0/yy); +} + static cairo_int_status_t cairo_cff_font_read_top_dict (cairo_cff_font_t *font) { @@ -984,6 +1157,9 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font) goto fail; font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index); + if (!font->is_opentype) + cairo_cff_font_read_font_metrics (font, font->top_dict); + if (font->is_cid) { operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size); decode_integer (operand, &offset); @@ -1005,6 +1181,8 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font) &font->local_sub_index, &font->local_sub_bias, &font->local_subs_used, + &font->default_width, + &font->nominal_width, font->data + offset, size); if (unlikely (status)) @@ -1243,21 +1421,30 @@ type2_decode_integer (unsigned char *p, int *integer) } /* Type 2 charstring parser for finding calls to local or global - * subroutines. When we find a subroutine operator, the subroutine is - * marked as in use and recursively followed. The subroutine number is - * the value on the top of the stack when the subroutine operator is - * executed. In most fonts the subroutine number is encoded in an - * integer immediately preceding the subroutine operator. However it - * is possible for the subroutine number on the stack to be the result - * of a computation (in which case there will be an operator preceding + * subroutines. For non Opentype CFF fonts it also gets the glyph + * widths. + * + * When we find a subroutine operator, the subroutine is marked as in + * use and recursively followed. The subroutine number is the value on + * the top of the stack when the subroutine operator is executed. In + * most fonts the subroutine number is encoded in an integer + * immediately preceding the subroutine operator. However it is + * possible for the subroutine number on the stack to be the result of + * a computation (in which case there will be an operator preceding * the subroutine operator). If this occurs, subroutine subsetting is * disabled since we can't easily determine which subroutines are * used. + * + * The width, if present, is the first integer in the charstring. The + * only way to confirm if an integer at the start of the charstring is + * the width is when the first stack clearing operator is parsed, + * check if there is an extra integer left over on the stack. */ static cairo_status_t cairo_cff_parse_charstring (cairo_cff_font_t *font, unsigned char *charstring, int length, - int glyph_id) + int glyph_id, + cairo_bool_t need_width) { unsigned char *p = charstring; unsigned char *end = charstring + length; @@ -1274,13 +1461,21 @@ cairo_cff_parse_charstring (cairo_cff_font_t *font, font->type2_stack_size++; font->type2_stack_top_value = integer; font->type2_stack_top_is_int = TRUE; + if (!font->type2_seen_first_int) { + font->type2_width = integer; + font->type2_seen_first_int = TRUE; + } } else if (*p == TYPE2_hstem || *p == TYPE2_vstem || *p == TYPE2_hstemhm || *p == TYPE2_vstemhm) { /* Hint operator. The number of hints declared by the * operator depends on the size of the stack. */ font->type2_stack_top_is_int = FALSE; font->type2_num_hints += font->type2_stack_size/2; + if (font->type2_find_width && font->type2_stack_size % 2) + font->type2_found_width = TRUE; + font->type2_stack_size = 0; + font->type2_find_width = FALSE; p++; } else if (*p == TYPE2_hintmask || *p == TYPE2_cntrmask) { /* Hintmask operator. These operators are followed by a @@ -1291,13 +1486,36 @@ cairo_cff_parse_charstring (cairo_cff_font_t *font, if (font->type2_hintmask_bytes == 0) { font->type2_stack_top_is_int = FALSE; font->type2_num_hints += font->type2_stack_size/2; + if (font->type2_find_width && font->type2_stack_size % 2) + font->type2_found_width = TRUE; + font->type2_stack_size = 0; + font->type2_find_width = FALSE; font->type2_hintmask_bytes = (font->type2_num_hints+7)/8; } hint_bytes = font->type2_hintmask_bytes; p++; p += hint_bytes; + } else if (*p == TYPE2_rmoveto) { + if (font->type2_find_width && font->type2_stack_size > 2) + font->type2_found_width = TRUE; + + font->type2_stack_size = 0; + font->type2_find_width = FALSE; + p++; + } else if (*p == TYPE2_hmoveto || *p == TYPE2_vmoveto) { + if (font->type2_find_width && font->type2_stack_size > 1) + font->type2_found_width = TRUE; + + font->type2_stack_size = 0; + font->type2_find_width = FALSE; + p++; + } else if (*p == TYPE2_endchar) { + if (font->type2_find_width && font->type2_stack_size > 0) + font->type2_found_width = TRUE; + + return CAIRO_STATUS_SUCCESS; } else if (*p == TYPE2_callsubr) { /* call to local subroutine */ if (! font->type2_stack_top_is_int) @@ -1309,20 +1527,25 @@ cairo_cff_parse_charstring (cairo_cff_font_t *font, p++; font->type2_stack_top_is_int = FALSE; font->type2_stack_size--; + if (font->type2_find_width && font->type2_stack_size == 0) + font->type2_seen_first_int = FALSE; + if (font->is_cid) { fd = font->fdselect[glyph_id]; sub_num = font->type2_stack_top_value + font->fd_local_sub_bias[fd]; element = _cairo_array_index (&font->fd_local_sub_index[fd], sub_num); if (! font->fd_local_subs_used[fd][sub_num]) { font->fd_local_subs_used[fd][sub_num] = TRUE; - cairo_cff_parse_charstring (font, element->data, element->length, glyph_id); + cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width); } } else { sub_num = font->type2_stack_top_value + font->local_sub_bias; element = _cairo_array_index (&font->local_sub_index, sub_num); - if (! font->local_subs_used[sub_num]) { + if (! font->local_subs_used[sub_num] || + (need_width && !font->type2_found_width)) + { font->local_subs_used[sub_num] = TRUE; - cairo_cff_parse_charstring (font, element->data, element->length, glyph_id); + cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width); } } font->type2_nesting_level--; @@ -1337,15 +1560,25 @@ cairo_cff_parse_charstring (cairo_cff_font_t *font, p++; font->type2_stack_size--; font->type2_stack_top_is_int = FALSE; + if (font->type2_find_width && font->type2_stack_size == 0) + font->type2_seen_first_int = FALSE; + sub_num = font->type2_stack_top_value + font->global_sub_bias; element = _cairo_array_index (&font->global_sub_index, sub_num); - if (! font->global_subs_used[sub_num]) { + if (! font->global_subs_used[sub_num] || + (need_width && !font->type2_found_width)) + { font->global_subs_used[sub_num] = TRUE; - cairo_cff_parse_charstring (font, element->data, element->length, glyph_id); + cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width); } font->type2_nesting_level--; } else if (*p == 12) { /* 2 byte instruction */ + + /* Most of the 2 byte operators */ + if (need_width && (p[1] < 0x22 || p[1] > 0x25)) + return CAIRO_INT_STATUS_UNSUPPORTED; + p += 2; font->type2_stack_top_is_int = FALSE; } else { @@ -1361,16 +1594,42 @@ cairo_cff_parse_charstring (cairo_cff_font_t *font, static cairo_status_t cairo_cff_find_subroutines_used (cairo_cff_font_t *font, unsigned char *charstring, int length, - int glyph_id) + int glyph_id, int subset_id) { + cairo_status_t status; + int width; + int fd; + font->type2_stack_size = 0; font->type2_stack_top_value = 0;; font->type2_stack_top_is_int = FALSE; font->type2_num_hints = 0; font->type2_hintmask_bytes = 0; font->type2_nesting_level = 0; + font->type2_seen_first_int = FALSE; + font->type2_find_width = TRUE; + font->type2_found_width = FALSE; + font->type2_width = 0; - return cairo_cff_parse_charstring (font, charstring, length, glyph_id); + status = cairo_cff_parse_charstring (font, charstring, length, glyph_id, TRUE); + + if (!font->is_opentype) { + if (font->is_cid) { + fd = font->fdselect[glyph_id]; + if (font->type2_found_width) + width = font->fd_nominal_width[fd] + font->type2_width; + else + width = font->fd_default_width[fd]; + } else { + if (font->type2_found_width) + width = font->nominal_width + font->type2_width; + else + width = font->default_width; + } + font->widths[subset_id] = width; + } + + return status; } static cairo_int_status_t @@ -1392,7 +1651,7 @@ cairo_cff_font_subset_charstrings_and_subroutines (cairo_cff_font_t *font) return status; if (font->subset_subroutines) { - status = cairo_cff_find_subroutines_used (font, element->data, element->length, glyph); + status = cairo_cff_find_subroutines_used (font, element->data, element->length, glyph, i); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { font->subset_subroutines = FALSE; } else if (unlikely (status)) @@ -1618,8 +1877,8 @@ cairo_cff_font_write_name (cairo_cff_font_t *font) cff_index_init (&index); status = cff_index_append_copy (&index, - (unsigned char *) font->subset_font_name, - strlen(font->subset_font_name)); + (unsigned char *) font->ps_name, + strlen(font->ps_name)); if (unlikely (status)) goto FAIL; @@ -2152,6 +2411,17 @@ cairo_cff_font_generate (cairo_cff_font_t *font, if (unlikely (status)) return status; + /* If the PS name is not found, create a CairoFont-x-y name. */ + if (font->ps_name == NULL) { + font->ps_name = malloc (30); + if (unlikely (font->ps_name == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + snprintf(font->ps_name, 30, "CairoFont-%u-%u", + font->scaled_font_subset->font_id, + font->scaled_font_subset->subset_id); + } + status = cairo_cff_font_subset_font (font); if (unlikely (status)) return status; @@ -2160,6 +2430,7 @@ cairo_cff_font_generate (cairo_cff_font_t *font, if (unlikely (status)) return status; + *data = _cairo_array_index (&font->output, 0); *length = _cairo_array_num_elements (&font->output); @@ -2187,7 +2458,7 @@ cairo_cff_font_create_set_widths (cairo_cff_font_t *font) return status; num_hmetrics = be16_to_cpu (hhea.num_hmetrics); - for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { glyph_index = font->scaled_font_subset->glyphs[i]; long_entry_size = 2 * sizeof (int16_t); short_entry_size = sizeof (int16_t); @@ -2214,69 +2485,63 @@ cairo_cff_font_create_set_widths (cairo_cff_font_t *font) return CAIRO_STATUS_SUCCESS; } -static cairo_int_status_t -_cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, - cairo_cff_font_t **font_return, - const char *subset_name) +static cairo_bool_t +check_fontdata_is_cff (const unsigned char *data, long length) { - const cairo_scaled_font_backend_t *backend; + cff_header_t *header; + + if (length < (long)sizeof (cff_header_t)) + return FALSE; + + header = (cff_header_t *) data; + if (header->major == 1 && + header->minor == 0 && + header->header_size == 4) + { + return TRUE; + } + + return FALSE; +} + +static cairo_int_status_t +_cairo_cff_font_load_opentype_cff (cairo_cff_font_t *font) +{ + const cairo_scaled_font_backend_t *backend = font->backend; cairo_status_t status; - cairo_cff_font_t *font; tt_head_t head; tt_hhea_t hhea; unsigned long size, data_length; - backend = scaled_font_subset->scaled_font->backend; if (!backend->load_truetype_table) return CAIRO_INT_STATUS_UNSUPPORTED; - /* We need to use a fallback font generated from the synthesized outlines. */ - if (backend->is_synthetic && backend->is_synthetic (scaled_font_subset->scaled_font)) - return CAIRO_INT_STATUS_UNSUPPORTED; - data_length = 0; - status = backend->load_truetype_table( scaled_font_subset->scaled_font, + status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_CFF, 0, NULL, &data_length); - if (unlikely (status)) + if (status) return status; size = sizeof (tt_head_t); - status = backend->load_truetype_table (scaled_font_subset->scaled_font, + status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_head, 0, (unsigned char *) &head, &size); if (unlikely (status)) return status; size = sizeof (tt_hhea_t); - status = backend->load_truetype_table (scaled_font_subset->scaled_font, + status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hhea, 0, (unsigned char *) &hhea, &size); if (unlikely (status)) return status; size = 0; - status = backend->load_truetype_table (scaled_font_subset->scaled_font, + status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, 0, NULL, &size); if (unlikely (status)) return status; - font = malloc (sizeof (cairo_cff_font_t)); - if (unlikely (font == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font->backend = backend; - font->scaled_font_subset = scaled_font_subset; - - _cairo_array_init (&font->output, sizeof (char)); - status = _cairo_array_grow_by (&font->output, 4096); - if (unlikely (status)) - goto fail2; - - font->subset_font_name = strdup (subset_name); - if (unlikely (font->subset_font_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail2; - } font->x_min = (int16_t) be16_to_cpu (head.x_min); font->y_min = (int16_t) be16_to_cpu (head.y_min); font->x_max = (int16_t) be16_to_cpu (head.x_max); @@ -2288,56 +2553,123 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, font->units_per_em = 1000; font->font_name = NULL; - status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font, + status = _cairo_truetype_read_font_name (font->scaled_font_subset->scaled_font, &font->ps_name, &font->font_name); if (_cairo_status_is_error (status)) - goto fail3; + return status; - /* If the PS name is not found, create a CairoFont-x-y name. */ - if (font->ps_name == NULL) { - font->ps_name = malloc (30); - if (unlikely (font->ps_name == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail3; - } + font->is_opentype = TRUE; + font->data_length = data_length; + font->data = malloc (data_length); + if (unlikely (font->data == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - snprintf(font->ps_name, 30, "CairoFont-%u-%u", - scaled_font_subset->font_id, - scaled_font_subset->subset_id); + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_CFF, 0, font->data, + &font->data_length); + if (unlikely (status)) + return status; + + if (!check_fontdata_is_cff (font->data, data_length)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_cff_font_load_cff (cairo_cff_font_t *font) +{ + const cairo_scaled_font_backend_t *backend = font->backend; + cairo_status_t status; + unsigned long data_length; + + if (!backend->load_type1_data) + return CAIRO_INT_STATUS_UNSUPPORTED; + + data_length = 0; + status = backend->load_type1_data (font->scaled_font_subset->scaled_font, + 0, NULL, &data_length); + if (unlikely (status)) + return status; + + font->font_name = NULL; + font->is_opentype = FALSE; + font->data_length = data_length; + font->data = malloc (data_length); + if (unlikely (font->data == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = font->backend->load_type1_data (font->scaled_font_subset->scaled_font, + 0, font->data, &font->data_length); + if (unlikely (status)) + return status; + + if (!check_fontdata_is_cff (font->data, data_length)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, + cairo_cff_font_t **font_return, + const char *subset_name) +{ + const cairo_scaled_font_backend_t *backend; + cairo_int_status_t status; + cairo_cff_font_t *font; + + backend = scaled_font_subset->scaled_font->backend; + + /* We need to use a fallback font generated from the synthesized outlines. */ + if (backend->is_synthetic && backend->is_synthetic (scaled_font_subset->scaled_font)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + font = malloc (sizeof (cairo_cff_font_t)); + if (unlikely (font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->backend = backend; + font->scaled_font_subset = scaled_font_subset; + + status = _cairo_cff_font_load_opentype_cff (font); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + status = _cairo_cff_font_load_cff (font); + if (status) + goto fail1; + + font->data_end = font->data + font->data_length; + _cairo_array_init (&font->output, sizeof (char)); + status = _cairo_array_grow_by (&font->output, 4096); + if (unlikely (status)) + goto fail2; + + font->subset_font_name = strdup (subset_name); + if (unlikely (font->subset_font_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; } font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); if (unlikely (font->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail4; + goto fail3; } - status = cairo_cff_font_create_set_widths (font); - if (unlikely (status)) - goto fail5; - - font->data_length = data_length; - font->data = malloc (data_length); - if (unlikely (font->data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto fail5; + if (font->is_opentype) { + status = cairo_cff_font_create_set_widths (font); + if (unlikely (status)) + goto fail4; } - status = font->backend->load_truetype_table ( font->scaled_font_subset->scaled_font, - TT_TAG_CFF, 0, font->data, - &font->data_length); - if (unlikely (status)) - goto fail6; - - font->data_end = font->data + font->data_length; status = cff_dict_init (&font->top_dict); if (unlikely (status)) - goto fail6; + goto fail4; status = cff_dict_init (&font->private_dict); if (unlikely (status)) - goto fail7; + goto fail5; cff_index_init (&font->strings_index); cff_index_init (&font->charstrings_index); @@ -2362,19 +2694,20 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset, return CAIRO_STATUS_SUCCESS; -fail7: - _cairo_hash_table_destroy (font->top_dict); -fail6: - free (font->data); fail5: - free (font->widths); + _cairo_hash_table_destroy (font->top_dict); fail4: - if (font->font_name) - free (font->font_name); + free (font->widths); fail3: free (font->subset_font_name); fail2: + free (font->data); + if (font->font_name) + free (font->font_name); + if (font->ps_name) + free (font->ps_name); _cairo_array_fini (&font->output); +fail1: free (font); return status; @@ -2444,7 +2777,10 @@ cairo_cff_font_destroy (cairo_cff_font_t *font) } free (font->fd_local_subs_used); } - + if (font->fd_default_width) + free (font->fd_default_width); + if (font->fd_nominal_width) + free (font->fd_nominal_width); } if (font->data) @@ -2556,27 +2892,47 @@ _cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font) cairo_bool_t is_cid = FALSE; backend = scaled_font->backend; - if (!backend->load_truetype_table) - return FALSE; - - /* check for CFF font */ + data = NULL; data_length = 0; - status = backend->load_truetype_table(scaled_font, - TT_TAG_CFF, 0, NULL, &data_length); - if (status) - return FALSE; + status = CAIRO_INT_STATUS_UNSUPPORTED; + /* Try to load an OpenType/CFF font */ + if (backend->load_truetype_table && + (status = backend->load_truetype_table (scaled_font, TT_TAG_CFF, + 0, NULL, &data_length)) == CAIRO_STATUS_SUCCESS) + { + data = malloc (data_length); + if (unlikely (data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } - /* load CFF data */ - data = malloc (data_length); - if (unlikely (data == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - return FALSE; - } - - status = backend->load_truetype_table (scaled_font, TT_TAG_CFF, + status = backend->load_truetype_table (scaled_font, TT_TAG_CFF, 0, data, &data_length); - if (unlikely (status)) - goto fail1; + if (unlikely (status)) + goto fail1; + } + /* Try to load a CFF font */ + if (status == CAIRO_INT_STATUS_UNSUPPORTED && + backend->load_type1_data && + (status = backend->load_type1_data (scaled_font, + 0, NULL, &data_length)) == CAIRO_STATUS_SUCCESS) + { + data = malloc (data_length); + if (unlikely (data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + status = backend->load_type1_data (scaled_font, 0, data, &data_length); + if (unlikely (status)) + goto fail1; + } + if (status) + goto fail1; + + /* Check if it looks like a CFF font */ + if (!check_fontdata_is_cff (data, data_length)) + goto fail1; data_end = data + data_length; @@ -2621,7 +2977,8 @@ fail2: cff_index_fini (&index); fail1: - free (data); + if (data) + free (data); return is_cid; } diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index ff737a305..822833a83 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -2521,8 +2521,18 @@ _cairo_ft_load_type1_data (void *abstract_font, if (!face) return _cairo_error (CAIRO_STATUS_NO_MEMORY); +#if HAVE_FT_LOAD_SFNT_TABLE + if (FT_IS_SFNT (face)) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto unlock; + } +#endif + font_format = FT_Get_X11_Font_Format (face); - if (!(font_format && strcmp (font_format, "Type 1") == 0)) { + if (!font_format || + !(strcmp (font_format, "Type 1") == 0 || + strcmp (font_format, "CFF") == 0)) + { status = CAIRO_INT_STATUS_UNSUPPORTED; goto unlock; }