mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-01 20:48:06 +02:00
Go through each Charstring looking for the local and global subroutines called. To avoid modifying the Charstrings [1], the unused subroutines are reduced to a single byte return op [2] leaving the remaining subroutines in their original array index position. Results of testing with some CFF fonts with a 26 glyph [a-z] subset: Font Subset size: Before After ------------------------------------------------------- LinBiolinum_Re-0.6.4.otf 48,423 8,295 LinBiolinum_It-0.5.1.otf 88,942 11,501 LinBiolinum_Sl-0.4.9.otf 89,231 11,505 LinLibertine_Re-4.7.5.otf 51,125 8,654 LinLibetine_It-4.2.6.otf 59,333 9,632 Inconsolata.otf 13,826 8,407 [1] Further reductions could be obtained by stripping out unused subroutines and renumbering the remaining subroutines but is more complicated due to the encoding used for subroutine numbers that is both variable length and a function of the size of the subroutine array. [2] Poppler and Fontforge do not seem to like zero length unused subroutines.
2927 lines
87 KiB
C
2927 lines
87 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/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
|
|
* http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf
|
|
*/
|
|
|
|
#define _BSD_SOURCE /* for snprintf(), strdup() */
|
|
#include "cairoint.h"
|
|
#include "cairo-error-private.h"
|
|
|
|
#if CAIRO_HAS_FONT_SUBSET
|
|
|
|
#include "cairo-scaled-font-subsets-private.h"
|
|
#include "cairo-truetype-subset-private.h"
|
|
#include <string.h>
|
|
|
|
/* CFF Dict Operators. If the high byte is 0 the command is encoded
|
|
* with a single byte. */
|
|
#define BASEFONTNAME_OP 0x0c16
|
|
#define CIDCOUNT_OP 0x0c22
|
|
#define CHARSET_OP 0x000f
|
|
#define CHARSTRINGS_OP 0x0011
|
|
#define COPYRIGHT_OP 0x0c00
|
|
#define ENCODING_OP 0x0010
|
|
#define FAMILYNAME_OP 0x0003
|
|
#define FDARRAY_OP 0x0c24
|
|
#define FDSELECT_OP 0x0c25
|
|
#define FONTBBOX_OP 0x0005
|
|
#define FONTNAME_OP 0x0c26
|
|
#define FULLNAME_OP 0x0002
|
|
#define LOCAL_SUB_OP 0x0013
|
|
#define NOTICE_OP 0x0001
|
|
#define POSTSCRIPT_OP 0x0c15
|
|
#define PRIVATE_OP 0x0012
|
|
#define ROS_OP 0x0c1e
|
|
#define UNIQUEID_OP 0x000d
|
|
#define VERSION_OP 0x0000
|
|
#define WEIGHT_OP 0x0004
|
|
#define XUID_OP 0x000e
|
|
|
|
#define NUM_STD_STRINGS 391
|
|
|
|
/* Type 2 Charstring operators */
|
|
#define TYPE2_hstem 0x0001
|
|
#define TYPE2_vstem 0x0003
|
|
#define TYPE2_callsubr 0x000a
|
|
|
|
#define TYPE2_return 0x000b
|
|
#define TYPE2_endchar 0x000e
|
|
|
|
#define TYPE2_hstemhm 0x0012
|
|
#define TYPE2_hintmask 0x0013
|
|
#define TYPE2_cntrmask 0x0014
|
|
#define TYPE2_vstemhm 0x0017
|
|
#define TYPE2_callgsubr 0x001d
|
|
|
|
#define MAX_SUBROUTINE_NESTING 10 /* From Type2 Charstring spec */
|
|
|
|
|
|
typedef struct _cff_header {
|
|
uint8_t major;
|
|
uint8_t minor;
|
|
uint8_t header_size;
|
|
uint8_t offset_size;
|
|
} cff_header_t;
|
|
|
|
typedef struct _cff_index_element {
|
|
cairo_bool_t is_copy;
|
|
unsigned char *data;
|
|
int length;
|
|
} cff_index_element_t;
|
|
|
|
typedef struct _cff_dict_operator {
|
|
cairo_hash_entry_t base;
|
|
|
|
unsigned short operator;
|
|
unsigned char *operand;
|
|
int operand_length;
|
|
int operand_offset;
|
|
} cff_dict_operator_t;
|
|
|
|
typedef struct _cairo_cff_font {
|
|
|
|
cairo_scaled_font_subset_t *scaled_font_subset;
|
|
const cairo_scaled_font_backend_t *backend;
|
|
|
|
/* Font Data */
|
|
unsigned char *data;
|
|
unsigned long data_length;
|
|
unsigned char *current_ptr;
|
|
unsigned char *data_end;
|
|
cff_header_t *header;
|
|
char *font_name;
|
|
char *ps_name;
|
|
cairo_hash_table_t *top_dict;
|
|
cairo_hash_table_t *private_dict;
|
|
cairo_array_t strings_index;
|
|
cairo_array_t charstrings_index;
|
|
cairo_array_t global_sub_index;
|
|
cairo_array_t local_sub_index;
|
|
int num_glyphs;
|
|
cairo_bool_t is_cid;
|
|
int units_per_em;
|
|
int global_sub_bias;
|
|
int local_sub_bias;
|
|
|
|
/* CID Font Data */
|
|
int *fdselect;
|
|
unsigned int num_fontdicts;
|
|
cairo_hash_table_t **fd_dict;
|
|
cairo_hash_table_t **fd_private_dict;
|
|
cairo_array_t *fd_local_sub_index;
|
|
int *fd_local_sub_bias;
|
|
|
|
/* Subsetted Font Data */
|
|
char *subset_font_name;
|
|
cairo_array_t charstrings_subset_index;
|
|
cairo_array_t strings_subset_index;
|
|
int euro_sid;
|
|
int *fdselect_subset;
|
|
unsigned int num_subset_fontdicts;
|
|
int *fd_subset_map;
|
|
int *private_dict_offset;
|
|
cairo_bool_t subset_subroutines;
|
|
cairo_bool_t *global_subs_used;
|
|
cairo_bool_t *local_subs_used;
|
|
cairo_bool_t **fd_local_subs_used;
|
|
cairo_array_t output;
|
|
|
|
/* Subset Metrics */
|
|
int *widths;
|
|
int x_min, y_min, x_max, y_max;
|
|
int ascent, descent;
|
|
|
|
/* Type 2 charstring data */
|
|
int type2_stack_size;
|
|
int type2_stack_top_value;
|
|
cairo_bool_t type2_stack_top_is_int;
|
|
int type2_num_hints;
|
|
int type2_hintmask_bytes;
|
|
int type2_nesting_level;
|
|
|
|
} cairo_cff_font_t;
|
|
|
|
/* Encoded integer using maximum sized encoding. This is required for
|
|
* operands that are later modified after encoding. */
|
|
static unsigned char *
|
|
encode_integer_max (unsigned char *p, int i)
|
|
{
|
|
*p++ = 29;
|
|
*p++ = i >> 24;
|
|
*p++ = (i >> 16) & 0xff;
|
|
*p++ = (i >> 8) & 0xff;
|
|
*p++ = i & 0xff;
|
|
return p;
|
|
}
|
|
|
|
static unsigned char *
|
|
encode_integer (unsigned char *p, int i)
|
|
{
|
|
if (i >= -107 && i <= 107) {
|
|
*p++ = i + 139;
|
|
} else if (i >= 108 && i <= 1131) {
|
|
i -= 108;
|
|
*p++ = (i >> 8)+ 247;
|
|
*p++ = i & 0xff;
|
|
} else if (i >= -1131 && i <= -108) {
|
|
i = -i - 108;
|
|
*p++ = (i >> 8)+ 251;
|
|
*p++ = i & 0xff;
|
|
} else if (i >= -32768 && i <= 32767) {
|
|
*p++ = 28;
|
|
*p++ = (i >> 8) & 0xff;
|
|
*p++ = i & 0xff;
|
|
} else {
|
|
p = encode_integer_max (p, i);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static unsigned char *
|
|
decode_integer (unsigned char *p, int *integer)
|
|
{
|
|
if (*p == 28) {
|
|
*integer = (int)(p[1]<<8 | p[2]);
|
|
p += 3;
|
|
} else if (*p == 29) {
|
|
*integer = (int)((p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]);
|
|
p += 5;
|
|
} else if (*p >= 32 && *p <= 246) {
|
|
*integer = *p++ - 139;
|
|
} else if (*p <= 250) {
|
|
*integer = (p[0] - 247) * 256 + p[1] + 108;
|
|
p += 2;
|
|
} else if (*p <= 254) {
|
|
*integer = -(p[0] - 251) * 256 - p[1] - 108;
|
|
p += 2;
|
|
} else {
|
|
*integer = 0;
|
|
p += 1;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static unsigned char *
|
|
decode_operator (unsigned char *p, unsigned short *operator)
|
|
{
|
|
unsigned short op = 0;
|
|
|
|
op = *p++;
|
|
if (op == 12) {
|
|
op <<= 8;
|
|
op |= *p++;
|
|
}
|
|
*operator = op;
|
|
return p;
|
|
}
|
|
|
|
/* return 0 if not an operand */
|
|
static int
|
|
operand_length (unsigned char *p)
|
|
{
|
|
unsigned char *begin = p;
|
|
|
|
if (*p == 28)
|
|
return 3;
|
|
|
|
if (*p == 29)
|
|
return 5;
|
|
|
|
if (*p >= 32 && *p <= 246)
|
|
return 1;
|
|
|
|
if (*p >= 247 && *p <= 254)
|
|
return 2;
|
|
|
|
if (*p == 30) {
|
|
while ((*p & 0x0f) != 0x0f)
|
|
p++;
|
|
return p - begin + 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned char *
|
|
encode_index_offset (unsigned char *p, int offset_size, unsigned long offset)
|
|
{
|
|
while (--offset_size >= 0) {
|
|
p[offset_size] = (unsigned char) (offset & 0xff);
|
|
offset >>= 8;
|
|
}
|
|
return p + offset_size;
|
|
}
|
|
|
|
static unsigned long
|
|
decode_index_offset(unsigned char *p, int off_size)
|
|
{
|
|
unsigned long offset = 0;
|
|
|
|
while (off_size-- > 0)
|
|
offset = offset*256 + *p++;
|
|
return offset;
|
|
}
|
|
|
|
static void
|
|
cff_index_init (cairo_array_t *index)
|
|
{
|
|
_cairo_array_init (index, sizeof (cff_index_element_t));
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_ptr)
|
|
{
|
|
cff_index_element_t element;
|
|
unsigned char *data, *p;
|
|
cairo_status_t status;
|
|
int offset_size, count, start, i;
|
|
int end = 0;
|
|
|
|
p = *ptr;
|
|
if (p + 2 > end_ptr)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
count = be16_to_cpu( *((uint16_t *)p) );
|
|
p += 2;
|
|
if (count > 0) {
|
|
offset_size = *p++;
|
|
if (p + (count + 1)*offset_size > end_ptr)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
data = p + offset_size*(count + 1) - 1;
|
|
start = decode_index_offset (p, offset_size);
|
|
p += offset_size;
|
|
for (i = 0; i < count; i++) {
|
|
end = decode_index_offset (p, offset_size);
|
|
p += offset_size;
|
|
if (p > end_ptr)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
element.length = end - start;
|
|
element.is_copy = FALSE;
|
|
element.data = data + start;
|
|
status = _cairo_array_append (index, &element);
|
|
if (unlikely (status))
|
|
return status;
|
|
start = end;
|
|
}
|
|
p = data + end;
|
|
}
|
|
*ptr = p;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cff_index_write (cairo_array_t *index, cairo_array_t *output)
|
|
{
|
|
int offset_size;
|
|
int offset;
|
|
int num_elem;
|
|
int i;
|
|
cff_index_element_t *element;
|
|
uint16_t count;
|
|
unsigned char buf[5];
|
|
cairo_status_t status;
|
|
|
|
num_elem = _cairo_array_num_elements (index);
|
|
count = cpu_to_be16 ((uint16_t) num_elem);
|
|
status = _cairo_array_append_multiple (output, &count, 2);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (num_elem == 0)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
/* Find maximum offset to determine offset size */
|
|
offset = 1;
|
|
for (i = 0; i < num_elem; i++) {
|
|
element = _cairo_array_index (index, i);
|
|
offset += element->length;
|
|
}
|
|
if (offset < 0x100)
|
|
offset_size = 1;
|
|
else if (offset < 0x10000)
|
|
offset_size = 2;
|
|
else if (offset < 0x1000000)
|
|
offset_size = 3;
|
|
else
|
|
offset_size = 4;
|
|
|
|
buf[0] = (unsigned char) offset_size;
|
|
status = _cairo_array_append (output, buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
offset = 1;
|
|
encode_index_offset (buf, offset_size, offset);
|
|
status = _cairo_array_append_multiple (output, buf, offset_size);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
for (i = 0; i < num_elem; i++) {
|
|
element = _cairo_array_index (index, i);
|
|
offset += element->length;
|
|
encode_index_offset (buf, offset_size, offset);
|
|
status = _cairo_array_append_multiple (output, buf, offset_size);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
for (i = 0; i < num_elem; i++) {
|
|
element = _cairo_array_index (index, i);
|
|
if (element->length > 0) {
|
|
status = _cairo_array_append_multiple (output,
|
|
element->data,
|
|
element->length);
|
|
}
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
cff_index_set_object (cairo_array_t *index, int obj_index,
|
|
unsigned char *object , int length)
|
|
{
|
|
cff_index_element_t *element;
|
|
|
|
element = _cairo_array_index (index, obj_index);
|
|
if (element->is_copy)
|
|
free (element->data);
|
|
|
|
element->data = object;
|
|
element->length = length;
|
|
element->is_copy = FALSE;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cff_index_append (cairo_array_t *index, unsigned char *object , int length)
|
|
{
|
|
cff_index_element_t element;
|
|
|
|
element.length = length;
|
|
element.is_copy = FALSE;
|
|
element.data = object;
|
|
|
|
return _cairo_array_append (index, &element);
|
|
}
|
|
|
|
static cairo_status_t
|
|
cff_index_append_copy (cairo_array_t *index,
|
|
const unsigned char *object,
|
|
unsigned int length)
|
|
{
|
|
cff_index_element_t element;
|
|
cairo_status_t status;
|
|
|
|
element.length = length;
|
|
element.is_copy = TRUE;
|
|
element.data = malloc (element.length);
|
|
if (unlikely (element.data == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
memcpy (element.data, object, element.length);
|
|
|
|
status = _cairo_array_append (index, &element);
|
|
if (unlikely (status)) {
|
|
free (element.data);
|
|
return status;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
cff_index_fini (cairo_array_t *index)
|
|
{
|
|
cff_index_element_t *element;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < _cairo_array_num_elements (index); i++) {
|
|
element = _cairo_array_index (index, i);
|
|
if (element->is_copy && element->data)
|
|
free (element->data);
|
|
}
|
|
_cairo_array_fini (index);
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_cairo_cff_dict_equal (const void *key_a, const void *key_b)
|
|
{
|
|
const cff_dict_operator_t *op_a = key_a;
|
|
const cff_dict_operator_t *op_b = key_b;
|
|
|
|
return op_a->operator == op_b->operator;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cff_dict_init (cairo_hash_table_t **dict)
|
|
{
|
|
*dict = _cairo_hash_table_create (_cairo_cff_dict_equal);
|
|
if (unlikely (*dict == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_dict_init_key (cff_dict_operator_t *key, int operator)
|
|
{
|
|
key->base.hash = (unsigned long) operator;
|
|
key->operator = operator;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cff_dict_create_operator (int operator,
|
|
unsigned char *operand,
|
|
int size,
|
|
cff_dict_operator_t **out)
|
|
{
|
|
cff_dict_operator_t *op;
|
|
|
|
op = malloc (sizeof (cff_dict_operator_t));
|
|
if (unlikely (op == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
_cairo_dict_init_key (op, operator);
|
|
op->operand = malloc (size);
|
|
if (unlikely (op->operand == NULL)) {
|
|
free (op);
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
memcpy (op->operand, operand, size);
|
|
op->operand_length = size;
|
|
op->operand_offset = -1;
|
|
|
|
*out = op;
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cff_dict_read (cairo_hash_table_t *dict, unsigned char *p, int dict_size)
|
|
{
|
|
unsigned char *end;
|
|
cairo_array_t operands;
|
|
cff_dict_operator_t *op;
|
|
unsigned short operator;
|
|
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
|
int size;
|
|
|
|
end = p + dict_size;
|
|
_cairo_array_init (&operands, 1);
|
|
while (p < end) {
|
|
size = operand_length (p);
|
|
if (size != 0) {
|
|
status = _cairo_array_append_multiple (&operands, p, size);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
p += size;
|
|
} else {
|
|
p = decode_operator (p, &operator);
|
|
status = cff_dict_create_operator (operator,
|
|
_cairo_array_index (&operands, 0),
|
|
_cairo_array_num_elements (&operands),
|
|
&op);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
status = _cairo_hash_table_insert (dict, &op->base);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
_cairo_array_truncate (&operands, 0);
|
|
}
|
|
}
|
|
|
|
fail:
|
|
_cairo_array_fini (&operands);
|
|
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
cff_dict_remove (cairo_hash_table_t *dict, unsigned short operator)
|
|
{
|
|
cff_dict_operator_t key, *op;
|
|
|
|
_cairo_dict_init_key (&key, operator);
|
|
op = _cairo_hash_table_lookup (dict, &key.base);
|
|
if (op != NULL) {
|
|
free (op->operand);
|
|
_cairo_hash_table_remove (dict, (cairo_hash_entry_t *) op);
|
|
free (op);
|
|
}
|
|
}
|
|
|
|
static unsigned char *
|
|
cff_dict_get_operands (cairo_hash_table_t *dict,
|
|
unsigned short operator,
|
|
int *size)
|
|
{
|
|
cff_dict_operator_t key, *op;
|
|
|
|
_cairo_dict_init_key (&key, operator);
|
|
op = _cairo_hash_table_lookup (dict, &key.base);
|
|
if (op != NULL) {
|
|
*size = op->operand_length;
|
|
return op->operand;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cff_dict_set_operands (cairo_hash_table_t *dict,
|
|
unsigned short operator,
|
|
unsigned char *operand,
|
|
int size)
|
|
{
|
|
cff_dict_operator_t key, *op;
|
|
cairo_status_t status;
|
|
|
|
_cairo_dict_init_key (&key, operator);
|
|
op = _cairo_hash_table_lookup (dict, &key.base);
|
|
if (op != NULL) {
|
|
free (op->operand);
|
|
op->operand = malloc (size);
|
|
if (unlikely (op->operand == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
memcpy (op->operand, operand, size);
|
|
op->operand_length = size;
|
|
}
|
|
else
|
|
{
|
|
status = cff_dict_create_operator (operator, operand, size, &op);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_hash_table_insert (dict, &op->base);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static int
|
|
cff_dict_get_location (cairo_hash_table_t *dict,
|
|
unsigned short operator,
|
|
int *size)
|
|
{
|
|
cff_dict_operator_t key, *op;
|
|
|
|
_cairo_dict_init_key (&key, operator);
|
|
op = _cairo_hash_table_lookup (dict, &key.base);
|
|
if (op != NULL) {
|
|
*size = op->operand_length;
|
|
return op->operand_offset;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
typedef struct _dict_write_info {
|
|
cairo_array_t *output;
|
|
cairo_status_t status;
|
|
} dict_write_info_t;
|
|
|
|
static void
|
|
cairo_dict_write_operator (cff_dict_operator_t *op, dict_write_info_t *write_info)
|
|
{
|
|
unsigned char data;
|
|
|
|
op->operand_offset = _cairo_array_num_elements (write_info->output);
|
|
write_info->status = _cairo_array_append_multiple (write_info->output, op->operand, op->operand_length);
|
|
if (write_info->status)
|
|
return;
|
|
|
|
if (op->operator & 0xff00) {
|
|
data = op->operator >> 8;
|
|
write_info->status = _cairo_array_append (write_info->output, &data);
|
|
if (write_info->status)
|
|
return;
|
|
}
|
|
data = op->operator & 0xff;
|
|
write_info->status = _cairo_array_append (write_info->output, &data);
|
|
}
|
|
|
|
static void
|
|
_cairo_dict_collect (void *entry, void *closure)
|
|
{
|
|
dict_write_info_t *write_info = closure;
|
|
cff_dict_operator_t *op = entry;
|
|
|
|
if (write_info->status)
|
|
return;
|
|
|
|
/* The ROS operator is handled separately in cff_dict_write() */
|
|
if (op->operator != ROS_OP)
|
|
cairo_dict_write_operator (op, write_info);
|
|
}
|
|
|
|
static cairo_status_t
|
|
cff_dict_write (cairo_hash_table_t *dict, cairo_array_t *output)
|
|
{
|
|
dict_write_info_t write_info;
|
|
cff_dict_operator_t key, *op;
|
|
|
|
write_info.output = output;
|
|
write_info.status = CAIRO_STATUS_SUCCESS;
|
|
|
|
/* The CFF specification requires that the Top Dict of CID fonts
|
|
* begin with the ROS operator. */
|
|
_cairo_dict_init_key (&key, ROS_OP);
|
|
op = _cairo_hash_table_lookup (dict, &key.base);
|
|
if (op != NULL)
|
|
cairo_dict_write_operator (op, &write_info);
|
|
|
|
_cairo_hash_table_foreach (dict, _cairo_dict_collect, &write_info);
|
|
|
|
return write_info.status;
|
|
}
|
|
|
|
static void
|
|
_cff_dict_entry_pluck (void *_entry, void *dict)
|
|
{
|
|
cff_dict_operator_t *entry = _entry;
|
|
|
|
_cairo_hash_table_remove (dict, &entry->base);
|
|
free (entry->operand);
|
|
free (entry);
|
|
}
|
|
|
|
static void
|
|
cff_dict_fini (cairo_hash_table_t *dict)
|
|
{
|
|
_cairo_hash_table_foreach (dict, _cff_dict_entry_pluck, dict);
|
|
_cairo_hash_table_destroy (dict);
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
cairo_cff_font_read_header (cairo_cff_font_t *font)
|
|
{
|
|
if (font->data_length < sizeof (cff_header_t))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
font->header = (cff_header_t *) font->data;
|
|
font->current_ptr = font->data + font->header->header_size;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
cairo_cff_font_read_name (cairo_cff_font_t *font)
|
|
{
|
|
cairo_array_t index;
|
|
cairo_int_status_t status;
|
|
|
|
/* The original font name is not used in the subset. Read the name
|
|
* index to skip over it. */
|
|
cff_index_init (&index);
|
|
status = cff_index_read (&index, &font->current_ptr, font->data_end);
|
|
cff_index_fini (&index);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
cairo_cff_font_read_private_dict (cairo_cff_font_t *font,
|
|
cairo_hash_table_t *private_dict,
|
|
cairo_array_t *local_sub_index,
|
|
int *local_sub_bias,
|
|
cairo_bool_t **local_subs_used,
|
|
unsigned char *ptr,
|
|
int size)
|
|
{
|
|
cairo_int_status_t status;
|
|
unsigned char buf[10];
|
|
unsigned char *end_buf;
|
|
int offset;
|
|
int i;
|
|
unsigned char *operand;
|
|
unsigned char *p;
|
|
int num_subs;
|
|
|
|
status = cff_dict_read (private_dict, ptr, size);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
operand = cff_dict_get_operands (private_dict, LOCAL_SUB_OP, &i);
|
|
if (operand) {
|
|
decode_integer (operand, &offset);
|
|
p = ptr + offset;
|
|
status = cff_index_read (local_sub_index, &p, font->data_end);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
/* Use maximum sized encoding to reserve space for later modification. */
|
|
end_buf = encode_integer_max (buf, 0);
|
|
status = cff_dict_set_operands (private_dict, LOCAL_SUB_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
}
|
|
|
|
num_subs = _cairo_array_num_elements (local_sub_index);
|
|
*local_subs_used = calloc (num_subs, sizeof (cairo_bool_t));
|
|
if (unlikely (*local_subs_used == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
if (num_subs < 1240)
|
|
*local_sub_bias = 107;
|
|
else if (num_subs < 33900)
|
|
*local_sub_bias = 1131;
|
|
else
|
|
*local_sub_bias = 32768;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
cairo_cff_font_read_fdselect (cairo_cff_font_t *font, unsigned char *p)
|
|
{
|
|
int type, num_ranges, first, last, fd, i, j;
|
|
|
|
font->fdselect = calloc (font->num_glyphs, sizeof (int));
|
|
if (unlikely (font->fdselect == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
type = *p++;
|
|
if (type == 0)
|
|
{
|
|
for (i = 0; i < font->num_glyphs; i++)
|
|
font->fdselect[i] = *p++;
|
|
} else if (type == 3) {
|
|
num_ranges = be16_to_cpu( *((uint16_t *)p) );
|
|
p += 2;
|
|
for (i = 0; i < num_ranges; i++)
|
|
{
|
|
first = be16_to_cpu( *((uint16_t *)p) );
|
|
p += 2;
|
|
fd = *p++;
|
|
last = be16_to_cpu( *((uint16_t *)p) );
|
|
for (j = first; j < last; j++)
|
|
font->fdselect[j] = fd;
|
|
}
|
|
} else {
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr)
|
|
{
|
|
cairo_array_t index;
|
|
cff_index_element_t *element;
|
|
unsigned int i;
|
|
int size;
|
|
unsigned char *operand;
|
|
int offset;
|
|
cairo_int_status_t status;
|
|
unsigned char buf[100];
|
|
unsigned char *end_buf;
|
|
|
|
cff_index_init (&index);
|
|
status = cff_index_read (&index, &ptr, font->data_end);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
font->num_fontdicts = _cairo_array_num_elements (&index);
|
|
|
|
font->fd_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts);
|
|
if (unlikely (font->fd_dict == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail;
|
|
}
|
|
|
|
font->fd_private_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts);
|
|
if (unlikely (font->fd_private_dict == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail;
|
|
}
|
|
|
|
font->fd_local_sub_index = calloc (sizeof (cairo_array_t), font->num_fontdicts);
|
|
if (unlikely (font->fd_local_sub_index == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail;
|
|
}
|
|
|
|
font->fd_local_sub_bias = calloc (sizeof (int), font->num_fontdicts);
|
|
if (unlikely (font->fd_local_sub_bias == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail;
|
|
}
|
|
|
|
font->fd_local_subs_used = calloc (sizeof (cairo_bool_t *), font->num_fontdicts);
|
|
if (unlikely (font->fd_local_subs_used == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail;
|
|
}
|
|
|
|
for (i = 0; i < font->num_fontdicts; i++) {
|
|
status = cff_dict_init (&font->fd_dict[i]);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
element = _cairo_array_index (&index, i);
|
|
status = cff_dict_read (font->fd_dict[i], element->data, element->length);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
operand = cff_dict_get_operands (font->fd_dict[i], PRIVATE_OP, &size);
|
|
if (operand == NULL) {
|
|
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
|
goto fail;
|
|
}
|
|
operand = decode_integer (operand, &size);
|
|
decode_integer (operand, &offset);
|
|
status = cff_dict_init (&font->fd_private_dict[i]);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
cff_index_init (&font->fd_local_sub_index[i]);
|
|
status = cairo_cff_font_read_private_dict (font,
|
|
font->fd_private_dict[i],
|
|
&font->fd_local_sub_index[i],
|
|
&font->fd_local_sub_bias[i],
|
|
&font->fd_local_subs_used[i],
|
|
font->data + offset,
|
|
size);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
/* Set integer operand to max value to use max size encoding to reserve
|
|
* space for any value later */
|
|
end_buf = encode_integer_max (buf, 0);
|
|
end_buf = encode_integer_max (end_buf, 0);
|
|
status = cff_dict_set_operands (font->fd_dict[i], PRIVATE_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
fail:
|
|
cff_index_fini (&index);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
cairo_cff_font_read_top_dict (cairo_cff_font_t *font)
|
|
{
|
|
cairo_array_t index;
|
|
cff_index_element_t *element;
|
|
unsigned char buf[20];
|
|
unsigned char *end_buf;
|
|
unsigned char *operand;
|
|
cairo_int_status_t status;
|
|
unsigned char *p;
|
|
int size;
|
|
int offset;
|
|
|
|
cff_index_init (&index);
|
|
status = cff_index_read (&index, &font->current_ptr, font->data_end);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
element = _cairo_array_index (&index, 0);
|
|
status = cff_dict_read (font->top_dict, element->data, element->length);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
if (cff_dict_get_operands (font->top_dict, ROS_OP, &size) != NULL)
|
|
font->is_cid = TRUE;
|
|
else
|
|
font->is_cid = FALSE;
|
|
|
|
operand = cff_dict_get_operands (font->top_dict, CHARSTRINGS_OP, &size);
|
|
decode_integer (operand, &offset);
|
|
p = font->data + offset;
|
|
status = cff_index_read (&font->charstrings_index, &p, font->data_end);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index);
|
|
|
|
if (font->is_cid) {
|
|
operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size);
|
|
decode_integer (operand, &offset);
|
|
status = cairo_cff_font_read_fdselect (font, font->data + offset);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
operand = cff_dict_get_operands (font->top_dict, FDARRAY_OP, &size);
|
|
decode_integer (operand, &offset);
|
|
status = cairo_cff_font_read_cid_fontdict (font, font->data + offset);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
} else {
|
|
operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size);
|
|
operand = decode_integer (operand, &size);
|
|
decode_integer (operand, &offset);
|
|
status = cairo_cff_font_read_private_dict (font,
|
|
font->private_dict,
|
|
&font->local_sub_index,
|
|
&font->local_sub_bias,
|
|
&font->local_subs_used,
|
|
font->data + offset,
|
|
size);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
}
|
|
|
|
/* Use maximum sized encoding to reserve space for later modification. */
|
|
end_buf = encode_integer_max (buf, 0);
|
|
status = cff_dict_set_operands (font->top_dict,
|
|
CHARSTRINGS_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
status = cff_dict_set_operands (font->top_dict,
|
|
CHARSET_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
if (font->scaled_font_subset->is_latin) {
|
|
status = cff_dict_set_operands (font->top_dict,
|
|
ENCODING_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
/* Private has two operands - size and offset */
|
|
end_buf = encode_integer_max (end_buf, 0);
|
|
cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
} else {
|
|
status = cff_dict_set_operands (font->top_dict,
|
|
FDSELECT_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
status = cff_dict_set_operands (font->top_dict,
|
|
FDARRAY_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
goto fail;
|
|
|
|
cff_dict_remove (font->top_dict, ENCODING_OP);
|
|
cff_dict_remove (font->top_dict, PRIVATE_OP);
|
|
}
|
|
|
|
/* Remove the unique identifier operators as the subsetted font is
|
|
* not the same is the original font. */
|
|
cff_dict_remove (font->top_dict, UNIQUEID_OP);
|
|
cff_dict_remove (font->top_dict, XUID_OP);
|
|
|
|
fail:
|
|
cff_index_fini (&index);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
cairo_cff_font_read_strings (cairo_cff_font_t *font)
|
|
{
|
|
return cff_index_read (&font->strings_index, &font->current_ptr, font->data_end);
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
cairo_cff_font_read_global_subroutines (cairo_cff_font_t *font)
|
|
{
|
|
cairo_int_status_t status;
|
|
int num_subs;
|
|
|
|
status = cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
num_subs = _cairo_array_num_elements (&font->global_sub_index);
|
|
font->global_subs_used = calloc (num_subs, sizeof(cairo_bool_t));
|
|
if (unlikely (font->global_subs_used == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
if (num_subs < 1240)
|
|
font->global_sub_bias = 107;
|
|
else if (num_subs < 33900)
|
|
font->global_sub_bias = 1131;
|
|
else
|
|
font->global_sub_bias = 32768;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
typedef cairo_int_status_t
|
|
(*font_read_t) (cairo_cff_font_t *font);
|
|
|
|
static const font_read_t font_read_funcs[] = {
|
|
cairo_cff_font_read_header,
|
|
cairo_cff_font_read_name,
|
|
cairo_cff_font_read_top_dict,
|
|
cairo_cff_font_read_strings,
|
|
cairo_cff_font_read_global_subroutines,
|
|
};
|
|
|
|
static cairo_int_status_t
|
|
cairo_cff_font_read_font (cairo_cff_font_t *font)
|
|
{
|
|
cairo_int_status_t status;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_LENGTH (font_read_funcs); i++) {
|
|
status = font_read_funcs[i] (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_set_ros_strings (cairo_cff_font_t *font)
|
|
{
|
|
cairo_status_t status;
|
|
unsigned char buf[30];
|
|
unsigned char *p;
|
|
int sid1, sid2;
|
|
const char *registry = "Adobe";
|
|
const char *ordering = "Identity";
|
|
|
|
sid1 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
|
|
status = cff_index_append_copy (&font->strings_subset_index,
|
|
(unsigned char *)registry,
|
|
strlen(registry));
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
sid2 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
|
|
status = cff_index_append_copy (&font->strings_subset_index,
|
|
(unsigned char *)ordering,
|
|
strlen(ordering));
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
p = encode_integer (buf, sid1);
|
|
p = encode_integer (p, sid2);
|
|
p = encode_integer (p, 0);
|
|
status = cff_dict_set_operands (font->top_dict, ROS_OP, buf, p - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
p = encode_integer (buf, font->scaled_font_subset->num_glyphs);
|
|
status = cff_dict_set_operands (font->top_dict, CIDCOUNT_OP, buf, p - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_subset_dict_string(cairo_cff_font_t *font,
|
|
cairo_hash_table_t *dict,
|
|
int operator)
|
|
{
|
|
int size;
|
|
unsigned char *p;
|
|
int sid;
|
|
unsigned char buf[100];
|
|
cff_index_element_t *element;
|
|
cairo_status_t status;
|
|
|
|
p = cff_dict_get_operands (dict, operator, &size);
|
|
if (!p)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
decode_integer (p, &sid);
|
|
if (sid < NUM_STD_STRINGS)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
element = _cairo_array_index (&font->strings_index, sid - NUM_STD_STRINGS);
|
|
sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
|
|
status = cff_index_append (&font->strings_subset_index, element->data, element->length);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
p = encode_integer (buf, sid);
|
|
status = cff_dict_set_operands (dict, operator, buf, p - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static const int dict_strings[] = {
|
|
VERSION_OP,
|
|
NOTICE_OP,
|
|
COPYRIGHT_OP,
|
|
FULLNAME_OP,
|
|
FAMILYNAME_OP,
|
|
WEIGHT_OP,
|
|
POSTSCRIPT_OP,
|
|
BASEFONTNAME_OP,
|
|
FONTNAME_OP,
|
|
};
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_subset_dict_strings (cairo_cff_font_t *font,
|
|
cairo_hash_table_t *dict)
|
|
{
|
|
cairo_status_t status;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_LENGTH (dict_strings); i++) {
|
|
status = cairo_cff_font_subset_dict_string (font, dict, dict_strings[i]);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static unsigned char *
|
|
type2_decode_integer (unsigned char *p, int *integer)
|
|
{
|
|
if (*p == 28) {
|
|
*integer = p[1] << 8 | p[2];
|
|
p += 3;
|
|
} else if (*p <= 246) {
|
|
*integer = *p++ - 139;
|
|
} else if (*p <= 250) {
|
|
*integer = (p[0] - 247) * 256 + p[1] + 108;
|
|
p += 2;
|
|
} else if (*p <= 254) {
|
|
*integer = -(p[0] - 251) * 256 - p[1] - 108;
|
|
p += 2;
|
|
} else { /* *p == 255 */
|
|
/* This actually a 16.16 fixed-point number however we are not interested in
|
|
* the value of fixed-point numbers. */
|
|
*integer = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4];
|
|
p += 5;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/* Type 2 charstring parser for finding calls to local or global
|
|
* subroutines. When we find a subroutine operator, the subroutine is
|
|
* marked as in use and recursively followed. The subroutine number is
|
|
* the value on the top of the stack when the subroutine operator is
|
|
* executed. In most fonts the subroutine number is encoded in an
|
|
* integer immediately preceding the subroutine operator. However it
|
|
* is possible for the subroutine number on the stack to be the result
|
|
* of a computation (in which case there will be an operator preceding
|
|
* the subroutine operator). If this occurs, subroutine subsetting is
|
|
* disabled since we can't easily determine which subroutines are
|
|
* used.
|
|
*/
|
|
static cairo_status_t
|
|
cairo_cff_parse_charstring (cairo_cff_font_t *font,
|
|
unsigned char *charstring, int length,
|
|
int glyph_id)
|
|
{
|
|
unsigned char *p = charstring;
|
|
unsigned char *end = charstring + length;
|
|
int integer;
|
|
int hint_bytes;
|
|
int sub_num;
|
|
cff_index_element_t *element;
|
|
int fd;
|
|
|
|
while (p < end) {
|
|
if (*p == 28 || *p >= 32) {
|
|
/* Integer value */
|
|
p = type2_decode_integer (p, &integer);
|
|
font->type2_stack_size++;
|
|
font->type2_stack_top_value = integer;
|
|
font->type2_stack_top_is_int = TRUE;
|
|
} else if (*p == TYPE2_hstem || *p == TYPE2_vstem ||
|
|
*p == TYPE2_hstemhm || *p == TYPE2_vstemhm) {
|
|
/* Hint operator. The number of hints declared by the
|
|
* operator depends on the size of the stack. */
|
|
font->type2_stack_top_is_int = FALSE;
|
|
font->type2_num_hints += font->type2_stack_size/2;
|
|
font->type2_stack_size = 0;
|
|
p++;
|
|
} else if (*p == TYPE2_hintmask || *p == TYPE2_cntrmask) {
|
|
/* Hintmask operator. These operators are followed by a
|
|
* variable length mask where the length depends on the
|
|
* number of hints declared. The first time this is called
|
|
* it is also an implicit vstem if there are arguments on
|
|
* the stack. */
|
|
if (font->type2_hintmask_bytes == 0) {
|
|
font->type2_stack_top_is_int = FALSE;
|
|
font->type2_num_hints += font->type2_stack_size/2;
|
|
font->type2_stack_size = 0;
|
|
font->type2_hintmask_bytes = (font->type2_num_hints+7)/8;
|
|
}
|
|
|
|
hint_bytes = font->type2_hintmask_bytes;
|
|
p++;
|
|
p += hint_bytes;
|
|
} else if (*p == TYPE2_callsubr) {
|
|
/* call to local subroutine */
|
|
if (! font->type2_stack_top_is_int)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
p++;
|
|
font->type2_stack_top_is_int = FALSE;
|
|
font->type2_stack_size--;
|
|
if (font->is_cid) {
|
|
fd = font->fdselect[glyph_id];
|
|
sub_num = font->type2_stack_top_value + font->fd_local_sub_bias[fd];
|
|
element = _cairo_array_index (&font->fd_local_sub_index[fd], sub_num);
|
|
if (! font->fd_local_subs_used[fd][sub_num]) {
|
|
font->fd_local_subs_used[fd][sub_num] = TRUE;
|
|
cairo_cff_parse_charstring (font, element->data, element->length, glyph_id);
|
|
}
|
|
} else {
|
|
sub_num = font->type2_stack_top_value + font->local_sub_bias;
|
|
element = _cairo_array_index (&font->local_sub_index, sub_num);
|
|
if (! font->local_subs_used[sub_num]) {
|
|
font->local_subs_used[sub_num] = TRUE;
|
|
cairo_cff_parse_charstring (font, element->data, element->length, glyph_id);
|
|
}
|
|
}
|
|
font->type2_nesting_level--;
|
|
} else if (*p == TYPE2_callgsubr) {
|
|
/* call to global subroutine */
|
|
if (! font->type2_stack_top_is_int)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
p++;
|
|
font->type2_stack_size--;
|
|
font->type2_stack_top_is_int = FALSE;
|
|
sub_num = font->type2_stack_top_value + font->global_sub_bias;
|
|
element = _cairo_array_index (&font->global_sub_index, sub_num);
|
|
if (! font->global_subs_used[sub_num]) {
|
|
font->global_subs_used[sub_num] = TRUE;
|
|
cairo_cff_parse_charstring (font, element->data, element->length, glyph_id);
|
|
}
|
|
font->type2_nesting_level--;
|
|
} else if (*p == 12) {
|
|
/* 2 byte instruction */
|
|
p += 2;
|
|
font->type2_stack_top_is_int = FALSE;
|
|
} else {
|
|
/* 1 byte instruction */
|
|
p++;
|
|
font->type2_stack_top_is_int = FALSE;
|
|
}
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_find_subroutines_used (cairo_cff_font_t *font,
|
|
unsigned char *charstring, int length,
|
|
int glyph_id)
|
|
{
|
|
font->type2_stack_size = 0;
|
|
font->type2_stack_top_value = 0;;
|
|
font->type2_stack_top_is_int = FALSE;
|
|
font->type2_num_hints = 0;
|
|
font->type2_hintmask_bytes = 0;
|
|
font->type2_nesting_level = 0;
|
|
|
|
return cairo_cff_parse_charstring (font, charstring, length, glyph_id);
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_subset_charstrings_and_subroutines (cairo_cff_font_t *font)
|
|
{
|
|
cff_index_element_t *element;
|
|
unsigned int i;
|
|
cairo_status_t status;
|
|
unsigned long glyph;
|
|
|
|
font->subset_subroutines = TRUE;
|
|
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
|
|
glyph = font->scaled_font_subset->glyphs[i];
|
|
element = _cairo_array_index (&font->charstrings_index, glyph);
|
|
status = cff_index_append (&font->charstrings_subset_index,
|
|
element->data,
|
|
element->length);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (font->subset_subroutines) {
|
|
status = cairo_cff_find_subroutines_used (font, element->data, element->length, glyph);
|
|
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
|
|
font->subset_subroutines = FALSE;
|
|
} else if (unlikely (status))
|
|
return status;
|
|
}
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_subset_fontdict (cairo_cff_font_t *font)
|
|
{
|
|
unsigned int i;
|
|
int fd;
|
|
int *reverse_map;
|
|
|
|
font->fdselect_subset = calloc (font->scaled_font_subset->num_glyphs,
|
|
sizeof (int));
|
|
if (unlikely (font->fdselect_subset == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
font->fd_subset_map = calloc (font->num_fontdicts, sizeof (int));
|
|
if (unlikely (font->fd_subset_map == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
font->private_dict_offset = calloc (font->num_fontdicts, sizeof (int));
|
|
if (unlikely (font->private_dict_offset == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
reverse_map = calloc (font->num_fontdicts, sizeof (int));
|
|
if (unlikely (reverse_map == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
for (i = 0; i < font->num_fontdicts; i++)
|
|
reverse_map[i] = -1;
|
|
|
|
font->num_subset_fontdicts = 0;
|
|
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
|
|
fd = font->fdselect[font->scaled_font_subset->glyphs[i]];
|
|
if (reverse_map[fd] < 0) {
|
|
font->fd_subset_map[font->num_subset_fontdicts] = fd;
|
|
reverse_map[fd] = font->num_subset_fontdicts++;
|
|
}
|
|
font->fdselect_subset[i] = reverse_map[fd];
|
|
}
|
|
|
|
free (reverse_map);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font)
|
|
{
|
|
unsigned char buf[100];
|
|
unsigned char *end_buf;
|
|
cairo_status_t status;
|
|
|
|
font->num_fontdicts = 1;
|
|
font->fd_dict = malloc (sizeof (cairo_hash_table_t *));
|
|
if (unlikely (font->fd_dict == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
if (cff_dict_init (&font->fd_dict[0])) {
|
|
free (font->fd_dict);
|
|
font->fd_dict = NULL;
|
|
font->num_fontdicts = 0;
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
font->fd_subset_map = malloc (sizeof (int));
|
|
if (unlikely (font->fd_subset_map == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
font->private_dict_offset = malloc (sizeof (int));
|
|
if (unlikely (font->private_dict_offset == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
font->fd_subset_map[0] = 0;
|
|
font->num_subset_fontdicts = 1;
|
|
|
|
/* Set integer operand to max value to use max size encoding to reserve
|
|
* space for any value later */
|
|
end_buf = encode_integer_max (buf, 0);
|
|
end_buf = encode_integer_max (end_buf, 0);
|
|
status = cff_dict_set_operands (font->fd_dict[0], PRIVATE_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_subset_strings (cairo_cff_font_t *font)
|
|
{
|
|
cairo_status_t status;
|
|
unsigned int i;
|
|
|
|
status = cairo_cff_font_subset_dict_strings (font, font->top_dict);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (font->is_cid) {
|
|
for (i = 0; i < font->num_subset_fontdicts; i++) {
|
|
status = cairo_cff_font_subset_dict_strings (font, font->fd_dict[font->fd_subset_map[i]]);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = cairo_cff_font_subset_dict_strings (font, font->fd_private_dict[font->fd_subset_map[i]]);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
} else {
|
|
status = cairo_cff_font_subset_dict_strings (font, font->private_dict);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/* The Euro is the only the only character in the winansi encoding
|
|
* with a glyph name that is not a CFF standard string. As the strings
|
|
* are written before the charset, we need to check during the
|
|
* subsetting phase if the Euro glyph is required and add the
|
|
* glyphname to the list of strings to write out.
|
|
*/
|
|
static cairo_status_t
|
|
cairo_cff_font_add_euro_charset_string (cairo_cff_font_t *font)
|
|
{
|
|
cairo_status_t status;
|
|
unsigned int i;
|
|
int ch;
|
|
const char *euro = "Euro";
|
|
|
|
for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
|
|
ch = font->scaled_font_subset->to_latin_char[i];
|
|
if (ch == 128) {
|
|
font->euro_sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
|
|
status = cff_index_append_copy (&font->strings_subset_index,
|
|
(unsigned char *)euro, strlen(euro));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_subset_font (cairo_cff_font_t *font)
|
|
{
|
|
cairo_status_t status;
|
|
|
|
if (!font->scaled_font_subset->is_latin) {
|
|
status = cairo_cff_font_set_ros_strings (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
status = cairo_cff_font_subset_charstrings_and_subroutines (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (!font->scaled_font_subset->is_latin) {
|
|
if (font->is_cid)
|
|
status = cairo_cff_font_subset_fontdict (font);
|
|
else
|
|
status = cairo_cff_font_create_cid_fontdict (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
} else {
|
|
font->private_dict_offset = malloc (sizeof (int));
|
|
if (unlikely (font->private_dict_offset == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
status = cairo_cff_font_subset_strings (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (font->scaled_font_subset->is_latin)
|
|
status = cairo_cff_font_add_euro_charset_string (font);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Set the operand of the specified operator in the (already written)
|
|
* top dict to point to the current position in the output
|
|
* array. Operands updated with this function must have previously
|
|
* been encoded with the 5-byte (max) integer encoding. */
|
|
static void
|
|
cairo_cff_font_set_topdict_operator_to_cur_pos (cairo_cff_font_t *font,
|
|
int operator)
|
|
{
|
|
int cur_pos;
|
|
int offset;
|
|
int size;
|
|
unsigned char buf[10];
|
|
unsigned char *buf_end;
|
|
unsigned char *op_ptr;
|
|
|
|
cur_pos = _cairo_array_num_elements (&font->output);
|
|
buf_end = encode_integer_max (buf, cur_pos);
|
|
offset = cff_dict_get_location (font->top_dict, operator, &size);
|
|
assert (offset > 0);
|
|
op_ptr = _cairo_array_index (&font->output, offset);
|
|
memcpy (op_ptr, buf, buf_end - buf);
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_header (cairo_cff_font_t *font)
|
|
{
|
|
return _cairo_array_append_multiple (&font->output,
|
|
font->header,
|
|
font->header->header_size);
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_name (cairo_cff_font_t *font)
|
|
{
|
|
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
|
cairo_array_t index;
|
|
|
|
cff_index_init (&index);
|
|
|
|
status = cff_index_append_copy (&index,
|
|
(unsigned char *) font->subset_font_name,
|
|
strlen(font->subset_font_name));
|
|
if (unlikely (status))
|
|
goto FAIL;
|
|
|
|
status = cff_index_write (&index, &font->output);
|
|
if (unlikely (status))
|
|
goto FAIL;
|
|
|
|
FAIL:
|
|
cff_index_fini (&index);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_top_dict (cairo_cff_font_t *font)
|
|
{
|
|
uint16_t count;
|
|
unsigned char buf[10];
|
|
unsigned char *p;
|
|
int offset_index;
|
|
int dict_start, dict_size;
|
|
int offset_size = 4;
|
|
cairo_status_t status;
|
|
|
|
/* Write an index containing the top dict */
|
|
|
|
count = cpu_to_be16 (1);
|
|
status = _cairo_array_append_multiple (&font->output, &count, 2);
|
|
if (unlikely (status))
|
|
return status;
|
|
buf[0] = offset_size;
|
|
status = _cairo_array_append (&font->output, buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
encode_index_offset (buf, offset_size, 1);
|
|
status = _cairo_array_append_multiple (&font->output, buf, offset_size);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
/* Reserve space for last element of offset array and update after
|
|
* dict is written */
|
|
offset_index = _cairo_array_num_elements (&font->output);
|
|
status = _cairo_array_append_multiple (&font->output, buf, offset_size);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
dict_start = _cairo_array_num_elements (&font->output);
|
|
status = cff_dict_write (font->top_dict, &font->output);
|
|
if (unlikely (status))
|
|
return status;
|
|
dict_size = _cairo_array_num_elements (&font->output) - dict_start;
|
|
|
|
encode_index_offset (buf, offset_size, dict_size + 1);
|
|
p = _cairo_array_index (&font->output, offset_index);
|
|
memcpy (p, buf, offset_size);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_strings (cairo_cff_font_t *font)
|
|
{
|
|
return cff_index_write (&font->strings_subset_index, &font->output);
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_global_subrs (cairo_cff_font_t *font)
|
|
{
|
|
unsigned int i;
|
|
unsigned char return_op = TYPE2_return;
|
|
|
|
/* poppler and fontforge don't like zero length subroutines so we
|
|
* replace unused subroutines with a 'return' instruction. */
|
|
if (font->subset_subroutines) {
|
|
for (i = 0; i < _cairo_array_num_elements (&font->global_sub_index); i++) {
|
|
if (! font->global_subs_used[i])
|
|
cff_index_set_object (&font->global_sub_index, i, &return_op, 1);
|
|
}
|
|
}
|
|
|
|
return cff_index_write (&font->global_sub_index, &font->output);
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_encoding (cairo_cff_font_t *font)
|
|
{
|
|
unsigned char buf[2];
|
|
cairo_status_t status;
|
|
unsigned int i;
|
|
|
|
cairo_cff_font_set_topdict_operator_to_cur_pos (font, ENCODING_OP);
|
|
buf[0] = 0; /* Format 0 */
|
|
buf[1] = font->scaled_font_subset->num_glyphs - 1;
|
|
status = _cairo_array_append_multiple (&font->output, buf, 2);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
|
|
unsigned char ch = font->scaled_font_subset->to_latin_char[i];
|
|
status = _cairo_array_append (&font->output, &ch);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_fdselect (cairo_cff_font_t *font)
|
|
{
|
|
unsigned char data;
|
|
unsigned int i;
|
|
cairo_int_status_t status;
|
|
|
|
cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDSELECT_OP);
|
|
|
|
if (font->is_cid) {
|
|
data = 0;
|
|
status = _cairo_array_append (&font->output, &data);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
|
|
data = font->fdselect_subset[i];
|
|
status = _cairo_array_append (&font->output, &data);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
} else {
|
|
unsigned char byte;
|
|
uint16_t word;
|
|
|
|
status = _cairo_array_grow_by (&font->output, 9);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
byte = 3;
|
|
status = _cairo_array_append (&font->output, &byte);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
|
|
word = cpu_to_be16 (1);
|
|
status = _cairo_array_append_multiple (&font->output, &word, 2);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
|
|
word = cpu_to_be16 (0);
|
|
status = _cairo_array_append_multiple (&font->output, &word, 2);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
|
|
byte = 0;
|
|
status = _cairo_array_append (&font->output, &byte);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
|
|
word = cpu_to_be16 (font->scaled_font_subset->num_glyphs);
|
|
status = _cairo_array_append_multiple (&font->output, &word, 2);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Winansi to CFF standard strings mapping for characters 128 to 255 */
|
|
static const int winansi_to_cff_std_string[] = {
|
|
/* 128 */
|
|
0, 0, 117, 101, 118, 121, 112, 113,
|
|
126, 122, 192, 107, 142, 0, 199, 0,
|
|
/* 144 */
|
|
0, 65, 8, 105, 119, 116, 111, 137,
|
|
127, 153, 221, 108, 148, 0, 228, 198,
|
|
/* 160 */
|
|
0, 96, 97, 98, 103, 100, 160, 102,
|
|
131, 170, 139, 106, 151, 0, 165, 128,
|
|
/* 176 */
|
|
161, 156, 164, 169, 125, 152, 115, 114,
|
|
133, 150, 143, 187, 158, 155, 163, 123,
|
|
/* 192 */
|
|
174, 171, 172, 176, 173, 175, 138, 177,
|
|
181, 178, 179, 180, 185, 182, 183, 184,
|
|
/* 208 */
|
|
154, 186, 190, 187, 188, 191, 189, 168,
|
|
141, 196, 193, 194, 195, 197, 157, 149,
|
|
/* 224 */
|
|
203, 200, 201, 205, 202, 204, 144, 206,
|
|
210, 207, 208, 209, 214, 211, 212, 213,
|
|
/* 240 */
|
|
167, 215, 219, 216, 217, 220, 218, 159,
|
|
147, 225, 222, 223, 224, 226, 162, 227,
|
|
};
|
|
|
|
static int
|
|
cairo_cff_font_get_sid_for_winansi_char (cairo_cff_font_t *font, int ch)
|
|
{
|
|
int sid;
|
|
|
|
if (ch == 39) {
|
|
sid = 104;
|
|
|
|
} else if (ch == 96) {
|
|
sid = 124;
|
|
|
|
} else if (ch >= 32 && ch <= 126) {
|
|
sid = ch - 31;
|
|
|
|
} else if (ch == 128) {
|
|
assert (font->euro_sid >= NUM_STD_STRINGS);
|
|
sid = font->euro_sid;
|
|
|
|
} else if (ch >= 128 && ch <= 255) {
|
|
sid = winansi_to_cff_std_string[ch - 128];
|
|
|
|
} else {
|
|
sid = 0;
|
|
}
|
|
|
|
return sid;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_type1_charset (cairo_cff_font_t *font)
|
|
{
|
|
unsigned char format = 0;
|
|
unsigned int i;
|
|
int ch, sid;
|
|
cairo_status_t status;
|
|
uint16_t sid_be16;
|
|
|
|
cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP);
|
|
status = _cairo_array_append (&font->output, &format);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
|
|
ch = font->scaled_font_subset->to_latin_char[i];
|
|
sid = cairo_cff_font_get_sid_for_winansi_char (font, ch);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
sid_be16 = cpu_to_be16(sid);
|
|
status = _cairo_array_append_multiple (&font->output, &sid_be16, sizeof(sid_be16));
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_cid_charset (cairo_cff_font_t *font)
|
|
{
|
|
unsigned char byte;
|
|
uint16_t word;
|
|
cairo_status_t status;
|
|
|
|
cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP);
|
|
status = _cairo_array_grow_by (&font->output, 5);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
byte = 2;
|
|
status = _cairo_array_append (&font->output, &byte);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
|
|
word = cpu_to_be16 (1);
|
|
status = _cairo_array_append_multiple (&font->output, &word, 2);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
|
|
word = cpu_to_be16 (font->scaled_font_subset->num_glyphs - 2);
|
|
status = _cairo_array_append_multiple (&font->output, &word, 2);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_charstrings (cairo_cff_font_t *font)
|
|
{
|
|
cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSTRINGS_OP);
|
|
|
|
return cff_index_write (&font->charstrings_subset_index, &font->output);
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font)
|
|
{
|
|
unsigned int i;
|
|
cairo_int_status_t status;
|
|
uint32_t *offset_array;
|
|
int offset_base;
|
|
uint16_t count;
|
|
uint8_t offset_size = 4;
|
|
|
|
cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDARRAY_OP);
|
|
count = cpu_to_be16 (font->num_subset_fontdicts);
|
|
status = _cairo_array_append_multiple (&font->output, &count, sizeof (uint16_t));
|
|
if (unlikely (status))
|
|
return status;
|
|
status = _cairo_array_append (&font->output, &offset_size);
|
|
if (unlikely (status))
|
|
return status;
|
|
status = _cairo_array_allocate (&font->output,
|
|
(font->num_subset_fontdicts + 1)*offset_size,
|
|
(void **) &offset_array);
|
|
if (unlikely (status))
|
|
return status;
|
|
offset_base = _cairo_array_num_elements (&font->output) - 1;
|
|
*offset_array++ = cpu_to_be32(1);
|
|
for (i = 0; i < font->num_subset_fontdicts; i++) {
|
|
status = cff_dict_write (font->fd_dict[font->fd_subset_map[i]],
|
|
&font->output);
|
|
if (unlikely (status))
|
|
return status;
|
|
*offset_array++ = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base);
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_private_dict (cairo_cff_font_t *font,
|
|
int dict_num,
|
|
cairo_hash_table_t *parent_dict,
|
|
cairo_hash_table_t *private_dict)
|
|
{
|
|
int offset;
|
|
int size;
|
|
unsigned char buf[10];
|
|
unsigned char *buf_end;
|
|
unsigned char *p;
|
|
cairo_status_t status;
|
|
|
|
/* Write private dict and update offset and size in top dict */
|
|
font->private_dict_offset[dict_num] = _cairo_array_num_elements (&font->output);
|
|
status = cff_dict_write (private_dict, &font->output);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
size = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num];
|
|
/* private entry has two operands - size and offset */
|
|
buf_end = encode_integer_max (buf, size);
|
|
buf_end = encode_integer_max (buf_end, font->private_dict_offset[dict_num]);
|
|
offset = cff_dict_get_location (parent_dict, PRIVATE_OP, &size);
|
|
assert (offset > 0);
|
|
p = _cairo_array_index (&font->output, offset);
|
|
memcpy (p, buf, buf_end - buf);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_local_sub (cairo_cff_font_t *font,
|
|
int dict_num,
|
|
cairo_hash_table_t *private_dict,
|
|
cairo_array_t *local_sub_index,
|
|
cairo_bool_t *local_subs_used)
|
|
{
|
|
int offset;
|
|
int size;
|
|
unsigned char buf[10];
|
|
unsigned char *buf_end;
|
|
unsigned char *p;
|
|
cairo_status_t status;
|
|
unsigned int i;
|
|
unsigned char return_op = TYPE2_return;
|
|
|
|
if (_cairo_array_num_elements (local_sub_index) > 0) {
|
|
/* Write local subroutines and update offset in private
|
|
* dict. Local subroutines offset is relative to start of
|
|
* private dict */
|
|
offset = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num];
|
|
buf_end = encode_integer_max (buf, offset);
|
|
offset = cff_dict_get_location (private_dict, LOCAL_SUB_OP, &size);
|
|
assert (offset > 0);
|
|
p = _cairo_array_index (&font->output, offset);
|
|
memcpy (p, buf, buf_end - buf);
|
|
|
|
/* poppler and fontforge don't like zero length subroutines so
|
|
* we replace unused subroutines with a 'return' instruction.
|
|
*/
|
|
if (font->subset_subroutines) {
|
|
for (i = 0; i < _cairo_array_num_elements (local_sub_index); i++) {
|
|
if (! local_subs_used[i])
|
|
cff_index_set_object (local_sub_index, i, &return_op, 1);
|
|
}
|
|
}
|
|
status = cff_index_write (local_sub_index, &font->output);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font)
|
|
{
|
|
unsigned int i;
|
|
cairo_int_status_t status;
|
|
|
|
if (font->is_cid) {
|
|
for (i = 0; i < font->num_subset_fontdicts; i++) {
|
|
status = cairo_cff_font_write_private_dict (
|
|
font,
|
|
i,
|
|
font->fd_dict[font->fd_subset_map[i]],
|
|
font->fd_private_dict[font->fd_subset_map[i]]);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
for (i = 0; i < font->num_subset_fontdicts; i++) {
|
|
status = cairo_cff_font_write_local_sub (
|
|
font,
|
|
i,
|
|
font->fd_private_dict[font->fd_subset_map[i]],
|
|
&font->fd_local_sub_index[font->fd_subset_map[i]],
|
|
font->fd_local_subs_used[font->fd_subset_map[i]]);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
} else {
|
|
status = cairo_cff_font_write_private_dict (font,
|
|
0,
|
|
font->fd_dict[0],
|
|
font->private_dict);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = cairo_cff_font_write_local_sub (font,
|
|
0,
|
|
font->private_dict,
|
|
&font->local_sub_index,
|
|
font->local_subs_used);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_type1_private_dict_and_local_sub (cairo_cff_font_t *font)
|
|
{
|
|
cairo_int_status_t status;
|
|
|
|
status = cairo_cff_font_write_private_dict (font,
|
|
0,
|
|
font->top_dict,
|
|
font->private_dict);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = cairo_cff_font_write_local_sub (font,
|
|
0,
|
|
font->private_dict,
|
|
&font->local_sub_index,
|
|
font->local_subs_used);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
typedef cairo_status_t
|
|
(*font_write_t) (cairo_cff_font_t *font);
|
|
|
|
static const font_write_t font_write_cid_funcs[] = {
|
|
cairo_cff_font_write_header,
|
|
cairo_cff_font_write_name,
|
|
cairo_cff_font_write_top_dict,
|
|
cairo_cff_font_write_strings,
|
|
cairo_cff_font_write_global_subrs,
|
|
cairo_cff_font_write_cid_charset,
|
|
cairo_cff_font_write_fdselect,
|
|
cairo_cff_font_write_charstrings,
|
|
cairo_cff_font_write_cid_fontdict,
|
|
cairo_cff_font_write_cid_private_dict_and_local_sub,
|
|
};
|
|
|
|
static const font_write_t font_write_type1_funcs[] = {
|
|
cairo_cff_font_write_header,
|
|
cairo_cff_font_write_name,
|
|
cairo_cff_font_write_top_dict,
|
|
cairo_cff_font_write_strings,
|
|
cairo_cff_font_write_global_subrs,
|
|
cairo_cff_font_write_encoding,
|
|
cairo_cff_font_write_type1_charset,
|
|
cairo_cff_font_write_charstrings,
|
|
cairo_cff_font_write_type1_private_dict_and_local_sub,
|
|
};
|
|
|
|
static cairo_status_t
|
|
cairo_cff_font_write_subset (cairo_cff_font_t *font)
|
|
{
|
|
cairo_int_status_t status;
|
|
unsigned int i;
|
|
|
|
if (font->scaled_font_subset->is_latin) {
|
|
for (i = 0; i < ARRAY_LENGTH (font_write_type1_funcs); i++) {
|
|
status = font_write_type1_funcs[i] (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
} else {
|
|
for (i = 0; i < ARRAY_LENGTH (font_write_cid_funcs); i++) {
|
|
status = font_write_cid_funcs[i] (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
cairo_cff_font_generate (cairo_cff_font_t *font,
|
|
const char **data,
|
|
unsigned long *length)
|
|
{
|
|
cairo_int_status_t status;
|
|
|
|
status = cairo_cff_font_read_font (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = cairo_cff_font_subset_font (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = cairo_cff_font_write_subset (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
*data = _cairo_array_index (&font->output, 0);
|
|
*length = _cairo_array_num_elements (&font->output);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
cairo_cff_font_create_set_widths (cairo_cff_font_t *font)
|
|
{
|
|
unsigned long size;
|
|
unsigned long long_entry_size;
|
|
unsigned long short_entry_size;
|
|
unsigned int i;
|
|
tt_hhea_t hhea;
|
|
int num_hmetrics;
|
|
unsigned char buf[10];
|
|
int glyph_index;
|
|
cairo_int_status_t status;
|
|
|
|
size = sizeof (tt_hhea_t);
|
|
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
|
|
TT_TAG_hhea, 0,
|
|
(unsigned char*) &hhea, &size);
|
|
if (unlikely (status))
|
|
return status;
|
|
num_hmetrics = be16_to_cpu (hhea.num_hmetrics);
|
|
|
|
for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
|
|
glyph_index = font->scaled_font_subset->glyphs[i];
|
|
long_entry_size = 2 * sizeof (int16_t);
|
|
short_entry_size = sizeof (int16_t);
|
|
if (glyph_index < num_hmetrics) {
|
|
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
|
|
TT_TAG_hmtx,
|
|
glyph_index * long_entry_size,
|
|
buf, &short_entry_size);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
else
|
|
{
|
|
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
|
|
TT_TAG_hmtx,
|
|
(num_hmetrics - 1) * long_entry_size,
|
|
buf, &short_entry_size);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
font->widths[i] = be16_to_cpu (*((int16_t*)buf));
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
|
cairo_cff_font_t **font_return,
|
|
const char *subset_name)
|
|
{
|
|
const cairo_scaled_font_backend_t *backend;
|
|
cairo_status_t status;
|
|
cairo_cff_font_t *font;
|
|
tt_head_t head;
|
|
tt_hhea_t hhea;
|
|
unsigned long size, data_length;
|
|
|
|
backend = scaled_font_subset->scaled_font->backend;
|
|
if (!backend->load_truetype_table)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
/* We need to use a fallback font generated from the synthesized outlines. */
|
|
if (backend->is_synthetic && backend->is_synthetic (scaled_font_subset->scaled_font))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
data_length = 0;
|
|
status = backend->load_truetype_table( scaled_font_subset->scaled_font,
|
|
TT_TAG_CFF, 0, NULL, &data_length);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
size = sizeof (tt_head_t);
|
|
status = backend->load_truetype_table (scaled_font_subset->scaled_font,
|
|
TT_TAG_head, 0,
|
|
(unsigned char *) &head, &size);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
size = sizeof (tt_hhea_t);
|
|
status = backend->load_truetype_table (scaled_font_subset->scaled_font,
|
|
TT_TAG_hhea, 0,
|
|
(unsigned char *) &hhea, &size);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
size = 0;
|
|
status = backend->load_truetype_table (scaled_font_subset->scaled_font,
|
|
TT_TAG_hmtx, 0, NULL, &size);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
font = malloc (sizeof (cairo_cff_font_t));
|
|
if (unlikely (font == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
font->backend = backend;
|
|
font->scaled_font_subset = scaled_font_subset;
|
|
|
|
_cairo_array_init (&font->output, sizeof (char));
|
|
status = _cairo_array_grow_by (&font->output, 4096);
|
|
if (unlikely (status))
|
|
goto fail2;
|
|
|
|
font->subset_font_name = strdup (subset_name);
|
|
if (unlikely (font->subset_font_name == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail2;
|
|
}
|
|
font->x_min = (int16_t) be16_to_cpu (head.x_min);
|
|
font->y_min = (int16_t) be16_to_cpu (head.y_min);
|
|
font->x_max = (int16_t) be16_to_cpu (head.x_max);
|
|
font->y_max = (int16_t) be16_to_cpu (head.y_max);
|
|
font->ascent = (int16_t) be16_to_cpu (hhea.ascender);
|
|
font->descent = (int16_t) be16_to_cpu (hhea.descender);
|
|
font->units_per_em = (int16_t) be16_to_cpu (head.units_per_em);
|
|
if (font->units_per_em == 0)
|
|
font->units_per_em = 1000;
|
|
|
|
font->font_name = NULL;
|
|
status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font,
|
|
&font->ps_name,
|
|
&font->font_name);
|
|
if (_cairo_status_is_error (status))
|
|
goto fail3;
|
|
|
|
/* If the PS name is not found, create a CairoFont-x-y name. */
|
|
if (font->ps_name == NULL) {
|
|
font->ps_name = malloc (30);
|
|
if (unlikely (font->ps_name == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail3;
|
|
}
|
|
|
|
snprintf(font->ps_name, 30, "CairoFont-%u-%u",
|
|
scaled_font_subset->font_id,
|
|
scaled_font_subset->subset_id);
|
|
}
|
|
|
|
font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int));
|
|
if (unlikely (font->widths == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail4;
|
|
}
|
|
|
|
status = cairo_cff_font_create_set_widths (font);
|
|
if (unlikely (status))
|
|
goto fail5;
|
|
|
|
font->data_length = data_length;
|
|
font->data = malloc (data_length);
|
|
if (unlikely (font->data == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail5;
|
|
}
|
|
status = font->backend->load_truetype_table ( font->scaled_font_subset->scaled_font,
|
|
TT_TAG_CFF, 0, font->data,
|
|
&font->data_length);
|
|
if (unlikely (status))
|
|
goto fail6;
|
|
|
|
font->data_end = font->data + font->data_length;
|
|
|
|
status = cff_dict_init (&font->top_dict);
|
|
if (unlikely (status))
|
|
goto fail6;
|
|
|
|
status = cff_dict_init (&font->private_dict);
|
|
if (unlikely (status))
|
|
goto fail7;
|
|
|
|
cff_index_init (&font->strings_index);
|
|
cff_index_init (&font->charstrings_index);
|
|
cff_index_init (&font->global_sub_index);
|
|
cff_index_init (&font->local_sub_index);
|
|
cff_index_init (&font->charstrings_subset_index);
|
|
cff_index_init (&font->strings_subset_index);
|
|
font->euro_sid = 0;
|
|
font->fdselect = NULL;
|
|
font->fd_dict = NULL;
|
|
font->fd_private_dict = NULL;
|
|
font->fd_local_sub_index = NULL;
|
|
font->fd_local_sub_bias = NULL;
|
|
font->fdselect_subset = NULL;
|
|
font->fd_subset_map = NULL;
|
|
font->private_dict_offset = NULL;
|
|
font->global_subs_used = NULL;
|
|
font->local_subs_used = NULL;
|
|
font->fd_local_subs_used = NULL;
|
|
|
|
*font_return = font;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
fail7:
|
|
_cairo_hash_table_destroy (font->top_dict);
|
|
fail6:
|
|
free (font->data);
|
|
fail5:
|
|
free (font->widths);
|
|
fail4:
|
|
if (font->font_name)
|
|
free (font->font_name);
|
|
fail3:
|
|
free (font->subset_font_name);
|
|
fail2:
|
|
_cairo_array_fini (&font->output);
|
|
free (font);
|
|
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
cairo_cff_font_destroy (cairo_cff_font_t *font)
|
|
{
|
|
unsigned int i;
|
|
|
|
free (font->widths);
|
|
if (font->font_name)
|
|
free (font->font_name);
|
|
free (font->ps_name);
|
|
free (font->subset_font_name);
|
|
_cairo_array_fini (&font->output);
|
|
cff_dict_fini (font->top_dict);
|
|
cff_dict_fini (font->private_dict);
|
|
cff_index_fini (&font->strings_index);
|
|
cff_index_fini (&font->charstrings_index);
|
|
cff_index_fini (&font->global_sub_index);
|
|
cff_index_fini (&font->local_sub_index);
|
|
cff_index_fini (&font->charstrings_subset_index);
|
|
cff_index_fini (&font->strings_subset_index);
|
|
|
|
/* If we bailed out early as a result of an error some of the
|
|
* following cairo_cff_font_t members may still be NULL */
|
|
if (font->fd_dict) {
|
|
for (i = 0; i < font->num_fontdicts; i++) {
|
|
if (font->fd_dict[i])
|
|
cff_dict_fini (font->fd_dict[i]);
|
|
}
|
|
free (font->fd_dict);
|
|
}
|
|
if (font->global_subs_used)
|
|
free (font->global_subs_used);
|
|
if (font->local_subs_used)
|
|
free (font->local_subs_used);
|
|
if (font->fd_subset_map)
|
|
free (font->fd_subset_map);
|
|
if (font->private_dict_offset)
|
|
free (font->private_dict_offset);
|
|
|
|
if (font->is_cid) {
|
|
if (font->fdselect)
|
|
free (font->fdselect);
|
|
if (font->fdselect_subset)
|
|
free (font->fdselect_subset);
|
|
if (font->fd_private_dict) {
|
|
for (i = 0; i < font->num_fontdicts; i++) {
|
|
if (font->fd_private_dict[i])
|
|
cff_dict_fini (font->fd_private_dict[i]);
|
|
}
|
|
free (font->fd_private_dict);
|
|
}
|
|
if (font->fd_local_sub_index) {
|
|
for (i = 0; i < font->num_fontdicts; i++)
|
|
cff_index_fini (&font->fd_local_sub_index[i]);
|
|
free (font->fd_local_sub_index);
|
|
}
|
|
if (font->fd_local_sub_bias)
|
|
free (font->fd_local_sub_bias);
|
|
if (font->fd_local_subs_used) {
|
|
for (i = 0; i < font->num_fontdicts; i++) {
|
|
if (font->fd_local_subs_used[i])
|
|
free (font->fd_local_subs_used[i]);
|
|
}
|
|
free (font->fd_local_subs_used);
|
|
}
|
|
|
|
}
|
|
|
|
if (font->data)
|
|
free (font->data);
|
|
|
|
free (font);
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_cff_subset_init (cairo_cff_subset_t *cff_subset,
|
|
const char *subset_name,
|
|
cairo_scaled_font_subset_t *font_subset)
|
|
{
|
|
cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */
|
|
cairo_status_t status;
|
|
const char *data = NULL; /* squelch bogus compiler warning */
|
|
unsigned long length = 0; /* squelch bogus compiler warning */
|
|
unsigned int i;
|
|
|
|
status = _cairo_cff_font_create (font_subset, &font, subset_name);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = cairo_cff_font_generate (font, &data, &length);
|
|
if (unlikely (status))
|
|
goto fail1;
|
|
|
|
cff_subset->ps_name = strdup (font->ps_name);
|
|
if (unlikely (cff_subset->ps_name == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail1;
|
|
}
|
|
|
|
if (font->font_name) {
|
|
cff_subset->font_name = strdup (font->font_name);
|
|
if (cff_subset->font_name == NULL) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail2;
|
|
}
|
|
} else {
|
|
cff_subset->font_name = NULL;
|
|
}
|
|
|
|
cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
|
|
if (unlikely (cff_subset->widths == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail3;
|
|
}
|
|
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
|
|
cff_subset->widths[i] = (double)font->widths[i]/font->units_per_em;
|
|
|
|
cff_subset->x_min = (double)font->x_min/font->units_per_em;
|
|
cff_subset->y_min = (double)font->y_min/font->units_per_em;
|
|
cff_subset->x_max = (double)font->x_max/font->units_per_em;
|
|
cff_subset->y_max = (double)font->y_max/font->units_per_em;
|
|
cff_subset->ascent = (double)font->ascent/font->units_per_em;
|
|
cff_subset->descent = (double)font->descent/font->units_per_em;
|
|
|
|
cff_subset->data = malloc (length);
|
|
if (unlikely (cff_subset->data == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail4;
|
|
}
|
|
|
|
memcpy (cff_subset->data, data, length);
|
|
cff_subset->data_length = length;
|
|
|
|
cairo_cff_font_destroy (font);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
fail4:
|
|
free (cff_subset->widths);
|
|
fail3:
|
|
if (cff_subset->font_name)
|
|
free (cff_subset->font_name);
|
|
fail2:
|
|
free (cff_subset->ps_name);
|
|
fail1:
|
|
cairo_cff_font_destroy (font);
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
_cairo_cff_subset_fini (cairo_cff_subset_t *subset)
|
|
{
|
|
free (subset->ps_name);
|
|
if (subset->font_name)
|
|
free (subset->font_name);
|
|
free (subset->widths);
|
|
free (subset->data);
|
|
}
|
|
|
|
cairo_bool_t
|
|
_cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font)
|
|
{
|
|
const cairo_scaled_font_backend_t *backend;
|
|
cairo_status_t status;
|
|
unsigned char *data;
|
|
unsigned long data_length;
|
|
unsigned char *current_ptr;
|
|
unsigned char *data_end;
|
|
cff_header_t *header;
|
|
cff_index_element_t *element;
|
|
cairo_hash_table_t *top_dict;
|
|
cairo_array_t index;
|
|
int size;
|
|
cairo_bool_t is_cid = FALSE;
|
|
|
|
backend = scaled_font->backend;
|
|
if (!backend->load_truetype_table)
|
|
return FALSE;
|
|
|
|
/* check for CFF font */
|
|
data_length = 0;
|
|
status = backend->load_truetype_table(scaled_font,
|
|
TT_TAG_CFF, 0, NULL, &data_length);
|
|
if (status)
|
|
return FALSE;
|
|
|
|
/* load CFF data */
|
|
data = malloc (data_length);
|
|
if (unlikely (data == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
status = backend->load_truetype_table (scaled_font, TT_TAG_CFF,
|
|
0, data, &data_length);
|
|
if (unlikely (status))
|
|
goto fail1;
|
|
|
|
data_end = data + data_length;
|
|
|
|
/* skip header */
|
|
if (data_length < sizeof (cff_header_t))
|
|
goto fail1;
|
|
|
|
header = (cff_header_t *) data;
|
|
current_ptr = data + header->header_size;
|
|
|
|
/* skip name */
|
|
cff_index_init (&index);
|
|
status = cff_index_read (&index, ¤t_ptr, data_end);
|
|
cff_index_fini (&index);
|
|
|
|
if (status)
|
|
goto fail1;
|
|
|
|
/* read top dict */
|
|
cff_index_init (&index);
|
|
status = cff_index_read (&index, ¤t_ptr, data_end);
|
|
if (unlikely (status))
|
|
goto fail2;
|
|
|
|
status = cff_dict_init (&top_dict);
|
|
if (unlikely (status))
|
|
goto fail2;
|
|
|
|
element = _cairo_array_index (&index, 0);
|
|
status = cff_dict_read (top_dict, element->data, element->length);
|
|
if (unlikely (status))
|
|
goto fail3;
|
|
|
|
/* check for ROS operator indicating a CID font */
|
|
if (cff_dict_get_operands (top_dict, ROS_OP, &size) != NULL)
|
|
is_cid = TRUE;
|
|
|
|
fail3:
|
|
cff_dict_fini (top_dict);
|
|
|
|
fail2:
|
|
cff_index_fini (&index);
|
|
|
|
fail1:
|
|
free (data);
|
|
|
|
return is_cid;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
|
cairo_cff_font_t **font_return,
|
|
const char *subset_name)
|
|
{
|
|
cairo_status_t status;
|
|
cairo_cff_font_t *font;
|
|
|
|
font = malloc (sizeof (cairo_cff_font_t));
|
|
if (unlikely (font == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
font->backend = NULL;
|
|
font->scaled_font_subset = scaled_font_subset;
|
|
|
|
_cairo_array_init (&font->output, sizeof (char));
|
|
status = _cairo_array_grow_by (&font->output, 4096);
|
|
if (unlikely (status))
|
|
goto fail1;
|
|
|
|
font->subset_font_name = strdup (subset_name);
|
|
if (unlikely (font->subset_font_name == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail1;
|
|
}
|
|
|
|
font->ps_name = strdup (subset_name);
|
|
if (unlikely (font->ps_name == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail2;
|
|
}
|
|
font->font_name = NULL;
|
|
|
|
font->x_min = 0;
|
|
font->y_min = 0;
|
|
font->x_max = 0;
|
|
font->y_max = 0;
|
|
font->ascent = 0;
|
|
font->descent = 0;
|
|
|
|
font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int));
|
|
if (unlikely (font->widths == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail3;
|
|
}
|
|
|
|
font->data_length = 0;
|
|
font->data = NULL;
|
|
font->data_end = NULL;
|
|
|
|
status = cff_dict_init (&font->top_dict);
|
|
if (unlikely (status))
|
|
goto fail4;
|
|
|
|
status = cff_dict_init (&font->private_dict);
|
|
if (unlikely (status))
|
|
goto fail5;
|
|
|
|
cff_index_init (&font->strings_index);
|
|
cff_index_init (&font->charstrings_index);
|
|
cff_index_init (&font->global_sub_index);
|
|
cff_index_init (&font->local_sub_index);
|
|
cff_index_init (&font->charstrings_subset_index);
|
|
cff_index_init (&font->strings_subset_index);
|
|
font->global_subs_used = NULL;
|
|
font->local_subs_used = NULL;
|
|
font->fdselect = NULL;
|
|
font->fd_dict = NULL;
|
|
font->fd_private_dict = NULL;
|
|
font->fd_local_sub_index = NULL;
|
|
font->fdselect_subset = NULL;
|
|
font->fd_subset_map = NULL;
|
|
font->private_dict_offset = NULL;
|
|
|
|
*font_return = font;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
fail5:
|
|
_cairo_hash_table_destroy (font->top_dict);
|
|
fail4:
|
|
free (font->widths);
|
|
fail3:
|
|
if (font->font_name)
|
|
free (font->font_name);
|
|
free (font->ps_name);
|
|
fail2:
|
|
free (font->subset_font_name);
|
|
fail1:
|
|
_cairo_array_fini (&font->output);
|
|
free (font);
|
|
return status;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
cairo_cff_font_fallback_generate (cairo_cff_font_t *font,
|
|
cairo_type2_charstrings_t *type2_subset,
|
|
const char **data,
|
|
unsigned long *length)
|
|
{
|
|
cairo_int_status_t status;
|
|
cff_header_t header;
|
|
cairo_array_t *charstring;
|
|
unsigned char buf[40];
|
|
unsigned char *end_buf, *end_buf2;
|
|
unsigned int i;
|
|
int sid;
|
|
|
|
/* Create header */
|
|
header.major = 1;
|
|
header.minor = 0;
|
|
header.header_size = 4;
|
|
header.offset_size = 4;
|
|
font->header = &header;
|
|
|
|
/* Create Top Dict */
|
|
font->is_cid = FALSE;
|
|
|
|
snprintf((char*)buf, sizeof(buf), "CairoFont-%u-%u",
|
|
font->scaled_font_subset->font_id,
|
|
font->scaled_font_subset->subset_id);
|
|
sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
|
|
status = cff_index_append_copy (&font->strings_subset_index,
|
|
(unsigned char *)buf,
|
|
strlen((char*)buf));
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
end_buf = encode_integer (buf, sid);
|
|
status = cff_dict_set_operands (font->top_dict, FULLNAME_OP,
|
|
buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = cff_dict_set_operands (font->top_dict, FAMILYNAME_OP,
|
|
buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
end_buf = encode_integer (buf, type2_subset->x_min);
|
|
end_buf = encode_integer (end_buf, type2_subset->y_min);
|
|
end_buf = encode_integer (end_buf, type2_subset->x_max);
|
|
end_buf = encode_integer (end_buf, type2_subset->y_max);
|
|
status = cff_dict_set_operands (font->top_dict,
|
|
FONTBBOX_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
end_buf = encode_integer_max (buf, 0);
|
|
status = cff_dict_set_operands (font->top_dict,
|
|
CHARSTRINGS_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
|
|
if (font->scaled_font_subset->is_latin) {
|
|
status = cff_dict_set_operands (font->top_dict,
|
|
ENCODING_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
/* Private has two operands - size and offset */
|
|
end_buf2 = encode_integer_max (end_buf, 0);
|
|
cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf2 - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
} else {
|
|
status = cff_dict_set_operands (font->top_dict,
|
|
FDSELECT_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = cff_dict_set_operands (font->top_dict,
|
|
FDARRAY_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
status = cff_dict_set_operands (font->top_dict,
|
|
CHARSET_OP, buf, end_buf - buf);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (!font->scaled_font_subset->is_latin) {
|
|
status = cairo_cff_font_set_ros_strings (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
/* Create CID FD dictionary */
|
|
status = cairo_cff_font_create_cid_fontdict (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
} else {
|
|
font->private_dict_offset = malloc (sizeof (int));
|
|
if (unlikely (font->private_dict_offset == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
/* Create charstrings */
|
|
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
|
|
charstring = _cairo_array_index(&type2_subset->charstrings, i);
|
|
|
|
status = cff_index_append (&font->charstrings_subset_index,
|
|
_cairo_array_index (charstring, 0),
|
|
_cairo_array_num_elements (charstring));
|
|
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
if (font->scaled_font_subset->is_latin)
|
|
status = cairo_cff_font_add_euro_charset_string (font);
|
|
|
|
status = cairo_cff_font_write_subset (font);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
*data = _cairo_array_index (&font->output, 0);
|
|
*length = _cairo_array_num_elements (&font->output);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset,
|
|
const char *subset_name,
|
|
cairo_scaled_font_subset_t *font_subset)
|
|
{
|
|
cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */
|
|
cairo_status_t status;
|
|
const char *data = NULL; /* squelch bogus compiler warning */
|
|
unsigned long length = 0; /* squelch bogus compiler warning */
|
|
unsigned int i;
|
|
cairo_type2_charstrings_t type2_subset;
|
|
|
|
status = _cairo_cff_font_fallback_create (font_subset, &font, subset_name);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_type2_charstrings_init (&type2_subset, font_subset);
|
|
if (unlikely (status))
|
|
goto fail1;
|
|
|
|
status = cairo_cff_font_fallback_generate (font, &type2_subset, &data, &length);
|
|
if (unlikely (status))
|
|
goto fail2;
|
|
|
|
cff_subset->font_name = NULL;
|
|
cff_subset->ps_name = strdup (font->ps_name);
|
|
if (unlikely (cff_subset->ps_name == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail2;
|
|
}
|
|
|
|
cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
|
|
if (unlikely (cff_subset->widths == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail3;
|
|
}
|
|
|
|
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
|
|
cff_subset->widths[i] = (double)type2_subset.widths[i]/1000;
|
|
|
|
cff_subset->x_min = (double)type2_subset.x_min/1000;
|
|
cff_subset->y_min = (double)type2_subset.y_min/1000;
|
|
cff_subset->x_max = (double)type2_subset.x_max/1000;
|
|
cff_subset->y_max = (double)type2_subset.y_max/1000;
|
|
cff_subset->ascent = (double)type2_subset.y_max/1000;
|
|
cff_subset->descent = (double)type2_subset.y_min/1000;
|
|
|
|
cff_subset->data = malloc (length);
|
|
if (unlikely (cff_subset->data == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto fail4;
|
|
}
|
|
|
|
memcpy (cff_subset->data, data, length);
|
|
cff_subset->data_length = length;
|
|
|
|
_cairo_type2_charstrings_fini (&type2_subset);
|
|
cairo_cff_font_destroy (font);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
fail4:
|
|
free (cff_subset->widths);
|
|
fail3:
|
|
free (cff_subset->ps_name);
|
|
fail2:
|
|
_cairo_type2_charstrings_fini (&type2_subset);
|
|
fail1:
|
|
cairo_cff_font_destroy (font);
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
_cairo_cff_fallback_fini (cairo_cff_subset_t *subset)
|
|
{
|
|
free (subset->ps_name);
|
|
free (subset->widths);
|
|
free (subset->data);
|
|
}
|
|
|
|
#endif /* CAIRO_HAS_FONT_SUBSET */
|