/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* cairo - a vector graphics library with display and print output * * Copyright © 2006 Adrian Johnson * * 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 Adrian Johnson. * * Contributor(s): * Adrian Johnson * Eugeniy Meshcheryakov */ /* * Useful links: * http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf * http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf */ #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-scaled-font-subsets-private.h" #include "cairo-truetype-subset-private.h" #include #include #include /* CFF Dict Operators. If the high byte is 0 the command is encoded * with a single byte. */ #define BASEFONTNAME_OP 0x0c16 #define CIDCOUNT_OP 0x0c22 #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 #define ROS_OP 0x0c1e #define UNIQUEID_OP 0x000d #define VERSION_OP 0x0000 #define WEIGHT_OP 0x0004 #define XUID_OP 0x000e #define NUM_STD_STRINGS 391 /* Type 2 Charstring operators */ #define TYPE2_hstem 0x0001 #define TYPE2_vstem 0x0003 #define TYPE2_callsubr 0x000a #define TYPE2_return 0x000b #define TYPE2_endchar 0x000e #define TYPE2_hstemhm 0x0012 #define TYPE2_hintmask 0x0013 #define TYPE2_cntrmask 0x0014 #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 */ typedef struct _cff_header { uint8_t major; uint8_t minor; uint8_t header_size; uint8_t offset_size; } cff_header_t; typedef struct _cff_index_element { cairo_bool_t is_copy; unsigned char *data; int length; } cff_index_element_t; typedef struct _cff_dict_operator { cairo_hash_entry_t base; unsigned short operator; unsigned char *operand; int operand_length; int operand_offset; } cff_dict_operator_t; typedef struct _cairo_cff_font { cairo_scaled_font_subset_t *scaled_font_subset; const cairo_scaled_font_backend_t *backend; /* Font Data */ unsigned char *data; unsigned long data_length; unsigned char *current_ptr; unsigned char *data_end; cff_header_t *header; char *font_name; char *ps_name; cairo_hash_table_t *top_dict; cairo_hash_table_t *private_dict; cairo_array_t strings_index; cairo_array_t charstrings_index; cairo_array_t global_sub_index; cairo_array_t local_sub_index; unsigned char *charset; 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; double default_width; double nominal_width; /* CID Font Data */ int *fdselect; unsigned int num_fontdicts; cairo_hash_table_t **fd_dict; cairo_hash_table_t **fd_private_dict; cairo_array_t *fd_local_sub_index; int *fd_local_sub_bias; double *fd_default_width; double *fd_nominal_width; /* Subsetted Font Data */ char *subset_font_name; cairo_array_t charstrings_subset_index; cairo_array_t strings_subset_index; int euro_sid; int *fdselect_subset; unsigned int num_subset_fontdicts; int *fd_subset_map; int *private_dict_offset; cairo_bool_t subset_subroutines; cairo_bool_t *global_subs_used; cairo_bool_t *local_subs_used; cairo_bool_t **fd_local_subs_used; cairo_array_t output; /* Subset Metrics */ int *widths; int x_min, y_min, x_max, y_max; int ascent, descent; /* Type 2 charstring data */ int type2_stack_size; int type2_stack_top_value; cairo_bool_t type2_stack_top_is_int; 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_bool_t type2_has_path; } cairo_cff_font_t; /* Encoded integer using maximum sized encoding. This is required for * operands that are later modified after encoding. */ static unsigned char * encode_integer_max (unsigned char *p, int i) { *p++ = 29; *p++ = i >> 24; *p++ = (i >> 16) & 0xff; *p++ = (i >> 8) & 0xff; *p++ = i & 0xff; return p; } static unsigned char * encode_integer (unsigned char *p, int i) { 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 (i >= -32768 && i <= 32767) { *p++ = 28; *p++ = (i >> 8) & 0xff; *p++ = i & 0xff; } else { p = encode_integer_max (p, i); } return p; } static unsigned char * decode_integer (unsigned char *p, int *integer) { if (*p == 28) { *integer = (int)(p[1]<<8 | p[2]); p += 3; } else if (*p == 29) { *integer = (int)((p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]); p += 5; } else if (*p >= 32 && *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 = 0; p += 1; } 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) { const char *decimal_point; int decimal_point_len; int n; char buffer[100]; char buffer2[200]; char *q; char *buf = buffer; char *buf_end = buffer + sizeof (buffer); decimal_point = cairo_get_locale_decimal_point (); decimal_point_len = strlen (decimal_point); assert (decimal_point_len != 0); assert (sizeof(buffer) + decimal_point_len < sizeof(buffer2)); 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) { p++; break; } p++; }; *buf = 0; buf = buffer; if (strchr (buffer, '.')) { q = strchr (buffer, '.'); strncpy (buffer2, buffer, q - buffer); buf = buffer2 + (q - buffer); strncpy (buf, decimal_point, decimal_point_len); buf += decimal_point_len; //(2017.04.17) Since securitY issue, replace strcpy to strncpy. //strcpy (buf, q + 1); strncpy (buf, q + 1, (buffer + sizeof(buffer)/sizeof(buffer[0])) - (q + 1)); //same as strncpy (buf, q + 1, 100 - (q + 1)); buf = buffer2; } if (sscanf(buf, "%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) { unsigned short op = 0; op = *p++; if (op == 12) { op <<= 8; op |= *p++; } *operator = op; return p; } /* return 0 if not an operand */ static int operand_length (unsigned char *p) { unsigned char *begin = p; if (*p == 28) return 3; if (*p == 29) return 5; if (*p >= 32 && *p <= 246) return 1; if (*p >= 247 && *p <= 254) return 2; if (*p == 30) { while ((*p & 0x0f) != 0x0f) p++; return p - begin + 1; } return 0; } static unsigned char * encode_index_offset (unsigned char *p, int offset_size, unsigned long offset) { while (--offset_size >= 0) { p[offset_size] = (unsigned char) (offset & 0xff); offset >>= 8; } return p + offset_size; } static unsigned long decode_index_offset(unsigned char *p, int off_size) { unsigned long offset = 0; while (off_size-- > 0) offset = offset*256 + *p++; return offset; } static void cff_index_init (cairo_array_t *index) { _cairo_array_init (index, sizeof (cff_index_element_t)); } static cairo_int_status_t cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_ptr) { cff_index_element_t element; unsigned char *data, *p; cairo_status_t status; int offset_size, count, start, i; int end = 0; p = *ptr; if (p + 2 > end_ptr) return CAIRO_INT_STATUS_UNSUPPORTED; count = get_unaligned_be16 (p); p += 2; if (count > 0) { offset_size = *p++; if (p + (count + 1)*offset_size > end_ptr) return CAIRO_INT_STATUS_UNSUPPORTED; data = p + offset_size*(count + 1) - 1; start = decode_index_offset (p, offset_size); p += offset_size; for (i = 0; i < count; i++) { end = decode_index_offset (p, offset_size); p += offset_size; if (p > end_ptr) return CAIRO_INT_STATUS_UNSUPPORTED; element.length = end - start; element.is_copy = FALSE; element.data = data + start; status = _cairo_array_append (index, &element); if (unlikely (status)) return status; start = end; } p = data + end; } *ptr = p; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cff_index_write (cairo_array_t *index, cairo_array_t *output) { int offset_size; int offset; int num_elem; int i; cff_index_element_t *element; uint16_t count; unsigned char buf[5]; cairo_status_t status; num_elem = _cairo_array_num_elements (index); count = cpu_to_be16 ((uint16_t) num_elem); status = _cairo_array_append_multiple (output, &count, 2); if (unlikely (status)) return status; if (num_elem == 0) return CAIRO_STATUS_SUCCESS; /* Find maximum offset to determine offset size */ offset = 1; for (i = 0; i < num_elem; i++) { element = _cairo_array_index (index, i); offset += element->length; } if (offset < 0x100) offset_size = 1; else if (offset < 0x10000) offset_size = 2; else if (offset < 0x1000000) offset_size = 3; else offset_size = 4; buf[0] = (unsigned char) offset_size; status = _cairo_array_append (output, buf); if (unlikely (status)) return status; offset = 1; encode_index_offset (buf, offset_size, offset); status = _cairo_array_append_multiple (output, buf, offset_size); if (unlikely (status)) return status; for (i = 0; i < num_elem; i++) { element = _cairo_array_index (index, i); offset += element->length; encode_index_offset (buf, offset_size, offset); status = _cairo_array_append_multiple (output, buf, offset_size); if (unlikely (status)) return status; } for (i = 0; i < num_elem; i++) { element = _cairo_array_index (index, i); if (element->length > 0) { status = _cairo_array_append_multiple (output, element->data, element->length); } if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static void cff_index_set_object (cairo_array_t *index, int obj_index, unsigned char *object , int length) { cff_index_element_t *element; element = _cairo_array_index (index, obj_index); if (element == NULL) return; if (element->is_copy) free (element->data); element->data = object; element->length = length; element->is_copy = FALSE; } static cairo_status_t cff_index_append (cairo_array_t *index, unsigned char *object , int length) { cff_index_element_t element; element.length = length; element.is_copy = FALSE; element.data = object; return _cairo_array_append (index, &element); } static cairo_status_t cff_index_append_copy (cairo_array_t *index, const unsigned char *object, unsigned int length) { cff_index_element_t element; cairo_status_t status; element.length = length; element.is_copy = TRUE; element.data = malloc (element.length); if (unlikely (element.data == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (element.data, object, element.length); status = _cairo_array_append (index, &element); if (unlikely (status)) { free (element.data); return status; } return CAIRO_STATUS_SUCCESS; } static void cff_index_fini (cairo_array_t *index) { cff_index_element_t *element; unsigned int i; for (i = 0; i < _cairo_array_num_elements (index); i++) { element = _cairo_array_index (index, i); if (element->is_copy && element->data) free (element->data); } _cairo_array_fini (index); } static cairo_bool_t _cairo_cff_dict_equal (const void *key_a, const void *key_b) { const cff_dict_operator_t *op_a = key_a; const cff_dict_operator_t *op_b = key_b; return op_a->operator == op_b->operator; } static cairo_status_t cff_dict_init (cairo_hash_table_t **dict) { *dict = _cairo_hash_table_create (_cairo_cff_dict_equal); if (unlikely (*dict == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } static void _cairo_dict_init_key (cff_dict_operator_t *key, int operator) { key->base.hash = (unsigned long) operator; key->operator = operator; } static cairo_status_t cff_dict_create_operator (int operator, unsigned char *operand, int size, cff_dict_operator_t **out) { cff_dict_operator_t *op; op = malloc (sizeof (cff_dict_operator_t)); if (unlikely (op == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_dict_init_key (op, operator); op->operand = malloc (size); if (unlikely (op->operand == NULL)) { free (op); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memcpy (op->operand, operand, size); op->operand_length = size; op->operand_offset = -1; *out = op; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cff_dict_read (cairo_hash_table_t *dict, unsigned char *p, int dict_size) { unsigned char *end; cairo_array_t operands; cff_dict_operator_t *op; unsigned short operator; cairo_status_t status = CAIRO_STATUS_SUCCESS; int size; end = p + dict_size; _cairo_array_init (&operands, 1); while (p < end) { size = operand_length (p); if (size != 0) { status = _cairo_array_append_multiple (&operands, p, size); if (unlikely (status)) goto fail; p += size; } else { p = decode_operator (p, &operator); status = cff_dict_create_operator (operator, _cairo_array_index (&operands, 0), _cairo_array_num_elements (&operands), &op); if (unlikely (status)) goto fail; status = _cairo_hash_table_insert (dict, &op->base); if (unlikely (status)) goto fail; _cairo_array_truncate (&operands, 0); } } fail: _cairo_array_fini (&operands); return status; } static void cff_dict_remove (cairo_hash_table_t *dict, unsigned short operator) { cff_dict_operator_t key, *op; _cairo_dict_init_key (&key, operator); op = _cairo_hash_table_lookup (dict, &key.base); if (op != NULL) { free (op->operand); _cairo_hash_table_remove (dict, (cairo_hash_entry_t *) op); free (op); } } static unsigned char * cff_dict_get_operands (cairo_hash_table_t *dict, unsigned short operator, int *size) { cff_dict_operator_t key, *op; _cairo_dict_init_key (&key, operator); op = _cairo_hash_table_lookup (dict, &key.base); if (op != NULL) { *size = op->operand_length; return op->operand; } return NULL; } static cairo_status_t cff_dict_set_operands (cairo_hash_table_t *dict, unsigned short operator, unsigned char *operand, int size) { cff_dict_operator_t key, *op; cairo_status_t status; _cairo_dict_init_key (&key, operator); op = _cairo_hash_table_lookup (dict, &key.base); if (op != NULL) { free (op->operand); op->operand = malloc (size); if (unlikely (op->operand == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (op->operand, operand, size); op->operand_length = size; } else { status = cff_dict_create_operator (operator, operand, size, &op); if (unlikely (status)) return status; status = _cairo_hash_table_insert (dict, &op->base); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static int cff_dict_get_location (cairo_hash_table_t *dict, unsigned short operator, int *size) { cff_dict_operator_t key, *op; _cairo_dict_init_key (&key, operator); op = _cairo_hash_table_lookup (dict, &key.base); if (op != NULL) { *size = op->operand_length; return op->operand_offset; } return -1; } typedef struct _dict_write_info { cairo_array_t *output; cairo_status_t status; } dict_write_info_t; static void cairo_dict_write_operator (cff_dict_operator_t *op, dict_write_info_t *write_info) { unsigned char data; op->operand_offset = _cairo_array_num_elements (write_info->output); write_info->status = _cairo_array_append_multiple (write_info->output, op->operand, op->operand_length); if (write_info->status) return; if (op->operator & 0xff00) { data = op->operator >> 8; write_info->status = _cairo_array_append (write_info->output, &data); if (write_info->status) return; } data = op->operator & 0xff; write_info->status = _cairo_array_append (write_info->output, &data); } static void _cairo_dict_collect (void *entry, void *closure) { dict_write_info_t *write_info = closure; cff_dict_operator_t *op = entry; if (write_info->status) return; /* The ROS operator is handled separately in cff_dict_write() */ if (op->operator != ROS_OP) cairo_dict_write_operator (op, write_info); } static cairo_status_t cff_dict_write (cairo_hash_table_t *dict, cairo_array_t *output) { dict_write_info_t write_info; cff_dict_operator_t key, *op; write_info.output = output; write_info.status = CAIRO_STATUS_SUCCESS; /* The CFF specification requires that the Top Dict of CID fonts * begin with the ROS operator. */ _cairo_dict_init_key (&key, ROS_OP); op = _cairo_hash_table_lookup (dict, &key.base); if (op != NULL) cairo_dict_write_operator (op, &write_info); _cairo_hash_table_foreach (dict, _cairo_dict_collect, &write_info); return write_info.status; } static void _cff_dict_entry_pluck (void *_entry, void *dict) { cff_dict_operator_t *entry = _entry; _cairo_hash_table_remove (dict, &entry->base); free (entry->operand); free (entry); } static void cff_dict_fini (cairo_hash_table_t *dict) { _cairo_hash_table_foreach (dict, _cff_dict_entry_pluck, dict); _cairo_hash_table_destroy (dict); } static cairo_int_status_t 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; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_cff_font_read_name (cairo_cff_font_t *font) { cairo_array_t index; cairo_int_status_t status; cff_index_element_t *element; unsigned char *p; int i, len; 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); if (element == NULL) return _cairo_error (CAIRO_STATUS_NULL_POINTER); p = element->data; len = element->length; /* If font name is prefixed with a subset tag, strip it off. */ if (len > 7 && p[6] == '+') { for (i = 0; i < 6; i++) if (p[i] < 'A' || p[i] > 'Z') break; if (i == 6) { p += 7; len -= 7; } } font->ps_name = malloc (len + 1); if (unlikely (font->ps_name == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (font->ps_name, p, len); font->ps_name[len] = 0; status = _cairo_escape_ps_name (&font->ps_name); } cff_index_fini (&index); return status; } static cairo_int_status_t cairo_cff_font_read_private_dict (cairo_cff_font_t *font, cairo_hash_table_t *private_dict, cairo_array_t *local_sub_index, int *local_sub_bias, cairo_bool_t **local_subs_used, double *default_width, double *nominal_width, unsigned char *ptr, int size) { cairo_int_status_t status; unsigned char buf[10]; unsigned char *end_buf; int offset; int i; unsigned char *operand; unsigned char *p; int num_subs; status = cff_dict_read (private_dict, ptr, size); if (unlikely (status)) return status; operand = cff_dict_get_operands (private_dict, LOCAL_SUB_OP, &i); if (operand) { decode_integer (operand, &offset); p = ptr + offset; status = cff_index_read (local_sub_index, &p, font->data_end); if (unlikely (status)) return status; /* Use maximum sized encoding to reserve space for later modification. */ end_buf = encode_integer_max (buf, 0); 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_number (operand, default_width); *nominal_width = 0; operand = cff_dict_get_operands (private_dict, NOMINALWIDTH_OP, &i); if (operand) decode_number (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)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (num_subs < 1240) *local_sub_bias = 107; else if (num_subs < 33900) *local_sub_bias = 1131; else *local_sub_bias = 32768; return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_cff_font_read_fdselect (cairo_cff_font_t *font, unsigned char *p) { int type, num_ranges, first, last, fd, i, j; font->fdselect = calloc (font->num_glyphs, sizeof (int)); if (unlikely (font->fdselect == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); type = *p++; if (type == 0) { for (i = 0; i < font->num_glyphs; i++) font->fdselect[i] = *p++; } else if (type == 3) { num_ranges = get_unaligned_be16 (p); p += 2; for (i = 0; i < num_ranges; i++) { first = get_unaligned_be16 (p); p += 2; fd = *p++; last = get_unaligned_be16 (p); for (j = first; j < last; j++) font->fdselect[j] = fd; } } else { return CAIRO_INT_STATUS_UNSUPPORTED; } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) { cairo_array_t index; cff_index_element_t *element; unsigned int i; int size; unsigned char *operand; int offset; cairo_int_status_t status; unsigned char buf[100]; unsigned char *end_buf; cff_index_init (&index); status = cff_index_read (&index, &ptr, font->data_end); if (unlikely (status)) goto fail; font->num_fontdicts = _cairo_array_num_elements (&index); font->fd_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts); if (unlikely (font->fd_dict == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_private_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts); if (unlikely (font->fd_private_dict == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_local_sub_index = calloc (sizeof (cairo_array_t), font->num_fontdicts); if (unlikely (font->fd_local_sub_index == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_local_sub_bias = calloc (sizeof (int), font->num_fontdicts); if (unlikely (font->fd_local_sub_bias == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_local_subs_used = calloc (sizeof (cairo_bool_t *), font->num_fontdicts); if (unlikely (font->fd_local_subs_used == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_default_width = calloc (font->num_fontdicts, sizeof (double)); if (unlikely (font->fd_default_width == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } font->fd_nominal_width = calloc (font->num_fontdicts, sizeof (double)); 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)) goto fail; element = _cairo_array_index (&index, i); status = cff_dict_read (font->fd_dict[i], element->data, element->length); if (unlikely (status)) goto fail; operand = cff_dict_get_operands (font->fd_dict[i], PRIVATE_OP, &size); if (operand == NULL) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto fail; } operand = decode_integer (operand, &size); decode_integer (operand, &offset); status = cff_dict_init (&font->fd_private_dict[i]); if (unlikely (status)) goto fail; cff_index_init (&font->fd_local_sub_index[i]); status = cairo_cff_font_read_private_dict (font, font->fd_private_dict[i], &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)) goto fail; /* Set integer operand to max value to use max size encoding to reserve * space for any value later */ end_buf = encode_integer_max (buf, 0); end_buf = encode_integer_max (end_buf, 0); status = cff_dict_set_operands (font->fd_dict[i], PRIVATE_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; } return CAIRO_STATUS_SUCCESS; fail: cff_index_fini (&index); 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; x_min = 0.0; y_min = 0.0; x_max = 0.0; 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) { cairo_array_t index; cff_index_element_t *element; unsigned char buf[20]; unsigned char *end_buf; unsigned char *operand; cairo_int_status_t status; unsigned char *p; int size; int offset; cff_index_init (&index); status = cff_index_read (&index, &font->current_ptr, font->data_end); if (unlikely (status)) goto fail; element = _cairo_array_index (&index, 0); if (element == NULL) { status = CAIRO_STATUS_NULL_POINTER; goto fail; } status = cff_dict_read (font->top_dict, element->data, element->length); if (unlikely (status)) goto fail; if (cff_dict_get_operands (font->top_dict, ROS_OP, &size) != NULL) font->is_cid = TRUE; else font->is_cid = FALSE; operand = cff_dict_get_operands (font->top_dict, CHARSTRINGS_OP, &size); if (operand == NULL) { status = CAIRO_STATUS_NULL_POINTER; goto fail; } decode_integer (operand, &offset); p = font->data + offset; status = cff_index_read (&font->charstrings_index, &p, font->data_end); if (unlikely (status)) goto fail; font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index); if (font->is_cid) { operand = cff_dict_get_operands (font->top_dict, CHARSET_OP, &size); if (!operand) return CAIRO_INT_STATUS_UNSUPPORTED; decode_integer (operand, &offset); font->charset = font->data + offset; if (font->charset >= font->data_end) return CAIRO_INT_STATUS_UNSUPPORTED; } 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); if (operand == NULL) { status = CAIRO_STATUS_NULL_POINTER; goto fail; } decode_integer (operand, &offset); status = cairo_cff_font_read_fdselect (font, font->data + offset); if (unlikely (status)) goto fail; operand = cff_dict_get_operands (font->top_dict, FDARRAY_OP, &size); if (operand == NULL) { status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto fail; } decode_integer (operand, &offset); status = cairo_cff_font_read_cid_fontdict (font, font->data + offset); if (unlikely (status)) goto fail; } else { operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size); operand = decode_integer (operand, &size); decode_integer (operand, &offset); status = cairo_cff_font_read_private_dict (font, font->private_dict, &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)) goto fail; } /* Use maximum sized encoding to reserve space for later modification. */ end_buf = encode_integer_max (buf, 0); status = cff_dict_set_operands (font->top_dict, CHARSTRINGS_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; status = cff_dict_set_operands (font->top_dict, CHARSET_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; if (font->scaled_font_subset->is_latin) { status = cff_dict_set_operands (font->top_dict, ENCODING_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; /* Private has two operands - size and offset */ end_buf = encode_integer_max (end_buf, 0); cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf - buf); } else { status = cff_dict_set_operands (font->top_dict, FDSELECT_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; status = cff_dict_set_operands (font->top_dict, FDARRAY_OP, buf, end_buf - buf); if (unlikely (status)) goto fail; cff_dict_remove (font->top_dict, ENCODING_OP); cff_dict_remove (font->top_dict, PRIVATE_OP); } /* Remove the unique identifier operators as the subsetted font is * not the same is the original font. */ cff_dict_remove (font->top_dict, UNIQUEID_OP); cff_dict_remove (font->top_dict, XUID_OP); fail: cff_index_fini (&index); return status; } static cairo_int_status_t cairo_cff_font_read_strings (cairo_cff_font_t *font) { return cff_index_read (&font->strings_index, &font->current_ptr, font->data_end); } static cairo_int_status_t cairo_cff_font_read_global_subroutines (cairo_cff_font_t *font) { cairo_int_status_t status; int num_subs; status = cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end); if (unlikely (status)) return status; num_subs = _cairo_array_num_elements (&font->global_sub_index); font->global_subs_used = calloc (num_subs, sizeof(cairo_bool_t)); if (unlikely (font->global_subs_used == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (num_subs < 1240) font->global_sub_bias = 107; else if (num_subs < 33900) font->global_sub_bias = 1131; else font->global_sub_bias = 32768; return CAIRO_STATUS_SUCCESS; } typedef cairo_int_status_t (*font_read_t) (cairo_cff_font_t *font); static const font_read_t font_read_funcs[] = { cairo_cff_font_read_header, cairo_cff_font_read_name, cairo_cff_font_read_top_dict, cairo_cff_font_read_strings, cairo_cff_font_read_global_subroutines, }; static cairo_int_status_t cairo_cff_font_read_font (cairo_cff_font_t *font) { cairo_int_status_t status; unsigned int i; for (i = 0; i < ARRAY_LENGTH (font_read_funcs); i++) { status = font_read_funcs[i] (font); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_set_ros_strings (cairo_cff_font_t *font) { cairo_status_t status; unsigned char buf[30]; unsigned char *p; int sid1, sid2; const char *registry = "Adobe"; const char *ordering = "Identity"; sid1 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); status = cff_index_append_copy (&font->strings_subset_index, (unsigned char *)registry, strlen(registry)); if (unlikely (status)) return status; sid2 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); status = cff_index_append_copy (&font->strings_subset_index, (unsigned char *)ordering, strlen(ordering)); if (unlikely (status)) return status; p = encode_integer (buf, sid1); p = encode_integer (p, sid2); p = encode_integer (p, 0); status = cff_dict_set_operands (font->top_dict, ROS_OP, buf, p - buf); if (unlikely (status)) return status; p = encode_integer (buf, font->scaled_font_subset->num_glyphs); status = cff_dict_set_operands (font->top_dict, CIDCOUNT_OP, buf, p - buf); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_subset_dict_string(cairo_cff_font_t *font, cairo_hash_table_t *dict, int operator) { int size; unsigned char *p; int sid; unsigned char buf[100]; cff_index_element_t *element; cairo_status_t status; p = cff_dict_get_operands (dict, operator, &size); if (!p) return CAIRO_STATUS_SUCCESS; decode_integer (p, &sid); if (sid < NUM_STD_STRINGS) return CAIRO_STATUS_SUCCESS; element = _cairo_array_index (&font->strings_index, sid - NUM_STD_STRINGS); if(element == NULL) return _cairo_error (CAIRO_STATUS_INVALID_INDEX); sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); status = cff_index_append (&font->strings_subset_index, element->data, element->length); if (unlikely (status)) return status; p = encode_integer (buf, sid); status = cff_dict_set_operands (dict, operator, buf, p - buf); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static const int dict_strings[] = { VERSION_OP, NOTICE_OP, COPYRIGHT_OP, FULLNAME_OP, FAMILYNAME_OP, WEIGHT_OP, POSTSCRIPT_OP, BASEFONTNAME_OP, FONTNAME_OP, }; static cairo_status_t cairo_cff_font_subset_dict_strings (cairo_cff_font_t *font, cairo_hash_table_t *dict) { cairo_status_t status; unsigned int i; for (i = 0; i < ARRAY_LENGTH (dict_strings); i++) { status = cairo_cff_font_subset_dict_string (font, dict, dict_strings[i]); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static unsigned char * type2_decode_integer (unsigned char *p, int *integer) { if (*p == 28) { *integer = p[1] << 8 | p[2]; p += 3; } else 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 { /* *p == 255 */ /* 16.16 fixed-point number. The fraction is ignored. */ *integer = (int16_t)((p[1] << 8) | p[2]); p += 5; } return p; } /* Type 2 charstring parser for finding calls to local or global * 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 the 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. * * When the first stack clearing operator is encountered * type2_find_width is set to FALSE and type2_found_width is set to * TRUE if an extra argument is found, otherwise FALSE. */ static cairo_status_t cairo_cff_parse_charstring (cairo_cff_font_t *font, unsigned char *charstring, int length, int glyph_id, cairo_bool_t need_width) { unsigned char *p = charstring; unsigned char *end = charstring + length; int integer; int hint_bytes; int sub_num; cff_index_element_t *element; int fd; while (p < end) { if (*p == 28 || *p >= 32) { /* Integer value */ p = type2_decode_integer (p, &integer); 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 * variable length mask where the length depends on the * number of hints declared. The first time this is called * it is also an implicit vstem if there are arguments on * the stack. */ 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; font->type2_has_path = TRUE; 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; font->type2_has_path = TRUE; p++; } else if (*p == TYPE2_endchar) { if (!font->type2_has_path && font->type2_stack_size > 3) return CAIRO_INT_STATUS_UNSUPPORTED; /* seac (Ref Appendix C of Type 2 Charstring Format */ 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) return CAIRO_INT_STATUS_UNSUPPORTED; if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING) return CAIRO_INT_STATUS_UNSUPPORTED; 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 (element == NULL) return _cairo_error (CAIRO_STATUS_NULL_POINTER); 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, 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 (element == NULL) return _cairo_error (CAIRO_STATUS_NULL_POINTER); 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, need_width); } } font->type2_nesting_level--; } else if (*p == TYPE2_callgsubr) { /* call to global subroutine */ if (! font->type2_stack_top_is_int) return CAIRO_INT_STATUS_UNSUPPORTED; if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING) return CAIRO_INT_STATUS_UNSUPPORTED; 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 (element == NULL) return _cairo_error (CAIRO_STATUS_NULL_POINTER); 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, need_width); } font->type2_nesting_level--; } else if (*p == 12) { /* 2 byte instruction */ /* All the 2 byte operators are either not valid before a * stack clearing operator or they are one of the * arithmetic, storage, or conditional operators. */ if (need_width && font->type2_find_width) return CAIRO_INT_STATUS_UNSUPPORTED; p += 2; font->type2_stack_top_is_int = FALSE; } else { /* 1 byte instruction */ p++; font->type2_stack_top_is_int = FALSE; } } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_find_width_and_subroutines_used (cairo_cff_font_t *font, unsigned char *charstring, int length, 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; font->type2_has_path = FALSE; status = cairo_cff_parse_charstring (font, charstring, length, glyph_id, TRUE); if (status) return status; 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 CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_cff_font_get_gid_for_cid (cairo_cff_font_t *font, unsigned long cid, unsigned long *gid) { unsigned char *p; unsigned long first_gid; unsigned long first_cid; int num_left; unsigned long c, g; if (cid == 0) { *gid = 0; return CAIRO_STATUS_SUCCESS; } switch (font->charset[0]) { /* Format 0 */ case 0: p = font->charset + 1; g = 1; while (g <= (unsigned)font->num_glyphs && p < font->data_end) { c = get_unaligned_be16 (p); if (c == cid) { *gid = g; return CAIRO_STATUS_SUCCESS; } g++; p += 2; } break; /* Format 1 */ case 1: first_gid = 1; p = font->charset + 1; while (first_gid <= (unsigned)font->num_glyphs && p + 2 < font->data_end) { first_cid = get_unaligned_be16 (p); num_left = p[2]; if (cid >= first_cid && cid <= first_cid + num_left) { *gid = first_gid + cid - first_cid; return CAIRO_STATUS_SUCCESS; } first_gid += num_left + 1; p += 3; } break; /* Format 2 */ case 2: first_gid = 1; p = font->charset + 1; while (first_gid <= (unsigned)font->num_glyphs && p + 3 < font->data_end) { first_cid = get_unaligned_be16 (p); num_left = get_unaligned_be16 (p+2); if (cid >= first_cid && cid <= first_cid + num_left) { *gid = first_gid + cid - first_cid; return CAIRO_STATUS_SUCCESS; } first_gid += num_left + 1; p += 4; } break; default: break; } return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_int_status_t cairo_cff_font_subset_charstrings_and_subroutines (cairo_cff_font_t *font) { cff_index_element_t *element; unsigned int i; cairo_int_status_t status; unsigned long glyph, cid; font->subset_subroutines = TRUE; for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { if (font->is_cid) { cid = font->scaled_font_subset->glyphs[i]; status = cairo_cff_font_get_gid_for_cid (font, cid, &glyph); if (unlikely (status)) return status; } else { glyph = font->scaled_font_subset->glyphs[i]; } element = _cairo_array_index (&font->charstrings_index, glyph); if (element == NULL) return CAIRO_STATUS_NULL_POINTER; status = cff_index_append (&font->charstrings_subset_index, element->data, element->length); if (unlikely (status)) return status; if (font->subset_subroutines) { status = cairo_cff_find_width_and_subroutines_used (font, element->data, element->length, glyph, i); if (status == CAIRO_INT_STATUS_UNSUPPORTED) { /* If parsing the charstrings fails we embed all the * subroutines. But if the font is not opentype we * need to successfully parse all charstrings to get * the widths. */ font->subset_subroutines = FALSE; if (!font->is_opentype) return status; } else if (unlikely (status)) { return status; } } } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_subset_fontdict (cairo_cff_font_t *font) { unsigned int i; int fd; int *reverse_map; unsigned long cid, gid; cairo_int_status_t status; font->fdselect_subset = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); if (unlikely (font->fdselect_subset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->fd_subset_map = calloc (font->num_fontdicts, sizeof (int)); if (unlikely (font->fd_subset_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->private_dict_offset = calloc (font->num_fontdicts, sizeof (int)); if (unlikely (font->private_dict_offset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); reverse_map = calloc (font->num_fontdicts, sizeof (int)); if (unlikely (reverse_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); for (i = 0; i < font->num_fontdicts; i++) reverse_map[i] = -1; font->num_subset_fontdicts = 0; for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { cid = font->scaled_font_subset->glyphs[i]; status = cairo_cff_font_get_gid_for_cid (font, cid, &gid); if (unlikely (status)) { free (reverse_map); return status; } fd = font->fdselect[gid]; if (reverse_map[fd] < 0) { font->fd_subset_map[font->num_subset_fontdicts] = fd; reverse_map[fd] = font->num_subset_fontdicts++; } font->fdselect_subset[i] = reverse_map[fd]; } free (reverse_map); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font) { unsigned char buf[100]; unsigned char *end_buf; cairo_status_t status; font->num_fontdicts = 1; font->fd_dict = malloc (sizeof (cairo_hash_table_t *)); if (unlikely (font->fd_dict == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (cff_dict_init (&font->fd_dict[0])) { free (font->fd_dict); font->fd_dict = NULL; font->num_fontdicts = 0; return _cairo_error (CAIRO_STATUS_NO_MEMORY); } font->fd_subset_map = malloc (sizeof (int)); if (unlikely (font->fd_subset_map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->private_dict_offset = malloc (sizeof (int)); if (unlikely (font->private_dict_offset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->fd_subset_map[0] = 0; font->num_subset_fontdicts = 1; /* Set integer operand to max value to use max size encoding to reserve * space for any value later */ end_buf = encode_integer_max (buf, 0); end_buf = encode_integer_max (end_buf, 0); status = cff_dict_set_operands (font->fd_dict[0], PRIVATE_OP, buf, end_buf - buf); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_subset_strings (cairo_cff_font_t *font) { cairo_status_t status; unsigned int i; status = cairo_cff_font_subset_dict_strings (font, font->top_dict); if (unlikely (status)) return status; if (font->is_cid) { for (i = 0; i < font->num_subset_fontdicts; i++) { status = cairo_cff_font_subset_dict_strings (font, font->fd_dict[font->fd_subset_map[i]]); if (unlikely (status)) return status; status = cairo_cff_font_subset_dict_strings (font, font->fd_private_dict[font->fd_subset_map[i]]); if (unlikely (status)) return status; } } else { status = cairo_cff_font_subset_dict_strings (font, font->private_dict); } return status; } /* The Euro is the only the only character in the winansi encoding * with a glyph name that is not a CFF standard string. As the strings * are written before the charset, we need to check during the * subsetting phase if the Euro glyph is required and add the * glyphname to the list of strings to write out. */ static cairo_status_t cairo_cff_font_add_euro_charset_string (cairo_cff_font_t *font) { cairo_status_t status; unsigned int i; int ch; const char *euro = "Euro"; for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { ch = font->scaled_font_subset->to_latin_char[i]; if (ch == 128) { font->euro_sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); status = cff_index_append_copy (&font->strings_subset_index, (unsigned char *)euro, strlen(euro)); return status; } } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_subset_font (cairo_cff_font_t *font) { cairo_status_t status; if (!font->scaled_font_subset->is_latin) { status = cairo_cff_font_set_ros_strings (font); if (unlikely (status)) return status; } status = cairo_cff_font_subset_charstrings_and_subroutines (font); if (unlikely (status)) return status; if (!font->scaled_font_subset->is_latin) { if (font->is_cid) status = cairo_cff_font_subset_fontdict (font); else status = cairo_cff_font_create_cid_fontdict (font); if (unlikely (status)) return status; } else { font->private_dict_offset = malloc (sizeof (int)); if (unlikely (font->private_dict_offset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } status = cairo_cff_font_subset_strings (font); if (unlikely (status)) return status; if (font->scaled_font_subset->is_latin) status = cairo_cff_font_add_euro_charset_string (font); return status; } /* Set the operand of the specified operator in the (already written) * top dict to point to the current position in the output * array. Operands updated with this function must have previously * been encoded with the 5-byte (max) integer encoding. */ static void cairo_cff_font_set_topdict_operator_to_cur_pos (cairo_cff_font_t *font, int operator) { int cur_pos; int offset; int size; unsigned char buf[10]; unsigned char *buf_end; unsigned char *op_ptr; cur_pos = _cairo_array_num_elements (&font->output); buf_end = encode_integer_max (buf, cur_pos); offset = cff_dict_get_location (font->top_dict, operator, &size); assert (offset > 0); op_ptr = _cairo_array_index (&font->output, offset); memcpy (op_ptr, buf, buf_end - buf); } static cairo_status_t cairo_cff_font_write_header (cairo_cff_font_t *font) { return _cairo_array_append_multiple (&font->output, font->header, font->header->header_size); } static cairo_status_t cairo_cff_font_write_name (cairo_cff_font_t *font) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_array_t index; cff_index_init (&index); status = cff_index_append_copy (&index, (unsigned char *) font->ps_name, strlen(font->ps_name)); if (unlikely (status)) goto FAIL; status = cff_index_write (&index, &font->output); if (unlikely (status)) goto FAIL; FAIL: cff_index_fini (&index); return status; } static cairo_status_t cairo_cff_font_write_top_dict (cairo_cff_font_t *font) { uint16_t count; unsigned char buf[10]; unsigned char *p; int offset_index; int dict_start, dict_size; int offset_size = 4; cairo_status_t status; /* Write an index containing the top dict */ count = cpu_to_be16 (1); status = _cairo_array_append_multiple (&font->output, &count, 2); if (unlikely (status)) return status; buf[0] = offset_size; status = _cairo_array_append (&font->output, buf); if (unlikely (status)) return status; encode_index_offset (buf, offset_size, 1); status = _cairo_array_append_multiple (&font->output, buf, offset_size); if (unlikely (status)) return status; /* Reserve space for last element of offset array and update after * dict is written */ offset_index = _cairo_array_num_elements (&font->output); status = _cairo_array_append_multiple (&font->output, buf, offset_size); if (unlikely (status)) return status; dict_start = _cairo_array_num_elements (&font->output); status = cff_dict_write (font->top_dict, &font->output); if (unlikely (status)) return status; dict_size = _cairo_array_num_elements (&font->output) - dict_start; encode_index_offset (buf, offset_size, dict_size + 1); p = _cairo_array_index (&font->output, offset_index); if (p == NULL) return CAIRO_STATUS_NULL_POINTER; memcpy (p, buf, offset_size); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_strings (cairo_cff_font_t *font) { return cff_index_write (&font->strings_subset_index, &font->output); } static cairo_status_t cairo_cff_font_write_global_subrs (cairo_cff_font_t *font) { unsigned int i; unsigned char return_op = TYPE2_return; /* poppler and fontforge don't like zero length subroutines so we * replace unused subroutines with a 'return' instruction. */ if (font->subset_subroutines) { for (i = 0; i < _cairo_array_num_elements (&font->global_sub_index); i++) { if (! font->global_subs_used[i]) cff_index_set_object (&font->global_sub_index, i, &return_op, 1); } } return cff_index_write (&font->global_sub_index, &font->output); } static cairo_status_t cairo_cff_font_write_encoding (cairo_cff_font_t *font) { unsigned char buf[2]; cairo_status_t status; unsigned int i; cairo_cff_font_set_topdict_operator_to_cur_pos (font, ENCODING_OP); buf[0] = 0; /* Format 0 */ buf[1] = font->scaled_font_subset->num_glyphs - 1; status = _cairo_array_append_multiple (&font->output, buf, 2); if (unlikely (status)) return status; for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { unsigned char ch = font->scaled_font_subset->to_latin_char[i]; status = _cairo_array_append (&font->output, &ch); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_fdselect (cairo_cff_font_t *font) { unsigned char data; unsigned int i; cairo_int_status_t status; cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDSELECT_OP); if (font->is_cid) { data = 0; status = _cairo_array_append (&font->output, &data); if (unlikely (status)) return status; for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { data = font->fdselect_subset[i]; status = _cairo_array_append (&font->output, &data); if (unlikely (status)) return status; } } else { unsigned char byte; uint16_t word; status = _cairo_array_grow_by (&font->output, 9); if (unlikely (status)) return status; byte = 3; status = _cairo_array_append (&font->output, &byte); assert (status == CAIRO_INT_STATUS_SUCCESS); word = cpu_to_be16 (1); status = _cairo_array_append_multiple (&font->output, &word, 2); assert (status == CAIRO_INT_STATUS_SUCCESS); word = cpu_to_be16 (0); status = _cairo_array_append_multiple (&font->output, &word, 2); assert (status == CAIRO_INT_STATUS_SUCCESS); byte = 0; status = _cairo_array_append (&font->output, &byte); assert (status == CAIRO_INT_STATUS_SUCCESS); word = cpu_to_be16 (font->scaled_font_subset->num_glyphs); status = _cairo_array_append_multiple (&font->output, &word, 2); assert (status == CAIRO_INT_STATUS_SUCCESS); } return CAIRO_STATUS_SUCCESS; } /* Winansi to CFF standard strings mapping for characters 128 to 255 */ static const int winansi_to_cff_std_string[] = { /* 128 */ 0, 0, 117, 101, 118, 121, 112, 113, 126, 122, 192, 107, 142, 0, 199, 0, /* 144 */ 0, 65, 8, 105, 119, 116, 111, 137, 127, 153, 221, 108, 148, 0, 228, 198, /* 160 */ 0, 96, 97, 98, 103, 100, 160, 102, 131, 170, 139, 106, 151, 0, 165, 128, /* 176 */ 161, 156, 164, 169, 125, 152, 115, 114, 133, 150, 143, 120, 158, 155, 163, 123, /* 192 */ 174, 171, 172, 176, 173, 175, 138, 177, 181, 178, 179, 180, 185, 182, 183, 184, /* 208 */ 154, 186, 190, 187, 188, 191, 189, 168, 141, 196, 193, 194, 195, 197, 157, 149, /* 224 */ 203, 200, 201, 205, 202, 204, 144, 206, 210, 207, 208, 209, 214, 211, 212, 213, /* 240 */ 167, 215, 219, 216, 217, 220, 218, 159, 147, 225, 222, 223, 224, 226, 162, 227, }; static int cairo_cff_font_get_sid_for_winansi_char (cairo_cff_font_t *font, int ch) { int sid; if (ch == 39) { sid = 104; } else if (ch == 96) { sid = 124; } else if (ch >= 32 && ch <= 126) { sid = ch - 31; } else if (ch == 128) { assert (font->euro_sid >= NUM_STD_STRINGS); sid = font->euro_sid; } else if (ch >= 128 && ch <= 255) { sid = winansi_to_cff_std_string[ch - 128]; } else { sid = 0; } return sid; } static cairo_status_t cairo_cff_font_write_type1_charset (cairo_cff_font_t *font) { unsigned char format = 0; unsigned int i; int ch, sid; cairo_status_t status; uint16_t sid_be16; cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP); status = _cairo_array_append (&font->output, &format); if (unlikely (status)) return status; for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) { ch = font->scaled_font_subset->to_latin_char[i]; sid = cairo_cff_font_get_sid_for_winansi_char (font, ch); sid_be16 = cpu_to_be16(sid); status = _cairo_array_append_multiple (&font->output, &sid_be16, sizeof(sid_be16)); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_cid_charset (cairo_cff_font_t *font) { unsigned char byte; uint16_t word; cairo_status_t status; cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP); status = _cairo_array_grow_by (&font->output, 5); if (unlikely (status)) return status; byte = 2; status = _cairo_array_append (&font->output, &byte); assert (status == CAIRO_STATUS_SUCCESS); word = cpu_to_be16 (1); status = _cairo_array_append_multiple (&font->output, &word, 2); assert (status == CAIRO_STATUS_SUCCESS); word = cpu_to_be16 (font->scaled_font_subset->num_glyphs - 2); status = _cairo_array_append_multiple (&font->output, &word, 2); assert (status == CAIRO_STATUS_SUCCESS); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_charstrings (cairo_cff_font_t *font) { cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSTRINGS_OP); return cff_index_write (&font->charstrings_subset_index, &font->output); } static cairo_status_t cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font) { unsigned int i; cairo_int_status_t status; unsigned int offset_array; unsigned char *offset_array_ptr; int offset_base; uint16_t count; uint8_t offset_size = 4; cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDARRAY_OP); count = cpu_to_be16 (font->num_subset_fontdicts); status = _cairo_array_append_multiple (&font->output, &count, sizeof (uint16_t)); if (unlikely (status)) return status; status = _cairo_array_append (&font->output, &offset_size); if (unlikely (status)) return status; offset_array = _cairo_array_num_elements (&font->output); status = _cairo_array_allocate (&font->output, (font->num_subset_fontdicts + 1)*offset_size, (void **) &offset_array_ptr); if (unlikely (status)) return status; offset_base = _cairo_array_num_elements (&font->output) - 1; put_unaligned_be32(1, offset_array_ptr); offset_array += sizeof(uint32_t); for (i = 0; i < font->num_subset_fontdicts; i++) { status = cff_dict_write (font->fd_dict[font->fd_subset_map[i]], &font->output); if (unlikely (status)) return status; offset_array_ptr = _cairo_array_index (&font->output, offset_array); put_unaligned_be32 (_cairo_array_num_elements (&font->output) - offset_base, offset_array_ptr); offset_array += sizeof(uint32_t); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_private_dict (cairo_cff_font_t *font, int dict_num, cairo_hash_table_t *parent_dict, cairo_hash_table_t *private_dict) { int offset; int size; unsigned char buf[10]; unsigned char *buf_end; unsigned char *p; cairo_status_t status; /* Write private dict and update offset and size in top dict */ font->private_dict_offset[dict_num] = _cairo_array_num_elements (&font->output); status = cff_dict_write (private_dict, &font->output); if (unlikely (status)) return status; size = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num]; /* private entry has two operands - size and offset */ buf_end = encode_integer_max (buf, size); buf_end = encode_integer_max (buf_end, font->private_dict_offset[dict_num]); offset = cff_dict_get_location (parent_dict, PRIVATE_OP, &size); assert (offset > 0); p = _cairo_array_index (&font->output, offset); memcpy (p, buf, buf_end - buf); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_local_sub (cairo_cff_font_t *font, int dict_num, cairo_hash_table_t *private_dict, cairo_array_t *local_sub_index, cairo_bool_t *local_subs_used) { int offset; int size; unsigned char buf[10]; unsigned char *buf_end; unsigned char *p; cairo_status_t status; unsigned int i; unsigned char return_op = TYPE2_return; if (_cairo_array_num_elements (local_sub_index) > 0) { /* Write local subroutines and update offset in private * dict. Local subroutines offset is relative to start of * private dict */ offset = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num]; buf_end = encode_integer_max (buf, offset); offset = cff_dict_get_location (private_dict, LOCAL_SUB_OP, &size); assert (offset > 0); p = _cairo_array_index (&font->output, offset); memcpy (p, buf, buf_end - buf); /* poppler and fontforge don't like zero length subroutines so * we replace unused subroutines with a 'return' instruction. */ if (font->subset_subroutines) { for (i = 0; i < _cairo_array_num_elements (local_sub_index); i++) { if (! local_subs_used[i]) cff_index_set_object (local_sub_index, i, &return_op, 1); } } status = cff_index_write (local_sub_index, &font->output); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font) { unsigned int i; cairo_int_status_t status; if (font->is_cid) { for (i = 0; i < font->num_subset_fontdicts; i++) { status = cairo_cff_font_write_private_dict ( font, i, font->fd_dict[font->fd_subset_map[i]], font->fd_private_dict[font->fd_subset_map[i]]); if (unlikely (status)) return status; } for (i = 0; i < font->num_subset_fontdicts; i++) { status = cairo_cff_font_write_local_sub ( font, i, font->fd_private_dict[font->fd_subset_map[i]], &font->fd_local_sub_index[font->fd_subset_map[i]], font->fd_local_subs_used[font->fd_subset_map[i]]); if (unlikely (status)) return status; } } else { status = cairo_cff_font_write_private_dict (font, 0, font->fd_dict[0], font->private_dict); if (unlikely (status)) return status; status = cairo_cff_font_write_local_sub (font, 0, font->private_dict, &font->local_sub_index, font->local_subs_used); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_cff_font_write_type1_private_dict_and_local_sub (cairo_cff_font_t *font) { cairo_int_status_t status; status = cairo_cff_font_write_private_dict (font, 0, font->top_dict, font->private_dict); if (unlikely (status)) return status; status = cairo_cff_font_write_local_sub (font, 0, font->private_dict, &font->local_sub_index, font->local_subs_used); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } typedef cairo_status_t (*font_write_t) (cairo_cff_font_t *font); static const font_write_t font_write_cid_funcs[] = { cairo_cff_font_write_header, cairo_cff_font_write_name, cairo_cff_font_write_top_dict, cairo_cff_font_write_strings, cairo_cff_font_write_global_subrs, cairo_cff_font_write_cid_charset, cairo_cff_font_write_fdselect, cairo_cff_font_write_charstrings, cairo_cff_font_write_cid_fontdict, cairo_cff_font_write_cid_private_dict_and_local_sub, }; static const font_write_t font_write_type1_funcs[] = { cairo_cff_font_write_header, cairo_cff_font_write_name, cairo_cff_font_write_top_dict, cairo_cff_font_write_strings, cairo_cff_font_write_global_subrs, cairo_cff_font_write_encoding, cairo_cff_font_write_type1_charset, cairo_cff_font_write_charstrings, cairo_cff_font_write_type1_private_dict_and_local_sub, }; static cairo_status_t cairo_cff_font_write_subset (cairo_cff_font_t *font) { cairo_int_status_t status; unsigned int i; if (font->scaled_font_subset->is_latin) { for (i = 0; i < ARRAY_LENGTH (font_write_type1_funcs); i++) { status = font_write_type1_funcs[i] (font); if (unlikely (status)) return status; } } else { for (i = 0; i < ARRAY_LENGTH (font_write_cid_funcs); i++) { status = font_write_cid_funcs[i] (font); if (unlikely (status)) return status; } } return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_cff_font_generate (cairo_cff_font_t *font, const char **data, unsigned long *length) { cairo_int_status_t status; status = cairo_cff_font_read_font (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; status = cairo_cff_font_write_subset (font); if (unlikely (status)) return status; *data = _cairo_array_index (&font->output, 0); *length = _cairo_array_num_elements (&font->output); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t cairo_cff_font_create_set_widths (cairo_cff_font_t *font) { unsigned long size; unsigned long long_entry_size; unsigned long short_entry_size; unsigned int i; tt_hhea_t hhea; int num_hmetrics; uint16_t short_entry; int glyph_index; cairo_int_status_t status; size = sizeof (tt_hhea_t); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hhea, 0, (unsigned char*) &hhea, &size); if (unlikely (status)) return status; num_hmetrics = be16_to_cpu (hhea.num_hmetrics); 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); if (glyph_index < num_hmetrics) { status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, glyph_index * long_entry_size, (unsigned char *) &short_entry, &short_entry_size); if (unlikely (status)) return status; } else { status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, (num_hmetrics - 1) * long_entry_size, (unsigned char *) &short_entry, &short_entry_size); if (unlikely (status)) return status; } font->widths[i] = be16_to_cpu (short_entry); } return CAIRO_STATUS_SUCCESS; } static cairo_bool_t check_fontdata_is_cff (const unsigned char *data, long length) { 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; tt_head_t head; tt_hhea_t hhea; unsigned long size, data_length; if (!backend->load_truetype_table) return CAIRO_INT_STATUS_UNSUPPORTED; data_length = 0; status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_CFF, 0, NULL, &data_length); if (status) return status; size = sizeof (tt_head_t); 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 (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 (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, 0, NULL, &size); if (unlikely (status)) return status; 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); font->y_max = (int16_t) be16_to_cpu (head.y_max); font->ascent = (int16_t) be16_to_cpu (hhea.ascender); font->descent = (int16_t) be16_to_cpu (hhea.descender); font->units_per_em = (int16_t) be16_to_cpu (head.units_per_em); if (font->units_per_em == 0) font->units_per_em = 1000; font->font_name = NULL; status = _cairo_truetype_read_font_name (font->scaled_font_subset->scaled_font, &font->ps_name, &font->font_name); if (_cairo_status_is_error (status)) return status; 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); 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 = calloc (1, 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 fail3; } if (font->is_opentype) { status = cairo_cff_font_create_set_widths (font); if (unlikely (status)) goto fail4; } status = cff_dict_init (&font->top_dict); if (unlikely (status)) goto fail4; status = cff_dict_init (&font->private_dict); if (unlikely (status)) goto fail5; cff_index_init (&font->strings_index); cff_index_init (&font->charstrings_index); cff_index_init (&font->global_sub_index); cff_index_init (&font->local_sub_index); cff_index_init (&font->charstrings_subset_index); cff_index_init (&font->strings_subset_index); font->euro_sid = 0; font->fdselect = NULL; font->fd_dict = NULL; font->fd_private_dict = NULL; font->fd_local_sub_index = NULL; font->fd_local_sub_bias = NULL; font->fdselect_subset = NULL; font->fd_subset_map = NULL; font->private_dict_offset = NULL; font->global_subs_used = NULL; font->local_subs_used = NULL; font->fd_local_subs_used = NULL; *font_return = font; return CAIRO_STATUS_SUCCESS; fail5: _cairo_hash_table_destroy (font->top_dict); fail4: free (font->widths); fail3: free (font->subset_font_name); fail2: free (font->ps_name); _cairo_array_fini (&font->output); fail1: free (font->data); free (font->font_name); free (font); return status; } static void cairo_cff_font_destroy (cairo_cff_font_t *font) { unsigned int i; free (font->widths); free (font->font_name); free (font->ps_name); free (font->subset_font_name); _cairo_array_fini (&font->output); cff_dict_fini (font->top_dict); cff_dict_fini (font->private_dict); cff_index_fini (&font->strings_index); cff_index_fini (&font->charstrings_index); cff_index_fini (&font->global_sub_index); cff_index_fini (&font->local_sub_index); cff_index_fini (&font->charstrings_subset_index); cff_index_fini (&font->strings_subset_index); /* If we bailed out early as a result of an error some of the * following cairo_cff_font_t members may still be NULL */ if (font->fd_dict) { for (i = 0; i < font->num_fontdicts; i++) { if (font->fd_dict[i]) cff_dict_fini (font->fd_dict[i]); } free (font->fd_dict); } free (font->global_subs_used); free (font->local_subs_used); free (font->fd_subset_map); free (font->private_dict_offset); if (font->is_cid) { free (font->fdselect); free (font->fdselect_subset); if (font->fd_private_dict) { for (i = 0; i < font->num_fontdicts; i++) { if (font->fd_private_dict[i]) cff_dict_fini (font->fd_private_dict[i]); } free (font->fd_private_dict); } if (font->fd_local_sub_index) { for (i = 0; i < font->num_fontdicts; i++) cff_index_fini (&font->fd_local_sub_index[i]); free (font->fd_local_sub_index); } free (font->fd_local_sub_bias); if (font->fd_local_subs_used) { for (i = 0; i < font->num_fontdicts; i++) { free (font->fd_local_subs_used[i]); } free (font->fd_local_subs_used); } free (font->fd_default_width); free (font->fd_nominal_width); } free (font->data); free (font); } cairo_status_t _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, const char *subset_name, cairo_scaled_font_subset_t *font_subset) { cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */ cairo_status_t status; const char *data = NULL; /* squelch bogus compiler warning */ unsigned long length = 0; /* squelch bogus compiler warning */ unsigned int i; status = _cairo_cff_font_create (font_subset, &font, subset_name); if (unlikely (status)) return status; status = cairo_cff_font_generate (font, &data, &length); if (unlikely (status)) goto fail1; cff_subset->ps_name = strdup (font->ps_name); if (unlikely (cff_subset->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } if (font->font_name) { cff_subset->family_name_utf8 = strdup (font->font_name); if (cff_subset->family_name_utf8 == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } } else { cff_subset->family_name_utf8 = NULL; } cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); if (unlikely (cff_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) cff_subset->widths[i] = (double)font->widths[i]/font->units_per_em; cff_subset->x_min = (double)font->x_min/font->units_per_em; cff_subset->y_min = (double)font->y_min/font->units_per_em; cff_subset->x_max = (double)font->x_max/font->units_per_em; cff_subset->y_max = (double)font->y_max/font->units_per_em; cff_subset->ascent = (double)font->ascent/font->units_per_em; cff_subset->descent = (double)font->descent/font->units_per_em; cff_subset->data = malloc (length); if (unlikely (cff_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; } memcpy (cff_subset->data, data, length); cff_subset->data_length = length; cairo_cff_font_destroy (font); return CAIRO_STATUS_SUCCESS; fail4: free (cff_subset->widths); fail3: free (cff_subset->family_name_utf8); fail2: free (cff_subset->ps_name); fail1: cairo_cff_font_destroy (font); return status; } void _cairo_cff_subset_fini (cairo_cff_subset_t *subset) { free (subset->ps_name); free (subset->family_name_utf8); free (subset->widths); free (subset->data); } cairo_bool_t _cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font) { const cairo_scaled_font_backend_t *backend; cairo_int_status_t status; unsigned char *data; unsigned long data_length; unsigned char *current_ptr; unsigned char *data_end; cff_header_t *header; cff_index_element_t *element; cairo_hash_table_t *top_dict; cairo_array_t index; int size; cairo_bool_t is_cid = FALSE; backend = scaled_font->backend; data = NULL; data_length = 0; 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_INT_STATUS_SUCCESS) { 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, 0, data, &data_length); 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_INT_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; /* skip header */ if (data_length < sizeof (cff_header_t)) goto fail1; header = (cff_header_t *) data; current_ptr = data + header->header_size; /* skip name */ cff_index_init (&index); status = cff_index_read (&index, ¤t_ptr, data_end); cff_index_fini (&index); if (status) goto fail1; /* read top dict */ cff_index_init (&index); status = cff_index_read (&index, ¤t_ptr, data_end); if (unlikely (status)) goto fail2; status = cff_dict_init (&top_dict); if (unlikely (status)) goto fail2; element = _cairo_array_index (&index, 0); if (element == NULL) { status = _cairo_error (CAIRO_STATUS_NULL_POINTER); goto fail3; } status = cff_dict_read (top_dict, element->data, element->length); if (unlikely (status)) goto fail3; /* check for ROS operator indicating a CID font */ if (cff_dict_get_operands (top_dict, ROS_OP, &size) != NULL) is_cid = TRUE; fail3: cff_dict_fini (top_dict); fail2: cff_index_fini (&index); fail1: free (data); return is_cid; } static cairo_int_status_t _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset, cairo_cff_font_t **font_return, const char *subset_name) { cairo_status_t status; cairo_cff_font_t *font; font = malloc (sizeof (cairo_cff_font_t)); if (unlikely (font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->backend = NULL; 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 fail1; font->subset_font_name = strdup (subset_name); if (unlikely (font->subset_font_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } font->ps_name = strdup (subset_name); if (unlikely (font->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } font->font_name = NULL; font->x_min = 0; font->y_min = 0; font->x_max = 0; font->y_max = 0; font->ascent = 0; font->descent = 0; font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); if (unlikely (font->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } font->data_length = 0; font->data = NULL; font->data_end = NULL; status = cff_dict_init (&font->top_dict); if (unlikely (status)) goto fail4; status = cff_dict_init (&font->private_dict); if (unlikely (status)) goto fail5; cff_index_init (&font->strings_index); cff_index_init (&font->charstrings_index); cff_index_init (&font->global_sub_index); cff_index_init (&font->local_sub_index); cff_index_init (&font->charstrings_subset_index); cff_index_init (&font->strings_subset_index); font->global_subs_used = NULL; font->local_subs_used = NULL; font->subset_subroutines = FALSE; font->fdselect = NULL; font->fd_dict = NULL; font->fd_private_dict = NULL; font->fd_local_sub_index = NULL; font->fdselect_subset = NULL; font->fd_subset_map = NULL; font->private_dict_offset = NULL; *font_return = font; return CAIRO_STATUS_SUCCESS; fail5: _cairo_hash_table_destroy (font->top_dict); fail4: free (font->widths); fail3: free (font->font_name); free (font->ps_name); fail2: free (font->subset_font_name); fail1: _cairo_array_fini (&font->output); free (font); return status; } static cairo_int_status_t cairo_cff_font_fallback_generate (cairo_cff_font_t *font, cairo_type2_charstrings_t *type2_subset, const char **data, unsigned long *length) { cairo_int_status_t status; cff_header_t header; cairo_array_t *charstring; unsigned char buf[40]; unsigned char *end_buf, *end_buf2; unsigned int i; int sid; /* Create header */ header.major = 1; header.minor = 0; header.header_size = 4; header.offset_size = 4; font->header = &header; /* Create Top Dict */ font->is_cid = FALSE; snprintf((char*)buf, sizeof(buf), "CairoFont-%u-%u", font->scaled_font_subset->font_id, font->scaled_font_subset->subset_id); sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); status = cff_index_append_copy (&font->strings_subset_index, (unsigned char *)buf, strlen((char*)buf)); if (unlikely (status)) return status; end_buf = encode_integer (buf, sid); status = cff_dict_set_operands (font->top_dict, FULLNAME_OP, buf, end_buf - buf); if (unlikely (status)) return status; status = cff_dict_set_operands (font->top_dict, FAMILYNAME_OP, buf, end_buf - buf); if (unlikely (status)) return status; end_buf = encode_integer (buf, type2_subset->x_min); end_buf = encode_integer (end_buf, type2_subset->y_min); end_buf = encode_integer (end_buf, type2_subset->x_max); end_buf = encode_integer (end_buf, type2_subset->y_max); status = cff_dict_set_operands (font->top_dict, FONTBBOX_OP, buf, end_buf - buf); if (unlikely (status)) return status; end_buf = encode_integer_max (buf, 0); status = cff_dict_set_operands (font->top_dict, CHARSTRINGS_OP, buf, end_buf - buf); if (unlikely (status)) return status; if (font->scaled_font_subset->is_latin) { status = cff_dict_set_operands (font->top_dict, ENCODING_OP, buf, end_buf - buf); if (unlikely (status)) return status; /* Private has two operands - size and offset */ end_buf2 = encode_integer_max (end_buf, 0); cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf2 - buf); } else { status = cff_dict_set_operands (font->top_dict, FDSELECT_OP, buf, end_buf - buf); if (unlikely (status)) return status; status = cff_dict_set_operands (font->top_dict, FDARRAY_OP, buf, end_buf - buf); if (unlikely (status)) return status; } status = cff_dict_set_operands (font->top_dict, CHARSET_OP, buf, end_buf - buf); if (unlikely (status)) return status; if (!font->scaled_font_subset->is_latin) { status = cairo_cff_font_set_ros_strings (font); if (unlikely (status)) return status; /* Create CID FD dictionary */ status = cairo_cff_font_create_cid_fontdict (font); if (unlikely (status)) return status; } else { font->private_dict_offset = malloc (sizeof (int)); if (unlikely (font->private_dict_offset == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } /* Create charstrings */ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { charstring = _cairo_array_index(&type2_subset->charstrings, i); if (charstring == NULL) return _cairo_error (CAIRO_STATUS_NULL_POINTER); status = cff_index_append (&font->charstrings_subset_index, _cairo_array_index (charstring, 0), _cairo_array_num_elements (charstring)); if (unlikely (status)) return status; } if (font->scaled_font_subset->is_latin) status = cairo_cff_font_add_euro_charset_string (font); status = cairo_cff_font_write_subset (font); if (unlikely (status)) return status; *data = _cairo_array_index (&font->output, 0); *length = _cairo_array_num_elements (&font->output); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, const char *subset_name, cairo_scaled_font_subset_t *font_subset) { cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */ cairo_status_t status; const char *data = NULL; /* squelch bogus compiler warning */ unsigned long length = 0; /* squelch bogus compiler warning */ unsigned int i; cairo_type2_charstrings_t type2_subset; status = _cairo_cff_font_fallback_create (font_subset, &font, subset_name); if (unlikely (status)) return status; status = _cairo_type2_charstrings_init (&type2_subset, font_subset); if (unlikely (status)) goto fail1; status = cairo_cff_font_fallback_generate (font, &type2_subset, &data, &length); if (unlikely (status)) goto fail2; cff_subset->family_name_utf8 = NULL; cff_subset->ps_name = strdup (font->ps_name); if (unlikely (cff_subset->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); if (unlikely (cff_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) cff_subset->widths[i] = (double)type2_subset.widths[i]/1000; cff_subset->x_min = (double)type2_subset.x_min/1000; cff_subset->y_min = (double)type2_subset.y_min/1000; cff_subset->x_max = (double)type2_subset.x_max/1000; cff_subset->y_max = (double)type2_subset.y_max/1000; cff_subset->ascent = (double)type2_subset.y_max/1000; cff_subset->descent = (double)type2_subset.y_min/1000; cff_subset->data = malloc (length); if (unlikely (cff_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; } memcpy (cff_subset->data, data, length); cff_subset->data_length = length; _cairo_type2_charstrings_fini (&type2_subset); cairo_cff_font_destroy (font); return CAIRO_STATUS_SUCCESS; fail4: free (cff_subset->widths); fail3: free (cff_subset->ps_name); fail2: _cairo_type2_charstrings_fini (&type2_subset); fail1: cairo_cff_font_destroy (font); return status; } void _cairo_cff_fallback_fini (cairo_cff_subset_t *subset) { free (subset->ps_name); free (subset->widths); free (subset->data); } #endif /* CAIRO_HAS_FONT_SUBSET */