| /* 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 <ajohnson@redneon.com> |
| * Eugeniy Meshcheryakov <eugen@debian.org> |
| */ |
| |
| /* |
| * 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-error-private.h" |
| |
| #if CAIRO_HAS_FONT_SUBSET |
| |
| #include "cairo-scaled-font-subsets-private.h" |
| #include "cairo-truetype-subset-private.h" |
| #include <string.h> |
| |
| /* 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 ENCODING_OP 0x0010 |
| #define FAMILYNAME_OP 0x0003 |
| #define FDARRAY_OP 0x0c24 |
| #define FDSELECT_OP 0x0c25 |
| #define FONTBBOX_OP 0x0005 |
| #define FONTNAME_OP 0x0c26 |
| #define FULLNAME_OP 0x0002 |
| #define LOCAL_SUB_OP 0x0013 |
| #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 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; |
| int num_glyphs; |
| cairo_bool_t is_cid; |
| int units_per_em; |
| int global_sub_bias; |
| int local_sub_bias; |
| |
| /* 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; |
| |
| /* 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_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 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 = be16_to_cpu( *((uint16_t *)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->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; |
| |
| /* 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); |
| 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, |
| 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; |
| |
| } |
| |
| 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 = be16_to_cpu( *((uint16_t *)p) ); |
| p += 2; |
| for (i = 0; i < num_ranges; i++) |
| { |
| first = be16_to_cpu( *((uint16_t *)p) ); |
| p += 2; |
| fd = *p++; |
| last = be16_to_cpu( *((uint16_t *)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; |
| } |
| |
| 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->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 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); |
| 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); |
| 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, FDSELECT_OP, &size); |
| 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); |
| 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->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); |
| if (unlikely (status)) |
| goto fail; |
| |
| } 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); |
| 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 */ |
| /* This actually a 16.16 fixed-point number however we are not interested in |
| * the value of fixed-point numbers. */ |
| *integer = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]; |
| p += 5; |
| } |
| return p; |
| } |
| |
| /* 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 |
| * the subroutine operator). If this occurs, subroutine subsetting is |
| * disabled since we can't easily determine which subroutines are |
| * used. |
| */ |
| static cairo_status_t |
| cairo_cff_parse_charstring (cairo_cff_font_t *font, |
| unsigned char *charstring, int length, |
| int glyph_id) |
| { |
| 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; |
| } 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; |
| font->type2_stack_size = 0; |
| 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; |
| font->type2_stack_size = 0; |
| font->type2_hintmask_bytes = (font->type2_num_hints+7)/8; |
| } |
| |
| hint_bytes = font->type2_hintmask_bytes; |
| p++; |
| p += hint_bytes; |
| } 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->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); |
| } |
| } 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]) { |
| font->local_subs_used[sub_num] = TRUE; |
| cairo_cff_parse_charstring (font, element->data, element->length, glyph_id); |
| } |
| } |
| 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; |
| 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]) { |
| font->global_subs_used[sub_num] = TRUE; |
| cairo_cff_parse_charstring (font, element->data, element->length, glyph_id); |
| } |
| font->type2_nesting_level--; |
| } else if (*p == 12) { |
| /* 2 byte instruction */ |
| 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_subroutines_used (cairo_cff_font_t *font, |
| unsigned char *charstring, int length, |
| int glyph_id) |
| { |
| 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; |
| |
| return cairo_cff_parse_charstring (font, charstring, length, glyph_id); |
| } |
| |
| static cairo_status_t |
| cairo_cff_font_subset_charstrings_and_subroutines (cairo_cff_font_t *font) |
| { |
| cff_index_element_t *element; |
| unsigned int i; |
| cairo_status_t status; |
| unsigned long glyph; |
| |
| font->subset_subroutines = TRUE; |
| for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { |
| glyph = font->scaled_font_subset->glyphs[i]; |
| element = _cairo_array_index (&font->charstrings_index, glyph); |
| 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_subroutines_used (font, element->data, element->length, glyph); |
| if (status == CAIRO_INT_STATUS_UNSUPPORTED) { |
| font->subset_subroutines = FALSE; |
| } 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; |
| |
| 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++) { |
| fd = font->fdselect[font->scaled_font_subset->glyphs[i]]; |
| 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->subset_font_name, |
| strlen(font->subset_font_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); |
| 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_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 (0); |
| status = _cairo_array_append_multiple (&font->output, &word, 2); |
| assert (status == CAIRO_STATUS_SUCCESS); |
| |
| byte = 0; |
| status = _cairo_array_append (&font->output, &byte); |
| assert (status == CAIRO_STATUS_SUCCESS); |
| |
| word = cpu_to_be16 (font->scaled_font_subset->num_glyphs); |
| status = _cairo_array_append_multiple (&font->output, &word, 2); |
| assert (status == CAIRO_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, 187, 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); |
| if (unlikely (status)) |
| return status; |
| |
| 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; |
| uint32_t *offset_array; |
| 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; |
| status = _cairo_array_allocate (&font->output, |
| (font->num_subset_fontdicts + 1)*offset_size, |
| (void **) &offset_array); |
| if (unlikely (status)) |
| return status; |
| offset_base = _cairo_array_num_elements (&font->output) - 1; |
| *offset_array++ = cpu_to_be32(1); |
| 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++ = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base); |
| } |
| |
| 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; |
| |
| 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; |
| unsigned char buf[10]; |
| 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 = 1; 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, |
| buf, &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, |
| buf, &short_entry_size); |
| if (unlikely (status)) |
| return status; |
| } |
| font->widths[i] = be16_to_cpu (*((int16_t*)buf)); |
| } |
| |
| 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_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, |
| TT_TAG_CFF, 0, NULL, &data_length); |
| if (unlikely (status)) |
| return status; |
| |
| size = sizeof (tt_head_t); |
| status = backend->load_truetype_table (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, |
| 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, |
| 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); |
| 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 (scaled_font_subset->scaled_font, |
| &font->ps_name, |
| &font->font_name); |
| if (_cairo_status_is_error (status)) |
| goto fail3; |
| |
| /* 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; |
| } |
| |
| snprintf(font->ps_name, 30, "CairoFont-%u-%u", |
| scaled_font_subset->font_id, |
| scaled_font_subset->subset_id); |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| 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; |
| |
| status = cff_dict_init (&font->private_dict); |
| if (unlikely (status)) |
| goto fail7; |
| |
| 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; |
| |
| fail7: |
| _cairo_hash_table_destroy (font->top_dict); |
| fail6: |
| free (font->data); |
| fail5: |
| free (font->widths); |
| fail4: |
| if (font->font_name) |
| free (font->font_name); |
| fail3: |
| free (font->subset_font_name); |
| fail2: |
| _cairo_array_fini (&font->output); |
| free (font); |
| |
| return status; |
| } |
| |
| static void |
| cairo_cff_font_destroy (cairo_cff_font_t *font) |
| { |
| unsigned int i; |
| |
| free (font->widths); |
| if (font->font_name) |
| 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); |
| } |
| if (font->global_subs_used) |
| free (font->global_subs_used); |
| if (font->local_subs_used) |
| free (font->local_subs_used); |
| if (font->fd_subset_map) |
| free (font->fd_subset_map); |
| if (font->private_dict_offset) |
| free (font->private_dict_offset); |
| |
| if (font->is_cid) { |
| if (font->fdselect) |
| free (font->fdselect); |
| if (font->fdselect_subset) |
| 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); |
| } |
| if (font->fd_local_sub_bias) |
| free (font->fd_local_sub_bias); |
| if (font->fd_local_subs_used) { |
| for (i = 0; i < font->num_fontdicts; i++) { |
| if (font->fd_local_subs_used[i]) |
| free (font->fd_local_subs_used[i]); |
| } |
| free (font->fd_local_subs_used); |
| } |
| |
| } |
| |
| if (font->data) |
| 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->font_name = strdup (font->font_name); |
| if (cff_subset->font_name == NULL) { |
| status = _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| goto fail2; |
| } |
| } else { |
| cff_subset->font_name = 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: |
| if (cff_subset->font_name) |
| free (cff_subset->font_name); |
| 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); |
| if (subset->font_name) |
| free (subset->font_name); |
| 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_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; |
| if (!backend->load_truetype_table) |
| return FALSE; |
| |
| /* check for CFF font */ |
| data_length = 0; |
| status = backend->load_truetype_table(scaled_font, |
| TT_TAG_CFF, 0, NULL, &data_length); |
| if (status) |
| 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, |
| 0, data, &data_length); |
| if (unlikely (status)) |
| 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); |
| 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->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: |
| if (font->font_name) |
| 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); |
| if (unlikely (status)) |
| return status; |
| |
| } 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); |
| |
| 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->font_name = 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 */ |