Skip to content
Permalink
b8a7f8621a
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
2267 lines (1912 sloc) 66 KB
/* 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/devnet/font/pdfs/5176.CFF.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
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;
/* 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;
/* Subsetted Font Data */
char *subset_font_name;
cairo_array_t charstrings_subset_index;
cairo_array_t strings_subset_index;
int *fdselect_subset;
unsigned int num_subset_fontdicts;
int *fd_subset_map;
int *private_dict_offset;
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 >= -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);
status = _cairo_array_append_multiple (output,
element->data,
element->length);
if (unlikely (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,
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;
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 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,
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;
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;
}
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;
}
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->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->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,
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;
status = cff_dict_set_operands (font->top_dict,
CHARSET_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)
{
return cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end);
}
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 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;
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 (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;
}
static cairo_status_t
cairo_cff_font_subset_font (cairo_cff_font_t *font)
{
cairo_status_t status;
status = cairo_cff_font_set_ros_strings (font);
if (unlikely (status))
return status;
status = cairo_cff_font_subset_charstrings (font);
if (unlikely (status))
return status;
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;
status = cairo_cff_font_subset_strings (font);
if (unlikely (status))
return status;
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)
{
return cff_index_write (&font->global_sub_index, &font->output);
}
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;
}
static cairo_status_t
cairo_cff_font_write_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)
{
int offset;
int size;
unsigned char buf[10];
unsigned char *buf_end;
unsigned char *p;
cairo_status_t status;
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);
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]]);
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);
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_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_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 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 (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;
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->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->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;
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->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->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 (int), 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] = 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 (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);
}
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->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;
unsigned int i;
/* 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;
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;
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;
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;
/* 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;
}
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 (int), 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] = type2_subset.widths[i];
cff_subset->x_min = type2_subset.x_min;
cff_subset->y_min = type2_subset.y_min;
cff_subset->x_max = type2_subset.x_max;
cff_subset->y_max = type2_subset.y_max;
cff_subset->ascent = type2_subset.y_max;
cff_subset->descent = type2_subset.y_min;
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;
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 */