diff options
author | Adrian Johnson <ajohnson@redneon.com> | 2021-07-28 20:27:45 +0000 |
---|---|---|
committer | Adrian Johnson <ajohnson@redneon.com> | 2021-07-28 20:27:45 +0000 |
commit | 4e3f6bf0c2eb4ff5065bb18fb846019d4fef597f (patch) | |
tree | 947481c6988b1f51d950b812c7e346adee63f28b /src/cairo-pdf-surface.c | |
parent | d60e3f3501f39b0487179601fa9dc4b703a49c93 (diff) | |
parent | fb6f3eb32efd372e84460720e22970b7fef571fe (diff) | |
download | cairo-4e3f6bf0c2eb4ff5065bb18fb846019d4fef597f.tar.gz |
Merge branch 'pdf-object-streams' into 'master'
pdf: use cross-reference stream for PDF >= 1.5
See merge request cairo/cairo!197
Diffstat (limited to 'src/cairo-pdf-surface.c')
-rw-r--r-- | src/cairo-pdf-surface.c | 559 |
1 files changed, 481 insertions, 78 deletions
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 7a13ecf57..2600c3595 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -81,6 +81,7 @@ * - PDF Stream * - Content Stream * - Group Stream + * - Object stream * * Calling _cairo_output_stream_printf (surface->output, ...) will * write to the currently open stream. @@ -110,6 +111,16 @@ * memory until it is closed. This allows some optimization such as * including the Resource dictionary and stream length inside the * XObject instead of using an indirect object. + * + * Object Stream (PDF 1.5) + * An Object Stream may be opened and closed with the following functions: + * _cairo_pdf_surface_open_object_stream () + * _cairo_pdf_surface_close_object_stream () + * + * An Object Stream contains one or more objects compressed into a stream. + * Only non stream objects are permitted. When emitting objects intended for + * the Object Stream, enclose the emit object operation with + * _cairo_pdf_surface_object_begin()/_cairo_pdf_surface_object_end(). */ /** @@ -222,10 +233,29 @@ static const char *_cairo_pdf_supported_mime_types[] = NULL }; +/* PDF cross-reference stream types */ +typedef enum { + PDF_OBJECT_FREE = 0, + PDF_OBJECT_UNCOMPRESSED = 1, + PDF_OBJECT_COMPRESSED = 2, +} cairo_pdf_object_type_t; + typedef struct _cairo_pdf_object { - long offset; + cairo_pdf_object_type_t type; + union { + long offset; /* type == PDF_OBJECT_UNCOMPRESSED */ + struct compressed_obj { /* type == PDF_OBJECT_COMPRESSED */ + cairo_pdf_resource_t xref_stream; + int index; + } compressed_obj; + } u; } cairo_pdf_object_t; +typedef struct _cairo_xref_stream_object { + cairo_pdf_resource_t resource; + long offset; +} cairo_xref_stream_object_t; + typedef struct _cairo_pdf_font { unsigned int font_id; unsigned int subset_id; @@ -276,21 +306,26 @@ _cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface, static cairo_int_status_t _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); -static void +static cairo_int_status_t _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface); -static cairo_pdf_resource_t -_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface); +static cairo_int_status_t +_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t catalog); static long _cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface); static cairo_int_status_t -_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface, - cairo_bool_t finish); +_cairo_pdf_surface_write_xref_stream (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t xref_res, + cairo_pdf_resource_t root_res, + cairo_pdf_resource_t info_res, + long *xref_offset); static cairo_int_status_t -_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface); +_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface, + cairo_bool_t finish); static cairo_int_status_t _cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface); @@ -308,7 +343,11 @@ _cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface) cairo_int_status_t status; cairo_pdf_object_t object; - object.offset = _cairo_output_stream_get_position (surface->output); + /* Default to Uncompressed. If this object is used with + * _cairo_pdf_surface_object_begin() and Object Streams are + * enabled it will be changed to Compressed. */ + object.type = PDF_OBJECT_UNCOMPRESSED; + object.u.offset = _cairo_output_stream_get_position (surface->output); status = _cairo_array_append (&surface->objects, &object); if (unlikely (status)) { @@ -329,7 +368,7 @@ _cairo_pdf_surface_update_object (cairo_pdf_surface_t *surface, cairo_pdf_object_t *object; object = _cairo_array_index (&surface->objects, resource.id - 1); - object->offset = _cairo_output_stream_get_position (surface->output); + object->u.offset = _cairo_output_stream_get_position (surface->output); } static void @@ -459,12 +498,15 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface->struct_tree_root.id = 0; surface->pdf_version = CAIRO_PDF_VERSION_1_5; - surface->compress_content = TRUE; + surface->compress_streams = TRUE; surface->pdf_stream.active = FALSE; surface->pdf_stream.old_output = NULL; surface->group_stream.active = FALSE; surface->group_stream.stream = NULL; surface->group_stream.mem_stream = NULL; + surface->object_stream.active = FALSE; + surface->object_stream.stream = NULL; + _cairo_array_init (&surface->object_stream.objects, sizeof (cairo_xref_stream_object_t)); surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; @@ -506,7 +548,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, surface->thumbnail_image = NULL; if (getenv ("CAIRO_DEBUG_PDF") != NULL) - surface->compress_content = FALSE; + surface->compress_streams = FALSE; surface->paginated_surface = _cairo_paginated_surface_create ( &surface->base, @@ -1954,7 +1996,7 @@ _cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface, resource.id, _cairo_memory_stream_length (mem_stream)); - if (surface->compress_content) { + if (surface->compress_streams) { _cairo_output_stream_printf (surface->output, " /Filter /FlateDecode\n"); } @@ -2003,7 +2045,7 @@ _cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface, surface->group_stream.mem_stream = _cairo_memory_stream_create (); - if (surface->compress_content) { + if (surface->compress_streams) { surface->group_stream.stream = _cairo_deflate_stream_create (surface->group_stream.mem_stream); } else { @@ -2057,7 +2099,7 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, if (unlikely (status)) return status; - if (surface->compress_content) { + if (surface->compress_streams) { status = _cairo_output_stream_destroy (surface->group_stream.stream); surface->group_stream.stream = NULL; @@ -2087,6 +2129,182 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, } static cairo_int_status_t +_cairo_pdf_surface_open_object_stream (cairo_pdf_surface_t *surface) +{ + if (surface->pdf_version < CAIRO_PDF_VERSION_1_5) { + /* Object streams not supported. All objects will be written + * directly to the file. */ + assert (surface->pdf_stream.active == FALSE); + assert (surface->group_stream.active == FALSE); + surface->object_stream.stream = surface->output; + } else { + surface->object_stream.resource = _cairo_pdf_surface_new_object (surface); + if (surface->object_stream.resource.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_array_truncate (&surface->object_stream.objects, 0); + surface->object_stream.stream = _cairo_memory_stream_create (); + surface->object_stream.active = TRUE; + } + return _cairo_output_stream_get_status (surface->object_stream.stream); +} + +cairo_int_status_t +_cairo_pdf_surface_object_begin (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t resource) +{ + cairo_xref_stream_object_t xref_obj; + cairo_pdf_object_t *object; + cairo_int_status_t status; + + if (surface->object_stream.active) { + xref_obj.resource = resource; + xref_obj.offset = _cairo_output_stream_get_position (surface->object_stream.stream); + status = _cairo_array_append (&surface->object_stream.objects, &xref_obj); + if (unlikely (status)) + return status; + + object = _cairo_array_index (&surface->objects, resource.id - 1); + object->type = PDF_OBJECT_COMPRESSED; + object->u.compressed_obj.xref_stream = surface->object_stream.resource; + object->u.compressed_obj.index = _cairo_array_num_elements (&surface->object_stream.objects) - 1; + + } else { + _cairo_pdf_surface_update_object (surface, resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n", + resource.id); + } + return CAIRO_INT_STATUS_SUCCESS; +} + +void +_cairo_pdf_surface_object_end (cairo_pdf_surface_t *surface) +{ + if (!surface->object_stream.active) { + _cairo_output_stream_printf (surface->output, + "endobj\n"); + } +} + +static int _cairo_xref_stream_object_compare (const void *a, const void *b) +{ + const cairo_xref_stream_object_t *a_obj = a; + const cairo_xref_stream_object_t *b_obj = b; + + if (a_obj->offset < b_obj->offset) + return -1; + else if (a_obj->offset > b_obj->offset) + return 1; + else + return 0; +} + +static cairo_int_status_t +_cairo_pdf_surface_close_object_stream (cairo_pdf_surface_t *surface) +{ + int i, num_objects; + cairo_xref_stream_object_t *xref_obj; + long start_pos, length; + cairo_output_stream_t *index_stream; + cairo_output_stream_t *deflate_stream; + cairo_pdf_resource_t length_res; + cairo_int_status_t status; + cairo_pdf_object_t *object; + + if (!surface->object_stream.active) { + surface->object_stream.stream = NULL; + return CAIRO_INT_STATUS_SUCCESS; + } + + num_objects = _cairo_array_num_elements (&surface->object_stream.objects); + if (num_objects == 0) { + object = _cairo_array_index (&surface->objects, surface->object_stream.resource.id - 1); + object->type = PDF_OBJECT_FREE; + return CAIRO_INT_STATUS_SUCCESS; + } + + index_stream = _cairo_memory_stream_create (); + /* PDF requires the object id/offset pairs to be sorted by offset. */ + _cairo_array_sort (&surface->object_stream.objects, _cairo_xref_stream_object_compare); + for (i = 0; i < num_objects; i++) { + xref_obj = _cairo_array_index (&surface->object_stream.objects, i); + _cairo_output_stream_printf (index_stream, + "%d %ld\n", + xref_obj->resource.id, + xref_obj->offset); + } + + length_res = _cairo_pdf_surface_new_object (surface); + if (length_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_pdf_surface_update_object (surface, surface->object_stream.resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /ObjStm\n" + " /Length %d 0 R\n" + " /N %d\n" + " /First %d\n", + surface->object_stream.resource.id, + length_res.id, + num_objects, + _cairo_memory_stream_length (index_stream)); + + if (surface->compress_streams) { + _cairo_output_stream_printf (surface->output, + " /Filter /FlateDecode\n"); + } + + _cairo_output_stream_printf (surface->output, + ">>\n" + "stream\n"); + + start_pos = _cairo_output_stream_get_position (surface->output); + if (surface->compress_streams) { + deflate_stream = _cairo_deflate_stream_create (surface->output); + _cairo_memory_stream_copy (index_stream, deflate_stream); + _cairo_memory_stream_copy (surface->object_stream.stream, deflate_stream); + status = _cairo_output_stream_destroy (deflate_stream); + if (unlikely (status)) + return status; + + length = _cairo_output_stream_get_position (surface->output) - start_pos; + } else { + _cairo_memory_stream_copy (index_stream, surface->output); + _cairo_memory_stream_copy (surface->object_stream.stream, surface->output); + length = _cairo_output_stream_get_position (surface->output) - start_pos; + } + + _cairo_output_stream_printf (surface->output, + "\n" + "endstream\n" + "endobj\n"); + + _cairo_pdf_surface_update_object (surface, + length_res); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + " %ld\n" + "endobj\n", + length_res.id, + length); + + status = _cairo_output_stream_destroy (index_stream); + if (unlikely (status)) + return status; + + status = _cairo_output_stream_destroy (surface->object_stream.stream); + if (unlikely (status)) + return status; + + surface->object_stream.stream = NULL; + surface->object_stream.active = FALSE; + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_int_status_t _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, const cairo_box_double_t *bbox, cairo_pdf_resource_t *resource, @@ -2109,7 +2327,7 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_open_stream (surface, resource, - surface->compress_content, + surface->compress_streams, " /Type /XObject\n" " /Subtype /Form\n" " /BBox [ %f %f %f %f ]\n" @@ -2129,7 +2347,7 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_open_stream (surface, resource, - surface->compress_content, + surface->compress_streams, " /Type /XObject\n" " /Subtype /Form\n" " /BBox [ %f %f %f %f ]\n" @@ -2144,7 +2362,7 @@ _cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_open_stream (surface, resource, - surface->compress_content, + surface->compress_streams, NULL); _cairo_output_stream_printf (surface->output, "1 0 0 -1 0 %f cm\n", @@ -2212,9 +2430,14 @@ _cairo_pdf_surface_finish (void *abstract_surface) cairo_pdf_source_surface_t doc_surface; cairo_pdf_jbig2_global_t *global; char *label; + cairo_pdf_resource_t xref_res; _cairo_pdf_surface_clear (surface); + status = _cairo_pdf_surface_open_object_stream (surface); + if (unlikely (status)) + return status; + /* Emit unbounded surfaces */ _cairo_pdf_surface_write_patterns_and_smask_groups (surface, TRUE); @@ -2222,28 +2445,46 @@ _cairo_pdf_surface_finish (void *abstract_surface) if (status == CAIRO_STATUS_SUCCESS) status = _cairo_pdf_surface_emit_font_subsets (surface); - _cairo_pdf_surface_write_pages (surface); + status = _cairo_pdf_surface_write_pages (surface); + if (unlikely (status)) + return status; status = _cairo_pdf_interchange_write_document_objects (surface); if (unlikely (status)) return status; - catalog = _cairo_pdf_surface_write_catalog (surface); - if (catalog.id == 0 && status == CAIRO_STATUS_SUCCESS) - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + catalog = _cairo_pdf_surface_new_object (surface); + if (catalog.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); - offset = _cairo_pdf_surface_write_xref (surface); + status = _cairo_pdf_surface_write_catalog (surface, catalog); + if (unlikely (status)) + return status; - _cairo_output_stream_printf (surface->output, - "trailer\n" - "<< /Size %d\n" - " /Root %d 0 R\n" - " /Info %d 0 R\n" - ">>\n", - surface->next_available_resource.id, - catalog.id, - surface->docinfo_res.id); + status = _cairo_pdf_surface_close_object_stream (surface); + if (unlikely (status)) + return status; + if (surface->pdf_version >= CAIRO_PDF_VERSION_1_5) + { + xref_res = _cairo_pdf_surface_new_object (surface); + status = _cairo_pdf_surface_write_xref_stream (surface, + xref_res, + catalog, + surface->docinfo_res, + &offset); + } else { + offset = _cairo_pdf_surface_write_xref (surface); + _cairo_output_stream_printf (surface->output, + "trailer\n" + "<< /Size %d\n" + " /Root %d 0 R\n" + " /Info %d 0 R\n" + ">>\n", + surface->next_available_resource.id, + catalog.id, + surface->docinfo_res.id); + } _cairo_output_stream_printf (surface->output, "startxref\n" "%ld\n" @@ -2290,6 +2531,7 @@ _cairo_pdf_surface_finish (void *abstract_surface) _cairo_array_fini (&surface->alpha_linear_functions); _cairo_array_fini (&surface->page_patterns); _cairo_array_fini (&surface->page_surfaces); + _cairo_array_fini (&surface->object_stream.objects); size = _cairo_array_num_elements (&surface->doc_surfaces); for (i = 0; i < size; i++) { @@ -4223,7 +4465,7 @@ cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface, } status = _cairo_pdf_surface_open_stream (surface, NULL, - surface->compress_content, + surface->compress_streams, " /Type /XObject\n" " /Subtype /Form\n" " /FormType 1\n" @@ -5125,34 +5367,38 @@ _cairo_pdf_surface_get_font_options (void *abstract_surface, _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF); } -static void +static cairo_int_status_t _cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface) { cairo_pdf_resource_t page; int num_pages, i; + cairo_int_status_t status; - _cairo_pdf_surface_update_object (surface, surface->pages_resource); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" + status = _cairo_pdf_surface_object_begin (surface, surface->pages_resource); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, "<< /Type /Pages\n" - " /Kids [ ", - surface->pages_resource.id); + " /Kids [ "); num_pages = _cairo_array_num_elements (&surface->pages); for (i = 0; i < num_pages; i++) { _cairo_array_copy_element (&surface->pages, i, &page); - _cairo_output_stream_printf (surface->output, "%d 0 R ", page.id); + _cairo_output_stream_printf (surface->object_stream.stream, "%d 0 R ", page.id); } - _cairo_output_stream_printf (surface->output, "]\n"); - _cairo_output_stream_printf (surface->output, " /Count %d\n", num_pages); + _cairo_output_stream_printf (surface->object_stream.stream, "]\n"); + _cairo_output_stream_printf (surface->object_stream.stream, " /Count %d\n", num_pages); /* TODO: Figure out which other defaults to be inherited by /Page * objects. */ - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); + _cairo_output_stream_printf (surface->object_stream.stream, + ">>\n"); + _cairo_pdf_surface_object_end (surface); + + return CAIRO_INT_STATUS_SUCCESS; } cairo_int_status_t @@ -5338,7 +5584,7 @@ _cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, status = _cairo_pdf_surface_open_stream (surface, NULL, - surface->compress_content, + surface->compress_streams, NULL); if (unlikely (status)) return status; @@ -6221,7 +6467,7 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, for (i = 0; i < font_subset->num_glyphs; i++) { status = _cairo_pdf_surface_open_stream (surface, NULL, - surface->compress_content, + surface->compress_streams, NULL); if (unlikely (status)) break; @@ -6436,55 +6682,54 @@ BAIL: return status; } -static cairo_pdf_resource_t -_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface) +static cairo_int_status_t +_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t catalog) { - cairo_pdf_resource_t catalog; + cairo_status_t status; - catalog = _cairo_pdf_surface_new_object (surface); - if (catalog.id == 0) - return catalog; + status = _cairo_pdf_surface_object_begin (surface, catalog); + if (unlikely (status)) + return status; - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" + _cairo_output_stream_printf (surface->object_stream.stream, "<< /Type /Catalog\n" " /Pages %d 0 R\n", - catalog.id, surface->pages_resource.id); if (surface->struct_tree_root.id != 0) { - _cairo_output_stream_printf (surface->output, + _cairo_output_stream_printf (surface->object_stream.stream, " /StructTreeRoot %d 0 R\n", surface->struct_tree_root.id); if (surface->tagged) { - _cairo_output_stream_printf (surface->output, + _cairo_output_stream_printf (surface->object_stream.stream, " /MarkInfo << /Marked true >>\n"); } } if (surface->outlines_dict_res.id != 0) { - _cairo_output_stream_printf (surface->output, + _cairo_output_stream_printf (surface->object_stream.stream, " /Outlines %d 0 R\n", surface->outlines_dict_res.id); } if (surface->page_labels_res.id != 0) { - _cairo_output_stream_printf (surface->output, + _cairo_output_stream_printf (surface->object_stream.stream, " /PageLabels %d 0 R\n", surface->page_labels_res.id); } if (surface->names_dict_res.id != 0) { - _cairo_output_stream_printf (surface->output, + _cairo_output_stream_printf (surface->object_stream.stream, " /Names %d 0 R\n", surface->names_dict_res.id); } - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); + _cairo_output_stream_printf (surface->object_stream.stream, + ">>\n"); + _cairo_pdf_surface_object_end (surface); - return catalog; + return status; } static long @@ -6507,7 +6752,7 @@ _cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface) "0000000000 65535 f \n"); for (i = 0; i < num_objects; i++) { object = _cairo_array_index (&surface->objects, i); - snprintf (buffer, sizeof buffer, "%010ld", object->offset); + snprintf (buffer, sizeof buffer, "%010ld", object->u.offset); _cairo_output_stream_printf (surface->output, "%s 00000 n \n", buffer); } @@ -6515,6 +6760,158 @@ _cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface) return offset; } +static void +_cairo_write_xref_stream_entry (cairo_output_stream_t *stream, + int id, + int type, + int field2_size, + long field2, + int field3, + cairo_bool_t write_as_comments) +{ + char buf[20]; + int i; + + if (write_as_comments) { + _cairo_output_stream_printf (stream, "%% %5d %2d %10ld %d\n", id, type, field2, field3); + } else { + /* Each field is big endian */ + buf[0] = type; /* field 1 */ + for (i = field2_size - 1; i >= 0; i--) { + buf[i + 1] = field2 & 0xff; + field2 >>= 8; + } + buf[field2_size + 1] = field3 >> 8; + buf[field2_size + 2] = field3 & 0xff; + _cairo_output_stream_write (stream, buf, field2_size + 3); + } +} + +static void +_cairo_write_xref_stream_entrys (cairo_pdf_surface_t *surface, + cairo_output_stream_t *stream, + int field2_size, + cairo_bool_t write_as_comments) +{ + cairo_pdf_object_t *object; + int num_objects, i; + + /* PDF requires this to be first entry */ + _cairo_write_xref_stream_entry (stream, + 0, + PDF_OBJECT_FREE, + field2_size, + 0, /* next free object number */ + 0xffff, /* next generation number */ + write_as_comments); + + num_objects = _cairo_array_num_elements (&surface->objects); + for (i = 0; i < num_objects; i++) { + object = _cairo_array_index (&surface->objects, i); + if (object->type == PDF_OBJECT_UNCOMPRESSED) { + _cairo_write_xref_stream_entry (stream, + i + 1, + object->type, + field2_size, + object->u.offset, + 0, /* generation number */ + write_as_comments); + } else if (object->type == PDF_OBJECT_COMPRESSED) { + _cairo_write_xref_stream_entry (stream, + i + 1, + object->type, + field2_size, + object->u.compressed_obj.xref_stream.id, + object->u.compressed_obj.index, + write_as_comments); + } else { + _cairo_write_xref_stream_entry (stream, + i + 1, + PDF_OBJECT_FREE, + field2_size, + 0, + 0xffff, + write_as_comments); + } + } +} + +static cairo_int_status_t +_cairo_pdf_surface_write_xref_stream (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t xref_res, + cairo_pdf_resource_t root_res, + cairo_pdf_resource_t info_res, + long *xref_offset) +{ + cairo_output_stream_t *mem_stream; + cairo_output_stream_t *xref_stream; + long offset; + int offset_bytes; + cairo_status_t status; + + *xref_offset = _cairo_output_stream_get_position (surface->output); + + /* Find the minimum number of bytes required to represent offsets in the generated file (up to this point). */ + offset_bytes = 0; + offset = *xref_offset; + while (offset > 0) { + offset >>= 8; + offset_bytes++; + } + + mem_stream = _cairo_memory_stream_create (); + xref_stream = _cairo_deflate_stream_create (mem_stream); + _cairo_write_xref_stream_entrys (surface, xref_stream, offset_bytes, FALSE); + + status = _cairo_output_stream_destroy (xref_stream); + if (unlikely (status)) + return status; + + _cairo_pdf_surface_update_object (surface, xref_res); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /XRef\n" + " /Length %d\n" + " /Filter /FlateDecode\n" + " /Size %d\n" + " /W [1 %d 2]\n" + " /Root %d 0 R\n" + " /Info %d 0 R\n" + ">>\n", + xref_res.id, + _cairo_memory_stream_length (mem_stream), + surface->next_available_resource.id, + offset_bytes, + root_res.id, + info_res.id); + + if (!surface->compress_streams) { + /* Adobe Reader requires xref streams to be flate encoded (PDF + * Reference 1.7, implementation note 20). This means + * compression must always be enabled on this stream. To + * facilitate debugging when compress_stream is disabled, emit + * a human readable format of the xref stream as PDF comments. + */ + _cairo_output_stream_printf (surface->output, + "%% id type offset/obj gen/index\n"); + _cairo_write_xref_stream_entrys (surface, surface->output, offset_bytes, TRUE); + } + + _cairo_output_stream_printf (surface->output, + "stream\n"); + _cairo_memory_stream_copy (mem_stream, surface->output); + status = _cairo_output_stream_destroy (mem_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "\n" + "endstream\n" + "endobj\n"); + + return _cairo_output_stream_get_status (surface->output); +} + static cairo_int_status_t _cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, cairo_pdf_smask_group_t *group) @@ -6868,6 +7265,10 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) cairo_int_status_t status; unsigned int i, len, page_num, num_annots; + status = _cairo_pdf_surface_open_object_stream (surface); + if (unlikely (status)) + return status; + status = _cairo_pdf_interchange_write_page_objects (surface); if (unlikely (status)) return status; @@ -6936,9 +7337,12 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) page_num = _cairo_array_num_elements (&surface->pages); page = _cairo_array_index (&surface->pages, page_num - 1); - _cairo_pdf_surface_update_object (surface, *page); - _cairo_output_stream_printf (surface->output, - "%d 0 obj\n" + + status = _cairo_pdf_surface_object_begin (surface, *page); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, "<< /Type /Page %% %d\n" " /Parent %d 0 R\n" " /MediaBox [ 0 0 %f %f ]\n" @@ -6950,7 +7354,6 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) " /CS /DeviceRGB\n" " >>\n" " /Resources %d 0 R\n", - page->id, page_num, surface->pages_resource.id, surface->width, @@ -6959,39 +7362,39 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) surface->content_resources.id); if (surface->page_parent_tree >= 0) { - _cairo_output_stream_printf (surface->output, + _cairo_output_stream_printf (surface->object_stream.stream, " /StructParents %d\n", surface->page_parent_tree); } num_annots = _cairo_array_num_elements (&surface->page_annots); if (num_annots > 0) { - _cairo_output_stream_printf (surface->output, + _cairo_output_stream_printf (surface->object_stream.stream, " /Annots [ "); for (i = 0; i < num_annots; i++) { _cairo_array_copy_element (&surface->page_annots, i, &res); - _cairo_output_stream_printf (surface->output, + _cairo_output_stream_printf (surface->object_stream.stream, "%d 0 R ", res.id); } - _cairo_output_stream_printf (surface->output, "]\n"); + _cairo_output_stream_printf (surface->object_stream.stream, "]\n"); } if (thumbnail_res.id) { - _cairo_output_stream_printf (surface->output, + _cairo_output_stream_printf (surface->object_stream.stream, " /Thumb %d 0 R\n", thumbnail_res.id); } - _cairo_output_stream_printf (surface->output, - ">>\n" - "endobj\n"); + _cairo_output_stream_printf (surface->object_stream.stream, + ">>\n"); + _cairo_pdf_surface_object_end (surface); status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface, FALSE); if (unlikely (status)) return status; - return CAIRO_STATUS_SUCCESS; + return _cairo_pdf_surface_close_object_stream (surface); } static cairo_int_status_t |