forked from KolibriOS/kolibrios
2272 lines
66 KiB
C
2272 lines
66 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., 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;
|
||
|
int units_per_em;
|
||
|
|
||
|
/* 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->units_per_em = (int16_t) be16_to_cpu (head.units_per_em);
|
||
|
if (font->units_per_em == 0)
|
||
|
font->units_per_em = 1000;
|
||
|
|
||
|
font->font_name = NULL;
|
||
|
status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font,
|
||
|
&font->ps_name,
|
||
|
&font->font_name);
|
||
|
if (_cairo_status_is_error (status))
|
||
|
goto fail3;
|
||
|
|
||
|
/* If the PS name is not found, create a CairoFont-x-y name. */
|
||
|
if (font->ps_name == NULL) {
|
||
|
font->ps_name = malloc (30);
|
||
|
if (unlikely (font->ps_name == NULL)) {
|
||
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||
|
goto fail3;
|
||
|
}
|
||
|
|
||
|
snprintf(font->ps_name, 30, "CairoFont-%u-%u",
|
||
|
scaled_font_subset->font_id,
|
||
|
scaled_font_subset->subset_id);
|
||
|
}
|
||
|
|
||
|
font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int));
|
||
|
if (unlikely (font->widths == NULL)) {
|
||
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||
|
goto fail4;
|
||
|
}
|
||
|
|
||
|
status = cairo_cff_font_create_set_widths (font);
|
||
|
if (unlikely (status))
|
||
|
goto fail5;
|
||
|
|
||
|
font->data_length = data_length;
|
||
|
font->data = malloc (data_length);
|
||
|
if (unlikely (font->data == NULL)) {
|
||
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||
|
goto fail5;
|
||
|
}
|
||
|
status = font->backend->load_truetype_table ( font->scaled_font_subset->scaled_font,
|
||
|
TT_TAG_CFF, 0, font->data,
|
||
|
&font->data_length);
|
||
|
if (unlikely (status))
|
||
|
goto fail6;
|
||
|
|
||
|
font->data_end = font->data + font->data_length;
|
||
|
|
||
|
status = cff_dict_init (&font->top_dict);
|
||
|
if (unlikely (status))
|
||
|
goto fail6;
|
||
|
|
||
|
status = cff_dict_init (&font->private_dict);
|
||
|
if (unlikely (status))
|
||
|
goto fail7;
|
||
|
|
||
|
cff_index_init (&font->strings_index);
|
||
|
cff_index_init (&font->charstrings_index);
|
||
|
cff_index_init (&font->global_sub_index);
|
||
|
cff_index_init (&font->local_sub_index);
|
||
|
cff_index_init (&font->charstrings_subset_index);
|
||
|
cff_index_init (&font->strings_subset_index);
|
||
|
font->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 (double), font->scaled_font_subset->num_glyphs);
|
||
|
if (unlikely (cff_subset->widths == NULL)) {
|
||
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||
|
goto fail3;
|
||
|
}
|
||
|
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
|
||
|
cff_subset->widths[i] = (double)font->widths[i]/font->units_per_em;
|
||
|
|
||
|
cff_subset->x_min = (double)font->x_min/font->units_per_em;
|
||
|
cff_subset->y_min = (double)font->y_min/font->units_per_em;
|
||
|
cff_subset->x_max = (double)font->x_max/font->units_per_em;
|
||
|
cff_subset->y_max = (double)font->y_max/font->units_per_em;
|
||
|
cff_subset->ascent = (double)font->ascent/font->units_per_em;
|
||
|
cff_subset->descent = (double)font->descent/font->units_per_em;
|
||
|
|
||
|
cff_subset->data = malloc (length);
|
||
|
if (unlikely (cff_subset->data == NULL)) {
|
||
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||
|
goto fail4;
|
||
|
}
|
||
|
|
||
|
memcpy (cff_subset->data, data, length);
|
||
|
cff_subset->data_length = length;
|
||
|
|
||
|
cairo_cff_font_destroy (font);
|
||
|
|
||
|
return CAIRO_STATUS_SUCCESS;
|
||
|
|
||
|
fail4:
|
||
|
free (cff_subset->widths);
|
||
|
fail3:
|
||
|
if (cff_subset->font_name)
|
||
|
free (cff_subset->font_name);
|
||
|
fail2:
|
||
|
free (cff_subset->ps_name);
|
||
|
fail1:
|
||
|
cairo_cff_font_destroy (font);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
_cairo_cff_subset_fini (cairo_cff_subset_t *subset)
|
||
|
{
|
||
|
free (subset->ps_name);
|
||
|
if (subset->font_name)
|
||
|
free (subset->font_name);
|
||
|
free (subset->widths);
|
||
|
free (subset->data);
|
||
|
}
|
||
|
|
||
|
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 (double), font->scaled_font_subset->num_glyphs);
|
||
|
if (unlikely (cff_subset->widths == NULL)) {
|
||
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||
|
goto fail3;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
|
||
|
cff_subset->widths[i] = (double)type2_subset.widths[i]/1000;
|
||
|
|
||
|
cff_subset->x_min = (double)type2_subset.x_min/1000;
|
||
|
cff_subset->y_min = (double)type2_subset.y_min/1000;
|
||
|
cff_subset->x_max = (double)type2_subset.x_max/1000;
|
||
|
cff_subset->y_max = (double)type2_subset.y_max/1000;
|
||
|
cff_subset->ascent = (double)type2_subset.y_max/1000;
|
||
|
cff_subset->descent = (double)type2_subset.y_min/1000;
|
||
|
|
||
|
cff_subset->data = malloc (length);
|
||
|
if (unlikely (cff_subset->data == NULL)) {
|
||
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||
|
goto fail4;
|
||
|
}
|
||
|
|
||
|
memcpy (cff_subset->data, data, length);
|
||
|
cff_subset->data_length = length;
|
||
|
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 */
|