Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 1896 → Rev 1897

/programs/develop/libraries/cairo/Makefile
8,9 → 8,9
 
DEFINES = -DHAVE_CONFIG_H -DCAIRO_NO_MUTEX -U_WIN32 -U_MSC_VER -U__WIN32__
 
INCLUDES = -I../pixman -I../newlib/include -I../newlib/include/sys
INCLUDES = -I../newlib/include -I../pixman -I../zlib -I../libpng
 
SOURCES = \
CAIRO_SOURCES = \
cairo-analysis-surface.c \
cairo-arc.c \
cairo-array.c \
55,7 → 55,9
cairo-path-in-fill.c \
cairo-path-stroke.c \
cairo-pattern.c \
cairo-pdf-operators.c \
cairo-pen.c \
cairo-png.c \
cairo-polygon.c \
cairo-rectangle.c \
cairo-rectangular-scan-converter.c \
73,6 → 75,7
cairo-surface-snapshot.c \
cairo-surface-subsurface.c \
cairo-surface-wrapper.c \
cairo-svg-surface.c \
cairo-tor-scan-converter.c \
cairo-toy-font-face.c \
cairo-traps.c \
83,8 → 86,18
$(NULL)
 
CAIRO_FONT = \
cairo-cff-subset.c \
cairo-scaled-font-subsets.c \
cairo-truetype-subset.c \
cairo-type1-fallback.c \
cairo-type1-subset.c \
cairo-type3-glyph-surface.c \
$(NULL)
 
 
SOURCES= $(CAIRO_SOURCES) $(CAIRO_FONT)
 
OBJECTS = $(patsubst %.c, src/%.o, $(SOURCES))
 
 
/programs/develop/libraries/cairo/config.h
24,11 → 24,13
/* Define to 1 to enable cairo's cairo-script-interpreter feature */
//#define CAIRO_HAS_INTERPRETER 1
 
#define CAIRO_NO_MUTEX 1
 
/* Define to 1 to enable cairo's pthread feature */
//#define CAIRO_HAS_PTHREAD 1
/* #undef CAIRO_HAS_PTHREAD */
 
/* Define to 1 if we have full pthread support */
//#define CAIRO_HAS_REAL_PTHREAD 1
/* #undef CAIRO_HAS_REAL_PTHREAD */
 
/* Define to 1 if libspectre is available */
/* #undef CAIRO_HAS_SPECTRE */
40,7 → 42,7
/* #undef CAIRO_HAS_TEST_SURFACES */
 
/* Define to 1 to enable cairo's cairo-trace feature */
//#define CAIRO_HAS_TRACE 1
/* #undef CAIRO_HAS_TRACE */
 
/* Define to 1 to disable certain code paths that rely heavily on double
precision floating-point calculation */
73,7 → 75,7
#define HAVE_DLFCN_H 1
 
/* Define to 1 if you have the `drand48' function. */
//#define HAVE_DRAND48 1
#define HAVE_DRAND48 1
 
/* Define to 1 if you have the `FcFini' function. */
/* #undef HAVE_FCFINI */
173,7 → 175,7
//#define HAVE_SCHED_H 1
 
/* Define to 1 if you have the <setjmp.h> header file. */
//#define HAVE_SETJMP_H 1
#define HAVE_SETJMP_H 1
 
/* Define to 1 if you have the <signal.h> header file. */
//#define HAVE_SIGNAL_H 1
342,7 → 344,7
#define X_DISPLAY_MISSING 1
 
/* Number of bits in a file offset, on hosts where this is settable. */
#define _FILE_OFFSET_BITS 64
/* #undef _FILE_OFFSET_BITS */
 
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
362,5 → 364,3
#ifndef __cplusplus
/* #undef inline */
#endif
 
 
/programs/develop/libraries/cairo/src/cairo-cff-subset.c
0,0 → 1,2271
/* 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 */
/programs/develop/libraries/cairo/src/cairo-pdf-operators.c
0,0 → 1,1475
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2004 Red Hat, Inc
* Copyright © 2006 Red Hat, Inc
* Copyright © 2007, 2008 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 University of Southern
* California.
*
* Contributor(s):
* Kristian Høgsberg <krh@redhat.com>
* Carl Worth <cworth@cworth.org>
* Adrian Johnson <ajohnson@redneon.com>
*/
 
#include "cairoint.h"
 
#if CAIRO_HAS_PDF_OPERATORS
 
#include "cairo-error-private.h"
#include "cairo-pdf-operators-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-scaled-font-subsets-private.h"
 
static cairo_status_t
_cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators);
 
 
void
_cairo_pdf_operators_init (cairo_pdf_operators_t *pdf_operators,
cairo_output_stream_t *stream,
cairo_matrix_t *cairo_to_pdf,
cairo_scaled_font_subsets_t *font_subsets)
{
pdf_operators->stream = stream;
pdf_operators->cairo_to_pdf = *cairo_to_pdf;
pdf_operators->font_subsets = font_subsets;
pdf_operators->use_font_subset = NULL;
pdf_operators->use_font_subset_closure = NULL;
pdf_operators->in_text_object = FALSE;
pdf_operators->num_glyphs = 0;
pdf_operators->has_line_style = FALSE;
pdf_operators->use_actual_text = FALSE;
}
 
cairo_status_t
_cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators)
{
return _cairo_pdf_operators_flush (pdf_operators);
}
 
void
_cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t *pdf_operators,
cairo_pdf_operators_use_font_subset_t use_font_subset,
void *closure)
{
pdf_operators->use_font_subset = use_font_subset;
pdf_operators->use_font_subset_closure = closure;
}
 
/* Change the output stream to a different stream.
* _cairo_pdf_operators_flush() should always be called before calling
* this function.
*/
void
_cairo_pdf_operators_set_stream (cairo_pdf_operators_t *pdf_operators,
cairo_output_stream_t *stream)
{
pdf_operators->stream = stream;
pdf_operators->has_line_style = FALSE;
}
 
void
_cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators,
cairo_matrix_t *cairo_to_pdf)
{
pdf_operators->cairo_to_pdf = *cairo_to_pdf;
pdf_operators->has_line_style = FALSE;
}
 
cairo_private void
_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators,
cairo_bool_t enable)
{
pdf_operators->use_actual_text = enable;
}
 
/* Finish writing out any pending commands to the stream. This
* function must be called by the surface before emitting anything
* into the PDF stream.
*
* pdf_operators may leave the emitted PDF for some operations
* unfinished in case subsequent operations can be merged. This
* function will finish off any incomplete operation so the stream
* will be in a state where the surface may emit its own PDF
* operations (eg changing patterns).
*
*/
cairo_status_t
_cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
if (pdf_operators->in_text_object)
status = _cairo_pdf_operators_end_text (pdf_operators);
 
return status;
}
 
/* Reset the known graphics state of the PDF consumer. ie no
* assumptions will be made about the state. The next time a
* particular graphics state is required (eg line width) the state
* operator is always emitted and then remembered for subsequent
* operatations.
*
* This should be called when starting a new stream or after emitting
* the 'Q' operator (where pdf-operators functions were called inside
* the q/Q pair).
*/
void
_cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators)
{
pdf_operators->has_line_style = FALSE;
}
 
/* A word wrap stream can be used as a filter to do word wrapping on
* top of an existing output stream. The word wrapping is quite
* simple, using isspace to determine characters that separate
* words. Any word that will cause the column count exceed the given
* max_column will have a '\n' character emitted before it.
*
* The stream is careful to maintain integrity for words that cross
* the boundary from one call to write to the next.
*
* Note: This stream does not guarantee that the output will never
* exceed max_column. In particular, if a single word is larger than
* max_column it will not be broken up.
*/
typedef struct _word_wrap_stream {
cairo_output_stream_t base;
cairo_output_stream_t *output;
int max_column;
int column;
cairo_bool_t last_write_was_space;
cairo_bool_t in_hexstring;
cairo_bool_t empty_hexstring;
} word_wrap_stream_t;
 
static int
_count_word_up_to (const unsigned char *s, int length)
{
int word = 0;
 
while (length--) {
if (! (_cairo_isspace (*s) || *s == '<')) {
s++;
word++;
} else {
return word;
}
}
 
return word;
}
 
 
/* Count up to either the end of the ASCII hexstring or the number
* of columns remaining.
*/
static int
_count_hexstring_up_to (const unsigned char *s, int length, int columns)
{
int word = 0;
 
while (length--) {
if (*s++ != '>')
word++;
else
return word;
 
columns--;
if (columns < 0 && word > 1)
return word;
}
 
return word;
}
 
static cairo_status_t
_word_wrap_stream_write (cairo_output_stream_t *base,
const unsigned char *data,
unsigned int length)
{
word_wrap_stream_t *stream = (word_wrap_stream_t *) base;
cairo_bool_t newline;
int word;
 
while (length) {
if (*data == '<') {
stream->in_hexstring = TRUE;
stream->empty_hexstring = TRUE;
stream->last_write_was_space = FALSE;
data++;
length--;
_cairo_output_stream_printf (stream->output, "<");
stream->column++;
} else if (*data == '>') {
stream->in_hexstring = FALSE;
stream->last_write_was_space = FALSE;
data++;
length--;
_cairo_output_stream_printf (stream->output, ">");
stream->column++;
} else if (_cairo_isspace (*data)) {
newline = (*data == '\n' || *data == '\r');
if (! newline && stream->column >= stream->max_column) {
_cairo_output_stream_printf (stream->output, "\n");
stream->column = 0;
}
_cairo_output_stream_write (stream->output, data, 1);
data++;
length--;
if (newline) {
stream->column = 0;
}
else
stream->column++;
stream->last_write_was_space = TRUE;
} else {
if (stream->in_hexstring) {
word = _count_hexstring_up_to (data, length,
MAX (stream->max_column - stream->column, 0));
} else {
word = _count_word_up_to (data, length);
}
/* Don't wrap if this word is a continuation of a non hex
* string word from a previous call to write. */
if (stream->column + word >= stream->max_column) {
if (stream->last_write_was_space ||
(stream->in_hexstring && !stream->empty_hexstring))
{
_cairo_output_stream_printf (stream->output, "\n");
stream->column = 0;
}
}
_cairo_output_stream_write (stream->output, data, word);
data += word;
length -= word;
stream->column += word;
stream->last_write_was_space = FALSE;
if (stream->in_hexstring)
stream->empty_hexstring = FALSE;
}
}
 
return _cairo_output_stream_get_status (stream->output);
}
 
static cairo_status_t
_word_wrap_stream_close (cairo_output_stream_t *base)
{
word_wrap_stream_t *stream = (word_wrap_stream_t *) base;
 
return _cairo_output_stream_get_status (stream->output);
}
 
static cairo_output_stream_t *
_word_wrap_stream_create (cairo_output_stream_t *output, int max_column)
{
word_wrap_stream_t *stream;
 
if (output->status)
return _cairo_output_stream_create_in_error (output->status);
 
stream = malloc (sizeof (word_wrap_stream_t));
if (unlikely (stream == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
}
 
_cairo_output_stream_init (&stream->base,
_word_wrap_stream_write,
NULL,
_word_wrap_stream_close);
stream->output = output;
stream->max_column = max_column;
stream->column = 0;
stream->last_write_was_space = FALSE;
stream->in_hexstring = FALSE;
stream->empty_hexstring = TRUE;
 
return &stream->base;
}
 
typedef struct _pdf_path_info {
cairo_output_stream_t *output;
cairo_matrix_t *path_transform;
cairo_line_cap_t line_cap;
cairo_point_t last_move_to_point;
cairo_bool_t has_sub_path;
} pdf_path_info_t;
 
static cairo_status_t
_cairo_pdf_path_move_to (void *closure,
const cairo_point_t *point)
{
pdf_path_info_t *info = closure;
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
 
info->last_move_to_point = *point;
info->has_sub_path = FALSE;
cairo_matrix_transform_point (info->path_transform, &x, &y);
_cairo_output_stream_printf (info->output,
"%g %g m ", x, y);
 
return _cairo_output_stream_get_status (info->output);
}
 
static cairo_status_t
_cairo_pdf_path_line_to (void *closure,
const cairo_point_t *point)
{
pdf_path_info_t *info = closure;
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
 
if (info->line_cap != CAIRO_LINE_CAP_ROUND &&
! info->has_sub_path &&
point->x == info->last_move_to_point.x &&
point->y == info->last_move_to_point.y)
{
return CAIRO_STATUS_SUCCESS;
}
 
info->has_sub_path = TRUE;
cairo_matrix_transform_point (info->path_transform, &x, &y);
_cairo_output_stream_printf (info->output,
"%g %g l ", x, y);
 
return _cairo_output_stream_get_status (info->output);
}
 
static cairo_status_t
_cairo_pdf_path_curve_to (void *closure,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d)
{
pdf_path_info_t *info = closure;
double bx = _cairo_fixed_to_double (b->x);
double by = _cairo_fixed_to_double (b->y);
double cx = _cairo_fixed_to_double (c->x);
double cy = _cairo_fixed_to_double (c->y);
double dx = _cairo_fixed_to_double (d->x);
double dy = _cairo_fixed_to_double (d->y);
 
info->has_sub_path = TRUE;
cairo_matrix_transform_point (info->path_transform, &bx, &by);
cairo_matrix_transform_point (info->path_transform, &cx, &cy);
cairo_matrix_transform_point (info->path_transform, &dx, &dy);
_cairo_output_stream_printf (info->output,
"%g %g %g %g %g %g c ",
bx, by, cx, cy, dx, dy);
return _cairo_output_stream_get_status (info->output);
}
 
static cairo_status_t
_cairo_pdf_path_close_path (void *closure)
{
pdf_path_info_t *info = closure;
 
if (info->line_cap != CAIRO_LINE_CAP_ROUND &&
! info->has_sub_path)
{
return CAIRO_STATUS_SUCCESS;
}
 
_cairo_output_stream_printf (info->output,
"h\n");
 
return _cairo_output_stream_get_status (info->output);
}
 
static cairo_status_t
_cairo_pdf_path_rectangle (pdf_path_info_t *info, cairo_box_t *box)
{
double x1 = _cairo_fixed_to_double (box->p1.x);
double y1 = _cairo_fixed_to_double (box->p1.y);
double x2 = _cairo_fixed_to_double (box->p2.x);
double y2 = _cairo_fixed_to_double (box->p2.y);
 
cairo_matrix_transform_point (info->path_transform, &x1, &y1);
cairo_matrix_transform_point (info->path_transform, &x2, &y2);
_cairo_output_stream_printf (info->output,
"%g %g %g %g re ",
x1, y1, x2 - x1, y2 - y1);
 
return _cairo_output_stream_get_status (info->output);
}
 
/* The line cap value is needed to workaround the fact that PostScript
* and PDF semantics for stroking degenerate sub-paths do not match
* cairo semantics. (PostScript draws something for any line cap
* value, while cairo draws something only for round caps).
*
* When using this function to emit a path to be filled, rather than
* stroked, simply pass %CAIRO_LINE_CAP_ROUND which will guarantee that
* the stroke workaround will not modify the path being emitted.
*/
static cairo_status_t
_cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
cairo_matrix_t *path_transform,
cairo_line_cap_t line_cap)
{
cairo_output_stream_t *word_wrap;
cairo_status_t status, status2;
pdf_path_info_t info;
cairo_box_t box;
 
word_wrap = _word_wrap_stream_create (pdf_operators->stream, 72);
status = _cairo_output_stream_get_status (word_wrap);
if (unlikely (status))
return _cairo_output_stream_destroy (word_wrap);
 
info.output = word_wrap;
info.path_transform = path_transform;
info.line_cap = line_cap;
if (_cairo_path_fixed_is_rectangle (path, &box)) {
status = _cairo_pdf_path_rectangle (&info, &box);
} else {
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_pdf_path_move_to,
_cairo_pdf_path_line_to,
_cairo_pdf_path_curve_to,
_cairo_pdf_path_close_path,
&info);
}
 
status2 = _cairo_output_stream_destroy (word_wrap);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
return status;
}
 
cairo_int_status_t
_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule)
{
const char *pdf_operator;
cairo_status_t status;
 
if (pdf_operators->in_text_object) {
status = _cairo_pdf_operators_end_text (pdf_operators);
if (unlikely (status))
return status;
}
 
if (! path->has_current_point) {
/* construct an empty path */
_cairo_output_stream_printf (pdf_operators->stream, "0 0 m ");
} else {
status = _cairo_pdf_operators_emit_path (pdf_operators,
path,
&pdf_operators->cairo_to_pdf,
CAIRO_LINE_CAP_ROUND);
if (unlikely (status))
return status;
}
 
switch (fill_rule) {
default:
ASSERT_NOT_REACHED;
case CAIRO_FILL_RULE_WINDING:
pdf_operator = "W";
break;
case CAIRO_FILL_RULE_EVEN_ODD:
pdf_operator = "W*";
break;
}
 
_cairo_output_stream_printf (pdf_operators->stream,
"%s n\n",
pdf_operator);
 
return _cairo_output_stream_get_status (pdf_operators->stream);
}
 
static int
_cairo_pdf_line_cap (cairo_line_cap_t cap)
{
switch (cap) {
case CAIRO_LINE_CAP_BUTT:
return 0;
case CAIRO_LINE_CAP_ROUND:
return 1;
case CAIRO_LINE_CAP_SQUARE:
return 2;
default:
ASSERT_NOT_REACHED;
return 0;
}
}
 
static int
_cairo_pdf_line_join (cairo_line_join_t join)
{
switch (join) {
case CAIRO_LINE_JOIN_MITER:
return 0;
case CAIRO_LINE_JOIN_ROUND:
return 1;
case CAIRO_LINE_JOIN_BEVEL:
return 2;
default:
ASSERT_NOT_REACHED;
return 0;
}
}
 
cairo_int_status_t
_cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators,
const cairo_stroke_style_t *style,
double scale)
{
double *dash = style->dash;
int num_dashes = style->num_dashes;
double dash_offset = style->dash_offset;
double line_width = style->line_width * scale;
 
/* PostScript has "special needs" when it comes to zero-length
* dash segments with butt caps. It apparently (at least
* according to ghostscript) draws hairlines for this
* case. That's not what the cairo semantics want, so we first
* touch up the array to eliminate any 0.0 values that will
* result in "on" segments.
*/
if (num_dashes && style->line_cap == CAIRO_LINE_CAP_BUTT) {
int i;
 
/* If there's an odd number of dash values they will each get
* interpreted as both on and off. So we first explicitly
* expand the array to remove the duplicate usage so that we
* can modify some of the values.
*/
if (num_dashes % 2) {
dash = _cairo_malloc_abc (num_dashes, 2, sizeof (double));
if (unlikely (dash == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memcpy (dash, style->dash, num_dashes * sizeof (double));
memcpy (dash + num_dashes, style->dash, num_dashes * sizeof (double));
 
num_dashes *= 2;
}
 
for (i = 0; i < num_dashes; i += 2) {
if (dash[i] == 0.0) {
/* Do not modify the dashes in-place, as we may need to also
* replay this stroke to an image fallback.
*/
if (dash == style->dash) {
dash = _cairo_malloc_ab (num_dashes, sizeof (double));
if (unlikely (dash == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
memcpy (dash, style->dash, num_dashes * sizeof (double));
}
 
/* If we're at the front of the list, we first rotate
* two elements from the end of the list to the front
* of the list before folding away the 0.0. Or, if
* there are only two dash elements, then there is
* nothing at all to draw.
*/
if (i == 0) {
double last_two[2];
 
if (num_dashes == 2) {
free (dash);
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
/* The cases of num_dashes == 0, 1, or 3 elements
* cannot exist, so the rotation of 2 elements
* will always be safe */
memcpy (last_two, dash + num_dashes - 2, sizeof (last_two));
memmove (dash + 2, dash, (num_dashes - 2) * sizeof (double));
memcpy (dash, last_two, sizeof (last_two));
dash_offset += dash[0] + dash[1];
i = 2;
}
dash[i-1] += dash[i+1];
num_dashes -= 2;
memmove (dash + i, dash + i + 2, (num_dashes - i) * sizeof (double));
/* If we might have just rotated, it's possible that
* we rotated a 0.0 value to the front of the list.
* Set i to -2 so it will get incremented to 0. */
if (i == 2)
i = -2;
}
}
}
 
if (!pdf_operators->has_line_style || pdf_operators->line_width != line_width) {
_cairo_output_stream_printf (pdf_operators->stream,
"%f w\n",
line_width);
pdf_operators->line_width = line_width;
}
 
if (!pdf_operators->has_line_style || pdf_operators->line_cap != style->line_cap) {
_cairo_output_stream_printf (pdf_operators->stream,
"%d J\n",
_cairo_pdf_line_cap (style->line_cap));
pdf_operators->line_cap = style->line_cap;
}
 
if (!pdf_operators->has_line_style || pdf_operators->line_join != style->line_join) {
_cairo_output_stream_printf (pdf_operators->stream,
"%d j\n",
_cairo_pdf_line_join (style->line_join));
pdf_operators->line_join = style->line_join;
}
 
if (num_dashes) {
int d;
 
_cairo_output_stream_printf (pdf_operators->stream, "[");
for (d = 0; d < num_dashes; d++)
_cairo_output_stream_printf (pdf_operators->stream, " %f", dash[d] * scale);
_cairo_output_stream_printf (pdf_operators->stream, "] %f d\n",
dash_offset * scale);
pdf_operators->has_dashes = TRUE;
} else if (!pdf_operators->has_line_style || pdf_operators->has_dashes) {
_cairo_output_stream_printf (pdf_operators->stream, "[] 0.0 d\n");
pdf_operators->has_dashes = FALSE;
}
if (dash != style->dash)
free (dash);
 
if (!pdf_operators->has_line_style || pdf_operators->miter_limit != style->miter_limit) {
_cairo_output_stream_printf (pdf_operators->stream,
"%f M ",
style->miter_limit < 1.0 ? 1.0 : style->miter_limit);
pdf_operators->miter_limit = style->miter_limit;
}
pdf_operators->has_line_style = TRUE;
 
return _cairo_output_stream_get_status (pdf_operators->stream);
}
 
/* Scale the matrix so the largest absolute value of the non
* translation components is 1.0. Return the scale required to restore
* the matrix to the original values.
*
* eg the matrix [ 100 0 0 50 20 10 ]
*
* is rescaled to [ 1 0 0 0.5 0.2 0.1 ]
* and the scale returned is 100
*/
static void
_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale)
{
double s;
 
s = fabs (m->xx);
if (fabs (m->xy) > s)
s = fabs (m->xy);
if (fabs (m->yx) > s)
s = fabs (m->yx);
if (fabs (m->yy) > s)
s = fabs (m->yy);
*scale = s;
s = 1.0/s;
cairo_matrix_scale (m, s, s);
}
 
static cairo_int_status_t
_cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
const char *pdf_operator)
{
cairo_status_t status;
cairo_matrix_t m, path_transform;
cairo_bool_t has_ctm = TRUE;
double scale = 1.0;
 
if (pdf_operators->in_text_object) {
status = _cairo_pdf_operators_end_text (pdf_operators);
if (unlikely (status))
return status;
}
 
/* Optimize away the stroke ctm when it does not affect the
* stroke. There are other ctm cases that could be optimized
* however this is the most common.
*/
if (fabs(ctm->xx) == 1.0 && fabs(ctm->yy) == 1.0 &&
fabs(ctm->xy) == 0.0 && fabs(ctm->yx) == 0.0)
{
has_ctm = FALSE;
}
 
/* The PDF CTM is transformed to the user space CTM when stroking
* so the corect pen shape will be used. This also requires that
* the path be transformed to user space when emitted. The
* conversion of path coordinates to user space may cause rounding
* errors. For example the device space point (1.234, 3.142) when
* transformed to a user space CTM of [100 0 0 100 0 0] will be
* emitted as (0.012, 0.031).
*
* To avoid the rounding problem we scale the user space CTM
* matrix so that all the non translation components of the matrix
* are <= 1. The line width and and dashes are scaled by the
* inverse of the scale applied to the CTM. This maintains the
* shape of the stroke pen while keeping the user space CTM within
* the range that maximizes the precision of the emitted path.
*/
if (has_ctm) {
m = *ctm;
/* Zero out the translation since it does not affect the pen
* shape however it may cause unnecessary digits to be emitted.
*/
m.x0 = 0.0;
m.y0 = 0.0;
_cairo_matrix_factor_out_scale (&m, &scale);
path_transform = m;
status = cairo_matrix_invert (&path_transform);
if (unlikely (status))
return status;
 
cairo_matrix_multiply (&m, &m, &pdf_operators->cairo_to_pdf);
}
 
status = _cairo_pdf_operators_emit_stroke_style (pdf_operators, style, scale);
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
return CAIRO_STATUS_SUCCESS;
if (unlikely (status))
return status;
 
if (has_ctm) {
_cairo_output_stream_printf (pdf_operators->stream,
"q %f %f %f %f %f %f cm\n",
m.xx, m.yx, m.xy, m.yy,
m.x0, m.y0);
} else {
path_transform = pdf_operators->cairo_to_pdf;
}
 
status = _cairo_pdf_operators_emit_path (pdf_operators,
path,
&path_transform,
style->line_cap);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (pdf_operators->stream, "%s", pdf_operator);
if (has_ctm)
_cairo_output_stream_printf (pdf_operators->stream, " Q");
 
_cairo_output_stream_printf (pdf_operators->stream, "\n");
 
return _cairo_output_stream_get_status (pdf_operators->stream);
}
 
cairo_int_status_t
_cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse)
{
return _cairo_pdf_operators_emit_stroke (pdf_operators,
path,
style,
ctm,
ctm_inverse,
"S");
}
 
cairo_int_status_t
_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule)
{
const char *pdf_operator;
cairo_status_t status;
 
if (pdf_operators->in_text_object) {
status = _cairo_pdf_operators_end_text (pdf_operators);
if (unlikely (status))
return status;
}
 
status = _cairo_pdf_operators_emit_path (pdf_operators,
path,
&pdf_operators->cairo_to_pdf,
CAIRO_LINE_CAP_ROUND);
if (unlikely (status))
return status;
 
switch (fill_rule) {
default:
ASSERT_NOT_REACHED;
case CAIRO_FILL_RULE_WINDING:
pdf_operator = "f";
break;
case CAIRO_FILL_RULE_EVEN_ODD:
pdf_operator = "f*";
break;
}
 
_cairo_output_stream_printf (pdf_operators->stream,
"%s\n",
pdf_operator);
 
return _cairo_output_stream_get_status (pdf_operators->stream);
}
 
cairo_int_status_t
_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse)
{
const char *operator;
 
switch (fill_rule) {
default:
ASSERT_NOT_REACHED;
case CAIRO_FILL_RULE_WINDING:
operator = "B";
break;
case CAIRO_FILL_RULE_EVEN_ODD:
operator = "B*";
break;
}
 
return _cairo_pdf_operators_emit_stroke (pdf_operators,
path,
style,
ctm,
ctm_inverse,
operator);
}
 
#define GLYPH_POSITION_TOLERANCE 0.001
 
/* Emit the string of glyphs using the 'Tj' operator. This requires
* that the glyphs are positioned at their natural glyph advances. */
static cairo_status_t
_cairo_pdf_operators_emit_glyph_string (cairo_pdf_operators_t *pdf_operators,
cairo_output_stream_t *stream)
{
int i;
 
_cairo_output_stream_printf (stream, "<");
for (i = 0; i < pdf_operators->num_glyphs; i++) {
_cairo_output_stream_printf (stream,
"%0*x",
pdf_operators->hex_width,
pdf_operators->glyphs[i].glyph_index);
pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
}
_cairo_output_stream_printf (stream, ">Tj\n");
 
return _cairo_output_stream_get_status (stream);
}
 
/* Emit the string of glyphs using the 'TJ' operator.
*
* The TJ operator takes an array of strings of glyphs. Each string of
* glyphs is displayed using the glyph advances of each glyph to
* position the glyphs. A relative adjustment to the glyph advance may
* be specified by including the adjustment between two strings. The
* adjustment is in units of text space * -1000.
*/
static cairo_status_t
_cairo_pdf_operators_emit_glyph_string_with_positioning (
cairo_pdf_operators_t *pdf_operators,
cairo_output_stream_t *stream)
{
int i;
 
_cairo_output_stream_printf (stream, "[<");
for (i = 0; i < pdf_operators->num_glyphs; i++) {
if (pdf_operators->glyphs[i].x_position != pdf_operators->cur_x)
{
double delta = pdf_operators->glyphs[i].x_position - pdf_operators->cur_x;
int rounded_delta;
 
delta = -1000.0*delta;
/* As the delta is in 1/1000 of a unit of text space,
* rounding to an integer should still provide sufficient
* precision. We round the delta before adding to Tm_x so
* that we keep track of the accumulated rounding error in
* the PDF interpreter and compensate for it when
* calculating subsequent deltas.
*/
rounded_delta = _cairo_lround (delta);
if (rounded_delta != 0) {
_cairo_output_stream_printf (stream,
">%d<",
rounded_delta);
}
 
/* Convert the rounded delta back to text
* space before adding to the current text
* position. */
delta = rounded_delta/-1000.0;
pdf_operators->cur_x += delta;
}
 
_cairo_output_stream_printf (stream,
"%0*x",
pdf_operators->hex_width,
pdf_operators->glyphs[i].glyph_index);
pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
}
_cairo_output_stream_printf (stream, ">]TJ\n");
 
return _cairo_output_stream_get_status (stream);
}
 
static cairo_status_t
_cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t *pdf_operators)
{
cairo_output_stream_t *word_wrap_stream;
cairo_status_t status, status2;
int i;
double x;
 
if (pdf_operators->num_glyphs == 0)
return CAIRO_STATUS_SUCCESS;
 
word_wrap_stream = _word_wrap_stream_create (pdf_operators->stream, 72);
status = _cairo_output_stream_get_status (word_wrap_stream);
if (unlikely (status))
return _cairo_output_stream_destroy (word_wrap_stream);
 
/* Check if glyph advance used to position every glyph */
x = pdf_operators->cur_x;
for (i = 0; i < pdf_operators->num_glyphs; i++) {
if (fabs(pdf_operators->glyphs[i].x_position - x) > GLYPH_POSITION_TOLERANCE)
break;
x += pdf_operators->glyphs[i].x_advance;
}
if (i == pdf_operators->num_glyphs) {
status = _cairo_pdf_operators_emit_glyph_string (pdf_operators,
word_wrap_stream);
} else {
status = _cairo_pdf_operators_emit_glyph_string_with_positioning (
pdf_operators, word_wrap_stream);
}
 
pdf_operators->num_glyphs = 0;
pdf_operators->glyph_buf_x_pos = pdf_operators->cur_x;
status2 = _cairo_output_stream_destroy (word_wrap_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
return status;
}
 
static cairo_status_t
_cairo_pdf_operators_add_glyph (cairo_pdf_operators_t *pdf_operators,
cairo_scaled_font_subsets_glyph_t *glyph,
double x_position)
{
double x, y;
 
x = glyph->x_advance;
y = glyph->y_advance;
if (glyph->is_scaled)
cairo_matrix_transform_distance (&pdf_operators->font_matrix_inverse, &x, &y);
 
pdf_operators->glyphs[pdf_operators->num_glyphs].x_position = x_position;
pdf_operators->glyphs[pdf_operators->num_glyphs].glyph_index = glyph->subset_glyph_index;
pdf_operators->glyphs[pdf_operators->num_glyphs].x_advance = x;
pdf_operators->glyph_buf_x_pos += x;
pdf_operators->num_glyphs++;
if (pdf_operators->num_glyphs == PDF_GLYPH_BUFFER_SIZE)
return _cairo_pdf_operators_flush_glyphs (pdf_operators);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* Use 'Tm' operator to set the PDF text matrix. */
static cairo_status_t
_cairo_pdf_operators_set_text_matrix (cairo_pdf_operators_t *pdf_operators,
cairo_matrix_t *matrix)
{
cairo_matrix_t inverse;
cairo_status_t status;
 
/* We require the matrix to be invertable. */
inverse = *matrix;
status = cairo_matrix_invert (&inverse);
if (unlikely (status))
return status;
 
pdf_operators->text_matrix = *matrix;
pdf_operators->cur_x = 0;
pdf_operators->cur_y = 0;
pdf_operators->glyph_buf_x_pos = 0;
_cairo_output_stream_printf (pdf_operators->stream,
"%f %f %f %f %f %f Tm\n",
pdf_operators->text_matrix.xx,
pdf_operators->text_matrix.yx,
pdf_operators->text_matrix.xy,
pdf_operators->text_matrix.yy,
pdf_operators->text_matrix.x0,
pdf_operators->text_matrix.y0);
 
pdf_operators->cairo_to_pdftext = *matrix;
status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext);
assert (status == CAIRO_STATUS_SUCCESS);
cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext,
&pdf_operators->cairo_to_pdf,
&pdf_operators->cairo_to_pdftext);
 
return _cairo_output_stream_get_status (pdf_operators->stream);
}
 
#define TEXT_MATRIX_TOLERANCE 1e-6
 
/* Set the translation components of the PDF text matrix to x, y. The
* 'Td' operator is used to transform the text matrix.
*/
static cairo_status_t
_cairo_pdf_operators_set_text_position (cairo_pdf_operators_t *pdf_operators,
double x,
double y)
{
cairo_matrix_t translate, inverse;
cairo_status_t status;
 
/* The Td operator transforms the text_matrix with:
*
* text_matrix' = T x text_matrix
*
* where T is a translation matrix with the translation components
* set to the Td operands tx and ty.
*/
inverse = pdf_operators->text_matrix;
status = cairo_matrix_invert (&inverse);
assert (status == CAIRO_STATUS_SUCCESS);
pdf_operators->text_matrix.x0 = x;
pdf_operators->text_matrix.y0 = y;
cairo_matrix_multiply (&translate, &pdf_operators->text_matrix, &inverse);
if (fabs(translate.x0) < TEXT_MATRIX_TOLERANCE)
translate.x0 = 0.0;
if (fabs(translate.y0) < TEXT_MATRIX_TOLERANCE)
translate.y0 = 0.0;
_cairo_output_stream_printf (pdf_operators->stream,
"%f %f Td\n",
translate.x0,
translate.y0);
pdf_operators->cur_x = 0;
pdf_operators->cur_y = 0;
pdf_operators->glyph_buf_x_pos = 0;
 
pdf_operators->cairo_to_pdftext = pdf_operators->text_matrix;
status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext);
assert (status == CAIRO_STATUS_SUCCESS);
cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext,
&pdf_operators->cairo_to_pdf,
&pdf_operators->cairo_to_pdftext);
 
return _cairo_output_stream_get_status (pdf_operators->stream);
}
 
/* Select the font using the 'Tf' operator. The font size is set to 1
* as we use the 'Tm' operator to set the font scale.
*/
static cairo_status_t
_cairo_pdf_operators_set_font_subset (cairo_pdf_operators_t *pdf_operators,
cairo_scaled_font_subsets_glyph_t *subset_glyph)
{
cairo_status_t status;
 
_cairo_output_stream_printf (pdf_operators->stream,
"/f-%d-%d 1 Tf\n",
subset_glyph->font_id,
subset_glyph->subset_id);
if (pdf_operators->use_font_subset) {
status = pdf_operators->use_font_subset (subset_glyph->font_id,
subset_glyph->subset_id,
pdf_operators->use_font_subset_closure);
if (unlikely (status))
return status;
}
pdf_operators->font_id = subset_glyph->font_id;
pdf_operators->subset_id = subset_glyph->subset_id;
 
if (subset_glyph->is_composite)
pdf_operators->hex_width = 4;
else
pdf_operators->hex_width = 2;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_pdf_operators_begin_text (cairo_pdf_operators_t *pdf_operators)
{
_cairo_output_stream_printf (pdf_operators->stream, "BT\n");
 
pdf_operators->in_text_object = TRUE;
pdf_operators->num_glyphs = 0;
pdf_operators->glyph_buf_x_pos = 0;
 
return _cairo_output_stream_get_status (pdf_operators->stream);
}
 
static cairo_status_t
_cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators)
{
cairo_status_t status;
 
status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (pdf_operators->stream, "ET\n");
 
pdf_operators->in_text_object = FALSE;
 
return _cairo_output_stream_get_status (pdf_operators->stream);
}
 
/* Compare the scale components of two matrices. The translation
* components are ignored. */
static cairo_bool_t
_cairo_matrix_scale_equal (cairo_matrix_t *a, cairo_matrix_t *b)
{
return (a->xx == b->xx &&
a->xy == b->xy &&
a->yx == b->yx &&
a->yy == b->yy);
}
 
static cairo_status_t
_cairo_pdf_operators_begin_actualtext (cairo_pdf_operators_t *pdf_operators,
const char *utf8,
int utf8_len)
{
uint16_t *utf16;
int utf16_len;
cairo_status_t status;
int i;
 
_cairo_output_stream_printf (pdf_operators->stream, "/Span << /ActualText <feff");
if (utf8_len) {
status = _cairo_utf8_to_utf16 (utf8, utf8_len, &utf16, &utf16_len);
if (unlikely (status))
return status;
 
for (i = 0; i < utf16_len; i++) {
_cairo_output_stream_printf (pdf_operators->stream,
"%04x", (int) (utf16[i]));
}
free (utf16);
}
_cairo_output_stream_printf (pdf_operators->stream, "> >> BDC\n");
 
return _cairo_output_stream_get_status (pdf_operators->stream);
}
 
static cairo_status_t
_cairo_pdf_operators_end_actualtext (cairo_pdf_operators_t *pdf_operators)
{
_cairo_output_stream_printf (pdf_operators->stream, "EMC\n");
 
return _cairo_output_stream_get_status (pdf_operators->stream);
}
 
static cairo_status_t
_cairo_pdf_operators_emit_glyph (cairo_pdf_operators_t *pdf_operators,
cairo_glyph_t *glyph,
cairo_scaled_font_subsets_glyph_t *subset_glyph)
{
double x, y;
cairo_status_t status;
 
if (pdf_operators->is_new_text_object ||
pdf_operators->font_id != subset_glyph->font_id ||
pdf_operators->subset_id != subset_glyph->subset_id)
{
status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
if (unlikely (status))
return status;
 
status = _cairo_pdf_operators_set_font_subset (pdf_operators, subset_glyph);
if (unlikely (status))
return status;
 
pdf_operators->is_new_text_object = FALSE;
}
 
x = glyph->x;
y = glyph->y;
cairo_matrix_transform_point (&pdf_operators->cairo_to_pdftext, &x, &y);
 
/* The TJ operator for displaying text strings can only set
* the horizontal position of the glyphs. If the y position
* (in text space) changes, use the Td operator to change the
* current position to the next glyph. We also use the Td
* operator to move the current position if the horizontal
* position changes by more than 10 (in text space
* units). This is becauses the horizontal glyph positioning
* in the TJ operator is intended for kerning and there may be
* PDF consumers that do not handle very large position
* adjustments in TJ.
*/
if (fabs(x - pdf_operators->glyph_buf_x_pos) > 10 ||
fabs(y - pdf_operators->cur_y) > GLYPH_POSITION_TOLERANCE)
{
status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
if (unlikely (status))
return status;
 
x = glyph->x;
y = glyph->y;
cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y);
status = _cairo_pdf_operators_set_text_position (pdf_operators, x, y);
if (unlikely (status))
return status;
 
x = 0.0;
y = 0.0;
}
 
status = _cairo_pdf_operators_add_glyph (pdf_operators,
subset_glyph,
x);
return status;
}
 
/* A utf8_len of -1 indicates no unicode text. A utf8_len = 0 is an
* empty string.
*/
static cairo_int_status_t
_cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font)
{
cairo_scaled_font_subsets_glyph_t subset_glyph;
cairo_glyph_t *cur_glyph;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
int i;
 
/* If the cluster maps 1 glyph to 1 or more unicode characters, we
* first try _map_glyph() with the unicode string to see if it can
* use toUnicode to map our glyph to the unicode. This will fail
* if the glyph is already mapped to a different unicode string.
*
* We also go through this path if no unicode mapping was
* supplied (utf8_len < 0).
*
* Mapping a glyph to a zero length unicode string requires the
* use of ActualText.
*/
if (num_glyphs == 1 && utf8_len != 0) {
status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets,
scaled_font,
glyphs->index,
utf8,
utf8_len,
&subset_glyph);
if (unlikely (status))
return status;
 
if (subset_glyph.utf8_is_mapped || utf8_len < 0) {
status = _cairo_pdf_operators_emit_glyph (pdf_operators,
glyphs,
&subset_glyph);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
}
 
if (pdf_operators->use_actual_text) {
/* Fallback to using ActualText to map zero or more glyphs to a
* unicode string. */
status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
if (unlikely (status))
return status;
 
status = _cairo_pdf_operators_begin_actualtext (pdf_operators, utf8, utf8_len);
if (unlikely (status))
return status;
}
 
cur_glyph = glyphs;
/* XXX
* If no glyphs, we should put *something* here for the text to be selectable. */
for (i = 0; i < num_glyphs; i++) {
status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets,
scaled_font,
cur_glyph->index,
NULL, -1,
&subset_glyph);
if (unlikely (status))
return status;
 
status = _cairo_pdf_operators_emit_glyph (pdf_operators,
cur_glyph,
&subset_glyph);
if (unlikely (status))
return status;
 
if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
cur_glyph--;
else
cur_glyph++;
}
 
if (pdf_operators->use_actual_text) {
status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
if (unlikely (status))
return status;
 
status = _cairo_pdf_operators_end_actualtext (pdf_operators);
}
 
return status;
}
 
cairo_int_status_t
_cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font)
{
cairo_status_t status;
int i;
cairo_matrix_t text_matrix, invert_y_axis;
double x, y;
const char *cur_text;
cairo_glyph_t *cur_glyph;
 
pdf_operators->font_matrix_inverse = scaled_font->font_matrix;
status = cairo_matrix_invert (&pdf_operators->font_matrix_inverse);
if (status == CAIRO_STATUS_INVALID_MATRIX)
return CAIRO_STATUS_SUCCESS;
assert (status == CAIRO_STATUS_SUCCESS);
 
pdf_operators->is_new_text_object = FALSE;
if (pdf_operators->in_text_object == FALSE) {
status = _cairo_pdf_operators_begin_text (pdf_operators);
if (unlikely (status))
return status;
 
/* Force Tm and Tf to be emitted when starting a new text
* object.*/
pdf_operators->is_new_text_object = TRUE;
}
 
cairo_matrix_init_scale (&invert_y_axis, 1, -1);
text_matrix = scaled_font->scale;
 
/* Invert y axis in font space */
cairo_matrix_multiply (&text_matrix, &text_matrix, &invert_y_axis);
 
/* Invert y axis in device space */
cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix);
 
if (pdf_operators->is_new_text_object ||
! _cairo_matrix_scale_equal (&pdf_operators->text_matrix, &text_matrix))
{
status = _cairo_pdf_operators_flush_glyphs (pdf_operators);
if (unlikely (status))
return status;
 
x = glyphs[0].x;
y = glyphs[0].y;
cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y);
text_matrix.x0 = x;
text_matrix.y0 = y;
status = _cairo_pdf_operators_set_text_matrix (pdf_operators, &text_matrix);
if (status == CAIRO_STATUS_INVALID_MATRIX)
return CAIRO_STATUS_SUCCESS;
if (unlikely (status))
return status;
}
 
if (num_clusters > 0) {
cur_text = utf8;
if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
cur_glyph = glyphs + num_glyphs;
else
cur_glyph = glyphs;
for (i = 0; i < num_clusters; i++) {
if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
cur_glyph -= clusters[i].num_glyphs;
status = _cairo_pdf_operators_emit_cluster (pdf_operators,
cur_text,
clusters[i].num_bytes,
cur_glyph,
clusters[i].num_glyphs,
cluster_flags,
scaled_font);
if (unlikely (status))
return status;
 
cur_text += clusters[i].num_bytes;
if (!(cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD))
cur_glyph += clusters[i].num_glyphs;
}
} else {
for (i = 0; i < num_glyphs; i++) {
status = _cairo_pdf_operators_emit_cluster (pdf_operators,
NULL,
-1, /* no unicode string available */
&glyphs[i],
1,
FALSE,
scaled_font);
if (unlikely (status))
return status;
}
}
 
return _cairo_output_stream_get_status (pdf_operators->stream);
}
 
#endif /* CAIRO_HAS_PDF_OPERATORS */
/programs/develop/libraries/cairo/src/cairo-png.c
0,0 → 1,798
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2003 University of Southern California
*
* 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 University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Kristian Høgsberg <krh@redhat.com>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-error-private.h"
#include "cairo-output-stream-private.h"
 
#include <stdio.h>
#include <errno.h>
#include <png.h>
 
/**
* SECTION:cairo-png
* @Title: PNG Support
* @Short_Description: Reading and writing PNG images
* @See_Also: #cairo_surface_t
*
* The PNG functions allow reading PNG images into image surfaces, and writing
* any surface to a PNG file.
*/
 
/**
* CAIRO_HAS_PNG_FUNCTIONS:
*
* Defined if the PNG functions are available.
* This macro can be used to conditionally compile code using the cairo
* PNG functions.
*/
 
struct png_read_closure_t {
cairo_read_func_t read_func;
void *closure;
cairo_output_stream_t *png_data;
};
 
 
/* Unpremultiplies data and converts native endian ARGB => RGBA bytes */
static void
unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data)
{
unsigned int i;
 
for (i = 0; i < row_info->rowbytes; i += 4) {
uint8_t *b = &data[i];
uint32_t pixel;
uint8_t alpha;
 
memcpy (&pixel, b, sizeof (uint32_t));
alpha = (pixel & 0xff000000) >> 24;
if (alpha == 0) {
b[0] = b[1] = b[2] = b[3] = 0;
} else {
b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
b[3] = alpha;
}
}
}
 
/* Converts native endian xRGB => RGBx bytes */
static void
convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data)
{
unsigned int i;
 
for (i = 0; i < row_info->rowbytes; i += 4) {
uint8_t *b = &data[i];
uint32_t pixel;
 
memcpy (&pixel, b, sizeof (uint32_t));
 
b[0] = (pixel & 0xff0000) >> 16;
b[1] = (pixel & 0x00ff00) >> 8;
b[2] = (pixel & 0x0000ff) >> 0;
b[3] = 0;
}
}
 
/* Use a couple of simple error callbacks that do not print anything to
* stderr and rely on the user to check for errors via the #cairo_status_t
* return.
*/
static void
png_simple_error_callback (png_structp png,
png_const_charp error_msg)
{
cairo_status_t *error = png_get_error_ptr (png);
 
/* default to the most likely error */
if (*error == CAIRO_STATUS_SUCCESS)
*error = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
#ifdef PNG_SETJMP_SUPPORTED
longjmp (png_jmpbuf (png), 1);
#endif
 
/* if we get here, then we have to choice but to abort ... */
}
 
static void
png_simple_warning_callback (png_structp png,
png_const_charp error_msg)
{
cairo_status_t *error = png_get_error_ptr (png);
 
/* default to the most likely error */
if (*error == CAIRO_STATUS_SUCCESS)
*error = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
/* png does not expect to abort and will try to tidy up after a warning */
}
 
 
/* Starting with libpng-1.2.30, we must explicitly specify an output_flush_fn.
* Otherwise, we will segfault if we are writing to a stream. */
static void
png_simple_output_flush_fn (png_structp png_ptr)
{
}
 
static cairo_status_t
write_png (cairo_surface_t *surface,
png_rw_ptr write_func,
void *closure)
{
int i;
cairo_status_t status;
cairo_image_surface_t *image;
cairo_image_surface_t * volatile clone;
void *image_extra;
png_struct *png;
png_info *info;
png_byte **volatile rows = NULL;
png_color_16 white;
int png_color_type;
int depth;
 
status = _cairo_surface_acquire_source_image (surface,
&image,
&image_extra);
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
else if (unlikely (status))
return status;
 
/* PNG complains about "Image width or height is zero in IHDR" */
if (image->width == 0 || image->height == 0) {
status = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
goto BAIL1;
}
 
/* Handle the various fallback formats (e.g. low bit-depth XServers)
* by coercing them to a simpler format using pixman.
*/
clone = _cairo_image_surface_coerce (image);
status = clone->base.status;
if (unlikely (status))
goto BAIL1;
 
rows = _cairo_malloc_ab (clone->height, sizeof (png_byte*));
if (unlikely (rows == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL2;
}
 
for (i = 0; i < clone->height; i++)
rows[i] = (png_byte *) clone->data + i * clone->stride;
 
png = png_create_write_struct (PNG_LIBPNG_VER_STRING, &status,
png_simple_error_callback,
png_simple_warning_callback);
if (unlikely (png == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL3;
}
 
info = png_create_info_struct (png);
if (unlikely (info == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL4;
}
 
#ifdef PNG_SETJMP_SUPPORTED
if (setjmp (png_jmpbuf (png)))
goto BAIL4;
#endif
 
png_set_write_fn (png, closure, write_func, png_simple_output_flush_fn);
 
switch (clone->format) {
case CAIRO_FORMAT_ARGB32:
depth = 8;
if (_cairo_image_analyze_transparency (clone) == CAIRO_IMAGE_IS_OPAQUE)
png_color_type = PNG_COLOR_TYPE_RGB;
else
png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
break;
case CAIRO_FORMAT_RGB24:
depth = 8;
png_color_type = PNG_COLOR_TYPE_RGB;
break;
case CAIRO_FORMAT_A8:
depth = 8;
png_color_type = PNG_COLOR_TYPE_GRAY;
break;
case CAIRO_FORMAT_A1:
depth = 1;
png_color_type = PNG_COLOR_TYPE_GRAY;
#ifndef WORDS_BIGENDIAN
png_set_packswap (png);
#endif
break;
case CAIRO_FORMAT_INVALID:
case CAIRO_FORMAT_RGB16_565:
default:
status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
goto BAIL4;
}
 
png_set_IHDR (png, info,
clone->width,
clone->height, depth,
png_color_type,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
 
white.gray = (1 << depth) - 1;
white.red = white.blue = white.green = white.gray;
png_set_bKGD (png, info, &white);
 
if (0) { /* XXX extract meta-data from surface (i.e. creation date) */
png_time pt;
 
png_convert_from_time_t (&pt, time (NULL));
png_set_tIME (png, info, &pt);
}
 
/* We have to call png_write_info() before setting up the write
* transformation, since it stores data internally in 'png'
* that is needed for the write transformation functions to work.
*/
png_write_info (png, info);
 
if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
png_set_write_user_transform_fn (png, unpremultiply_data);
} else if (png_color_type == PNG_COLOR_TYPE_RGB) {
png_set_write_user_transform_fn (png, convert_data_to_bytes);
png_set_filler (png, 0, PNG_FILLER_AFTER);
}
 
png_write_image (png, rows);
png_write_end (png, info);
 
BAIL4:
png_destroy_write_struct (&png, &info);
BAIL3:
free (rows);
BAIL2:
cairo_surface_destroy (&clone->base);
BAIL1:
_cairo_surface_release_source_image (surface, image, image_extra);
 
return status;
}
 
static void
stdio_write_func (png_structp png, png_bytep data, png_size_t size)
{
FILE *fp;
 
fp = png_get_io_ptr (png);
while (size) {
size_t ret = fwrite (data, 1, size, fp);
size -= ret;
data += ret;
if (size && ferror (fp)) {
cairo_status_t *error = png_get_error_ptr (png);
if (*error == CAIRO_STATUS_SUCCESS)
*error = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
png_error (png, NULL);
}
}
}
 
/**
* cairo_surface_write_to_png:
* @surface: a #cairo_surface_t with pixel contents
* @filename: the name of a file to write to
*
* Writes the contents of @surface to a new file @filename as a PNG
* image.
*
* Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written
* successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY if memory could not
* be allocated for the operation or
* %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have
* pixel contents, or %CAIRO_STATUS_WRITE_ERROR if an I/O error occurs
* while attempting to write the file.
**/
cairo_status_t
cairo_surface_write_to_png (cairo_surface_t *surface,
const char *filename)
{
FILE *fp;
cairo_status_t status;
 
if (surface->status)
return surface->status;
 
if (surface->finished)
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
 
fp = fopen (filename, "wb");
if (fp == NULL) {
switch (errno) {
case ENOMEM:
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
default:
return _cairo_error (CAIRO_STATUS_WRITE_ERROR);
}
}
 
status = write_png (surface, stdio_write_func, fp);
 
if (fclose (fp) && status == CAIRO_STATUS_SUCCESS)
status = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
 
return status;
}
 
struct png_write_closure_t {
cairo_write_func_t write_func;
void *closure;
};
 
static void
stream_write_func (png_structp png, png_bytep data, png_size_t size)
{
cairo_status_t status;
struct png_write_closure_t *png_closure;
 
png_closure = png_get_io_ptr (png);
status = png_closure->write_func (png_closure->closure, data, size);
if (unlikely (status)) {
cairo_status_t *error = png_get_error_ptr (png);
if (*error == CAIRO_STATUS_SUCCESS)
*error = status;
png_error (png, NULL);
}
}
 
/**
* cairo_surface_write_to_png_stream:
* @surface: a #cairo_surface_t with pixel contents
* @write_func: a #cairo_write_func_t
* @closure: closure data for the write function
*
* Writes the image surface to the write function.
*
* Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written
* successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY is returned if
* memory could not be allocated for the operation,
* %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have
* pixel contents.
**/
cairo_status_t
cairo_surface_write_to_png_stream (cairo_surface_t *surface,
cairo_write_func_t write_func,
void *closure)
{
struct png_write_closure_t png_closure;
 
if (surface->status)
return surface->status;
 
if (surface->finished)
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
 
png_closure.write_func = write_func;
png_closure.closure = closure;
 
return write_png (surface, stream_write_func, &png_closure);
}
slim_hidden_def (cairo_surface_write_to_png_stream);
 
static inline int
multiply_alpha (int alpha, int color)
{
int temp = (alpha * color) + 0x80;
return ((temp + (temp >> 8)) >> 8);
}
 
/* Premultiplies data and converts RGBA bytes => native endian */
static void
premultiply_data (png_structp png,
png_row_infop row_info,
png_bytep data)
{
unsigned int i;
 
for (i = 0; i < row_info->rowbytes; i += 4) {
uint8_t *base = &data[i];
uint8_t alpha = base[3];
uint32_t p;
 
if (alpha == 0) {
p = 0;
} else {
uint8_t red = base[0];
uint8_t green = base[1];
uint8_t blue = base[2];
 
if (alpha != 0xff) {
red = multiply_alpha (alpha, red);
green = multiply_alpha (alpha, green);
blue = multiply_alpha (alpha, blue);
}
p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
}
memcpy (base, &p, sizeof (uint32_t));
}
}
 
/* Converts RGBx bytes to native endian xRGB */
static void
convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data)
{
unsigned int i;
 
for (i = 0; i < row_info->rowbytes; i += 4) {
uint8_t *base = &data[i];
uint8_t red = base[0];
uint8_t green = base[1];
uint8_t blue = base[2];
uint32_t pixel;
 
pixel = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0);
memcpy (base, &pixel, sizeof (uint32_t));
}
}
 
static cairo_status_t
stdio_read_func (void *closure, unsigned char *data, unsigned int size)
{
FILE *file = closure;
 
while (size) {
size_t ret;
 
ret = fread (data, 1, size, file);
size -= ret;
data += ret;
 
if (size && (feof (file) || ferror (file)))
return _cairo_error (CAIRO_STATUS_READ_ERROR);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
stream_read_func (png_structp png, png_bytep data, png_size_t size)
{
cairo_status_t status;
struct png_read_closure_t *png_closure;
 
png_closure = png_get_io_ptr (png);
status = png_closure->read_func (png_closure->closure, data, size);
if (unlikely (status)) {
cairo_status_t *error = png_get_error_ptr (png);
if (*error == CAIRO_STATUS_SUCCESS)
*error = status;
png_error (png, NULL);
}
 
_cairo_output_stream_write (png_closure->png_data, data, size);
}
 
static cairo_surface_t *
read_png (struct png_read_closure_t *png_closure)
{
cairo_surface_t *surface;
png_struct *png = NULL;
png_info *info;
png_byte *data = NULL;
png_byte **row_pointers = NULL;
png_uint_32 png_width, png_height;
int depth, color_type, interlace, stride;
unsigned int i;
cairo_format_t format;
cairo_status_t status;
unsigned char *mime_data;
unsigned long mime_data_length;
 
png_closure->png_data = _cairo_memory_stream_create ();
 
/* XXX: Perhaps we'll want some other error handlers? */
png = png_create_read_struct (PNG_LIBPNG_VER_STRING,
&status,
png_simple_error_callback,
png_simple_warning_callback);
if (unlikely (png == NULL)) {
surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
goto BAIL;
}
 
info = png_create_info_struct (png);
if (unlikely (info == NULL)) {
surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
goto BAIL;
}
 
png_set_read_fn (png, png_closure, stream_read_func);
 
status = CAIRO_STATUS_SUCCESS;
#ifdef PNG_SETJMP_SUPPORTED
if (setjmp (png_jmpbuf (png))) {
surface = _cairo_surface_create_in_error (status);
goto BAIL;
}
#endif
 
png_read_info (png, info);
 
png_get_IHDR (png, info,
&png_width, &png_height, &depth,
&color_type, &interlace, NULL, NULL);
if (unlikely (status)) { /* catch any early warnings */
surface = _cairo_surface_create_in_error (status);
goto BAIL;
}
 
/* convert palette/gray image to rgb */
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb (png);
 
/* expand gray bit depth if needed */
if (color_type == PNG_COLOR_TYPE_GRAY) {
#if PNG_LIBPNG_VER >= 10209
png_set_expand_gray_1_2_4_to_8 (png);
#else
png_set_gray_1_2_4_to_8 (png);
#endif
}
 
/* transform transparency to alpha */
if (png_get_valid (png, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha (png);
 
if (depth == 16)
png_set_strip_16 (png);
 
if (depth < 8)
png_set_packing (png);
 
/* convert grayscale to RGB */
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
png_set_gray_to_rgb (png);
}
 
if (interlace != PNG_INTERLACE_NONE)
png_set_interlace_handling (png);
 
png_set_filler (png, 0xff, PNG_FILLER_AFTER);
 
/* recheck header after setting EXPAND options */
png_read_update_info (png, info);
png_get_IHDR (png, info,
&png_width, &png_height, &depth,
&color_type, &interlace, NULL, NULL);
if (depth != 8 ||
! (color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_RGB_ALPHA))
{
surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_READ_ERROR));
goto BAIL;
}
 
switch (color_type) {
default:
ASSERT_NOT_REACHED;
/* fall-through just in case ;-) */
 
case PNG_COLOR_TYPE_RGB_ALPHA:
format = CAIRO_FORMAT_ARGB32;
png_set_read_user_transform_fn (png, premultiply_data);
break;
 
case PNG_COLOR_TYPE_RGB:
format = CAIRO_FORMAT_RGB24;
png_set_read_user_transform_fn (png, convert_bytes_to_data);
break;
}
 
stride = cairo_format_stride_for_width (format, png_width);
if (stride < 0) {
surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE));
goto BAIL;
}
 
data = _cairo_malloc_ab (png_height, stride);
if (unlikely (data == NULL)) {
surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
goto BAIL;
}
 
row_pointers = _cairo_malloc_ab (png_height, sizeof (char *));
if (unlikely (row_pointers == NULL)) {
surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
goto BAIL;
}
 
for (i = 0; i < png_height; i++)
row_pointers[i] = &data[i * stride];
 
png_read_image (png, row_pointers);
png_read_end (png, info);
 
if (unlikely (status)) { /* catch any late warnings - probably hit an error already */
surface = _cairo_surface_create_in_error (status);
goto BAIL;
}
 
surface = cairo_image_surface_create_for_data (data, format,
png_width, png_height,
stride);
if (surface->status)
goto BAIL;
 
_cairo_image_surface_assume_ownership_of_data ((cairo_image_surface_t*)surface);
data = NULL;
 
_cairo_debug_check_image_surface_is_defined (surface);
 
status = _cairo_memory_stream_destroy (png_closure->png_data,
&mime_data,
&mime_data_length);
png_closure->png_data = NULL;
if (unlikely (status)) {
cairo_surface_destroy (surface);
surface = _cairo_surface_create_in_error (status);
goto BAIL;
}
 
status = cairo_surface_set_mime_data (surface,
CAIRO_MIME_TYPE_PNG,
mime_data,
mime_data_length,
free,
mime_data);
if (unlikely (status)) {
free (mime_data);
cairo_surface_destroy (surface);
surface = _cairo_surface_create_in_error (status);
goto BAIL;
}
 
BAIL:
if (row_pointers != NULL)
free (row_pointers);
if (data != NULL)
free (data);
if (png != NULL)
png_destroy_read_struct (&png, &info, NULL);
if (png_closure->png_data != NULL) {
cairo_status_t status_ignored;
 
status_ignored = _cairo_output_stream_destroy (png_closure->png_data);
}
 
return surface;
}
 
/**
* cairo_image_surface_create_from_png:
* @filename: name of PNG file to load
*
* Creates a new image surface and initializes the contents to the
* given PNG file.
*
* Return value: a new #cairo_surface_t initialized with the contents
* of the PNG file, or a "nil" surface if any error occurred. A nil
* surface can be checked for with cairo_surface_status(surface) which
* may return one of the following values:
*
* %CAIRO_STATUS_NO_MEMORY
* %CAIRO_STATUS_FILE_NOT_FOUND
* %CAIRO_STATUS_READ_ERROR
*
* Alternatively, you can allow errors to propagate through the drawing
* operations and check the status on the context upon completion
* using cairo_status().
**/
cairo_surface_t *
cairo_image_surface_create_from_png (const char *filename)
{
struct png_read_closure_t png_closure;
cairo_surface_t *surface;
 
png_closure.closure = fopen (filename, "rb");
if (png_closure.closure == NULL) {
cairo_status_t status;
switch (errno) {
case ENOMEM:
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
break;
case ENOENT:
status = _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND);
break;
default:
status = _cairo_error (CAIRO_STATUS_READ_ERROR);
break;
}
return _cairo_surface_create_in_error (status);
}
 
png_closure.read_func = stdio_read_func;
 
surface = read_png (&png_closure);
 
fclose (png_closure.closure);
 
return surface;
}
 
/**
* cairo_image_surface_create_from_png_stream:
* @read_func: function called to read the data of the file
* @closure: data to pass to @read_func.
*
* Creates a new image surface from PNG data read incrementally
* via the @read_func function.
*
* Return value: a new #cairo_surface_t initialized with the contents
* of the PNG file or a "nil" surface if the data read is not a valid PNG image
* or memory could not be allocated for the operation. A nil
* surface can be checked for with cairo_surface_status(surface) which
* may return one of the following values:
*
* %CAIRO_STATUS_NO_MEMORY
* %CAIRO_STATUS_READ_ERROR
*
* Alternatively, you can allow errors to propagate through the drawing
* operations and check the status on the context upon completion
* using cairo_status().
**/
cairo_surface_t *
cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func,
void *closure)
{
struct png_read_closure_t png_closure;
 
png_closure.read_func = read_func;
png_closure.closure = closure;
 
return read_png (&png_closure);
}
/programs/develop/libraries/cairo/src/cairo-scaled-font-subsets.c
0,0 → 1,1091
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2003 University of Southern California
* Copyright © 2005 Red Hat, Inc
* Copyright © 2006 Keith Packard
* Copyright © 2006 Red Hat, Inc
*
* 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 University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Kristian Høgsberg <krh@redhat.com>
* Keith Packard <keithp@keithp.com>
* Adrian Johnson <ajohnson@redneon.com>
*/
 
#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-user-font-private.h"
 
#define MAX_GLYPHS_PER_SIMPLE_FONT 256
#define MAX_GLYPHS_PER_COMPOSITE_FONT 65536
 
typedef enum {
CAIRO_SUBSETS_SCALED,
CAIRO_SUBSETS_SIMPLE,
CAIRO_SUBSETS_COMPOSITE
} cairo_subsets_type_t;
 
typedef enum {
CAIRO_SUBSETS_FOREACH_UNSCALED,
CAIRO_SUBSETS_FOREACH_SCALED,
CAIRO_SUBSETS_FOREACH_USER
} cairo_subsets_foreach_type_t;
 
typedef struct _cairo_sub_font {
cairo_hash_entry_t base;
 
cairo_bool_t is_scaled;
cairo_bool_t is_composite;
cairo_bool_t is_user;
cairo_scaled_font_subsets_t *parent;
cairo_scaled_font_t *scaled_font;
unsigned int font_id;
 
int current_subset;
int num_glyphs_in_current_subset;
int max_glyphs_per_subset;
 
cairo_hash_table_t *sub_font_glyphs;
struct _cairo_sub_font *next;
} cairo_sub_font_t;
 
struct _cairo_scaled_font_subsets {
cairo_subsets_type_t type;
 
int max_glyphs_per_unscaled_subset_used;
cairo_hash_table_t *unscaled_sub_fonts;
cairo_sub_font_t *unscaled_sub_fonts_list;
cairo_sub_font_t *unscaled_sub_fonts_list_end;
 
int max_glyphs_per_scaled_subset_used;
cairo_hash_table_t *scaled_sub_fonts;
cairo_sub_font_t *scaled_sub_fonts_list;
cairo_sub_font_t *scaled_sub_fonts_list_end;
 
int num_sub_fonts;
};
 
typedef struct _cairo_sub_font_glyph {
cairo_hash_entry_t base;
 
unsigned int subset_id;
unsigned int subset_glyph_index;
double x_advance;
double y_advance;
 
cairo_bool_t is_mapped;
uint32_t unicode;
char *utf8;
int utf8_len;
} cairo_sub_font_glyph_t;
 
typedef struct _cairo_sub_font_collection {
unsigned long *glyphs; /* scaled_font_glyph_index */
char **utf8;
unsigned int glyphs_size;
unsigned int max_glyph;
unsigned int num_glyphs;
 
unsigned int subset_id;
 
cairo_status_t status;
cairo_scaled_font_subset_callback_func_t font_subset_callback;
void *font_subset_callback_closure;
} cairo_sub_font_collection_t;
 
typedef struct _cairo_string_entry {
cairo_hash_entry_t base;
char *string;
} cairo_string_entry_t;
 
static cairo_status_t
_cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font,
unsigned long scaled_font_glyph_index,
const char * utf8,
int utf8_len,
cairo_scaled_font_subsets_glyph_t *subset_glyph);
 
static void
_cairo_sub_font_glyph_init_key (cairo_sub_font_glyph_t *sub_font_glyph,
unsigned long scaled_font_glyph_index)
{
sub_font_glyph->base.hash = scaled_font_glyph_index;
}
 
static cairo_bool_t
_cairo_sub_font_glyphs_equal (const void *key_a, const void *key_b)
{
const cairo_sub_font_glyph_t *sub_font_glyph_a = key_a;
const cairo_sub_font_glyph_t *sub_font_glyph_b = key_b;
 
return sub_font_glyph_a->base.hash == sub_font_glyph_b->base.hash;
}
 
static cairo_sub_font_glyph_t *
_cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index,
unsigned int subset_id,
unsigned int subset_glyph_index,
double x_advance,
double y_advance)
{
cairo_sub_font_glyph_t *sub_font_glyph;
 
sub_font_glyph = malloc (sizeof (cairo_sub_font_glyph_t));
if (unlikely (sub_font_glyph == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return NULL;
}
 
_cairo_sub_font_glyph_init_key (sub_font_glyph, scaled_font_glyph_index);
sub_font_glyph->subset_id = subset_id;
sub_font_glyph->subset_glyph_index = subset_glyph_index;
sub_font_glyph->x_advance = x_advance;
sub_font_glyph->y_advance = y_advance;
sub_font_glyph->is_mapped = FALSE;
sub_font_glyph->unicode = -1;
sub_font_glyph->utf8 = NULL;
sub_font_glyph->utf8_len = 0;
 
return sub_font_glyph;
}
 
static void
_cairo_sub_font_glyph_destroy (cairo_sub_font_glyph_t *sub_font_glyph)
{
if (sub_font_glyph->utf8 != NULL)
free (sub_font_glyph->utf8);
 
free (sub_font_glyph);
}
 
static void
_cairo_sub_font_glyph_pluck (void *entry, void *closure)
{
cairo_sub_font_glyph_t *sub_font_glyph = entry;
cairo_hash_table_t *sub_font_glyphs = closure;
 
_cairo_hash_table_remove (sub_font_glyphs, &sub_font_glyph->base);
_cairo_sub_font_glyph_destroy (sub_font_glyph);
}
 
static void
_cairo_sub_font_glyph_collect (void *entry, void *closure)
{
cairo_sub_font_glyph_t *sub_font_glyph = entry;
cairo_sub_font_collection_t *collection = closure;
unsigned long scaled_font_glyph_index;
unsigned int subset_glyph_index;
 
if (sub_font_glyph->subset_id != collection->subset_id)
return;
 
scaled_font_glyph_index = sub_font_glyph->base.hash;
subset_glyph_index = sub_font_glyph->subset_glyph_index;
 
/* Ensure we don't exceed the allocated bounds. */
assert (subset_glyph_index < collection->glyphs_size);
 
collection->glyphs[subset_glyph_index] = scaled_font_glyph_index;
collection->utf8[subset_glyph_index] = sub_font_glyph->utf8;
if (subset_glyph_index > collection->max_glyph)
collection->max_glyph = subset_glyph_index;
 
collection->num_glyphs++;
}
 
static cairo_bool_t
_cairo_sub_fonts_equal (const void *key_a, const void *key_b)
{
const cairo_sub_font_t *sub_font_a = key_a;
const cairo_sub_font_t *sub_font_b = key_b;
cairo_scaled_font_t *a = sub_font_a->scaled_font;
cairo_scaled_font_t *b = sub_font_b->scaled_font;
 
if (sub_font_a->is_scaled)
return a == b;
else
return a->font_face == b->font_face || a->original_font_face == b->original_font_face;
}
 
static void
_cairo_sub_font_init_key (cairo_sub_font_t *sub_font,
cairo_scaled_font_t *scaled_font)
{
if (sub_font->is_scaled)
{
sub_font->base.hash = (unsigned long) scaled_font;
sub_font->scaled_font = scaled_font;
}
else
{
sub_font->base.hash = (unsigned long) scaled_font->font_face;
sub_font->scaled_font = scaled_font;
}
}
 
static cairo_status_t
_cairo_sub_font_create (cairo_scaled_font_subsets_t *parent,
cairo_scaled_font_t *scaled_font,
unsigned int font_id,
int max_glyphs_per_subset,
cairo_bool_t is_scaled,
cairo_bool_t is_composite,
cairo_sub_font_t **sub_font_out)
{
cairo_sub_font_t *sub_font;
cairo_status_t status;
cairo_scaled_font_subsets_glyph_t subset_glyph;
 
sub_font = malloc (sizeof (cairo_sub_font_t));
if (unlikely (sub_font == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
sub_font->is_scaled = is_scaled;
sub_font->is_composite = is_composite;
sub_font->is_user = _cairo_font_face_is_user (scaled_font->font_face);
_cairo_sub_font_init_key (sub_font, scaled_font);
 
sub_font->parent = parent;
sub_font->scaled_font = scaled_font;
sub_font->font_id = font_id;
 
sub_font->current_subset = 0;
sub_font->num_glyphs_in_current_subset = 0;
sub_font->max_glyphs_per_subset = max_glyphs_per_subset;
 
sub_font->sub_font_glyphs = _cairo_hash_table_create (_cairo_sub_font_glyphs_equal);
if (unlikely (sub_font->sub_font_glyphs == NULL)) {
free (sub_font);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
sub_font->next = NULL;
 
/* Reserve first glyph in subset for the .notdef glyph except for
* Type 3 fonts */
if (! is_scaled) {
status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &subset_glyph);
if (unlikely (status)) {
_cairo_hash_table_destroy (sub_font->sub_font_glyphs);
free (sub_font);
return status;
}
}
 
*sub_font_out = sub_font;
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_sub_font_destroy (cairo_sub_font_t *sub_font)
{
_cairo_hash_table_foreach (sub_font->sub_font_glyphs,
_cairo_sub_font_glyph_pluck,
sub_font->sub_font_glyphs);
_cairo_hash_table_destroy (sub_font->sub_font_glyphs);
cairo_scaled_font_destroy (sub_font->scaled_font);
free (sub_font);
}
 
static void
_cairo_sub_font_pluck (void *entry, void *closure)
{
cairo_sub_font_t *sub_font = entry;
cairo_hash_table_t *sub_fonts = closure;
 
_cairo_hash_table_remove (sub_fonts, &sub_font->base);
_cairo_sub_font_destroy (sub_font);
}
 
static cairo_status_t
_cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
cairo_scaled_font_t *scaled_font,
unsigned long scaled_font_glyph_index)
{
uint32_t unicode;
char buf[8];
int len;
cairo_status_t status;
 
/* Do a reverse lookup on the glyph index. unicode is -1 if the
* index could not be mapped to a unicode character. */
unicode = -1;
status = _cairo_truetype_index_to_ucs4 (scaled_font,
scaled_font_glyph_index,
&unicode);
if (_cairo_status_is_error (status))
return status;
 
if (unicode == (uint32_t)-1 && scaled_font->backend->index_to_ucs4) {
status = scaled_font->backend->index_to_ucs4 (scaled_font,
scaled_font_glyph_index,
&unicode);
if (unlikely (status))
return status;
}
 
sub_font_glyph->unicode = unicode;
sub_font_glyph->utf8 = NULL;
sub_font_glyph->utf8_len = 0;
if (unicode != (uint32_t) -1) {
len = _cairo_ucs4_to_utf8 (unicode, buf);
if (len > 0) {
sub_font_glyph->utf8 = malloc (len + 1);
if (unlikely (sub_font_glyph->utf8 == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memcpy (sub_font_glyph->utf8, buf, len);
sub_font_glyph->utf8[len] = 0;
sub_font_glyph->utf8_len = len;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
const char *utf8,
int utf8_len,
cairo_bool_t *is_mapped)
{
*is_mapped = FALSE;
 
if (utf8_len < 0)
return CAIRO_STATUS_SUCCESS;
 
if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0')
utf8_len--;
 
if (utf8 != NULL && utf8_len != 0) {
if (sub_font_glyph->utf8 != NULL) {
if (utf8_len == sub_font_glyph->utf8_len &&
memcmp (utf8, sub_font_glyph->utf8, utf8_len) == 0)
{
/* Requested utf8 mapping matches the existing mapping */
*is_mapped = TRUE;
}
} else {
/* No existing mapping. Use the requested mapping */
sub_font_glyph->utf8 = malloc (utf8_len + 1);
if (unlikely (sub_font_glyph->utf8 == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memcpy (sub_font_glyph->utf8, utf8, utf8_len);
sub_font_glyph->utf8[utf8_len] = 0;
sub_font_glyph->utf8_len = utf8_len;
*is_mapped = TRUE;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font,
unsigned long scaled_font_glyph_index,
const char *utf8,
int utf8_len,
cairo_scaled_font_subsets_glyph_t *subset_glyph)
{
cairo_sub_font_glyph_t key, *sub_font_glyph;
cairo_int_status_t status;
 
_cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index);
sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs,
&key.base);
if (sub_font_glyph != NULL) {
subset_glyph->font_id = sub_font->font_id;
subset_glyph->subset_id = sub_font_glyph->subset_id;
subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index;
subset_glyph->is_scaled = sub_font->is_scaled;
subset_glyph->is_composite = sub_font->is_composite;
subset_glyph->x_advance = sub_font_glyph->x_advance;
subset_glyph->y_advance = sub_font_glyph->y_advance;
status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph,
utf8, utf8_len,
&subset_glyph->utf8_is_mapped);
subset_glyph->unicode = sub_font_glyph->unicode;
 
return status;
}
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static cairo_status_t
_cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font,
unsigned long scaled_font_glyph_index,
const char *utf8,
int utf8_len,
cairo_scaled_font_subsets_glyph_t *subset_glyph)
{
cairo_sub_font_glyph_t key, *sub_font_glyph;
cairo_status_t status;
 
_cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index);
sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs,
&key.base);
if (sub_font_glyph == NULL) {
cairo_scaled_glyph_t *scaled_glyph;
 
if (sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset)
{
cairo_scaled_font_subsets_glyph_t tmp_subset_glyph;
 
sub_font->current_subset++;
sub_font->num_glyphs_in_current_subset = 0;
 
/* Reserve first glyph in subset for the .notdef glyph
* except for Type 3 fonts */
if (! _cairo_font_face_is_user (sub_font->scaled_font->font_face)) {
status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &tmp_subset_glyph);
if (unlikely (status))
return status;
}
}
 
_cairo_scaled_font_freeze_cache (sub_font->scaled_font);
status = _cairo_scaled_glyph_lookup (sub_font->scaled_font,
scaled_font_glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&scaled_glyph);
assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
if (unlikely (status)) {
_cairo_scaled_font_thaw_cache (sub_font->scaled_font);
return status;
}
 
sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index,
sub_font->current_subset,
sub_font->num_glyphs_in_current_subset,
scaled_glyph->metrics.x_advance,
scaled_glyph->metrics.y_advance);
_cairo_scaled_font_thaw_cache (sub_font->scaled_font);
 
if (unlikely (sub_font_glyph == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = _cairo_sub_font_glyph_lookup_unicode (sub_font_glyph,
sub_font->scaled_font,
scaled_font_glyph_index);
if (unlikely (status)) {
_cairo_sub_font_glyph_destroy (sub_font_glyph);
return status;
}
 
status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base);
if (unlikely (status)) {
_cairo_sub_font_glyph_destroy (sub_font_glyph);
return status;
}
 
sub_font->num_glyphs_in_current_subset++;
 
if (sub_font->is_scaled) {
if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_scaled_subset_used)
sub_font->parent->max_glyphs_per_scaled_subset_used = sub_font->num_glyphs_in_current_subset;
} else {
if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_unscaled_subset_used)
sub_font->parent->max_glyphs_per_unscaled_subset_used = sub_font->num_glyphs_in_current_subset;
}
}
 
subset_glyph->font_id = sub_font->font_id;
subset_glyph->subset_id = sub_font_glyph->subset_id;
subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index;
subset_glyph->is_scaled = sub_font->is_scaled;
subset_glyph->is_composite = sub_font->is_composite;
subset_glyph->x_advance = sub_font_glyph->x_advance;
subset_glyph->y_advance = sub_font_glyph->y_advance;
status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph,
utf8, utf8_len,
&subset_glyph->utf8_is_mapped);
subset_glyph->unicode = sub_font_glyph->unicode;
 
return status;
}
 
static void
_cairo_sub_font_collect (void *entry, void *closure)
{
cairo_sub_font_t *sub_font = entry;
cairo_sub_font_collection_t *collection = closure;
cairo_scaled_font_subset_t subset;
int i;
unsigned int j;
 
if (collection->status)
return;
 
collection->status = sub_font->scaled_font->status;
if (collection->status)
return;
 
for (i = 0; i <= sub_font->current_subset; i++) {
collection->subset_id = i;
collection->num_glyphs = 0;
collection->max_glyph = 0;
 
_cairo_hash_table_foreach (sub_font->sub_font_glyphs,
_cairo_sub_font_glyph_collect, collection);
if (collection->status)
break;
if (collection->num_glyphs == 0)
continue;
 
/* Ensure the resulting array has no uninitialized holes */
assert (collection->num_glyphs == collection->max_glyph + 1);
 
subset.scaled_font = sub_font->scaled_font;
subset.is_composite = sub_font->is_composite;
subset.is_scaled = sub_font->is_scaled;
subset.font_id = sub_font->font_id;
subset.subset_id = i;
subset.glyphs = collection->glyphs;
subset.utf8 = collection->utf8;
subset.num_glyphs = collection->num_glyphs;
subset.glyph_names = NULL;
/* No need to check for out of memory here. If to_unicode is NULL, the PDF
* surface does not emit an ToUnicode stream */
subset.to_unicode = _cairo_malloc_ab (collection->num_glyphs, sizeof (unsigned long));
if (subset.to_unicode) {
for (j = 0; j < collection->num_glyphs; j++) {
/* default unicode character required when mapping fails */
subset.to_unicode[j] = 0xfffd;
}
}
collection->status = (collection->font_subset_callback) (&subset,
collection->font_subset_callback_closure);
 
if (subset.to_unicode != NULL)
free (subset.to_unicode);
 
if (subset.glyph_names != NULL) {
for (j = 0; j < collection->num_glyphs; j++)
free (subset.glyph_names[j]);
free (subset.glyph_names);
}
 
if (collection->status)
break;
}
}
 
static cairo_scaled_font_subsets_t *
_cairo_scaled_font_subsets_create_internal (cairo_subsets_type_t type)
{
cairo_scaled_font_subsets_t *subsets;
 
subsets = malloc (sizeof (cairo_scaled_font_subsets_t));
if (unlikely (subsets == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return NULL;
}
 
subsets->type = type;
subsets->max_glyphs_per_unscaled_subset_used = 0;
subsets->max_glyphs_per_scaled_subset_used = 0;
subsets->num_sub_fonts = 0;
 
subsets->unscaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal);
if (! subsets->unscaled_sub_fonts) {
free (subsets);
return NULL;
}
subsets->unscaled_sub_fonts_list = NULL;
subsets->unscaled_sub_fonts_list_end = NULL;
 
subsets->scaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal);
if (! subsets->scaled_sub_fonts) {
_cairo_hash_table_destroy (subsets->unscaled_sub_fonts);
free (subsets);
return NULL;
}
subsets->scaled_sub_fonts_list = NULL;
subsets->scaled_sub_fonts_list_end = NULL;
 
return subsets;
}
 
cairo_scaled_font_subsets_t *
_cairo_scaled_font_subsets_create_scaled (void)
{
return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SCALED);
}
 
cairo_scaled_font_subsets_t *
_cairo_scaled_font_subsets_create_simple (void)
{
return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SIMPLE);
}
 
cairo_scaled_font_subsets_t *
_cairo_scaled_font_subsets_create_composite (void)
{
return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_COMPOSITE);
}
 
void
_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *subsets)
{
_cairo_hash_table_foreach (subsets->scaled_sub_fonts, _cairo_sub_font_pluck, subsets->scaled_sub_fonts);
_cairo_hash_table_destroy (subsets->scaled_sub_fonts);
 
_cairo_hash_table_foreach (subsets->unscaled_sub_fonts, _cairo_sub_font_pluck, subsets->unscaled_sub_fonts);
_cairo_hash_table_destroy (subsets->unscaled_sub_fonts);
 
free (subsets);
}
 
cairo_status_t
_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets,
cairo_scaled_font_t *scaled_font,
unsigned long scaled_font_glyph_index,
const char * utf8,
int utf8_len,
cairo_scaled_font_subsets_glyph_t *subset_glyph)
{
cairo_sub_font_t key, *sub_font;
cairo_scaled_glyph_t *scaled_glyph;
cairo_font_face_t *font_face;
cairo_matrix_t identity;
cairo_font_options_t font_options;
cairo_scaled_font_t *unscaled_font;
cairo_status_t status;
int max_glyphs;
cairo_bool_t type1_font;
 
/* Lookup glyph in unscaled subsets */
if (subsets->type != CAIRO_SUBSETS_SCALED) {
key.is_scaled = FALSE;
_cairo_sub_font_init_key (&key, scaled_font);
sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts,
&key.base);
if (sub_font != NULL) {
status = _cairo_sub_font_lookup_glyph (sub_font,
scaled_font_glyph_index,
utf8, utf8_len,
subset_glyph);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
}
 
/* Lookup glyph in scaled subsets */
key.is_scaled = TRUE;
_cairo_sub_font_init_key (&key, scaled_font);
sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts,
&key.base);
if (sub_font != NULL) {
status = _cairo_sub_font_lookup_glyph (sub_font,
scaled_font_glyph_index,
utf8, utf8_len,
subset_glyph);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
/* Glyph not found. Determine whether the glyph is outline or
* bitmap and add to the appropriate subset.
*
* glyph_index 0 (the .notdef glyph) is a special case. Some fonts
* will return CAIRO_INT_STATUS_UNSUPPORTED when doing a
* _scaled_glyph_lookup(_GLYPH_INFO_PATH). Type1-fallback creates
* empty glyphs in this case so we can put the glyph in a unscaled
* subset. */
if (scaled_font_glyph_index == 0 ||
_cairo_font_face_is_user (scaled_font->font_face)) {
status = CAIRO_STATUS_SUCCESS;
} else {
_cairo_scaled_font_freeze_cache (scaled_font);
status = _cairo_scaled_glyph_lookup (scaled_font,
scaled_font_glyph_index,
CAIRO_SCALED_GLYPH_INFO_PATH,
&scaled_glyph);
_cairo_scaled_font_thaw_cache (scaled_font);
}
if (_cairo_status_is_error (status))
return status;
 
if (status == CAIRO_STATUS_SUCCESS &&
subsets->type != CAIRO_SUBSETS_SCALED &&
! _cairo_font_face_is_user (scaled_font->font_face))
{
/* Path available. Add to unscaled subset. */
key.is_scaled = FALSE;
_cairo_sub_font_init_key (&key, scaled_font);
sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts,
&key.base);
if (sub_font == NULL) {
font_face = cairo_scaled_font_get_font_face (scaled_font);
cairo_matrix_init_identity (&identity);
_cairo_font_options_init_default (&font_options);
cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF);
unscaled_font = cairo_scaled_font_create (font_face,
&identity,
&identity,
&font_options);
if (unlikely (unscaled_font->status))
return unscaled_font->status;
 
subset_glyph->is_scaled = FALSE;
type1_font = FALSE;
#if CAIRO_HAS_FT_FONT
type1_font = _cairo_type1_scaled_font_is_type1 (unscaled_font);
#endif
if (subsets->type == CAIRO_SUBSETS_COMPOSITE && !type1_font) {
max_glyphs = MAX_GLYPHS_PER_COMPOSITE_FONT;
subset_glyph->is_composite = TRUE;
} else {
max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT;
subset_glyph->is_composite = FALSE;
}
 
status = _cairo_sub_font_create (subsets,
unscaled_font,
subsets->num_sub_fonts,
max_glyphs,
subset_glyph->is_scaled,
subset_glyph->is_composite,
&sub_font);
 
if (unlikely (status)) {
cairo_scaled_font_destroy (unscaled_font);
return status;
}
 
status = _cairo_hash_table_insert (subsets->unscaled_sub_fonts,
&sub_font->base);
 
if (unlikely (status)) {
_cairo_sub_font_destroy (sub_font);
return status;
}
if (!subsets->unscaled_sub_fonts_list)
subsets->unscaled_sub_fonts_list = sub_font;
else
subsets->unscaled_sub_fonts_list_end->next = sub_font;
subsets->unscaled_sub_fonts_list_end = sub_font;
subsets->num_sub_fonts++;
}
} else {
/* No path available. Add to scaled subset. */
key.is_scaled = TRUE;
_cairo_sub_font_init_key (&key, scaled_font);
sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts,
&key.base);
if (sub_font == NULL) {
subset_glyph->is_scaled = TRUE;
subset_glyph->is_composite = FALSE;
if (subsets->type == CAIRO_SUBSETS_SCALED)
max_glyphs = INT_MAX;
else
max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT;
 
status = _cairo_sub_font_create (subsets,
cairo_scaled_font_reference (scaled_font),
subsets->num_sub_fonts,
max_glyphs,
subset_glyph->is_scaled,
subset_glyph->is_composite,
&sub_font);
if (unlikely (status)) {
cairo_scaled_font_destroy (scaled_font);
return status;
}
 
status = _cairo_hash_table_insert (subsets->scaled_sub_fonts,
&sub_font->base);
if (unlikely (status)) {
_cairo_sub_font_destroy (sub_font);
return status;
}
if (!subsets->scaled_sub_fonts_list)
subsets->scaled_sub_fonts_list = sub_font;
else
subsets->scaled_sub_fonts_list_end->next = sub_font;
subsets->scaled_sub_fonts_list_end = sub_font;
subsets->num_sub_fonts++;
}
}
 
return _cairo_sub_font_map_glyph (sub_font,
scaled_font_glyph_index,
utf8, utf8_len,
subset_glyph);
}
 
static cairo_status_t
_cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t *font_subsets,
cairo_scaled_font_subset_callback_func_t font_subset_callback,
void *closure,
cairo_subsets_foreach_type_t type)
{
cairo_sub_font_collection_t collection;
cairo_sub_font_t *sub_font;
cairo_bool_t is_scaled, is_user;
 
is_scaled = FALSE;
is_user = FALSE;
 
if (type == CAIRO_SUBSETS_FOREACH_USER)
is_user = TRUE;
 
if (type == CAIRO_SUBSETS_FOREACH_SCALED ||
type == CAIRO_SUBSETS_FOREACH_USER)
{
is_scaled = TRUE;
}
 
if (is_scaled)
collection.glyphs_size = font_subsets->max_glyphs_per_scaled_subset_used;
else
collection.glyphs_size = font_subsets->max_glyphs_per_unscaled_subset_used;
 
if (! collection.glyphs_size)
return CAIRO_STATUS_SUCCESS;
 
collection.glyphs = _cairo_malloc_ab (collection.glyphs_size, sizeof(unsigned long));
collection.utf8 = _cairo_malloc_ab (collection.glyphs_size, sizeof(char *));
if (unlikely (collection.glyphs == NULL || collection.utf8 == NULL)) {
if (collection.glyphs != NULL)
free (collection.glyphs);
if (collection.utf8 != NULL)
free (collection.utf8);
 
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
collection.font_subset_callback = font_subset_callback;
collection.font_subset_callback_closure = closure;
collection.status = CAIRO_STATUS_SUCCESS;
 
if (is_scaled)
sub_font = font_subsets->scaled_sub_fonts_list;
else
sub_font = font_subsets->unscaled_sub_fonts_list;
 
while (sub_font) {
if (sub_font->is_user == is_user)
_cairo_sub_font_collect (sub_font, &collection);
 
sub_font = sub_font->next;
}
free (collection.utf8);
free (collection.glyphs);
 
return collection.status;
}
 
cairo_status_t
_cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t *font_subsets,
cairo_scaled_font_subset_callback_func_t font_subset_callback,
void *closure)
{
return _cairo_scaled_font_subsets_foreach_internal (font_subsets,
font_subset_callback,
closure,
CAIRO_SUBSETS_FOREACH_SCALED);
}
 
cairo_status_t
_cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *font_subsets,
cairo_scaled_font_subset_callback_func_t font_subset_callback,
void *closure)
{
return _cairo_scaled_font_subsets_foreach_internal (font_subsets,
font_subset_callback,
closure,
CAIRO_SUBSETS_FOREACH_UNSCALED);
}
 
cairo_status_t
_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets,
cairo_scaled_font_subset_callback_func_t font_subset_callback,
void *closure)
{
return _cairo_scaled_font_subsets_foreach_internal (font_subsets,
font_subset_callback,
closure,
CAIRO_SUBSETS_FOREACH_USER);
}
 
static cairo_bool_t
_cairo_string_equal (const void *key_a, const void *key_b)
{
const cairo_string_entry_t *a = key_a;
const cairo_string_entry_t *b = key_b;
 
if (strcmp (a->string, b->string) == 0)
return TRUE;
else
return FALSE;
}
 
static void
_cairo_string_init_key (cairo_string_entry_t *key, char *s)
{
unsigned long sum = 0;
unsigned int i;
 
for (i = 0; i < strlen(s); i++)
sum += s[i];
key->base.hash = sum;
key->string = s;
}
 
static cairo_status_t
create_string_entry (char *s, cairo_string_entry_t **entry)
{
*entry = malloc (sizeof (cairo_string_entry_t));
if (unlikely (*entry == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_string_init_key (*entry, s);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_pluck_entry (void *entry, void *closure)
{
_cairo_hash_table_remove (closure, entry);
free (entry);
}
 
cairo_int_status_t
_cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset)
{
unsigned int i;
cairo_hash_table_t *names;
cairo_string_entry_t key, *entry;
char buf[30];
char *utf8;
uint16_t *utf16;
int utf16_len;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
names = _cairo_hash_table_create (_cairo_string_equal);
if (unlikely (names == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
subset->glyph_names = calloc (subset->num_glyphs, sizeof (char *));
if (unlikely (subset->glyph_names == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_HASH;
}
 
i = 0;
if (! subset->is_scaled) {
subset->glyph_names[0] = strdup (".notdef");
if (unlikely (subset->glyph_names[0] == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_HASH;
}
 
status = create_string_entry (subset->glyph_names[0], &entry);
if (unlikely (status))
goto CLEANUP_HASH;
 
status = _cairo_hash_table_insert (names, &entry->base);
if (unlikely (status)) {
free (entry);
goto CLEANUP_HASH;
}
i++;
}
 
for (; i < subset->num_glyphs; i++) {
utf8 = subset->utf8[i];
utf16 = NULL;
utf16_len = 0;
if (utf8 && *utf8) {
status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
if (unlikely (status))
goto CLEANUP_HASH;
}
 
if (utf16_len == 1) {
snprintf (buf, sizeof (buf), "uni%04X", (int) utf16[0]);
_cairo_string_init_key (&key, buf);
entry = _cairo_hash_table_lookup (names, &key.base);
if (entry != NULL)
snprintf (buf, sizeof (buf), "g%d", i);
} else {
snprintf (buf, sizeof (buf), "g%d", i);
}
if (utf16)
free (utf16);
 
subset->glyph_names[i] = strdup (buf);
if (unlikely (subset->glyph_names[i] == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_HASH;
}
 
status = create_string_entry (subset->glyph_names[i], &entry);
if (unlikely (status))
goto CLEANUP_HASH;
 
status = _cairo_hash_table_insert (names, &entry->base);
if (unlikely (status)) {
free (entry);
goto CLEANUP_HASH;
}
}
 
CLEANUP_HASH:
_cairo_hash_table_foreach (names, _pluck_entry, names);
_cairo_hash_table_destroy (names);
 
if (likely (status == CAIRO_STATUS_SUCCESS))
return CAIRO_STATUS_SUCCESS;
 
if (subset->glyph_names != NULL) {
for (i = 0; i < subset->num_glyphs; i++) {
if (subset->glyph_names[i] != NULL)
free (subset->glyph_names[i]);
}
 
free (subset->glyph_names);
subset->glyph_names = NULL;
}
 
return status;
}
 
#endif /* CAIRO_HAS_FONT_SUBSET */
/programs/develop/libraries/cairo/src/cairo-svg-surface.c
0,0 → 1,2848
/* vim: set sw=4 sts=4: -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2004 Red Hat, Inc
* Copyright © 2005-2007 Emmanuel Pacaud <emmanuel.pacaud@free.fr>
* Copyright © 2006 Red Hat, Inc
*
* 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 University of Southern
* California.
*
* Contributor(s):
* Kristian Høgsberg <krh@redhat.com>
* Emmanuel Pacaud <emmanuel.pacaud@free.fr>
* Carl Worth <cworth@cworth.org>
*/
 
#define _BSD_SOURCE /* for snprintf() */
#include "cairoint.h"
#include "cairo-svg.h"
#include "cairo-analysis-surface-private.h"
#include "cairo-error-private.h"
#include "cairo-image-info-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-paginated-private.h"
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-svg-surface-private.h"
 
/**
* SECTION:cairo-svg
* @Title: SVG Surfaces
* @Short_Description: Rendering SVG documents
* @See_Also: #cairo_surface_t
*
* The SVG surface is used to render cairo graphics to
* SVG files and is a multi-page vector surface backend.
*/
 
/**
* CAIRO_HAS_SVG_SURFACE:
*
* Defined if the SVG surface backend is available.
* This macro can be used to conditionally compile backend-specific code.
*/
 
typedef struct cairo_svg_page cairo_svg_page_t;
 
static const int invalid_pattern_id = -1;
 
static const cairo_svg_version_t _cairo_svg_versions[] =
{
CAIRO_SVG_VERSION_1_1,
CAIRO_SVG_VERSION_1_2
};
 
#define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
 
static void
_cairo_svg_surface_emit_path (cairo_output_stream_t *output,
cairo_path_fixed_t *path,
const cairo_matrix_t *ctm_inverse);
 
static cairo_bool_t
_cairo_svg_version_has_page_set_support (cairo_svg_version_t version)
{
return version > CAIRO_SVG_VERSION_1_1;
}
 
static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] =
{
"SVG 1.1",
"SVG 1.2"
};
 
static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] =
{
"1.1",
"1.2"
};
 
struct cairo_svg_page {
unsigned int surface_id;
unsigned int clip_level;
cairo_output_stream_t *xml_node;
};
 
struct cairo_svg_document {
cairo_output_stream_t *output_stream;
unsigned long refcount;
cairo_surface_t *owner;
cairo_bool_t finished;
 
double width;
double height;
 
cairo_output_stream_t *xml_node_defs;
cairo_output_stream_t *xml_node_glyphs;
 
unsigned int linear_pattern_id;
unsigned int radial_pattern_id;
unsigned int pattern_id;
unsigned int filter_id;
unsigned int clip_id;
unsigned int mask_id;
 
cairo_bool_t alpha_filter;
 
cairo_svg_version_t svg_version;
 
cairo_scaled_font_subsets_t *font_subsets;
};
 
static cairo_status_t
_cairo_svg_document_create (cairo_output_stream_t *stream,
double width,
double height,
cairo_svg_version_t version,
cairo_svg_document_t **document_out);
 
static cairo_status_t
_cairo_svg_document_destroy (cairo_svg_document_t *document);
 
static cairo_status_t
_cairo_svg_document_finish (cairo_svg_document_t *document);
 
static cairo_svg_document_t *
_cairo_svg_document_reference (cairo_svg_document_t *document);
 
static unsigned int
_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document);
 
static cairo_surface_t *
_cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
cairo_content_t content,
double width,
double height);
static cairo_surface_t *
_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
double width,
double height,
cairo_svg_version_t version);
 
static const cairo_surface_backend_t cairo_svg_surface_backend;
static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend;
 
/**
* cairo_svg_surface_create_for_stream:
* @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
* to indicate a no-op @write_func. With a no-op @write_func,
* the surface may be queried or used as a source without
* generating any temporary files.
* @closure: the closure argument for @write_func
* @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
* @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
*
* Creates a SVG surface of the specified size in points to be written
* incrementally to the stream represented by @write_func and @closure.
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.2
*/
cairo_surface_t *
cairo_svg_surface_create_for_stream (cairo_write_func_t write_func,
void *closure,
double width,
double height)
{
cairo_output_stream_t *stream;
 
stream = _cairo_output_stream_create (write_func, NULL, closure);
if (_cairo_output_stream_get_status (stream))
return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
 
return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
}
 
/**
* cairo_svg_surface_create:
* @filename: a filename for the SVG output (must be writable), %NULL may be
* used to specify no output. This will generate a SVG surface that
* may be queried and used as a source, without generating a
* temporary file.
* @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
* @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
*
* Creates a SVG surface of the specified size in points to be written
* to @filename.
*
* The SVG surface backend recognizes the following MIME types for the
* data attached to a surface (see cairo_surface_set_mime_data()) when
* it is used as a source pattern for drawing on this surface:
* %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG,
* %CAIRO_MIME_TYPE_URI. If any of them is specified, the SVG backend
* emits a href with the content of MIME data instead of a surface
* snapshot (PNG, Base64-encoded) in the corresponding image tag.
*
* The unofficial MIME type %CAIRO_MIME_TYPE_URI is examined
* first. If present, the URI is emitted as is: assuring the
* correctness of URI is left to the client code.
*
* If %CAIRO_MIME_TYPE_URI is not present, but %CAIRO_MIME_TYPE_JPEG
* or %CAIRO_MIME_TYPE_PNG is specified, the corresponding data is
* Base64-encoded and emitted.
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.2
**/
cairo_surface_t *
cairo_svg_surface_create (const char *filename,
double width,
double height)
{
cairo_output_stream_t *stream;
 
stream = _cairo_output_stream_create_for_filename (filename);
if (_cairo_output_stream_get_status (stream))
return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
 
return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1);
}
 
static cairo_bool_t
_cairo_surface_is_svg (cairo_surface_t *surface)
{
return surface->backend == &cairo_svg_surface_backend;
}
 
/* If the abstract_surface is a paginated surface, and that paginated
* surface's target is a svg_surface, then set svg_surface to that
* target. Otherwise return FALSE.
*/
static cairo_bool_t
_extract_svg_surface (cairo_surface_t *surface,
cairo_svg_surface_t **svg_surface)
{
cairo_surface_t *target;
cairo_status_t status_ignored;
 
if (surface->status)
return FALSE;
if (surface->finished) {
status_ignored = _cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return FALSE;
}
 
if (! _cairo_surface_is_paginated (surface)) {
status_ignored = _cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return FALSE;
}
 
target = _cairo_paginated_surface_get_target (surface);
if (target->status) {
status_ignored = _cairo_surface_set_error (surface,
target->status);
return FALSE;
}
if (target->finished) {
status_ignored = _cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return FALSE;
}
 
if (! _cairo_surface_is_svg (target)) {
status_ignored = _cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return FALSE;
}
 
*svg_surface = (cairo_svg_surface_t *) target;
return TRUE;
}
 
/**
* cairo_svg_surface_restrict_to_version:
* @surface: a SVG #cairo_surface_t
* @version: SVG version
*
* Restricts the generated SVG file to @version. See cairo_svg_get_versions()
* for a list of available version values that can be used here.
*
* This function should only be called before any drawing operations
* have been performed on the given surface. The simplest way to do
* this is to call this function immediately after creating the
* surface.
*
* Since: 1.2
**/
void
cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface,
cairo_svg_version_t version)
{
cairo_svg_surface_t *surface = NULL; /* hide compiler warning */
 
if (! _extract_svg_surface (abstract_surface, &surface))
return;
 
if (version < CAIRO_SVG_VERSION_LAST)
surface->document->svg_version = version;
}
 
/**
* cairo_svg_get_versions:
* @versions: supported version list
* @num_versions: list length
*
* Used to retrieve the list of supported versions. See
* cairo_svg_surface_restrict_to_version().
*
* Since: 1.2
**/
void
cairo_svg_get_versions (cairo_svg_version_t const **versions,
int *num_versions)
{
if (versions != NULL)
*versions = _cairo_svg_versions;
 
if (num_versions != NULL)
*num_versions = CAIRO_SVG_VERSION_LAST;
}
 
/**
* cairo_svg_version_to_string:
* @version: a version id
*
* Get the string representation of the given @version id. This function
* will return %NULL if @version isn't valid. See cairo_svg_get_versions()
* for a way to get the list of valid version ids.
*
* Return value: the string associated to given version.
*
* Since: 1.2
**/
const char *
cairo_svg_version_to_string (cairo_svg_version_t version)
{
if (version >= CAIRO_SVG_VERSION_LAST)
return NULL;
 
return _cairo_svg_version_strings[version];
}
 
static cairo_bool_t
_cliprect_covers_surface (cairo_svg_surface_t *surface,
cairo_path_fixed_t *path)
{
cairo_box_t box;
 
if (_cairo_path_fixed_is_box (path, &box)) {
if (box.p1.x <= 0 &&
box.p1.y <= 0 &&
_cairo_fixed_to_double (box.p2.x) >= surface->width &&
_cairo_fixed_to_double (box.p2.y) >= surface->height)
{
return TRUE;
}
}
 
return FALSE;
}
 
static cairo_status_t
_cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_svg_surface_t *surface = cairo_container_of (clipper,
cairo_svg_surface_t,
clipper);
cairo_svg_document_t *document = surface->document;
unsigned int i;
 
if (path == NULL) {
for (i = 0; i < surface->clip_level; i++)
_cairo_output_stream_printf (surface->xml_node, "</g>\n");
 
surface->clip_level = 0;
return CAIRO_STATUS_SUCCESS;
}
 
/* skip trivial whole-page clips */
if (_cliprect_covers_surface (surface, path))
return CAIRO_STATUS_SUCCESS;
 
_cairo_output_stream_printf (document->xml_node_defs,
"<clipPath id=\"clip%d\">\n"
" <path ",
document->clip_id);
_cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL);
 
_cairo_output_stream_printf (document->xml_node_defs,
"/>\n"
"</clipPath>\n");
 
_cairo_output_stream_printf (surface->xml_node,
"<g clip-path=\"url(#clip%d)\" "
"clip-rule=\"%s\">\n",
document->clip_id,
fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
"evenodd" : "nonzero");
 
document->clip_id++;
surface->clip_level++;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_surface_t *
_cairo_svg_surface_create_for_document (cairo_svg_document_t *document,
cairo_content_t content,
double width,
double height)
{
cairo_svg_surface_t *surface;
cairo_surface_t *paginated;
cairo_status_t status, status_ignored;
 
surface = malloc (sizeof (cairo_svg_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&surface->base,
&cairo_svg_surface_backend,
NULL, /* device */
content);
 
surface->width = width;
surface->height = height;
 
surface->document = _cairo_svg_document_reference (document);
 
surface->clip_level = 0;
_cairo_surface_clipper_init (&surface->clipper,
_cairo_svg_surface_clipper_intersect_clip_path);
 
surface->base_clip = document->clip_id++;
surface->is_base_clip_emitted = FALSE;
 
surface->xml_node = _cairo_memory_stream_create ();
status = _cairo_output_stream_get_status (surface->xml_node);
if (unlikely (status))
goto CLEANUP;
 
_cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t));
 
if (content == CAIRO_CONTENT_COLOR) {
_cairo_output_stream_printf (surface->xml_node,
"<rect width=\"%f\" height=\"%f\" "
"style=\"opacity:1;stroke:none;"
"fill:rgb(0,0,0);\"/>\n",
width, height);
status = _cairo_output_stream_get_status (surface->xml_node);
if (unlikely (status))
goto CLEANUP;
}
 
surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
surface->force_fallbacks = FALSE;
surface->content = content;
 
paginated = _cairo_paginated_surface_create (&surface->base,
surface->content,
&cairo_svg_surface_paginated_backend);
status = paginated->status;
if (status == CAIRO_STATUS_SUCCESS) {
/* paginated keeps the only reference to surface now, drop ours */
cairo_surface_destroy (&surface->base);
return paginated;
}
 
/* ignore status as we are on the error path */
CLEANUP:
status_ignored = _cairo_output_stream_destroy (surface->xml_node);
status_ignored = _cairo_svg_document_destroy (document);
 
free (surface);
 
return _cairo_surface_create_in_error (status);
}
 
static cairo_surface_t *
_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream,
double width,
double height,
cairo_svg_version_t version)
{
cairo_svg_document_t *document = NULL; /* silence compiler */
cairo_surface_t *surface;
cairo_status_t status;
 
status = _cairo_svg_document_create (stream,
width, height, version,
&document);
if (unlikely (status)) {
surface = _cairo_surface_create_in_error (status);
/* consume the output stream on behalf of caller */
status = _cairo_output_stream_destroy (stream);
return surface;
}
 
surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA,
width, height);
if (surface->status) {
status = _cairo_svg_document_destroy (document);
return surface;
}
 
document->owner = surface;
status = _cairo_svg_document_destroy (document);
/* the ref count should be 2 at this point */
assert (status == CAIRO_STATUS_SUCCESS);
 
return surface;
}
 
static cairo_svg_page_t *
_cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
{
unsigned int i;
cairo_svg_page_t page;
cairo_output_stream_t *stream;
cairo_status_t status;
 
stream = _cairo_memory_stream_create ();
if (_cairo_output_stream_get_status (stream)) {
status = _cairo_output_stream_destroy (stream);
return NULL;
}
 
page.surface_id = surface->base.unique_id;
page.clip_level = surface->clip_level;
page.xml_node = surface->xml_node;
 
if (_cairo_array_append (&surface->page_set, &page)) {
status = _cairo_output_stream_destroy (stream);
return NULL;
}
 
surface->xml_node = stream;
surface->clip_level = 0;
for (i = 0; i < page.clip_level; i++)
_cairo_output_stream_printf (page.xml_node, "</g>\n");
 
_cairo_surface_clipper_reset (&surface->clipper);
 
return _cairo_array_index (&surface->page_set,
surface->page_set.num_elements - 1);
}
 
static cairo_int_status_t
_cairo_svg_surface_copy_page (void *abstract_surface)
{
cairo_svg_surface_t *surface = abstract_surface;
cairo_svg_page_t *page;
 
page = _cairo_svg_surface_store_page (surface);
if (unlikely (page == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_memory_stream_copy (page->xml_node, surface->xml_node);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_svg_surface_show_page (void *abstract_surface)
{
cairo_svg_surface_t *surface = abstract_surface;
 
if (unlikely (_cairo_svg_surface_store_page (surface) == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_svg_surface_emit_transform (cairo_output_stream_t *output,
char const *attribute_str,
const cairo_matrix_t *object_matrix,
const cairo_matrix_t *parent_matrix)
{
cairo_matrix_t matrix = *object_matrix;
 
if (parent_matrix != NULL)
cairo_matrix_multiply (&matrix, &matrix, parent_matrix);
 
if (!_cairo_matrix_is_identity (&matrix))
_cairo_output_stream_printf (output,
"%s=\"matrix(%f,%f,%f,%f,%f,%f)\"",
attribute_str,
matrix.xx, matrix.yx,
matrix.xy, matrix.yy,
matrix.x0, matrix.y0);
}
 
typedef struct {
cairo_output_stream_t *output;
const cairo_matrix_t *ctm_inverse;
} svg_path_info_t;
 
static cairo_status_t
_cairo_svg_path_move_to (void *closure,
const cairo_point_t *point)
{
svg_path_info_t *info = closure;
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
 
if (info->ctm_inverse)
cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
 
_cairo_output_stream_printf (info->output, "M %f %f ", x, y);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_svg_path_line_to (void *closure,
const cairo_point_t *point)
{
svg_path_info_t *info = closure;
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
 
if (info->ctm_inverse)
cairo_matrix_transform_point (info->ctm_inverse, &x, &y);
 
_cairo_output_stream_printf (info->output, "L %f %f ", x, y);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_svg_path_curve_to (void *closure,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d)
{
svg_path_info_t *info = closure;
double bx = _cairo_fixed_to_double (b->x);
double by = _cairo_fixed_to_double (b->y);
double cx = _cairo_fixed_to_double (c->x);
double cy = _cairo_fixed_to_double (c->y);
double dx = _cairo_fixed_to_double (d->x);
double dy = _cairo_fixed_to_double (d->y);
 
if (info->ctm_inverse) {
cairo_matrix_transform_point (info->ctm_inverse, &bx, &by);
cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy);
cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy);
}
 
_cairo_output_stream_printf (info->output,
"C %f %f %f %f %f %f ",
bx, by, cx, cy, dx, dy);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_svg_path_close_path (void *closure)
{
svg_path_info_t *info = closure;
 
_cairo_output_stream_printf (info->output, "Z ");
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_svg_surface_emit_path (cairo_output_stream_t *output,
cairo_path_fixed_t *path,
const cairo_matrix_t *ctm_inverse)
{
cairo_status_t status;
svg_path_info_t info;
 
_cairo_output_stream_printf (output, "d=\"");
 
info.output = output;
info.ctm_inverse = ctm_inverse;
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_svg_path_move_to,
_cairo_svg_path_line_to,
_cairo_svg_path_curve_to,
_cairo_svg_path_close_path,
&info);
assert (status == CAIRO_STATUS_SUCCESS);
 
_cairo_output_stream_printf (output, "\"");
}
 
static cairo_int_status_t
_cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document,
cairo_scaled_font_t *scaled_font,
unsigned long glyph_index)
{
cairo_scaled_glyph_t *scaled_glyph;
cairo_int_status_t status;
 
status = _cairo_scaled_glyph_lookup (scaled_font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS|
CAIRO_SCALED_GLYPH_INFO_PATH,
&scaled_glyph);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (document->xml_node_glyphs,
"<path style=\"stroke:none;\" ");
 
_cairo_svg_surface_emit_path (document->xml_node_glyphs,
scaled_glyph->path, NULL);
 
_cairo_output_stream_printf (document->xml_node_glyphs,
"/>\n");
 
return status;
}
 
static cairo_int_status_t
_cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document,
cairo_scaled_font_t *scaled_font,
unsigned long glyph_index)
{
cairo_scaled_glyph_t *scaled_glyph;
cairo_image_surface_t *image;
cairo_status_t status;
uint8_t *row, *byte;
int rows, cols;
int x, y, bit;
 
status = _cairo_scaled_glyph_lookup (scaled_font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS |
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
if (unlikely (status))
return status;
 
image = _cairo_image_surface_coerce_to_format (scaled_glyph->surface,
CAIRO_FORMAT_A1);
status = image->base.status;
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (document->xml_node_glyphs, "<g");
_cairo_svg_surface_emit_transform (document->xml_node_glyphs, " transform",
&image->base.device_transform_inverse, NULL);
_cairo_output_stream_printf (document->xml_node_glyphs, ">/n");
 
for (y = 0, row = image->data, rows = image->height; rows; row += image->stride, rows--, y++) {
for (x = 0, byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
for (bit = 7; bit >= 0 && x < image->width; bit--, x++) {
if (output_byte & (1 << bit)) {
_cairo_output_stream_printf (document->xml_node_glyphs,
"<rect x=\"%d\" y=\"%d\" width=\"1\" height=\"1\"/>\n",
x, y);
}
}
}
}
_cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n");
 
cairo_surface_destroy (&image->base);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
cairo_scaled_font_t *scaled_font,
unsigned long scaled_font_glyph_index,
unsigned int font_id,
unsigned int subset_glyph_index)
{
cairo_status_t status;
 
_cairo_output_stream_printf (document->xml_node_glyphs,
"<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n",
font_id,
subset_glyph_index);
 
status = _cairo_svg_document_emit_outline_glyph_data (document,
scaled_font,
scaled_font_glyph_index);
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
status = _cairo_svg_document_emit_bitmap_glyph_data (document,
scaled_font,
scaled_font_glyph_index);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
void *closure)
{
cairo_svg_document_t *document = closure;
unsigned int i;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
_cairo_scaled_font_freeze_cache (font_subset->scaled_font);
for (i = 0; i < font_subset->num_glyphs; i++) {
status = _cairo_svg_document_emit_glyph (document,
font_subset->scaled_font,
font_subset->glyphs[i],
font_subset->font_id, i);
if (unlikely (status))
break;
}
_cairo_scaled_font_thaw_cache (font_subset->scaled_font);
 
return status;
}
 
static cairo_status_t
_cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document)
{
cairo_status_t status;
 
status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets,
_cairo_svg_document_emit_font_subset,
document);
if (unlikely (status))
goto FAIL;
 
status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets,
_cairo_svg_document_emit_font_subset,
document);
 
FAIL:
_cairo_scaled_font_subsets_destroy (document->font_subsets);
document->font_subsets = NULL;
 
return status;
}
 
static char const *
_cairo_svg_surface_operators[] = {
"clear",
 
"src", "src-over", "src-in",
"src-out", "src-atop",
 
"dst", "dst-over", "dst-in",
"dst-out", "dst-atop",
 
"xor", "plus",
"color-dodge", /* FIXME: saturate ? */
 
"multiply", "screen", "overlay",
"darken", "lighten",
"color-dodge", "color-burn",
"hard-light", "soft-light",
"difference", "exclusion"
};
 
static cairo_bool_t
_cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface,
cairo_operator_t op)
{
/* guard against newly added operators */
if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* allow operators being NULL if they are unsupported */
if (_cairo_svg_surface_operators[op] == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *pattern)
{
cairo_svg_document_t *document = surface->document;
 
if (surface->force_fallbacks &&
surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
/* SVG doesn't support extend reflect for image pattern */
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
pattern->extend == CAIRO_EXTEND_REFLECT)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (document->svg_version >= CAIRO_SVG_VERSION_1_2)
return _cairo_svg_surface_analyze_operator (surface, op);
 
if (op == CAIRO_OPERATOR_OVER)
return CAIRO_STATUS_SUCCESS;
 
/* The SOURCE operator is only supported if there is nothing
* painted underneath. */
if (op == CAIRO_OPERATOR_SOURCE)
return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static cairo_int_status_t
_cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *pattern)
{
return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static cairo_status_t
_cairo_svg_surface_finish (void *abstract_surface)
{
cairo_status_t status, status2;
cairo_svg_surface_t *surface = abstract_surface;
cairo_svg_document_t *document = surface->document;
cairo_svg_page_t *page;
unsigned int i;
 
if (_cairo_paginated_surface_get_target (document->owner) == &surface->base)
status = _cairo_svg_document_finish (document);
else
status = CAIRO_STATUS_SUCCESS;
 
if (surface->xml_node != NULL) {
status2 = _cairo_output_stream_destroy (surface->xml_node);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
}
 
for (i = 0; i < surface->page_set.num_elements; i++) {
page = _cairo_array_index (&surface->page_set, i);
status2 = _cairo_output_stream_destroy (page->xml_node);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
}
_cairo_array_fini (&surface->page_set);
 
_cairo_surface_clipper_reset (&surface->clipper);
 
status2 = _cairo_svg_document_destroy (document);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
return status;
}
 
 
static void
_cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document)
{
if (document->alpha_filter)
return;
 
_cairo_output_stream_printf (document->xml_node_defs,
"<filter id=\"alpha\" "
"filterUnits=\"objectBoundingBox\" "
"x=\"0%%\" y=\"0%%\" "
"width=\"100%%\" height=\"100%%\">\n"
" <feColorMatrix type=\"matrix\" "
"in=\"SourceGraphic\" "
"values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n"
"</filter>\n");
 
document->alpha_filter = TRUE;
}
 
typedef struct {
cairo_output_stream_t *output;
unsigned int in_mem;
unsigned int trailing;
unsigned char src[3];
} base64_write_closure_t;
 
static char const base64_table[64] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
static cairo_status_t
base64_write_func (void *closure,
const unsigned char *data,
unsigned int length)
{
base64_write_closure_t *info = (base64_write_closure_t *) closure;
unsigned int i;
unsigned char *src;
 
src = info->src;
 
if (info->in_mem + length < 3) {
for (i = 0; i < length; i++) {
src[i + info->in_mem] = *data++;
}
info->in_mem += length;
return CAIRO_STATUS_SUCCESS;
}
 
do {
unsigned char dst[4];
 
for (i = info->in_mem; i < 3; i++) {
src[i] = *data++;
length--;
}
info->in_mem = 0;
 
dst[0] = base64_table[src[0] >> 2];
dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4];
dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6];
dst[3] = base64_table[src[2] & 0xfc >> 2];
/* Special case for the last missing bits */
switch (info->trailing) {
case 2:
dst[2] = '=';
case 1:
dst[3] = '=';
default:
break;
}
_cairo_output_stream_write (info->output, dst, 4);
} while (length >= 3);
 
for (i = 0; i < length; i++) {
src[i] = *data++;
}
info->in_mem = length;
 
return _cairo_output_stream_get_status (info->output);
}
 
static cairo_int_status_t
_cairo_surface_base64_encode_jpeg (cairo_surface_t *surface,
cairo_output_stream_t *output)
{
const unsigned char *mime_data;
unsigned long mime_data_length;
cairo_image_info_t image_info;
base64_write_closure_t info;
cairo_status_t status;
 
cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG,
&mime_data, &mime_data_length);
if (mime_data == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (output, "data:image/jpeg;base64,");
 
info.output = output;
info.in_mem = 0;
info.trailing = 0;
 
status = base64_write_func (&info, mime_data, mime_data_length);
if (unlikely (status))
return status;
 
if (info.in_mem > 0) {
memset (info.src + info.in_mem, 0, 3 - info.in_mem);
info.trailing = 3 - info.in_mem;
info.in_mem = 3;
status = base64_write_func (&info, NULL, 0);
}
 
return status;
}
 
static cairo_int_status_t
_cairo_surface_base64_encode_png (cairo_surface_t *surface,
cairo_output_stream_t *output)
{
const unsigned char *mime_data;
unsigned long mime_data_length;
base64_write_closure_t info;
cairo_status_t status;
 
cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG,
&mime_data, &mime_data_length);
if (unlikely (surface->status))
return surface->status;
if (mime_data == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
_cairo_output_stream_printf (output, "data:image/png;base64,");
 
info.output = output;
info.in_mem = 0;
info.trailing = 0;
 
status = base64_write_func (&info, mime_data, mime_data_length);
if (unlikely (status))
return status;
 
if (info.in_mem > 0) {
memset (info.src + info.in_mem, 0, 3 - info.in_mem);
info.trailing = 3 - info.in_mem;
info.in_mem = 3;
status = base64_write_func (&info, NULL, 0);
}
 
return status;
}
 
static cairo_int_status_t
_cairo_surface_base64_encode (cairo_surface_t *surface,
cairo_output_stream_t *output)
{
cairo_status_t status;
base64_write_closure_t info;
 
status = _cairo_surface_base64_encode_jpeg (surface, output);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
status = _cairo_surface_base64_encode_png (surface, output);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
info.output = output;
info.in_mem = 0;
info.trailing = 0;
 
_cairo_output_stream_printf (info.output, "data:image/png;base64,");
 
status = cairo_surface_write_to_png_stream (surface, base64_write_func,
(void *) &info);
 
if (unlikely (status))
return status;
 
if (info.in_mem > 0) {
memset (info.src + info.in_mem, 0, 3 - info.in_mem);
info.trailing = 3 - info.in_mem;
info.in_mem = 3;
status = base64_write_func (&info, NULL, 0);
}
 
return status;
}
 
static void
_cairo_svg_surface_emit_operator (cairo_output_stream_t *output,
cairo_svg_surface_t *surface,
cairo_operator_t op)
{
if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
op != CAIRO_OPERATOR_OVER) {
_cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]);
if (!_cairo_operator_bounded_by_source (op))
_cairo_output_stream_printf (output, " clip-to-self=\"true\"");
}
}
 
static void
_cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output,
cairo_svg_surface_t *surface,
cairo_operator_t op)
{
if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 &&
op != CAIRO_OPERATOR_OVER) {
_cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]);
if (!_cairo_operator_bounded_by_source (op))
_cairo_output_stream_printf (output, "clip-to-self:true;");
}
}
 
/**
* _cairo_svg_surface_emit_attr_value:
*
* Write the value to output the stream as a sequence of characters,
* while escaping those which have special meaning in the XML
* attribute's value context: &amp; and &quot;.
**/
static void
_cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream,
const unsigned char *value,
unsigned int length)
{
const unsigned char *p;
const unsigned char *q;
unsigned int i;
 
/* we'll accumulate non-special chars in [q, p) range */
p = value;
q = p;
for (i = 0; i < length; i++, p++) {
if (*p == '&' || *p == '"') {
/* flush what's left before special char */
if (p != q) {
_cairo_output_stream_write (stream, q, p - q);
q = p + 1;
}
 
if (*p == '&')
_cairo_output_stream_printf (stream, "&amp;");
else // p == '"'
_cairo_output_stream_printf (stream, "&quot;");
}
}
 
/* flush the trailing chars if any */
if (p != q)
_cairo_output_stream_write (stream, q, p - q);
}
 
static cairo_status_t
_cairo_svg_surface_emit_surface (cairo_svg_document_t *document,
cairo_surface_t *surface)
{
cairo_rectangle_int_t extents;
cairo_bool_t is_bounded;
cairo_status_t status;
const unsigned char *uri;
unsigned long uri_len;
 
if (_cairo_user_data_array_get_data (&surface->user_data,
(cairo_user_data_key_t *) document))
{
return CAIRO_STATUS_SUCCESS;
}
 
is_bounded = _cairo_surface_get_extents (surface, &extents);
assert (is_bounded);
 
_cairo_output_stream_printf (document->xml_node_defs,
"<image id=\"image%d\" width=\"%d\" height=\"%d\"",
surface->unique_id,
extents.width, extents.height);
 
_cairo_output_stream_printf (document->xml_node_defs, " xlink:href=\"");
 
cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_URI,
&uri, &uri_len);
if (uri != NULL) {
_cairo_svg_surface_emit_attr_value (document->xml_node_defs,
uri, uri_len);
} else {
status = _cairo_surface_base64_encode (surface,
document->xml_node_defs);
if (unlikely (status))
return status;
}
 
_cairo_output_stream_printf (document->xml_node_defs, "\"/>\n");
 
/* and tag it */
return _cairo_user_data_array_set_data (&surface->user_data,
(cairo_user_data_key_t *) document,
document, NULL);
}
 
static cairo_status_t
_cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *output,
cairo_svg_surface_t *svg_surface,
cairo_operator_t op,
cairo_surface_pattern_t *pattern,
int pattern_id,
const cairo_matrix_t *parent_matrix,
const char *extra_attributes)
{
cairo_status_t status;
cairo_matrix_t p2u;
 
p2u = pattern->base.matrix;
status = cairo_matrix_invert (&p2u);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
status = _cairo_svg_surface_emit_surface (svg_surface->document,
pattern->surface);
if (unlikely (status))
return status;
 
if (pattern_id != invalid_pattern_id) {
cairo_rectangle_int_t extents;
cairo_bool_t is_bounded;
 
is_bounded = _cairo_surface_get_extents (pattern->surface, &extents);
assert (is_bounded);
 
_cairo_output_stream_printf (output,
"<pattern id=\"pattern%d\" "
"patternUnits=\"userSpaceOnUse\" "
"width=\"%d\" height=\"%d\" ",
pattern_id,
extents.width, extents.height);
_cairo_svg_surface_emit_transform (output,
" patternTransform",
&p2u, parent_matrix);
_cairo_output_stream_printf (output, ">\n ");
}
 
_cairo_output_stream_printf (output,
"<use xlink:href=\"#image%d\"",
pattern->surface->unique_id);
if (extra_attributes)
_cairo_output_stream_printf (output, " %s", extra_attributes);
 
if (pattern_id == invalid_pattern_id) {
_cairo_svg_surface_emit_operator (output, svg_surface, op);
_cairo_svg_surface_emit_transform (output,
" transform",
&p2u, parent_matrix);
}
_cairo_output_stream_printf (output, "/>\n");
 
 
if (pattern_id != invalid_pattern_id)
_cairo_output_stream_printf (output, "</pattern>\n");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document,
cairo_recording_surface_t *source)
{
cairo_status_t status;
cairo_surface_t *paginated_surface;
cairo_svg_surface_t *svg_surface;
cairo_array_t *page_set;
 
cairo_output_stream_t *contents;
 
if (_cairo_user_data_array_get_data (&source->base.user_data,
(cairo_user_data_key_t *) document))
{
return CAIRO_STATUS_SUCCESS;
}
 
paginated_surface = _cairo_svg_surface_create_for_document (document,
source->content,
source->extents_pixels.width,
source->extents_pixels.height);
if (unlikely (paginated_surface->status))
return paginated_surface->status;
 
svg_surface = (cairo_svg_surface_t *)
_cairo_paginated_surface_get_target (paginated_surface);
cairo_surface_set_fallback_resolution (paginated_surface,
document->owner->x_fallback_resolution,
document->owner->y_fallback_resolution);
cairo_surface_set_device_offset (&svg_surface->base,
-source->extents_pixels.x,
-source->extents_pixels.y);
 
status = _cairo_recording_surface_replay (&source->base, paginated_surface);
if (unlikely (status)) {
cairo_surface_destroy (paginated_surface);
return status;
}
 
cairo_surface_show_page (paginated_surface);
status = cairo_surface_status (paginated_surface);
if (unlikely (status)) {
cairo_surface_destroy (paginated_surface);
return status;
}
 
if (! svg_surface->is_base_clip_emitted) {
svg_surface->is_base_clip_emitted = TRUE;
_cairo_output_stream_printf (document->xml_node_defs,
"<clipPath id=\"clip%d\">\n"
" <rect width=\"%f\" height=\"%f\"/>\n"
"</clipPath>\n",
svg_surface->base_clip,
svg_surface->width,
svg_surface->height);
}
 
if (source->content == CAIRO_CONTENT_ALPHA) {
_cairo_svg_surface_emit_alpha_filter (document);
_cairo_output_stream_printf (document->xml_node_defs,
"<g id=\"surface%d\" "
"clip-path=\"url(#clip%d)\" "
"filter=\"url(#alpha)\">\n",
source->base.unique_id,
svg_surface->base_clip);
} else {
_cairo_output_stream_printf (document->xml_node_defs,
"<g id=\"surface%d\" "
"clip-path=\"url(#clip%d)\">\n",
source->base.unique_id,
svg_surface->base_clip);
}
 
contents = svg_surface->xml_node;
page_set = &svg_surface->page_set;
 
if (_cairo_memory_stream_length (contents) > 0) {
if (unlikely (_cairo_svg_surface_store_page (svg_surface) == NULL)) {
cairo_surface_destroy (paginated_surface);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
 
if (page_set->num_elements > 0) {
cairo_svg_page_t *page;
 
page = _cairo_array_index (page_set, page_set->num_elements - 1);
_cairo_memory_stream_copy (page->xml_node, document->xml_node_defs);
}
 
_cairo_output_stream_printf (document->xml_node_defs, "</g>\n");
 
status = cairo_surface_status (paginated_surface);
cairo_surface_destroy (paginated_surface);
 
if (unlikely (status))
return status;
 
/* and tag it */
return _cairo_user_data_array_set_data (&source->base.user_data,
(cairo_user_data_key_t *) document,
document, NULL);
}
 
static cairo_status_t
_cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *output,
cairo_svg_surface_t *surface,
cairo_operator_t op,
cairo_surface_pattern_t *pattern,
int pattern_id,
const cairo_matrix_t *parent_matrix,
const char *extra_attributes)
{
cairo_svg_document_t *document = surface->document;
cairo_recording_surface_t *recording_surface;
cairo_matrix_t p2u;
cairo_status_t status;
 
p2u = pattern->base.matrix;
status = cairo_matrix_invert (&p2u);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
recording_surface = (cairo_recording_surface_t *) pattern->surface;
status = _cairo_svg_surface_emit_recording_surface (document, recording_surface);
if (unlikely (status))
return status;
 
if (pattern_id != invalid_pattern_id) {
_cairo_output_stream_printf (output,
"<pattern id=\"pattern%d\" "
"patternUnits=\"userSpaceOnUse\" "
"width=\"%d\" height=\"%d\"",
pattern_id,
recording_surface->extents.width,
recording_surface->extents.height);
_cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix);
_cairo_output_stream_printf (output, ">\n");
}
 
_cairo_output_stream_printf (output,
"<use xlink:href=\"#surface%d\"",
recording_surface->base.unique_id);
 
if (pattern_id == invalid_pattern_id) {
_cairo_svg_surface_emit_operator (output, surface, op);
_cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
}
 
if (extra_attributes)
_cairo_output_stream_printf (output, " %s", extra_attributes);
 
_cairo_output_stream_printf (output, "/>\n");
 
if (pattern_id != invalid_pattern_id)
_cairo_output_stream_printf (output, "</pattern>\n");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output,
cairo_svg_surface_t *surface,
cairo_operator_t op,
cairo_surface_pattern_t *pattern,
int pattern_id,
const cairo_matrix_t *parent_matrix,
const char *extra_attributes)
{
 
if (_cairo_surface_is_recording (pattern->surface)) {
return _cairo_svg_surface_emit_composite_recording_pattern (output, surface,
op, pattern,
pattern_id,
parent_matrix,
extra_attributes);
}
 
return _cairo_svg_surface_emit_composite_surface_pattern (output, surface,
op, pattern,
pattern_id,
parent_matrix,
extra_attributes);
}
 
static cairo_status_t
_cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface,
cairo_solid_pattern_t *pattern,
cairo_output_stream_t *style,
cairo_bool_t is_stroke)
{
_cairo_output_stream_printf (style, is_stroke ?
"stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;":
"fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;",
pattern->color.red * 100.0,
pattern->color.green * 100.0,
pattern->color.blue * 100.0,
pattern->color.alpha);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface,
cairo_surface_pattern_t *pattern,
cairo_output_stream_t *style,
cairo_bool_t is_stroke,
const cairo_matrix_t *parent_matrix)
{
cairo_svg_document_t *document = surface->document;
cairo_status_t status;
int pattern_id;
 
pattern_id = document->pattern_id++;
status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs,
surface, CAIRO_OPERATOR_SOURCE, pattern,
pattern_id, parent_matrix, NULL);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (style,
"%s:url(#pattern%d);",
is_stroke ? "stroke" : "fill",
pattern_id);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output,
cairo_gradient_pattern_t const *pattern,
double start_offset,
cairo_bool_t reverse_stops,
cairo_bool_t emulate_reflect)
{
cairo_gradient_stop_t *stops;
double offset;
unsigned int n_stops;
unsigned int i;
 
if (pattern->n_stops < 1)
return CAIRO_STATUS_SUCCESS;
 
if (pattern->n_stops == 1) {
_cairo_output_stream_printf (output,
"<stop offset=\"%f\" style=\""
"stop-color:rgb(%f%%,%f%%,%f%%);"
"stop-opacity:%f;\"/>\n",
pattern->stops[0].offset,
pattern->stops[0].color.red * 100.0,
pattern->stops[0].color.green * 100.0,
pattern->stops[0].color.blue * 100.0,
pattern->stops[0].color.alpha);
return CAIRO_STATUS_SUCCESS;
}
 
if (emulate_reflect || reverse_stops) {
n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops;
stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t));
if (unlikely (stops == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
for (i = 0; i < pattern->n_stops; i++) {
if (reverse_stops) {
stops[i] = pattern->stops[pattern->n_stops - i - 1];
stops[i].offset = 1.0 - stops[i].offset;
} else
stops[i] = pattern->stops[i];
if (emulate_reflect) {
stops[i].offset /= 2;
if (i > 0 && i < (pattern->n_stops - 1)) {
if (reverse_stops) {
stops[i + pattern->n_stops - 1] = pattern->stops[i];
stops[i + pattern->n_stops - 1].offset =
0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset;
} else {
stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1];
stops[i + pattern->n_stops - 1].offset =
1 - 0.5 * stops[i + pattern->n_stops - 1].offset;
}
}
}
}
} else {
n_stops = pattern->n_stops;
stops = pattern->stops;
}
 
if (start_offset >= 0.0)
for (i = 0; i < n_stops; i++) {
offset = start_offset + (1 - start_offset ) * stops[i].offset;
_cairo_output_stream_printf (output,
"<stop offset=\"%f\" style=\""
"stop-color:rgb(%f%%,%f%%,%f%%);"
"stop-opacity:%f;\"/>\n",
offset,
stops[i].color.red * 100.0,
stops[i].color.green * 100.0,
stops[i].color.blue * 100.0,
stops[i].color.alpha);
}
else {
cairo_bool_t found = FALSE;
unsigned int offset_index;
cairo_color_stop_t offset_color_start, offset_color_stop;
 
for (i = 0; i < n_stops; i++) {
if (stops[i].offset >= -start_offset) {
if (i > 0) {
if (stops[i].offset != stops[i-1].offset) {
double x0, x1;
cairo_color_stop_t *color0, *color1;
 
x0 = stops[i-1].offset;
x1 = stops[i].offset;
color0 = &stops[i-1].color;
color1 = &stops[i].color;
offset_color_start.red = color0->red + (color1->red - color0->red)
* (-start_offset - x0) / (x1 - x0);
offset_color_start.green = color0->green + (color1->green - color0->green)
* (-start_offset - x0) / (x1 - x0);
offset_color_start.blue = color0->blue + (color1->blue - color0->blue)
* (-start_offset - x0) / (x1 - x0);
offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha)
* (-start_offset - x0) / (x1 - x0);
offset_color_stop = offset_color_start;
} else {
offset_color_stop = stops[i-1].color;
offset_color_start = stops[i].color;
}
} else
offset_color_stop = offset_color_start = stops[i].color;
offset_index = i;
found = TRUE;
break;
}
}
 
if (!found) {
offset_index = n_stops - 1;
offset_color_stop = offset_color_start = stops[offset_index].color;
}
 
_cairo_output_stream_printf (output,
"<stop offset=\"0\" style=\""
"stop-color:rgb(%f%%,%f%%,%f%%);"
"stop-opacity:%f;\"/>\n",
offset_color_start.red * 100.0,
offset_color_start.green * 100.0,
offset_color_start.blue * 100.0,
offset_color_start.alpha);
for (i = offset_index; i < n_stops; i++) {
_cairo_output_stream_printf (output,
"<stop offset=\"%f\" style=\""
"stop-color:rgb(%f%%,%f%%,%f%%);"
"stop-opacity:%f;\"/>\n",
stops[i].offset + start_offset,
stops[i].color.red * 100.0,
stops[i].color.green * 100.0,
stops[i].color.blue * 100.0,
stops[i].color.alpha);
}
for (i = 0; i < offset_index; i++) {
_cairo_output_stream_printf (output,
"<stop offset=\"%f\" style=\""
"stop-color:rgb(%f%%,%f%%,%f%%);"
"stop-opacity:%f;\"/>\n",
1.0 + stops[i].offset + start_offset,
stops[i].color.red * 100.0,
stops[i].color.green * 100.0,
stops[i].color.blue * 100.0,
stops[i].color.alpha);
}
 
_cairo_output_stream_printf (output,
"<stop offset=\"1\" style=\""
"stop-color:rgb(%f%%,%f%%,%f%%);"
"stop-opacity:%f;\"/>\n",
offset_color_stop.red * 100.0,
offset_color_stop.green * 100.0,
offset_color_stop.blue * 100.0,
offset_color_stop.alpha);
 
}
 
if (reverse_stops || emulate_reflect)
free (stops);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output,
cairo_pattern_t *pattern)
{
switch (pattern->extend) {
case CAIRO_EXTEND_REPEAT:
_cairo_output_stream_printf (output, "spreadMethod=\"repeat\" ");
break;
case CAIRO_EXTEND_REFLECT:
_cairo_output_stream_printf (output, "spreadMethod=\"reflect\" ");
break;
case CAIRO_EXTEND_NONE:
case CAIRO_EXTEND_PAD:
break;
}
}
 
static cairo_status_t
_cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface,
cairo_linear_pattern_t *pattern,
cairo_output_stream_t *style,
cairo_bool_t is_stroke,
const cairo_matrix_t *parent_matrix)
{
cairo_svg_document_t *document = surface->document;
double x0, y0, x1, y1;
cairo_matrix_t p2u;
cairo_status_t status;
 
p2u = pattern->base.base.matrix;
status = cairo_matrix_invert (&p2u);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
x0 = _cairo_fixed_to_double (pattern->p1.x);
y0 = _cairo_fixed_to_double (pattern->p1.y);
x1 = _cairo_fixed_to_double (pattern->p2.x);
y1 = _cairo_fixed_to_double (pattern->p2.y);
 
_cairo_output_stream_printf (document->xml_node_defs,
"<linearGradient id=\"linear%d\" "
"gradientUnits=\"userSpaceOnUse\" "
"x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
document->linear_pattern_id,
x0, y0, x1, y1);
 
_cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
_cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
_cairo_output_stream_printf (document->xml_node_defs, ">\n");
 
status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
&pattern->base, 0.0,
FALSE, FALSE);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (document->xml_node_defs,
"</linearGradient>\n");
 
_cairo_output_stream_printf (style,
"%s:url(#linear%d);",
is_stroke ? "stroke" : "fill",
document->linear_pattern_id);
 
document->linear_pattern_id++;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface,
cairo_radial_pattern_t *pattern,
cairo_output_stream_t *style,
cairo_bool_t is_stroke,
const cairo_matrix_t *parent_matrix)
{
cairo_svg_document_t *document = surface->document;
cairo_matrix_t p2u;
cairo_extend_t extend;
double x0, y0, x1, y1, r0, r1;
double fx, fy;
cairo_bool_t reverse_stops;
cairo_status_t status;
cairo_point_t *c0, *c1;
cairo_fixed_t radius0, radius1;
 
extend = pattern->base.base.extend;
 
if (pattern->r1 < pattern->r2) {
c0 = &pattern->c1;
c1 = &pattern->c2;
radius0 = pattern->r1;
radius1 = pattern->r2;
reverse_stops = FALSE;
} else {
c0 = &pattern->c2;
c1 = &pattern->c1;
radius0 = pattern->r2;
radius1 = pattern->r1;
reverse_stops = TRUE;
}
 
x0 = _cairo_fixed_to_double (c0->x);
y0 = _cairo_fixed_to_double (c0->y);
r0 = _cairo_fixed_to_double (radius0);
x1 = _cairo_fixed_to_double (c1->x);
y1 = _cairo_fixed_to_double (c1->y);
r1 = _cairo_fixed_to_double (radius1);
 
p2u = pattern->base.base.matrix;
status = cairo_matrix_invert (&p2u);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
if (pattern->r1 == pattern->r2) {
unsigned int n_stops = pattern->base.n_stops;
 
_cairo_output_stream_printf (document->xml_node_defs,
"<radialGradient id=\"radial%d\" "
"gradientUnits=\"userSpaceOnUse\" "
"cx=\"%f\" cy=\"%f\" "
"fx=\"%f\" fy=\"%f\" r=\"%f\" ",
document->radial_pattern_id,
x1, y1,
x1, y1, r1);
_cairo_svg_surface_emit_transform (document->xml_node_defs,
"gradientTransform",
&p2u, parent_matrix);
_cairo_output_stream_printf (document->xml_node_defs, ">\n");
 
if (extend == CAIRO_EXTEND_NONE || n_stops < 1)
_cairo_output_stream_printf (document->xml_node_defs,
"<stop offset=\"0\" style=\""
"stop-color:rgb(0%%,0%%,0%%);"
"stop-opacity:0;\"/>\n");
else {
_cairo_output_stream_printf (document->xml_node_defs,
"<stop offset=\"0\" style=\""
"stop-color:rgb(%f%%,%f%%,%f%%);"
"stop-opacity %f;\"/>\n",
pattern->base.stops[0].color.red * 100.0,
pattern->base.stops[0].color.green * 100.0,
pattern->base.stops[0].color.blue * 100.0,
pattern->base.stops[0].color.alpha);
if (n_stops > 1)
_cairo_output_stream_printf (document->xml_node_defs,
"<stop offset=\"0\" style=\""
"stop-color:rgb(%f%%,%f%%,%f%%);"
"stop-opacity:%f;\"/>\n",
pattern->base.stops[n_stops - 1].color.red * 100.0,
pattern->base.stops[n_stops - 1].color.green * 100.0,
pattern->base.stops[n_stops - 1].color.blue * 100.0,
pattern->base.stops[n_stops - 1].color.alpha);
}
 
} else {
double offset, r, x, y;
cairo_bool_t emulate_reflect = FALSE;
 
fx = (r1 * x0 - r0 * x1) / (r1 - r0);
fy = (r1 * y0 - r0 * y1) / (r1 - r0);
 
/* SVG doesn't support the inner circle and use instead a gradient focal.
* That means we need to emulate the cairo behaviour by processing the
* cairo gradient stops.
* The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
* it's just a matter of stop position translation and calculation of
* the corresponding SVG radial gradient focal.
* The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
* radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
* case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
* list that maps to the original cairo stop list.
*/
if ((extend == CAIRO_EXTEND_REFLECT
|| extend == CAIRO_EXTEND_REPEAT)
&& r0 > 0.0) {
double r_org = r1;
 
if (extend == CAIRO_EXTEND_REFLECT) {
r1 = 2 * r1 - r0;
emulate_reflect = TRUE;
}
 
offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
r = r1 - r0;
 
/* New position of outer circle. */
x = r * (x1 - fx) / r_org + fx;
y = r * (y1 - fy) / r_org + fy;
 
x1 = x;
y1 = y;
r1 = r;
r0 = 0.0;
} else {
offset = r0 / r1;
}
 
_cairo_output_stream_printf (document->xml_node_defs,
"<radialGradient id=\"radial%d\" "
"gradientUnits=\"userSpaceOnUse\" "
"cx=\"%f\" cy=\"%f\" "
"fx=\"%f\" fy=\"%f\" r=\"%f\" ",
document->radial_pattern_id,
x1, y1,
fx, fy, r1);
 
if (emulate_reflect)
_cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" ");
else
_cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base);
_cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
_cairo_output_stream_printf (document->xml_node_defs, ">\n");
 
/* To support cairo's EXTEND_NONE, (for which SVG has no similar
* notion), we add transparent color stops on either end of the
* user-provided stops. */
if (extend == CAIRO_EXTEND_NONE) {
_cairo_output_stream_printf (document->xml_node_defs,
"<stop offset=\"0\" style=\""
"stop-color:rgb(0%%,0%%,0%%);"
"stop-opacity:0;\"/>\n");
if (r0 != 0.0)
_cairo_output_stream_printf (document->xml_node_defs,
"<stop offset=\"%f\" style=\""
"stop-color:rgb(0%%,0%%,0%%);"
"stop-opacity:0;\"/>\n",
r0 / r1);
}
status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs,
&pattern->base, offset,
reverse_stops,
emulate_reflect);
if (unlikely (status))
return status;
 
if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
_cairo_output_stream_printf (document->xml_node_defs,
"<stop offset=\"1.0\" style=\""
"stop-color:rgb(0%%,0%%,0%%);"
"stop-opacity:0;\"/>\n");
}
 
_cairo_output_stream_printf (document->xml_node_defs,
"</radialGradient>\n");
 
_cairo_output_stream_printf (style,
"%s:url(#radial%d);",
is_stroke ? "stroke" : "fill",
document->radial_pattern_id);
 
document->radial_pattern_id++;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface,
const cairo_pattern_t *pattern,
cairo_output_stream_t *output,
cairo_bool_t is_stroke,
const cairo_matrix_t *parent_matrix)
{
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern,
output, is_stroke);
 
case CAIRO_PATTERN_TYPE_SURFACE:
return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern,
output, is_stroke, parent_matrix);
 
case CAIRO_PATTERN_TYPE_LINEAR:
return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern,
output, is_stroke, parent_matrix);
 
case CAIRO_PATTERN_TYPE_RADIAL:
return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern,
output, is_stroke, parent_matrix);
}
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
}
 
static cairo_status_t
_cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output,
cairo_svg_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_fill_rule_t fill_rule,
const cairo_matrix_t *parent_matrix)
{
_cairo_output_stream_printf (output,
"fill-rule:%s;",
fill_rule == CAIRO_FILL_RULE_EVEN_ODD ?
"evenodd" : "nonzero");
_cairo_svg_surface_emit_operator_for_style (output, surface, op);
return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix);
}
 
static cairo_status_t
_cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output,
cairo_svg_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *parent_matrix)
{
cairo_status_t status;
const char *line_cap, *line_join;
unsigned int i;
 
switch (stroke_style->line_cap) {
case CAIRO_LINE_CAP_BUTT:
line_cap = "butt";
break;
case CAIRO_LINE_CAP_ROUND:
line_cap = "round";
break;
case CAIRO_LINE_CAP_SQUARE:
line_cap = "square";
break;
default:
ASSERT_NOT_REACHED;
}
 
switch (stroke_style->line_join) {
case CAIRO_LINE_JOIN_MITER:
line_join = "miter";
break;
case CAIRO_LINE_JOIN_ROUND:
line_join = "round";
break;
case CAIRO_LINE_JOIN_BEVEL:
line_join = "bevel";
break;
default:
ASSERT_NOT_REACHED;
}
 
_cairo_output_stream_printf (output,
"stroke-width:%f;"
"stroke-linecap:%s;"
"stroke-linejoin:%s;",
stroke_style->line_width,
line_cap,
line_join);
 
status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix);
if (unlikely (status))
return status;
 
_cairo_svg_surface_emit_operator_for_style (output, surface, op);
 
if (stroke_style->num_dashes > 0) {
_cairo_output_stream_printf (output, "stroke-dasharray:");
for (i = 0; i < stroke_style->num_dashes; i++) {
_cairo_output_stream_printf (output, "%f",
stroke_style->dash[i]);
if (i + 1 < stroke_style->num_dashes)
_cairo_output_stream_printf (output, ",");
else
_cairo_output_stream_printf (output, ";");
}
if (stroke_style->dash_offset != 0.0) {
_cairo_output_stream_printf (output,
"stroke-dashoffset:%f;",
stroke_style->dash_offset);
}
}
 
_cairo_output_stream_printf (output,
"stroke-miterlimit:%f;",
stroke_style->miter_limit);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_svg_surface_fill_stroke (void *abstract_surface,
cairo_operator_t fill_op,
const cairo_pattern_t *fill_source,
cairo_fill_rule_t fill_rule,
double fill_tolerance,
cairo_antialias_t fill_antialias,
cairo_path_fixed_t *path,
cairo_operator_t stroke_op,
const cairo_pattern_t *stroke_source,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *stroke_ctm,
const cairo_matrix_t *stroke_ctm_inverse,
double stroke_tolerance,
cairo_antialias_t stroke_antialias,
cairo_clip_t *clip)
{
cairo_svg_surface_t *surface = abstract_surface;
cairo_status_t status;
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->xml_node, "<path style=\"");
status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, fill_op,
fill_source, fill_rule, stroke_ctm_inverse);
if (unlikely (status))
return status;
 
status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op,
stroke_source, stroke_style, stroke_ctm_inverse);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->xml_node, "\" ");
 
_cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse);
 
_cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL);
_cairo_output_stream_printf (surface->xml_node, "/>\n");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_svg_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
cairo_svg_surface_t *surface = abstract_surface;
cairo_status_t status;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
return _cairo_svg_surface_analyze_operation (surface, op, source);
 
assert (_cairo_svg_surface_operation_supported (surface, op, source));
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->xml_node, "<path style=\" stroke:none;");
status = _cairo_svg_surface_emit_fill_style (surface->xml_node, surface, op, source, fill_rule, NULL);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->xml_node, "\" ");
 
_cairo_svg_surface_emit_path (surface->xml_node, path, NULL);
 
_cairo_output_stream_printf (surface->xml_node, "/>\n");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
_cairo_svg_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_svg_surface_t *surface = abstract_surface;
 
rectangle->x = 0;
rectangle->y = 0;
 
/* XXX: The conversion to integers here is pretty bogus, (not to
* mention the arbitrary limitation of width to a short(!). We
* may need to come up with a better interface for get_size.
*/
rectangle->width = ceil (surface->width);
rectangle->height = ceil (surface->height);
 
return TRUE;
}
 
static cairo_status_t
_cairo_svg_surface_emit_paint (cairo_output_stream_t *output,
cairo_svg_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask_source,
const char *extra_attributes)
{
cairo_status_t status;
 
if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
source->extend == CAIRO_EXTEND_NONE)
return _cairo_svg_surface_emit_composite_pattern (output,
surface,
op,
(cairo_surface_pattern_t *) source,
invalid_pattern_id,
mask_source ? &mask_source->matrix :NULL,
extra_attributes);
 
_cairo_output_stream_printf (output,
"<rect x=\"0\" y=\"0\" "
"width=\"%f\" height=\"%f\" "
"style=\"",
surface->width, surface->height);
_cairo_svg_surface_emit_operator_for_style (output, surface, op);
status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (output, "stroke:none;\"");
 
if (extra_attributes)
_cairo_output_stream_printf (output, " %s", extra_attributes);
 
_cairo_output_stream_printf (output, "/>\n");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_svg_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
{
cairo_status_t status;
cairo_svg_surface_t *surface = abstract_surface;
 
/* Emulation of clear and source operators, when no clipping region
* is defined. We just delete existing content of surface root node,
* and exit early if operator is clear.
*/
if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) &&
clip == NULL)
{
switch (surface->paginated_mode) {
case CAIRO_PAGINATED_MODE_FALLBACK:
ASSERT_NOT_REACHED;
case CAIRO_PAGINATED_MODE_ANALYZE:
return CAIRO_STATUS_SUCCESS;
 
case CAIRO_PAGINATED_MODE_RENDER:
status = _cairo_output_stream_destroy (surface->xml_node);
if (unlikely (status)) {
surface->xml_node = NULL;
return status;
}
 
surface->xml_node = _cairo_memory_stream_create ();
if (_cairo_output_stream_get_status (surface->xml_node)) {
status = _cairo_output_stream_destroy (surface->xml_node);
surface->xml_node = NULL;
return status;
}
 
if (op == CAIRO_OPERATOR_CLEAR) {
if (surface->content == CAIRO_CONTENT_COLOR) {
_cairo_output_stream_printf (surface->xml_node,
"<rect "
"width=\"%f\" height=\"%f\" "
"style=\"opacity:1;"
"stroke:none;"
"fill:rgb(0,0,0);\"/>\n",
surface->width, surface->height);
}
return CAIRO_STATUS_SUCCESS;
}
break;
}
} else {
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
return _cairo_svg_surface_analyze_operation (surface, op, source);
 
assert (_cairo_svg_surface_operation_supported (surface, op, source));
}
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
 
return _cairo_svg_surface_emit_paint (surface->xml_node,
surface, op, source, 0, NULL);
}
 
static cairo_int_status_t
_cairo_svg_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
{
cairo_status_t status;
cairo_svg_surface_t *surface = abstract_surface;
cairo_svg_document_t *document = surface->document;
cairo_output_stream_t *mask_stream;
char buffer[64];
cairo_bool_t discard_filter = FALSE;
unsigned int mask_id;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
cairo_status_t source_status, mask_status;
 
source_status = _cairo_svg_surface_analyze_operation (surface, op, source);
if (_cairo_status_is_error (source_status))
return source_status;
 
if (mask->has_component_alpha) {
mask_status = CAIRO_INT_STATUS_UNSUPPORTED;
} else {
mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask);
if (_cairo_status_is_error (mask_status))
return mask_status;
}
 
return _cairo_analysis_surface_merge_status (source_status,
mask_status);
}
 
assert (_cairo_svg_surface_operation_supported (surface, op, source));
assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask));
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
 
if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask;
cairo_content_t content = cairo_surface_get_content (surface_pattern->surface);
if (content == CAIRO_CONTENT_ALPHA)
discard_filter = TRUE;
}
 
if (!discard_filter)
_cairo_svg_surface_emit_alpha_filter (document);
 
/* _cairo_svg_surface_emit_paint() will output a pattern definition to
* document->xml_node_defs so we need to write the mask element to
* a temporary stream and then copy that to xml_node_defs. */
mask_stream = _cairo_memory_stream_create ();
if (_cairo_output_stream_get_status (mask_stream))
return _cairo_output_stream_destroy (mask_stream);
 
mask_id = _cairo_svg_document_allocate_mask_id (document);
 
_cairo_output_stream_printf (mask_stream,
"<mask id=\"mask%d\">\n"
"%s",
mask_id,
discard_filter ? "" : " <g filter=\"url(#alpha)\">\n");
status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL);
if (unlikely (status)) {
cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream);
return status;
(void) ignore;
}
 
_cairo_output_stream_printf (mask_stream,
"%s"
"</mask>\n",
discard_filter ? "" : " </g>\n");
_cairo_memory_stream_copy (mask_stream, document->xml_node_defs);
 
status = _cairo_output_stream_destroy (mask_stream);
if (unlikely (status))
return status;
 
snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"",
mask_id);
status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_svg_surface_stroke (void *abstract_dst,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
cairo_svg_surface_t *surface = abstract_dst;
cairo_status_t status;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
return _cairo_svg_surface_analyze_operation (surface, op, source);
 
assert (_cairo_svg_surface_operation_supported (surface, op, source));
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->xml_node, "<path style=\"fill:none;");
status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, op,
source, stroke_style, ctm_inverse);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->xml_node, "\" ");
 
_cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse);
 
_cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL);
_cairo_output_stream_printf (surface->xml_node, "/>\n");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_svg_surface_show_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip,
int *remaining_glyphs)
{
cairo_svg_surface_t *surface = abstract_surface;
cairo_svg_document_t *document = surface->document;
cairo_path_fixed_t path;
cairo_status_t status;
cairo_scaled_font_subsets_glyph_t subset_glyph;
int i;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
return _cairo_svg_surface_analyze_operation (surface, op, pattern);
 
assert (_cairo_svg_surface_operation_supported (surface, op, pattern));
 
if (num_glyphs <= 0)
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
 
/* FIXME it's probably possible to apply a pattern of a gradient to
* a group of symbols, but I don't know how yet. Gradients or patterns
* are translated by x and y properties of use element. */
if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
goto FALLBACK;
 
_cairo_output_stream_printf (surface->xml_node, "<g style=\"");
status = _cairo_svg_surface_emit_pattern (surface, pattern,
surface->xml_node, FALSE, NULL);
if (unlikely (status))
return status;
 
_cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op);
 
_cairo_output_stream_printf (surface->xml_node, "\">\n");
 
for (i = 0; i < num_glyphs; i++) {
status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets,
scaled_font, glyphs[i].index,
NULL, 0,
&subset_glyph);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
_cairo_output_stream_printf (surface->xml_node, "</g>\n");
 
glyphs += i;
num_glyphs -= i;
goto FALLBACK;
}
 
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->xml_node,
" <use xlink:href=\"#glyph%d-%d\" "
"x=\"%f\" y=\"%f\"/>\n",
subset_glyph.font_id,
subset_glyph.subset_glyph_index,
glyphs[i].x, glyphs[i].y);
}
 
_cairo_output_stream_printf (surface->xml_node, "</g>\n");
 
return CAIRO_STATUS_SUCCESS;
 
FALLBACK:
_cairo_path_fixed_init (&path);
 
status = _cairo_scaled_font_glyph_path (scaled_font,
(cairo_glyph_t *) glyphs,
num_glyphs, &path);
 
if (unlikely (status)) {
_cairo_path_fixed_fini (&path);
return status;
}
 
status = _cairo_svg_surface_fill (abstract_surface, op, pattern,
&path, CAIRO_FILL_RULE_WINDING,
0.0, CAIRO_ANTIALIAS_SUBPIXEL,
clip);
 
_cairo_path_fixed_fini (&path);
 
return status;
}
 
static void
_cairo_svg_surface_get_font_options (void *abstract_surface,
cairo_font_options_t *options)
{
_cairo_font_options_init_default (options);
 
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
}
 
static const cairo_surface_backend_t cairo_svg_surface_backend = {
CAIRO_SURFACE_TYPE_SVG,
NULL, /* create_similar: handled by wrapper */
_cairo_svg_surface_finish,
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* acquire_dest_image */
NULL, /* release_dest_image */
NULL, /* clone_similar */
NULL, /* _cairo_svg_surface_composite, */
NULL, /* _cairo_svg_surface_fill_rectangles, */
NULL, /* _cairo_svg_surface_composite_trapezoids,*/
NULL, /* create_span_renderer */
NULL, /* check_span_renderer */
_cairo_svg_surface_copy_page,
_cairo_svg_surface_show_page,
_cairo_svg_surface_get_extents,
NULL, /* _cairo_svg_surface_old_show_glyphs, */
_cairo_svg_surface_get_font_options,
NULL, /* flush */
NULL, /* mark dirty rectangle */
NULL, /* scaled font fini */
NULL, /* scaled glyph fini */
_cairo_svg_surface_paint,
_cairo_svg_surface_mask,
_cairo_svg_surface_stroke,
_cairo_svg_surface_fill,
_cairo_svg_surface_show_glyphs,
NULL, /* snapshot */
NULL, /* is_similar */
_cairo_svg_surface_fill_stroke
};
 
static cairo_status_t
_cairo_svg_document_create (cairo_output_stream_t *output_stream,
double width,
double height,
cairo_svg_version_t version,
cairo_svg_document_t **document_out)
{
cairo_svg_document_t *document;
cairo_status_t status, status_ignored;
 
if (output_stream->status)
return output_stream->status;
 
document = malloc (sizeof (cairo_svg_document_t));
if (unlikely (document == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
/* The use of defs for font glyphs imposes no per-subset limit. */
document->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
if (unlikely (document->font_subsets == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_DOCUMENT;
}
 
document->output_stream = output_stream;
document->refcount = 1;
document->owner = NULL;
document->finished = FALSE;
document->width = width;
document->height = height;
 
document->linear_pattern_id = 0;
document->radial_pattern_id = 0;
document->pattern_id = 0;
document->filter_id = 0;
document->clip_id = 0;
document->mask_id = 0;
 
document->xml_node_defs = _cairo_memory_stream_create ();
status = _cairo_output_stream_get_status (document->xml_node_defs);
if (unlikely (status))
goto CLEANUP_NODE_DEFS;
 
document->xml_node_glyphs = _cairo_memory_stream_create ();
status = _cairo_output_stream_get_status (document->xml_node_glyphs);
if (unlikely (status))
goto CLEANUP_NODE_GLYPHS;
 
document->alpha_filter = FALSE;
 
document->svg_version = version;
 
*document_out = document;
return CAIRO_STATUS_SUCCESS;
 
CLEANUP_NODE_GLYPHS:
status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs);
CLEANUP_NODE_DEFS:
status_ignored = _cairo_output_stream_destroy (document->xml_node_defs);
_cairo_scaled_font_subsets_destroy (document->font_subsets);
CLEANUP_DOCUMENT:
free (document);
return status;
}
 
static cairo_svg_document_t *
_cairo_svg_document_reference (cairo_svg_document_t *document)
{
document->refcount++;
 
return document;
}
 
static unsigned int
_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document)
{
return document->mask_id++;
}
 
static cairo_status_t
_cairo_svg_document_destroy (cairo_svg_document_t *document)
{
cairo_status_t status;
 
document->refcount--;
if (document->refcount > 0)
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_svg_document_finish (document);
 
free (document);
 
return status;
}
 
static cairo_status_t
_cairo_svg_document_finish (cairo_svg_document_t *document)
{
cairo_status_t status, status2;
cairo_output_stream_t *output = document->output_stream;
cairo_svg_page_t *page;
unsigned int i;
 
if (document->finished)
return CAIRO_STATUS_SUCCESS;
 
/*
* Should we add DOCTYPE?
*
* Google says no.
*
* http://tech.groups.yahoo.com/group/svg-developers/message/48562:
* There's a bunch of issues, but just to pick a few:
* - they'll give false positives.
* - they'll give false negatives.
* - they're namespace-unaware.
* - they don't wildcard.
* So when they say OK they really haven't checked anything, when
* they say NOT OK they might be on crack, and like all
* namespace-unaware things they're a dead branch of the XML tree.
*
* http://jwatt.org/svg/authoring/:
* Unfortunately the SVG DTDs are a source of so many issues that the
* SVG WG has decided not to write one for the upcoming SVG 1.2
* standard. In fact SVG WG members are even telling people not to use
* a DOCTYPE declaration in SVG 1.0 and 1.1 documents.
*/
 
_cairo_output_stream_printf (output,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<svg xmlns=\"http://www.w3.org/2000/svg\" "
"xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
"width=\"%fpt\" height=\"%fpt\" "
"viewBox=\"0 0 %f %f\" version=\"%s\">\n",
document->width, document->height,
document->width, document->height,
_cairo_svg_internal_version_strings [document->svg_version]);
 
status = _cairo_svg_document_emit_font_subsets (document);
 
if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 ||
_cairo_memory_stream_length (document->xml_node_defs) > 0) {
_cairo_output_stream_printf (output, "<defs>\n");
if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
_cairo_output_stream_printf (output, "<g>\n");
_cairo_memory_stream_copy (document->xml_node_glyphs, output);
_cairo_output_stream_printf (output, "</g>\n");
}
_cairo_memory_stream_copy (document->xml_node_defs, output);
_cairo_output_stream_printf (output, "</defs>\n");
}
 
if (document->owner != NULL) {
cairo_svg_surface_t *surface;
 
surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner);
if (surface->xml_node != NULL &&
_cairo_memory_stream_length (surface->xml_node) > 0) {
if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) {
if (status == CAIRO_STATUS_SUCCESS)
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
 
if (surface->page_set.num_elements > 1 &&
_cairo_svg_version_has_page_set_support (document->svg_version)) {
_cairo_output_stream_printf (output, "<pageSet>\n");
for (i = 0; i < surface->page_set.num_elements; i++) {
page = _cairo_array_index (&surface->page_set, i);
_cairo_output_stream_printf (output, "<page>\n");
_cairo_output_stream_printf (output,
"<g id=\"surface%d\">\n",
page->surface_id);
_cairo_memory_stream_copy (page->xml_node, output);
_cairo_output_stream_printf (output, "</g>\n</page>\n");
}
_cairo_output_stream_printf (output, "</pageSet>\n");
} else if (surface->page_set.num_elements > 0) {
page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1);
_cairo_output_stream_printf (output,
"<g id=\"surface%d\">\n",
page->surface_id);
_cairo_memory_stream_copy (page->xml_node, output);
_cairo_output_stream_printf (output, "</g>\n");
}
}
 
_cairo_output_stream_printf (output, "</svg>\n");
 
status2 = _cairo_output_stream_destroy (document->xml_node_glyphs);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
status2 = _cairo_output_stream_destroy (document->xml_node_defs);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
status2 = _cairo_output_stream_destroy (output);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
document->finished = TRUE;
 
return status;
}
 
static void
_cairo_svg_surface_set_paginated_mode (void *abstract_surface,
cairo_paginated_mode_t paginated_mode)
{
cairo_svg_surface_t *surface = abstract_surface;
 
surface->paginated_mode = paginated_mode;
}
 
static cairo_bool_t
_cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface)
{
cairo_svg_surface_t *surface = abstract_surface;
cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
 
if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) {
status = _cairo_svg_surface_analyze_operator (surface,
CAIRO_OPERATOR_SOURCE);
}
 
return status == CAIRO_STATUS_SUCCESS;
}
 
static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
NULL /*_cairo_svg_surface_start_page*/,
_cairo_svg_surface_set_paginated_mode,
NULL, /* _cairo_svg_surface_set_bounding_box */
NULL, /* _cairo_svg_surface_set_fallback_images_required */
_cairo_svg_surface_supports_fine_grained_fallbacks,
 
};
/programs/develop/libraries/cairo/src/cairo-truetype-subset.c
0,0 → 1,1433
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2004 Red Hat, Inc
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Kristian Høgsberg <krh@redhat.com>
* Adrian Johnson <ajohnson@redneon.com>
*/
 
/*
* Useful links:
* http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html
* http://www.microsoft.com/typography/specs/default.htm
*/
 
#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"
 
 
typedef struct subset_glyph subset_glyph_t;
struct subset_glyph {
int parent_index;
unsigned long location;
};
 
typedef struct _cairo_truetype_font cairo_truetype_font_t;
 
typedef struct table table_t;
struct table {
unsigned long tag;
cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag);
int pos; /* position in the font directory */
};
 
struct _cairo_truetype_font {
 
cairo_scaled_font_subset_t *scaled_font_subset;
 
table_t truetype_tables[10];
int num_tables;
 
struct {
char *font_name;
char *ps_name;
unsigned int num_glyphs;
int *widths;
long x_min, y_min, x_max, y_max;
long ascent, descent;
int units_per_em;
} base;
 
subset_glyph_t *glyphs;
const cairo_scaled_font_backend_t *backend;
int num_glyphs_in_face;
int checksum_index;
cairo_array_t output;
cairo_array_t string_offsets;
unsigned long last_offset;
unsigned long last_boundary;
int *parent_to_subset;
cairo_status_t status;
 
};
 
/*
* Test that the structs we define for TrueType tables have the
* correct size, ie. they are not padded.
*/
#define check(T, S) COMPILE_TIME_ASSERT (sizeof (T) == (S))
check (tt_head_t, 54);
check (tt_hhea_t, 36);
check (tt_maxp_t, 32);
check (tt_name_record_t, 12);
check (tt_name_t, 18);
check (tt_name_t, 18);
check (tt_composite_glyph_t, 16);
check (tt_glyph_data_t, 26);
#undef check
 
static cairo_status_t
cairo_truetype_font_use_glyph (cairo_truetype_font_t *font,
unsigned short glyph,
unsigned short *out);
 
#define SFNT_VERSION 0x00010000
#define SFNT_STRING_MAX_LENGTH 65535
 
static cairo_status_t
_cairo_truetype_font_set_error (cairo_truetype_font_t *font,
cairo_status_t status)
{
if (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
_cairo_status_set_error (&font->status, status);
 
return _cairo_error (status);
}
 
static cairo_status_t
_cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
cairo_truetype_font_t **font_return)
{
cairo_status_t status;
cairo_truetype_font_t *font;
const cairo_scaled_font_backend_t *backend;
tt_head_t head;
tt_hhea_t hhea;
tt_maxp_t maxp;
unsigned long size;
 
backend = scaled_font_subset->scaled_font->backend;
if (!backend->load_truetype_table)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* FIXME: We should either support subsetting vertical fonts, or fail on
* vertical. Currently font_options_t doesn't have vertical flag, but
* it should be added in the future. For now, the freetype backend
* returns UNSUPPORTED in load_truetype_table if the font is vertical.
*
* if (cairo_font_options_get_vertical_layout (scaled_font_subset->scaled_font))
* return CAIRO_INT_STATUS_UNSUPPORTED;
*/
 
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_maxp_t);
status = backend->load_truetype_table (scaled_font_subset->scaled_font,
TT_TAG_maxp, 0,
(unsigned char *) &maxp,
&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;
 
font = malloc (sizeof (cairo_truetype_font_t));
if (unlikely (font == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
font->backend = backend;
font->num_glyphs_in_face = be16_to_cpu (maxp.num_glyphs);
font->scaled_font_subset = scaled_font_subset;
 
font->last_offset = 0;
font->last_boundary = 0;
_cairo_array_init (&font->output, sizeof (char));
status = _cairo_array_grow_by (&font->output, 4096);
if (unlikely (status))
goto fail1;
 
font->glyphs = calloc (font->num_glyphs_in_face + 1, sizeof (subset_glyph_t));
if (unlikely (font->glyphs == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail1;
}
 
font->parent_to_subset = calloc (font->num_glyphs_in_face, sizeof (int));
if (unlikely (font->parent_to_subset == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail2;
}
 
font->base.num_glyphs = 0;
font->base.x_min = (int16_t) be16_to_cpu (head.x_min);
font->base.y_min = (int16_t) be16_to_cpu (head.y_min);
font->base.x_max = (int16_t) be16_to_cpu (head.x_max);
font->base.y_max = (int16_t) be16_to_cpu (head.y_max);
font->base.ascent = (int16_t) be16_to_cpu (hhea.ascender);
font->base.descent = (int16_t) be16_to_cpu (hhea.descender);
font->base.units_per_em = (int16_t) be16_to_cpu (head.units_per_em);
if (font->base.units_per_em == 0)
font->base.units_per_em = 2048;
 
font->base.ps_name = NULL;
font->base.font_name = NULL;
status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font,
&font->base.ps_name,
&font->base.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->base.ps_name == NULL) {
font->base.ps_name = malloc (30);
if (unlikely (font->base.ps_name == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail3;
}
 
snprintf(font->base.ps_name, 30, "CairoFont-%u-%u",
scaled_font_subset->font_id,
scaled_font_subset->subset_id);
}
 
font->base.widths = calloc (font->num_glyphs_in_face, sizeof (int));
if (unlikely (font->base.widths == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail4;
}
 
_cairo_array_init (&font->string_offsets, sizeof (unsigned long));
status = _cairo_array_grow_by (&font->string_offsets, 10);
if (unlikely (status))
goto fail5;
 
font->status = CAIRO_STATUS_SUCCESS;
 
*font_return = font;
 
return CAIRO_STATUS_SUCCESS;
 
fail5:
_cairo_array_fini (&font->string_offsets);
free (font->base.widths);
fail4:
free (font->base.ps_name);
fail3:
free (font->parent_to_subset);
if (font->base.font_name)
free (font->base.font_name);
fail2:
free (font->glyphs);
fail1:
_cairo_array_fini (&font->output);
free (font);
 
return status;
}
 
static void
cairo_truetype_font_destroy (cairo_truetype_font_t *font)
{
_cairo_array_fini (&font->string_offsets);
free (font->base.widths);
free (font->base.ps_name);
if (font->base.font_name)
free (font->base.font_name);
free (font->parent_to_subset);
free (font->glyphs);
_cairo_array_fini (&font->output);
free (font);
}
 
static cairo_status_t
cairo_truetype_font_allocate_write_buffer (cairo_truetype_font_t *font,
size_t length,
unsigned char **buffer)
{
cairo_status_t status;
 
if (font->status)
return font->status;
 
status = _cairo_array_allocate (&font->output, length, (void **) buffer);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
cairo_truetype_font_write (cairo_truetype_font_t *font,
const void *data,
size_t length)
{
cairo_status_t status;
 
if (font->status)
return;
 
status = _cairo_array_append_multiple (&font->output, data, length);
if (unlikely (status))
status = _cairo_truetype_font_set_error (font, status);
}
 
static void
cairo_truetype_font_write_be16 (cairo_truetype_font_t *font,
uint16_t value)
{
uint16_t be16_value;
 
if (font->status)
return;
 
be16_value = cpu_to_be16 (value);
cairo_truetype_font_write (font, &be16_value, sizeof be16_value);
}
 
static void
cairo_truetype_font_write_be32 (cairo_truetype_font_t *font,
uint32_t value)
{
uint32_t be32_value;
 
if (font->status)
return;
 
be32_value = cpu_to_be32 (value);
cairo_truetype_font_write (font, &be32_value, sizeof be32_value);
}
 
static cairo_status_t
cairo_truetype_font_align_output (cairo_truetype_font_t *font,
unsigned long *aligned)
{
int length, pad;
unsigned char *padding;
 
length = _cairo_array_num_elements (&font->output);
*aligned = (length + 3) & ~3;
pad = *aligned - length;
 
if (pad) {
cairo_status_t status;
 
status = cairo_truetype_font_allocate_write_buffer (font, pad,
&padding);
if (unlikely (status))
return status;
 
memset (padding, 0, pad);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_truetype_font_check_boundary (cairo_truetype_font_t *font,
unsigned long boundary)
{
cairo_status_t status;
 
if (font->status)
return font->status;
 
if (boundary - font->last_offset > SFNT_STRING_MAX_LENGTH)
{
status = _cairo_array_append (&font->string_offsets,
&font->last_boundary);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
font->last_offset = font->last_boundary;
}
font->last_boundary = boundary;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_truetype_font_write_cmap_table (cairo_truetype_font_t *font,
unsigned long tag)
{
unsigned int i;
 
cairo_truetype_font_write_be16 (font, 0); /* Table version */
cairo_truetype_font_write_be16 (font, 2); /* Num tables */
 
cairo_truetype_font_write_be16 (font, 3); /* Platform */
cairo_truetype_font_write_be16 (font, 0); /* Encoding */
cairo_truetype_font_write_be32 (font, 20); /* Offset to start of table */
 
cairo_truetype_font_write_be16 (font, 1); /* Platform */
cairo_truetype_font_write_be16 (font, 0); /* Encoding */
cairo_truetype_font_write_be32 (font, 52); /* Offset to start of table */
 
/* Output a format 4 encoding table. */
 
cairo_truetype_font_write_be16 (font, 4); /* Format */
cairo_truetype_font_write_be16 (font, 32); /* Length */
cairo_truetype_font_write_be16 (font, 0); /* Version */
cairo_truetype_font_write_be16 (font, 4); /* 2*segcount */
cairo_truetype_font_write_be16 (font, 4); /* searchrange */
cairo_truetype_font_write_be16 (font, 1); /* entry selector */
cairo_truetype_font_write_be16 (font, 0); /* rangeshift */
cairo_truetype_font_write_be16 (font, 0xf000 + font->base.num_glyphs - 1); /* end count[0] */
cairo_truetype_font_write_be16 (font, 0xffff); /* end count[1] */
cairo_truetype_font_write_be16 (font, 0); /* reserved */
cairo_truetype_font_write_be16 (font, 0xf000); /* startCode[0] */
cairo_truetype_font_write_be16 (font, 0xffff); /* startCode[1] */
cairo_truetype_font_write_be16 (font, 0x1000); /* delta[0] */
cairo_truetype_font_write_be16 (font, 1); /* delta[1] */
cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[0] */
cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[1] */
 
/* Output a format 6 encoding table. */
 
cairo_truetype_font_write_be16 (font, 6);
cairo_truetype_font_write_be16 (font, 10 + 2 * font->base.num_glyphs);
cairo_truetype_font_write_be16 (font, 0);
cairo_truetype_font_write_be16 (font, 0); /* First character */
cairo_truetype_font_write_be16 (font, font->base.num_glyphs);
for (i = 0; i < font->base.num_glyphs; i++)
cairo_truetype_font_write_be16 (font, i);
 
return font->status;
}
 
static cairo_status_t
cairo_truetype_font_write_generic_table (cairo_truetype_font_t *font,
unsigned long tag)
{
cairo_status_t status;
unsigned char *buffer;
unsigned long size;
 
if (font->status)
return font->status;
 
size = 0;
status = font->backend->load_truetype_table(font->scaled_font_subset->scaled_font,
tag, 0, NULL, &size);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
tag, 0, buffer, &size);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_truetype_font_remap_composite_glyph (cairo_truetype_font_t *font,
unsigned char *buffer,
unsigned long size)
{
tt_glyph_data_t *glyph_data;
tt_composite_glyph_t *composite_glyph;
int num_args;
int has_more_components;
unsigned short flags;
unsigned short index;
cairo_status_t status;
unsigned char *end = buffer + size;
 
if (font->status)
return font->status;
 
glyph_data = (tt_glyph_data_t *) buffer;
if ((unsigned char *)(&glyph_data->data) >= end)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if ((int16_t)be16_to_cpu (glyph_data->num_contours) >= 0)
return CAIRO_STATUS_SUCCESS;
 
composite_glyph = &glyph_data->glyph;
do {
if ((unsigned char *)(&composite_glyph->args[1]) > end)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
flags = be16_to_cpu (composite_glyph->flags);
has_more_components = flags & TT_MORE_COMPONENTS;
status = cairo_truetype_font_use_glyph (font, be16_to_cpu (composite_glyph->index), &index);
if (unlikely (status))
return status;
 
composite_glyph->index = cpu_to_be16 (index);
num_args = 1;
if (flags & TT_ARG_1_AND_2_ARE_WORDS)
num_args += 1;
 
if (flags & TT_WE_HAVE_A_SCALE)
num_args += 1;
else if (flags & TT_WE_HAVE_AN_X_AND_Y_SCALE)
num_args += 2;
else if (flags & TT_WE_HAVE_A_TWO_BY_TWO)
num_args += 4;
 
composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]);
} while (has_more_components);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font,
unsigned long tag)
{
unsigned long start_offset, index, size, next;
tt_head_t header;
unsigned long begin, end;
unsigned char *buffer;
unsigned int i;
union {
unsigned char *bytes;
uint16_t *short_offsets;
uint32_t *long_offsets;
} u;
cairo_status_t status;
 
if (font->status)
return font->status;
 
size = sizeof (tt_head_t);
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_head, 0,
(unsigned char*) &header, &size);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
if (be16_to_cpu (header.index_to_loc_format) == 0)
size = sizeof (int16_t) * (font->num_glyphs_in_face + 1);
else
size = sizeof (int32_t) * (font->num_glyphs_in_face + 1);
 
u.bytes = malloc (size);
if (unlikely (u.bytes == NULL))
return _cairo_truetype_font_set_error (font, CAIRO_STATUS_NO_MEMORY);
 
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_loca, 0, u.bytes, &size);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
start_offset = _cairo_array_num_elements (&font->output);
for (i = 0; i < font->base.num_glyphs; i++) {
index = font->glyphs[i].parent_index;
if (be16_to_cpu (header.index_to_loc_format) == 0) {
begin = be16_to_cpu (u.short_offsets[index]) * 2;
end = be16_to_cpu (u.short_offsets[index + 1]) * 2;
}
else {
begin = be32_to_cpu (u.long_offsets[index]);
end = be32_to_cpu (u.long_offsets[index + 1]);
}
 
/* quick sanity check... */
if (end < begin) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto FAIL;
}
 
size = end - begin;
status = cairo_truetype_font_align_output (font, &next);
if (unlikely (status))
goto FAIL;
 
status = cairo_truetype_font_check_boundary (font, next);
if (unlikely (status))
goto FAIL;
 
font->glyphs[i].location = next - start_offset;
 
status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer);
if (unlikely (status))
goto FAIL;
 
if (size != 0) {
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_glyf, begin, buffer, &size);
if (unlikely (status))
goto FAIL;
 
status = cairo_truetype_font_remap_composite_glyph (font, buffer, size);
if (unlikely (status))
goto FAIL;
}
}
 
status = cairo_truetype_font_align_output (font, &next);
if (unlikely (status))
goto FAIL;
 
font->glyphs[i].location = next - start_offset;
 
status = font->status;
FAIL:
free (u.bytes);
 
return _cairo_truetype_font_set_error (font, status);
}
 
static cairo_status_t
cairo_truetype_font_write_head_table (cairo_truetype_font_t *font,
unsigned long tag)
{
unsigned char *buffer;
unsigned long size;
cairo_status_t status;
 
if (font->status)
return font->status;
 
size = 0;
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
tag, 0, NULL, &size);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
font->checksum_index = _cairo_array_num_elements (&font->output) + 8;
status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
tag, 0, buffer, &size);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
/* set checkSumAdjustment to 0 for table checksum calculation */
*(uint32_t *)(buffer + 8) = 0;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_truetype_font_write_hhea_table (cairo_truetype_font_t *font, unsigned long tag)
{
tt_hhea_t *hhea;
unsigned long size;
cairo_status_t status;
 
if (font->status)
return font->status;
 
size = sizeof (tt_hhea_t);
status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &hhea);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
tag, 0, (unsigned char *) hhea, &size);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
hhea->num_hmetrics = cpu_to_be16 ((uint16_t)(font->base.num_glyphs));
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font,
unsigned long tag)
{
unsigned long size;
unsigned long long_entry_size;
unsigned long short_entry_size;
short *p;
unsigned int i;
tt_hhea_t hhea;
int num_hmetrics;
cairo_status_t status;
 
if (font->status)
return font->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 _cairo_truetype_font_set_error (font, status);
 
num_hmetrics = be16_to_cpu(hhea.num_hmetrics);
 
for (i = 0; i < font->base.num_glyphs; i++) {
long_entry_size = 2 * sizeof (int16_t);
short_entry_size = sizeof (int16_t);
status = cairo_truetype_font_allocate_write_buffer (font,
long_entry_size,
(unsigned char **) &p);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
if (font->glyphs[i].parent_index < num_hmetrics) {
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_hmtx,
font->glyphs[i].parent_index * long_entry_size,
(unsigned char *) p, &long_entry_size);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
}
else
{
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_hmtx,
(num_hmetrics - 1) * long_entry_size,
(unsigned char *) p, &short_entry_size);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_hmtx,
num_hmetrics * long_entry_size +
(font->glyphs[i].parent_index - num_hmetrics) * short_entry_size,
(unsigned char *) (p + 1), &short_entry_size);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
}
font->base.widths[i] = be16_to_cpu (p[0]);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_truetype_font_write_loca_table (cairo_truetype_font_t *font,
unsigned long tag)
{
unsigned int i;
tt_head_t header;
unsigned long size;
cairo_status_t status;
 
if (font->status)
return font->status;
 
size = sizeof(tt_head_t);
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_head, 0,
(unsigned char*) &header, &size);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
if (be16_to_cpu (header.index_to_loc_format) == 0)
{
for (i = 0; i < font->base.num_glyphs + 1; i++)
cairo_truetype_font_write_be16 (font, font->glyphs[i].location / 2);
} else {
for (i = 0; i < font->base.num_glyphs + 1; i++)
cairo_truetype_font_write_be32 (font, font->glyphs[i].location);
}
 
return font->status;
}
 
static cairo_status_t
cairo_truetype_font_write_maxp_table (cairo_truetype_font_t *font,
unsigned long tag)
{
tt_maxp_t *maxp;
unsigned long size;
cairo_status_t status;
 
if (font->status)
return font->status;
 
size = sizeof (tt_maxp_t);
status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &maxp);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
tag, 0, (unsigned char *) maxp, &size);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
maxp->num_glyphs = cpu_to_be16 (font->base.num_glyphs);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_truetype_font_write_offset_table (cairo_truetype_font_t *font)
{
cairo_status_t status;
unsigned char *table_buffer;
size_t table_buffer_length;
unsigned short search_range, entry_selector, range_shift;
 
if (font->status)
return font->status;
 
search_range = 1;
entry_selector = 0;
while (search_range * 2 <= font->num_tables) {
search_range *= 2;
entry_selector++;
}
search_range *= 16;
range_shift = font->num_tables * 16 - search_range;
 
cairo_truetype_font_write_be32 (font, SFNT_VERSION);
cairo_truetype_font_write_be16 (font, font->num_tables);
cairo_truetype_font_write_be16 (font, search_range);
cairo_truetype_font_write_be16 (font, entry_selector);
cairo_truetype_font_write_be16 (font, range_shift);
 
/* Allocate space for the table directory. Each directory entry
* will be filled in by cairo_truetype_font_update_entry() after
* the table is written. */
table_buffer_length = font->num_tables * 16;
status = cairo_truetype_font_allocate_write_buffer (font, table_buffer_length,
&table_buffer);
if (unlikely (status))
return _cairo_truetype_font_set_error (font, status);
 
return CAIRO_STATUS_SUCCESS;
}
 
static uint32_t
cairo_truetype_font_calculate_checksum (cairo_truetype_font_t *font,
unsigned long start,
unsigned long end)
{
uint32_t *padded_end;
uint32_t *p;
uint32_t checksum;
char *data;
 
checksum = 0;
data = _cairo_array_index (&font->output, 0);
p = (uint32_t *) (data + start);
padded_end = (uint32_t *) (data + ((end + 3) & ~3));
while (p < padded_end)
checksum += be32_to_cpu(*p++);
 
return checksum;
}
 
static void
cairo_truetype_font_update_entry (cairo_truetype_font_t *font,
int index,
unsigned long tag,
unsigned long start,
unsigned long end)
{
uint32_t *entry;
 
entry = _cairo_array_index (&font->output, 12 + 16 * index);
entry[0] = cpu_to_be32 ((uint32_t)tag);
entry[1] = cpu_to_be32 (cairo_truetype_font_calculate_checksum (font, start, end));
entry[2] = cpu_to_be32 ((uint32_t)start);
entry[3] = cpu_to_be32 ((uint32_t)(end - start));
}
 
static cairo_status_t
cairo_truetype_font_generate (cairo_truetype_font_t *font,
const char **data,
unsigned long *length,
const unsigned long **string_offsets,
unsigned long *num_strings)
{
cairo_status_t status;
unsigned long start, end, next;
uint32_t checksum, *checksum_location;
int i;
 
if (font->status)
return font->status;
 
status = cairo_truetype_font_write_offset_table (font);
if (unlikely (status))
goto FAIL;
 
status = cairo_truetype_font_align_output (font, &start);
if (unlikely (status))
goto FAIL;
 
end = 0;
for (i = 0; i < font->num_tables; i++) {
status = font->truetype_tables[i].write (font, font->truetype_tables[i].tag);
if (unlikely (status))
goto FAIL;
 
end = _cairo_array_num_elements (&font->output);
status = cairo_truetype_font_align_output (font, &next);
if (unlikely (status))
goto FAIL;
 
cairo_truetype_font_update_entry (font, font->truetype_tables[i].pos,
font->truetype_tables[i].tag, start, end);
status = cairo_truetype_font_check_boundary (font, next);
if (unlikely (status))
goto FAIL;
 
start = next;
}
 
checksum =
0xb1b0afba - cairo_truetype_font_calculate_checksum (font, 0, end);
checksum_location = _cairo_array_index (&font->output, font->checksum_index);
*checksum_location = cpu_to_be32 (checksum);
 
*data = _cairo_array_index (&font->output, 0);
*length = _cairo_array_num_elements (&font->output);
*num_strings = _cairo_array_num_elements (&font->string_offsets);
if (*num_strings != 0)
*string_offsets = _cairo_array_index (&font->string_offsets, 0);
else
*string_offsets = NULL;
 
FAIL:
return _cairo_truetype_font_set_error (font, status);
}
 
static cairo_status_t
cairo_truetype_font_use_glyph (cairo_truetype_font_t *font,
unsigned short glyph,
unsigned short *out)
{
if (glyph >= font->num_glyphs_in_face)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (font->parent_to_subset[glyph] == 0) {
font->parent_to_subset[glyph] = font->base.num_glyphs;
font->glyphs[font->base.num_glyphs].parent_index = glyph;
font->base.num_glyphs++;
}
 
*out = font->parent_to_subset[glyph];
return CAIRO_STATUS_SUCCESS;
}
 
static void
cairo_truetype_font_add_truetype_table (cairo_truetype_font_t *font,
unsigned long tag,
cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag),
int pos)
{
font->truetype_tables[font->num_tables].tag = tag;
font->truetype_tables[font->num_tables].write = write;
font->truetype_tables[font->num_tables].pos = pos;
font->num_tables++;
}
 
/* cairo_truetype_font_create_truetype_table_list() builds the list of
* truetype tables to be embedded in the subsetted font. Each call to
* cairo_truetype_font_add_truetype_table() adds a table, the callback
* for generating the table, and the position in the table directory
* to the truetype_tables array.
*
* As we write out the glyf table we remap composite glyphs.
* Remapping composite glyphs will reference the sub glyphs the
* composite glyph is made up of. The "glyf" table callback needs to
* be called first so we have all the glyphs in the subset before
* going further.
*
* The order in which tables are added to the truetype_table array
* using cairo_truetype_font_add_truetype_table() specifies the order
* in which the callback functions will be called.
*
* The tables in the table directory must be listed in alphabetical
* order. The "cvt", "fpgm", and "prep" are optional tables. They
* will only be embedded in the subset if they exist in the source
* font. The pos parameter of cairo_truetype_font_add_truetype_table()
* specifies the position of the table in the table directory.
*/
static void
cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font)
{
cairo_bool_t has_cvt = FALSE;
cairo_bool_t has_fpgm = FALSE;
cairo_bool_t has_prep = FALSE;
unsigned long size;
int pos;
 
size = 0;
if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_cvt, 0, NULL,
&size) == CAIRO_STATUS_SUCCESS)
has_cvt = TRUE;
 
size = 0;
if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_fpgm, 0, NULL,
&size) == CAIRO_STATUS_SUCCESS)
has_fpgm = TRUE;
 
size = 0;
if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_prep, 0, NULL,
&size) == CAIRO_STATUS_SUCCESS)
has_prep = TRUE;
 
font->num_tables = 0;
pos = 1;
if (has_cvt)
pos++;
if (has_fpgm)
pos++;
cairo_truetype_font_add_truetype_table (font, TT_TAG_glyf, cairo_truetype_font_write_glyf_table, pos);
 
pos = 0;
cairo_truetype_font_add_truetype_table (font, TT_TAG_cmap, cairo_truetype_font_write_cmap_table, pos++);
if (has_cvt)
cairo_truetype_font_add_truetype_table (font, TT_TAG_cvt, cairo_truetype_font_write_generic_table, pos++);
if (has_fpgm)
cairo_truetype_font_add_truetype_table (font, TT_TAG_fpgm, cairo_truetype_font_write_generic_table, pos++);
pos++;
cairo_truetype_font_add_truetype_table (font, TT_TAG_head, cairo_truetype_font_write_head_table, pos++);
cairo_truetype_font_add_truetype_table (font, TT_TAG_hhea, cairo_truetype_font_write_hhea_table, pos++);
cairo_truetype_font_add_truetype_table (font, TT_TAG_hmtx, cairo_truetype_font_write_hmtx_table, pos++);
cairo_truetype_font_add_truetype_table (font, TT_TAG_loca, cairo_truetype_font_write_loca_table, pos++);
cairo_truetype_font_add_truetype_table (font, TT_TAG_maxp, cairo_truetype_font_write_maxp_table, pos++);
if (has_prep)
cairo_truetype_font_add_truetype_table (font, TT_TAG_prep, cairo_truetype_font_write_generic_table, pos);
}
 
cairo_status_t
_cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset,
cairo_scaled_font_subset_t *font_subset)
{
cairo_truetype_font_t *font = NULL;
cairo_status_t status;
const char *data = NULL; /* squelch bogus compiler warning */
unsigned long length = 0; /* squelch bogus compiler warning */
unsigned long offsets_length;
unsigned int i;
const unsigned long *string_offsets = NULL;
unsigned long num_strings = 0;
 
status = _cairo_truetype_font_create (font_subset, &font);
if (unlikely (status))
return status;
 
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
unsigned short parent_glyph = font->scaled_font_subset->glyphs[i];
status = cairo_truetype_font_use_glyph (font, parent_glyph, &parent_glyph);
if (unlikely (status))
goto fail1;
}
 
cairo_truetype_font_create_truetype_table_list (font);
status = cairo_truetype_font_generate (font, &data, &length,
&string_offsets, &num_strings);
if (unlikely (status))
goto fail1;
 
truetype_subset->ps_name = strdup (font->base.ps_name);
if (unlikely (truetype_subset->ps_name == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail1;
}
 
if (font->base.font_name != NULL) {
truetype_subset->font_name = strdup (font->base.font_name);
if (unlikely (truetype_subset->font_name == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail2;
}
} else {
truetype_subset->font_name = NULL;
}
 
/* The widths array returned must contain only widths for the
* glyphs in font_subset. Any subglyphs appended after
* font_subset->num_glyphs are omitted. */
truetype_subset->widths = calloc (sizeof (double),
font->scaled_font_subset->num_glyphs);
if (unlikely (truetype_subset->widths == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail3;
}
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
truetype_subset->widths[i] = (double)font->base.widths[i]/font->base.units_per_em;
 
truetype_subset->x_min = (double)font->base.x_min/font->base.units_per_em;
truetype_subset->y_min = (double)font->base.y_min/font->base.units_per_em;
truetype_subset->x_max = (double)font->base.x_max/font->base.units_per_em;
truetype_subset->y_max = (double)font->base.y_max/font->base.units_per_em;
truetype_subset->ascent = (double)font->base.ascent/font->base.units_per_em;
truetype_subset->descent = (double)font->base.descent/font->base.units_per_em;
 
if (length) {
truetype_subset->data = malloc (length);
if (unlikely (truetype_subset->data == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail4;
}
 
memcpy (truetype_subset->data, data, length);
} else
truetype_subset->data = NULL;
truetype_subset->data_length = length;
 
if (num_strings) {
offsets_length = num_strings * sizeof (unsigned long);
truetype_subset->string_offsets = malloc (offsets_length);
if (unlikely (truetype_subset->string_offsets == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail5;
}
 
memcpy (truetype_subset->string_offsets, string_offsets, offsets_length);
truetype_subset->num_string_offsets = num_strings;
} else {
truetype_subset->string_offsets = NULL;
truetype_subset->num_string_offsets = 0;
}
 
cairo_truetype_font_destroy (font);
 
return CAIRO_STATUS_SUCCESS;
 
fail5:
free (truetype_subset->data);
fail4:
free (truetype_subset->widths);
fail3:
if (truetype_subset->font_name)
free (truetype_subset->font_name);
fail2:
free (truetype_subset->ps_name);
fail1:
cairo_truetype_font_destroy (font);
 
return status;
}
 
void
_cairo_truetype_subset_fini (cairo_truetype_subset_t *subset)
{
free (subset->ps_name);
if (subset->font_name)
free (subset->font_name);
free (subset->widths);
free (subset->data);
free (subset->string_offsets);
}
 
static cairo_int_status_t
_cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font,
unsigned long table_offset,
unsigned long index,
uint32_t *ucs4)
{
cairo_status_t status;
const cairo_scaled_font_backend_t *backend;
tt_segment_map_t *map;
char buf[4];
unsigned int num_segments, i;
unsigned long size;
uint16_t *start_code;
uint16_t *end_code;
uint16_t *delta;
uint16_t *range_offset;
uint16_t *glyph_array;
uint16_t c;
 
backend = scaled_font->backend;
size = 4;
status = backend->load_truetype_table (scaled_font,
TT_TAG_cmap, table_offset,
(unsigned char *) &buf,
&size);
if (unlikely (status))
return status;
 
/* All table formats have the same first two words */
map = (tt_segment_map_t *) buf;
if (be16_to_cpu (map->format) != 4)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
size = be16_to_cpu (map->length);
map = malloc (size);
if (unlikely (map == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = backend->load_truetype_table (scaled_font,
TT_TAG_cmap, table_offset,
(unsigned char *) map,
&size);
if (unlikely (status))
goto fail;
 
num_segments = be16_to_cpu (map->segCountX2)/2;
 
/* A Format 4 cmap contains 8 uint16_t numbers and 4 arrays of
* uint16_t each num_segments long. */
if (size < (8 + 4*num_segments)*sizeof(uint16_t))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
end_code = map->endCount;
start_code = &(end_code[num_segments + 1]);
delta = &(start_code[num_segments]);
range_offset = &(delta[num_segments]);
glyph_array = &(range_offset[num_segments]);
 
/* search for glyph in segments with rangeOffset=0 */
for (i = 0; i < num_segments; i++) {
c = index - be16_to_cpu (delta[i]);
if (range_offset[i] == 0 &&
c >= be16_to_cpu (start_code[i]) &&
c <= be16_to_cpu (end_code[i]))
{
*ucs4 = c;
goto found;
}
}
 
/* search for glyph in segments with rangeOffset=1 */
for (i = 0; i < num_segments; i++) {
if (range_offset[i] != 0) {
uint16_t *glyph_ids = &range_offset[i] + be16_to_cpu (range_offset[i])/2;
int range_size = be16_to_cpu (end_code[i]) - be16_to_cpu (start_code[i]) + 1;
uint16_t g_id_be = cpu_to_be16 (index);
int j;
 
if (range_size > 0) {
if ((char*)glyph_ids + 2*range_size > (char*)map + size)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
for (j = 0; j < range_size; j++) {
if (glyph_ids[j] == g_id_be) {
*ucs4 = be16_to_cpu (start_code[i]) + j;
goto found;
}
}
}
}
}
 
/* glyph not found */
*ucs4 = -1;
 
found:
status = CAIRO_STATUS_SUCCESS;
 
fail:
free (map);
 
return status;
}
 
cairo_int_status_t
_cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font,
unsigned long index,
uint32_t *ucs4)
{
cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
const cairo_scaled_font_backend_t *backend;
tt_cmap_t *cmap;
char buf[4];
int num_tables, i;
unsigned long size;
 
backend = scaled_font->backend;
if (!backend->load_truetype_table)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
size = 4;
status = backend->load_truetype_table (scaled_font,
TT_TAG_cmap, 0,
(unsigned char *) &buf,
&size);
if (unlikely (status))
return status;
 
cmap = (tt_cmap_t *) buf;
num_tables = be16_to_cpu (cmap->num_tables);
size = 4 + num_tables*sizeof(tt_cmap_index_t);
cmap = _cairo_malloc_ab_plus_c (num_tables, sizeof (tt_cmap_index_t), 4);
if (unlikely (cmap == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = backend->load_truetype_table (scaled_font,
TT_TAG_cmap, 0,
(unsigned char *) cmap,
&size);
if (unlikely (status))
goto cleanup;
 
/* Find a table with Unicode mapping */
for (i = 0; i < num_tables; i++) {
if (be16_to_cpu (cmap->index[i].platform) == 3 &&
be16_to_cpu (cmap->index[i].encoding) == 1) {
status = _cairo_truetype_reverse_cmap (scaled_font,
be32_to_cpu (cmap->index[i].offset),
index,
ucs4);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
break;
}
}
 
cleanup:
free (cmap);
 
return status;
}
 
cairo_int_status_t
_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font,
char **ps_name_out,
char **font_name_out)
{
cairo_status_t status;
const cairo_scaled_font_backend_t *backend;
tt_name_t *name;
tt_name_record_t *record;
unsigned long size;
int i, j;
char *ps_name = NULL;
char *font_name = NULL;
 
backend = scaled_font->backend;
if (!backend->load_truetype_table)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
size = 0;
status = backend->load_truetype_table (scaled_font,
TT_TAG_name, 0,
NULL,
&size);
if (status)
return status;
 
name = malloc (size);
if (name == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = backend->load_truetype_table (scaled_font,
TT_TAG_name, 0,
(unsigned char *) name,
&size);
if (status)
goto fail;
 
/* Extract the font name and PS name from the name table. At
* present this just looks for the Mac platform/Roman encoded font
* name. It should be extended to use any suitable font name in
* the name table.
*/
for (i = 0; i < be16_to_cpu(name->num_records); i++) {
record = &(name->records[i]);
if ((be16_to_cpu (record->platform) == 1) &&
(be16_to_cpu (record->encoding) == 0)) {
 
if (be16_to_cpu (record->name) == 4) {
font_name = malloc (be16_to_cpu(record->length) + 1);
if (font_name == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail;
}
strncpy(font_name,
((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset),
be16_to_cpu (record->length));
font_name[be16_to_cpu (record->length)] = 0;
}
 
if (be16_to_cpu (record->name) == 6) {
ps_name = malloc (be16_to_cpu(record->length) + 1);
if (ps_name == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail;
}
strncpy(ps_name,
((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset),
be16_to_cpu (record->length));
ps_name[be16_to_cpu (record->length)] = 0;
}
 
if (font_name && ps_name)
break;
}
}
 
free (name);
 
/* Ensure PS name does not contain any spaces */
if (ps_name) {
for (i = 0, j = 0; ps_name[j]; j++) {
if (ps_name[j] == ' ')
continue;
ps_name[i++] = ps_name[j];
}
ps_name[i] = '\0';
}
 
*ps_name_out = ps_name;
*font_name_out = font_name;
 
return CAIRO_STATUS_SUCCESS;
 
fail:
free (name);
 
if (ps_name != NULL)
free (ps_name);
 
if (font_name != NULL)
free (font_name);
 
*ps_name_out = NULL;
*font_name_out = NULL;
 
return status;
}
 
#endif /* CAIRO_HAS_FONT_SUBSET */
/programs/develop/libraries/cairo/src/cairo-type1-fallback.c
0,0 → 1,887
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2006 Red Hat, Inc
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Adrian Johnson <ajohnson@redneon.com>
*/
 
#define _BSD_SOURCE /* for snprintf(), strdup() */
#include "cairoint.h"
#include "cairo-error-private.h"
 
#if CAIRO_HAS_FONT_SUBSET
 
#include "cairo-type1-private.h"
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-output-stream-private.h"
 
typedef enum {
CAIRO_CHARSTRING_TYPE1,
CAIRO_CHARSTRING_TYPE2
} cairo_charstring_type_t;
 
typedef struct _cairo_type1_font {
int *widths;
 
cairo_scaled_font_subset_t *scaled_font_subset;
cairo_scaled_font_t *type1_scaled_font;
 
cairo_array_t contents;
 
double x_min, y_min, x_max, y_max;
 
const char *data;
unsigned long header_size;
unsigned long data_size;
unsigned long trailer_size;
int bbox_position;
int bbox_max_chars;
 
cairo_output_stream_t *output;
 
unsigned short eexec_key;
cairo_bool_t hex_encode;
int hex_column;
} cairo_type1_font_t;
 
static cairo_status_t
cairo_type1_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
cairo_type1_font_t **subset_return,
cairo_bool_t hex_encode)
{
cairo_type1_font_t *font;
cairo_font_face_t *font_face;
cairo_matrix_t font_matrix;
cairo_matrix_t ctm;
cairo_font_options_t font_options;
cairo_status_t status;
 
font = calloc (1, sizeof (cairo_type1_font_t));
if (unlikely (font == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
font->widths = calloc (scaled_font_subset->num_glyphs, sizeof (int));
if (unlikely (font->widths == NULL)) {
free (font);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
font->scaled_font_subset = scaled_font_subset;
font->hex_encode = hex_encode;
 
font_face = cairo_scaled_font_get_font_face (scaled_font_subset->scaled_font);
 
cairo_matrix_init_scale (&font_matrix, 1000, -1000);
cairo_matrix_init_identity (&ctm);
 
_cairo_font_options_init_default (&font_options);
cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF);
 
font->type1_scaled_font = cairo_scaled_font_create (font_face,
&font_matrix,
&ctm,
&font_options);
status = font->type1_scaled_font->status;
if (unlikely (status))
goto fail;
 
_cairo_array_init (&font->contents, sizeof (unsigned char));
font->output = NULL;
 
*subset_return = font;
 
return CAIRO_STATUS_SUCCESS;
 
fail:
free (font->widths);
free (font);
 
return status;
}
 
/* Charstring commands. If the high byte is 0 the command is encoded
* with a single byte. */
#define CHARSTRING_sbw 0x0c07
#define CHARSTRING_rmoveto 0x0015
#define CHARSTRING_rlineto 0x0005
#define CHARSTRING_rcurveto 0x0008
#define CHARSTRING_closepath 0x0009
#define CHARSTRING_endchar 0x000e
 
/* Before calling this function, the caller must allocate sufficient
* space in data (see _cairo_array_grow_by). The maximum number of
* bytes that will be used is 2.
*/
static void
charstring_encode_command (cairo_array_t *data, int command)
{
cairo_status_t status;
int orig_size;
unsigned char buf[5];
unsigned char *p = buf;
 
if (command & 0xff00)
*p++ = command >> 8;
*p++ = command & 0x00ff;
 
/* Ensure the array doesn't grow, which allows this function to
* have no possibility of failure. */
orig_size = _cairo_array_size (data);
status = _cairo_array_append_multiple (data, buf, p - buf);
 
assert (status == CAIRO_STATUS_SUCCESS);
assert (_cairo_array_size (data) == orig_size);
}
 
/* Before calling this function, the caller must allocate sufficient
* space in data (see _cairo_array_grow_by). The maximum number of
* bytes that will be used is 5.
*/
static void
charstring_encode_integer (cairo_array_t *data,
int i,
cairo_charstring_type_t type)
{
cairo_status_t status;
int orig_size;
unsigned char buf[10];
unsigned char *p = buf;
 
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 (type == CAIRO_CHARSTRING_TYPE1) {
*p++ = 0xff;
*p++ = i >> 24;
*p++ = (i >> 16) & 0xff;
*p++ = (i >> 8) & 0xff;
*p++ = i & 0xff;
} else {
*p++ = 0xff;
*p++ = (i >> 8) & 0xff;
*p++ = i & 0xff;
*p++ = 0;
*p++ = 0;
}
}
 
/* Ensure the array doesn't grow, which allows this function to
* have no possibility of failure. */
orig_size = _cairo_array_size (data);
status = _cairo_array_append_multiple (data, buf, p - buf);
 
assert (status == CAIRO_STATUS_SUCCESS);
assert (_cairo_array_size (data) == orig_size);
}
 
typedef struct _ps_path_info {
cairo_array_t *data;
int current_x, current_y;
cairo_charstring_type_t type;
} t1_path_info_t;
 
static cairo_status_t
_charstring_move_to (void *closure,
const cairo_point_t *point)
{
t1_path_info_t *path_info = (t1_path_info_t *) closure;
int dx, dy;
cairo_status_t status;
 
status = _cairo_array_grow_by (path_info->data, 12);
if (unlikely (status))
return status;
 
dx = _cairo_fixed_integer_part (point->x) - path_info->current_x;
dy = _cairo_fixed_integer_part (point->y) - path_info->current_y;
charstring_encode_integer (path_info->data, dx, path_info->type);
charstring_encode_integer (path_info->data, dy, path_info->type);
path_info->current_x += dx;
path_info->current_y += dy;
 
charstring_encode_command (path_info->data, CHARSTRING_rmoveto);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_charstring_line_to (void *closure,
const cairo_point_t *point)
{
t1_path_info_t *path_info = (t1_path_info_t *) closure;
int dx, dy;
cairo_status_t status;
 
status = _cairo_array_grow_by (path_info->data, 12);
if (unlikely (status))
return status;
 
dx = _cairo_fixed_integer_part (point->x) - path_info->current_x;
dy = _cairo_fixed_integer_part (point->y) - path_info->current_y;
charstring_encode_integer (path_info->data, dx, path_info->type);
charstring_encode_integer (path_info->data, dy, path_info->type);
path_info->current_x += dx;
path_info->current_y += dy;
 
charstring_encode_command (path_info->data, CHARSTRING_rlineto);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_charstring_curve_to (void *closure,
const cairo_point_t *point1,
const cairo_point_t *point2,
const cairo_point_t *point3)
{
t1_path_info_t *path_info = (t1_path_info_t *) closure;
int dx1, dy1, dx2, dy2, dx3, dy3;
cairo_status_t status;
 
status = _cairo_array_grow_by (path_info->data, 32);
if (unlikely (status))
return status;
 
dx1 = _cairo_fixed_integer_part (point1->x) - path_info->current_x;
dy1 = _cairo_fixed_integer_part (point1->y) - path_info->current_y;
dx2 = _cairo_fixed_integer_part (point2->x) - path_info->current_x - dx1;
dy2 = _cairo_fixed_integer_part (point2->y) - path_info->current_y - dy1;
dx3 = _cairo_fixed_integer_part (point3->x) - path_info->current_x - dx1 - dx2;
dy3 = _cairo_fixed_integer_part (point3->y) - path_info->current_y - dy1 - dy2;
charstring_encode_integer (path_info->data, dx1, path_info->type);
charstring_encode_integer (path_info->data, dy1, path_info->type);
charstring_encode_integer (path_info->data, dx2, path_info->type);
charstring_encode_integer (path_info->data, dy2, path_info->type);
charstring_encode_integer (path_info->data, dx3, path_info->type);
charstring_encode_integer (path_info->data, dy3, path_info->type);
path_info->current_x += dx1 + dx2 + dx3;
path_info->current_y += dy1 + dy2 + dy3;
charstring_encode_command (path_info->data, CHARSTRING_rcurveto);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_charstring_close_path (void *closure)
{
cairo_status_t status;
t1_path_info_t *path_info = (t1_path_info_t *) closure;
 
if (path_info->type == CAIRO_CHARSTRING_TYPE2)
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_array_grow_by (path_info->data, 2);
if (unlikely (status))
return status;
 
charstring_encode_command (path_info->data, CHARSTRING_closepath);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
charstring_encrypt (cairo_array_t *data)
{
unsigned char *d, *end;
uint16_t c, p, r;
 
r = CAIRO_TYPE1_CHARSTRING_KEY;
d = (unsigned char *) _cairo_array_index (data, 0);
end = d + _cairo_array_num_elements (data);
while (d < end) {
p = *d;
c = p ^ (r >> 8);
r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
*d++ = c;
}
}
 
static cairo_int_status_t
cairo_type1_font_create_charstring (cairo_type1_font_t *font,
int subset_index,
int glyph_index,
cairo_charstring_type_t type,
cairo_array_t *data)
{
cairo_int_status_t status;
cairo_scaled_glyph_t *scaled_glyph;
t1_path_info_t path_info;
cairo_text_extents_t *metrics;
cairo_bool_t emit_path = TRUE;
 
/* This call may return CAIRO_INT_STATUS_UNSUPPORTED for bitmap fonts. */
status = _cairo_scaled_glyph_lookup (font->type1_scaled_font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS|
CAIRO_SCALED_GLYPH_INFO_PATH,
&scaled_glyph);
 
/* It is ok for the .notdef glyph to not have a path available. We
* just need the metrics to emit an empty glyph. */
if (glyph_index == 0 && status == CAIRO_INT_STATUS_UNSUPPORTED) {
emit_path = FALSE;
status = _cairo_scaled_glyph_lookup (font->type1_scaled_font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&scaled_glyph);
}
if (unlikely (status))
return status;
 
metrics = &scaled_glyph->metrics;
if (subset_index == 0) {
font->x_min = metrics->x_bearing;
font->y_min = metrics->y_bearing;
font->x_max = metrics->x_bearing + metrics->width;
font->y_max = metrics->y_bearing + metrics->height;
} else {
if (metrics->x_bearing < font->x_min)
font->x_min = metrics->x_bearing;
if (metrics->y_bearing < font->y_min)
font->y_min = metrics->y_bearing;
if (metrics->x_bearing + metrics->width > font->x_max)
font->x_max = metrics->x_bearing + metrics->width;
if (metrics->y_bearing + metrics->height > font->y_max)
font->y_max = metrics->y_bearing + metrics->height;
}
font->widths[subset_index] = metrics->x_advance;
 
status = _cairo_array_grow_by (data, 30);
if (unlikely (status))
return status;
 
if (type == CAIRO_CHARSTRING_TYPE1) {
charstring_encode_integer (data, (int) scaled_glyph->metrics.x_bearing, type);
charstring_encode_integer (data, (int) scaled_glyph->metrics.y_bearing, type);
charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type);
charstring_encode_integer (data, (int) scaled_glyph->metrics.y_advance, type);
charstring_encode_command (data, CHARSTRING_sbw);
 
path_info.current_x = (int) scaled_glyph->metrics.x_bearing;
path_info.current_y = (int) scaled_glyph->metrics.y_bearing;
} else {
charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type);
 
path_info.current_x = 0;
path_info.current_y = 0;
}
path_info.data = data;
path_info.type = type;
if (emit_path) {
status = _cairo_path_fixed_interpret (scaled_glyph->path,
CAIRO_DIRECTION_FORWARD,
_charstring_move_to,
_charstring_line_to,
_charstring_curve_to,
_charstring_close_path,
&path_info);
if (unlikely (status))
return status;
}
 
status = _cairo_array_grow_by (data, 1);
if (unlikely (status))
return status;
charstring_encode_command (path_info.data, CHARSTRING_endchar);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
cairo_type1_font_write_charstrings (cairo_type1_font_t *font,
cairo_output_stream_t *encrypted_output)
{
cairo_status_t status;
unsigned char zeros[] = { 0, 0, 0, 0 };
cairo_array_t data;
unsigned int i;
int length;
 
_cairo_array_init (&data, sizeof (unsigned char));
status = _cairo_array_grow_by (&data, 1024);
if (unlikely (status))
goto fail;
 
_cairo_output_stream_printf (encrypted_output,
"2 index /CharStrings %d dict dup begin\n",
font->scaled_font_subset->num_glyphs + 1);
 
_cairo_scaled_font_freeze_cache (font->type1_scaled_font);
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
_cairo_array_truncate (&data, 0);
/* four "random" bytes required by encryption algorithm */
status = _cairo_array_append_multiple (&data, zeros, 4);
if (unlikely (status))
break;
 
status = cairo_type1_font_create_charstring (font, i,
font->scaled_font_subset->glyphs[i],
CAIRO_CHARSTRING_TYPE1,
&data);
if (unlikely (status))
break;
 
charstring_encrypt (&data);
length = _cairo_array_num_elements (&data);
if (font->scaled_font_subset->glyph_names != NULL) {
_cairo_output_stream_printf (encrypted_output, "/%s %d RD ",
font->scaled_font_subset->glyph_names[i],
length);
} else if (i == 0) {
_cairo_output_stream_printf (encrypted_output, "/.notdef %d RD ", length);
} else {
_cairo_output_stream_printf (encrypted_output, "/g%d %d RD ", i, length);
}
_cairo_output_stream_write (encrypted_output,
_cairo_array_index (&data, 0),
length);
_cairo_output_stream_printf (encrypted_output, " ND\n");
}
_cairo_scaled_font_thaw_cache (font->type1_scaled_font);
 
fail:
_cairo_array_fini (&data);
return status;
}
 
static void
cairo_type1_font_write_header (cairo_type1_font_t *font,
const char *name)
{
unsigned int i;
const char spaces[50] = " ";
 
_cairo_output_stream_printf (font->output,
"%%!FontType1-1.1 %s 1.0\n"
"11 dict begin\n"
"/FontName /%s def\n"
"/PaintType 0 def\n"
"/FontType 1 def\n"
"/FontMatrix [0.001 0 0 0.001 0 0] readonly def\n",
name,
name);
 
/* We don't know the bbox values until after the charstrings have
* been generated. Reserve some space and fill in the bbox
* later. */
 
/* Worst case for four signed ints with spaces between each number */
font->bbox_max_chars = 50;
 
_cairo_output_stream_printf (font->output, "/FontBBox {");
font->bbox_position = _cairo_output_stream_get_position (font->output);
_cairo_output_stream_write (font->output, spaces, font->bbox_max_chars);
 
_cairo_output_stream_printf (font->output,
"} readonly def\n"
"/Encoding 256 array\n"
"0 1 255 {1 index exch /.notdef put} for\n");
for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
if (font->scaled_font_subset->glyph_names != NULL) {
_cairo_output_stream_printf (font->output, "dup %d /%s put\n",
i, font->scaled_font_subset->glyph_names[i]);
} else {
_cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i);
}
}
_cairo_output_stream_printf (font->output,
"readonly def\n"
"currentdict end\n"
"currentfile eexec\n");
}
 
static cairo_status_t
cairo_type1_write_stream_encrypted (void *closure,
const unsigned char *data,
unsigned int length)
{
const unsigned char *in, *end;
uint16_t c, p;
static const char hex_digits[16] = "0123456789abcdef";
char digits[3];
cairo_type1_font_t *font = closure;
 
in = (const unsigned char *) data;
end = (const unsigned char *) data + length;
while (in < end) {
p = *in++;
c = p ^ (font->eexec_key >> 8);
font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
 
if (font->hex_encode) {
digits[0] = hex_digits[c >> 4];
digits[1] = hex_digits[c & 0x0f];
digits[2] = '\n';
font->hex_column += 2;
 
if (font->hex_column == 78) {
_cairo_output_stream_write (font->output, digits, 3);
font->hex_column = 0;
} else {
_cairo_output_stream_write (font->output, digits, 2);
}
} else {
digits[0] = c;
_cairo_output_stream_write (font->output, digits, 1);
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
cairo_type1_font_write_private_dict (cairo_type1_font_t *font,
const char *name)
{
cairo_int_status_t status;
cairo_status_t status2;
cairo_output_stream_t *encrypted_output;
 
font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY;
font->hex_column = 0;
encrypted_output = _cairo_output_stream_create (
cairo_type1_write_stream_encrypted,
NULL,
font);
if (_cairo_output_stream_get_status (encrypted_output))
return _cairo_output_stream_destroy (encrypted_output);
 
/* Note: the first four spaces at the start of this private dict
* are the four "random" bytes of plaintext required by the
* encryption algorithm */
_cairo_output_stream_printf (encrypted_output,
" dup /Private 9 dict dup begin\n"
"/RD {string currentfile exch readstring pop}"
" bind executeonly def\n"
"/ND {noaccess def} executeonly def\n"
"/NP {noaccess put} executeonly def\n"
"/BlueValues [] def\n"
"/MinFeature {16 16} def\n"
"/lenIV 4 def\n"
"/password 5839 def\n");
 
status = cairo_type1_font_write_charstrings (font, encrypted_output);
if (unlikely (status))
goto fail;
 
_cairo_output_stream_printf (encrypted_output,
"end\n"
"end\n"
"readonly put\n"
"noaccess put\n"
"dup /FontName get exch definefont pop\n"
"mark currentfile closefile\n");
 
fail:
status2 = _cairo_output_stream_destroy (encrypted_output);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
return status;
}
 
static void
cairo_type1_font_write_trailer(cairo_type1_font_t *font)
{
int i;
static const char zeros[65] =
"0000000000000000000000000000000000000000000000000000000000000000\n";
 
for (i = 0; i < 8; i++)
_cairo_output_stream_write (font->output, zeros, sizeof zeros);
 
_cairo_output_stream_printf (font->output, "cleartomark\n");
}
 
static cairo_status_t
cairo_type1_write_stream (void *closure,
const unsigned char *data,
unsigned int length)
{
cairo_type1_font_t *font = closure;
 
return _cairo_array_append_multiple (&font->contents, data, length);
}
 
static cairo_int_status_t
cairo_type1_font_write (cairo_type1_font_t *font,
const char *name)
{
cairo_int_status_t status;
 
cairo_type1_font_write_header (font, name);
font->header_size = _cairo_output_stream_get_position (font->output);
 
status = cairo_type1_font_write_private_dict (font, name);
if (unlikely (status))
return status;
 
font->data_size = _cairo_output_stream_get_position (font->output) -
font->header_size;
 
cairo_type1_font_write_trailer (font);
font->trailer_size =
_cairo_output_stream_get_position (font->output) -
font->header_size - font->data_size;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
cairo_type1_font_generate (cairo_type1_font_t *font, const char *name)
{
cairo_int_status_t status;
 
status = _cairo_array_grow_by (&font->contents, 4096);
if (unlikely (status))
return status;
 
font->output = _cairo_output_stream_create (cairo_type1_write_stream, NULL, font);
if (_cairo_output_stream_get_status (font->output))
return _cairo_output_stream_destroy (font->output);
 
status = cairo_type1_font_write (font, name);
if (unlikely (status))
return status;
 
font->data = _cairo_array_index (&font->contents, 0);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_type1_font_destroy (cairo_type1_font_t *font)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
free (font->widths);
cairo_scaled_font_destroy (font->type1_scaled_font);
_cairo_array_fini (&font->contents);
if (font->output)
status = _cairo_output_stream_destroy (font->output);
free (font);
 
return status;
}
 
static cairo_status_t
_cairo_type1_fallback_init_internal (cairo_type1_subset_t *type1_subset,
const char *name,
cairo_scaled_font_subset_t *scaled_font_subset,
cairo_bool_t hex_encode)
{
cairo_type1_font_t *font;
cairo_status_t status;
unsigned long length;
unsigned int i, len;
 
status = cairo_type1_font_create (scaled_font_subset, &font, hex_encode);
if (unlikely (status))
return status;
 
status = cairo_type1_font_generate (font, name);
if (unlikely (status))
goto fail1;
 
type1_subset->base_font = strdup (name);
if (unlikely (type1_subset->base_font == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail1;
}
 
type1_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
if (unlikely (type1_subset->widths == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail2;
}
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
type1_subset->widths[i] = (double)font->widths[i]/1000;
 
type1_subset->x_min = (double)font->x_min/1000;
type1_subset->y_min = (double)font->y_min/1000;
type1_subset->x_max = (double)font->x_max/1000;
type1_subset->y_max = (double)font->y_max/1000;
type1_subset->ascent = (double)font->y_max/1000;
type1_subset->descent = (double)font->y_min/1000;
 
length = font->header_size + font->data_size +
font->trailer_size;
type1_subset->data = malloc (length);
if (unlikely (type1_subset->data == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail3;
}
memcpy (type1_subset->data,
_cairo_array_index (&font->contents, 0), length);
 
len = snprintf(type1_subset->data + font->bbox_position,
font->bbox_max_chars,
"%d %d %d %d",
(int)font->x_min,
(int)font->y_min,
(int)font->x_max,
(int)font->y_max);
type1_subset->data[font->bbox_position + len] = ' ';
 
type1_subset->header_length = font->header_size;
type1_subset->data_length = font->data_size;
type1_subset->trailer_length = font->trailer_size;
 
return cairo_type1_font_destroy (font);
 
fail3:
free (type1_subset->widths);
fail2:
free (type1_subset->base_font);
fail1:
/* status is already set, ignore further errors */
cairo_type1_font_destroy (font);
 
return status;
}
 
cairo_status_t
_cairo_type1_fallback_init_binary (cairo_type1_subset_t *type1_subset,
const char *name,
cairo_scaled_font_subset_t *scaled_font_subset)
{
return _cairo_type1_fallback_init_internal (type1_subset,
name,
scaled_font_subset, FALSE);
}
 
cairo_status_t
_cairo_type1_fallback_init_hex (cairo_type1_subset_t *type1_subset,
const char *name,
cairo_scaled_font_subset_t *scaled_font_subset)
{
return _cairo_type1_fallback_init_internal (type1_subset,
name,
scaled_font_subset, TRUE);
}
 
void
_cairo_type1_fallback_fini (cairo_type1_subset_t *subset)
{
free (subset->base_font);
free (subset->widths);
free (subset->data);
}
 
cairo_status_t
_cairo_type2_charstrings_init (cairo_type2_charstrings_t *type2_subset,
cairo_scaled_font_subset_t *scaled_font_subset)
{
cairo_type1_font_t *font;
cairo_status_t status;
unsigned int i;
cairo_array_t charstring;
 
status = cairo_type1_font_create (scaled_font_subset, &font, FALSE);
if (unlikely (status))
return status;
 
_cairo_array_init (&type2_subset->charstrings, sizeof (cairo_array_t));
 
type2_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs);
if (unlikely (type2_subset->widths == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail1;
}
 
_cairo_scaled_font_freeze_cache (font->type1_scaled_font);
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
_cairo_array_init (&charstring, sizeof (unsigned char));
status = _cairo_array_grow_by (&charstring, 32);
if (unlikely (status))
goto fail2;
 
status = cairo_type1_font_create_charstring (font, i,
font->scaled_font_subset->glyphs[i],
CAIRO_CHARSTRING_TYPE2,
&charstring);
if (unlikely (status))
goto fail2;
 
status = _cairo_array_append (&type2_subset->charstrings, &charstring);
if (unlikely (status))
goto fail2;
}
_cairo_scaled_font_thaw_cache (font->type1_scaled_font);
 
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
type2_subset->widths[i] = font->widths[i];
 
type2_subset->x_min = (int) font->x_min;
type2_subset->y_min = (int) font->y_min;
type2_subset->x_max = (int) font->x_max;
type2_subset->y_max = (int) font->y_max;
type2_subset->ascent = (int) font->y_max;
type2_subset->descent = (int) font->y_min;
 
return cairo_type1_font_destroy (font);
 
fail2:
_cairo_scaled_font_thaw_cache (font->type1_scaled_font);
_cairo_array_fini (&charstring);
_cairo_type2_charstrings_fini (type2_subset);
fail1:
cairo_type1_font_destroy (font);
return status;
}
 
void
_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *type2_subset)
{
unsigned int i, num_charstrings;
cairo_array_t *charstring;
 
num_charstrings = _cairo_array_num_elements (&type2_subset->charstrings);
for (i = 0; i < num_charstrings; i++) {
charstring = _cairo_array_index (&type2_subset->charstrings, i);
_cairo_array_fini (charstring);
}
_cairo_array_fini (&type2_subset->charstrings);
 
free (type2_subset->widths);
}
 
#endif /* CAIRO_HAS_FONT_SUBSET */
/programs/develop/libraries/cairo/src/cairo-type1-subset.c
0,0 → 1,1431
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2006 Red Hat, Inc
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Kristian Høgsberg <krh@redhat.com>
*/
 
/*
* Useful links:
* http://partners.adobe.com/public/developer/en/font/T1_SPEC.PDF
*/
 
 
#define _BSD_SOURCE /* for snprintf(), strdup() */
#include "cairoint.h"
#include "cairo-error-private.h"
 
#if CAIRO_HAS_FONT_SUBSET
 
#include "cairo-type1-private.h"
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-output-stream-private.h"
 
/* XXX: Eventually, we need to handle other font backends */
#if CAIRO_HAS_FT_FONT
 
#include "cairo-ft-private.h"
 
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include FT_TYPE1_TABLES_H
 
#include <ctype.h>
 
typedef struct _cairo_type1_font_subset {
cairo_scaled_font_subset_t *scaled_font_subset;
 
struct {
cairo_unscaled_font_t *unscaled_font;
unsigned int font_id;
char *base_font;
unsigned int num_glyphs;
double x_min, y_min, x_max, y_max;
double ascent, descent;
 
const char *data;
unsigned long header_size;
unsigned long data_size;
unsigned long trailer_size;
} base;
 
FT_Face face;
int num_glyphs;
 
struct {
int subset_index;
double width;
char *name;
} *glyphs;
 
cairo_output_stream_t *output;
cairo_array_t contents;
 
const char *rd, *nd;
 
char *type1_data;
unsigned int type1_length;
char *type1_end;
 
char *header_segment;
int header_segment_size;
char *eexec_segment;
int eexec_segment_size;
cairo_bool_t eexec_segment_is_ascii;
 
char *cleartext;
char *cleartext_end;
 
int header_size;
 
unsigned short eexec_key;
cairo_bool_t hex_encode;
int hex_column;
} cairo_type1_font_subset_t;
 
 
static cairo_status_t
_cairo_type1_font_subset_init (cairo_type1_font_subset_t *font,
cairo_unscaled_font_t *unscaled_font,
cairo_bool_t hex_encode)
{
cairo_ft_unscaled_font_t *ft_unscaled_font;
cairo_status_t status;
FT_Face face;
PS_FontInfoRec font_info;
int i, j;
 
ft_unscaled_font = (cairo_ft_unscaled_font_t *) unscaled_font;
 
face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font);
if (unlikely (face == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (FT_Get_PS_Font_Info(face, &font_info) != 0) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto fail1;
}
 
/* OpenType/CFF fonts also have a PS_FontInfoRec */
#if HAVE_FT_LOAD_SFNT_TABLE
if (FT_IS_SFNT (face)) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto fail1;
}
#endif
 
memset (font, 0, sizeof (*font));
font->base.unscaled_font = _cairo_unscaled_font_reference (unscaled_font);
font->base.num_glyphs = face->num_glyphs;
font->base.x_min = face->bbox.xMin / (double)face->units_per_EM;
font->base.y_min = face->bbox.yMin / (double)face->units_per_EM;
font->base.x_max = face->bbox.xMax / (double)face->units_per_EM;
font->base.y_max = face->bbox.yMax / (double)face->units_per_EM;
font->base.ascent = face->ascender / (double)face->units_per_EM;
font->base.descent = face->descender / (double)face->units_per_EM;
 
if (face->family_name) {
font->base.base_font = strdup (face->family_name);
if (unlikely (font->base.base_font == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail2;
}
for (i = 0, j = 0; font->base.base_font[j]; j++) {
if (font->base.base_font[j] == ' ')
continue;
font->base.base_font[i++] = font->base.base_font[j];
}
font->base.base_font[i] = '\0';
}
 
font->glyphs = calloc (face->num_glyphs, sizeof font->glyphs[0]);
if (unlikely (font->glyphs == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail3;
}
 
font->hex_encode = hex_encode;
font->num_glyphs = 0;
for (i = 0; i < face->num_glyphs; i++)
font->glyphs[i].subset_index = -1;
 
_cairo_array_init (&font->contents, sizeof (char));
 
_cairo_ft_unscaled_font_unlock_face (ft_unscaled_font);
 
return CAIRO_STATUS_SUCCESS;
 
fail3:
if (font->base.base_font)
free (font->base.base_font);
fail2:
_cairo_unscaled_font_destroy (unscaled_font);
fail1:
_cairo_ft_unscaled_font_unlock_face (ft_unscaled_font);
 
return status;
}
 
static void
cairo_type1_font_subset_use_glyph (cairo_type1_font_subset_t *font, int glyph)
{
if (font->glyphs[glyph].subset_index >= 0)
return;
 
font->glyphs[glyph].subset_index = font->num_glyphs++;
}
 
static cairo_bool_t
is_ps_delimiter(int c)
{
static const char delimiters[] = "()[]{}<>/% \t\r\n";
 
return strchr (delimiters, c) != NULL;
}
 
static const char *
find_token (const char *buffer, const char *end, const char *token)
{
int i, length;
/* FIXME: find substring really must be find_token */
 
if (buffer == NULL)
return NULL;
 
length = strlen (token);
for (i = 0; buffer + i < end - length + 1; i++)
if (memcmp (buffer + i, token, length) == 0)
if ((i == 0 || token[0] == '/' || is_ps_delimiter(buffer[i - 1])) &&
(buffer + i == end - length || is_ps_delimiter(buffer[i + length])))
return buffer + i;
 
return NULL;
}
 
static cairo_status_t
cairo_type1_font_subset_find_segments (cairo_type1_font_subset_t *font)
{
unsigned char *p;
const char *eexec_token;
int size, i;
 
p = (unsigned char *) font->type1_data;
font->type1_end = font->type1_data + font->type1_length;
if (p[0] == 0x80 && p[1] == 0x01) {
font->header_segment_size =
p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24);
font->header_segment = (char *) p + 6;
 
p += 6 + font->header_segment_size;
font->eexec_segment_size =
p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24);
font->eexec_segment = (char *) p + 6;
font->eexec_segment_is_ascii = (p[1] == 1);
 
p += 6 + font->eexec_segment_size;
while (p < (unsigned char *) (font->type1_end) && p[1] != 0x03) {
size = p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24);
p += 6 + size;
}
font->type1_end = (char *) p;
} else {
eexec_token = find_token ((char *) p, font->type1_end, "eexec");
if (eexec_token == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
font->header_segment_size = eexec_token - (char *) p + strlen ("eexec\n");
font->header_segment = (char *) p;
font->eexec_segment_size = font->type1_length - font->header_segment_size;
font->eexec_segment = (char *) p + font->header_segment_size;
font->eexec_segment_is_ascii = TRUE;
for (i = 0; i < 4; i++) {
if (!isxdigit(font->eexec_segment[i]))
font->eexec_segment_is_ascii = FALSE;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
/* Search for the definition of key and erase it by overwriting with spaces.
* This function is looks for definitions of the form:
*
* /key1 1234 def
* /key2 [12 34 56] def
*
* ie a key defined as an integer or array of integers.
*
*/
static void
cairo_type1_font_erase_dict_key (cairo_type1_font_subset_t *font,
const char *key)
{
const char *start, *p, *segment_end;
 
segment_end = font->header_segment + font->header_segment_size;
 
start = font->header_segment;
do {
start = find_token (start, segment_end, key);
if (start) {
p = start + strlen(key);
/* skip integers or array of integers */
while (p < segment_end &&
(_cairo_isspace(*p) ||
_cairo_isdigit(*p) ||
*p == '[' ||
*p == ']'))
{
p++;
}
 
if (p + 3 < segment_end && memcmp(p, "def", 3) == 0) {
/* erase definition of the key */
memset((char *) start, ' ', p + 3 - start);
}
start += strlen(key);
}
} while (start);
}
 
static cairo_status_t
cairo_type1_font_subset_write_header (cairo_type1_font_subset_t *font,
const char *name)
{
const char *start, *end, *segment_end;
unsigned int i;
 
/* FIXME:
* This function assumes that /FontName always appears
* before /Encoding. This appears to always be the case with Type1
* fonts.
*
* The more recently added code for removing the UniqueID and XUID
* keys can not make any assumptions about the position of the
* keys in the dictionary so it is implemented by overwriting the
* key definition with spaces before we start copying the font to
* the output.
*
* This code should be rewritten to not make any assumptions about
* the order of dictionary keys. This will allow UniqueID to be
* stripped out instead of leaving a bunch of spaces in the
* output.
*/
cairo_type1_font_erase_dict_key (font, "/UniqueID");
cairo_type1_font_erase_dict_key (font, "/XUID");
 
segment_end = font->header_segment + font->header_segment_size;
 
/* Type 1 fonts created by Fontforge have some PostScript code at
* the start of the font that skips the font if the printer has a
* cached copy of the font with the same unique id. This breaks
* our subsetted font so we disable it by searching for the
* PostScript operator "known" when used to check for the
* "/UniqueID" dictionary key. We append " pop false " after it to
* pop the result of this check off the stack and replace it with
* "false" to make the PostScript code think "/UniqueID" does not
* exist.
*/
end = font->header_segment;
start = find_token (font->header_segment, segment_end, "/UniqueID");
if (start) {
start += 9;
while (start < segment_end && _cairo_isspace (*start))
start++;
if (start + 5 < segment_end && memcmp(start, "known", 5) == 0) {
_cairo_output_stream_write (font->output, font->header_segment,
start + 5 - font->header_segment);
_cairo_output_stream_printf (font->output, " pop false ");
end = start + 5;
}
}
 
start = find_token (end, segment_end, "/FontName");
if (start == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
_cairo_output_stream_write (font->output, end,
start - end);
 
_cairo_output_stream_printf (font->output, "/FontName /%s def", name);
 
end = find_token (start, segment_end, "def");
if (end == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
end += 3;
 
start = find_token (end, segment_end, "/Encoding");
if (start == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
_cairo_output_stream_write (font->output, end, start - end);
 
_cairo_output_stream_printf (font->output,
"/Encoding 256 array\n"
"0 1 255 {1 index exch /.notdef put} for\n");
for (i = 1; i < font->base.num_glyphs; i++) {
if (font->glyphs[i].subset_index < 0)
continue;
_cairo_output_stream_printf (font->output,
"dup %d /%s put\n",
font->glyphs[i].subset_index,
font->glyphs[i].name);
}
_cairo_output_stream_printf (font->output, "readonly def");
 
end = find_token (start, segment_end, "def");
if (end == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
end += 3;
 
_cairo_output_stream_write (font->output, end, segment_end - end);
 
return font->output->status;
}
 
static int
hex_to_int (int ch)
{
if (ch <= '9')
return ch - '0';
else if (ch <= 'F')
return ch - 'A' + 10;
else
return ch - 'a' + 10;
}
 
static cairo_status_t
cairo_type1_font_subset_write_encrypted (cairo_type1_font_subset_t *font,
const char *data, unsigned int length)
{
const unsigned char *in, *end;
int c, p;
static const char hex_digits[16] = "0123456789abcdef";
char digits[3];
 
in = (const unsigned char *) data;
end = (const unsigned char *) data + length;
while (in < end) {
p = *in++;
c = p ^ (font->eexec_key >> 8);
font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
 
if (font->hex_encode) {
digits[0] = hex_digits[c >> 4];
digits[1] = hex_digits[c & 0x0f];
digits[2] = '\n';
font->hex_column += 2;
 
if (font->hex_column == 78) {
_cairo_output_stream_write (font->output, digits, 3);
font->hex_column = 0;
} else {
_cairo_output_stream_write (font->output, digits, 2);
}
} else {
digits[0] = c;
_cairo_output_stream_write (font->output, digits, 1);
}
}
 
return font->output->status;
}
 
static cairo_status_t
cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font)
{
unsigned short r = CAIRO_TYPE1_PRIVATE_DICT_KEY;
unsigned char *in, *end;
char *out;
int c, p;
int i;
 
in = (unsigned char *) font->eexec_segment;
end = (unsigned char *) in + font->eexec_segment_size;
 
font->cleartext = malloc (font->eexec_segment_size);
if (unlikely (font->cleartext == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
out = font->cleartext;
while (in < end) {
if (font->eexec_segment_is_ascii) {
c = *in++;
if (_cairo_isspace (c))
continue;
c = (hex_to_int (c) << 4) | hex_to_int (*in++);
} else {
c = *in++;
}
p = c ^ (r >> 8);
r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
 
*out++ = p;
}
font->cleartext_end = out;
 
/* Overwrite random bytes with spaces.
*
* The first 4 bytes of the cleartext are the random bytes
* required by the encryption algorithm. When encrypting the
* cleartext, the first ciphertext byte must not be a white space
* character and the first 4 bytes must not be an ASCII Hex
* character. Some fonts do not check that their randomly chosen
* bytes results in ciphertext that complies with this
* restriction. This may cause problems for some PDF consumers. By
* replacing the random bytes with spaces, the first four bytes of
* ciphertext will always be 0xf9, 0x83, 0xef, 0x00 which complies
* with this restriction. Using spaces also means we don't have to
* skip over the random bytes when parsing the cleartext.
*/
for (i = 0; i < 4 && i < font->eexec_segment_size; i++)
font->cleartext[i] = ' ';
 
return CAIRO_STATUS_SUCCESS;
}
 
static const char *
skip_token (const char *p, const char *end)
{
while (p < end && _cairo_isspace(*p))
p++;
 
while (p < end && !_cairo_isspace(*p))
p++;
 
if (p == end)
return NULL;
 
return p;
}
 
static int
cairo_type1_font_subset_lookup_glyph (cairo_type1_font_subset_t *font,
const char *glyph_name, int length)
{
unsigned int i;
 
for (i = 0; i < font->base.num_glyphs; i++) {
if (font->glyphs[i].name &&
strncmp (font->glyphs[i].name, glyph_name, length) == 0 &&
font->glyphs[i].name[length] == '\0')
return i;
}
 
return -1;
}
 
static cairo_status_t
cairo_type1_font_subset_get_glyph_names_and_widths (cairo_type1_font_subset_t *font)
{
unsigned int i;
char buffer[256];
FT_Error error;
 
/* Get glyph names and width using the freetype API */
for (i = 0; i < font->base.num_glyphs; i++) {
if (font->glyphs[i].name != NULL)
continue;
 
error = FT_Load_Glyph (font->face, i,
FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING |
FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM);
if (error != FT_Err_Ok) {
/* propagate fatal errors from FreeType */
if (error == FT_Err_Out_Of_Memory)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
font->glyphs[i].width = font->face->glyph->metrics.horiAdvance / (double)font->face->units_per_EM;
 
error = FT_Get_Glyph_Name(font->face, i, buffer, sizeof buffer);
if (error != FT_Err_Ok) {
/* propagate fatal errors from FreeType */
if (error == FT_Err_Out_Of_Memory)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
font->glyphs[i].name = strdup (buffer);
if (unlikely (font->glyphs[i].name == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
cairo_type1_font_subset_decrypt_charstring (const unsigned char *in, int size, unsigned char *out)
{
unsigned short r = CAIRO_TYPE1_CHARSTRING_KEY;
int c, p, i;
 
for (i = 0; i < size; i++) {
c = *in++;
p = c ^ (r >> 8);
r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2;
*out++ = p;
}
}
 
static const unsigned char *
cairo_type1_font_subset_decode_integer (const unsigned char *p, int *integer)
{
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 {
*integer = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4];
p += 5;
}
 
return p;
}
 
#if 0
/*
* The two tables that follow are generated using this perl code:
*/
 
@encoding = (
/* 0 */
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
/* 16 */
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
/* 32 */
"space", "exclam", "quotedbl", "numbersign",
"dollar", "percent", "ampersand", "quoteright",
"parenleft", "parenright", "asterisk", "plus",
"comma", "hyphen", "period", "slash",
/* 48 */
"zero", "one", "two", "three",
"four", "five", "six", "seven",
"eight", "nine", "colon", "semicolon",
"less", "equal", "greater", "question",
/* 64 */
"at", "A", "B", "C",
"D", "E", "F", "G",
"H", "I", "J", "K",
"L", "M", "N", "O",
/* 80 */
"P", "Q", "R", "S",
"T", "U", "V", "W",
"X", "Y", "Z", "bracketleft",
"backslash", "bracketright", "asciicircum", "underscore",
/* 96 */
"quoteleft", "a", "b", "c",
"d", "e", "f", "g",
"h", "i", "j", "k",
"l", "m", "n", "o",
/* 112 */
"p", "q", "r", "s",
"t", "u", "v", "w",
"x", "y", "z", "braceleft",
"bar", "braceright", "asciitilde", NULL,
/* 128 */
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
/* 144 */
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
/* 160 */
NULL, "exclamdown", "cent", "sterling",
"fraction", "yen", "florin", "section",
"currency", "quotesingle", "quotedblleft", "guillemotleft",
"guilsinglleft","guilsinglright","fi", "fl",
/* 176 */
NULL, "endash", "dagger", "daggerdbl",
"periodcentered",NULL, "paragraph", "bullet",
"quotesinglbase","quotedblbase","quotedblright","guillemotright",
"ellipsis", "perthousand", NULL, "questiondown",
/* 192 */
NULL, "grave", "acute", "circumflex",
"tilde", "macron", "breve", "dotaccent",
"dieresis", NULL, "ring", "cedilla",
NULL, "hungarumlaut", "ogonek", "caron",
/* 208 */
"emdash", NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
/* 224 */
NULL, "AE", NULL, "ordfeminine",
NULL, NULL, NULL, NULL,
"Lslash", "Oslash", "OE", "ordmasculine",
NULL, NULL, NULL, NULL,
/* 240 */
NULL, "ae", NULL, NULL,
NULL, "dotlessi", NULL, NULL,
"lslash", "oslash", "oe", "germandbls",
NULL, NULL, NULL, NULL
);
 
print "static const char ps_standard_encoding_symbol[] = {\n";
$s = qq( "\\0");
for $sym (@encoding) {
if (! ($sym eq NULL)) {
$ss = qq( "$sym\\0");
if (length($s) + length($ss) > 78) {
print qq( $s\n);
$s = "";
}
$s .= $ss;
}
}
print qq( $s\n);
print "};\n\n";
print "static const int16_t ps_standard_encoding_offset[256] = {\n";
$offset = 1;
$s = qq();
for $sym (@encoding) {
if (! ($sym eq NULL)) {
$ss = qq( $offset/*$sym*/,);
$offset += length($sym) + 1;
} else {
$ss = qq( 0,);
}
if (length($s) + length($ss) > 78) {
print qq( $s\n);
$s = "";
}
$s .= $ss;
}
print qq( $s\n);
print "};\n";
exit;
#endif
 
static const char ps_standard_encoding_symbol[] = {
"\0" "space\0" "exclam\0" "quotedbl\0" "numbersign\0" "dollar\0" "percent\0"
"ampersand\0" "quoteright\0" "parenleft\0" "parenright\0" "asterisk\0"
"plus\0" "comma\0" "hyphen\0" "period\0" "slash\0" "zero\0" "one\0" "two\0"
"three\0" "four\0" "five\0" "six\0" "seven\0" "eight\0" "nine\0" "colon\0"
"semicolon\0" "less\0" "equal\0" "greater\0" "question\0" "at\0" "A\0" "B\0"
"C\0" "D\0" "E\0" "F\0" "G\0" "H\0" "I\0" "J\0" "K\0" "L\0" "M\0" "N\0" "O\0"
"P\0" "Q\0" "R\0" "S\0" "T\0" "U\0" "V\0" "W\0" "X\0" "Y\0" "Z\0"
"bracketleft\0" "backslash\0" "bracketright\0" "asciicircum\0" "underscore\0"
"quoteleft\0" "a\0" "b\0" "c\0" "d\0" "e\0" "f\0" "g\0" "h\0" "i\0" "j\0"
"k\0" "l\0" "m\0" "n\0" "o\0" "p\0" "q\0" "r\0" "s\0" "t\0" "u\0" "v\0" "w\0"
"x\0" "y\0" "z\0" "braceleft\0" "bar\0" "braceright\0" "asciitilde\0"
"exclamdown\0" "cent\0" "sterling\0" "fraction\0" "yen\0" "florin\0"
"section\0" "currency\0" "quotesingle\0" "quotedblleft\0" "guillemotleft\0"
"guilsinglleft\0" "guilsinglright\0" "fi\0" "fl\0" "endash\0" "dagger\0"
"daggerdbl\0" "periodcentered\0" "paragraph\0" "bullet\0" "quotesinglbase\0"
"quotedblbase\0" "quotedblright\0" "guillemotright\0" "ellipsis\0"
"perthousand\0" "questiondown\0" "grave\0" "acute\0" "circumflex\0" "tilde\0"
"macron\0" "breve\0" "dotaccent\0" "dieresis\0" "ring\0" "cedilla\0"
"hungarumlaut\0" "ogonek\0" "caron\0" "emdash\0" "AE\0" "ordfeminine\0"
"Lslash\0" "Oslash\0" "OE\0" "ordmasculine\0" "ae\0" "dotlessi\0" "lslash\0"
"oslash\0" "oe\0" "germandbls\0"
};
 
static const int16_t ps_standard_encoding_offset[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/,
34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 59/*quoteright*/,
70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/,
111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/,
140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/,
170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/,
202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/,
232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/,
246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/,
260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/,
274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/,
302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 338/*quoteleft*/,
348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/,
362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/,
376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/,
390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/,
410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
436/*exclamdown*/, 447/*cent*/, 452/*sterling*/, 461/*fraction*/, 470/*yen*/,
474/*florin*/, 481/*section*/, 489/*currency*/, 498/*quotesingle*/,
510/*quotedblleft*/, 523/*guillemotleft*/, 537/*guilsinglleft*/,
551/*guilsinglright*/, 566/*fi*/, 569/*fl*/, 0, 572/*endash*/, 579/*dagger*/,
586/*daggerdbl*/, 596/*periodcentered*/, 0, 611/*paragraph*/, 621/*bullet*/,
628/*quotesinglbase*/, 643/*quotedblbase*/, 656/*quotedblright*/,
670/*guillemotright*/, 685/*ellipsis*/, 694/*perthousand*/, 0,
706/*questiondown*/, 0, 719/*grave*/, 725/*acute*/, 731/*circumflex*/,
742/*tilde*/, 748/*macron*/, 755/*breve*/, 761/*dotaccent*/, 771/*dieresis*/,
0, 780/*ring*/, 785/*cedilla*/, 0, 793/*hungarumlaut*/, 806/*ogonek*/,
813/*caron*/, 819/*emdash*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
826/*AE*/, 0, 829/*ordfeminine*/, 0, 0, 0, 0, 841/*Lslash*/, 848/*Oslash*/,
855/*OE*/, 858/*ordmasculine*/, 0, 0, 0, 0, 0, 871/*ae*/, 0, 0, 0,
874/*dotlessi*/, 0, 0, 883/*lslash*/, 890/*oslash*/, 897/*oe*/,
900/*germandbls*/, 0, 0, 0, 0,
};
 
#define ps_standard_encoding(index) ((index) ? ps_standard_encoding_symbol+ps_standard_encoding_offset[(index)] : NULL)
 
static cairo_status_t
use_standard_encoding_glyph (cairo_type1_font_subset_t *font, int index)
{
const char *glyph_name;
 
if (index < 0 || index > 255)
return CAIRO_STATUS_SUCCESS;
 
glyph_name = ps_standard_encoding(index);
if (glyph_name == NULL)
return CAIRO_STATUS_SUCCESS;
 
index = cairo_type1_font_subset_lookup_glyph (font,
glyph_name,
strlen(glyph_name));
if (index < 0)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
cairo_type1_font_subset_use_glyph (font, index);
 
return CAIRO_STATUS_SUCCESS;
}
 
#define TYPE1_CHARSTRING_COMMAND_ESCAPE (12)
#define TYPE1_CHARSTRING_COMMAND_SEAC (32 + 6)
 
static cairo_status_t
cairo_type1_font_subset_look_for_seac(cairo_type1_font_subset_t *font,
const char *name, int name_length,
const char *encrypted_charstring, int encrypted_charstring_length)
{
cairo_status_t status;
unsigned char *charstring;
const unsigned char *end;
const unsigned char *p;
int stack[5], sp, value;
int command;
 
charstring = malloc (encrypted_charstring_length);
if (unlikely (charstring == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
cairo_type1_font_subset_decrypt_charstring ((const unsigned char *)
encrypted_charstring,
encrypted_charstring_length,
charstring);
end = charstring + encrypted_charstring_length;
 
p = charstring + 4;
sp = 0;
 
while (p < end) {
if (*p < 32) {
command = *p++;
 
if (command == TYPE1_CHARSTRING_COMMAND_ESCAPE)
command = 32 + *p++;
 
switch (command) {
case TYPE1_CHARSTRING_COMMAND_SEAC:
/* The seac command takes five integer arguments. The
* last two are glyph indices into the PS standard
* encoding give the names of the glyphs that this
* glyph is composed from. All we need to do is to
* make sure those glyphs are present in the subset
* under their standard names. */
status = use_standard_encoding_glyph (font, stack[3]);
if (unlikely (status))
return status;
 
status = use_standard_encoding_glyph (font, stack[4]);
if (unlikely (status))
return status;
 
sp = 0;
break;
 
default:
sp = 0;
break;
}
} else {
/* integer argument */
p = cairo_type1_font_subset_decode_integer (p, &value);
if (sp < 5)
stack[sp++] = value;
}
}
 
free (charstring);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
write_used_glyphs (cairo_type1_font_subset_t *font,
const char *name, int name_length,
const char *charstring, int charstring_length)
{
cairo_status_t status;
char buffer[256];
int length;
 
length = snprintf (buffer, sizeof buffer,
"/%.*s %d %s ",
name_length, name, charstring_length, font->rd);
status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
if (unlikely (status))
return status;
 
status = cairo_type1_font_subset_write_encrypted (font,
charstring,
charstring_length);
if (unlikely (status))
return status;
 
length = snprintf (buffer, sizeof buffer, "%s\n", font->nd);
status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
typedef cairo_status_t (*glyph_func_t) (cairo_type1_font_subset_t *font,
const char *name, int name_length,
const char *charstring, int charstring_length);
 
static cairo_status_t
cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font,
const char *dict_start,
const char *dict_end,
glyph_func_t func,
const char **dict_out)
{
int charstring_length, name_length, glyph_index;
const char *p, *charstring, *name;
char *end;
 
/* We're looking at '/' in the name of the first glyph. The glyph
* definitions are on the form:
*
* /name 23 RD <23 binary bytes> ND
*
* or alternatively using -| and |- instead of RD and ND.
*
* We parse the glyph name and see if it is in the subset. If it
* is, we call the specified callback with the glyph name and
* glyph data, otherwise we just skip it. We need to parse
* through a glyph definition; we can't just find the next '/',
* since the binary data could contain a '/'.
*/
 
p = dict_start;
 
while (*p == '/') {
name = p + 1;
p = skip_token (p, dict_end);
name_length = p - name;
 
charstring_length = strtol (p, &end, 10);
if (p == end)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Skip past -| or RD to binary data. There is exactly one space
* between the -| or RD token and the encrypted data, thus '+ 1'. */
charstring = skip_token (end, dict_end) + 1;
 
/* Skip binary data and |- or ND token. */
p = skip_token (charstring + charstring_length, dict_end);
while (p < dict_end && _cairo_isspace(*p))
p++;
 
/* In case any of the skip_token() calls above reached EOF, p will
* be equal to dict_end. */
if (p == dict_end)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
glyph_index = cairo_type1_font_subset_lookup_glyph (font,
name, name_length);
if (font->glyphs[glyph_index].subset_index >= 0) {
cairo_status_t status = func (font,
name, name_length,
charstring, charstring_length);
if (unlikely (status))
return status;
}
}
 
*dict_out = p;
 
return CAIRO_STATUS_SUCCESS;
}
 
 
static cairo_status_t
cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font,
const char *name)
{
cairo_status_t status;
const char *p, *charstrings, *dict_start;
const char *closefile_token;
char buffer[32], *glyph_count_end;
int num_charstrings, length;
 
/* The private dict holds hint information, common subroutines and
* the actual glyph definitions (charstrings).
*
* FIXME: update this comment.
*
* What we do here is scan directly the /CharString token, which
* marks the beginning of the glyph definitions. Then we parse
* through the glyph definitions and weed out the glyphs not in
* our subset. Everything else before and after the glyph
* definitions is copied verbatim to the output. It might be
* worthwile to figure out which of the common subroutines are
* used by the glyphs in the subset and get rid of the rest. */
 
/* FIXME: The /Subrs array contains binary data and could
* conceivably have "/CharStrings" in it, so we might need to skip
* this more cleverly. */
charstrings = find_token (font->cleartext, font->cleartext_end, "/CharStrings");
if (charstrings == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Scan past /CharStrings and the integer following it. */
p = charstrings + strlen ("/CharStrings");
num_charstrings = strtol (p, &glyph_count_end, 10);
if (p == glyph_count_end)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Look for a '/' which marks the beginning of the first glyph
* definition. */
for (p = glyph_count_end; p < font->cleartext_end; p++)
if (*p == '/')
break;
if (p == font->cleartext_end)
return CAIRO_INT_STATUS_UNSUPPORTED;
dict_start = p;
 
status = cairo_type1_font_subset_get_glyph_names_and_widths (font);
if (unlikely (status))
return status;
 
/* Now that we have the private dictionary broken down in
* sections, do the first pass through the glyph definitions to
* figure out which subrs and othersubrs are use and which extra
* glyphs may be required by the seac operator. */
status = cairo_type1_font_subset_for_each_glyph (font,
dict_start,
font->cleartext_end,
cairo_type1_font_subset_look_for_seac,
&p);
if (unlikely (status))
return status;
 
closefile_token = find_token (p, font->cleartext_end, "closefile");
if (closefile_token == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = cairo_type1_font_subset_get_glyph_names_and_widths (font);
if (unlikely (status))
return status;
 
/* We're ready to start outputting. First write the header,
* i.e. the public part of the font dict.*/
status = cairo_type1_font_subset_write_header (font, name);
if (unlikely (status))
return status;
 
font->base.header_size = _cairo_output_stream_get_position (font->output);
 
 
/* Start outputting the private dict. First output everything up
* to the /CharStrings token. */
status = cairo_type1_font_subset_write_encrypted (font, font->cleartext,
charstrings - font->cleartext);
if (unlikely (status))
return status;
 
/* Write out new charstring count */
length = snprintf (buffer, sizeof buffer,
"/CharStrings %d", font->num_glyphs);
status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
if (unlikely (status))
return status;
 
/* Write out text between the charstring count and the first
* charstring definition */
status = cairo_type1_font_subset_write_encrypted (font, glyph_count_end,
dict_start - glyph_count_end);
if (unlikely (status))
return status;
 
/* Write out the charstring definitions for each of the glyphs in
* the subset. */
status = cairo_type1_font_subset_for_each_glyph (font,
dict_start,
font->cleartext_end,
write_used_glyphs,
&p);
if (unlikely (status))
return status;
 
/* Output what's left between the end of the glyph definitions and
* the end of the private dict to the output. */
status = cairo_type1_font_subset_write_encrypted (font, p,
closefile_token - p + strlen ("closefile") + 1);
if (unlikely (status))
return status;
 
if (font->hex_encode)
_cairo_output_stream_write (font->output, "\n", 1);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_type1_font_subset_write_trailer(cairo_type1_font_subset_t *font)
{
const char *cleartomark_token;
int i;
static const char zeros[65] =
"0000000000000000000000000000000000000000000000000000000000000000\n";
 
 
for (i = 0; i < 8; i++)
_cairo_output_stream_write (font->output, zeros, sizeof zeros);
 
cleartomark_token = find_token (font->type1_data, font->type1_end, "cleartomark");
if (cleartomark_token) {
/* Some fonts have conditional save/restore around the entire
* font dict, so we need to retain whatever postscript code
* that may come after 'cleartomark'. */
 
_cairo_output_stream_write (font->output, cleartomark_token,
font->type1_end - cleartomark_token);
} else if (!font->eexec_segment_is_ascii) {
/* Fonts embedded in PDF may omit the fixed-content portion
* that includes the 'cleartomark' operator. Type 1 in PDF is
* always binary. */
 
_cairo_output_stream_printf (font->output, "cleartomark");
} else {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
/* some fonts do not have a newline at the end of the last line */
_cairo_output_stream_printf (font->output, "\n");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
type1_font_write (void *closure, const unsigned char *data, unsigned int length)
{
cairo_type1_font_subset_t *font = closure;
 
return _cairo_array_append_multiple (&font->contents, data, length);
}
 
static cairo_status_t
cairo_type1_font_subset_write (cairo_type1_font_subset_t *font,
const char *name)
{
cairo_status_t status;
 
status = cairo_type1_font_subset_find_segments (font);
if (unlikely (status))
return status;
 
status = cairo_type1_font_subset_decrypt_eexec_segment (font);
if (unlikely (status))
return status;
 
/* Determine which glyph definition delimiters to use. */
if (find_token (font->cleartext, font->cleartext_end, "/-|") != NULL) {
font->rd = "-|";
font->nd = "|-";
} else if (find_token (font->cleartext, font->cleartext_end, "/RD") != NULL) {
font->rd = "RD";
font->nd = "ND";
} else {
/* Don't know *what* kind of font this is... */
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY;
font->hex_column = 0;
 
status = cairo_type1_font_subset_write_private_dict (font, name);
if (unlikely (status))
return status;
 
font->base.data_size = _cairo_output_stream_get_position (font->output) -
font->base.header_size;
 
status = cairo_type1_font_subset_write_trailer (font);
if (unlikely (status))
return status;
 
font->base.trailer_size =
_cairo_output_stream_get_position (font->output) -
font->base.header_size - font->base.data_size;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_type1_font_subset_generate (void *abstract_font,
const char *name)
 
{
cairo_type1_font_subset_t *font = abstract_font;
cairo_ft_unscaled_font_t *ft_unscaled_font;
unsigned long ret;
cairo_status_t status;
 
ft_unscaled_font = (cairo_ft_unscaled_font_t *) font->base.unscaled_font;
font->face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font);
if (unlikely (font->face == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
font->type1_length = font->face->stream->size;
font->type1_data = malloc (font->type1_length);
if (unlikely (font->type1_data == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail;
}
 
if (font->face->stream->read != NULL) {
/* Note that read() may be implemented as a macro, thanks POSIX!, so we
* need to wrap the following usage in parentheses in order to
* disambiguate it for the pre-processor - using the verbose function
* pointer dereference for clarity.
*/
ret = (* font->face->stream->read) (font->face->stream, 0,
(unsigned char *) font->type1_data,
font->type1_length);
if (ret != font->type1_length) {
status = _cairo_error (CAIRO_STATUS_READ_ERROR);
goto fail;
}
} else {
memcpy (font->type1_data,
font->face->stream->base, font->type1_length);
}
 
status = _cairo_array_grow_by (&font->contents, 4096);
if (unlikely (status))
goto fail;
 
font->output = _cairo_output_stream_create (type1_font_write, NULL, font);
if (unlikely ((status = font->output->status)))
goto fail;
 
status = cairo_type1_font_subset_write (font, name);
if (unlikely (status))
goto fail;
 
font->base.data = _cairo_array_index (&font->contents, 0);
 
fail:
_cairo_ft_unscaled_font_unlock_face (ft_unscaled_font);
 
return status;
}
 
static cairo_status_t
_cairo_type1_font_subset_fini (cairo_type1_font_subset_t *font)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
unsigned int i;
 
/* If the subset generation failed, some of the pointers below may
* be NULL depending on at which point the error occurred. */
 
_cairo_array_fini (&font->contents);
 
free (font->type1_data);
if (font->glyphs != NULL) {
for (i = 0; i < font->base.num_glyphs; i++)
free (font->glyphs[i].name);
}
 
_cairo_unscaled_font_destroy (font->base.unscaled_font);
 
if (font->output != NULL)
status = _cairo_output_stream_destroy (font->output);
 
if (font->base.base_font)
free (font->base.base_font);
free (font->glyphs);
 
return status;
}
 
cairo_status_t
_cairo_type1_subset_init (cairo_type1_subset_t *type1_subset,
const char *name,
cairo_scaled_font_subset_t *scaled_font_subset,
cairo_bool_t hex_encode)
{
cairo_type1_font_subset_t font;
cairo_status_t status, status_ignored;
unsigned long parent_glyph, length;
unsigned int i;
cairo_unscaled_font_t *unscaled_font;
char buf[30];
 
/* XXX: Need to fix this to work with a general cairo_unscaled_font_t. */
if (!_cairo_scaled_font_is_ft (scaled_font_subset->scaled_font))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (_cairo_ft_scaled_font_is_vertical (scaled_font_subset->scaled_font))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font_subset->scaled_font);
 
status = _cairo_type1_font_subset_init (&font, unscaled_font, hex_encode);
if (unlikely (status))
return status;
 
for (i = 0; i < scaled_font_subset->num_glyphs; i++) {
parent_glyph = scaled_font_subset->glyphs[i];
cairo_type1_font_subset_use_glyph (&font, parent_glyph);
}
 
status = cairo_type1_font_subset_generate (&font, name);
if (unlikely (status))
goto fail1;
 
if (font.base.base_font) {
type1_subset->base_font = strdup (font.base.base_font);
} else {
snprintf(buf, sizeof (buf), "CairoFont-%u-%u",
scaled_font_subset->font_id, scaled_font_subset->subset_id);
type1_subset->base_font = strdup (buf);
}
if (unlikely (type1_subset->base_font == NULL))
goto fail1;
 
type1_subset->widths = calloc (sizeof (double), font.num_glyphs);
if (unlikely (type1_subset->widths == NULL))
goto fail2;
for (i = 0; i < font.base.num_glyphs; i++) {
if (font.glyphs[i].subset_index < 0)
continue;
type1_subset->widths[font.glyphs[i].subset_index] =
font.glyphs[i].width;
}
 
type1_subset->x_min = font.base.x_min;
type1_subset->y_min = font.base.y_min;
type1_subset->x_max = font.base.x_max;
type1_subset->y_max = font.base.y_max;
type1_subset->ascent = font.base.ascent;
type1_subset->descent = font.base.descent;
 
length = font.base.header_size +
font.base.data_size +
font.base.trailer_size;
type1_subset->data = malloc (length);
if (unlikely (type1_subset->data == NULL))
goto fail3;
 
memcpy (type1_subset->data,
_cairo_array_index (&font.contents, 0), length);
 
type1_subset->header_length = font.base.header_size;
type1_subset->data_length = font.base.data_size;
type1_subset->trailer_length = font.base.trailer_size;
 
return _cairo_type1_font_subset_fini (&font);
 
fail3:
free (type1_subset->widths);
fail2:
free (type1_subset->base_font);
fail1:
status_ignored = _cairo_type1_font_subset_fini (&font);
 
return status;
}
 
void
_cairo_type1_subset_fini (cairo_type1_subset_t *subset)
{
free (subset->base_font);
free (subset->widths);
free (subset->data);
}
 
cairo_bool_t
_cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font)
{
cairo_ft_unscaled_font_t *unscaled;
FT_Face face;
PS_FontInfoRec font_info;
cairo_bool_t is_type1 = FALSE;
 
if (!_cairo_scaled_font_is_ft (scaled_font))
return FALSE;
unscaled = (cairo_ft_unscaled_font_t *) _cairo_ft_scaled_font_get_unscaled_font (scaled_font);
face = _cairo_ft_unscaled_font_lock_face (unscaled);
if (!face)
return FALSE;
 
if (FT_Get_PS_Font_Info(face, &font_info) == 0)
is_type1 = TRUE;
 
/* OpenType/CFF fonts also have a PS_FontInfoRec */
#if HAVE_FT_LOAD_SFNT_TABLE
if (FT_IS_SFNT (face))
is_type1 = FALSE;
#endif
 
_cairo_ft_unscaled_font_unlock_face (unscaled);
 
return is_type1;
}
 
#endif /* CAIRO_HAS_FT_FONT */
 
#endif /* CAIRO_HAS_FONT_SUBSET */
/programs/develop/libraries/cairo/src/cairo-type3-glyph-surface.c
0,0 → 1,563
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2008 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>
*/
 
#include "cairoint.h"
 
#if CAIRO_HAS_FONT_SUBSET
 
#include "cairo-type3-glyph-surface-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-analysis-surface-private.h"
#include "cairo-error-private.h"
#include "cairo-surface-clipper-private.h"
 
static const cairo_surface_backend_t cairo_type3_glyph_surface_backend;
 
static cairo_status_t
_cairo_type3_glyph_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_type3_glyph_surface_t *surface = cairo_container_of (clipper,
cairo_type3_glyph_surface_t,
clipper);
 
if (path == NULL) {
_cairo_output_stream_printf (surface->stream, "Q q\n");
return CAIRO_STATUS_SUCCESS;
}
 
return _cairo_pdf_operators_clip (&surface->pdf_operators,
path,
fill_rule);
}
 
cairo_surface_t *
_cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font,
cairo_output_stream_t *stream,
cairo_type3_glyph_surface_emit_image_t emit_image,
cairo_scaled_font_subsets_t *font_subsets)
{
cairo_type3_glyph_surface_t *surface;
cairo_matrix_t invert_y_axis;
 
if (unlikely (stream != NULL && stream->status))
return _cairo_surface_create_in_error (stream->status);
 
surface = malloc (sizeof (cairo_type3_glyph_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&surface->base,
&cairo_type3_glyph_surface_backend,
NULL, /* device */
CAIRO_CONTENT_COLOR_ALPHA);
 
surface->scaled_font = scaled_font;
surface->stream = stream;
surface->emit_image = emit_image;
 
/* Setup the transform from the user-font device space to Type 3
* font space. The Type 3 font space is defined by the FontMatrix
* entry in the Type 3 dictionary. In the PDF backend this is an
* identity matrix. */
surface->cairo_to_pdf = scaled_font->scale_inverse;
cairo_matrix_init_scale (&invert_y_axis, 1, -1);
cairo_matrix_multiply (&surface->cairo_to_pdf, &surface->cairo_to_pdf, &invert_y_axis);
 
_cairo_pdf_operators_init (&surface->pdf_operators,
surface->stream,
&surface->cairo_to_pdf,
font_subsets);
 
_cairo_surface_clipper_init (&surface->clipper,
_cairo_type3_glyph_surface_clipper_intersect_clip_path);
 
return &surface->base;
}
 
static cairo_status_t
_cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface,
cairo_image_surface_t *image,
cairo_matrix_t *image_matrix)
{
cairo_status_t status;
 
/* The only image type supported by Type 3 fonts are 1-bit masks */
image = _cairo_image_surface_coerce_to_format (image, CAIRO_FORMAT_A1);
status = image->base.status;
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->stream,
"q %f %f %f %f %f %f cm\n",
image_matrix->xx,
image_matrix->xy,
image_matrix->yx,
image_matrix->yy,
image_matrix->x0,
image_matrix->y0);
 
status = surface->emit_image (image, surface->stream);
cairo_surface_destroy (&image->base);
 
_cairo_output_stream_printf (surface->stream,
"Q\n");
 
return status;
}
 
static cairo_status_t
_cairo_type3_glyph_surface_emit_image_pattern (cairo_type3_glyph_surface_t *surface,
cairo_image_surface_t *image,
const cairo_matrix_t *pattern_matrix)
{
cairo_matrix_t mat, upside_down;
cairo_status_t status;
 
if (image->width == 0 || image->height == 0)
return CAIRO_STATUS_SUCCESS;
 
mat = *pattern_matrix;
 
/* Get the pattern space to user space matrix */
status = cairo_matrix_invert (&mat);
 
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
/* Make this a pattern space to Type 3 font space matrix */
cairo_matrix_multiply (&mat, &mat, &surface->cairo_to_pdf);
 
/* PDF images are in a 1 unit by 1 unit image space. Turn the 1 by
* 1 image upside down to convert to flip the Y-axis going from
* cairo to PDF. Then scale the image up to the required size. */
cairo_matrix_scale (&mat, image->width, image->height);
cairo_matrix_init (&upside_down, 1, 0, 0, -1, 0, 1);
cairo_matrix_multiply (&mat, &upside_down, &mat);
 
return _cairo_type3_glyph_surface_emit_image (surface, image, &mat);
}
 
static cairo_status_t
_cairo_type3_glyph_surface_finish (void *abstract_surface)
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
 
return _cairo_pdf_operators_fini (&surface->pdf_operators);
}
 
static cairo_int_status_t
_cairo_type3_glyph_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
const cairo_surface_pattern_t *pattern;
cairo_image_surface_t *image;
void *image_extra;
cairo_status_t status;
 
if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
return CAIRO_INT_STATUS_IMAGE_FALLBACK;
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
 
pattern = (const cairo_surface_pattern_t *) source;
status = _cairo_surface_acquire_source_image (pattern->surface,
&image, &image_extra);
if (unlikely (status))
goto fail;
 
status = _cairo_type3_glyph_surface_emit_image_pattern (surface,
image,
&pattern->base.matrix);
 
fail:
_cairo_surface_release_source_image (pattern->surface, image, image_extra);
 
return status;
}
 
static cairo_int_status_t
_cairo_type3_glyph_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
{
return _cairo_type3_glyph_surface_paint (abstract_surface,
op, mask,
clip);
}
 
static cairo_int_status_t
_cairo_type3_glyph_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
cairo_int_status_t status;
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
 
return _cairo_pdf_operators_stroke (&surface->pdf_operators,
path,
style,
ctm,
ctm_inverse);
}
 
static cairo_int_status_t
_cairo_type3_glyph_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
cairo_int_status_t status;
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
 
return _cairo_pdf_operators_fill (&surface->pdf_operators,
path,
fill_rule);
}
 
static cairo_int_status_t
_cairo_type3_glyph_surface_show_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip,
int *remaining_glyphs)
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
cairo_int_status_t status;
cairo_scaled_font_t *font;
cairo_matrix_t new_ctm, invert_y_axis;
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
 
cairo_matrix_init_scale (&invert_y_axis, 1, -1);
cairo_matrix_multiply (&new_ctm, &invert_y_axis, &scaled_font->ctm);
cairo_matrix_multiply (&new_ctm, &surface->cairo_to_pdf, &new_ctm);
font = cairo_scaled_font_create (scaled_font->font_face,
&scaled_font->font_matrix,
&new_ctm,
&scaled_font->options);
if (unlikely (font->status))
return font->status;
 
status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
NULL, 0,
glyphs, num_glyphs,
NULL, 0,
FALSE,
font);
 
cairo_scaled_font_destroy (font);
 
return status;
}
 
static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH,
NULL, /* _cairo_type3_glyph_surface_create_similar */
_cairo_type3_glyph_surface_finish,
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* acquire_dest_image */
NULL, /* release_dest_image */
NULL, /* clone_similar */
NULL, /* composite */
NULL, /* fill_rectangles */
NULL, /* composite_trapezoids */
NULL, /* create_span_renderer */
NULL, /* check_span_renderer */
NULL, /* cairo_type3_glyph_surface_copy_page */
NULL, /* _cairo_type3_glyph_surface_show_page */
NULL, /* _cairo_type3_glyph_surface_get_extents */
NULL, /* old_show_glyphs */
NULL, /* _cairo_type3_glyph_surface_get_font_options */
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
NULL, /* scaled_font_fini */
NULL, /* scaled_glyph_fini */
_cairo_type3_glyph_surface_paint,
_cairo_type3_glyph_surface_mask,
_cairo_type3_glyph_surface_stroke,
_cairo_type3_glyph_surface_fill,
_cairo_type3_glyph_surface_show_glyphs,
NULL, /* snapshot */
};
 
static void
_cairo_type3_glyph_surface_set_stream (cairo_type3_glyph_surface_t *surface,
cairo_output_stream_t *stream)
{
surface->stream = stream;
_cairo_pdf_operators_set_stream (&surface->pdf_operators, stream);
}
 
static cairo_status_t
_cairo_type3_glyph_surface_emit_fallback_image (cairo_type3_glyph_surface_t *surface,
unsigned long glyph_index)
{
cairo_scaled_glyph_t *scaled_glyph;
cairo_status_t status;
cairo_image_surface_t *image;
cairo_matrix_t mat;
double x, y;
 
status = _cairo_scaled_glyph_lookup (surface->scaled_font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS |
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
if (unlikely (status))
return status;
 
image = scaled_glyph->surface;
if (image->width == 0 || image->height == 0)
return CAIRO_STATUS_SUCCESS;
 
x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x);
y = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y);
mat.xx = image->width;
mat.xy = 0;
mat.yx = 0;
mat.yy = image->height;
mat.x0 = x;
mat.y0 = y;
cairo_matrix_multiply (&mat, &mat, &surface->scaled_font->scale_inverse);
mat.y0 *= -1;
 
return _cairo_type3_glyph_surface_emit_image (surface, image, &mat);
}
 
void
_cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract_surface,
cairo_pdf_operators_use_font_subset_t use_font_subset,
void *closure)
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
 
if (unlikely (surface->base.status))
return;
 
_cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators,
use_font_subset,
closure);
}
 
cairo_status_t
_cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface,
unsigned long glyph_index)
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
cairo_scaled_glyph_t *scaled_glyph;
cairo_status_t status, status2;
cairo_output_stream_t *null_stream;
 
if (unlikely (surface->base.status))
return surface->base.status;
 
null_stream = _cairo_null_stream_create ();
if (unlikely (null_stream->status))
return null_stream->status;
 
_cairo_type3_glyph_surface_set_stream (surface, null_stream);
 
_cairo_scaled_font_freeze_cache (surface->scaled_font);
status = _cairo_scaled_glyph_lookup (surface->scaled_font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
&scaled_glyph);
 
if (_cairo_status_is_error (status))
goto cleanup;
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = CAIRO_STATUS_SUCCESS;
goto cleanup;
}
 
status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
&surface->base);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
status = CAIRO_STATUS_SUCCESS;
 
cleanup:
_cairo_scaled_font_thaw_cache (surface->scaled_font);
 
status2 = _cairo_output_stream_destroy (null_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
return status;
}
 
cairo_status_t
_cairo_type3_glyph_surface_emit_glyph (void *abstract_surface,
cairo_output_stream_t *stream,
unsigned long glyph_index,
cairo_box_t *bbox,
double *width)
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
cairo_scaled_glyph_t *scaled_glyph;
cairo_status_t status, status2;
double x_advance, y_advance;
cairo_matrix_t font_matrix_inverse;
 
if (unlikely (surface->base.status))
return surface->base.status;
 
_cairo_type3_glyph_surface_set_stream (surface, stream);
 
_cairo_scaled_font_freeze_cache (surface->scaled_font);
status = _cairo_scaled_glyph_lookup (surface->scaled_font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS |
CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
&scaled_glyph);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = _cairo_scaled_glyph_lookup (surface->scaled_font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&scaled_glyph);
if (status == CAIRO_STATUS_SUCCESS)
status = CAIRO_INT_STATUS_IMAGE_FALLBACK;
}
if (_cairo_status_is_error (status)) {
_cairo_scaled_font_thaw_cache (surface->scaled_font);
return status;
}
 
x_advance = scaled_glyph->metrics.x_advance;
y_advance = scaled_glyph->metrics.y_advance;
font_matrix_inverse = surface->scaled_font->font_matrix;
status2 = cairo_matrix_invert (&font_matrix_inverse);
 
/* The invertability of font_matrix is tested in
* pdf_operators_show_glyphs before any glyphs are mapped to the
* subset. */
assert (status2 == CAIRO_STATUS_SUCCESS);
 
cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance);
*width = x_advance;
 
*bbox = scaled_glyph->bbox;
_cairo_matrix_transform_bounding_box_fixed (&surface->scaled_font->scale_inverse,
bbox, NULL);
 
_cairo_output_stream_printf (surface->stream,
"%f 0 %f %f %f %f d1\n",
x_advance,
_cairo_fixed_to_double (bbox->p1.x),
- _cairo_fixed_to_double (bbox->p2.y),
_cairo_fixed_to_double (bbox->p2.x),
- _cairo_fixed_to_double (bbox->p1.y));
 
if (status == CAIRO_STATUS_SUCCESS) {
cairo_output_stream_t *mem_stream;
 
mem_stream = _cairo_memory_stream_create ();
status = mem_stream->status;
if (unlikely (status))
goto FAIL;
 
_cairo_type3_glyph_surface_set_stream (surface, mem_stream);
 
_cairo_output_stream_printf (surface->stream, "q\n");
status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
&surface->base);
 
status2 = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
_cairo_output_stream_printf (surface->stream, "Q\n");
 
_cairo_type3_glyph_surface_set_stream (surface, stream);
if (status == CAIRO_STATUS_SUCCESS)
_cairo_memory_stream_copy (mem_stream, stream);
 
status2 = _cairo_output_stream_destroy (mem_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
}
 
if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
status = _cairo_type3_glyph_surface_emit_fallback_image (surface, glyph_index);
 
FAIL:
_cairo_scaled_font_thaw_cache (surface->scaled_font);
 
return status;
}
 
#endif /* CAIRO_HAS_FONT_SUBSET */