summaryrefslogtreecommitdiff
path: root/src/cairo-pdf-surface.c
diff options
context:
space:
mode:
authorAdrian Johnson <ajohnson@redneon.com>2021-07-28 20:27:45 +0000
committerAdrian Johnson <ajohnson@redneon.com>2021-07-28 20:27:45 +0000
commit4e3f6bf0c2eb4ff5065bb18fb846019d4fef597f (patch)
tree947481c6988b1f51d950b812c7e346adee63f28b /src/cairo-pdf-surface.c
parentd60e3f3501f39b0487179601fa9dc4b703a49c93 (diff)
parentfb6f3eb32efd372e84460720e22970b7fef571fe (diff)
downloadcairo-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.c559
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