cairo/src/cairo-cff-subset.c

1664 lines
48 KiB
C

/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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>
*/
#include "cairoint.h"
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-truetype-subset-private.h"
/* CFF Dict Operators. If the high byte is 0 the command is encoded
* with a single byte. */
#define BASEFONTNAME_OP 0x0c16
#define CHARSET_OP 0x000f
#define CHARSTRINGS_OP 0x0011
#define COPYRIGHT_OP 0x0c00
#define ENCODING_OP 0x0010
#define FAMILYNAME_OP 0x0003
#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 VERSION_OP 0x0000
#define WEIGHT_OP 0x0004
#define NUM_STD_STRINGS 391
#define ARRAY_LENGTH(a) ( (sizeof (a)) / (sizeof ((a)[0])) )
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 _cff_charset {
cairo_bool_t is_builtin;
const uint16_t *sids;
const unsigned char *data;
int length;
} cff_charset_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;
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;
cff_charset_t charset;
int charset_offset;
/* Subsetted Font Data */
char *subset_font_name;
cairo_array_t charstrings_subset_index;
cairo_array_t strings_subset_index;
cairo_array_t charset_subset;
cairo_array_t output;
/* Subset Metrics */
int *widths;
int x_min, y_min, x_max, y_max;
int ascent, descent;
} 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 >= -1131 && i <= -108) {
*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 (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 (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 (status)
return status;
offset = 1;
encode_index_offset (buf, offset_size, offset);
status = _cairo_array_append_multiple (output, buf, offset_size);
if (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 (status)
return status;
}
for (i = 0; i < num_elem; i++) {
element = _cairo_array_index (index, i);
status = _cairo_array_append_multiple (output,
element->data,
element->length);
if (status)
return status;
}
return CAIRO_STATUS_SUCCESS;
}
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, unsigned char *object , int length)
{
cff_index_element_t element;
element.length = length;
element.is_copy = TRUE;
element.data = malloc (element.length);
if (element.data == NULL)
return CAIRO_STATUS_NO_MEMORY;
memcpy (element.data, object, element.length);
return _cairo_array_append (index, &element);
}
static void
cff_index_fini (cairo_array_t *index)
{
cff_index_element_t *element;
int i;
for (i = 0; i < _cairo_array_num_elements (index); i++) {
element = _cairo_array_index (index, i);
if (element->is_copy)
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 void
cff_dict_init (cairo_hash_table_t **dict)
{
*dict = _cairo_hash_table_create (_cairo_cff_dict_equal);
}
static void
_cairo_dict_init_key (cff_dict_operator_t *key, int operator)
{
key->base.hash = (unsigned long) operator;
key->operator = operator;
}
static cff_dict_operator_t *
cff_dict_create_operator (int operator,
unsigned char *operand,
int operand_length)
{
cff_dict_operator_t *op;
op = malloc (sizeof (cff_dict_operator_t));
if (op == NULL)
return NULL;
_cairo_dict_init_key (op, operator);
op->operand = malloc (operand_length);
if (op->operand == NULL) {
free (op);
return NULL;
}
memcpy (op->operand, operand, operand_length);
op->operand_length = operand_length;
op->operand_offset = -1;
return op;
}
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 (status)
goto fail;
p += size;
} else {
p = decode_operator (p, &operator);
op = cff_dict_create_operator (operator,
_cairo_array_index (&operands, 0),
_cairo_array_num_elements (&operands));
if (op == NULL) {
status = CAIRO_STATUS_NO_MEMORY;
goto fail;
}
status = _cairo_hash_table_insert (dict, &op->base);
if (status)
goto fail;
_cairo_array_truncate (&operands, 0);
}
}
fail:
_cairo_array_fini (&operands);
return status;
}
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);
if (_cairo_hash_table_lookup (dict, &key.base,
(cairo_hash_entry_t **) &op))
{
*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);
if (_cairo_hash_table_lookup (dict, &key.base,
(cairo_hash_entry_t **) &op))
{
free (op->operand);
op->operand = malloc (size);
if (op->operand == NULL)
return CAIRO_STATUS_NO_MEMORY;
memcpy (op->operand, operand, size);
op->operand_length = size;
}
else
{
op = cff_dict_create_operator (operator, operand, size);
if (op == NULL)
return CAIRO_STATUS_NO_MEMORY;
status = _cairo_hash_table_insert (dict, &op->base);
if (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);
if (_cairo_hash_table_lookup (dict, &key.base,
(cairo_hash_entry_t **) &op))
{
*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_collect (void *entry, void *closure)
{
dict_write_info_t *write_info = closure;
cff_dict_operator_t *op = entry;
unsigned char data;
if (write_info->status)
return;
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 cairo_status_t
cff_dict_write (cairo_hash_table_t *dict, cairo_array_t *output)
{
dict_write_info_t write_info;
write_info.output = output;
write_info.status = CAIRO_STATUS_SUCCESS;
_cairo_hash_table_foreach (dict, _cairo_dict_collect, &write_info);
return write_info.status;
}
static void
cff_dict_fini (cairo_hash_table_t *dict)
{
cff_dict_operator_t *entry;
while (1) {
entry = _cairo_hash_table_random_entry (dict, NULL);
if (entry == NULL)
break;
free (entry->operand);
_cairo_hash_table_remove (dict, (cairo_hash_entry_t *) entry);
free (entry);
}
_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,
unsigned char *ptr,
int size)
{
unsigned char buf[10];
unsigned char *end_buf;
int offset;
int i;
unsigned char *operand;
unsigned char *p;
cff_dict_read (font->private_dict, ptr, size);
operand = cff_dict_get_operands (font->private_dict, LOCAL_SUB_OP, &i);
if (operand) {
decode_integer (operand, &offset);
p = ptr + offset;
cff_index_read (&font->local_sub_index, &p, font->data_end);
/* Use maximum sized encoding to reserve space for later modification. */
end_buf = encode_integer_max (buf, 0);
cff_dict_set_operands (font->private_dict, LOCAL_SUB_OP, buf, end_buf - buf);
}
return CAIRO_STATUS_SUCCESS;
}
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 (status)
goto fail;
element = _cairo_array_index (&index, 0);
cff_dict_read (font->top_dict, element->data, element->length);
/* CID fonts are NYI */
if (cff_dict_get_operands (font->top_dict, ROS_OP, &size) != NULL) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto fail;
}
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 (status)
goto fail;
font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index);
operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size);
operand = decode_integer (operand, &size);
decode_integer (operand, &offset);
cairo_cff_font_read_private_dict (font, font->data + offset, size);
operand = cff_dict_get_operands (font->top_dict, CHARSET_OP, &size);
if (!operand)
font->charset_offset = 0;
else {
decode_integer (operand, &offset);
font->charset_offset = offset;
}
/* Use maximum sized encoding to reserve space for later modification. */
end_buf = encode_integer_max (buf, 0);
cff_dict_set_operands (font->top_dict, CHARSTRINGS_OP, buf, end_buf - buf);
cff_dict_set_operands (font->top_dict, ENCODING_OP, buf, end_buf - buf);
cff_dict_set_operands (font->top_dict, CHARSET_OP, buf, end_buf - buf);
/* 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);
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)
{
return cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end);
}
static cairo_int_status_t
cff_charset_read_data (cff_charset_t *charset, const unsigned char *data,
const unsigned char *data_end, int num_glyphs)
{
const unsigned char *p = data;
num_glyphs -= 1; /* do not count .notdef */
if (p + 1 > data_end)
return CAIRO_INT_STATUS_UNSUPPORTED;
switch (*p++) {
case 0:
if (p + num_glyphs*2 > data_end)
return CAIRO_INT_STATUS_UNSUPPORTED;
charset->is_builtin = FALSE;
charset->data = data;
charset->length = num_glyphs * 2 + 1;
break;
case 1:
while (num_glyphs > 0) {
if (p + 3 > data_end)
return CAIRO_INT_STATUS_UNSUPPORTED;
num_glyphs -= p[2] + 1;
p += 3;
}
if (num_glyphs < 0)
return CAIRO_INT_STATUS_UNSUPPORTED;
charset->is_builtin = FALSE;
charset->data = data;
charset->length = p - data;
break;
case 2:
while (num_glyphs > 0) {
if (p + 4 > data_end)
return CAIRO_INT_STATUS_UNSUPPORTED;
num_glyphs -= be16_to_cpu(*(uint16_t *)(p + 2)) + 1;
p += 4;
}
if (num_glyphs < 0)
return CAIRO_INT_STATUS_UNSUPPORTED;
charset->is_builtin = FALSE;
charset->data = data;
charset->length = p - data;
break;
default:
return CAIRO_INT_STATUS_UNSUPPORTED;
}
return CAIRO_STATUS_SUCCESS;
}
static const uint16_t ISOAdobe_charset[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177,
178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
222, 223, 224, 225, 226, 227, 228,
};
static const uint16_t Expert_charset[] = {
1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13,
14, 15, 99, 239, 240, 241, 242, 243, 244, 245, 246, 247,
248, 27, 28, 249, 250, 251, 252, 253, 254, 255, 256, 257,
258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110,
267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277,
278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310,
311, 312, 313, 314, 315, 316, 317, 318, 158, 155, 163,
319, 320, 321, 322, 323, 324, 325, 326, 150, 164, 169,
327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337,
338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348,
349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359,
360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370,
371, 372, 373, 374, 375, 376, 377, 378,
};
static const uint16_t ExpertSubset_charset[] = {
1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240,
241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250,
251, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262,
263, 264, 265, 266, 109, 110, 267, 268, 269, 270, 272,
300, 301, 302, 305, 314, 315, 158, 155, 163, 320, 321,
322, 323, 324, 325, 326, 150, 164, 169, 327, 328, 329,
330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340,
341, 342, 343, 344, 345, 346,
};
static cairo_int_status_t
cairo_cff_font_read_charset (cairo_cff_font_t *font)
{
switch (font->charset_offset) {
case 0:
/* ISOAdobe charset */
font->charset.is_builtin = TRUE;
font->charset.sids = ISOAdobe_charset;
font->charset.length = sizeof (ISOAdobe_charset);
return CAIRO_STATUS_SUCCESS;
case 1:
/* Expert charset */
font->charset.is_builtin = TRUE;
font->charset.sids = Expert_charset;
font->charset.length = sizeof (Expert_charset);
return CAIRO_STATUS_SUCCESS;
case 2:
/* ExpertSubset charset */;
font->charset.is_builtin = TRUE;
font->charset.sids = ExpertSubset_charset;
font->charset.length = sizeof (ExpertSubset_charset);
return CAIRO_STATUS_SUCCESS;
default:
break;
}
return cff_charset_read_data (&font->charset, font->data + (unsigned)font->charset_offset,
font->data_end, font->num_glyphs);
}
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,
/* non-contiguous */
cairo_cff_font_read_charset,
};
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 (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 (status)
return status;
p = encode_integer (buf, sid);
cff_dict_set_operands (dict, operator, buf, p - buf);
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 (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;
status = cairo_cff_font_subset_dict_strings (font, font->top_dict);
if (status)
return status;
status = cairo_cff_font_subset_dict_strings (font, font->private_dict);
return status;
}
static cairo_status_t
cairo_cff_font_subset_charstrings (cairo_cff_font_t *font)
{
cff_index_element_t *element;
unsigned int i;
cairo_status_t status;
/* add .notdef */
element = _cairo_array_index (&font->charstrings_index, 0);
status = cff_index_append (&font->charstrings_subset_index,
element->data,
element->length);
if (status)
return status;
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
element = _cairo_array_index (&font->charstrings_index,
font->scaled_font_subset->glyphs[i]);
status = cff_index_append (&font->charstrings_subset_index,
element->data,
element->length);
if (status)
return status;
}
return CAIRO_STATUS_SUCCESS;
}
static uint16_t
cff_sid_from_gid (const cff_charset_t *charset, int gid)
{
const uint16_t *sids;
const unsigned char *p;
int prev_glyph;
if (charset->is_builtin) {
if (gid - 1 < charset->length / 2)
return charset->sids[gid - 1];
}
else {
/* no need to check sizes here, this was done during reading */
switch (charset->data[0]) {
case 0:
sids = (const uint16_t *)(charset->data + 1);
return be16_to_cpu(sids[gid - 1]);
case 1:
prev_glyph = 1;
for (p = charset->data + 1; p < charset->data + charset->length; p += 3) {
if (gid <= prev_glyph + p[2]) {
uint16_t sid = be16_to_cpu(*(const uint16_t *)p);
return sid + gid - prev_glyph;
}
prev_glyph += p[2] + 1;
}
break;
case 2:
prev_glyph = 1;
for (p = charset->data + 1; p < charset->data + charset->length; p += 4) {
uint16_t nLeft = be16_to_cpu(*(const uint16_t *)(p + 2));
if (gid <= prev_glyph + nLeft) {
uint16_t sid = be16_to_cpu(*(const uint16_t *)p);
return sid + gid - prev_glyph;
}
prev_glyph += nLeft + 1;
}
break;
default:
break;
}
}
return 0;
}
static cairo_status_t
cairo_cff_font_subset_charset (cairo_cff_font_t *font)
{
unsigned int i;
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
int gid = font->scaled_font_subset->glyphs[i];
uint16_t original_sid = cff_sid_from_gid(&font->charset, gid);
uint16_t new_sid;
cff_index_element_t *element;
cairo_status_t status;
if (original_sid >= NUM_STD_STRINGS) {
element = _cairo_array_index (&font->strings_index, original_sid - NUM_STD_STRINGS);
new_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 (status)
return status;
}
else
new_sid = original_sid;
status = _cairo_array_append(&font->charset_subset, &new_sid);
if (status)
return status;
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
cairo_cff_font_subset_font (cairo_cff_font_t *font)
{
cairo_status_t status;
/* TODO: subset subroutines */
status = cairo_cff_font_subset_strings (font);
if (status)
return status;
status = cairo_cff_font_subset_charstrings (font);
if (status)
return status;
status = cairo_cff_font_subset_charset (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_array_t index;
cairo_status_t status;
cff_index_init (&index);
status = cff_index_append_copy (&index,
(unsigned char *) font->subset_font_name,
strlen(font->subset_font_name));
if (status)
return status;
status = cff_index_write (&index, &font->output);
if (status)
return status;
cff_index_fini (&index);
return CAIRO_STATUS_SUCCESS;
}
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 (status)
return status;
buf[0] = offset_size;
status = _cairo_array_append (&font->output, buf);
if (status)
return status;
encode_index_offset (buf, offset_size, 1);
status = _cairo_array_append_multiple (&font->output, buf, offset_size);
if (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 (status)
return status;
dict_start = _cairo_array_num_elements (&font->output);
status = cff_dict_write (font->top_dict, &font->output);
if (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)
{
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[10];
cairo_cff_font_set_topdict_operator_to_cur_pos (font, ENCODING_OP);
buf[0] = 1; /* Format 1 */
buf[1] = 1; /* Number of ranges */
buf[2] = 0; /* First code in range */
/* Codes left in range excluding first */
buf[3] = font->scaled_font_subset->num_glyphs - 1;
return _cairo_array_append_multiple (&font->output, buf, 4);
}
static cairo_status_t
cairo_cff_font_write_charset (cairo_cff_font_t *font)
{
unsigned char format = 0;
unsigned int i;
cairo_status_t status;
cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP);
status = _cairo_array_append (&font->output, &format);
if (status)
return status;
for (i = 0; i < (unsigned)_cairo_array_num_elements(&font->charset_subset); i++) {
uint16_t sid = cpu_to_be16(*(uint16_t *)_cairo_array_index(&font->charset_subset, i));
status = _cairo_array_append_multiple (&font->output, &sid, sizeof(sid));
if (status)
return status;
}
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_private_dict_and_local_sub (cairo_cff_font_t *font)
{
int offset, private_dict_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 */
private_dict_offset = _cairo_array_num_elements (&font->output);
status = cff_dict_write (font->private_dict, &font->output);
if (status)
return status;
size = _cairo_array_num_elements (&font->output) - private_dict_offset;
/* private entry has two operands - size and offset */
buf_end = encode_integer_max (buf, size);
buf_end = encode_integer_max (buf_end, private_dict_offset);
offset = cff_dict_get_location (font->top_dict, PRIVATE_OP, &size);
assert (offset > 0);
p = _cairo_array_index (&font->output, offset);
memcpy (p, buf, buf_end - buf);
if (_cairo_array_num_elements (&font->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) - private_dict_offset;
buf_end = encode_integer_max (buf, offset);
offset = cff_dict_get_location (font->private_dict, LOCAL_SUB_OP, &size);
assert (offset > 0);
p = _cairo_array_index (&font->output, offset);
memcpy (p, buf, buf_end - buf);
status = cff_index_write (&font->local_sub_index, &font->output);
if (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_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_charset,
cairo_cff_font_write_charstrings,
cairo_cff_font_write_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;
for (i = 0; i < ARRAY_LENGTH (font_write_funcs); i++) {
status = font_write_funcs[i] (font);
if (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 (status)
return status;
status = cairo_cff_font_subset_font (font);
if (status)
return status;
status = cairo_cff_font_write_subset (font);
if (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 (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,
buf, &short_entry_size);
if (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 (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;
tt_name_t *name;
tt_name_record_t *record;
unsigned long size, data_length;
int i, j;
backend = scaled_font_subset->scaled_font->backend;
if (!backend->load_truetype_table)
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 (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 (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 (status)
return status;
size = 0;
status = backend->load_truetype_table (scaled_font_subset->scaled_font,
TT_TAG_hmtx, 0, NULL, &size);
if (status)
return status;
size = 0;
status = backend->load_truetype_table (scaled_font_subset->scaled_font,
TT_TAG_name, 0, NULL, &size);
if (status)
return status;
name = malloc (size);
if (name == NULL)
return CAIRO_STATUS_NO_MEMORY;
status = backend->load_truetype_table (scaled_font_subset->scaled_font,
TT_TAG_name, 0,
(unsigned char *) name, &size);
if (status)
goto fail1;
font = malloc (sizeof (cairo_cff_font_t));
if (font == NULL) {
status = CAIRO_STATUS_NO_MEMORY;
goto fail1;
}
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 (status)
goto fail2;
font->subset_font_name = strdup (subset_name);
if (font->subset_font_name == NULL) {
status = CAIRO_STATUS_NO_MEMORY;
goto fail3;
}
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);
/* Extract the font name from the name table. At present this
* just looks for the Mac platform/Roman encoded font name. It
* should be extended to use any suitable font name in the
* name table. If the mac/roman font name is not found a
* CairoFont-x-y name is created.
*/
font->font_name = NULL;
for (i = 0; i < be16_to_cpu(name->num_records); i++) {
record = &(name->records[i]);
if ((be16_to_cpu (record->platform) == 1) &&
(be16_to_cpu (record->encoding) == 0) &&
(be16_to_cpu (record->name) == 4)) {
font->font_name = malloc (be16_to_cpu(record->length) + 1);
if (font->font_name) {
strncpy(font->font_name,
((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset),
be16_to_cpu (record->length));
font->font_name[be16_to_cpu (record->length)] = 0;
}
break;
}
}
if (font->font_name == NULL) {
font->font_name = malloc (30);
if (font->font_name == NULL) {
status = CAIRO_STATUS_NO_MEMORY;
goto fail4;
}
snprintf(font->font_name, 30, "CairoFont-%u-%u",
scaled_font_subset->font_id,
scaled_font_subset->subset_id);
}
for (i = 0, j = 0; font->font_name[j]; j++) {
if (font->font_name[j] == ' ')
continue;
font->font_name[i++] = font->font_name[j];
}
font->font_name[i] = '\0';
font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int));
if (font->widths == NULL) {
status = CAIRO_STATUS_NO_MEMORY;
goto fail5;
}
cairo_cff_font_create_set_widths (font);
font->data_length = data_length;
font->data = malloc (data_length);
if (font->data == NULL) {
status = CAIRO_STATUS_NO_MEMORY;
goto fail6;
}
status = font->backend->load_truetype_table ( font->scaled_font_subset->scaled_font,
TT_TAG_CFF, 0, font->data,
&font->data_length);
if (status)
goto fail7;
font->data_end = font->data + font->data_length;
cff_dict_init (&font->top_dict);
cff_dict_init (&font->private_dict);
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);
_cairo_array_init (&font->charset_subset, sizeof(uint16_t));
free (name);
*font_return = font;
return CAIRO_STATUS_SUCCESS;
fail7:
free (font->data);
fail6:
free (font->widths);
fail5:
free (font->font_name);
fail4:
free (font->subset_font_name);
fail3:
_cairo_array_fini (&font->output);
fail2:
free (font);
fail1:
free (name);
return status;
}
static void
cairo_cff_font_destroy (cairo_cff_font_t *font)
{
free (font->widths);
free (font->font_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);
_cairo_array_fini (&font->charset_subset);
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 (status)
return status;
status = cairo_cff_font_generate (font, &data, &length);
if (status)
goto fail1;
cff_subset->base_font = strdup (font->font_name);
if (cff_subset->base_font == NULL)
goto fail1;
cff_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs);
if (cff_subset->widths == NULL)
goto fail2;
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
cff_subset->widths[i] = font->widths[i];
cff_subset->x_min = font->x_min;
cff_subset->y_min = font->y_min;
cff_subset->x_max = font->x_max;
cff_subset->y_max = font->y_max;
cff_subset->ascent = font->ascent;
cff_subset->descent = font->descent;
cff_subset->data = malloc (length);
if (cff_subset->data == NULL)
goto fail3;
memcpy (cff_subset->data, data, length);
cff_subset->data_length = length;
cairo_cff_font_destroy (font);
return CAIRO_STATUS_SUCCESS;
fail3:
free (cff_subset->widths);
fail2:
free (cff_subset->base_font);
fail1:
cairo_cff_font_destroy (font);
return status;
}
void
_cairo_cff_subset_fini (cairo_cff_subset_t *subset)
{
free (subset->base_font);
free (subset->widths);
free (subset->data);
}