diff --git a/ChangeLog b/ChangeLog index ad6ccceaa..a2ca8f85b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2005-01-17 Kristian Høgsberg + + * src/cairo_pdf_surface.c: Add preliminary text support, including + support for truetype font subsetting. + + * src/cairoint.h: Change type of 'surface' argument in show_glyphs + to void * as it is for all other surface virtual functions. + * src/cairo_xlib_surface.c (_cairo_xlib_surface_show_glyphs): + Update accordingly. + + * configure.in: Add check for endianess. + + * src/cairo_array.c (_cairo_array_grow_by): Fix bug in array + growing loop. + (_cairo_array_append): Accept NULL for elements argument, in which + case we just allocate space in the array. + 2005-01-17 Kristian Høgsberg * test/Makefile.am (EXTRA_DIST): Take image_rotate-ref.png out of diff --git a/configure.in b/configure.in index f43e4741b..e9fa90fb1 100644 --- a/configure.in +++ b/configure.in @@ -33,6 +33,7 @@ AC_PROG_CC AC_PROG_CPP AM_PROG_LIBTOOL AC_STDC_HEADERS +AC_C_BIGENDIAN AC_CHECK_LIBM diff --git a/src/cairo-array.c b/src/cairo-array.c index 9645380b2..2b1cf9d61 100644 --- a/src/cairo-array.c +++ b/src/cairo-array.c @@ -68,7 +68,7 @@ _cairo_array_grow_by (cairo_array_t *array, int additional) new_size = old_size * 2; while (new_size < required_size) - new_size = old_size * 2; + new_size = new_size * 2; array->size = new_size; new_elements = realloc (array->elements, @@ -105,22 +105,26 @@ _cairo_array_copy_element (cairo_array_t *array, int index, void *dst) memcpy (dst, _cairo_array_index (array, index), array->element_size); } -cairo_status_t -_cairo_array_append (cairo_array_t *array, void *elements, int num_elements) +void * +_cairo_array_append (cairo_array_t *array, + const void *elements, int num_elements) { cairo_status_t status; + void *dest; status = _cairo_array_grow_by (array, num_elements); if (status != CAIRO_STATUS_SUCCESS) - return status; + return NULL; assert (array->num_elements + num_elements <= array->size); - memcpy (&array->elements[array->num_elements * array->element_size], - elements, num_elements * array->element_size); + dest = &array->elements[array->num_elements * array->element_size]; array->num_elements += num_elements; - return CAIRO_STATUS_SUCCESS; + if (elements != NULL) + memcpy (dest, elements, num_elements * array->element_size); + + return dest; } int diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 4b4b05a0a..593829d69 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -36,6 +36,12 @@ #include "cairoint.h" +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_TRUETYPE_TAGS_H +#include FT_TRUETYPE_TABLES_H + #include #include @@ -88,6 +94,45 @@ * instead of outputting the cm operator in every page. */ +typedef struct ft_subset_glyph ft_subset_glyph_t; +struct ft_subset_glyph { + int parent_index; + unsigned long location; +}; + +typedef struct cairo_pdf_font_backend cairo_pdf_font_backend_t; +struct cairo_pdf_font_backend { + int (*use_glyph) (void *abstract_font, + int glyph); + cairo_status_t (*generate) (void *abstract_font, + const char **data, + unsigned long *length); + void (*destroy) (void *abstract_font); +}; + +typedef struct cairo_pdf_font cairo_pdf_font_t; +struct cairo_pdf_font { + cairo_pdf_font_backend_t *backend; + cairo_unscaled_font_t *unscaled_font; + unsigned int font_id; + char *base_font; + int num_glyphs; + int *widths; + long x_min, y_min, x_max, y_max; + long ascent, descent; +}; + +typedef struct cairo_pdf_ft_font cairo_pdf_ft_font_t; +struct cairo_pdf_ft_font { + cairo_pdf_font_t base; + ft_subset_glyph_t *glyphs; + FT_Face face; + unsigned long *checksum_location; + cairo_array_t output; + int *parent_to_subset; + cairo_status_t status; +}; + typedef struct cairo_pdf_object cairo_pdf_object_t; typedef struct cairo_pdf_resource cairo_pdf_resource_t; typedef struct cairo_pdf_stream cairo_pdf_stream_t; @@ -124,6 +169,8 @@ struct cairo_pdf_document { cairo_array_t objects; cairo_array_t pages; + + cairo_array_t fonts; }; struct cairo_pdf_surface { @@ -142,6 +189,7 @@ struct cairo_pdf_surface { cairo_array_t xobjects; cairo_array_t streams; cairo_array_t alphas; + cairo_array_t fonts; }; @@ -158,6 +206,9 @@ _cairo_pdf_document_destroy (cairo_pdf_document_t *document); static void _cairo_pdf_document_reference (cairo_pdf_document_t *document); +static unsigned int +_cairo_pdf_document_new_object (cairo_pdf_document_t *document); + static cairo_status_t _cairo_pdf_document_add_page (cairo_pdf_document_t *document, cairo_pdf_surface_t *surface); @@ -180,18 +231,592 @@ _cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface); static const cairo_surface_backend_t cairo_pdf_surface_backend; +/* Truetype font subsetting code */ + +#define ARRAY_LENGTH(a) ( (sizeof (a)) / (sizeof ((a)[0])) ) + +#define SFNT_VERSION 0x00010000 +#define OFFSET_TABLE_SIZE 12 +#define TABLE_DIRECTORY_ENTRY_SIZE 16 + +#ifdef WORDS_BIGENDIAN + +#define cpu_to_be16(v) (v) +#define cpu_to_be32(v) (v) + +#else + +static inline unsigned short +cpu_to_be16(unsigned short v) +{ + return (v << 8) | (v >> 8); +} + +static inline unsigned short +be16_to_cpu(unsigned short v) +{ + return cpu_to_be16 (v); +} + +static inline unsigned long +cpu_to_be32(unsigned long v) +{ + return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16); +} + +static inline unsigned long +be32_to_cpu(unsigned long v) +{ + return cpu_to_be32 (v); +} + +#endif + +static cairo_pdf_font_backend_t cairo_pdf_ft_font_backend; + +static int +cairo_pdf_font_use_glyph (cairo_pdf_font_t *font, int glyph) +{ + return font->backend->use_glyph (font, glyph); +} + +static cairo_status_t +cairo_pdf_font_generate (cairo_pdf_font_t *font, + const char **data, unsigned long *length) +{ + return font->backend->generate (font, data, length); +} + +static void +cairo_pdf_font_destroy (cairo_pdf_font_t *font) +{ + font->backend->destroy (font); +} + +static cairo_pdf_font_t * +cairo_pdf_ft_font_create (cairo_pdf_document_t *document, FT_Face face) +{ + cairo_pdf_ft_font_t *font; + unsigned long size; + int i, j; + + /* We currently only support freetype truetype fonts. */ + size = 0; + if (!FT_IS_SFNT (face) || + FT_Load_Sfnt_Table (face, TTAG_glyf, 0, NULL, &size) != 0) + return NULL; + + font = malloc (sizeof (cairo_pdf_ft_font_t)); + if (font == NULL) + return NULL; + + font->base.backend = &cairo_pdf_ft_font_backend; + font->base.font_id = _cairo_pdf_document_new_object (document); + + _cairo_array_init (&font->output, sizeof (char)); + if (_cairo_array_grow_by (&font->output, 4096) != CAIRO_STATUS_SUCCESS) + goto fail1; + + font->face = face; + font->glyphs = calloc (face->num_glyphs + 1, sizeof (ft_subset_glyph_t)); + if (font->glyphs == NULL) + goto fail2; + + font->parent_to_subset = calloc (face->num_glyphs, sizeof (int)); + if (font->parent_to_subset == NULL) + goto fail3; + + font->base.num_glyphs = 1; + font->base.x_min = face->bbox.xMin; + font->base.y_min = face->bbox.yMin; + font->base.x_max = face->bbox.xMax; + font->base.y_max = face->bbox.yMax; + font->base.ascent = face->ascender; + font->base.descent = face->descender; + font->base.base_font = strdup (face->family_name); + if (font->base.base_font == NULL) + goto fail4; + + for (i = 0, j = 0; font->base.base_font[j]; j++) { + if (font->base.base_font[j] == ' ') + continue; + font->base.base_font[i++] = font->base.base_font[j]; + } + font->base.base_font[i] = '\0'; + + font->base.widths = calloc (face->num_glyphs, sizeof (int)); + if (font->base.widths == NULL) + goto fail5; + + font->status = CAIRO_STATUS_SUCCESS; + + return &font->base; + + fail5: + free (font->base.base_font); + fail4: + free (font->parent_to_subset); + fail3: + free (font->glyphs); + fail2: + _cairo_array_fini (&font->output); + fail1: + free (font); + return NULL; +} + +static void +cairo_pdf_ft_font_destroy (void *abstract_font) +{ + cairo_pdf_ft_font_t *font = abstract_font; + + free (font->base.base_font); + free (font->parent_to_subset); + free (font->glyphs); + _cairo_array_fini (&font->output); + free (font); +} + +static void * +cairo_pdf_ft_font_write (cairo_pdf_ft_font_t *font, + const void *data, size_t length) +{ + void *p; + + p = _cairo_array_append (&font->output, data, length); + if (p == NULL) + font->status = CAIRO_STATUS_NO_MEMORY; + + return p; +} + +static void +cairo_pdf_ft_font_write_be16 (cairo_pdf_ft_font_t *font, + unsigned short value) +{ + unsigned short be16_value; + + be16_value = cpu_to_be16 (value); + cairo_pdf_ft_font_write (font, &be16_value, sizeof be16_value); +} + +static void +cairo_pdf_ft_font_write_be32 (cairo_pdf_ft_font_t *font, unsigned long value) +{ + unsigned long be32_value; + + be32_value = cpu_to_be32 (value); + cairo_pdf_ft_font_write (font, &be32_value, sizeof be32_value); +} + +static unsigned long +cairo_pdf_ft_font_align_output (cairo_pdf_ft_font_t *font) +{ + int length, aligned; + static const char pad[4]; + + length = _cairo_array_num_elements (&font->output); + aligned = (length + 3) & ~3; + cairo_pdf_ft_font_write (font, pad, aligned - length); + + return aligned; +} + +static int +cairo_pdf_ft_font_write_cmap_table (cairo_pdf_ft_font_t *font, unsigned long tag) +{ + int i; + + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 1); + + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be32 (font, 12); + + /* Output a format 6 encoding table. */ + + cairo_pdf_ft_font_write_be16 (font, 6); + cairo_pdf_ft_font_write_be16 (font, 10 + 2 * (font->base.num_glyphs - 1)); + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 1); /* First glyph */ + cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs - 1); + for (i = 1; i < font->base.num_glyphs; i++) + cairo_pdf_ft_font_write_be16 (font, i); + + return font->status; +} + +static int +cairo_pdf_ft_font_write_generic_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + char *buffer; + unsigned long size; + + size = 0; + FT_Load_Sfnt_Table (font->face, tag, 0, NULL, &size); + buffer = cairo_pdf_ft_font_write (font, NULL, size); + FT_Load_Sfnt_Table (font->face, tag, 0, buffer, &size); + + return 0; +} + +static int +cairo_pdf_ft_font_write_glyf_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + unsigned long start_offset, index, size; + TT_Header *header; + unsigned long begin, end; + char *buffer; + int i; + union { + unsigned char *bytes; + unsigned short *short_offsets; + unsigned long *long_offsets; + } u; + + header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); + if (header->Index_To_Loc_Format == 0) + size = sizeof (short) * (font->face->num_glyphs + 1); + else + size = sizeof (long) * (font->face->num_glyphs + 1); + + u.bytes = malloc (size); + if (u.bytes == NULL) { + font->status = CAIRO_STATUS_NO_MEMORY; + return font->status; + } + FT_Load_Sfnt_Table (font->face, TTAG_loca, 0, u.bytes, &size); + + start_offset = _cairo_array_num_elements (&font->output); + for (i = 0; i < font->base.num_glyphs; i++) { + index = font->glyphs[i].parent_index; + if (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]); + } + + size = end - begin; + + font->glyphs[i].location = + cairo_pdf_ft_font_align_output (font) - start_offset; + buffer = cairo_pdf_ft_font_write (font, NULL, size); + if (buffer == NULL) + break; + FT_Load_Sfnt_Table (font->face, TTAG_glyf, begin, buffer, &size); + /* FIXME: remap composite glyphs */ + } + + font->glyphs[i].location = + cairo_pdf_ft_font_align_output (font) - start_offset; + + free (u.bytes); + + return font->status; +} + +static int +cairo_pdf_ft_font_write_head_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + TT_Header *head; + + head = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); + + cairo_pdf_ft_font_write_be32 (font, head->Table_Version); + cairo_pdf_ft_font_write_be32 (font, head->Font_Revision); + + font->checksum_location = + (unsigned long *) _cairo_array_index (&font->output, 0) + + _cairo_array_num_elements (&font->output) / sizeof (long); + cairo_pdf_ft_font_write_be32 (font, 0); + cairo_pdf_ft_font_write_be32 (font, head->Magic_Number); + + cairo_pdf_ft_font_write_be16 (font, head->Flags); + cairo_pdf_ft_font_write_be16 (font, head->Units_Per_EM); + + cairo_pdf_ft_font_write_be32 (font, head->Created[0]); + cairo_pdf_ft_font_write_be32 (font, head->Created[1]); + cairo_pdf_ft_font_write_be32 (font, head->Modified[0]); + cairo_pdf_ft_font_write_be32 (font, head->Modified[1]); + + cairo_pdf_ft_font_write_be16 (font, head->xMin); + cairo_pdf_ft_font_write_be16 (font, head->yMin); + cairo_pdf_ft_font_write_be16 (font, head->xMax); + cairo_pdf_ft_font_write_be16 (font, head->yMax); + + cairo_pdf_ft_font_write_be16 (font, head->Mac_Style); + cairo_pdf_ft_font_write_be16 (font, head->Lowest_Rec_PPEM); + + cairo_pdf_ft_font_write_be16 (font, head->Font_Direction); + cairo_pdf_ft_font_write_be16 (font, head->Index_To_Loc_Format); + cairo_pdf_ft_font_write_be16 (font, head->Glyph_Data_Format); + + return font->status; +} + +static int cairo_pdf_ft_font_write_hhea_table (cairo_pdf_ft_font_t *font, unsigned long tag) +{ + TT_HoriHeader *hhea; + + hhea = FT_Get_Sfnt_Table (font->face, ft_sfnt_hhea); + + cairo_pdf_ft_font_write_be32 (font, hhea->Version); + cairo_pdf_ft_font_write_be16 (font, hhea->Ascender); + cairo_pdf_ft_font_write_be16 (font, hhea->Descender); + cairo_pdf_ft_font_write_be16 (font, hhea->Line_Gap); + + cairo_pdf_ft_font_write_be16 (font, hhea->advance_Width_Max); + + cairo_pdf_ft_font_write_be16 (font, hhea->min_Left_Side_Bearing); + cairo_pdf_ft_font_write_be16 (font, hhea->min_Right_Side_Bearing); + cairo_pdf_ft_font_write_be16 (font, hhea->xMax_Extent); + cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Rise); + cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Run); + cairo_pdf_ft_font_write_be16 (font, hhea->caret_Offset); + + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 0); + + cairo_pdf_ft_font_write_be16 (font, hhea->metric_Data_Format); + cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs); + + return font->status; +} + +static int +cairo_pdf_ft_font_write_hmtx_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + unsigned long entry_size; + short *p; + int i; + + for (i = 0; i < font->base.num_glyphs; i++) { + entry_size = 2 * sizeof (short); + p = cairo_pdf_ft_font_write (font, NULL, entry_size); + FT_Load_Sfnt_Table (font->face, TTAG_hmtx, + font->glyphs[i].parent_index * entry_size, + (FT_Byte *) p, &entry_size); + font->base.widths[i] = be16_to_cpu (p[0]); + } + + return font->status; +} + +static int +cairo_pdf_ft_font_write_loca_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + int i; + TT_Header *header; + + header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); + + if (header->Index_To_Loc_Format == 0) { + for (i = 0; i < font->base.num_glyphs + 1; i++) + cairo_pdf_ft_font_write_be16 (font, font->glyphs[i].location / 2); + } + else { + for (i = 0; i < font->base.num_glyphs + 1; i++) + cairo_pdf_ft_font_write_be32 (font, font->glyphs[i].location); + } + + return font->status; +} + +static int +cairo_pdf_ft_font_write_maxp_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + TT_MaxProfile *maxp; + + maxp = FT_Get_Sfnt_Table (font->face, ft_sfnt_maxp); + + cairo_pdf_ft_font_write_be32 (font, maxp->version); + cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs); + cairo_pdf_ft_font_write_be16 (font, maxp->maxPoints); + cairo_pdf_ft_font_write_be16 (font, maxp->maxContours); + cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositePoints); + cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositeContours); + cairo_pdf_ft_font_write_be16 (font, maxp->maxZones); + cairo_pdf_ft_font_write_be16 (font, maxp->maxTwilightPoints); + cairo_pdf_ft_font_write_be16 (font, maxp->maxStorage); + cairo_pdf_ft_font_write_be16 (font, maxp->maxFunctionDefs); + cairo_pdf_ft_font_write_be16 (font, maxp->maxInstructionDefs); + cairo_pdf_ft_font_write_be16 (font, maxp->maxStackElements); + cairo_pdf_ft_font_write_be16 (font, maxp->maxSizeOfInstructions); + cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentElements); + cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentDepth); + + return font->status; +} + +typedef struct table table_t; +struct table { + unsigned long tag; + int (*write) (cairo_pdf_ft_font_t *font, unsigned long tag); +}; + +static const table_t truetype_tables[] = { + { TTAG_cmap, cairo_pdf_ft_font_write_cmap_table }, + { TTAG_cvt, cairo_pdf_ft_font_write_generic_table }, + { TTAG_fpgm, cairo_pdf_ft_font_write_generic_table }, + { TTAG_glyf, cairo_pdf_ft_font_write_glyf_table }, + { TTAG_head, cairo_pdf_ft_font_write_head_table }, + { TTAG_hhea, cairo_pdf_ft_font_write_hhea_table }, + { TTAG_hmtx, cairo_pdf_ft_font_write_hmtx_table }, + { TTAG_loca, cairo_pdf_ft_font_write_loca_table }, + { TTAG_maxp, cairo_pdf_ft_font_write_maxp_table }, + { TTAG_name, cairo_pdf_ft_font_write_generic_table }, + { TTAG_prep, cairo_pdf_ft_font_write_generic_table }, +}; + +static cairo_status_t +cairo_pdf_ft_font_write_offset_table (cairo_pdf_ft_font_t *font) +{ + unsigned short search_range, entry_selector, range_shift; + int num_tables; + + num_tables = ARRAY_LENGTH (truetype_tables); + search_range = 1; + entry_selector = 0; + while (search_range * 2 <= num_tables) { + search_range *= 2; + entry_selector++; + } + search_range *= 16; + range_shift = num_tables * 16 - search_range; + + cairo_pdf_ft_font_write_be32 (font, SFNT_VERSION); + cairo_pdf_ft_font_write_be16 (font, num_tables); + cairo_pdf_ft_font_write_be16 (font, search_range); + cairo_pdf_ft_font_write_be16 (font, entry_selector); + cairo_pdf_ft_font_write_be16 (font, range_shift); + + cairo_pdf_ft_font_write (font, NULL, ARRAY_LENGTH (truetype_tables) * 16); + + return font->status; +} + +static unsigned long +cairo_pdf_ft_font_calculate_checksum (cairo_pdf_ft_font_t *font, + unsigned long start, unsigned long end) +{ + unsigned long *padded_end; + unsigned long *p; + unsigned long checksum; + char *data; + + checksum = 0; + data = _cairo_array_index (&font->output, 0); + p = (unsigned long *) (data + start); + padded_end = (unsigned long *) (data + ((end + 3) & ~3)); + while (p < padded_end) + checksum += *p++; + + return checksum; +} + +static void +cairo_pdf_ft_font_update_entry (cairo_pdf_ft_font_t *font, int index, unsigned long tag, + unsigned long start, unsigned long end) +{ + unsigned long *entry; + + entry = _cairo_array_index (&font->output, 12 + 16 * index); + entry[0] = cpu_to_be32 (tag); + entry[1] = cpu_to_be32 (cairo_pdf_ft_font_calculate_checksum (font, start, end)); + entry[2] = cpu_to_be32 (start); + entry[3] = cpu_to_be32 (end - start); +} + +static cairo_status_t +cairo_pdf_ft_font_generate (void *abstract_font, + const char **data, unsigned long *length) +{ + cairo_pdf_ft_font_t *font = abstract_font; + unsigned long start, end, next, checksum; + int i; + + if (cairo_pdf_ft_font_write_offset_table (font)) + return font->status; + + start = cairo_pdf_ft_font_align_output (font); + + for (i = 0; i < ARRAY_LENGTH (truetype_tables); i++) { + if (truetype_tables[i].write (font, truetype_tables[i].tag)) + break; + + end = _cairo_array_num_elements (&font->output); + next = cairo_pdf_ft_font_align_output (font); + cairo_pdf_ft_font_update_entry (font, i, truetype_tables[i].tag, + start, end); + start = next; + } + + checksum = + 0xb1b0afba - cairo_pdf_ft_font_calculate_checksum (font, 0, end); + *font->checksum_location = cpu_to_be32 (checksum); + + *data = _cairo_array_index (&font->output, 0); + *length = _cairo_array_num_elements (&font->output); + + return font->status; +} + +static int +cairo_pdf_ft_font_use_glyph (void *abstract_font, int glyph) +{ + cairo_pdf_ft_font_t *font = abstract_font; + + 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++; + } + + return font->parent_to_subset[glyph]; +} + +static cairo_pdf_font_backend_t cairo_pdf_ft_font_backend = { + cairo_pdf_ft_font_use_glyph, + cairo_pdf_ft_font_generate, + cairo_pdf_ft_font_destroy +}; + +/* PDF Generation */ + static unsigned int _cairo_pdf_document_new_object (cairo_pdf_document_t *document) { cairo_pdf_object_t object; object.offset = ftell (document->file); - /* FIXME: check return value */ - _cairo_array_append (&document->objects, &object, 1); + if (_cairo_array_append (&document->objects, &object, 1) == NULL) + return 0; return document->next_available_id++; } +static void +_cairo_pdf_document_update_object (cairo_pdf_document_t *document, + unsigned int id) +{ + cairo_pdf_object_t *object; + + object = _cairo_array_index (&document->objects, id - 1); + object->offset = ftell (document->file); +} + static void _cairo_pdf_surface_add_stream (cairo_pdf_surface_t *surface, cairo_pdf_stream_t *stream) @@ -243,6 +868,23 @@ _cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, double alpha) return _cairo_array_num_elements (&surface->alphas) - 1; } +static void +_cairo_pdf_surface_add_font (cairo_pdf_surface_t *surface, unsigned int id) +{ + cairo_pdf_resource_t resource; + int i, num_fonts; + + num_fonts = _cairo_array_num_elements (&surface->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&surface->fonts, i, &resource); + if (resource.id == id) + return; + } + + resource.id = id; + _cairo_array_append (&surface->fonts, &resource, 1); +} + cairo_surface_t * cairo_pdf_surface_create (FILE *file, double width_inches, @@ -293,6 +935,7 @@ _cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document, _cairo_array_init (&surface->patterns, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&surface->xobjects, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&surface->alphas, sizeof (double)); + _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t)); return &surface->base; } @@ -313,6 +956,7 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) _cairo_array_truncate (&surface->patterns, 0); _cairo_array_truncate (&surface->xobjects, 0); _cairo_array_truncate (&surface->alphas, 0); + _cairo_array_truncate (&surface->fonts, 0); } static cairo_surface_t * @@ -366,7 +1010,6 @@ _cairo_pdf_document_close_stream (cairo_pdf_document_t *document) FILE *file = document->file; long length; cairo_pdf_stream_t *stream; - cairo_pdf_object_t *object; stream = document->current_stream; if (stream == NULL) @@ -378,8 +1021,7 @@ _cairo_pdf_document_close_stream (cairo_pdf_document_t *document) "endstream\r\n" "endobj\r\n"); - object = _cairo_array_index (&document->objects, stream->length_id - 1); - object->offset = ftell(file); + _cairo_pdf_document_update_object (document, stream->length_id); fprintf (file, "%d 0 obj\r\n" " %ld\r\n" @@ -475,10 +1117,21 @@ _cairo_pdf_surface_set_repeat (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } -static unsigned long -compress_bound (unsigned long source_size) +static void * +compress_dup (const void *data, unsigned long data_size, + unsigned long *compressed_size) { - return source_size + (source_size >> 12) + (source_size >> 14) + 11; + void *compressed; + + /* Bound calculation taken from zlib. */ + *compressed_size = data_size + (data_size >> 12) + (data_size >> 14) + 11; + compressed = malloc (*compressed_size); + if (compressed == NULL) + return NULL; + + compress (compressed, compressed_size, data, data_size); + + return compressed; } static unsigned int @@ -498,13 +1151,6 @@ emit_image_data (cairo_pdf_document_t *document, if (rgb == NULL) return 0; - compressed_size = compress_bound (rgb_size); - compressed = malloc (compressed_size); - if (rgb == NULL) { - free (rgb); - return 0; - } - i = 0; for (y = 0; y < image->height; y++) { pixel = (pixman_bits_t *) (image->data + y * image->stride); @@ -516,7 +1162,11 @@ emit_image_data (cairo_pdf_document_t *document, } } - compress (compressed, &compressed_size, rgb, rgb_size); + compressed = compress_dup (rgb, rgb_size, &compressed_size); + if (compressed == NULL) { + free (rgb); + return 0; + } _cairo_pdf_document_close_stream (document); @@ -1033,6 +1683,87 @@ _cairo_pdf_surface_create_pattern (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } +static cairo_pdf_font_t * +_cairo_pdf_document_get_font (cairo_pdf_document_t *document, + cairo_unscaled_font_t *unscaled_font, + cairo_font_scale_t *scale) +{ + FT_Face face; + cairo_font_t scaled_font; + cairo_pdf_font_t *font; + unsigned int num_fonts, i; + + num_fonts = _cairo_array_num_elements (&document->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&document->fonts, i, &font); + if (font->unscaled_font == unscaled_font) + return font; + } + + /* FIXME: Figure out here which font backend is in use and call + * the appropriate constructor. */ + + /* FIXME: Why do I have to pass a scaled font to get the FT_Face? */ + _cairo_font_init (&scaled_font, scale, unscaled_font); + face = cairo_ft_font_face (&scaled_font); + font = cairo_pdf_ft_font_create (document, face); + if (font == NULL) + return NULL; + + if (_cairo_array_append (&document->fonts, &font, 1) == NULL) { + cairo_pdf_font_destroy (font); + free (font); + return NULL; + } + + return font; +} + +static cairo_status_t +_cairo_pdf_surface_show_glyphs (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + cairo_surface_t *source, + void *abstract_surface, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_pdf_document_t *document = surface->document; + FILE *file = document->file; + cairo_pdf_font_t *pdf_font; + int i, index; + + pdf_font = _cairo_pdf_document_get_font (document, font, scale); + if (pdf_font == NULL) + return CAIRO_STATUS_NO_MEMORY; + + _cairo_pdf_surface_ensure_stream (surface); + + fprintf (file, "0 0 0 rg BT /res%u 1 Tf", pdf_font->font_id); + for (i = 0; i < num_glyphs; i++) { + + index = cairo_pdf_font_use_glyph (pdf_font, glyphs[i].index); + + fprintf (file, + " %f %f %f %f %f %f Tm (%c) Tj", + scale->matrix[0][0], + scale->matrix[0][1], + scale->matrix[1][0], + -scale->matrix[1][1], + glyphs[i].x, + glyphs[i].y, + index); + } + fprintf (file, " ET\r\n"); + + _cairo_pdf_surface_add_font (surface, pdf_font->font_id); + + return CAIRO_STATUS_SUCCESS; +} + static const cairo_surface_backend_t cairo_pdf_surface_backend = { _cairo_pdf_surface_create_similar, _cairo_pdf_surface_destroy, @@ -1049,7 +1780,7 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { _cairo_pdf_surface_show_page, _cairo_pdf_surface_set_clip_region, _cairo_pdf_surface_create_pattern, - NULL, /* show_glyphs */ + _cairo_pdf_surface_show_glyphs }; static cairo_pdf_document_t * @@ -1080,6 +1811,8 @@ _cairo_pdf_document_create (FILE *file, document->pages_id = _cairo_pdf_document_new_object (document); + _cairo_array_init (&document->fonts, sizeof (cairo_pdf_font_t *)); + /* Document header */ fprintf (file, "%%PDF-1.4\r\n"); @@ -1108,14 +1841,10 @@ static void _cairo_pdf_document_write_pages (cairo_pdf_document_t *document) { FILE *file = document->file; - cairo_pdf_object_t *pages_object; unsigned int page_id; int num_pages, i; - pages_object = _cairo_array_index (&document->objects, - document->pages_id - 1); - pages_object->offset = ftell (file); - + _cairo_pdf_document_update_object (document, document->pages_id); fprintf (file, "%d 0 obj\r\n" "<< /Type /Pages\r\n" @@ -1141,6 +1870,111 @@ _cairo_pdf_document_write_pages (cairo_pdf_document_t *document) document->height_inches * document->y_ppi); } +static cairo_status_t +_cairo_pdf_document_write_fonts (cairo_pdf_document_t *document) +{ + FILE *file = document->file; + cairo_pdf_font_t *font; + int num_fonts, i, j; + const char *data; + char *compressed; + unsigned long data_size, compressed_size; + unsigned int stream_id, descriptor_id; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + num_fonts = _cairo_array_num_elements (&document->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&document->fonts, i, &font); + + status = cairo_pdf_font_generate (font, &data, &data_size); + if (status) + goto fail; + + compressed = compress_dup (data, data_size, &compressed_size); + if (compressed == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto fail; + } + + stream_id = _cairo_pdf_document_new_object (document); + fprintf (file, + "%d 0 obj\r\n" + "<< /Filter /FlateDecode\r\n" + " /Length %lu\r\n" + " /Length1 %lu\r\n" + ">>\r\n" + "stream\r\n", + stream_id, + compressed_size, + data_size); + fwrite (compressed, 1, compressed_size, file); + fprintf (file, + "\r\n" + "endstream\r\n" + "endobj\r\n"); + free (compressed); + + descriptor_id = _cairo_pdf_document_new_object (document); + fprintf (file, + "%d 0 obj\r\n" + "<< /Type /FontDescriptor\r\n" + " /FontName /%s\r\n" + " /Flags 32\r\n" + " /FontBBox [ %ld %ld %ld %ld ]\r\n" + " /ItalicAngle 0\r\n" + " /Ascent %ld\r\n" + " /Descent %ld\r\n" + " /CapHeight 500\r\n" + " /StemV 80\r\n" + " /StemH 80\r\n" + " /FontFile2 %u 0 R\r\n" + ">>\r\n" + "endobj\r\n", + descriptor_id, + font->base_font, + font->x_min, + font->y_min, + font->x_max, + font->y_max, + font->ascent, + font->descent, + stream_id); + + _cairo_pdf_document_update_object (document, font->font_id); + fprintf (file, + "%d 0 obj\r\n" + "<< /Type /Font\r\n" + " /Subtype /TrueType\r\n" + " /BaseFont /%s\r\n" + " /FirstChar 0\r\n" + " /LastChar %d\r\n" + " /FontDescriptor %d 0 R\r\n" + " /Widths ", + font->font_id, + font->base_font, + font->num_glyphs, + descriptor_id); + + fprintf (file, + "["); + + for (j = 0; j < font->num_glyphs; j++) + fprintf (file, + " %d", + font->widths[j]); + + fprintf (file, + " ]\r\n" + ">>\r\n" + "endobj\r\n"); + + fail: + cairo_pdf_ft_font_destroy (font); + } + + return status; +} + static unsigned int _cairo_pdf_document_write_catalog (cairo_pdf_document_t *document) { @@ -1203,6 +2037,7 @@ _cairo_pdf_document_destroy (cairo_pdf_document_t *document) _cairo_pdf_document_close_stream (document); _cairo_pdf_document_write_pages (document); + _cairo_pdf_document_write_fonts (document); info_id = _cairo_pdf_document_write_info (document); catalog_id = _cairo_pdf_document_write_catalog (document); offset = _cairo_pdf_document_write_xref (document); @@ -1260,6 +2095,21 @@ _cairo_pdf_document_add_page (cairo_pdf_document_t *document, " ]\r\n" " /Resources <<\r\n"); + num_resources = _cairo_array_num_elements (&surface->fonts); + if (num_resources > 0) { + fprintf (file, + " /Font <<"); + + for (i = 0; i < num_resources; i++) { + res = _cairo_array_index (&surface->fonts, i); + fprintf (file, + " /res%d %d 0 R", + res->id, res->id); + } + + fprintf (file, + " >>\r\n"); + } num_alphas = _cairo_array_num_elements (&surface->alphas); if (num_alphas > 0) { diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index 383bf247c..1867d5985 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -697,7 +697,7 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, cairo_font_scale_t *scale, cairo_operator_t operator, cairo_surface_t *source, - cairo_surface_t *surface, + void *abstract_surface, int source_x, int source_y, const cairo_glyph_t *glyphs, @@ -1250,14 +1250,14 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, cairo_font_scale_t *scale, cairo_operator_t operator, cairo_surface_t *source, - cairo_surface_t *surface, + void *abstract_surface, int source_x, int source_y, const cairo_glyph_t *glyphs, int num_glyphs) { unsigned int elt_size; - cairo_xlib_surface_t *self = (cairo_xlib_surface_t *) surface; + cairo_xlib_surface_t *self = abstract_surface; cairo_image_surface_t *tmp = NULL; cairo_xlib_surface_t *src = NULL; glyphset_cache_t *g; @@ -1278,7 +1278,7 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, } /* prep the source surface. */ - if (source->backend == surface->backend) { + if (source->backend == self->base.backend) { src = (cairo_xlib_surface_t *) source; } else { @@ -1287,7 +1287,7 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, goto FREE_ENTRIES; src = (cairo_xlib_surface_t *) - _cairo_surface_create_similar_scratch (surface, self->format, 1, + _cairo_surface_create_similar_scratch (&self->base, self->format, 1, tmp->width, tmp->height); if (src == NULL) diff --git a/src/cairo_array.c b/src/cairo_array.c index 9645380b2..2b1cf9d61 100644 --- a/src/cairo_array.c +++ b/src/cairo_array.c @@ -68,7 +68,7 @@ _cairo_array_grow_by (cairo_array_t *array, int additional) new_size = old_size * 2; while (new_size < required_size) - new_size = old_size * 2; + new_size = new_size * 2; array->size = new_size; new_elements = realloc (array->elements, @@ -105,22 +105,26 @@ _cairo_array_copy_element (cairo_array_t *array, int index, void *dst) memcpy (dst, _cairo_array_index (array, index), array->element_size); } -cairo_status_t -_cairo_array_append (cairo_array_t *array, void *elements, int num_elements) +void * +_cairo_array_append (cairo_array_t *array, + const void *elements, int num_elements) { cairo_status_t status; + void *dest; status = _cairo_array_grow_by (array, num_elements); if (status != CAIRO_STATUS_SUCCESS) - return status; + return NULL; assert (array->num_elements + num_elements <= array->size); - memcpy (&array->elements[array->num_elements * array->element_size], - elements, num_elements * array->element_size); + dest = &array->elements[array->num_elements * array->element_size]; array->num_elements += num_elements; - return CAIRO_STATUS_SUCCESS; + if (elements != NULL) + memcpy (dest, elements, num_elements * array->element_size); + + return dest; } int diff --git a/src/cairo_pdf_surface.c b/src/cairo_pdf_surface.c index 4b4b05a0a..593829d69 100644 --- a/src/cairo_pdf_surface.c +++ b/src/cairo_pdf_surface.c @@ -36,6 +36,12 @@ #include "cairoint.h" +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_TRUETYPE_TAGS_H +#include FT_TRUETYPE_TABLES_H + #include #include @@ -88,6 +94,45 @@ * instead of outputting the cm operator in every page. */ +typedef struct ft_subset_glyph ft_subset_glyph_t; +struct ft_subset_glyph { + int parent_index; + unsigned long location; +}; + +typedef struct cairo_pdf_font_backend cairo_pdf_font_backend_t; +struct cairo_pdf_font_backend { + int (*use_glyph) (void *abstract_font, + int glyph); + cairo_status_t (*generate) (void *abstract_font, + const char **data, + unsigned long *length); + void (*destroy) (void *abstract_font); +}; + +typedef struct cairo_pdf_font cairo_pdf_font_t; +struct cairo_pdf_font { + cairo_pdf_font_backend_t *backend; + cairo_unscaled_font_t *unscaled_font; + unsigned int font_id; + char *base_font; + int num_glyphs; + int *widths; + long x_min, y_min, x_max, y_max; + long ascent, descent; +}; + +typedef struct cairo_pdf_ft_font cairo_pdf_ft_font_t; +struct cairo_pdf_ft_font { + cairo_pdf_font_t base; + ft_subset_glyph_t *glyphs; + FT_Face face; + unsigned long *checksum_location; + cairo_array_t output; + int *parent_to_subset; + cairo_status_t status; +}; + typedef struct cairo_pdf_object cairo_pdf_object_t; typedef struct cairo_pdf_resource cairo_pdf_resource_t; typedef struct cairo_pdf_stream cairo_pdf_stream_t; @@ -124,6 +169,8 @@ struct cairo_pdf_document { cairo_array_t objects; cairo_array_t pages; + + cairo_array_t fonts; }; struct cairo_pdf_surface { @@ -142,6 +189,7 @@ struct cairo_pdf_surface { cairo_array_t xobjects; cairo_array_t streams; cairo_array_t alphas; + cairo_array_t fonts; }; @@ -158,6 +206,9 @@ _cairo_pdf_document_destroy (cairo_pdf_document_t *document); static void _cairo_pdf_document_reference (cairo_pdf_document_t *document); +static unsigned int +_cairo_pdf_document_new_object (cairo_pdf_document_t *document); + static cairo_status_t _cairo_pdf_document_add_page (cairo_pdf_document_t *document, cairo_pdf_surface_t *surface); @@ -180,18 +231,592 @@ _cairo_pdf_surface_ensure_stream (cairo_pdf_surface_t *surface); static const cairo_surface_backend_t cairo_pdf_surface_backend; +/* Truetype font subsetting code */ + +#define ARRAY_LENGTH(a) ( (sizeof (a)) / (sizeof ((a)[0])) ) + +#define SFNT_VERSION 0x00010000 +#define OFFSET_TABLE_SIZE 12 +#define TABLE_DIRECTORY_ENTRY_SIZE 16 + +#ifdef WORDS_BIGENDIAN + +#define cpu_to_be16(v) (v) +#define cpu_to_be32(v) (v) + +#else + +static inline unsigned short +cpu_to_be16(unsigned short v) +{ + return (v << 8) | (v >> 8); +} + +static inline unsigned short +be16_to_cpu(unsigned short v) +{ + return cpu_to_be16 (v); +} + +static inline unsigned long +cpu_to_be32(unsigned long v) +{ + return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16); +} + +static inline unsigned long +be32_to_cpu(unsigned long v) +{ + return cpu_to_be32 (v); +} + +#endif + +static cairo_pdf_font_backend_t cairo_pdf_ft_font_backend; + +static int +cairo_pdf_font_use_glyph (cairo_pdf_font_t *font, int glyph) +{ + return font->backend->use_glyph (font, glyph); +} + +static cairo_status_t +cairo_pdf_font_generate (cairo_pdf_font_t *font, + const char **data, unsigned long *length) +{ + return font->backend->generate (font, data, length); +} + +static void +cairo_pdf_font_destroy (cairo_pdf_font_t *font) +{ + font->backend->destroy (font); +} + +static cairo_pdf_font_t * +cairo_pdf_ft_font_create (cairo_pdf_document_t *document, FT_Face face) +{ + cairo_pdf_ft_font_t *font; + unsigned long size; + int i, j; + + /* We currently only support freetype truetype fonts. */ + size = 0; + if (!FT_IS_SFNT (face) || + FT_Load_Sfnt_Table (face, TTAG_glyf, 0, NULL, &size) != 0) + return NULL; + + font = malloc (sizeof (cairo_pdf_ft_font_t)); + if (font == NULL) + return NULL; + + font->base.backend = &cairo_pdf_ft_font_backend; + font->base.font_id = _cairo_pdf_document_new_object (document); + + _cairo_array_init (&font->output, sizeof (char)); + if (_cairo_array_grow_by (&font->output, 4096) != CAIRO_STATUS_SUCCESS) + goto fail1; + + font->face = face; + font->glyphs = calloc (face->num_glyphs + 1, sizeof (ft_subset_glyph_t)); + if (font->glyphs == NULL) + goto fail2; + + font->parent_to_subset = calloc (face->num_glyphs, sizeof (int)); + if (font->parent_to_subset == NULL) + goto fail3; + + font->base.num_glyphs = 1; + font->base.x_min = face->bbox.xMin; + font->base.y_min = face->bbox.yMin; + font->base.x_max = face->bbox.xMax; + font->base.y_max = face->bbox.yMax; + font->base.ascent = face->ascender; + font->base.descent = face->descender; + font->base.base_font = strdup (face->family_name); + if (font->base.base_font == NULL) + goto fail4; + + for (i = 0, j = 0; font->base.base_font[j]; j++) { + if (font->base.base_font[j] == ' ') + continue; + font->base.base_font[i++] = font->base.base_font[j]; + } + font->base.base_font[i] = '\0'; + + font->base.widths = calloc (face->num_glyphs, sizeof (int)); + if (font->base.widths == NULL) + goto fail5; + + font->status = CAIRO_STATUS_SUCCESS; + + return &font->base; + + fail5: + free (font->base.base_font); + fail4: + free (font->parent_to_subset); + fail3: + free (font->glyphs); + fail2: + _cairo_array_fini (&font->output); + fail1: + free (font); + return NULL; +} + +static void +cairo_pdf_ft_font_destroy (void *abstract_font) +{ + cairo_pdf_ft_font_t *font = abstract_font; + + free (font->base.base_font); + free (font->parent_to_subset); + free (font->glyphs); + _cairo_array_fini (&font->output); + free (font); +} + +static void * +cairo_pdf_ft_font_write (cairo_pdf_ft_font_t *font, + const void *data, size_t length) +{ + void *p; + + p = _cairo_array_append (&font->output, data, length); + if (p == NULL) + font->status = CAIRO_STATUS_NO_MEMORY; + + return p; +} + +static void +cairo_pdf_ft_font_write_be16 (cairo_pdf_ft_font_t *font, + unsigned short value) +{ + unsigned short be16_value; + + be16_value = cpu_to_be16 (value); + cairo_pdf_ft_font_write (font, &be16_value, sizeof be16_value); +} + +static void +cairo_pdf_ft_font_write_be32 (cairo_pdf_ft_font_t *font, unsigned long value) +{ + unsigned long be32_value; + + be32_value = cpu_to_be32 (value); + cairo_pdf_ft_font_write (font, &be32_value, sizeof be32_value); +} + +static unsigned long +cairo_pdf_ft_font_align_output (cairo_pdf_ft_font_t *font) +{ + int length, aligned; + static const char pad[4]; + + length = _cairo_array_num_elements (&font->output); + aligned = (length + 3) & ~3; + cairo_pdf_ft_font_write (font, pad, aligned - length); + + return aligned; +} + +static int +cairo_pdf_ft_font_write_cmap_table (cairo_pdf_ft_font_t *font, unsigned long tag) +{ + int i; + + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 1); + + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be32 (font, 12); + + /* Output a format 6 encoding table. */ + + cairo_pdf_ft_font_write_be16 (font, 6); + cairo_pdf_ft_font_write_be16 (font, 10 + 2 * (font->base.num_glyphs - 1)); + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 1); /* First glyph */ + cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs - 1); + for (i = 1; i < font->base.num_glyphs; i++) + cairo_pdf_ft_font_write_be16 (font, i); + + return font->status; +} + +static int +cairo_pdf_ft_font_write_generic_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + char *buffer; + unsigned long size; + + size = 0; + FT_Load_Sfnt_Table (font->face, tag, 0, NULL, &size); + buffer = cairo_pdf_ft_font_write (font, NULL, size); + FT_Load_Sfnt_Table (font->face, tag, 0, buffer, &size); + + return 0; +} + +static int +cairo_pdf_ft_font_write_glyf_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + unsigned long start_offset, index, size; + TT_Header *header; + unsigned long begin, end; + char *buffer; + int i; + union { + unsigned char *bytes; + unsigned short *short_offsets; + unsigned long *long_offsets; + } u; + + header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); + if (header->Index_To_Loc_Format == 0) + size = sizeof (short) * (font->face->num_glyphs + 1); + else + size = sizeof (long) * (font->face->num_glyphs + 1); + + u.bytes = malloc (size); + if (u.bytes == NULL) { + font->status = CAIRO_STATUS_NO_MEMORY; + return font->status; + } + FT_Load_Sfnt_Table (font->face, TTAG_loca, 0, u.bytes, &size); + + start_offset = _cairo_array_num_elements (&font->output); + for (i = 0; i < font->base.num_glyphs; i++) { + index = font->glyphs[i].parent_index; + if (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]); + } + + size = end - begin; + + font->glyphs[i].location = + cairo_pdf_ft_font_align_output (font) - start_offset; + buffer = cairo_pdf_ft_font_write (font, NULL, size); + if (buffer == NULL) + break; + FT_Load_Sfnt_Table (font->face, TTAG_glyf, begin, buffer, &size); + /* FIXME: remap composite glyphs */ + } + + font->glyphs[i].location = + cairo_pdf_ft_font_align_output (font) - start_offset; + + free (u.bytes); + + return font->status; +} + +static int +cairo_pdf_ft_font_write_head_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + TT_Header *head; + + head = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); + + cairo_pdf_ft_font_write_be32 (font, head->Table_Version); + cairo_pdf_ft_font_write_be32 (font, head->Font_Revision); + + font->checksum_location = + (unsigned long *) _cairo_array_index (&font->output, 0) + + _cairo_array_num_elements (&font->output) / sizeof (long); + cairo_pdf_ft_font_write_be32 (font, 0); + cairo_pdf_ft_font_write_be32 (font, head->Magic_Number); + + cairo_pdf_ft_font_write_be16 (font, head->Flags); + cairo_pdf_ft_font_write_be16 (font, head->Units_Per_EM); + + cairo_pdf_ft_font_write_be32 (font, head->Created[0]); + cairo_pdf_ft_font_write_be32 (font, head->Created[1]); + cairo_pdf_ft_font_write_be32 (font, head->Modified[0]); + cairo_pdf_ft_font_write_be32 (font, head->Modified[1]); + + cairo_pdf_ft_font_write_be16 (font, head->xMin); + cairo_pdf_ft_font_write_be16 (font, head->yMin); + cairo_pdf_ft_font_write_be16 (font, head->xMax); + cairo_pdf_ft_font_write_be16 (font, head->yMax); + + cairo_pdf_ft_font_write_be16 (font, head->Mac_Style); + cairo_pdf_ft_font_write_be16 (font, head->Lowest_Rec_PPEM); + + cairo_pdf_ft_font_write_be16 (font, head->Font_Direction); + cairo_pdf_ft_font_write_be16 (font, head->Index_To_Loc_Format); + cairo_pdf_ft_font_write_be16 (font, head->Glyph_Data_Format); + + return font->status; +} + +static int cairo_pdf_ft_font_write_hhea_table (cairo_pdf_ft_font_t *font, unsigned long tag) +{ + TT_HoriHeader *hhea; + + hhea = FT_Get_Sfnt_Table (font->face, ft_sfnt_hhea); + + cairo_pdf_ft_font_write_be32 (font, hhea->Version); + cairo_pdf_ft_font_write_be16 (font, hhea->Ascender); + cairo_pdf_ft_font_write_be16 (font, hhea->Descender); + cairo_pdf_ft_font_write_be16 (font, hhea->Line_Gap); + + cairo_pdf_ft_font_write_be16 (font, hhea->advance_Width_Max); + + cairo_pdf_ft_font_write_be16 (font, hhea->min_Left_Side_Bearing); + cairo_pdf_ft_font_write_be16 (font, hhea->min_Right_Side_Bearing); + cairo_pdf_ft_font_write_be16 (font, hhea->xMax_Extent); + cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Rise); + cairo_pdf_ft_font_write_be16 (font, hhea->caret_Slope_Run); + cairo_pdf_ft_font_write_be16 (font, hhea->caret_Offset); + + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 0); + cairo_pdf_ft_font_write_be16 (font, 0); + + cairo_pdf_ft_font_write_be16 (font, hhea->metric_Data_Format); + cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs); + + return font->status; +} + +static int +cairo_pdf_ft_font_write_hmtx_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + unsigned long entry_size; + short *p; + int i; + + for (i = 0; i < font->base.num_glyphs; i++) { + entry_size = 2 * sizeof (short); + p = cairo_pdf_ft_font_write (font, NULL, entry_size); + FT_Load_Sfnt_Table (font->face, TTAG_hmtx, + font->glyphs[i].parent_index * entry_size, + (FT_Byte *) p, &entry_size); + font->base.widths[i] = be16_to_cpu (p[0]); + } + + return font->status; +} + +static int +cairo_pdf_ft_font_write_loca_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + int i; + TT_Header *header; + + header = FT_Get_Sfnt_Table (font->face, ft_sfnt_head); + + if (header->Index_To_Loc_Format == 0) { + for (i = 0; i < font->base.num_glyphs + 1; i++) + cairo_pdf_ft_font_write_be16 (font, font->glyphs[i].location / 2); + } + else { + for (i = 0; i < font->base.num_glyphs + 1; i++) + cairo_pdf_ft_font_write_be32 (font, font->glyphs[i].location); + } + + return font->status; +} + +static int +cairo_pdf_ft_font_write_maxp_table (cairo_pdf_ft_font_t *font, + unsigned long tag) +{ + TT_MaxProfile *maxp; + + maxp = FT_Get_Sfnt_Table (font->face, ft_sfnt_maxp); + + cairo_pdf_ft_font_write_be32 (font, maxp->version); + cairo_pdf_ft_font_write_be16 (font, font->base.num_glyphs); + cairo_pdf_ft_font_write_be16 (font, maxp->maxPoints); + cairo_pdf_ft_font_write_be16 (font, maxp->maxContours); + cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositePoints); + cairo_pdf_ft_font_write_be16 (font, maxp->maxCompositeContours); + cairo_pdf_ft_font_write_be16 (font, maxp->maxZones); + cairo_pdf_ft_font_write_be16 (font, maxp->maxTwilightPoints); + cairo_pdf_ft_font_write_be16 (font, maxp->maxStorage); + cairo_pdf_ft_font_write_be16 (font, maxp->maxFunctionDefs); + cairo_pdf_ft_font_write_be16 (font, maxp->maxInstructionDefs); + cairo_pdf_ft_font_write_be16 (font, maxp->maxStackElements); + cairo_pdf_ft_font_write_be16 (font, maxp->maxSizeOfInstructions); + cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentElements); + cairo_pdf_ft_font_write_be16 (font, maxp->maxComponentDepth); + + return font->status; +} + +typedef struct table table_t; +struct table { + unsigned long tag; + int (*write) (cairo_pdf_ft_font_t *font, unsigned long tag); +}; + +static const table_t truetype_tables[] = { + { TTAG_cmap, cairo_pdf_ft_font_write_cmap_table }, + { TTAG_cvt, cairo_pdf_ft_font_write_generic_table }, + { TTAG_fpgm, cairo_pdf_ft_font_write_generic_table }, + { TTAG_glyf, cairo_pdf_ft_font_write_glyf_table }, + { TTAG_head, cairo_pdf_ft_font_write_head_table }, + { TTAG_hhea, cairo_pdf_ft_font_write_hhea_table }, + { TTAG_hmtx, cairo_pdf_ft_font_write_hmtx_table }, + { TTAG_loca, cairo_pdf_ft_font_write_loca_table }, + { TTAG_maxp, cairo_pdf_ft_font_write_maxp_table }, + { TTAG_name, cairo_pdf_ft_font_write_generic_table }, + { TTAG_prep, cairo_pdf_ft_font_write_generic_table }, +}; + +static cairo_status_t +cairo_pdf_ft_font_write_offset_table (cairo_pdf_ft_font_t *font) +{ + unsigned short search_range, entry_selector, range_shift; + int num_tables; + + num_tables = ARRAY_LENGTH (truetype_tables); + search_range = 1; + entry_selector = 0; + while (search_range * 2 <= num_tables) { + search_range *= 2; + entry_selector++; + } + search_range *= 16; + range_shift = num_tables * 16 - search_range; + + cairo_pdf_ft_font_write_be32 (font, SFNT_VERSION); + cairo_pdf_ft_font_write_be16 (font, num_tables); + cairo_pdf_ft_font_write_be16 (font, search_range); + cairo_pdf_ft_font_write_be16 (font, entry_selector); + cairo_pdf_ft_font_write_be16 (font, range_shift); + + cairo_pdf_ft_font_write (font, NULL, ARRAY_LENGTH (truetype_tables) * 16); + + return font->status; +} + +static unsigned long +cairo_pdf_ft_font_calculate_checksum (cairo_pdf_ft_font_t *font, + unsigned long start, unsigned long end) +{ + unsigned long *padded_end; + unsigned long *p; + unsigned long checksum; + char *data; + + checksum = 0; + data = _cairo_array_index (&font->output, 0); + p = (unsigned long *) (data + start); + padded_end = (unsigned long *) (data + ((end + 3) & ~3)); + while (p < padded_end) + checksum += *p++; + + return checksum; +} + +static void +cairo_pdf_ft_font_update_entry (cairo_pdf_ft_font_t *font, int index, unsigned long tag, + unsigned long start, unsigned long end) +{ + unsigned long *entry; + + entry = _cairo_array_index (&font->output, 12 + 16 * index); + entry[0] = cpu_to_be32 (tag); + entry[1] = cpu_to_be32 (cairo_pdf_ft_font_calculate_checksum (font, start, end)); + entry[2] = cpu_to_be32 (start); + entry[3] = cpu_to_be32 (end - start); +} + +static cairo_status_t +cairo_pdf_ft_font_generate (void *abstract_font, + const char **data, unsigned long *length) +{ + cairo_pdf_ft_font_t *font = abstract_font; + unsigned long start, end, next, checksum; + int i; + + if (cairo_pdf_ft_font_write_offset_table (font)) + return font->status; + + start = cairo_pdf_ft_font_align_output (font); + + for (i = 0; i < ARRAY_LENGTH (truetype_tables); i++) { + if (truetype_tables[i].write (font, truetype_tables[i].tag)) + break; + + end = _cairo_array_num_elements (&font->output); + next = cairo_pdf_ft_font_align_output (font); + cairo_pdf_ft_font_update_entry (font, i, truetype_tables[i].tag, + start, end); + start = next; + } + + checksum = + 0xb1b0afba - cairo_pdf_ft_font_calculate_checksum (font, 0, end); + *font->checksum_location = cpu_to_be32 (checksum); + + *data = _cairo_array_index (&font->output, 0); + *length = _cairo_array_num_elements (&font->output); + + return font->status; +} + +static int +cairo_pdf_ft_font_use_glyph (void *abstract_font, int glyph) +{ + cairo_pdf_ft_font_t *font = abstract_font; + + 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++; + } + + return font->parent_to_subset[glyph]; +} + +static cairo_pdf_font_backend_t cairo_pdf_ft_font_backend = { + cairo_pdf_ft_font_use_glyph, + cairo_pdf_ft_font_generate, + cairo_pdf_ft_font_destroy +}; + +/* PDF Generation */ + static unsigned int _cairo_pdf_document_new_object (cairo_pdf_document_t *document) { cairo_pdf_object_t object; object.offset = ftell (document->file); - /* FIXME: check return value */ - _cairo_array_append (&document->objects, &object, 1); + if (_cairo_array_append (&document->objects, &object, 1) == NULL) + return 0; return document->next_available_id++; } +static void +_cairo_pdf_document_update_object (cairo_pdf_document_t *document, + unsigned int id) +{ + cairo_pdf_object_t *object; + + object = _cairo_array_index (&document->objects, id - 1); + object->offset = ftell (document->file); +} + static void _cairo_pdf_surface_add_stream (cairo_pdf_surface_t *surface, cairo_pdf_stream_t *stream) @@ -243,6 +868,23 @@ _cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface, double alpha) return _cairo_array_num_elements (&surface->alphas) - 1; } +static void +_cairo_pdf_surface_add_font (cairo_pdf_surface_t *surface, unsigned int id) +{ + cairo_pdf_resource_t resource; + int i, num_fonts; + + num_fonts = _cairo_array_num_elements (&surface->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&surface->fonts, i, &resource); + if (resource.id == id) + return; + } + + resource.id = id; + _cairo_array_append (&surface->fonts, &resource, 1); +} + cairo_surface_t * cairo_pdf_surface_create (FILE *file, double width_inches, @@ -293,6 +935,7 @@ _cairo_pdf_surface_create_for_document (cairo_pdf_document_t *document, _cairo_array_init (&surface->patterns, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&surface->xobjects, sizeof (cairo_pdf_resource_t)); _cairo_array_init (&surface->alphas, sizeof (double)); + _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t)); return &surface->base; } @@ -313,6 +956,7 @@ _cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) _cairo_array_truncate (&surface->patterns, 0); _cairo_array_truncate (&surface->xobjects, 0); _cairo_array_truncate (&surface->alphas, 0); + _cairo_array_truncate (&surface->fonts, 0); } static cairo_surface_t * @@ -366,7 +1010,6 @@ _cairo_pdf_document_close_stream (cairo_pdf_document_t *document) FILE *file = document->file; long length; cairo_pdf_stream_t *stream; - cairo_pdf_object_t *object; stream = document->current_stream; if (stream == NULL) @@ -378,8 +1021,7 @@ _cairo_pdf_document_close_stream (cairo_pdf_document_t *document) "endstream\r\n" "endobj\r\n"); - object = _cairo_array_index (&document->objects, stream->length_id - 1); - object->offset = ftell(file); + _cairo_pdf_document_update_object (document, stream->length_id); fprintf (file, "%d 0 obj\r\n" " %ld\r\n" @@ -475,10 +1117,21 @@ _cairo_pdf_surface_set_repeat (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } -static unsigned long -compress_bound (unsigned long source_size) +static void * +compress_dup (const void *data, unsigned long data_size, + unsigned long *compressed_size) { - return source_size + (source_size >> 12) + (source_size >> 14) + 11; + void *compressed; + + /* Bound calculation taken from zlib. */ + *compressed_size = data_size + (data_size >> 12) + (data_size >> 14) + 11; + compressed = malloc (*compressed_size); + if (compressed == NULL) + return NULL; + + compress (compressed, compressed_size, data, data_size); + + return compressed; } static unsigned int @@ -498,13 +1151,6 @@ emit_image_data (cairo_pdf_document_t *document, if (rgb == NULL) return 0; - compressed_size = compress_bound (rgb_size); - compressed = malloc (compressed_size); - if (rgb == NULL) { - free (rgb); - return 0; - } - i = 0; for (y = 0; y < image->height; y++) { pixel = (pixman_bits_t *) (image->data + y * image->stride); @@ -516,7 +1162,11 @@ emit_image_data (cairo_pdf_document_t *document, } } - compress (compressed, &compressed_size, rgb, rgb_size); + compressed = compress_dup (rgb, rgb_size, &compressed_size); + if (compressed == NULL) { + free (rgb); + return 0; + } _cairo_pdf_document_close_stream (document); @@ -1033,6 +1683,87 @@ _cairo_pdf_surface_create_pattern (void *abstract_surface, return CAIRO_STATUS_SUCCESS; } +static cairo_pdf_font_t * +_cairo_pdf_document_get_font (cairo_pdf_document_t *document, + cairo_unscaled_font_t *unscaled_font, + cairo_font_scale_t *scale) +{ + FT_Face face; + cairo_font_t scaled_font; + cairo_pdf_font_t *font; + unsigned int num_fonts, i; + + num_fonts = _cairo_array_num_elements (&document->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&document->fonts, i, &font); + if (font->unscaled_font == unscaled_font) + return font; + } + + /* FIXME: Figure out here which font backend is in use and call + * the appropriate constructor. */ + + /* FIXME: Why do I have to pass a scaled font to get the FT_Face? */ + _cairo_font_init (&scaled_font, scale, unscaled_font); + face = cairo_ft_font_face (&scaled_font); + font = cairo_pdf_ft_font_create (document, face); + if (font == NULL) + return NULL; + + if (_cairo_array_append (&document->fonts, &font, 1) == NULL) { + cairo_pdf_font_destroy (font); + free (font); + return NULL; + } + + return font; +} + +static cairo_status_t +_cairo_pdf_surface_show_glyphs (cairo_unscaled_font_t *font, + cairo_font_scale_t *scale, + cairo_operator_t operator, + cairo_surface_t *source, + void *abstract_surface, + int source_x, + int source_y, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_pdf_document_t *document = surface->document; + FILE *file = document->file; + cairo_pdf_font_t *pdf_font; + int i, index; + + pdf_font = _cairo_pdf_document_get_font (document, font, scale); + if (pdf_font == NULL) + return CAIRO_STATUS_NO_MEMORY; + + _cairo_pdf_surface_ensure_stream (surface); + + fprintf (file, "0 0 0 rg BT /res%u 1 Tf", pdf_font->font_id); + for (i = 0; i < num_glyphs; i++) { + + index = cairo_pdf_font_use_glyph (pdf_font, glyphs[i].index); + + fprintf (file, + " %f %f %f %f %f %f Tm (%c) Tj", + scale->matrix[0][0], + scale->matrix[0][1], + scale->matrix[1][0], + -scale->matrix[1][1], + glyphs[i].x, + glyphs[i].y, + index); + } + fprintf (file, " ET\r\n"); + + _cairo_pdf_surface_add_font (surface, pdf_font->font_id); + + return CAIRO_STATUS_SUCCESS; +} + static const cairo_surface_backend_t cairo_pdf_surface_backend = { _cairo_pdf_surface_create_similar, _cairo_pdf_surface_destroy, @@ -1049,7 +1780,7 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { _cairo_pdf_surface_show_page, _cairo_pdf_surface_set_clip_region, _cairo_pdf_surface_create_pattern, - NULL, /* show_glyphs */ + _cairo_pdf_surface_show_glyphs }; static cairo_pdf_document_t * @@ -1080,6 +1811,8 @@ _cairo_pdf_document_create (FILE *file, document->pages_id = _cairo_pdf_document_new_object (document); + _cairo_array_init (&document->fonts, sizeof (cairo_pdf_font_t *)); + /* Document header */ fprintf (file, "%%PDF-1.4\r\n"); @@ -1108,14 +1841,10 @@ static void _cairo_pdf_document_write_pages (cairo_pdf_document_t *document) { FILE *file = document->file; - cairo_pdf_object_t *pages_object; unsigned int page_id; int num_pages, i; - pages_object = _cairo_array_index (&document->objects, - document->pages_id - 1); - pages_object->offset = ftell (file); - + _cairo_pdf_document_update_object (document, document->pages_id); fprintf (file, "%d 0 obj\r\n" "<< /Type /Pages\r\n" @@ -1141,6 +1870,111 @@ _cairo_pdf_document_write_pages (cairo_pdf_document_t *document) document->height_inches * document->y_ppi); } +static cairo_status_t +_cairo_pdf_document_write_fonts (cairo_pdf_document_t *document) +{ + FILE *file = document->file; + cairo_pdf_font_t *font; + int num_fonts, i, j; + const char *data; + char *compressed; + unsigned long data_size, compressed_size; + unsigned int stream_id, descriptor_id; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + num_fonts = _cairo_array_num_elements (&document->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&document->fonts, i, &font); + + status = cairo_pdf_font_generate (font, &data, &data_size); + if (status) + goto fail; + + compressed = compress_dup (data, data_size, &compressed_size); + if (compressed == NULL) { + status = CAIRO_STATUS_NO_MEMORY; + goto fail; + } + + stream_id = _cairo_pdf_document_new_object (document); + fprintf (file, + "%d 0 obj\r\n" + "<< /Filter /FlateDecode\r\n" + " /Length %lu\r\n" + " /Length1 %lu\r\n" + ">>\r\n" + "stream\r\n", + stream_id, + compressed_size, + data_size); + fwrite (compressed, 1, compressed_size, file); + fprintf (file, + "\r\n" + "endstream\r\n" + "endobj\r\n"); + free (compressed); + + descriptor_id = _cairo_pdf_document_new_object (document); + fprintf (file, + "%d 0 obj\r\n" + "<< /Type /FontDescriptor\r\n" + " /FontName /%s\r\n" + " /Flags 32\r\n" + " /FontBBox [ %ld %ld %ld %ld ]\r\n" + " /ItalicAngle 0\r\n" + " /Ascent %ld\r\n" + " /Descent %ld\r\n" + " /CapHeight 500\r\n" + " /StemV 80\r\n" + " /StemH 80\r\n" + " /FontFile2 %u 0 R\r\n" + ">>\r\n" + "endobj\r\n", + descriptor_id, + font->base_font, + font->x_min, + font->y_min, + font->x_max, + font->y_max, + font->ascent, + font->descent, + stream_id); + + _cairo_pdf_document_update_object (document, font->font_id); + fprintf (file, + "%d 0 obj\r\n" + "<< /Type /Font\r\n" + " /Subtype /TrueType\r\n" + " /BaseFont /%s\r\n" + " /FirstChar 0\r\n" + " /LastChar %d\r\n" + " /FontDescriptor %d 0 R\r\n" + " /Widths ", + font->font_id, + font->base_font, + font->num_glyphs, + descriptor_id); + + fprintf (file, + "["); + + for (j = 0; j < font->num_glyphs; j++) + fprintf (file, + " %d", + font->widths[j]); + + fprintf (file, + " ]\r\n" + ">>\r\n" + "endobj\r\n"); + + fail: + cairo_pdf_ft_font_destroy (font); + } + + return status; +} + static unsigned int _cairo_pdf_document_write_catalog (cairo_pdf_document_t *document) { @@ -1203,6 +2037,7 @@ _cairo_pdf_document_destroy (cairo_pdf_document_t *document) _cairo_pdf_document_close_stream (document); _cairo_pdf_document_write_pages (document); + _cairo_pdf_document_write_fonts (document); info_id = _cairo_pdf_document_write_info (document); catalog_id = _cairo_pdf_document_write_catalog (document); offset = _cairo_pdf_document_write_xref (document); @@ -1260,6 +2095,21 @@ _cairo_pdf_document_add_page (cairo_pdf_document_t *document, " ]\r\n" " /Resources <<\r\n"); + num_resources = _cairo_array_num_elements (&surface->fonts); + if (num_resources > 0) { + fprintf (file, + " /Font <<"); + + for (i = 0; i < num_resources; i++) { + res = _cairo_array_index (&surface->fonts, i); + fprintf (file, + " /res%d %d 0 R", + res->id, res->id); + } + + fprintf (file, + " >>\r\n"); + } num_alphas = _cairo_array_num_elements (&surface->alphas); if (num_alphas > 0) { diff --git a/src/cairo_xlib_surface.c b/src/cairo_xlib_surface.c index 383bf247c..1867d5985 100644 --- a/src/cairo_xlib_surface.c +++ b/src/cairo_xlib_surface.c @@ -697,7 +697,7 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, cairo_font_scale_t *scale, cairo_operator_t operator, cairo_surface_t *source, - cairo_surface_t *surface, + void *abstract_surface, int source_x, int source_y, const cairo_glyph_t *glyphs, @@ -1250,14 +1250,14 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, cairo_font_scale_t *scale, cairo_operator_t operator, cairo_surface_t *source, - cairo_surface_t *surface, + void *abstract_surface, int source_x, int source_y, const cairo_glyph_t *glyphs, int num_glyphs) { unsigned int elt_size; - cairo_xlib_surface_t *self = (cairo_xlib_surface_t *) surface; + cairo_xlib_surface_t *self = abstract_surface; cairo_image_surface_t *tmp = NULL; cairo_xlib_surface_t *src = NULL; glyphset_cache_t *g; @@ -1278,7 +1278,7 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, } /* prep the source surface. */ - if (source->backend == surface->backend) { + if (source->backend == self->base.backend) { src = (cairo_xlib_surface_t *) source; } else { @@ -1287,7 +1287,7 @@ _cairo_xlib_surface_show_glyphs (cairo_unscaled_font_t *font, goto FREE_ENTRIES; src = (cairo_xlib_surface_t *) - _cairo_surface_create_similar_scratch (surface, self->format, 1, + _cairo_surface_create_similar_scratch (&self->base, self->format, 1, tmp->width, tmp->height); if (src == NULL) diff --git a/src/cairoint.h b/src/cairoint.h index fd8ce09dd..d10ed9523 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -276,8 +276,9 @@ _cairo_array_grow_by (cairo_array_t *array, int additional); cairo_private void _cairo_array_truncate (cairo_array_t *array, int length); -cairo_private cairo_status_t -_cairo_array_append (cairo_array_t *array, void *elements, int num_elements); +cairo_private void * +_cairo_array_append (cairo_array_t *array, + const void *elements, int num_elements); cairo_private void * _cairo_array_index (cairo_array_t *array, int index); @@ -606,7 +607,7 @@ typedef struct _cairo_surface_backend { cairo_font_scale_t *scale, cairo_operator_t operator, cairo_surface_t *source, - cairo_surface_t *surface, + void *surface, int source_x, int source_y, const cairo_glyph_t *glyphs,