[cairo-commit] 2 commits - src/cairo-cff-subset.c src/cairo-pdf-surface.c src/cairo-ps-surface.c src/cairo-scaled-font-subsets-private.h src/cairo-truetype-subset-private.h src/Makefile.am

Adrian Johnson ajohnson at kemper.freedesktop.org
Thu Oct 26 07:41:30 PDT 2006


 src/Makefile.am                         |    1 
 src/cairo-cff-subset.c                  | 1452 ++++++++++++++++++++++++++++++++
 src/cairo-pdf-surface.c                 |  107 ++
 src/cairo-ps-surface.c                  |  149 ++-
 src/cairo-scaled-font-subsets-private.h |   40 
 src/cairo-truetype-subset-private.h     |    1 
 6 files changed, 1722 insertions(+), 28 deletions(-)

New commits:
diff-tree bd0f9919086f2978eb1df22cd5c1fffb7621d33f (from e4c3da80806804574c99170b1df3cb92f6493283)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Fri Oct 27 00:00:24 2006 +0930

    Add OpenType/CFF Subsetting

diff --git a/src/Makefile.am b/src/Makefile.am
index f6ac7d0..71df10e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,4 +1,5 @@
 font_subset_sources =				\
+	cairo-cff-subset.c			\
 	cairo-truetype-subset.c			\
 	cairo-type1-fallback.c			\
 	cairo-truetype-subset-private.h		\
diff --git a/src/cairo-cff-subset.c b/src/cairo-cff-subset.c
new file mode 100644
index 0000000..d26ee87
--- /dev/null
+++ b/src/cairo-cff-subset.c
@@ -0,0 +1,1452 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2006 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ *	Adrian Johnson <ajohnson at redneon.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-truetype-subset-private.h"
+
+/* CFF Dict Operators. If the high byte is 0 the command is encoded
+ * with a single byte. */
+#define BASEFONTNAME_OP  0x0c16
+#define CHARSET_OP       0x000f
+#define CHARSTRINGS_OP   0x0011
+#define COPYRIGHT_OP     0x0c00
+#define ENCODING_OP      0x0010
+#define FAMILYNAME_OP    0x0003
+#define FONTNAME_OP      0x0c26
+#define FULLNAME_OP      0x0002
+#define LOCAL_SUB_OP     0x0013
+#define NOTICE_OP        0x0001
+#define POSTSCRIPT_OP    0x0c15
+#define PRIVATE_OP       0x0012
+#define ROS_OP           0x0c1e
+#define VERSION_OP       0x0000
+#define WEIGHT_OP        0x0004
+
+#define NUM_STD_STRINGS 391
+
+#define ARRAY_LENGTH(a) ( (sizeof (a)) / (sizeof ((a)[0])) )
+
+typedef struct _cff_header {
+    uint8_t major;
+    uint8_t minor;
+    uint8_t header_size;
+    uint8_t offset_size;
+} cff_header_t;
+
+typedef struct _cff_index_element {
+    cairo_bool_t   is_copy;
+    unsigned char *data;
+    int            length;
+} cff_index_element_t;
+
+typedef struct _cff_dict_operator {
+    cairo_hash_entry_t base;
+
+    unsigned short operator;
+    unsigned char *operand;
+    int            operand_length;
+    int            operand_offset;
+} cff_dict_operator_t;
+
+typedef struct _cairo_cff_font {
+
+    cairo_scaled_font_subset_t *scaled_font_subset;
+    const cairo_scaled_font_backend_t *backend;
+
+    /* Font Data */
+    unsigned char       *data;
+    unsigned long        data_length;
+    unsigned char       *current_ptr;
+    unsigned char       *data_end;
+    cff_header_t        *header;
+    char                *font_name;
+    cairo_hash_table_t  *top_dict;
+    cairo_hash_table_t  *private_dict;
+    cairo_array_t        strings_index;
+    cairo_array_t        charstrings_index;
+    cairo_array_t        global_sub_index;
+    cairo_array_t        local_sub_index;
+    int                  num_glyphs;
+
+    /* Subsetted Font Data */
+    char                *subset_font_name;
+    cairo_array_t        charstrings_subset_index;
+    cairo_array_t        strings_subset_index;
+    cairo_array_t        output;
+
+    /* Subset Metrics */
+    int                 *widths;
+    int                  x_min, y_min, x_max, y_max;
+    int                  ascent, descent;
+
+} cairo_cff_font_t;
+
+#ifdef WORDS_BIGENDIAN
+
+#define cpu_to_be16(v) (v)
+#define be16_to_cpu(v) (v)
+#define cpu_to_be32(v) (v)
+
+#else
+
+static inline uint16_t
+cpu_to_be16(uint16_t v)
+{
+    return (v << 8) | (v >> 8);
+}
+
+static inline uint16_t
+be16_to_cpu(uint16_t v)
+{
+    return cpu_to_be16 (v);
+}
+
+static inline uint32_t
+cpu_to_be32(uint32_t v)
+{
+    return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16);
+}
+
+#endif
+
+/* Encoded integer using maximum sized encoding. This is required for
+ * operands that are later modified after encoding. */
+static unsigned char *
+encode_integer_max (unsigned char *p, int i)
+{
+    *p++ = 29;
+    *p++ = i >> 24;
+    *p++ = (i >> 16) & 0xff;
+    *p++ = (i >> 8)  & 0xff;
+    *p++ = i & 0xff;
+    return p;
+}
+
+static unsigned char *
+encode_integer (unsigned char *p, int i)
+{
+    if (i >= -107 && i <= 107) {
+        *p++ = i + 139;
+    } else if (i >= 108 && i <= 1131) {
+        i -= 108;
+        *p++ = (i >> 8)+ 247;
+        *p++ = i & 0xff;
+    } else if (i >= -1131 && i <= -108) {
+        i = -i - 108;
+        *p++ = (i >> 8)+ 251;
+        *p++ = i & 0xff;
+    } else if (i >= -1131 && i <= -108) {
+        *p++ = 28;
+        *p++ = (i >> 8)  & 0xff;
+        *p++ = i & 0xff;
+    } else {
+        p = encode_integer_max (p, i);
+    }
+    return p;
+}
+
+static unsigned char *
+decode_integer (unsigned char *p, int *integer)
+{
+    if (*p == 28) {
+        *integer = (int)(p[1]<<8 | p[2]);
+        p += 3;
+    } else if (*p == 29) {
+        *integer = (int)((p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]);
+        p += 5;
+    } else if (*p >= 32 && *p <= 246) {
+        *integer = *p++ - 139;
+    } else if (*p <= 250) {
+        *integer = (p[0] - 247) * 256 + p[1] + 108;
+        p += 2;
+    } else if (*p <= 254) {
+        *integer = -(p[0] - 251) * 256 - p[1] - 108;
+        p += 2;
+    } else {
+        *integer = 0;
+        p += 1;
+    }
+    return p;
+}
+
+static unsigned char *
+decode_operator (unsigned char *p, unsigned short *operator)
+{
+    unsigned short op = 0;
+
+    op = *p++;
+    if (op == 12) {
+        op <<= 8;
+        op |= *p++;
+    }
+    *operator = op;
+    return p;
+}
+
+/* return 0 if not an operand */
+static int
+operand_length (unsigned char *p)
+{
+    unsigned char *begin = p;
+
+    if (*p == 28)
+        return 3;
+
+    if (*p == 29)
+        return 5;
+
+    if (*p >= 32 && *p <= 246)
+        return 1;
+
+    if (*p >= 247 && *p <= 254)
+        return 2;
+
+    if (*p == 30) {
+        while ((*p & 0x0f) != 0x0f)
+            p++;
+        return p - begin + 1;
+    }
+
+    return 0;
+}
+
+static unsigned char *
+encode_index_offset (unsigned char *p, int offset_size, unsigned long offset)
+{
+    while (--offset_size >= 0) {
+        p[offset_size] = (unsigned char) (offset & 0xff);
+        offset >>= 8;
+    }
+    return p + offset_size;
+}
+
+static unsigned long
+decode_index_offset(unsigned char *p, int off_size)
+{
+    unsigned long offset = 0;
+
+    while (off_size-- > 0)
+        offset = offset*256 + *p++;
+    return offset;
+}
+
+static void
+cff_index_init (cairo_array_t *index)
+{
+    _cairo_array_init (index, sizeof (cff_index_element_t));
+}
+
+static cairo_int_status_t
+cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_ptr)
+{
+    cff_index_element_t element;
+    unsigned char *data, *p;
+    cairo_status_t status;
+    int offset_size, count, start, i;
+    int end = 0;
+
+    p = *ptr;
+    if (p + 2 > end_ptr)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    count = be16_to_cpu( *((uint16_t *)p) );
+    p += 2;
+    if (count > 0) {
+        offset_size = *p++;
+        if (p + (count + 1)*offset_size > end_ptr)
+            return CAIRO_INT_STATUS_UNSUPPORTED;
+        data = p + offset_size*(count + 1) - 1;
+        start = decode_index_offset (p, offset_size);
+        p += offset_size;
+        for (i = 0; i < count; i++) {
+            end = decode_index_offset (p, offset_size);
+            p += offset_size;
+            if (p > end_ptr)
+                return CAIRO_INT_STATUS_UNSUPPORTED;
+            element.length = end - start;
+            element.is_copy = FALSE;
+            element.data = data + start;
+            status = _cairo_array_append (index, &element);
+            if (status)
+                return status;
+            start = end;
+        }
+        p = data + end;
+    }
+    *ptr = p;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cff_index_write (cairo_array_t *index, cairo_array_t *output)
+{
+    int offset_size;
+    int offset;
+    int num_elem;
+    int i;
+    cff_index_element_t *element;
+    uint16_t count;
+    unsigned char buf[5];
+    cairo_status_t status;
+
+    num_elem = _cairo_array_num_elements (index);
+    count = cpu_to_be16 ((uint16_t) num_elem);
+    status = _cairo_array_append_multiple (output, &count, 2);
+    if (status)
+        return status;
+
+    if (num_elem == 0)
+        return CAIRO_STATUS_SUCCESS;
+
+    /* Find maximum offset to determine offset size */
+    offset = 1;
+    for (i = 0; i < num_elem; i++) {
+        element = _cairo_array_index (index, i);
+        offset += element->length;
+    }
+    if (offset < 0x100)
+        offset_size = 1;
+    else if (offset < 0x10000)
+        offset_size = 2;
+    else if (offset < 0x1000000)
+        offset_size = 3;
+    else
+        offset_size = 4;
+
+    buf[0] = (unsigned char) offset_size;
+    status = _cairo_array_append (output, buf);
+    if (status)
+        return status;
+
+    offset = 1;
+    encode_index_offset (buf, offset_size, offset);
+    status = _cairo_array_append_multiple (output, buf, offset_size);
+    if (status)
+        return status;
+
+    for (i = 0; i < num_elem; i++) {
+        element = _cairo_array_index (index, i);
+        offset += element->length;
+        encode_index_offset (buf, offset_size, offset);
+        status = _cairo_array_append_multiple (output, buf, offset_size);
+        if (status)
+            return status;
+
+    }
+
+    for (i = 0; i < num_elem; i++) {
+        element = _cairo_array_index (index, i);
+        status = _cairo_array_append_multiple (output,
+                                               element->data,
+                                               element->length);
+        if (status)
+            return status;
+    }
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cff_index_append (cairo_array_t *index, unsigned char *object , int length)
+{
+    cff_index_element_t element;
+
+    element.length = length;
+    element.is_copy = FALSE;
+    element.data = object;
+
+    return _cairo_array_append (index, &element);
+}
+
+static cairo_status_t
+cff_index_append_copy (cairo_array_t *index, unsigned char *object , int length)
+{
+    cff_index_element_t element;
+
+    element.length = length;
+    element.is_copy = TRUE;
+    element.data = malloc (element.length);
+    if (element.data == NULL)
+        return CAIRO_STATUS_NO_MEMORY;
+    memcpy (element.data, object, element.length);
+
+    return _cairo_array_append (index, &element);
+}
+
+static void
+cff_index_fini (cairo_array_t *index)
+{
+    cff_index_element_t *element;
+    int i;
+
+    for (i = 0; i < _cairo_array_num_elements (index); i++) {
+        element = _cairo_array_index (index, i);
+        if (element->is_copy)
+            free (element->data);
+    }
+    _cairo_array_fini (index);
+}
+
+static cairo_bool_t
+_cairo_cff_dict_equal (const void *key_a, const void *key_b)
+{
+    const cff_dict_operator_t *op_a = key_a;
+    const cff_dict_operator_t *op_b = key_b;
+
+    return op_a->operator == op_b->operator;
+}
+
+static void
+cff_dict_init (cairo_hash_table_t **dict)
+{
+    *dict = _cairo_hash_table_create (_cairo_cff_dict_equal);
+}
+
+static void
+_cairo_dict_init_key (cff_dict_operator_t *key, int operator)
+{
+    key->base.hash = (unsigned long) operator;
+    key->operator = operator;
+}
+
+static cff_dict_operator_t *
+cff_dict_create_operator (int            operator,
+                          unsigned char *operand,
+                          int            operand_length)
+{
+    cff_dict_operator_t *op;
+
+    op = malloc (sizeof (cff_dict_operator_t));
+    if (op == NULL)
+        return NULL;
+    _cairo_dict_init_key (op, operator);
+    op->operand = malloc (operand_length);
+    if (op->operand == NULL) {
+        free (op);
+        return NULL;
+    }
+    memcpy (op->operand, operand, operand_length);
+    op->operand_length = operand_length;
+    op->operand_offset = -1;
+
+    return op;
+}
+
+static cairo_status_t
+cff_dict_read (cairo_hash_table_t *dict, unsigned char *p, int dict_size)
+{
+    unsigned char *end;
+    cairo_array_t operands;
+    cff_dict_operator_t *op;
+    unsigned short operator;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    int size;
+
+    end = p + dict_size;
+    _cairo_array_init (&operands, 1);
+    while (p < end) {
+        size = operand_length (p);
+        if (size != 0) {
+            status = _cairo_array_append_multiple (&operands, p, size);
+            if (status)
+                goto fail;
+            p += size;
+        } else {
+            p = decode_operator (p, &operator);
+            op = cff_dict_create_operator (operator,
+                                           _cairo_array_index (&operands, 0),
+                                           _cairo_array_num_elements (&operands));
+            if (op == NULL) {
+                status = CAIRO_STATUS_NO_MEMORY;
+                goto fail;
+            }
+            status = _cairo_hash_table_insert (dict, &op->base);
+            if (status)
+                goto fail;
+            _cairo_array_truncate (&operands, 0);
+        }
+    }
+
+fail:
+    _cairo_array_fini (&operands);
+
+    return status;
+}
+
+static void
+cff_dict_remove (cairo_hash_table_t *dict, unsigned short operator)
+{
+    cff_dict_operator_t key, *op;
+
+    _cairo_dict_init_key (&key, operator);
+    if (_cairo_hash_table_lookup (dict, &key.base,
+                                  (cairo_hash_entry_t **) &op))
+    {
+        free (op->operand);
+        _cairo_hash_table_remove (dict, (cairo_hash_entry_t *) 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);
+    if (_cairo_hash_table_lookup (dict, &key.base,
+                                  (cairo_hash_entry_t **) &op))
+    {
+        *size = op->operand_length;
+        return op->operand;
+    }
+
+    return NULL;
+}
+
+static cairo_status_t
+cff_dict_set_operands (cairo_hash_table_t *dict,
+                       unsigned short      operator,
+                       unsigned char      *operand,
+                       int                 size)
+{
+    cff_dict_operator_t key, *op;
+    cairo_status_t status;
+
+    _cairo_dict_init_key (&key, operator);
+    if (_cairo_hash_table_lookup (dict, &key.base,
+                                  (cairo_hash_entry_t **) &op))
+    {
+        free (op->operand);
+        op->operand = malloc (size);
+        if (op->operand == NULL)
+            return CAIRO_STATUS_NO_MEMORY;
+        memcpy (op->operand, operand, size);
+        op->operand_length = size;
+    }
+    else
+    {
+        op = cff_dict_create_operator (operator, operand, size);
+        if (op == NULL)
+	    return CAIRO_STATUS_NO_MEMORY;
+	status = _cairo_hash_table_insert (dict, &op->base);
+	if (status)
+	    return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static int
+cff_dict_get_location (cairo_hash_table_t *dict,
+                       unsigned short      operator,
+                       int                *size)
+{
+    cff_dict_operator_t key, *op;
+
+    _cairo_dict_init_key (&key, operator);
+    if (_cairo_hash_table_lookup (dict, &key.base,
+                                  (cairo_hash_entry_t **) &op))
+    {
+        *size = op->operand_length;
+        return op->operand_offset;
+    }
+
+    return -1;
+}
+
+typedef struct _dict_write_info {
+    cairo_array_t *output;
+    cairo_status_t status;
+} dict_write_info_t;
+
+static void
+_cairo_dict_collect (void *entry, void *closure)
+{
+    dict_write_info_t *write_info = closure;
+    cff_dict_operator_t *op = entry;
+    unsigned char data;
+
+    if (write_info->status)
+        return;
+
+    op->operand_offset = _cairo_array_num_elements (write_info->output);
+    write_info->status = _cairo_array_append_multiple (write_info->output,
+                                                       op->operand,
+                                                       op->operand_length);
+    if (write_info->status)
+        return;
+
+    if (op->operator & 0xff00) {
+        data = op->operator >> 8;
+        write_info->status = _cairo_array_append (write_info->output, &data);
+        if (write_info->status)
+            return;
+    }
+    data = op->operator & 0xff;
+    write_info->status = _cairo_array_append (write_info->output, &data);
+}
+
+static cairo_status_t
+cff_dict_write (cairo_hash_table_t *dict, cairo_array_t *output)
+{
+    dict_write_info_t write_info;
+
+    write_info.output = output;
+    write_info.status = CAIRO_STATUS_SUCCESS;
+    _cairo_hash_table_foreach (dict, _cairo_dict_collect, &write_info);
+
+    return write_info.status;
+}
+
+static void
+cff_dict_fini (cairo_hash_table_t *dict)
+{
+    cff_dict_operator_t *entry;
+
+    while (1) {
+	entry = _cairo_hash_table_random_entry (dict, NULL);
+	if (entry == NULL)
+	    break;
+        free (entry->operand);
+        _cairo_hash_table_remove (dict, (cairo_hash_entry_t *) entry);
+        free (entry);
+    }
+    _cairo_hash_table_destroy (dict);
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_header (cairo_cff_font_t *font)
+{
+    if (font->data_length < sizeof (cff_header_t))
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    font->header = (cff_header_t *) font->data;
+    font->current_ptr = font->data + font->header->header_size;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_name (cairo_cff_font_t *font)
+{
+    cairo_array_t index;
+    cairo_int_status_t status;
+
+    /* The original font name is not used in the subset. Read the name
+     * index to skip over it. */
+    cff_index_init (&index);
+    status = cff_index_read (&index, &font->current_ptr, font->data_end);
+    cff_index_fini (&index);
+
+    return status;
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_private_dict (cairo_cff_font_t   *font,
+                                  unsigned char      *ptr,
+                                  int                 size)
+{
+    unsigned char buf[10];
+    unsigned char *end_buf;
+    int offset;
+    int i;
+    unsigned char *operand;
+    unsigned char *p;
+
+    cff_dict_read (font->private_dict, ptr, size);
+    operand = cff_dict_get_operands (font->private_dict, LOCAL_SUB_OP, &i);
+    if (operand) {
+        decode_integer (operand, &offset);
+        p = ptr + offset;
+        cff_index_read (&font->local_sub_index, &p, font->data_end);
+
+        /* Use maximum sized encoding to reserve space for later modification. */
+        end_buf = encode_integer_max (buf, 0);
+        cff_dict_set_operands (font->private_dict, LOCAL_SUB_OP, buf, end_buf - buf);
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_top_dict (cairo_cff_font_t *font)
+{
+    cairo_array_t index;
+    cff_index_element_t *element;
+    unsigned char buf[20];
+    unsigned char *end_buf;
+    unsigned char *operand;
+    cairo_int_status_t status;
+    unsigned char *p;
+    int size;
+    int offset;
+
+    cff_index_init (&index);
+    status = cff_index_read (&index, &font->current_ptr, font->data_end);
+    if (status)
+        goto fail;
+
+    element = _cairo_array_index (&index, 0);
+    cff_dict_read (font->top_dict, element->data, element->length);
+
+    /* CID fonts are NYI */
+    if (cff_dict_get_operands (font->top_dict, ROS_OP, &size) != NULL) {
+        status = CAIRO_INT_STATUS_UNSUPPORTED;
+        goto fail;
+    }
+
+    operand = cff_dict_get_operands (font->top_dict, CHARSTRINGS_OP, &size);
+    decode_integer (operand, &offset);
+    p = font->data + offset;
+    status = cff_index_read (&font->charstrings_index, &p, font->data_end);
+    if (status)
+        goto fail;
+    font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index);
+
+    operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size);
+    operand = decode_integer (operand, &size);
+    decode_integer (operand, &offset);
+    cairo_cff_font_read_private_dict (font, font->data + offset, size);
+
+    cff_dict_remove (font->top_dict, CHARSET_OP);
+
+    /* Use maximum sized encoding to reserve space for later modification. */
+    end_buf = encode_integer_max (buf, 0);
+    cff_dict_set_operands (font->top_dict, CHARSTRINGS_OP, buf, end_buf - buf);
+    cff_dict_set_operands (font->top_dict, ENCODING_OP, buf, end_buf - buf);
+    /* Private has two operands - size and offset */
+    end_buf = encode_integer_max (end_buf, 0);
+    cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf - buf);
+
+fail:
+    cff_index_fini (&index);
+
+    return status;
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_strings (cairo_cff_font_t *font)
+{
+    return cff_index_read (&font->strings_index, &font->current_ptr, font->data_end);
+}
+
+static cairo_int_status_t
+cairo_cff_font_read_global_subroutines (cairo_cff_font_t *font)
+{
+    return cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end);
+}
+
+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 (status)
+            return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_subset_dict_string(cairo_cff_font_t   *font,
+                                  cairo_hash_table_t *dict,
+                                  int                 operator)
+{
+    int size;
+    unsigned char *p;
+    int sid;
+    unsigned char buf[100];
+    cff_index_element_t *element;
+    cairo_status_t status;
+
+    p = cff_dict_get_operands (dict, operator, &size);
+    if (!p)
+        return CAIRO_STATUS_SUCCESS;
+
+    decode_integer (p, &sid);
+    if (sid < NUM_STD_STRINGS)
+        return CAIRO_STATUS_SUCCESS;
+
+    element = _cairo_array_index (&font->strings_index, sid - NUM_STD_STRINGS);
+    sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
+    status = cff_index_append (&font->strings_subset_index, element->data, element->length);
+    if (status)
+        return status;
+
+    p = encode_integer (buf, sid);
+    cff_dict_set_operands (dict, operator, buf, p - buf);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static const int dict_strings[] = {
+    VERSION_OP,
+    NOTICE_OP,
+    COPYRIGHT_OP,
+    FULLNAME_OP,
+    FAMILYNAME_OP,
+    WEIGHT_OP,
+    POSTSCRIPT_OP,
+    BASEFONTNAME_OP,
+    FONTNAME_OP,
+};
+
+static cairo_status_t
+cairo_cff_font_subset_dict_strings (cairo_cff_font_t   *font,
+                                    cairo_hash_table_t *dict)
+{
+    cairo_status_t status;
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_LENGTH (dict_strings); i++) {
+        status = cairo_cff_font_subset_dict_string (font, dict, dict_strings[i]);
+        if (status)
+            return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_subset_strings (cairo_cff_font_t *font)
+{
+    cairo_status_t status;
+
+    status = cairo_cff_font_subset_dict_strings (font, font->top_dict);
+    if (status)
+        return status;
+
+    status = cairo_cff_font_subset_dict_strings (font, font->private_dict);
+
+    return status;
+}
+
+static cairo_status_t
+cairo_cff_font_subset_charstrings (cairo_cff_font_t  *font)
+{
+    cff_index_element_t *element;
+    unsigned int i;
+    cairo_status_t status;
+
+    /* add .notdef */
+    element = _cairo_array_index (&font->charstrings_index, 0);
+    status = cff_index_append (&font->charstrings_subset_index,
+                               element->data,
+                               element->length);
+    if (status)
+        return status;
+
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+        element = _cairo_array_index (&font->charstrings_index,
+                                      font->scaled_font_subset->glyphs[i]);
+        status = cff_index_append (&font->charstrings_subset_index,
+                                   element->data,
+                                   element->length);
+        if (status)
+            return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_subset_font (cairo_cff_font_t  *font)
+{
+    cairo_status_t status;
+
+    /* TODO: subset subroutines */
+
+    status = cairo_cff_font_subset_strings (font);
+    if (status)
+        return status;
+
+    status = cairo_cff_font_subset_charstrings (font);
+
+    return status;
+}
+
+/* Set the operand of the specified operator in the (already written)
+ * top dict to point to the current position in the output
+ * array. Operands updated with this function must have previously
+ * been encoded with the 5-byte (max) integer encoding. */
+static void
+cairo_cff_font_set_topdict_operator_to_cur_pos (cairo_cff_font_t  *font,
+                                                int                operator)
+{
+    int cur_pos;
+    int offset;
+    int size;
+    unsigned char buf[10];
+    unsigned char *buf_end;
+    unsigned char *op_ptr;
+
+    cur_pos = _cairo_array_num_elements (&font->output);
+    buf_end = encode_integer_max (buf, cur_pos);
+    offset = cff_dict_get_location (font->top_dict, operator, &size);
+    assert (offset > 0);
+    op_ptr = _cairo_array_index (&font->output, offset);
+    memcpy (op_ptr, buf, buf_end - buf);
+}
+
+static cairo_status_t
+cairo_cff_font_write_header (cairo_cff_font_t *font)
+{
+    return _cairo_array_append_multiple (&font->output,
+                                         font->header,
+                                         font->header->header_size);
+}
+
+static cairo_status_t
+cairo_cff_font_write_name (cairo_cff_font_t *font)
+{
+    cairo_array_t index;
+    cairo_status_t status;
+
+    cff_index_init (&index);
+    status = cff_index_append_copy (&index,
+                                    (unsigned char *) font->subset_font_name,
+                                    strlen(font->subset_font_name));
+    if (status)
+        return status;
+    status = cff_index_write (&index, &font->output);
+    if (status)
+        return status;
+    cff_index_fini (&index);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_top_dict (cairo_cff_font_t *font)
+{
+    uint16_t count;
+    unsigned char buf[10];
+    unsigned char *p;
+    int offset_index;
+    int dict_start, dict_size;
+    int offset_size = 4;
+    cairo_status_t status;
+
+    /* Write an index containing the top dict */
+
+    count = cpu_to_be16 (1);
+    status = _cairo_array_append_multiple (&font->output, &count, 2);
+    if (status)
+        return status;
+    buf[0] = offset_size;
+    status = _cairo_array_append (&font->output, buf);
+    if (status)
+        return status;
+    encode_index_offset (buf, offset_size, 1);
+    status = _cairo_array_append_multiple (&font->output, buf, offset_size);
+    if (status)
+        return status;
+
+    /* Reserve space for last element of offset array and update after
+     * dict is written */
+    offset_index = _cairo_array_num_elements (&font->output);
+    status = _cairo_array_append_multiple (&font->output, buf, offset_size);
+    if (status)
+        return status;
+
+    dict_start = _cairo_array_num_elements (&font->output);
+    status = cff_dict_write (font->top_dict, &font->output);
+    if (status)
+        return status;
+    dict_size = _cairo_array_num_elements (&font->output) - dict_start;
+
+    encode_index_offset (buf, offset_size, dict_size + 1);
+    p = _cairo_array_index (&font->output, offset_index);
+    memcpy (p, buf, offset_size);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_strings (cairo_cff_font_t  *font)
+{
+    return cff_index_write (&font->strings_subset_index, &font->output);
+}
+
+static cairo_status_t
+cairo_cff_font_write_global_subrs (cairo_cff_font_t  *font)
+{
+    return cff_index_write (&font->global_sub_index, &font->output);
+}
+
+static cairo_status_t
+cairo_cff_font_write_encoding (cairo_cff_font_t  *font)
+{
+    unsigned char buf[10];
+
+    cairo_cff_font_set_topdict_operator_to_cur_pos (font, ENCODING_OP);
+    buf[0] = 1; /* Format 1 */
+    buf[1] = 1; /* Number of ranges */
+    buf[2] = 0; /* First code in range */
+    /* Codes left in range excluding first */
+    buf[3] = font->scaled_font_subset->num_glyphs - 1;
+
+    return _cairo_array_append_multiple (&font->output, buf, 4);
+}
+
+static cairo_status_t
+cairo_cff_font_write_charstrings (cairo_cff_font_t  *font)
+{
+    cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSTRINGS_OP);
+
+    return cff_index_write (&font->charstrings_subset_index, &font->output);
+}
+
+static cairo_status_t
+cairo_cff_font_write_private_dict_and_local_sub (cairo_cff_font_t *font)
+{
+    int offset, private_dict_offset;
+    int size;
+    unsigned char buf[10];
+    unsigned char *buf_end;
+    unsigned char *p;
+    cairo_status_t status;
+
+    /* Write private dict and update offset and size in top dict */
+    private_dict_offset = _cairo_array_num_elements (&font->output);
+    status = cff_dict_write (font->private_dict, &font->output);
+    if (status)
+        return status;
+    size = _cairo_array_num_elements (&font->output) - private_dict_offset;
+    /* private entry has two operands - size and offset */
+    buf_end = encode_integer_max (buf, size);
+    buf_end = encode_integer_max (buf_end, private_dict_offset);
+    offset = cff_dict_get_location (font->top_dict, PRIVATE_OP, &size);
+    assert (offset > 0);
+    p = _cairo_array_index (&font->output, offset);
+    memcpy (p, buf, buf_end - buf);
+
+    if (_cairo_array_num_elements (&font->local_sub_index) > 0) {
+        /* Write local subroutines and update offset in private
+         * dict. Local subroutines offset is relative to start of
+         * private dict */
+        offset = _cairo_array_num_elements (&font->output) - private_dict_offset;
+        buf_end = encode_integer_max (buf, offset);
+        offset = cff_dict_get_location (font->private_dict, LOCAL_SUB_OP, &size);
+        assert (offset > 0);
+        p = _cairo_array_index (&font->output, offset);
+        memcpy (p, buf, buf_end - buf);
+        status = cff_index_write (&font->local_sub_index, &font->output);
+        if (status)
+            return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+typedef cairo_status_t
+(*font_write_t) (cairo_cff_font_t *font);
+
+static const font_write_t font_write_funcs[] = {
+    cairo_cff_font_write_header,
+    cairo_cff_font_write_name,
+    cairo_cff_font_write_top_dict,
+    cairo_cff_font_write_strings,
+    cairo_cff_font_write_global_subrs,
+    cairo_cff_font_write_encoding,
+    cairo_cff_font_write_charstrings,
+    cairo_cff_font_write_private_dict_and_local_sub,
+};
+
+static cairo_status_t
+cairo_cff_font_write_subset (cairo_cff_font_t *font)
+{
+    cairo_int_status_t status;
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_LENGTH (font_write_funcs); i++) {
+        status = font_write_funcs[i] (font);
+        if (status)
+            return status;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_generate (cairo_cff_font_t  *font,
+                         const char       **data,
+                         unsigned long     *length)
+{
+    cairo_int_status_t status;
+
+    status = cairo_cff_font_read_font (font);
+    if (status)
+        return status;
+
+    status = cairo_cff_font_subset_font (font);
+    if (status)
+        return status;
+
+    status = cairo_cff_font_write_subset (font);
+    if (status)
+        return status;
+
+    *data = _cairo_array_index (&font->output, 0);
+    *length = _cairo_array_num_elements (&font->output);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+cairo_cff_font_create_set_widths (cairo_cff_font_t *font)
+{
+    unsigned long size;
+    unsigned long long_entry_size;
+    unsigned long short_entry_size;
+    unsigned int i;
+    tt_hhea_t hhea;
+    int num_hmetrics;
+    unsigned char buf[10];
+    int glyph_index;
+    cairo_int_status_t status;
+
+    size = sizeof (tt_hhea_t);
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                 TT_TAG_hhea, 0,
+                                                 (unsigned char*) &hhea, &size);
+    if (status)
+        return status;
+    num_hmetrics = be16_to_cpu (hhea.num_hmetrics);
+
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+        glyph_index = font->scaled_font_subset->glyphs[i];
+        long_entry_size = 2 * sizeof (int16_t);
+        short_entry_size = sizeof (int16_t);
+        if (glyph_index < num_hmetrics) {
+            status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                         TT_TAG_hmtx,
+                                                         glyph_index * long_entry_size,
+                                                         buf, &short_entry_size);
+            if (status)
+                return status;
+        }
+        else
+        {
+            status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+                                                         TT_TAG_hmtx,
+                                                         (num_hmetrics - 1) * long_entry_size,
+                                                         buf, &short_entry_size);
+            if (status)
+                return status;
+        }
+        font->widths[i] = be16_to_cpu (*((int16_t*)buf));
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_cff_font_create (cairo_scaled_font_subset_t  *scaled_font_subset,
+                        cairo_cff_font_t           **font_return,
+                        const char                  *subset_name)
+{
+    const cairo_scaled_font_backend_t *backend;
+    cairo_status_t status;
+    cairo_cff_font_t *font;
+    tt_head_t head;
+    tt_hhea_t hhea;
+    tt_name_t *name;
+    tt_name_record_t *record;
+    unsigned long size, data_length;
+    int i, j;
+
+    backend = scaled_font_subset->scaled_font->backend;
+    if (!backend->load_truetype_table)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    data_length = 0;
+    status = backend->load_truetype_table( scaled_font_subset->scaled_font,
+                                           TT_TAG_CFF, 0, NULL, &data_length);
+    if (status)
+        return status;
+
+    size = sizeof (tt_head_t);
+    status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+                                           TT_TAG_head, 0,
+                                           (unsigned char *) &head, &size);
+    if (status)
+        return status;
+
+    size = sizeof (tt_hhea_t);
+    status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+                                           TT_TAG_hhea, 0,
+                                           (unsigned char *) &hhea, &size);
+    if (status)
+        return status;
+
+    size = 0;
+    status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+                                           TT_TAG_hmtx, 0, NULL, &size);
+    if (status)
+        return status;
+
+    size = 0;
+    status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+                                           TT_TAG_name, 0, NULL, &size);
+    if (status)
+        return status;
+
+    name = malloc (size);
+    if (name == NULL)
+        return CAIRO_STATUS_NO_MEMORY;
+    status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+                                           TT_TAG_name, 0,
+                                           (unsigned char *) name, &size);
+    if (status)
+        goto fail1;
+
+    font = malloc (sizeof (cairo_cff_font_t));
+    if (font == NULL) {
+        status = CAIRO_STATUS_NO_MEMORY;
+	goto fail1;
+    }
+
+    font->backend = backend;
+    font->scaled_font_subset = scaled_font_subset;
+
+    _cairo_array_init (&font->output, sizeof (char));
+    status = _cairo_array_grow_by (&font->output, 4096);
+    if (status)
+	goto fail2;
+
+    font->subset_font_name = strdup (subset_name);
+    if (font->subset_font_name == NULL) {
+        status = CAIRO_STATUS_NO_MEMORY;
+	goto fail3;
+    }
+    font->x_min = (int16_t) be16_to_cpu (head.x_min);
+    font->y_min = (int16_t) be16_to_cpu (head.y_min);
+    font->x_max = (int16_t) be16_to_cpu (head.x_max);
+    font->y_max = (int16_t) be16_to_cpu (head.y_max);
+    font->ascent = (int16_t) be16_to_cpu (hhea.ascender);
+    font->descent = (int16_t) be16_to_cpu (hhea.descender);
+
+    /* Extract the font name from the name table. At present this
+     * just looks for the Mac platform/Roman encoded font name. It
+     * should be extended to use any suitable font name in the
+     * name table. If the mac/roman font name is not found a
+     * CairoFont-x-y name is created.
+     */
+    font->font_name = NULL;
+    for (i = 0; i < be16_to_cpu(name->num_records); i++) {
+        record = &(name->records[i]);
+        if ((be16_to_cpu (record->platform) == 1) &&
+            (be16_to_cpu (record->encoding) == 0) &&
+            (be16_to_cpu (record->name) == 4)) {
+            font->font_name = malloc (be16_to_cpu(record->length) + 1);
+            if (font->font_name) {
+                strncpy(font->font_name,
+                        ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset),
+                        be16_to_cpu (record->length));
+                font->font_name[be16_to_cpu (record->length)] = 0;
+            }
+            break;
+        }
+    }
+
+    if (font->font_name == NULL) {
+        font->font_name = malloc (30);
+        if (font->font_name == NULL) {
+            status = CAIRO_STATUS_NO_MEMORY;
+            goto fail4;
+        }
+        snprintf(font->font_name, 30, "CairoFont-%u-%u",
+                 scaled_font_subset->font_id,
+                 scaled_font_subset->subset_id);
+    }
+
+    for (i = 0, j = 0; font->font_name[j]; j++) {
+	if (font->font_name[j] == ' ')
+	    continue;
+	font->font_name[i++] = font->font_name[j];
+    }
+    font->font_name[i] = '\0';
+
+    font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int));
+    if (font->widths == NULL) {
+        status = CAIRO_STATUS_NO_MEMORY;
+        goto fail5;
+    }
+    cairo_cff_font_create_set_widths (font);
+
+    font->data_length = data_length;
+    font->data = malloc (data_length);
+    if (font->data == NULL) {
+        status = CAIRO_STATUS_NO_MEMORY;
+        goto fail6;
+    }
+    status = font->backend->load_truetype_table ( font->scaled_font_subset->scaled_font,
+                                                  TT_TAG_CFF, 0, font->data,
+                                                  &font->data_length);
+    if (status)
+        goto fail7;
+    font->data_end = font->data + font->data_length;
+
+    cff_dict_init (&font->top_dict);
+    cff_dict_init (&font->private_dict);
+    cff_index_init (&font->strings_index);
+    cff_index_init (&font->charstrings_index);
+    cff_index_init (&font->global_sub_index);
+    cff_index_init (&font->local_sub_index);
+    cff_index_init (&font->charstrings_subset_index);
+    cff_index_init (&font->strings_subset_index);
+
+    free (name);
+    *font_return = font;
+
+    return CAIRO_STATUS_SUCCESS;
+
+fail7:
+    free (font->data);
+fail6:
+    free (font->widths);
+fail5:
+    free (font->font_name);
+fail4:
+    free (font->subset_font_name);
+fail3:
+    _cairo_array_fini (&font->output);
+fail2:
+    free (font);
+fail1:
+    free (name);
+    return status;
+}
+
+static void
+cairo_cff_font_destroy (cairo_cff_font_t *font)
+{
+    free (font->widths);
+    free (font->font_name);
+    free (font->subset_font_name);
+    _cairo_array_fini (&font->output);
+    cff_dict_fini (font->top_dict);
+    cff_dict_fini (font->private_dict);
+    cff_index_fini (&font->strings_index);
+    cff_index_fini (&font->charstrings_index);
+    cff_index_fini (&font->global_sub_index);
+    cff_index_fini (&font->local_sub_index);
+    cff_index_fini (&font->charstrings_subset_index);
+    cff_index_fini (&font->strings_subset_index);
+    free (font);
+}
+
+cairo_status_t
+_cairo_cff_subset_init (cairo_cff_subset_t          *cff_subset,
+                        const char		    *subset_name,
+                        cairo_scaled_font_subset_t  *font_subset)
+{
+    cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */
+    cairo_status_t status;
+    const char *data = NULL; /* squelch bogus compiler warning */
+    unsigned long length = 0; /* squelch bogus compiler warning */
+    unsigned int i;
+
+    status = _cairo_cff_font_create (font_subset, &font, subset_name);
+    if (status)
+	return status;
+
+    status = cairo_cff_font_generate (font, &data, &length);
+    if (status)
+	goto fail1;
+
+    cff_subset->base_font = strdup (font->font_name);
+    if (cff_subset->base_font == NULL)
+	goto fail1;
+
+    cff_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs);
+    if (cff_subset->widths == NULL)
+	goto fail2;
+    for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
+        cff_subset->widths[i] = font->widths[i];
+
+    cff_subset->x_min = font->x_min;
+    cff_subset->y_min = font->y_min;
+    cff_subset->x_max = font->x_max;
+    cff_subset->y_max = font->y_max;
+    cff_subset->ascent = font->ascent;
+    cff_subset->descent = font->descent;
+
+    cff_subset->data = malloc (length);
+    if (cff_subset->data == NULL)
+	goto fail3;
+
+    memcpy (cff_subset->data, data, length);
+    cff_subset->data_length = length;
+
+    cairo_cff_font_destroy (font);
+
+    return CAIRO_STATUS_SUCCESS;
+
+ fail3:
+    free (cff_subset->widths);
+ fail2:
+    free (cff_subset->base_font);
+ fail1:
+    cairo_cff_font_destroy (font);
+
+    return status;
+}
+
+void
+_cairo_cff_subset_fini (cairo_cff_subset_t *subset)
+{
+    free (subset->base_font);
+    free (subset->widths);
+    free (subset->data);
+}
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 39bd695..25d367e 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -1580,6 +1580,109 @@ _cairo_pdf_surface_write_pages (cairo_pd
 }
 
 static cairo_status_t
+_cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t		*surface,
+                                         cairo_scaled_font_subset_t	*font_subset)
+{
+    cairo_pdf_resource_t stream, descriptor, subset_resource;
+    cairo_status_t status;
+    cairo_pdf_font_t font;
+    cairo_cff_subset_t subset;
+    unsigned long compressed_length;
+    char *compressed;
+    unsigned int i;
+    char name[64];
+
+    snprintf (name, sizeof name, "CairoFont-%d-%d",
+	      font_subset->font_id, font_subset->subset_id);
+    status = _cairo_cff_subset_init (&subset, name, font_subset);
+    if (status)
+	return status;
+
+    compressed = compress_dup (subset.data, subset.data_length, &compressed_length);
+    if (compressed == NULL) {
+	_cairo_cff_subset_fini (&subset);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    stream = _cairo_pdf_surface_new_object (surface);
+    _cairo_output_stream_printf (surface->output,
+				 "%d 0 obj\r\n"
+				 "<< /Filter /FlateDecode\r\n"
+				 "   /Length %lu\r\n"
+				 "   /Subtype /Type1C\r\n"
+				 ">>\r\n"
+				 "stream\r\n",
+				 stream.id,
+				 compressed_length);
+    _cairo_output_stream_write (surface->output, compressed, compressed_length);
+    _cairo_output_stream_printf (surface->output,
+				 "\r\n"
+				 "endstream\r\n"
+				 "endobj\r\n");
+    free (compressed);
+
+    descriptor = _cairo_pdf_surface_new_object (surface);
+    _cairo_output_stream_printf (surface->output,
+				 "%d 0 obj\r\n"
+				 "<< /Type /FontDescriptor\r\n"
+				 "   /FontName /%s\r\n"
+				 "   /Flags 4\r\n"
+				 "   /FontBBox [ %ld %ld %ld %ld ]\r\n"
+				 "   /ItalicAngle 0\r\n"
+				 "   /Ascent %ld\r\n"
+				 "   /Descent %ld\r\n"
+				 "   /CapHeight 500\r\n"
+				 "   /StemV 80\r\n"
+				 "   /StemH 80\r\n"
+				 "   /FontFile3 %u 0 R\r\n"
+				 ">>\r\n"
+				 "endobj\r\n",
+				 descriptor.id,
+				 subset.base_font,
+				 subset.x_min,
+				 subset.y_min,
+				 subset.x_max,
+				 subset.y_max,
+				 subset.ascent,
+				 subset.descent,
+				 stream.id);
+
+    subset_resource = _cairo_pdf_surface_new_object (surface);
+    _cairo_output_stream_printf (surface->output,
+				 "%d 0 obj\r\n"
+				 "<< /Type /Font\r\n"
+				 "   /Subtype /Type1\r\n"
+				 "   /BaseFont /%s\r\n"
+				 "   /FirstChar 0\r\n"
+				 "   /LastChar %d\r\n"
+				 "   /FontDescriptor %d 0 R\r\n"
+				 "   /Widths [",
+				 subset_resource.id,
+				 subset.base_font,
+				 font_subset->num_glyphs,
+				 descriptor.id);
+
+    for (i = 0; i < font_subset->num_glyphs; i++)
+	_cairo_output_stream_printf (surface->output,
+				     " %d",
+				     subset.widths[i]);
+
+    _cairo_output_stream_printf (surface->output,
+				 " ]\r\n"
+				 ">>\r\n"
+				 "endobj\r\n");
+
+    font.font_id = font_subset->font_id;
+    font.subset_id = font_subset->subset_id;
+    font.subset_resource = subset_resource;
+    _cairo_array_append (&surface->fonts, &font);
+
+    _cairo_cff_subset_fini (&subset);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
 _cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t		*surface,
                                     cairo_scaled_font_subset_t	*font_subset,
                                     cairo_type1_subset_t        *subset)
@@ -2059,6 +2162,10 @@ _cairo_pdf_surface_emit_font_subset (cai
     cairo_pdf_surface_t *surface = closure;
     cairo_status_t status;
 
+    status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset);
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	return;
+
 #if CAIRO_HAS_FT_FONT
     status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset);
     if (status != CAIRO_INT_STATUS_UNSUPPORTED)
diff --git a/src/cairo-scaled-font-subsets-private.h b/src/cairo-scaled-font-subsets-private.h
index 01eeb3f..2c81fdc 100644
--- a/src/cairo-scaled-font-subsets-private.h
+++ b/src/cairo-scaled-font-subsets-private.h
@@ -179,6 +179,46 @@ _cairo_scaled_font_subsets_foreach (cair
 				    cairo_scaled_font_subset_callback_func_t	 font_subset_callback,
 				    void					*closure);
 
+typedef struct _cairo_cff_subset {
+    char *base_font;
+    int *widths;
+    long x_min, y_min, x_max, y_max;
+    long ascent, descent;
+    char *data;
+    unsigned long data_length;
+} cairo_cff_subset_t;
+
+/**
+ * _cairo_cff_subset_init:
+ * @cff_subset: a #cairo_cff_subset_t to initialize
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * cairo_scaled_font_t and the font backend in use) generate a
+ * cff file corresponding to @font_subset and initialize
+ * @cff_subset with information about the subset and the cff
+ * data.
+ *
+ * Return value: CAIRO_STATUS_SUCCESS if successful,
+ * CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a
+ * cff file, or an non-zero value indicating an error.  Possible
+ * errors include CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_status_t
+_cairo_cff_subset_init (cairo_cff_subset_t          *cff_subset,
+                        const char                  *name,
+                        cairo_scaled_font_subset_t  *font_subset);
+
+/**
+ * _cairo_cff_subset_fini:
+ * @cff_subset: a #cairo_cff_subset_t
+ *
+ * Free all resources associated with @cff_subset.  After this
+ * call, @cff_subset should not be used again without a
+ * subsequent call to _cairo_cff_subset_init() again first.
+ **/
+cairo_private void
+_cairo_cff_subset_fini (cairo_cff_subset_t *cff_subset);
 
 typedef struct _cairo_truetype_subset {
     char *base_font;
diff --git a/src/cairo-truetype-subset-private.h b/src/cairo-truetype-subset-private.h
index 51bf906..e9b2c47 100644
--- a/src/cairo-truetype-subset-private.h
+++ b/src/cairo-truetype-subset-private.h
@@ -51,6 +51,7 @@
  */
 
 #define MAKE_TT_TAG(a, b, c, d)    (a<<24 | b<<16 | c<<8 | d)
+#define TT_TAG_CFF    MAKE_TT_TAG('C','F','F',' ')
 #define TT_TAG_cmap   MAKE_TT_TAG('c','m','a','p')
 #define TT_TAG_cvt    MAKE_TT_TAG('c','v','t',' ')
 #define TT_TAG_fpgm   MAKE_TT_TAG('f','p','g','m')
diff-tree e4c3da80806804574c99170b1df3cb92f6493283 (from e1ded5b1e042c8cefa7f136228d5a63a7bdf84b5)
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Thu Oct 26 23:41:57 2006 +0930

    PS: Use xshow/yshow/xyshow for strings of glyphs
    
    Optimize show glyphs by looking for strings of glyphs from the same subset
    and use the xyshow operator to display. As a further optimization the xshow
    and yshow operators are used for displaying horizontal and vertical text.

diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 25b9f6b..d02b251 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -368,6 +368,9 @@ _cairo_ps_surface_emit_header (cairo_ps_
 				 "/P{closepath}bind def\n"
 				 "/R{setrgbcolor}bind def\n"
 				 "/S{show}bind def\n"
+				 "/xS{xshow}bind def\n"
+				 "/yS{yshow}bind def\n"
+				 "/xyS{xyshow}bind def\n"
 				 "%%%%EndProlog\n");
 
     num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments);
@@ -2120,6 +2123,15 @@ _cairo_ps_surface_fill (void		*abstract_
     return status;
 }
 
+/* This size keeps the length of the hex encoded string of glyphs
+ * within 80 columns. */
+#define MAX_GLYPHS_PER_SHOW  36
+
+typedef struct _cairo_ps_glyph_id {
+    unsigned int subset_id;
+    unsigned int glyph_id;
+} cairo_ps_glyph_id_t;
+
 static cairo_int_status_t
 _cairo_ps_surface_show_glyphs (void		     *abstract_surface,
 			       cairo_operator_t	      op,
@@ -2131,9 +2143,12 @@ _cairo_ps_surface_show_glyphs (void		   
     cairo_ps_surface_t *surface = abstract_surface;
     cairo_output_stream_t *stream = surface->stream;
     unsigned int current_subset_id = -1;
-    unsigned int font_id, subset_id, subset_glyph_index;
+    unsigned int font_id;
+    cairo_ps_glyph_id_t *glyph_ids;
     cairo_status_t status;
-    int i;
+    int i, j, last, end;
+    cairo_bool_t vertical, horizontal;
+    cairo_output_stream_t *word_wrap;
 
     if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
 	return _analyze_operation (surface, op, source);
@@ -2143,37 +2158,115 @@ _cairo_ps_surface_show_glyphs (void		   
     _cairo_output_stream_printf (stream,
 				 "%% _cairo_ps_surface_show_glyphs\n");
 
-    if (num_glyphs)
-	emit_pattern (surface, source);
+    if (num_glyphs == 0)
+        return CAIRO_STATUS_SUCCESS;
+
+    emit_pattern (surface, source);
+    glyph_ids = malloc (num_glyphs*sizeof (cairo_ps_glyph_id_t));
+    if (glyph_ids == NULL)
+        return CAIRO_STATUS_NO_MEMORY;
 
     for (i = 0; i < num_glyphs; i++) {
-	status = _cairo_scaled_font_subsets_map_glyph (surface->font_subsets,
-						       scaled_font, glyphs[i].index,
-						       &font_id, &subset_id, &subset_glyph_index);
-	if (status)
-	    return status;
-
-	if (subset_id != current_subset_id) {
-	    _cairo_output_stream_printf (surface->stream,
-					 "/CairoFont-%d-%d findfont\n"
-					 "[ %f %f %f %f 0 0 ] makefont\n"
-					 "setfont\n",
-					 font_id,
-					 subset_id,
-					 scaled_font->scale.xx,
-					 scaled_font->scale.yx,
-					 -scaled_font->scale.xy,
-					 -scaled_font->scale.yy);
-	    current_subset_id = subset_id;
-	}
+        status = _cairo_scaled_font_subsets_map_glyph (surface->font_subsets,
+                                                       scaled_font, glyphs[i].index,
+                                                       &font_id,
+                                                       &(glyph_ids[i].subset_id),
+                                                       &(glyph_ids[i].glyph_id));
+        if (status)
+            goto fail;
+    }
 
-	_cairo_output_stream_printf (surface->stream,
-				     "%f %f M <%02x> S\n",
-				     glyphs[i].x, glyphs[i].y,
-				     subset_glyph_index);
+    i = 0;
+    while (i < num_glyphs) {
+        if (glyph_ids[i].subset_id != current_subset_id) {
+            _cairo_output_stream_printf (surface->stream,
+                                         "/CairoFont-%d-%d findfont\n"
+                                         "[ %f %f %f %f 0 0 ] makefont\n"
+                                         "setfont\n",
+                                         font_id,
+                                         glyph_ids[i].subset_id,
+                                         scaled_font->scale.xx,
+                                         scaled_font->scale.yx,
+                                         -scaled_font->scale.xy,
+                                         -scaled_font->scale.yy);
+            current_subset_id = glyph_ids[i].subset_id;
+        }
+
+        if (i == 0)
+            _cairo_output_stream_printf (stream,
+                                         "%f %f M\n",
+                                         glyphs[i].x,
+                                         glyphs[i].y);
+
+        horizontal = TRUE;
+        vertical = TRUE;
+        end = num_glyphs;
+        if (end - i > MAX_GLYPHS_PER_SHOW)
+            end = i + MAX_GLYPHS_PER_SHOW;
+        last = end - 1;
+        for (j = i; j < end - 1; j++) {
+            if ((glyphs[j].y != glyphs[j + 1].y))
+                horizontal = FALSE;
+            if ((glyphs[j].x != glyphs[j + 1].x))
+                vertical = FALSE;
+            if (glyph_ids[j].subset_id != glyph_ids[j + 1].subset_id) {
+                last = j;
+                break;
+            }
+        }
+
+        if (i == last) {
+            _cairo_output_stream_printf (surface->stream, "<%02x> S\n", glyph_ids[i].glyph_id);
+        } else {
+            word_wrap = _word_wrap_stream_create (surface->stream, 79);
+            _cairo_output_stream_printf (word_wrap, "<");
+            for (j = i; j <= last; j++)
+                _cairo_output_stream_printf (word_wrap, "%02x", glyph_ids[j].glyph_id);
+            _cairo_output_stream_printf (word_wrap, ">\n[");
+
+            if (horizontal) {
+                for (j = i; j <= last; j++) {
+                    if (j == num_glyphs - 1)
+                        _cairo_output_stream_printf (word_wrap, "0 ");
+                    else
+                        _cairo_output_stream_printf (word_wrap,
+                                                     "%f ", glyphs[j + 1].x - glyphs[j].x);
+                }
+                _cairo_output_stream_printf (word_wrap, "] xS\n");
+            } else if (vertical) {
+                for (j = i; j <= last; j++) {
+                    if (j == num_glyphs - 1)
+                        _cairo_output_stream_printf (word_wrap, "0 ");
+                    else
+                        _cairo_output_stream_printf (word_wrap,
+                                                     "%f ", glyphs[j + 1].y - glyphs[j].y);
+                }
+                _cairo_output_stream_printf (word_wrap, "] yS\n");
+            } else {
+                for (j = i; j <= last; j++) {
+                    if (j == num_glyphs - 1)
+                        _cairo_output_stream_printf (word_wrap, "0 ");
+                    else
+                        _cairo_output_stream_printf (word_wrap,
+                                                     "%f %f ",
+                                                     glyphs[j + 1].x - glyphs[j].x,
+                                                     glyphs[j + 1].y - glyphs[j].y);
+                }
+                _cairo_output_stream_printf (word_wrap, "] xyS\n");
+            }
+
+            status = _cairo_output_stream_destroy (word_wrap);
+            if (status)
+                goto fail;
+        }
+        i = last + 1;
     }
 
-    return _cairo_output_stream_get_status (surface->stream);
+    status = _cairo_output_stream_get_status (surface->stream);
+fail:
+    free (glyph_ids);
+
+    return status;
 }
 
 static void


More information about the cairo-commit mailing list