summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Kasik <mkasik@redhat.com>2014-01-09 17:28:32 +0100
committerAdrian Johnson <ajohnson@redneon.com>2014-01-10 20:51:08 +1030
commit97f6e2005d9cbc9c9dd7cc21445df7c08e084c83 (patch)
tree02512a45a78a4a14f1598db2bf79ab636c790acb
parentf81b1406755a1e63f0c44996ca9ce3802b1877ba (diff)
font: Generate PDFs with correct font names
Escape PostScript names of loaded fonts. These can not contain white spaces and delimiter characters when saving them to a PostScript file or a PDF file.
-rw-r--r--src/cairo-cff-subset.c2
-rw-r--r--src/cairo-scaled-font-subsets-private.h15
-rw-r--r--src/cairo-scaled-font-subsets.c40
-rw-r--r--src/cairo-truetype-subset.c1631
-rw-r--r--src/cairo-type1-subset.c9
5 files changed, 1691 insertions, 6 deletions
diff --git a/src/cairo-cff-subset.c b/src/cairo-cff-subset.c
index c420bd4..1ae032c 100644
--- a/src/cairo-cff-subset.c
+++ b/src/cairo-cff-subset.c
@@ -899,6 +899,8 @@ cairo_cff_font_read_name (cairo_cff_font_t *font)
memcpy (font->ps_name, p, len);
font->ps_name[len] = 0;
+
+ status = _cairo_escape_ps_name (&font->ps_name);
}
cff_index_fini (&index);
diff --git a/src/cairo-scaled-font-subsets-private.h b/src/cairo-scaled-font-subsets-private.h
index dd19962..866e63d 100644
--- a/src/cairo-scaled-font-subsets-private.h
+++ b/src/cairo-scaled-font-subsets-private.h
@@ -715,6 +715,21 @@ _cairo_truetype_get_style (cairo_scaled_font_t *scaled_font,
cairo_bool_t *bold,
cairo_bool_t *italic);
+/**
+ * _cairo_escape_ps_name:
+ * @ps_name: returns the PostScript name with all invalid characters escaped
+ *
+ * Ensure that PostSript name is a valid PDF/PostSript name object.
+ * In PDF names are treated as UTF8 and non ASCII bytes, ' ',
+ * and '#' are encoded as '#' followed by 2 hex digits that
+ * encode the byte.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful. Possible errors include
+ * %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_int_status_t
+_cairo_escape_ps_name (char **ps_name);
+
#endif /* CAIRO_HAS_FONT_SUBSET */
#endif /* CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H */
diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c
index e78e0c2..2121761 100644
--- a/src/cairo-scaled-font-subsets.c
+++ b/src/cairo-scaled-font-subsets.c
@@ -1256,4 +1256,44 @@ CLEANUP_HASH:
return status;
}
+cairo_int_status_t
+_cairo_escape_ps_name (char **ps_name)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ /* Ensure PS name is a valid PDF/PS name object. In PDF names are
+ * treated as UTF8 and non ASCII bytes, ' ', and '#' are encoded
+ * as '#' followed by 2 hex digits that encode the byte. By also
+ * encoding the characters in the reserved string we ensure the
+ * name is also PS compatible. */
+ if (*ps_name) {
+ static const char *reserved = "()<>[]{}/%#\\";
+ char buf[128]; /* max name length is 127 bytes */
+ char *src = *ps_name;
+ char *dst = buf;
+
+ while (*src && dst < buf + 127) {
+ unsigned char c = *src;
+ if (c < 0x21 || c > 0x7e || strchr (reserved, c)) {
+ if (dst + 4 > buf + 127)
+ break;
+
+ snprintf (dst, 4, "#%02X", c);
+ src++;
+ dst += 3;
+ } else {
+ *dst++ = *src++;
+ }
+ }
+ *dst = 0;
+ free (*ps_name);
+ *ps_name = strdup (buf);
+ if (*ps_name == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ }
+
+ return status;
+}
+
#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-truetype-subset.c b/src/cairo-truetype-subset.c
index 18ee685..3d55fef 100644
--- a/src/cairo-truetype-subset.c
+++ b/src/cairo-truetype-subset.c
@@ -0,0 +1,1631 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Kristian Høgsberg <krh@redhat.com>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+/*
+ * Useful links:
+ * http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html
+ * http://www.microsoft.com/typography/specs/default.htm
+ */
+
+#define _BSD_SOURCE /* for snprintf(), strdup() */
+#include "cairoint.h"
+
+#include "cairo-array-private.h"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_FONT_SUBSET
+
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-truetype-subset-private.h"
+
+
+typedef struct subset_glyph subset_glyph_t;
+struct subset_glyph {
+ int parent_index;
+ unsigned long location;
+};
+
+typedef struct _cairo_truetype_font cairo_truetype_font_t;
+
+typedef struct table table_t;
+struct table {
+ unsigned long tag;
+ cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag);
+ int pos; /* position in the font directory */
+};
+
+struct _cairo_truetype_font {
+
+ cairo_scaled_font_subset_t *scaled_font_subset;
+
+ table_t truetype_tables[10];
+ int num_tables;
+
+ struct {
+ char *font_name;
+ char *ps_name;
+ unsigned int num_glyphs;
+ int *widths;
+ long x_min, y_min, x_max, y_max;
+ long ascent, descent;
+ int units_per_em;
+ } base;
+
+ subset_glyph_t *glyphs;
+ const cairo_scaled_font_backend_t *backend;
+ int num_glyphs_in_face;
+ int checksum_index;
+ cairo_array_t output;
+ cairo_array_t string_offsets;
+ unsigned long last_offset;
+ unsigned long last_boundary;
+ int *parent_to_subset;
+ cairo_status_t status;
+ cairo_bool_t is_pdf;
+};
+
+/*
+ * Test that the structs we define for TrueType tables have the
+ * correct size, ie. they are not padded.
+ */
+#define check(T, S) COMPILE_TIME_ASSERT (sizeof (T) == (S))
+check (tt_head_t, 54);
+check (tt_hhea_t, 36);
+check (tt_maxp_t, 32);
+check (tt_name_record_t, 12);
+check (tt_name_t, 18);
+check (tt_name_t, 18);
+check (tt_composite_glyph_t, 16);
+check (tt_glyph_data_t, 26);
+#undef check
+
+static cairo_status_t
+cairo_truetype_font_use_glyph (cairo_truetype_font_t *font,
+ unsigned short glyph,
+ unsigned short *out);
+
+#define SFNT_VERSION 0x00010000
+#define SFNT_STRING_MAX_LENGTH 65535
+
+static cairo_status_t
+_cairo_truetype_font_set_error (cairo_truetype_font_t *font,
+ cairo_status_t status)
+{
+ if (status == CAIRO_STATUS_SUCCESS ||
+ status == (int)CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ _cairo_status_set_error (&font->status, status);
+
+ return _cairo_error (status);
+}
+
+static cairo_status_t
+_cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
+ cairo_bool_t is_pdf,
+ cairo_truetype_font_t **font_return)
+{
+ cairo_status_t status;
+ cairo_truetype_font_t *font;
+ const cairo_scaled_font_backend_t *backend;
+ tt_head_t head;
+ tt_hhea_t hhea;
+ tt_maxp_t maxp;
+ unsigned long size;
+
+ backend = scaled_font_subset->scaled_font->backend;
+ if (!backend->load_truetype_table)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* FIXME: We should either support subsetting vertical fonts, or fail on
+ * vertical. Currently font_options_t doesn't have vertical flag, but
+ * it should be added in the future. For now, the freetype backend
+ * returns UNSUPPORTED in load_truetype_table if the font is vertical.
+ *
+ * if (cairo_font_options_get_vertical_layout (scaled_font_subset->scaled_font))
+ * return CAIRO_INT_STATUS_UNSUPPORTED;
+ */
+
+ /* 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;
+
+ size = sizeof (tt_head_t);
+ status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+ TT_TAG_head, 0,
+ (unsigned char *) &head,
+ &size);
+ if (unlikely (status))
+ return status;
+
+ size = sizeof (tt_maxp_t);
+ status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+ TT_TAG_maxp, 0,
+ (unsigned char *) &maxp,
+ &size);
+ if (unlikely (status))
+ return status;
+
+ size = sizeof (tt_hhea_t);
+ status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+ TT_TAG_hhea, 0,
+ (unsigned char *) &hhea,
+ &size);
+ if (unlikely (status))
+ return status;
+
+ font = malloc (sizeof (cairo_truetype_font_t));
+ if (unlikely (font == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font->backend = backend;
+ font->num_glyphs_in_face = be16_to_cpu (maxp.num_glyphs);
+ font->scaled_font_subset = scaled_font_subset;
+
+ font->last_offset = 0;
+ font->last_boundary = 0;
+ _cairo_array_init (&font->output, sizeof (char));
+ status = _cairo_array_grow_by (&font->output, 4096);
+ if (unlikely (status))
+ goto fail1;
+
+ font->glyphs = calloc (font->num_glyphs_in_face + 1, sizeof (subset_glyph_t));
+ if (unlikely (font->glyphs == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail1;
+ }
+
+ font->parent_to_subset = calloc (font->num_glyphs_in_face, sizeof (int));
+ if (unlikely (font->parent_to_subset == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+
+ font->is_pdf = is_pdf;
+ font->base.num_glyphs = 0;
+ font->base.x_min = (int16_t) be16_to_cpu (head.x_min);
+ font->base.y_min = (int16_t) be16_to_cpu (head.y_min);
+ font->base.x_max = (int16_t) be16_to_cpu (head.x_max);
+ font->base.y_max = (int16_t) be16_to_cpu (head.y_max);
+ font->base.ascent = (int16_t) be16_to_cpu (hhea.ascender);
+ font->base.descent = (int16_t) be16_to_cpu (hhea.descender);
+ font->base.units_per_em = (int16_t) be16_to_cpu (head.units_per_em);
+ if (font->base.units_per_em == 0)
+ font->base.units_per_em = 2048;
+
+ font->base.ps_name = NULL;
+ font->base.font_name = NULL;
+ status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font,
+ &font->base.ps_name,
+ &font->base.font_name);
+ if (_cairo_status_is_error (status))
+ goto fail3;
+
+ /* If the PS name is not found, create a CairoFont-x-y name. */
+ if (font->base.ps_name == NULL) {
+ font->base.ps_name = malloc (30);
+ if (unlikely (font->base.ps_name == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail3;
+ }
+
+ snprintf(font->base.ps_name, 30, "CairoFont-%u-%u",
+ scaled_font_subset->font_id,
+ scaled_font_subset->subset_id);
+ }
+
+ font->base.widths = calloc (font->num_glyphs_in_face, sizeof (int));
+ if (unlikely (font->base.widths == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail4;
+ }
+
+ _cairo_array_init (&font->string_offsets, sizeof (unsigned long));
+ status = _cairo_array_grow_by (&font->string_offsets, 10);
+ if (unlikely (status))
+ goto fail5;
+
+ font->status = CAIRO_STATUS_SUCCESS;
+
+ *font_return = font;
+
+ return CAIRO_STATUS_SUCCESS;
+
+ fail5:
+ _cairo_array_fini (&font->string_offsets);
+ free (font->base.widths);
+ fail4:
+ free (font->base.ps_name);
+ fail3:
+ free (font->parent_to_subset);
+ free (font->base.font_name);
+ fail2:
+ free (font->glyphs);
+ fail1:
+ _cairo_array_fini (&font->output);
+ free (font);
+
+ return status;
+}
+
+static void
+cairo_truetype_font_destroy (cairo_truetype_font_t *font)
+{
+ _cairo_array_fini (&font->string_offsets);
+ free (font->base.widths);
+ free (font->base.ps_name);
+ free (font->base.font_name);
+ free (font->parent_to_subset);
+ free (font->glyphs);
+ _cairo_array_fini (&font->output);
+ free (font);
+}
+
+static cairo_status_t
+cairo_truetype_font_allocate_write_buffer (cairo_truetype_font_t *font,
+ size_t length,
+ unsigned char **buffer)
+{
+ cairo_status_t status;
+
+ if (font->status)
+ return font->status;
+
+ status = _cairo_array_allocate (&font->output, length, (void **) buffer);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cairo_truetype_font_write (cairo_truetype_font_t *font,
+ const void *data,
+ size_t length)
+{
+ cairo_status_t status;
+
+ if (font->status)
+ return;
+
+ status = _cairo_array_append_multiple (&font->output, data, length);
+ if (unlikely (status))
+ status = _cairo_truetype_font_set_error (font, status);
+}
+
+static void
+cairo_truetype_font_write_be16 (cairo_truetype_font_t *font,
+ uint16_t value)
+{
+ uint16_t be16_value;
+
+ if (font->status)
+ return;
+
+ be16_value = cpu_to_be16 (value);
+ cairo_truetype_font_write (font, &be16_value, sizeof be16_value);
+}
+
+static void
+cairo_truetype_font_write_be32 (cairo_truetype_font_t *font,
+ uint32_t value)
+{
+ uint32_t be32_value;
+
+ if (font->status)
+ return;
+
+ be32_value = cpu_to_be32 (value);
+ cairo_truetype_font_write (font, &be32_value, sizeof be32_value);
+}
+
+static cairo_status_t
+cairo_truetype_font_align_output (cairo_truetype_font_t *font,
+ unsigned long *aligned)
+{
+ int length, pad;
+ unsigned char *padding;
+
+ length = _cairo_array_num_elements (&font->output);
+ *aligned = (length + 3) & ~3;
+ pad = *aligned - length;
+
+ if (pad) {
+ cairo_status_t status;
+
+ status = cairo_truetype_font_allocate_write_buffer (font, pad,
+ &padding);
+ if (unlikely (status))
+ return status;
+
+ memset (padding, 0, pad);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_check_boundary (cairo_truetype_font_t *font,
+ unsigned long boundary)
+{
+ cairo_status_t status;
+
+ if (font->status)
+ return font->status;
+
+ if (boundary - font->last_offset > SFNT_STRING_MAX_LENGTH)
+ {
+ status = _cairo_array_append (&font->string_offsets,
+ &font->last_boundary);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ font->last_offset = font->last_boundary;
+ }
+ font->last_boundary = boundary;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct _cmap_unicode_range {
+ unsigned int start;
+ unsigned int end;
+} cmap_unicode_range_t;
+
+static cmap_unicode_range_t winansi_unicode_ranges[] = {
+ { 0x0020, 0x007f },
+ { 0x00a0, 0x00ff },
+ { 0x0152, 0x0153 },
+ { 0x0160, 0x0161 },
+ { 0x0178, 0x0178 },
+ { 0x017d, 0x017e },
+ { 0x0192, 0x0192 },
+ { 0x02c6, 0x02c6 },
+ { 0x02dc, 0x02dc },
+ { 0x2013, 0x2026 },
+ { 0x2030, 0x2030 },
+ { 0x2039, 0x203a },
+ { 0x20ac, 0x20ac },
+ { 0x2122, 0x2122 },
+};
+
+static cairo_status_t
+cairo_truetype_font_write_cmap_table (cairo_truetype_font_t *font,
+ unsigned long tag)
+{
+ int i;
+ unsigned int j;
+ int range_offset;
+ int num_ranges;
+ int entry_selector;
+ int length;
+
+ num_ranges = ARRAY_LENGTH (winansi_unicode_ranges);
+
+ length = 16 + (num_ranges + 1)*8;
+ for (i = 0; i < num_ranges; i++)
+ length += (winansi_unicode_ranges[i].end - winansi_unicode_ranges[i].start + 1)*2;
+
+ entry_selector = 0;
+ while ((1 << entry_selector) <= (num_ranges + 1))
+ entry_selector++;
+
+ entry_selector--;
+
+ cairo_truetype_font_write_be16 (font, 0); /* Table version */
+ cairo_truetype_font_write_be16 (font, 1); /* Num tables */
+
+ cairo_truetype_font_write_be16 (font, 3); /* Platform */
+ cairo_truetype_font_write_be16 (font, 1); /* Encoding */
+ cairo_truetype_font_write_be32 (font, 12); /* Offset to start of table */
+
+ /* Output a format 4 encoding table for the winansi encoding */
+
+ cairo_truetype_font_write_be16 (font, 4); /* Format */
+ cairo_truetype_font_write_be16 (font, length); /* Length */
+ cairo_truetype_font_write_be16 (font, 0); /* Version */
+ cairo_truetype_font_write_be16 (font, num_ranges*2 + 2); /* 2*segcount */
+ cairo_truetype_font_write_be16 (font, (1 << (entry_selector + 1))); /* searchrange */
+ cairo_truetype_font_write_be16 (font, entry_selector); /* entry selector */
+ cairo_truetype_font_write_be16 (font, num_ranges*2 + 2 - (1 << (entry_selector + 1))); /* rangeshift */
+ for (i = 0; i < num_ranges; i++)
+ cairo_truetype_font_write_be16 (font, winansi_unicode_ranges[i].end); /* end count[] */
+ cairo_truetype_font_write_be16 (font, 0xffff); /* end count[] */
+
+ cairo_truetype_font_write_be16 (font, 0); /* reserved */
+
+ for (i = 0; i < num_ranges; i++)
+ cairo_truetype_font_write_be16 (font, winansi_unicode_ranges[i].start); /* startCode[] */
+ cairo_truetype_font_write_be16 (font, 0xffff); /* startCode[] */
+
+ for (i = 0; i < num_ranges; i++)
+ cairo_truetype_font_write_be16 (font, 0x0000); /* delta[] */
+ cairo_truetype_font_write_be16 (font, 1); /* delta[] */
+
+ range_offset = num_ranges*2 + 2;
+ for (i = 0; i < num_ranges; i++) {
+ cairo_truetype_font_write_be16 (font, range_offset); /* rangeOffset[] */
+ range_offset += (winansi_unicode_ranges[i].end - winansi_unicode_ranges[i].start + 1)*2 - 2;
+ }
+ cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[] */
+
+ for (i = 0; i < num_ranges; i++) {
+ for (j = winansi_unicode_ranges[i].start; j < winansi_unicode_ranges[i].end + 1; j++) {
+ int ch = _cairo_unicode_to_winansi (j);
+ int glyph;
+
+ if (ch > 0)
+ glyph = font->scaled_font_subset->latin_to_subset_glyph_index[ch];
+ else
+ glyph = 0;
+ cairo_truetype_font_write_be16 (font, glyph);
+ }
+ }
+
+ return font->status;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_generic_table (cairo_truetype_font_t *font,
+ unsigned long tag)
+{
+ cairo_status_t status;
+ unsigned char *buffer;
+ unsigned long size;
+
+ if (font->status)
+ return font->status;
+
+ size = 0;
+ status = font->backend->load_truetype_table(font->scaled_font_subset->scaled_font,
+ tag, 0, NULL, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ tag, 0, buffer, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_remap_composite_glyph (cairo_truetype_font_t *font,
+ unsigned char *buffer,
+ unsigned long size)
+{
+ tt_glyph_data_t *glyph_data;
+ tt_composite_glyph_t *composite_glyph;
+ int num_args;
+ int has_more_components;
+ unsigned short flags;
+ unsigned short index;
+ cairo_status_t status;
+ unsigned char *end = buffer + size;
+
+ if (font->status)
+ return font->status;
+
+ glyph_data = (tt_glyph_data_t *) buffer;
+ if ((unsigned char *)(&glyph_data->data) >= end)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if ((int16_t)be16_to_cpu (glyph_data->num_contours) >= 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ composite_glyph = &glyph_data->glyph;
+ do {
+ if ((unsigned char *)(&composite_glyph->args[1]) > end)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ flags = be16_to_cpu (composite_glyph->flags);
+ has_more_components = flags & TT_MORE_COMPONENTS;
+ status = cairo_truetype_font_use_glyph (font, be16_to_cpu (composite_glyph->index), &index);
+ if (unlikely (status))
+ return status;
+
+ composite_glyph->index = cpu_to_be16 (index);
+ num_args = 1;
+ if (flags & TT_ARG_1_AND_2_ARE_WORDS)
+ num_args += 1;
+
+ if (flags & TT_WE_HAVE_A_SCALE)
+ num_args += 1;
+ else if (flags & TT_WE_HAVE_AN_X_AND_Y_SCALE)
+ num_args += 2;
+ else if (flags & TT_WE_HAVE_A_TWO_BY_TWO)
+ num_args += 4;
+
+ composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]);
+ } while (has_more_components);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font,
+ unsigned long tag)
+{
+ unsigned long start_offset, index, size, next;
+ tt_head_t header;
+ unsigned long begin, end;
+ unsigned char *buffer;
+ unsigned int i;
+ union {
+ unsigned char *bytes;
+ uint16_t *short_offsets;
+ uint32_t *long_offsets;
+ } u;
+ cairo_status_t status;
+
+ if (font->status)
+ return font->status;
+
+ size = sizeof (tt_head_t);
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_head, 0,
+ (unsigned char*) &header, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ if (be16_to_cpu (header.index_to_loc_format) == 0)
+ size = sizeof (int16_t) * (font->num_glyphs_in_face + 1);
+ else
+ size = sizeof (int32_t) * (font->num_glyphs_in_face + 1);
+
+ u.bytes = malloc (size);
+ if (unlikely (u.bytes == NULL))
+ return _cairo_truetype_font_set_error (font, CAIRO_STATUS_NO_MEMORY);
+
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_loca, 0, u.bytes, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ start_offset = _cairo_array_num_elements (&font->output);
+ for (i = 0; i < font->base.num_glyphs; i++) {
+ index = font->glyphs[i].parent_index;
+ if (be16_to_cpu (header.index_to_loc_format) == 0) {
+ begin = be16_to_cpu (u.short_offsets[index]) * 2;
+ end = be16_to_cpu (u.short_offsets[index + 1]) * 2;
+ }
+ else {
+ begin = be32_to_cpu (u.long_offsets[index]);
+ end = be32_to_cpu (u.long_offsets[index + 1]);
+ }
+
+ /* quick sanity check... */
+ if (end < begin) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto FAIL;
+ }
+
+ size = end - begin;
+ status = cairo_truetype_font_align_output (font, &next);
+ if (unlikely (status))
+ goto FAIL;
+
+ status = cairo_truetype_font_check_boundary (font, next);
+ if (unlikely (status))
+ goto FAIL;
+
+ font->glyphs[i].location = next - start_offset;
+
+ status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer);
+ if (unlikely (status))
+ goto FAIL;
+
+ if (size != 0) {
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_glyf, begin, buffer, &size);
+ if (unlikely (status))
+ goto FAIL;
+
+ status = cairo_truetype_font_remap_composite_glyph (font, buffer, size);
+ if (unlikely (status))
+ goto FAIL;
+ }
+ }
+
+ status = cairo_truetype_font_align_output (font, &next);
+ if (unlikely (status))
+ goto FAIL;
+
+ font->glyphs[i].location = next - start_offset;
+
+ status = font->status;
+FAIL:
+ free (u.bytes);
+
+ return _cairo_truetype_font_set_error (font, status);
+}
+
+static cairo_status_t
+cairo_truetype_font_write_head_table (cairo_truetype_font_t *font,
+ unsigned long tag)
+{
+ unsigned char *buffer;
+ unsigned long size;
+ cairo_status_t status;
+
+ if (font->status)
+ return font->status;
+
+ size = 0;
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ tag, 0, NULL, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ font->checksum_index = _cairo_array_num_elements (&font->output) + 8;
+ status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ tag, 0, buffer, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ /* set checkSumAdjustment to 0 for table checksum calculation */
+ *(uint32_t *)(buffer + 8) = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_hhea_table (cairo_truetype_font_t *font, unsigned long tag)
+{
+ tt_hhea_t *hhea;
+ unsigned long size;
+ cairo_status_t status;
+
+ if (font->status)
+ return font->status;
+
+ size = sizeof (tt_hhea_t);
+ status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &hhea);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ tag, 0, (unsigned char *) hhea, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ hhea->num_hmetrics = cpu_to_be16 ((uint16_t)(font->base.num_glyphs));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font,
+ unsigned long tag)
+{
+ unsigned long size;
+ unsigned long long_entry_size;
+ unsigned long short_entry_size;
+ short *p;
+ unsigned int i;
+ tt_hhea_t hhea;
+ int num_hmetrics;
+ cairo_status_t status;
+
+ if (font->status)
+ return font->status;
+
+ size = sizeof (tt_hhea_t);
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_hhea, 0,
+ (unsigned char*) &hhea, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ num_hmetrics = be16_to_cpu(hhea.num_hmetrics);
+
+ for (i = 0; i < font->base.num_glyphs; i++) {
+ long_entry_size = 2 * sizeof (int16_t);
+ short_entry_size = sizeof (int16_t);
+ status = cairo_truetype_font_allocate_write_buffer (font,
+ long_entry_size,
+ (unsigned char **) &p);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ if (font->glyphs[i].parent_index < num_hmetrics) {
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_hmtx,
+ font->glyphs[i].parent_index * long_entry_size,
+ (unsigned char *) p, &long_entry_size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+ }
+ else
+ {
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_hmtx,
+ (num_hmetrics - 1) * long_entry_size,
+ (unsigned char *) p, &short_entry_size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_hmtx,
+ num_hmetrics * long_entry_size +
+ (font->glyphs[i].parent_index - num_hmetrics) * short_entry_size,
+ (unsigned char *) (p + 1), &short_entry_size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+ }
+ font->base.widths[i] = be16_to_cpu (p[0]);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_loca_table (cairo_truetype_font_t *font,
+ unsigned long tag)
+{
+ unsigned int i;
+ tt_head_t header;
+ unsigned long size;
+ cairo_status_t status;
+
+ if (font->status)
+ return font->status;
+
+ size = sizeof(tt_head_t);
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_head, 0,
+ (unsigned char*) &header, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ if (be16_to_cpu (header.index_to_loc_format) == 0)
+ {
+ for (i = 0; i < font->base.num_glyphs + 1; i++)
+ cairo_truetype_font_write_be16 (font, font->glyphs[i].location / 2);
+ } else {
+ for (i = 0; i < font->base.num_glyphs + 1; i++)
+ cairo_truetype_font_write_be32 (font, font->glyphs[i].location);
+ }
+
+ return font->status;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_maxp_table (cairo_truetype_font_t *font,
+ unsigned long tag)
+{
+ tt_maxp_t *maxp;
+ unsigned long size;
+ cairo_status_t status;
+
+ if (font->status)
+ return font->status;
+
+ size = sizeof (tt_maxp_t);
+ status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &maxp);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ tag, 0, (unsigned char *) maxp, &size);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ maxp->num_glyphs = cpu_to_be16 (font->base.num_glyphs);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_offset_table (cairo_truetype_font_t *font)
+{
+ cairo_status_t status;
+ unsigned char *table_buffer;
+ size_t table_buffer_length;
+ unsigned short search_range, entry_selector, range_shift;
+
+ if (font->status)
+ return font->status;
+
+ search_range = 1;
+ entry_selector = 0;
+ while (search_range * 2 <= font->num_tables) {
+ search_range *= 2;
+ entry_selector++;
+ }
+ search_range *= 16;
+ range_shift = font->num_tables * 16 - search_range;
+
+ cairo_truetype_font_write_be32 (font, SFNT_VERSION);
+ cairo_truetype_font_write_be16 (font, font->num_tables);
+ cairo_truetype_font_write_be16 (font, search_range);
+ cairo_truetype_font_write_be16 (font, entry_selector);
+ cairo_truetype_font_write_be16 (font, range_shift);
+
+ /* Allocate space for the table directory. Each directory entry
+ * will be filled in by cairo_truetype_font_update_entry() after
+ * the table is written. */
+ table_buffer_length = font->num_tables * 16;
+ status = cairo_truetype_font_allocate_write_buffer (font, table_buffer_length,
+ &table_buffer);
+ if (unlikely (status))
+ return _cairo_truetype_font_set_error (font, status);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static uint32_t
+cairo_truetype_font_calculate_checksum (cairo_truetype_font_t *font,
+ unsigned long start,
+ unsigned long end)
+{
+ uint32_t *padded_end;
+ uint32_t *p;
+ uint32_t checksum;
+ char *data;
+
+ checksum = 0;
+ data = _cairo_array_index (&font->output, 0);
+ p = (uint32_t *) (data + start);
+ padded_end = (uint32_t *) (data + ((end + 3) & ~3));
+ while (p < padded_end)
+ checksum += be32_to_cpu(*p++);
+
+ return checksum;
+}
+
+static void
+cairo_truetype_font_update_entry (cairo_truetype_font_t *font,
+ int index,
+ unsigned long tag,
+ unsigned long start,
+ unsigned long end)
+{
+ uint32_t *entry;
+
+ entry = _cairo_array_index (&font->output, 12 + 16 * index);
+ entry[0] = cpu_to_be32 ((uint32_t)tag);
+ entry[1] = cpu_to_be32 (cairo_truetype_font_calculate_checksum (font, start, end));
+ entry[2] = cpu_to_be32 ((uint32_t)start);
+ entry[3] = cpu_to_be32 ((uint32_t)(end - start));
+}
+
+static cairo_status_t
+cairo_truetype_font_generate (cairo_truetype_font_t *font,
+ const char **data,
+ unsigned long *length,
+ const unsigned long **string_offsets,
+ unsigned long *num_strings)
+{
+ cairo_status_t status;
+ unsigned long start, end, next;
+ uint32_t checksum, *checksum_location;
+ int i;
+
+ if (font->status)
+ return font->status;
+
+ status = cairo_truetype_font_write_offset_table (font);
+ if (unlikely (status))
+ goto FAIL;
+
+ status = cairo_truetype_font_align_output (font, &start);
+ if (unlikely (status))
+ goto FAIL;
+
+ end = 0;
+ for (i = 0; i < font->num_tables; i++) {
+ status = font->truetype_tables[i].write (font, font->truetype_tables[i].tag);
+ if (unlikely (status))
+ goto FAIL;
+
+ end = _cairo_array_num_elements (&font->output);
+ status = cairo_truetype_font_align_output (font, &next);
+ if (unlikely (status))
+ goto FAIL;
+
+ cairo_truetype_font_update_entry (font, font->truetype_tables[i].pos,
+ font->truetype_tables[i].tag, start, end);
+ status = cairo_truetype_font_check_boundary (font, next);
+ if (unlikely (status))
+ goto FAIL;
+
+ start = next;
+ }
+
+ checksum =
+ 0xb1b0afba - cairo_truetype_font_calculate_checksum (font, 0, end);
+ checksum_location = _cairo_array_index (&font->output, font->checksum_index);
+ *checksum_location = cpu_to_be32 (checksum);
+
+ *data = _cairo_array_index (&font->output, 0);
+ *length = _cairo_array_num_elements (&font->output);
+ *num_strings = _cairo_array_num_elements (&font->string_offsets);
+ if (*num_strings != 0)
+ *string_offsets = _cairo_array_index (&font->string_offsets, 0);
+ else
+ *string_offsets = NULL;
+
+ FAIL:
+ return _cairo_truetype_font_set_error (font, status);
+}
+
+static cairo_status_t
+cairo_truetype_font_use_glyph (cairo_truetype_font_t *font,
+ unsigned short glyph,
+ unsigned short *out)
+{
+ if (glyph >= font->num_glyphs_in_face)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (font->parent_to_subset[glyph] == 0) {
+ font->parent_to_subset[glyph] = font->base.num_glyphs;
+ font->glyphs[font->base.num_glyphs].parent_index = glyph;
+ font->base.num_glyphs++;
+ }
+
+ *out = font->parent_to_subset[glyph];
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cairo_truetype_font_add_truetype_table (cairo_truetype_font_t *font,
+ unsigned long tag,
+ cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag),
+ int pos)
+{
+ font->truetype_tables[font->num_tables].tag = tag;
+ font->truetype_tables[font->num_tables].write = write;
+ font->truetype_tables[font->num_tables].pos = pos;
+ font->num_tables++;
+}
+
+/* cairo_truetype_font_create_truetype_table_list() builds the list of
+ * truetype tables to be embedded in the subsetted font. Each call to
+ * cairo_truetype_font_add_truetype_table() adds a table, the callback
+ * for generating the table, and the position in the table directory
+ * to the truetype_tables array.
+ *
+ * As we write out the glyf table we remap composite glyphs.
+ * Remapping composite glyphs will reference the sub glyphs the
+ * composite glyph is made up of. The "glyf" table callback needs to
+ * be called first so we have all the glyphs in the subset before
+ * going further.
+ *
+ * The order in which tables are added to the truetype_table array
+ * using cairo_truetype_font_add_truetype_table() specifies the order
+ * in which the callback functions will be called.
+ *
+ * The tables in the table directory must be listed in alphabetical
+ * order. The "cvt", "fpgm", and "prep" are optional tables. They
+ * will only be embedded in the subset if they exist in the source
+ * font. "cmap" is only embedded for latin fonts. The pos parameter of
+ * cairo_truetype_font_add_truetype_table() specifies the position of
+ * the table in the table directory.
+ */
+static void
+cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font)
+{
+ cairo_bool_t has_cvt = FALSE;
+ cairo_bool_t has_fpgm = FALSE;
+ cairo_bool_t has_prep = FALSE;
+ unsigned long size;
+ int pos;
+
+ size = 0;
+ if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_cvt, 0, NULL,
+ &size) == CAIRO_INT_STATUS_SUCCESS)
+ has_cvt = TRUE;
+
+ size = 0;
+ if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_fpgm, 0, NULL,
+ &size) == CAIRO_INT_STATUS_SUCCESS)
+ has_fpgm = TRUE;
+
+ size = 0;
+ if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_prep, 0, NULL,
+ &size) == CAIRO_INT_STATUS_SUCCESS)
+ has_prep = TRUE;
+
+ font->num_tables = 0;
+ pos = 0;
+ if (font->is_pdf && font->scaled_font_subset->is_latin)
+ pos++;
+ if (has_cvt)
+ pos++;
+ if (has_fpgm)
+ pos++;
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_glyf, cairo_truetype_font_write_glyf_table, pos);
+
+ pos = 0;
+ if (font->is_pdf && font->scaled_font_subset->is_latin)
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_cmap, cairo_truetype_font_write_cmap_table, pos++);
+ if (has_cvt)
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_cvt, cairo_truetype_font_write_generic_table, pos++);
+ if (has_fpgm)
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_fpgm, cairo_truetype_font_write_generic_table, pos++);
+ pos++;
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_head, cairo_truetype_font_write_head_table, pos++);
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_hhea, cairo_truetype_font_write_hhea_table, pos++);
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_hmtx, cairo_truetype_font_write_hmtx_table, pos++);
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_loca, cairo_truetype_font_write_loca_table, pos++);
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_maxp, cairo_truetype_font_write_maxp_table, pos++);
+ if (has_prep)
+ cairo_truetype_font_add_truetype_table (font, TT_TAG_prep, cairo_truetype_font_write_generic_table, pos);
+}
+
+static cairo_status_t
+cairo_truetype_subset_init_internal (cairo_truetype_subset_t *truetype_subset,
+ cairo_scaled_font_subset_t *font_subset,
+ cairo_bool_t is_pdf)
+{
+ cairo_truetype_font_t *font = NULL;
+ cairo_status_t status;
+ const char *data = NULL; /* squelch bogus compiler warning */
+ unsigned long length = 0; /* squelch bogus compiler warning */
+ unsigned long offsets_length;
+ unsigned int i;
+ const unsigned long *string_offsets = NULL;
+ unsigned long num_strings = 0;
+
+ status = _cairo_truetype_font_create (font_subset, is_pdf, &font);
+ if (unlikely (status))
+ return status;
+
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+ unsigned short parent_glyph = font->scaled_font_subset->glyphs[i];
+ status = cairo_truetype_font_use_glyph (font, parent_glyph, &parent_glyph);
+ if (unlikely (status))
+ goto fail1;
+ }
+
+ cairo_truetype_font_create_truetype_table_list (font);
+ status = cairo_truetype_font_generate (font, &data, &length,
+ &string_offsets, &num_strings);
+ if (unlikely (status))
+ goto fail1;
+
+ truetype_subset->ps_name = strdup (font->base.ps_name);
+ if (unlikely (truetype_subset->ps_name == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail1;
+ }
+
+ if (font->base.font_name != NULL) {
+ truetype_subset->family_name_utf8 = strdup (font->base.font_name);
+ if (unlikely (truetype_subset->family_name_utf8 == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+ } else {
+ truetype_subset->family_name_utf8 = NULL;
+ }
+
+ /* The widths array returned must contain only widths for the
+ * glyphs in font_subset. Any subglyphs appended after
+ * font_subset->num_glyphs are omitted. */
+ truetype_subset->widths = calloc (sizeof (double),
+ font->scaled_font_subset->num_glyphs);
+ if (unlikely (truetype_subset->widths == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail3;
+ }
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
+ truetype_subset->widths[i] = (double)font->base.widths[i]/font->base.units_per_em;
+
+ truetype_subset->x_min = (double)font->base.x_min/font->base.units_per_em;
+ truetype_subset->y_min = (double)font->base.y_min/font->base.units_per_em;
+ truetype_subset->x_max = (double)font->base.x_max/font->base.units_per_em;
+ truetype_subset->y_max = (double)font->base.y_max/font->base.units_per_em;
+ truetype_subset->ascent = (double)font->base.ascent/font->base.units_per_em;
+ truetype_subset->descent = (double)font->base.descent/font->base.units_per_em;
+
+ if (length) {
+ truetype_subset->data = malloc (length);
+ if (unlikely (truetype_subset->data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail4;
+ }
+
+ memcpy (truetype_subset->data, data, length);
+ } else
+ truetype_subset->data = NULL;
+ truetype_subset->data_length = length;
+
+ if (num_strings) {
+ offsets_length = num_strings * sizeof (unsigned long);
+ truetype_subset->string_offsets = malloc (offsets_length);
+ if (unlikely (truetype_subset->string_offsets == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail5;
+ }
+
+ memcpy (truetype_subset->string_offsets, string_offsets, offsets_length);
+ truetype_subset->num_string_offsets = num_strings;
+ } else {
+ truetype_subset->string_offsets = NULL;
+ truetype_subset->num_string_offsets = 0;
+ }
+
+ cairo_truetype_font_destroy (font);
+
+ return CAIRO_STATUS_SUCCESS;
+
+ fail5:
+ free (truetype_subset->data);
+ fail4:
+ free (truetype_subset->widths);
+ fail3:
+ free (truetype_subset->family_name_utf8);
+ fail2:
+ free (truetype_subset->ps_name);
+ fail1:
+ cairo_truetype_font_destroy (font);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_truetype_subset_init_ps (cairo_truetype_subset_t *truetype_subset,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ return cairo_truetype_subset_init_internal (truetype_subset, font_subset, FALSE);
+}
+
+cairo_status_t
+_cairo_truetype_subset_init_pdf (cairo_truetype_subset_t *truetype_subset,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ return cairo_truetype_subset_init_internal (truetype_subset, font_subset, TRUE);
+}
+
+void
+_cairo_truetype_subset_fini (cairo_truetype_subset_t *subset)
+{
+ free (subset->ps_name);
+ free (subset->family_name_utf8);
+ free (subset->widths);
+ free (subset->data);
+ free (subset->string_offsets);
+}
+
+static cairo_int_status_t
+_cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font,
+ unsigned long table_offset,
+ unsigned long index,
+ uint32_t *ucs4)
+{
+ cairo_status_t status;
+ const cairo_scaled_font_backend_t *backend;
+ tt_segment_map_t *map;
+ char buf[4];
+ unsigned int num_segments, i;
+ unsigned long size;
+ uint16_t *start_code;
+ uint16_t *end_code;
+ uint16_t *delta;
+ uint16_t *range_offset;
+ uint16_t c;
+
+ backend = scaled_font->backend;
+ size = 4;
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_cmap, table_offset,
+ (unsigned char *) &buf,
+ &size);
+ if (unlikely (status))
+ return status;
+
+ /* All table formats have the same first two words */
+ map = (tt_segment_map_t *) buf;
+ if (be16_to_cpu (map->format) != 4)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = be16_to_cpu (map->length);
+ map = malloc (size);
+ if (unlikely (map == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_cmap, table_offset,
+ (unsigned char *) map,
+ &size);
+ if (unlikely (status))
+ goto fail;
+
+ num_segments = be16_to_cpu (map->segCountX2)/2;
+
+ /* A Format 4 cmap contains 8 uint16_t numbers and 4 arrays of
+ * uint16_t each num_segments long. */
+ if (size < (8 + 4*num_segments)*sizeof(uint16_t))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ end_code = map->endCount;
+ start_code = &(end_code[num_segments + 1]);
+ delta = &(start_code[num_segments]);
+ range_offset = &(delta[num_segments]);
+
+ /* search for glyph in segments with rangeOffset=0 */
+ for (i = 0; i < num_segments; i++) {
+ c = index - be16_to_cpu (delta[i]);
+ if (range_offset[i] == 0 &&
+ c >= be16_to_cpu (start_code[i]) &&
+ c <= be16_to_cpu (end_code[i]))
+ {
+ *ucs4 = c;
+ goto found;
+ }
+ }
+
+ /* search for glyph in segments with rangeOffset=1 */
+ for (i = 0; i < num_segments; i++) {
+ if (range_offset[i] != 0) {
+ uint16_t *glyph_ids = &range_offset[i] + be16_to_cpu (range_offset[i])/2;
+ int range_size = be16_to_cpu (end_code[i]) - be16_to_cpu (start_code[i]) + 1;
+ uint16_t g_id_be = cpu_to_be16 (index);
+ int j;
+
+ if (range_size > 0) {
+ if ((char*)glyph_ids + 2*range_size > (char*)map + size)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ for (j = 0; j < range_size; j++) {
+ if (glyph_ids[j] == g_id_be) {
+ *ucs4 = be16_to_cpu (start_code[i]) + j;
+ goto found;
+ }
+ }
+ }
+ }
+ }
+
+ /* glyph not found */
+ *ucs4 = -1;
+
+found:
+ status = CAIRO_STATUS_SUCCESS;
+
+fail:
+ free (map);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font,
+ unsigned long index,
+ uint32_t *ucs4)
+{
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+ const cairo_scaled_font_backend_t *backend;
+ tt_cmap_t *cmap;
+ char buf[4];
+ int num_tables, i;
+ unsigned long size;
+
+ backend = scaled_font->backend;
+ if (!backend->load_truetype_table)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = 4;
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_cmap, 0,
+ (unsigned char *) &buf,
+ &size);
+ if (unlikely (status))
+ return status;
+
+ cmap = (tt_cmap_t *) buf;
+ num_tables = be16_to_cpu (cmap->num_tables);
+ size = 4 + num_tables*sizeof(tt_cmap_index_t);
+ cmap = _cairo_malloc_ab_plus_c (num_tables, sizeof (tt_cmap_index_t), 4);
+ if (unlikely (cmap == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_cmap, 0,
+ (unsigned char *) cmap,
+ &size);
+ if (unlikely (status))
+ goto cleanup;
+
+ /* Find a table with Unicode mapping */
+ for (i = 0; i < num_tables; i++) {
+ if (be16_to_cpu (cmap->index[i].platform) == 3 &&
+ be16_to_cpu (cmap->index[i].encoding) == 1) {
+ status = _cairo_truetype_reverse_cmap (scaled_font,
+ be32_to_cpu (cmap->index[i].offset),
+ index,
+ ucs4);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ break;
+ }
+ }
+
+cleanup:
+ free (cmap);
+
+ return status;
+}
+
+static cairo_status_t
+find_name (tt_name_t *name, int name_id, int platform, int encoding, int language, char **str_out)
+{
+ tt_name_record_t *record;
+ int i, len;
+ char *str;
+ char *p;
+ cairo_bool_t has_tag;
+ cairo_status_t status;
+
+ str = NULL;
+ for (i = 0; i < be16_to_cpu (name->num_records); i++) {
+ record = &(name->records[i]);
+ if (be16_to_cpu (record->name) == name_id &&
+ be16_to_cpu (record->platform) == platform &&
+ be16_to_cpu (record->encoding) == encoding &&
+ (language == -1 || be16_to_cpu (record->language) == language)) {
+
+ str = malloc (be16_to_cpu (record->length) + 1);
+ if (str == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ len = be16_to_cpu (record->length);
+ memcpy (str,
+ ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset),
+ len);
+ str[be16_to_cpu (record->length)] = 0;
+ break;
+ }
+ }
+ if (str == NULL) {
+ *str_out = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (platform == 3) { /* Win platform, unicode encoding */
+ /* convert to utf8 */
+ int size = 0;
+ char *utf8;
+ uint16_t *u = (uint16_t *) str;
+ int u_len = len/2;
+
+ for (i = 0; i < u_len; i++)
+ size += _cairo_ucs4_to_utf8 (be16_to_cpu(u[i]), NULL);
+
+ utf8 = malloc (size + 1);
+ if (utf8 == NULL) {
+ status =_cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail;
+ }
+ p = utf8;
+ for (i = 0; i < u_len; i++)
+ p += _cairo_ucs4_to_utf8 (be16_to_cpu(u[i]), p);
+ *p = 0;
+ free (str);
+ str = utf8;
+ } else if (platform == 1) { /* Mac platform, Mac Roman encoding */
+ /* Replace characters above 127 with underscores. We could use
+ * a lookup table to convert to unicode but since most fonts
+ * include a unicode name this is just a rarely used fallback. */
+ for (i = 0; i < len; i++) {
+ if ((unsigned char)str[i] > 127)
+ str[i] = '_';
+ }
+ }
+
+ /* If font name is prefixed with a PDF subset tag, strip it off. */
+ p = str;
+ len = strlen (str);
+ has_tag = FALSE;
+ if (len > 7 && p[6] == '+') {
+ has_tag = TRUE;
+ for (i = 0; i < 6; i++) {
+ if (p[i] < 'A' || p[i] > 'Z') {
+ has_tag = FALSE;
+ break;
+ }
+ }
+ }
+ if (has_tag) {
+ p = malloc (len - 6);
+ if (unlikely (p == NULL)) {
+ status =_cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail;
+ }
+ memcpy (p, str + 7, len - 7);
+ p[len-7] = 0;
+ free (str);
+ str = p;
+ }
+
+ *str_out = str;
+
+ return CAIRO_STATUS_SUCCESS;
+
+ fail:
+ free (str);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font,
+ char **ps_name_out,
+ char **font_name_out)
+{
+ cairo_status_t status;
+ const cairo_scaled_font_backend_t *backend;
+ tt_name_t *name;
+ unsigned long size;
+ char *ps_name = NULL;
+ char *family_name = NULL;
+
+ backend = scaled_font->backend;
+ if (!backend->load_truetype_table)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = 0;
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_name, 0,
+ NULL,
+ &size);
+ if (status)
+ return status;
+
+ name = malloc (size);
+ if (name == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_name, 0,
+ (unsigned char *) name,
+ &size);
+ if (status)
+ goto fail;
+
+ /* Find PS Name (name_id = 6). OT spec says PS name must be one of
+ * the following two encodings */
+ status = find_name (name, 6, 3, 1, 0x409, &ps_name); /* win, unicode, english-us */
+ if (unlikely(status))
+ goto fail;
+
+ if (!ps_name) {
+ status = find_name (name, 6, 1, 0, 0, &ps_name); /* mac, roman, english */
+ if (unlikely(status))
+ goto fail;
+ }
+
+ /* Find Family name (name_id = 1) */
+ status = find_name (name, 1, 3, 1, 0x409, &family_name); /* win, unicode, english-us */
+ if (unlikely(status))
+ goto fail;
+
+ if (!family_name) {
+ status = find_name (name, 1, 3, 0, 0x409, &family_name); /* win, symbol, english-us */
+ if (unlikely(status))
+ goto fail;
+ }
+
+ if (!family_name) {
+ status = find_name (name, 1, 1, 0, 0, &family_name); /* mac, roman, english */
+ if (unlikely(status))
+ goto fail;
+ }
+
+ if (!family_name) {
+ status = find_name (name, 1, 3, 1, -1, &family_name); /* win, unicode, any language */
+ if (unlikely(status))
+ goto fail;
+ }
+
+ free (name);
+
+ status = _cairo_escape_ps_name (&ps_name);
+ if (unlikely(status))
+ goto fail;
+
+ *ps_name_out = ps_name;
+ *font_name_out = family_name;
+
+ return CAIRO_STATUS_SUCCESS;
+
+fail:
+ free (name);
+ free (ps_name);
+ free (family_name);
+ *ps_name_out = NULL;
+ *font_name_out = NULL;
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_truetype_get_style (cairo_scaled_font_t *scaled_font,
+ int *weight,
+ cairo_bool_t *bold,
+ cairo_bool_t *italic)
+{
+ cairo_status_t status;
+ const cairo_scaled_font_backend_t *backend;
+ tt_os2_t os2;
+ unsigned long size;
+ uint16_t selection;
+
+ backend = scaled_font->backend;
+ if (!backend->load_truetype_table)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = 0;
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_OS2, 0,
+ NULL,
+ &size);
+ if (status)
+ return status;
+
+ if (size < sizeof(os2))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = sizeof (os2);
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_OS2, 0,
+ (unsigned char *) &os2,
+ &size);
+ if (status)
+ return status;
+
+ *weight = be16_to_cpu (os2.usWeightClass);
+ selection = be16_to_cpu (os2.fsSelection);
+ *bold = (selection & TT_FS_SELECTION_BOLD) ? TRUE : FALSE;
+ *italic = (selection & TT_FS_SELECTION_ITALIC) ? TRUE : FALSE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/src/cairo-type1-subset.c b/src/cairo-type1-subset.c
index 99830b4..3b2cc0a 100644
--- a/src/cairo-type1-subset.c
+++ b/src/cairo-type1-subset.c
@@ -407,6 +407,7 @@ cairo_type1_font_subset_get_fontname (cairo_type1_font_subset_t *font)
const char *start, *end, *segment_end;
char *s;
int i;
+ cairo_status_t status;
segment_end = font->header_segment + font->header_segment_size;
start = find_token (font->header_segment, segment_end, "/FontName");
@@ -447,13 +448,9 @@ cairo_type1_font_subset_get_fontname (cairo_type1_font_subset_t *font)
if (unlikely (font->base.base_font == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- s = font->base.base_font;
- while (*s && !is_ps_delimiter(*s))
- s++;
-
- *s = 0;
+ status = _cairo_escape_ps_name (&font->base.base_font);
- return CAIRO_STATUS_SUCCESS;
+ return status;
}
static cairo_status_t