[cairo-commit] src/cairo-cff-subset.c src/cairo-ft-font.c

Adrian Johnson ajohnson at kemper.freedesktop.org
Thu Jul 21 04:55:56 PDT 2011


 src/cairo-cff-subset.c |  577 +++++++++++++++++++++++++++++++++++++++----------
 src/cairo-ft-font.c    |   12 -
 2 files changed, 478 insertions(+), 111 deletions(-)

New commits:
commit f6043b06d658d307b9e3ac36f14d049f0a8664bf
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Thu Jul 21 21:17:18 2011 +0930

    Add support for subsetting bare CFF fonts
    
    This avoids fallback when using poppler cairo for printing PDFs with
    CFF fonts.
    
    The current CFF subsetting only works with Opentype/CFF fonts. CFF
    fonts inside PDF files are usually embedded as a bare CFF font without
    the Opentype wrapper.
    
    Making the CFF subset work with bare CFF fonts requires doing a bit of
    extra work to extract the fontname, font bbox, and glyph widths from
    the CFF data instead of using the Opentype tables.

diff --git a/src/cairo-cff-subset.c b/src/cairo-cff-subset.c
index 02f2cba..918a0b2 100644
--- a/src/cairo-cff-subset.c
+++ b/src/cairo-cff-subset.c
@@ -57,14 +57,17 @@
 #define CHARSET_OP       0x000f
 #define CHARSTRINGS_OP   0x0011
 #define COPYRIGHT_OP     0x0c00
+#define DEFAULTWIDTH_OP  0x0014
 #define ENCODING_OP      0x0010
 #define FAMILYNAME_OP    0x0003
 #define FDARRAY_OP       0x0c24
 #define FDSELECT_OP      0x0c25
 #define FONTBBOX_OP      0x0005
+#define FONTMATRIX_OP    0x0c07
 #define FONTNAME_OP      0x0c26
 #define FULLNAME_OP      0x0002
 #define LOCAL_SUB_OP     0x0013
+#define NOMINALWIDTH_OP  0x0015
 #define NOTICE_OP        0x0001
 #define POSTSCRIPT_OP    0x0c15
 #define PRIVATE_OP       0x0012
@@ -90,6 +93,11 @@
 #define TYPE2_vstemhm   0x0017
 #define TYPE2_callgsubr 0x001d
 
+#define TYPE2_rmoveto   0x0015
+#define TYPE2_hmoveto   0x0016
+#define TYPE2_vmoveto   0x0004
+
+
 #define MAX_SUBROUTINE_NESTING 10 /* From Type2 Charstring spec */
 
 
@@ -136,9 +144,12 @@ typedef struct _cairo_cff_font {
     cairo_array_t        local_sub_index;
     int                  num_glyphs;
     cairo_bool_t         is_cid;
+    cairo_bool_t         is_opentype;
     int  		 units_per_em;
     int 		 global_sub_bias;
     int			 local_sub_bias;
+    int                  default_width;
+    int                  nominal_width;
 
     /* CID Font Data */
     int                 *fdselect;
@@ -147,6 +158,8 @@ typedef struct _cairo_cff_font {
     cairo_hash_table_t **fd_private_dict;
     cairo_array_t       *fd_local_sub_index;
     int			*fd_local_sub_bias;
+    int                 *fd_default_width;
+    int                 *fd_nominal_width;
 
     /* Subsetted Font Data */
     char                *subset_font_name;
@@ -175,6 +188,10 @@ typedef struct _cairo_cff_font {
     int 		 type2_num_hints;
     int 		 type2_hintmask_bytes;
     int                  type2_nesting_level;
+    cairo_bool_t         type2_seen_first_int;
+    cairo_bool_t         type2_find_width;
+    cairo_bool_t         type2_found_width;
+    int                  type2_width;
 
 } cairo_cff_font_t;
 
@@ -238,6 +255,76 @@ decode_integer (unsigned char *p, int *integer)
     return p;
 }
 
+static char *
+decode_nibble (int n, char *buf)
+{
+    switch (n)
+    {
+    case 0xa:
+	*buf++ = '.';
+	break;
+    case 0xb:
+	*buf++ = 'E';
+	break;
+    case 0xc:
+	*buf++ = 'E';
+	*buf++ = '-';
+	break;
+    case 0xd:
+	*buf++ = '-';
+	break;
+    case 0xe:
+	*buf++ = '-';
+	break;
+    case 0xf:
+	break;
+    default:
+	*buf++ = '0' + n;
+	break;
+    }
+
+    return buf;
+}
+
+static unsigned char *
+decode_real (unsigned char *p, double *real)
+{
+    int n;
+    char buffer[100];
+    char *buf = buffer;
+    char *buf_end = buffer + sizeof (buf);
+
+    p++;
+    while (buf + 2 < buf_end) {
+	n = *p >> 4;
+	buf = decode_nibble (n, buf);
+	n = *p & 0x0f;
+	buf = decode_nibble (n, buf);
+        if ((*p & 0x0f) == 0x0f)
+            break;
+	p++;
+    };
+    *buf = 0;
+
+    if (sscanf(buffer, "%lf", real) != 1)
+        *real = 0.0;
+
+    return p;
+}
+
+static unsigned char *
+decode_number (unsigned char *p, double *number)
+{
+    if (*p == 30) {
+        p = decode_real (p, number);
+    } else {
+        int i;
+        p = decode_integer (p, &i);
+        *number = i;
+    }
+    return p;
+}
+
 static unsigned char *
 decode_operator (unsigned char *p, unsigned short *operator)
 {
@@ -742,6 +829,7 @@ 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;
 
@@ -753,11 +841,19 @@ cairo_cff_font_read_name (cairo_cff_font_t *font)
 {
     cairo_array_t index;
     cairo_int_status_t status;
+    cff_index_element_t *element;
 
-    /* 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);
+    if (!font->is_opentype) {
+        element = _cairo_array_index (&index, 0);
+        font->ps_name = malloc (element->length + 1);
+        if (unlikely (font->ps_name == NULL))
+            return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+        memcpy (font->ps_name, element->data, element->length);
+        font->ps_name[element->length] = 0;
+    }
     cff_index_fini (&index);
 
     return status;
@@ -769,6 +865,8 @@ cairo_cff_font_read_private_dict (cairo_cff_font_t   *font,
                                   cairo_array_t      *local_sub_index,
                                   int                *local_sub_bias,
                                   cairo_bool_t      **local_subs_used,
+                                  int                *default_width,
+                                  int                *nominal_width,
                                   unsigned char      *ptr,
                                   int                 size)
 {
@@ -798,9 +896,18 @@ cairo_cff_font_read_private_dict (cairo_cff_font_t   *font,
 	status = cff_dict_set_operands (private_dict, LOCAL_SUB_OP, buf, end_buf - buf);
 	if (unlikely (status))
 	    return status;
-
     }
 
+    *default_width = 0;
+    operand = cff_dict_get_operands (private_dict, DEFAULTWIDTH_OP, &i);
+    if (operand)
+        decode_integer (operand, default_width);
+
+    *nominal_width = 0;
+    operand = cff_dict_get_operands (private_dict, NOMINALWIDTH_OP, &i);
+    if (operand)
+        decode_integer (operand, nominal_width);
+
     num_subs = _cairo_array_num_elements (local_sub_index);
     *local_subs_used = calloc (num_subs, sizeof (cairo_bool_t));
     if (unlikely (*local_subs_used == NULL))
@@ -899,6 +1006,18 @@ cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr)
         goto fail;
     }
 
+    font->fd_default_width = calloc (sizeof (int), font->num_fontdicts);
+    if (unlikely (font->fd_default_width == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail;
+    }
+
+    font->fd_nominal_width = calloc (sizeof (int), font->num_fontdicts);
+    if (unlikely (font->fd_nominal_width == 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))
@@ -926,6 +1045,8 @@ cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr)
                                                    &font->fd_local_sub_index[i],
                                                    &font->fd_local_sub_bias[i],
                                                    &font->fd_local_subs_used[i],
+                                                   &font->fd_default_width[i],
+                                                   &font->fd_nominal_width[i],
                                                    font->data + offset,
                                                    size);
         if (unlikely (status))
@@ -948,6 +1069,58 @@ fail:
     return status;
 }
 
+static void
+cairo_cff_font_read_font_metrics (cairo_cff_font_t *font, cairo_hash_table_t  *top_dict)
+{
+    unsigned char *p;
+    unsigned char *end;
+    int size;
+    double x_min, y_min, x_max, y_max;
+    double xx, yx, xy, yy;
+
+    font->x_min = 0.0;
+    font->y_min = 0.0;
+    font->x_max = 0.0;
+    font->y_max = 0.0;
+    p = cff_dict_get_operands (font->top_dict, FONTBBOX_OP, &size);
+    if (p) {
+        end = p + size;
+        if (p < end)
+            p = decode_number (p, &x_min);
+        if (p < end)
+            p = decode_number (p, &y_min);
+        if (p < end)
+            p = decode_number (p, &x_max);
+        if (p < end)
+            p = decode_number (p, &y_max);
+    }
+    font->x_min = floor (x_min);
+    font->y_min = floor (y_min);
+    font->x_max = floor (x_max);
+    font->y_max = floor (y_max);
+    font->ascent = font->y_max;
+    font->descent = font->y_min;
+
+    xx = 0.001;
+    yx = 0.0;
+    xy = 0.0;
+    yy = 0.001;
+    p = cff_dict_get_operands (font->top_dict, FONTMATRIX_OP, &size);
+    if (p) {
+        end = p + size;
+        if (p < end)
+            p = decode_number (p, &xx);
+        if (p < end)
+            p = decode_number (p, &yx);
+        if (p < end)
+            p = decode_number (p, &xy);
+        if (p < end)
+            p = decode_number (p, &yy);
+    }
+    /* Freetype uses 1/yy to get units per EM */
+    font->units_per_em = _cairo_round(1.0/yy);
+}
+
 static cairo_int_status_t
 cairo_cff_font_read_top_dict (cairo_cff_font_t *font)
 {
@@ -984,6 +1157,9 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font)
         goto fail;
     font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index);
 
+    if (!font->is_opentype)
+        cairo_cff_font_read_font_metrics (font, font->top_dict);
+
     if (font->is_cid) {
         operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size);
         decode_integer (operand, &offset);
@@ -1005,6 +1181,8 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font)
 						   &font->local_sub_index,
 						   &font->local_sub_bias,
 						   &font->local_subs_used,
+                                                   &font->default_width,
+                                                   &font->nominal_width,
 						   font->data + offset,
 						   size);
 	if (unlikely (status))
@@ -1243,21 +1421,30 @@ type2_decode_integer (unsigned char *p, int *integer)
 }
 
 /* Type 2 charstring parser for finding calls to local or global
- * subroutines.  When we find a subroutine operator, the subroutine is
- * marked as in use and recursively followed. The subroutine number is
- * the value on the top of the stack when the subroutine operator is
- * executed. In most fonts the subroutine number is encoded in an
- * integer immediately preceding the subroutine operator. However it
- * is possible for the subroutine number on the stack to be the result
- * of a computation (in which case there will be an operator preceding
+ * subroutines. For non Opentype CFF fonts it also gets the glyph
+ * widths.
+ *
+ * When we find a subroutine operator, the subroutine is marked as in
+ * use and recursively followed. The subroutine number is the value on
+ * the top of the stack when the subroutine operator is executed. In
+ * most fonts the subroutine number is encoded in an integer
+ * immediately preceding the subroutine operator. However it is
+ * possible for the subroutine number on the stack to be the result of
+ * a computation (in which case there will be an operator preceding
  * the subroutine operator). If this occurs, subroutine subsetting is
  * disabled since we can't easily determine which subroutines are
  * used.
+ *
+ * The width, if present, is the first integer in the charstring. The
+ * only way to confirm if an integer at the start of the charstring is
+ * the width is when the first stack clearing operator is parsed,
+ * check if there is an extra integer left over on the stack.
  */
 static cairo_status_t
 cairo_cff_parse_charstring (cairo_cff_font_t *font,
                             unsigned char *charstring, int length,
-                            int glyph_id)
+                            int glyph_id,
+			    cairo_bool_t need_width)
 {
     unsigned char *p = charstring;
     unsigned char *end = charstring + length;
@@ -1274,13 +1461,21 @@ cairo_cff_parse_charstring (cairo_cff_font_t *font,
             font->type2_stack_size++;
             font->type2_stack_top_value = integer;
             font->type2_stack_top_is_int = TRUE;
+	    if (!font->type2_seen_first_int) {
+		font->type2_width = integer;
+		font->type2_seen_first_int = TRUE;
+	    }
 	} else if (*p == TYPE2_hstem || *p == TYPE2_vstem ||
 		   *p == TYPE2_hstemhm || *p == TYPE2_vstemhm) {
             /* Hint operator. The number of hints declared by the
              * operator depends on the size of the stack. */
 	    font->type2_stack_top_is_int = FALSE;
 	    font->type2_num_hints += font->type2_stack_size/2;
+	    if (font->type2_find_width && font->type2_stack_size % 2)
+		font->type2_found_width = TRUE;
+
 	    font->type2_stack_size = 0;
+	    font->type2_find_width = FALSE;
 	    p++;
 	} else if (*p == TYPE2_hintmask || *p == TYPE2_cntrmask) {
 	    /* Hintmask operator. These operators are followed by a
@@ -1291,13 +1486,36 @@ cairo_cff_parse_charstring (cairo_cff_font_t *font,
 	    if (font->type2_hintmask_bytes == 0) {
 		font->type2_stack_top_is_int = FALSE;
 		font->type2_num_hints += font->type2_stack_size/2;
+		if (font->type2_find_width && font->type2_stack_size % 2)
+		    font->type2_found_width = TRUE;
+
 		font->type2_stack_size = 0;
+		font->type2_find_width = FALSE;
 		font->type2_hintmask_bytes = (font->type2_num_hints+7)/8;
 	    }
 
 	    hint_bytes = font->type2_hintmask_bytes;
 	    p++;
 	    p += hint_bytes;
+	} else if (*p == TYPE2_rmoveto) {
+	    if (font->type2_find_width && font->type2_stack_size > 2)
+		font->type2_found_width = TRUE;
+
+	    font->type2_stack_size = 0;
+	    font->type2_find_width = FALSE;
+	    p++;
+	} else if (*p == TYPE2_hmoveto || *p == TYPE2_vmoveto) {
+	    if (font->type2_find_width && font->type2_stack_size > 1)
+		font->type2_found_width = TRUE;
+
+	    font->type2_stack_size = 0;
+	    font->type2_find_width = FALSE;
+	    p++;
+	} else if (*p == TYPE2_endchar) {
+	    if (font->type2_find_width && font->type2_stack_size > 0)
+		font->type2_found_width = TRUE;
+
+	    return CAIRO_STATUS_SUCCESS;
         } else if (*p == TYPE2_callsubr) {
             /* call to local subroutine */
 	    if (! font->type2_stack_top_is_int)
@@ -1309,20 +1527,25 @@ cairo_cff_parse_charstring (cairo_cff_font_t *font,
             p++;
 	    font->type2_stack_top_is_int = FALSE;
             font->type2_stack_size--;
+	    if (font->type2_find_width && font->type2_stack_size == 0)
+		font->type2_seen_first_int = FALSE;
+
             if (font->is_cid) {
                 fd = font->fdselect[glyph_id];
                 sub_num = font->type2_stack_top_value + font->fd_local_sub_bias[fd];
                 element = _cairo_array_index (&font->fd_local_sub_index[fd], sub_num);
                 if (! font->fd_local_subs_used[fd][sub_num]) {
 		    font->fd_local_subs_used[fd][sub_num] = TRUE;
-		    cairo_cff_parse_charstring (font, element->data, element->length, glyph_id);
+		    cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width);
 		}
             } else {
                 sub_num = font->type2_stack_top_value + font->local_sub_bias;
                 element = _cairo_array_index (&font->local_sub_index, sub_num);
-                if (! font->local_subs_used[sub_num]) {
+                if (! font->local_subs_used[sub_num] ||
+		    (need_width && !font->type2_found_width))
+		{
 		    font->local_subs_used[sub_num] = TRUE;
-		    cairo_cff_parse_charstring (font, element->data, element->length, glyph_id);
+		    cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width);
 		}
             }
             font->type2_nesting_level--;
@@ -1337,15 +1560,25 @@ cairo_cff_parse_charstring (cairo_cff_font_t *font,
             p++;
             font->type2_stack_size--;
 	    font->type2_stack_top_is_int = FALSE;
+	    if (font->type2_find_width && font->type2_stack_size == 0)
+		font->type2_seen_first_int = FALSE;
+
 	    sub_num = font->type2_stack_top_value + font->global_sub_bias;
 	    element = _cairo_array_index (&font->global_sub_index, sub_num);
-            if (! font->global_subs_used[sub_num]) {
+            if (! font->global_subs_used[sub_num] ||
+		(need_width && !font->type2_found_width))
+	    {
                 font->global_subs_used[sub_num] = TRUE;
-                cairo_cff_parse_charstring (font, element->data, element->length, glyph_id);
+                cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width);
             }
             font->type2_nesting_level--;
         } else if (*p == 12) {
             /* 2 byte instruction */
+
+	    /* Most of the 2 byte operators */
+	    if (need_width && (p[1] < 0x22 || p[1] > 0x25))
+		return CAIRO_INT_STATUS_UNSUPPORTED;
+
             p += 2;
 	    font->type2_stack_top_is_int = FALSE;
         } else {
@@ -1361,16 +1594,42 @@ cairo_cff_parse_charstring (cairo_cff_font_t *font,
 static cairo_status_t
 cairo_cff_find_subroutines_used (cairo_cff_font_t  *font,
                                  unsigned char *charstring, int length,
-                                 int glyph_id)
+                                 int glyph_id, int subset_id)
 {
+    cairo_status_t status;
+    int width;
+    int fd;
+
     font->type2_stack_size = 0;
     font->type2_stack_top_value = 0;;
     font->type2_stack_top_is_int = FALSE;
     font->type2_num_hints = 0;
     font->type2_hintmask_bytes = 0;
     font->type2_nesting_level = 0;
+    font->type2_seen_first_int = FALSE;
+    font->type2_find_width = TRUE;
+    font->type2_found_width = FALSE;
+    font->type2_width = 0;
+
+    status = cairo_cff_parse_charstring (font, charstring, length, glyph_id, TRUE);
+
+    if (!font->is_opentype) {
+        if (font->is_cid) {
+            fd = font->fdselect[glyph_id];
+            if (font->type2_found_width)
+                width = font->fd_nominal_width[fd] + font->type2_width;
+            else
+                width = font->fd_default_width[fd];
+        } else {
+            if (font->type2_found_width)
+                width = font->nominal_width + font->type2_width;
+            else
+                width = font->default_width;
+        }
+        font->widths[subset_id] = width;
+    }
 
-    return cairo_cff_parse_charstring (font, charstring, length, glyph_id);
+    return status;
 }
 
 static cairo_int_status_t
@@ -1392,7 +1651,7 @@ cairo_cff_font_subset_charstrings_and_subroutines (cairo_cff_font_t  *font)
             return status;
 
 	if (font->subset_subroutines) {
-	    status = cairo_cff_find_subroutines_used (font, element->data, element->length, glyph);
+	    status = cairo_cff_find_subroutines_used (font, element->data, element->length, glyph, i);
 	    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
 		font->subset_subroutines = FALSE;
 	    } else if (unlikely (status))
@@ -1618,8 +1877,8 @@ cairo_cff_font_write_name (cairo_cff_font_t *font)
     cff_index_init (&index);
 
     status = cff_index_append_copy (&index,
-                                    (unsigned char *) font->subset_font_name,
-                                    strlen(font->subset_font_name));
+                                    (unsigned char *) font->ps_name,
+                                    strlen(font->ps_name));
     if (unlikely (status))
 	goto FAIL;
 
@@ -2152,6 +2411,17 @@ cairo_cff_font_generate (cairo_cff_font_t  *font,
     if (unlikely (status))
         return status;
 
+    /* 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))
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+        snprintf(font->ps_name, 30, "CairoFont-%u-%u",
+                 font->scaled_font_subset->font_id,
+                 font->scaled_font_subset->subset_id);
+    }
+
     status = cairo_cff_font_subset_font (font);
     if (unlikely (status))
         return status;
@@ -2160,6 +2430,7 @@ cairo_cff_font_generate (cairo_cff_font_t  *font,
     if (unlikely (status))
         return status;
 
+
     *data = _cairo_array_index (&font->output, 0);
     *length = _cairo_array_num_elements (&font->output);
 
@@ -2187,7 +2458,7 @@ cairo_cff_font_create_set_widths (cairo_cff_font_t *font)
         return status;
     num_hmetrics = be16_to_cpu (hhea.num_hmetrics);
 
-    for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
+    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);
@@ -2214,69 +2485,63 @@ cairo_cff_font_create_set_widths (cairo_cff_font_t *font)
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_bool_t
+check_fontdata_is_cff (const unsigned char *data, long length)
+{
+    cff_header_t *header;
+
+    if (length < (long)sizeof (cff_header_t))
+        return FALSE;
+
+    header = (cff_header_t *) data;
+    if (header->major == 1 &&
+	header->minor == 0 &&
+	header->header_size == 4)
+    {
+	return TRUE;
+    }
+
+    return FALSE;
+}
+
 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)
+_cairo_cff_font_load_opentype_cff (cairo_cff_font_t  *font)
 {
-    const cairo_scaled_font_backend_t *backend;
+    const cairo_scaled_font_backend_t *backend = font->backend;
     cairo_status_t status;
-    cairo_cff_font_t *font;
     tt_head_t head;
     tt_hhea_t hhea;
     unsigned long size, data_length;
 
-    backend = scaled_font_subset->scaled_font->backend;
     if (!backend->load_truetype_table)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
-    /* We need to use a fallback font generated from the synthesized outlines. */
-    if (backend->is_synthetic && backend->is_synthetic (scaled_font_subset->scaled_font))
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-
     data_length = 0;
-    status = backend->load_truetype_table( scaled_font_subset->scaled_font,
+    status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
                                            TT_TAG_CFF, 0, NULL, &data_length);
-    if (unlikely (status))
+    if (status)
         return status;
 
     size = sizeof (tt_head_t);
-    status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+    status = backend->load_truetype_table (font->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,
+    status = backend->load_truetype_table (font->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,
+    status = backend->load_truetype_table (font->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);
@@ -2288,56 +2553,123 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t  *scaled_font_subset,
         font->units_per_em = 1000;
 
     font->font_name = NULL;
-    status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font,
+    status = _cairo_truetype_read_font_name (font->scaled_font_subset->scaled_font,
 					     &font->ps_name,
 					     &font->font_name);
     if (_cairo_status_is_error (status))
-	goto fail3;
+	return status;
 
-    /* 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;
-	}
+    font->is_opentype = TRUE;
+    font->data_length = data_length;
+    font->data = malloc (data_length);
+    if (unlikely (font->data == NULL))
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-        snprintf(font->ps_name, 30, "CairoFont-%u-%u",
-                 scaled_font_subset->font_id,
-                 scaled_font_subset->subset_id);
-    }
+    status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+						 TT_TAG_CFF, 0, font->data,
+						 &font->data_length);
+    if (unlikely (status))
+        return status;
 
-    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;
-    }
+    if (!check_fontdata_is_cff (font->data, data_length))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
 
-    status = cairo_cff_font_create_set_widths (font);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_cff_font_load_cff (cairo_cff_font_t  *font)
+{
+    const cairo_scaled_font_backend_t *backend = font->backend;
+    cairo_status_t status;
+    unsigned long data_length;
+
+    if (!backend->load_type1_data)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    data_length = 0;
+    status = backend->load_type1_data (font->scaled_font_subset->scaled_font,
+				      0, NULL, &data_length);
     if (unlikely (status))
-	goto fail5;
+        return status;
 
+    font->font_name = NULL;
+    font->is_opentype = FALSE;
     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 (font->data == NULL))
+        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    status = font->backend->load_type1_data (font->scaled_font_subset->scaled_font,
+					     0, font->data, &font->data_length);
     if (unlikely (status))
-        goto fail6;
+        return status;
+
+    if (!check_fontdata_is_cff (font->data, data_length))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    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_int_status_t status;
+    cairo_cff_font_t *font;
+
+    backend = scaled_font_subset->scaled_font->backend;
+
+    /* We need to use a fallback font generated from the synthesized outlines. */
+    if (backend->is_synthetic && backend->is_synthetic (scaled_font_subset->scaled_font))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    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;
+
+    status = _cairo_cff_font_load_opentype_cff (font);
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+	status = _cairo_cff_font_load_cff (font);
+    if (status)
+	goto fail1;
 
     font->data_end = font->data + font->data_length;
+    _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->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int));
+    if (unlikely (font->widths == NULL)) {
+        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        goto fail3;
+    }
+
+    if (font->is_opentype) {
+	status = cairo_cff_font_create_set_widths (font);
+	if (unlikely (status))
+	    goto fail4;
+    }
 
     status = cff_dict_init (&font->top_dict);
     if (unlikely (status))
-	goto fail6;
+	goto fail4;
 
     status = cff_dict_init (&font->private_dict);
     if (unlikely (status))
-	goto fail7;
+	goto fail5;
 
     cff_index_init (&font->strings_index);
     cff_index_init (&font->charstrings_index);
@@ -2362,19 +2694,20 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t  *scaled_font_subset,
 
     return CAIRO_STATUS_SUCCESS;
 
-fail7:
-    _cairo_hash_table_destroy (font->top_dict);
-fail6:
-    free (font->data);
 fail5:
-    free (font->widths);
+    _cairo_hash_table_destroy (font->top_dict);
 fail4:
-    if (font->font_name)
-	free (font->font_name);
+    free (font->widths);
 fail3:
     free (font->subset_font_name);
 fail2:
+    free (font->data);
+    if (font->font_name)
+	free (font->font_name);
+    if (font->ps_name)
+	free (font->ps_name);
     _cairo_array_fini (&font->output);
+fail1:
     free (font);
 
     return status;
@@ -2444,7 +2777,10 @@ cairo_cff_font_destroy (cairo_cff_font_t *font)
             }
             free (font->fd_local_subs_used);
         }
-
+        if (font->fd_default_width)
+            free (font->fd_default_width);
+        if (font->fd_nominal_width)
+            free (font->fd_nominal_width);
     }
 
     if (font->data)
@@ -2556,27 +2892,47 @@ _cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font)
     cairo_bool_t is_cid = FALSE;
 
     backend = scaled_font->backend;
-    if (!backend->load_truetype_table)
-	return FALSE;
-
-    /* check for CFF font */
+    data = NULL;
     data_length = 0;
-    status = backend->load_truetype_table(scaled_font,
-					  TT_TAG_CFF, 0, NULL, &data_length);
-    if (status)
-        return FALSE;
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+    /* Try to load an OpenType/CFF font */
+    if (backend->load_truetype_table &&
+	(status = backend->load_truetype_table (scaled_font, TT_TAG_CFF,
+						0, NULL, &data_length)) == CAIRO_STATUS_SUCCESS)
+    {
+	data = malloc (data_length);
+	if (unlikely (data == NULL)) {
+	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    return FALSE;
+	}
 
-    /* load CFF data */
-    data = malloc (data_length);
-    if (unlikely (data == NULL)) {
-        status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-        return FALSE;
+	status = backend->load_truetype_table (scaled_font, TT_TAG_CFF,
+					   0, data, &data_length);
+	if (unlikely (status))
+	    goto fail1;
     }
+    /* Try to load a CFF font */
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED &&
+	backend->load_type1_data &&
+	(status = backend->load_type1_data (scaled_font,
+					    0, NULL, &data_length)) == CAIRO_STATUS_SUCCESS)
+    {
+	data = malloc (data_length);
+	if (unlikely (data == NULL)) {
+	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    return FALSE;
+	}
 
-    status = backend->load_truetype_table (scaled_font, TT_TAG_CFF,
-					   0, data, &data_length);
-    if (unlikely (status))
-        goto fail1;
+	status = backend->load_type1_data (scaled_font, 0, data, &data_length);
+	if (unlikely (status))
+	    goto fail1;
+    }
+    if (status)
+	goto fail1;
+
+    /* Check if it looks like a CFF font */
+    if (!check_fontdata_is_cff (data, data_length))
+	goto fail1;
 
     data_end = data + data_length;
 
@@ -2621,7 +2977,8 @@ fail2:
     cff_index_fini (&index);
 
 fail1:
-    free (data);
+    if (data)
+	free (data);
 
     return is_cid;
 }
diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index ff737a3..822833a 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -2521,8 +2521,18 @@ _cairo_ft_load_type1_data (void	            *abstract_font,
     if (!face)
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
+#if HAVE_FT_LOAD_SFNT_TABLE
+    if (FT_IS_SFNT (face)) {
+	status = CAIRO_INT_STATUS_UNSUPPORTED;
+	goto unlock;
+    }
+#endif
+
     font_format = FT_Get_X11_Font_Format (face);
-    if (!(font_format && strcmp (font_format, "Type 1") == 0)) {
+    if (!font_format ||
+	!(strcmp (font_format, "Type 1") == 0 ||
+	  strcmp (font_format, "CFF") == 0))
+    {
         status = CAIRO_INT_STATUS_UNSUPPORTED;
 	goto unlock;
     }


More information about the cairo-commit mailing list