/* 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., 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 Red Hat, Inc. * * Contributor(s): * Kristian Høgsberg * Adrian Johnson */ /* * Useful links: * http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html * http://www.microsoft.com/typography/specs/default.htm */ #define _BSD_SOURCE /* for snprintf(), strdup() */ #include "cairoint.h" #include "cairo-error-private.h" #if CAIRO_HAS_FONT_SUBSET #include "cairo-scaled-font-subsets-private.h" #include "cairo-truetype-subset-private.h" typedef struct subset_glyph subset_glyph_t; struct subset_glyph { int parent_index; unsigned long location; }; typedef struct _cairo_truetype_font cairo_truetype_font_t; typedef struct table table_t; struct table { unsigned long tag; cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag); int pos; /* position in the font directory */ }; struct _cairo_truetype_font { cairo_scaled_font_subset_t *scaled_font_subset; table_t truetype_tables[10]; int num_tables; struct { char *font_name; char *ps_name; unsigned int num_glyphs; int *widths; long x_min, y_min, x_max, y_max; long ascent, descent; int units_per_em; } base; subset_glyph_t *glyphs; const cairo_scaled_font_backend_t *backend; int num_glyphs_in_face; int checksum_index; cairo_array_t output; cairo_array_t string_offsets; unsigned long last_offset; unsigned long last_boundary; int *parent_to_subset; cairo_status_t status; }; /* * Test that the structs we define for TrueType tables have the * correct size, ie. they are not padded. */ #define check(T, S) COMPILE_TIME_ASSERT (sizeof (T) == (S)) check (tt_head_t, 54); check (tt_hhea_t, 36); check (tt_maxp_t, 32); check (tt_name_record_t, 12); check (tt_name_t, 18); check (tt_name_t, 18); check (tt_composite_glyph_t, 16); check (tt_glyph_data_t, 26); #undef check static cairo_status_t cairo_truetype_font_use_glyph (cairo_truetype_font_t *font, unsigned short glyph, unsigned short *out); #define SFNT_VERSION 0x00010000 #define SFNT_STRING_MAX_LENGTH 65535 static cairo_status_t _cairo_truetype_font_set_error (cairo_truetype_font_t *font, cairo_status_t status) { if (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED) return status; _cairo_status_set_error (&font->status, status); return _cairo_error (status); } static cairo_status_t _cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset, cairo_truetype_font_t **font_return) { cairo_status_t status; cairo_truetype_font_t *font; const cairo_scaled_font_backend_t *backend; tt_head_t head; tt_hhea_t hhea; tt_maxp_t maxp; unsigned long size; backend = scaled_font_subset->scaled_font->backend; if (!backend->load_truetype_table) return CAIRO_INT_STATUS_UNSUPPORTED; /* FIXME: We should either support subsetting vertical fonts, or fail on * vertical. Currently font_options_t doesn't have vertical flag, but * it should be added in the future. For now, the freetype backend * returns UNSUPPORTED in load_truetype_table if the font is vertical. * * if (cairo_font_options_get_vertical_layout (scaled_font_subset->scaled_font)) * return CAIRO_INT_STATUS_UNSUPPORTED; */ size = sizeof (tt_head_t); status = backend->load_truetype_table (scaled_font_subset->scaled_font, TT_TAG_head, 0, (unsigned char *) &head, &size); if (unlikely (status)) return status; size = sizeof (tt_maxp_t); status = backend->load_truetype_table (scaled_font_subset->scaled_font, TT_TAG_maxp, 0, (unsigned char *) &maxp, &size); if (unlikely (status)) return status; size = sizeof (tt_hhea_t); status = backend->load_truetype_table (scaled_font_subset->scaled_font, TT_TAG_hhea, 0, (unsigned char *) &hhea, &size); if (unlikely (status)) return status; font = malloc (sizeof (cairo_truetype_font_t)); if (unlikely (font == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); font->backend = backend; font->num_glyphs_in_face = be16_to_cpu (maxp.num_glyphs); font->scaled_font_subset = scaled_font_subset; font->last_offset = 0; font->last_boundary = 0; _cairo_array_init (&font->output, sizeof (char)); status = _cairo_array_grow_by (&font->output, 4096); if (unlikely (status)) goto fail1; font->glyphs = calloc (font->num_glyphs_in_face + 1, sizeof (subset_glyph_t)); if (unlikely (font->glyphs == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } font->parent_to_subset = calloc (font->num_glyphs_in_face, sizeof (int)); if (unlikely (font->parent_to_subset == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } font->base.num_glyphs = 0; font->base.x_min = (int16_t) be16_to_cpu (head.x_min); font->base.y_min = (int16_t) be16_to_cpu (head.y_min); font->base.x_max = (int16_t) be16_to_cpu (head.x_max); font->base.y_max = (int16_t) be16_to_cpu (head.y_max); font->base.ascent = (int16_t) be16_to_cpu (hhea.ascender); font->base.descent = (int16_t) be16_to_cpu (hhea.descender); font->base.units_per_em = (int16_t) be16_to_cpu (head.units_per_em); if (font->base.units_per_em == 0) font->base.units_per_em = 2048; font->base.ps_name = NULL; font->base.font_name = NULL; status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font, &font->base.ps_name, &font->base.font_name); if (_cairo_status_is_error (status)) goto fail3; /* If the PS name is not found, create a CairoFont-x-y name. */ if (font->base.ps_name == NULL) { font->base.ps_name = malloc (30); if (unlikely (font->base.ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } snprintf(font->base.ps_name, 30, "CairoFont-%u-%u", scaled_font_subset->font_id, scaled_font_subset->subset_id); } font->base.widths = calloc (font->num_glyphs_in_face, sizeof (int)); if (unlikely (font->base.widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; } _cairo_array_init (&font->string_offsets, sizeof (unsigned long)); status = _cairo_array_grow_by (&font->string_offsets, 10); if (unlikely (status)) goto fail5; font->status = CAIRO_STATUS_SUCCESS; *font_return = font; return CAIRO_STATUS_SUCCESS; fail5: _cairo_array_fini (&font->string_offsets); free (font->base.widths); fail4: free (font->base.ps_name); fail3: free (font->parent_to_subset); if (font->base.font_name) free (font->base.font_name); fail2: free (font->glyphs); fail1: _cairo_array_fini (&font->output); free (font); return status; } static void cairo_truetype_font_destroy (cairo_truetype_font_t *font) { _cairo_array_fini (&font->string_offsets); free (font->base.widths); free (font->base.ps_name); if (font->base.font_name) free (font->base.font_name); free (font->parent_to_subset); free (font->glyphs); _cairo_array_fini (&font->output); free (font); } static cairo_status_t cairo_truetype_font_allocate_write_buffer (cairo_truetype_font_t *font, size_t length, unsigned char **buffer) { cairo_status_t status; if (font->status) return font->status; status = _cairo_array_allocate (&font->output, length, (void **) buffer); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); return CAIRO_STATUS_SUCCESS; } static void cairo_truetype_font_write (cairo_truetype_font_t *font, const void *data, size_t length) { cairo_status_t status; if (font->status) return; status = _cairo_array_append_multiple (&font->output, data, length); if (unlikely (status)) status = _cairo_truetype_font_set_error (font, status); } static void cairo_truetype_font_write_be16 (cairo_truetype_font_t *font, uint16_t value) { uint16_t be16_value; if (font->status) return; be16_value = cpu_to_be16 (value); cairo_truetype_font_write (font, &be16_value, sizeof be16_value); } static void cairo_truetype_font_write_be32 (cairo_truetype_font_t *font, uint32_t value) { uint32_t be32_value; if (font->status) return; be32_value = cpu_to_be32 (value); cairo_truetype_font_write (font, &be32_value, sizeof be32_value); } static cairo_status_t cairo_truetype_font_align_output (cairo_truetype_font_t *font, unsigned long *aligned) { int length, pad; unsigned char *padding; length = _cairo_array_num_elements (&font->output); *aligned = (length + 3) & ~3; pad = *aligned - length; if (pad) { cairo_status_t status; status = cairo_truetype_font_allocate_write_buffer (font, pad, &padding); if (unlikely (status)) return status; memset (padding, 0, pad); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_check_boundary (cairo_truetype_font_t *font, unsigned long boundary) { cairo_status_t status; if (font->status) return font->status; if (boundary - font->last_offset > SFNT_STRING_MAX_LENGTH) { status = _cairo_array_append (&font->string_offsets, &font->last_boundary); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); font->last_offset = font->last_boundary; } font->last_boundary = boundary; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_write_cmap_table (cairo_truetype_font_t *font, unsigned long tag) { unsigned int i; cairo_truetype_font_write_be16 (font, 0); /* Table version */ cairo_truetype_font_write_be16 (font, 2); /* Num tables */ cairo_truetype_font_write_be16 (font, 3); /* Platform */ cairo_truetype_font_write_be16 (font, 0); /* Encoding */ cairo_truetype_font_write_be32 (font, 20); /* Offset to start of table */ cairo_truetype_font_write_be16 (font, 1); /* Platform */ cairo_truetype_font_write_be16 (font, 0); /* Encoding */ cairo_truetype_font_write_be32 (font, 52); /* Offset to start of table */ /* Output a format 4 encoding table. */ cairo_truetype_font_write_be16 (font, 4); /* Format */ cairo_truetype_font_write_be16 (font, 32); /* Length */ cairo_truetype_font_write_be16 (font, 0); /* Version */ cairo_truetype_font_write_be16 (font, 4); /* 2*segcount */ cairo_truetype_font_write_be16 (font, 4); /* searchrange */ cairo_truetype_font_write_be16 (font, 1); /* entry selector */ cairo_truetype_font_write_be16 (font, 0); /* rangeshift */ cairo_truetype_font_write_be16 (font, 0xf000 + font->base.num_glyphs - 1); /* end count[0] */ cairo_truetype_font_write_be16 (font, 0xffff); /* end count[1] */ cairo_truetype_font_write_be16 (font, 0); /* reserved */ cairo_truetype_font_write_be16 (font, 0xf000); /* startCode[0] */ cairo_truetype_font_write_be16 (font, 0xffff); /* startCode[1] */ cairo_truetype_font_write_be16 (font, 0x1000); /* delta[0] */ cairo_truetype_font_write_be16 (font, 1); /* delta[1] */ cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[0] */ cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[1] */ /* Output a format 6 encoding table. */ cairo_truetype_font_write_be16 (font, 6); cairo_truetype_font_write_be16 (font, 10 + 2 * font->base.num_glyphs); cairo_truetype_font_write_be16 (font, 0); cairo_truetype_font_write_be16 (font, 0); /* First character */ cairo_truetype_font_write_be16 (font, font->base.num_glyphs); for (i = 0; i < font->base.num_glyphs; i++) cairo_truetype_font_write_be16 (font, i); return font->status; } static cairo_status_t cairo_truetype_font_write_generic_table (cairo_truetype_font_t *font, unsigned long tag) { cairo_status_t status; unsigned char *buffer; unsigned long size; if (font->status) return font->status; size = 0; status = font->backend->load_truetype_table(font->scaled_font_subset->scaled_font, tag, 0, NULL, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, buffer, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_remap_composite_glyph (cairo_truetype_font_t *font, unsigned char *buffer, unsigned long size) { tt_glyph_data_t *glyph_data; tt_composite_glyph_t *composite_glyph; int num_args; int has_more_components; unsigned short flags; unsigned short index; cairo_status_t status; unsigned char *end = buffer + size; if (font->status) return font->status; glyph_data = (tt_glyph_data_t *) buffer; if ((unsigned char *)(&glyph_data->data) >= end) return CAIRO_INT_STATUS_UNSUPPORTED; if ((int16_t)be16_to_cpu (glyph_data->num_contours) >= 0) return CAIRO_STATUS_SUCCESS; composite_glyph = &glyph_data->glyph; do { if ((unsigned char *)(&composite_glyph->args[1]) > end) return CAIRO_INT_STATUS_UNSUPPORTED; flags = be16_to_cpu (composite_glyph->flags); has_more_components = flags & TT_MORE_COMPONENTS; status = cairo_truetype_font_use_glyph (font, be16_to_cpu (composite_glyph->index), &index); if (unlikely (status)) return status; composite_glyph->index = cpu_to_be16 (index); num_args = 1; if (flags & TT_ARG_1_AND_2_ARE_WORDS) num_args += 1; if (flags & TT_WE_HAVE_A_SCALE) num_args += 1; else if (flags & TT_WE_HAVE_AN_X_AND_Y_SCALE) num_args += 2; else if (flags & TT_WE_HAVE_A_TWO_BY_TWO) num_args += 4; composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]); } while (has_more_components); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font, unsigned long tag) { unsigned long start_offset, index, size, next; tt_head_t header; unsigned long begin, end; unsigned char *buffer; unsigned int i; union { unsigned char *bytes; uint16_t *short_offsets; uint32_t *long_offsets; } u; cairo_status_t status; if (font->status) return font->status; size = sizeof (tt_head_t); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_head, 0, (unsigned char*) &header, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); if (be16_to_cpu (header.index_to_loc_format) == 0) size = sizeof (int16_t) * (font->num_glyphs_in_face + 1); else size = sizeof (int32_t) * (font->num_glyphs_in_face + 1); u.bytes = malloc (size); if (unlikely (u.bytes == NULL)) return _cairo_truetype_font_set_error (font, CAIRO_STATUS_NO_MEMORY); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_loca, 0, u.bytes, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); start_offset = _cairo_array_num_elements (&font->output); for (i = 0; i < font->base.num_glyphs; i++) { index = font->glyphs[i].parent_index; if (be16_to_cpu (header.index_to_loc_format) == 0) { begin = be16_to_cpu (u.short_offsets[index]) * 2; end = be16_to_cpu (u.short_offsets[index + 1]) * 2; } else { begin = be32_to_cpu (u.long_offsets[index]); end = be32_to_cpu (u.long_offsets[index + 1]); } /* quick sanity check... */ if (end < begin) { status = CAIRO_INT_STATUS_UNSUPPORTED; goto FAIL; } size = end - begin; status = cairo_truetype_font_align_output (font, &next); if (unlikely (status)) goto FAIL; status = cairo_truetype_font_check_boundary (font, next); if (unlikely (status)) goto FAIL; font->glyphs[i].location = next - start_offset; status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); if (unlikely (status)) goto FAIL; if (size != 0) { status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_glyf, begin, buffer, &size); if (unlikely (status)) goto FAIL; status = cairo_truetype_font_remap_composite_glyph (font, buffer, size); if (unlikely (status)) goto FAIL; } } status = cairo_truetype_font_align_output (font, &next); if (unlikely (status)) goto FAIL; font->glyphs[i].location = next - start_offset; status = font->status; FAIL: free (u.bytes); return _cairo_truetype_font_set_error (font, status); } static cairo_status_t cairo_truetype_font_write_head_table (cairo_truetype_font_t *font, unsigned long tag) { unsigned char *buffer; unsigned long size; cairo_status_t status; if (font->status) return font->status; size = 0; status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, NULL, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); font->checksum_index = _cairo_array_num_elements (&font->output) + 8; status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, buffer, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); /* set checkSumAdjustment to 0 for table checksum calculation */ *(uint32_t *)(buffer + 8) = 0; return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_write_hhea_table (cairo_truetype_font_t *font, unsigned long tag) { tt_hhea_t *hhea; unsigned long size; cairo_status_t status; if (font->status) return font->status; size = sizeof (tt_hhea_t); status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &hhea); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, (unsigned char *) hhea, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); hhea->num_hmetrics = cpu_to_be16 ((uint16_t)(font->base.num_glyphs)); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font, unsigned long tag) { unsigned long size; unsigned long long_entry_size; unsigned long short_entry_size; short *p; unsigned int i; tt_hhea_t hhea; int num_hmetrics; cairo_status_t status; if (font->status) return font->status; size = sizeof (tt_hhea_t); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hhea, 0, (unsigned char*) &hhea, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); num_hmetrics = be16_to_cpu(hhea.num_hmetrics); for (i = 0; i < font->base.num_glyphs; i++) { long_entry_size = 2 * sizeof (int16_t); short_entry_size = sizeof (int16_t); status = cairo_truetype_font_allocate_write_buffer (font, long_entry_size, (unsigned char **) &p); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); if (font->glyphs[i].parent_index < num_hmetrics) { status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, font->glyphs[i].parent_index * long_entry_size, (unsigned char *) p, &long_entry_size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); } else { status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, (num_hmetrics - 1) * long_entry_size, (unsigned char *) p, &short_entry_size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_hmtx, num_hmetrics * long_entry_size + (font->glyphs[i].parent_index - num_hmetrics) * short_entry_size, (unsigned char *) (p + 1), &short_entry_size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); } font->base.widths[i] = be16_to_cpu (p[0]); } return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_write_loca_table (cairo_truetype_font_t *font, unsigned long tag) { unsigned int i; tt_head_t header; unsigned long size; cairo_status_t status; if (font->status) return font->status; size = sizeof(tt_head_t); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_head, 0, (unsigned char*) &header, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); if (be16_to_cpu (header.index_to_loc_format) == 0) { for (i = 0; i < font->base.num_glyphs + 1; i++) cairo_truetype_font_write_be16 (font, font->glyphs[i].location / 2); } else { for (i = 0; i < font->base.num_glyphs + 1; i++) cairo_truetype_font_write_be32 (font, font->glyphs[i].location); } return font->status; } static cairo_status_t cairo_truetype_font_write_maxp_table (cairo_truetype_font_t *font, unsigned long tag) { tt_maxp_t *maxp; unsigned long size; cairo_status_t status; if (font->status) return font->status; size = sizeof (tt_maxp_t); status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &maxp); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, tag, 0, (unsigned char *) maxp, &size); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); maxp->num_glyphs = cpu_to_be16 (font->base.num_glyphs); return CAIRO_STATUS_SUCCESS; } static cairo_status_t cairo_truetype_font_write_offset_table (cairo_truetype_font_t *font) { cairo_status_t status; unsigned char *table_buffer; size_t table_buffer_length; unsigned short search_range, entry_selector, range_shift; if (font->status) return font->status; search_range = 1; entry_selector = 0; while (search_range * 2 <= font->num_tables) { search_range *= 2; entry_selector++; } search_range *= 16; range_shift = font->num_tables * 16 - search_range; cairo_truetype_font_write_be32 (font, SFNT_VERSION); cairo_truetype_font_write_be16 (font, font->num_tables); cairo_truetype_font_write_be16 (font, search_range); cairo_truetype_font_write_be16 (font, entry_selector); cairo_truetype_font_write_be16 (font, range_shift); /* Allocate space for the table directory. Each directory entry * will be filled in by cairo_truetype_font_update_entry() after * the table is written. */ table_buffer_length = font->num_tables * 16; status = cairo_truetype_font_allocate_write_buffer (font, table_buffer_length, &table_buffer); if (unlikely (status)) return _cairo_truetype_font_set_error (font, status); return CAIRO_STATUS_SUCCESS; } static uint32_t cairo_truetype_font_calculate_checksum (cairo_truetype_font_t *font, unsigned long start, unsigned long end) { uint32_t *padded_end; uint32_t *p; uint32_t checksum; char *data; checksum = 0; data = _cairo_array_index (&font->output, 0); p = (uint32_t *) (data + start); padded_end = (uint32_t *) (data + ((end + 3) & ~3)); while (p < padded_end) checksum += be32_to_cpu(*p++); return checksum; } static void cairo_truetype_font_update_entry (cairo_truetype_font_t *font, int index, unsigned long tag, unsigned long start, unsigned long end) { uint32_t *entry; entry = _cairo_array_index (&font->output, 12 + 16 * index); entry[0] = cpu_to_be32 ((uint32_t)tag); entry[1] = cpu_to_be32 (cairo_truetype_font_calculate_checksum (font, start, end)); entry[2] = cpu_to_be32 ((uint32_t)start); entry[3] = cpu_to_be32 ((uint32_t)(end - start)); } static cairo_status_t cairo_truetype_font_generate (cairo_truetype_font_t *font, const char **data, unsigned long *length, const unsigned long **string_offsets, unsigned long *num_strings) { cairo_status_t status; unsigned long start, end, next; uint32_t checksum, *checksum_location; int i; if (font->status) return font->status; status = cairo_truetype_font_write_offset_table (font); if (unlikely (status)) goto FAIL; status = cairo_truetype_font_align_output (font, &start); if (unlikely (status)) goto FAIL; end = 0; for (i = 0; i < font->num_tables; i++) { status = font->truetype_tables[i].write (font, font->truetype_tables[i].tag); if (unlikely (status)) goto FAIL; end = _cairo_array_num_elements (&font->output); status = cairo_truetype_font_align_output (font, &next); if (unlikely (status)) goto FAIL; cairo_truetype_font_update_entry (font, font->truetype_tables[i].pos, font->truetype_tables[i].tag, start, end); status = cairo_truetype_font_check_boundary (font, next); if (unlikely (status)) goto FAIL; start = next; } checksum = 0xb1b0afba - cairo_truetype_font_calculate_checksum (font, 0, end); checksum_location = _cairo_array_index (&font->output, font->checksum_index); *checksum_location = cpu_to_be32 (checksum); *data = _cairo_array_index (&font->output, 0); *length = _cairo_array_num_elements (&font->output); *num_strings = _cairo_array_num_elements (&font->string_offsets); if (*num_strings != 0) *string_offsets = _cairo_array_index (&font->string_offsets, 0); else *string_offsets = NULL; FAIL: return _cairo_truetype_font_set_error (font, status); } static cairo_status_t cairo_truetype_font_use_glyph (cairo_truetype_font_t *font, unsigned short glyph, unsigned short *out) { if (glyph >= font->num_glyphs_in_face) return CAIRO_INT_STATUS_UNSUPPORTED; if (font->parent_to_subset[glyph] == 0) { font->parent_to_subset[glyph] = font->base.num_glyphs; font->glyphs[font->base.num_glyphs].parent_index = glyph; font->base.num_glyphs++; } *out = font->parent_to_subset[glyph]; return CAIRO_STATUS_SUCCESS; } static void cairo_truetype_font_add_truetype_table (cairo_truetype_font_t *font, unsigned long tag, cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag), int pos) { font->truetype_tables[font->num_tables].tag = tag; font->truetype_tables[font->num_tables].write = write; font->truetype_tables[font->num_tables].pos = pos; font->num_tables++; } /* cairo_truetype_font_create_truetype_table_list() builds the list of * truetype tables to be embedded in the subsetted font. Each call to * cairo_truetype_font_add_truetype_table() adds a table, the callback * for generating the table, and the position in the table directory * to the truetype_tables array. * * As we write out the glyf table we remap composite glyphs. * Remapping composite glyphs will reference the sub glyphs the * composite glyph is made up of. The "glyf" table callback needs to * be called first so we have all the glyphs in the subset before * going further. * * The order in which tables are added to the truetype_table array * using cairo_truetype_font_add_truetype_table() specifies the order * in which the callback functions will be called. * * The tables in the table directory must be listed in alphabetical * order. The "cvt", "fpgm", and "prep" are optional tables. They * will only be embedded in the subset if they exist in the source * font. The pos parameter of cairo_truetype_font_add_truetype_table() * specifies the position of the table in the table directory. */ static void cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font) { cairo_bool_t has_cvt = FALSE; cairo_bool_t has_fpgm = FALSE; cairo_bool_t has_prep = FALSE; unsigned long size; int pos; size = 0; if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_cvt, 0, NULL, &size) == CAIRO_STATUS_SUCCESS) has_cvt = TRUE; size = 0; if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_fpgm, 0, NULL, &size) == CAIRO_STATUS_SUCCESS) has_fpgm = TRUE; size = 0; if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, TT_TAG_prep, 0, NULL, &size) == CAIRO_STATUS_SUCCESS) has_prep = TRUE; font->num_tables = 0; pos = 1; if (has_cvt) pos++; if (has_fpgm) pos++; cairo_truetype_font_add_truetype_table (font, TT_TAG_glyf, cairo_truetype_font_write_glyf_table, pos); pos = 0; cairo_truetype_font_add_truetype_table (font, TT_TAG_cmap, cairo_truetype_font_write_cmap_table, pos++); if (has_cvt) cairo_truetype_font_add_truetype_table (font, TT_TAG_cvt, cairo_truetype_font_write_generic_table, pos++); if (has_fpgm) cairo_truetype_font_add_truetype_table (font, TT_TAG_fpgm, cairo_truetype_font_write_generic_table, pos++); pos++; cairo_truetype_font_add_truetype_table (font, TT_TAG_head, cairo_truetype_font_write_head_table, pos++); cairo_truetype_font_add_truetype_table (font, TT_TAG_hhea, cairo_truetype_font_write_hhea_table, pos++); cairo_truetype_font_add_truetype_table (font, TT_TAG_hmtx, cairo_truetype_font_write_hmtx_table, pos++); cairo_truetype_font_add_truetype_table (font, TT_TAG_loca, cairo_truetype_font_write_loca_table, pos++); cairo_truetype_font_add_truetype_table (font, TT_TAG_maxp, cairo_truetype_font_write_maxp_table, pos++); if (has_prep) cairo_truetype_font_add_truetype_table (font, TT_TAG_prep, cairo_truetype_font_write_generic_table, pos); } cairo_status_t _cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset, cairo_scaled_font_subset_t *font_subset) { cairo_truetype_font_t *font = NULL; cairo_status_t status; const char *data = NULL; /* squelch bogus compiler warning */ unsigned long length = 0; /* squelch bogus compiler warning */ unsigned long offsets_length; unsigned int i; const unsigned long *string_offsets = NULL; unsigned long num_strings = 0; status = _cairo_truetype_font_create (font_subset, &font); if (unlikely (status)) return status; for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { unsigned short parent_glyph = font->scaled_font_subset->glyphs[i]; status = cairo_truetype_font_use_glyph (font, parent_glyph, &parent_glyph); if (unlikely (status)) goto fail1; } cairo_truetype_font_create_truetype_table_list (font); status = cairo_truetype_font_generate (font, &data, &length, &string_offsets, &num_strings); if (unlikely (status)) goto fail1; truetype_subset->ps_name = strdup (font->base.ps_name); if (unlikely (truetype_subset->ps_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail1; } if (font->base.font_name != NULL) { truetype_subset->font_name = strdup (font->base.font_name); if (unlikely (truetype_subset->font_name == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail2; } } else { truetype_subset->font_name = NULL; } /* The widths array returned must contain only widths for the * glyphs in font_subset. Any subglyphs appended after * font_subset->num_glyphs are omitted. */ truetype_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); if (unlikely (truetype_subset->widths == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail3; } for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) truetype_subset->widths[i] = (double)font->base.widths[i]/font->base.units_per_em; truetype_subset->x_min = (double)font->base.x_min/font->base.units_per_em; truetype_subset->y_min = (double)font->base.y_min/font->base.units_per_em; truetype_subset->x_max = (double)font->base.x_max/font->base.units_per_em; truetype_subset->y_max = (double)font->base.y_max/font->base.units_per_em; truetype_subset->ascent = (double)font->base.ascent/font->base.units_per_em; truetype_subset->descent = (double)font->base.descent/font->base.units_per_em; if (length) { truetype_subset->data = malloc (length); if (unlikely (truetype_subset->data == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail4; } memcpy (truetype_subset->data, data, length); } else truetype_subset->data = NULL; truetype_subset->data_length = length; if (num_strings) { offsets_length = num_strings * sizeof (unsigned long); truetype_subset->string_offsets = malloc (offsets_length); if (unlikely (truetype_subset->string_offsets == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail5; } memcpy (truetype_subset->string_offsets, string_offsets, offsets_length); truetype_subset->num_string_offsets = num_strings; } else { truetype_subset->string_offsets = NULL; truetype_subset->num_string_offsets = 0; } cairo_truetype_font_destroy (font); return CAIRO_STATUS_SUCCESS; fail5: free (truetype_subset->data); fail4: free (truetype_subset->widths); fail3: if (truetype_subset->font_name) free (truetype_subset->font_name); fail2: free (truetype_subset->ps_name); fail1: cairo_truetype_font_destroy (font); return status; } void _cairo_truetype_subset_fini (cairo_truetype_subset_t *subset) { free (subset->ps_name); if (subset->font_name) free (subset->font_name); free (subset->widths); free (subset->data); free (subset->string_offsets); } static cairo_int_status_t _cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, unsigned long table_offset, unsigned long index, uint32_t *ucs4) { cairo_status_t status; const cairo_scaled_font_backend_t *backend; tt_segment_map_t *map; char buf[4]; unsigned int num_segments, i; unsigned long size; uint16_t *start_code; uint16_t *end_code; uint16_t *delta; uint16_t *range_offset; uint16_t *glyph_array; uint16_t c; backend = scaled_font->backend; size = 4; status = backend->load_truetype_table (scaled_font, TT_TAG_cmap, table_offset, (unsigned char *) &buf, &size); if (unlikely (status)) return status; /* All table formats have the same first two words */ map = (tt_segment_map_t *) buf; if (be16_to_cpu (map->format) != 4) return CAIRO_INT_STATUS_UNSUPPORTED; size = be16_to_cpu (map->length); map = malloc (size); if (unlikely (map == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = backend->load_truetype_table (scaled_font, TT_TAG_cmap, table_offset, (unsigned char *) map, &size); if (unlikely (status)) goto fail; num_segments = be16_to_cpu (map->segCountX2)/2; /* A Format 4 cmap contains 8 uint16_t numbers and 4 arrays of * uint16_t each num_segments long. */ if (size < (8 + 4*num_segments)*sizeof(uint16_t)) return CAIRO_INT_STATUS_UNSUPPORTED; end_code = map->endCount; start_code = &(end_code[num_segments + 1]); delta = &(start_code[num_segments]); range_offset = &(delta[num_segments]); glyph_array = &(range_offset[num_segments]); /* search for glyph in segments with rangeOffset=0 */ for (i = 0; i < num_segments; i++) { c = index - be16_to_cpu (delta[i]); if (range_offset[i] == 0 && c >= be16_to_cpu (start_code[i]) && c <= be16_to_cpu (end_code[i])) { *ucs4 = c; goto found; } } /* search for glyph in segments with rangeOffset=1 */ for (i = 0; i < num_segments; i++) { if (range_offset[i] != 0) { uint16_t *glyph_ids = &range_offset[i] + be16_to_cpu (range_offset[i])/2; int range_size = be16_to_cpu (end_code[i]) - be16_to_cpu (start_code[i]) + 1; uint16_t g_id_be = cpu_to_be16 (index); int j; if (range_size > 0) { if ((char*)glyph_ids + 2*range_size > (char*)map + size) return CAIRO_INT_STATUS_UNSUPPORTED; for (j = 0; j < range_size; j++) { if (glyph_ids[j] == g_id_be) { *ucs4 = be16_to_cpu (start_code[i]) + j; goto found; } } } } } /* glyph not found */ *ucs4 = -1; found: status = CAIRO_STATUS_SUCCESS; fail: free (map); return status; } cairo_int_status_t _cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, unsigned long index, uint32_t *ucs4) { cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; const cairo_scaled_font_backend_t *backend; tt_cmap_t *cmap; char buf[4]; int num_tables, i; unsigned long size; backend = scaled_font->backend; if (!backend->load_truetype_table) return CAIRO_INT_STATUS_UNSUPPORTED; size = 4; status = backend->load_truetype_table (scaled_font, TT_TAG_cmap, 0, (unsigned char *) &buf, &size); if (unlikely (status)) return status; cmap = (tt_cmap_t *) buf; num_tables = be16_to_cpu (cmap->num_tables); size = 4 + num_tables*sizeof(tt_cmap_index_t); cmap = _cairo_malloc_ab_plus_c (num_tables, sizeof (tt_cmap_index_t), 4); if (unlikely (cmap == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = backend->load_truetype_table (scaled_font, TT_TAG_cmap, 0, (unsigned char *) cmap, &size); if (unlikely (status)) goto cleanup; /* Find a table with Unicode mapping */ for (i = 0; i < num_tables; i++) { if (be16_to_cpu (cmap->index[i].platform) == 3 && be16_to_cpu (cmap->index[i].encoding) == 1) { status = _cairo_truetype_reverse_cmap (scaled_font, be32_to_cpu (cmap->index[i].offset), index, ucs4); if (status != CAIRO_INT_STATUS_UNSUPPORTED) break; } } cleanup: free (cmap); return status; } cairo_int_status_t _cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, char **ps_name_out, char **font_name_out) { cairo_status_t status; const cairo_scaled_font_backend_t *backend; tt_name_t *name; tt_name_record_t *record; unsigned long size; int i, j; char *ps_name = NULL; char *font_name = NULL; backend = scaled_font->backend; if (!backend->load_truetype_table) return CAIRO_INT_STATUS_UNSUPPORTED; size = 0; status = backend->load_truetype_table (scaled_font, TT_TAG_name, 0, NULL, &size); if (status) return status; name = malloc (size); if (name == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); status = backend->load_truetype_table (scaled_font, TT_TAG_name, 0, (unsigned char *) name, &size); if (status) goto fail; /* Extract the font name and PS name from the name table. At * present this just looks for the Mac platform/Roman encoded font * name. It should be extended to use any suitable font name in * the name table. */ for (i = 0; i < be16_to_cpu(name->num_records); i++) { record = &(name->records[i]); if ((be16_to_cpu (record->platform) == 1) && (be16_to_cpu (record->encoding) == 0)) { if (be16_to_cpu (record->name) == 4) { font_name = malloc (be16_to_cpu(record->length) + 1); if (font_name == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } strncpy(font_name, ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), be16_to_cpu (record->length)); font_name[be16_to_cpu (record->length)] = 0; } if (be16_to_cpu (record->name) == 6) { ps_name = malloc (be16_to_cpu(record->length) + 1); if (ps_name == NULL) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto fail; } strncpy(ps_name, ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset), be16_to_cpu (record->length)); ps_name[be16_to_cpu (record->length)] = 0; } if (font_name && ps_name) break; } } free (name); /* Ensure PS name does not contain any spaces */ if (ps_name) { for (i = 0, j = 0; ps_name[j]; j++) { if (ps_name[j] == ' ') continue; ps_name[i++] = ps_name[j]; } ps_name[i] = '\0'; } *ps_name_out = ps_name; *font_name_out = font_name; return CAIRO_STATUS_SUCCESS; fail: free (name); if (ps_name != NULL) free (ps_name); if (font_name != NULL) free (font_name); *ps_name_out = NULL; *font_name_out = NULL; return status; } #endif /* CAIRO_HAS_FONT_SUBSET */