[cairo-commit] 6 commits - src/cairo-array.c src/cairo-array-private.h src/cairo-pdf-interchange.c src/cairo-pdf-surface.c src/cairo-pdf-surface-private.h test/pdf-tagged-text.c
GitLab Mirror
gitlab-mirror at kemper.freedesktop.org
Wed Jul 28 20:27:47 UTC 2021
src/cairo-array-private.h | 3
src/cairo-array.c | 6
src/cairo-pdf-interchange.c | 319 +++++++++++++---------
src/cairo-pdf-surface-private.h | 17 +
src/cairo-pdf-surface.c | 559 ++++++++++++++++++++++++++++++++++------
test/pdf-tagged-text.c | 42 ++-
6 files changed, 727 insertions(+), 219 deletions(-)
New commits:
commit 4e3f6bf0c2eb4ff5065bb18fb846019d4fef597f
Merge: d60e3f350 fb6f3eb32
Author: Adrian Johnson <ajohnson at redneon.com>
Date: Wed Jul 28 20:27:45 2021 +0000
Merge branch 'pdf-object-streams' into 'master'
pdf: use cross-reference stream for PDF >= 1.5
See merge request cairo/cairo!197
commit fb6f3eb32efd372e84460720e22970b7fef571fe
Author: Adrian Johnson <ajohnson at redneon.com>
Date: Wed Jul 28 06:18:01 2021 +0930
pdf-tagged-text: generate both 1.4 and 1.5 PDFs
diff --git a/test/pdf-tagged-text.c b/test/pdf-tagged-text.c
index 62233a7f3..5bfb55886 100644
--- a/test/pdf-tagged-text.c
+++ b/test/pdf-tagged-text.c
@@ -510,23 +510,27 @@ check_created_pdf(cairo_test_context_t *ctx, const char* filename)
}
static cairo_test_status_t
-preamble (cairo_test_context_t *ctx)
+create_pdf (cairo_test_context_t *ctx, cairo_bool_t check_output)
{
cairo_surface_t *surface;
cairo_t *cr;
cairo_status_t status, status2;
cairo_test_status_t result;
+ cairo_pdf_version_t version;
char *filename;
const char *path = cairo_test_mkdir (CAIRO_TEST_OUTPUT_DIR) ? CAIRO_TEST_OUTPUT_DIR : ".";
- if (! cairo_test_is_target_enabled (ctx, "pdf"))
- return CAIRO_TEST_UNTESTED;
+ /* check_created_pdf() only works with version 1.4. In version 1.5
+ * the text that is searched for is compressed. */
+ version = check_output ? CAIRO_PDF_VERSION_1_4 : CAIRO_PDF_VERSION_1_5;
- xasprintf (&filename, "%s/%s.pdf", path, BASENAME);
+ xasprintf (&filename, "%s/%s-%s.pdf",
+ path,
+ BASENAME,
+ check_output ? "1.4" : "1.5");
surface = cairo_pdf_surface_create (filename, PAGE_WIDTH, PAGE_HEIGHT);
- /* Disable object stream compression as this prevents check_created_pdf() from working */
- cairo_pdf_surface_restrict_to_version (surface, CAIRO_PDF_VERSION_1_4);
+ cairo_pdf_surface_restrict_to_version (surface, version);
cr = cairo_create (surface);
create_document (surface, cr);
@@ -545,13 +549,38 @@ preamble (cairo_test_context_t *ctx)
return CAIRO_TEST_FAILURE;
}
- result = check_created_pdf(ctx, filename);
+ result = CAIRO_TEST_SUCCESS;
+ if (check_output)
+ result = check_created_pdf(ctx, filename);
free (filename);
return result;
}
+static cairo_test_status_t
+preamble (cairo_test_context_t *ctx)
+{
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ cairo_status_t status, status2;
+ cairo_test_status_t result;
+ char *filename;
+ const char *path = cairo_test_mkdir (CAIRO_TEST_OUTPUT_DIR) ? CAIRO_TEST_OUTPUT_DIR : ".";
+
+ if (! cairo_test_is_target_enabled (ctx, "pdf"))
+ return CAIRO_TEST_UNTESTED;
+
+ /* Create version 1.5 PDF. This can only be manually checked */
+ create_pdf (ctx, FALSE);
+
+ /* Create version 1.4 PDF and checkout output */
+ result = create_pdf (ctx, TRUE);
+
+
+ return result;
+}
+
CAIRO_TEST (pdf_tagged_text,
"Check tagged text, hyperlinks and PDF document features",
"pdf", /* keywords */
commit 6b8d8712fbf62d54e79ec4788e1cea3e5ffa53e9
Author: Adrian Johnson <ajohnson at redneon.com>
Date: Mon Jul 26 18:33:34 2021 +0930
pdf-tagged-text test: disable object stream compression
to allow the test to find the strings it is looking for.
diff --git a/test/pdf-tagged-text.c b/test/pdf-tagged-text.c
index 95b779306..62233a7f3 100644
--- a/test/pdf-tagged-text.c
+++ b/test/pdf-tagged-text.c
@@ -525,6 +525,9 @@ preamble (cairo_test_context_t *ctx)
xasprintf (&filename, "%s/%s.pdf", path, BASENAME);
surface = cairo_pdf_surface_create (filename, PAGE_WIDTH, PAGE_HEIGHT);
+ /* Disable object stream compression as this prevents check_created_pdf() from working */
+ cairo_pdf_surface_restrict_to_version (surface, CAIRO_PDF_VERSION_1_4);
+
cr = cairo_create (surface);
create_document (surface, cr);
commit 90193cc3a2259356ac068f2491f469e10abed141
Author: Adrian Johnson <ajohnson at redneon.com>
Date: Sun Jul 18 07:54:30 2021 +0930
pdf: convert all document interchange features to use object streams
diff --git a/src/cairo-pdf-interchange.c b/src/cairo-pdf-interchange.c
index 8158161fb..aacc728e5 100644
--- a/src/cairo-pdf-interchange.c
+++ b/src/cairo-pdf-interchange.c
@@ -228,34 +228,36 @@ cairo_pdf_interchange_write_node_object (cairo_pdf_surface_t *surface
int i, num_mcid, first_page;
cairo_pdf_resource_t *page_res;
cairo_pdf_struct_tree_node_t *child;
+ cairo_int_status_t status;
+
+ status = _cairo_pdf_surface_object_begin (surface, node->res);
+ if (unlikely (status))
+ return status;
- _cairo_pdf_surface_update_object (surface, node->res);
- _cairo_output_stream_printf (surface->output,
- "%d 0 obj\n"
+ _cairo_output_stream_printf (surface->object_stream.stream,
"<< /Type /StructElem\n"
" /S /%s\n"
" /P %d 0 R\n",
- node->res.id,
node->name,
node->parent->res.id);
if (! cairo_list_is_empty (&node->children)) {
if (cairo_list_is_singular (&node->children) && node->annot_res.id == 0) {
child = cairo_list_first_entry (&node->children, cairo_pdf_struct_tree_node_t, link);
- _cairo_output_stream_printf (surface->output, " /K %d 0 R\n", child->res.id);
+ _cairo_output_stream_printf (surface->object_stream.stream, " /K %d 0 R\n", child->res.id);
} else {
- _cairo_output_stream_printf (surface->output, " /K [ ");
+ _cairo_output_stream_printf (surface->object_stream.stream, " /K [ ");
if (node->annot_res.id != 0) {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
"<< /Type /OBJR /Obj %d 0 R >> ",
node->annot_res.id);
}
cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t,
&node->children, link)
{
- _cairo_output_stream_printf (surface->output, "%d 0 R ", child->res.id);
+ _cairo_output_stream_printf (surface->object_stream.stream, "%d 0 R ", child->res.id);
}
- _cairo_output_stream_printf (surface->output, "]\n");
+ _cairo_output_stream_printf (surface->object_stream.stream, "]\n");
}
} else {
num_mcid = _cairo_array_num_elements (&node->mcid);
@@ -263,14 +265,14 @@ cairo_pdf_interchange_write_node_object (cairo_pdf_surface_t *surface
mcid_elem = _cairo_array_index (&node->mcid, 0);
first_page = mcid_elem->page;
page_res = _cairo_array_index (&surface->pages, first_page - 1);
- _cairo_output_stream_printf (surface->output, " /Pg %d 0 R\n", page_res->id);
+ _cairo_output_stream_printf (surface->object_stream.stream, " /Pg %d 0 R\n", page_res->id);
if (num_mcid == 1 && node->annot_res.id == 0) {
- _cairo_output_stream_printf (surface->output, " /K %d\n", mcid_elem->mcid);
+ _cairo_output_stream_printf (surface->object_stream.stream, " /K %d\n", mcid_elem->mcid);
} else {
- _cairo_output_stream_printf (surface->output, " /K [ ");
+ _cairo_output_stream_printf (surface->object_stream.stream, " /K [ ");
if (node->annot_res.id != 0) {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
"<< /Type /OBJR /Obj %d 0 R >> ",
node->annot_res.id);
}
@@ -278,23 +280,24 @@ cairo_pdf_interchange_write_node_object (cairo_pdf_surface_t *surface
mcid_elem = _cairo_array_index (&node->mcid, i);
page_res = _cairo_array_index (&surface->pages, mcid_elem->page - 1);
if (mcid_elem->page == first_page) {
- _cairo_output_stream_printf (surface->output, "%d ", mcid_elem->mcid);
+ _cairo_output_stream_printf (surface->object_stream.stream, "%d ", mcid_elem->mcid);
} else {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
"\n << /Type /MCR /Pg %d 0 R /MCID %d >> ",
page_res->id,
mcid_elem->mcid);
}
}
- _cairo_output_stream_printf (surface->output, "]\n");
+ _cairo_output_stream_printf (surface->object_stream.stream, "]\n");
}
}
}
- _cairo_output_stream_printf (surface->output,
- ">>\n"
- "endobj\n");
+ _cairo_output_stream_printf (surface->object_stream.stream,
+ ">>\n");
- return _cairo_output_stream_get_status (surface->output);
+ _cairo_pdf_surface_object_end (surface);
+
+ return _cairo_output_stream_get_status (surface->object_stream.stream);
}
static void
@@ -338,13 +341,13 @@ cairo_pdf_interchange_write_explicit_dest (cairo_pdf_surface_t *surface,
_cairo_array_copy_element (&surface->page_heights, page - 1, &height);
_cairo_array_copy_element (&surface->pages, page - 1, &res);
if (has_pos) {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
"[%d 0 R /XYZ %f %f 0]\n",
res.id,
x,
height - y);
} else {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
"[%d 0 R /XYZ null null 0]\n",
res.id);
}
@@ -387,7 +390,7 @@ cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface,
if (named_dest->attrs.y_valid)
y = named_dest->attrs.y;
- _cairo_output_stream_printf (surface->output, " /Dest ");
+ _cairo_output_stream_printf (surface->object_stream.stream, " /Dest ");
status = cairo_pdf_interchange_write_explicit_dest (surface,
named_dest->page,
TRUE,
@@ -401,7 +404,7 @@ cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface,
if (unlikely (status))
return status;
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /Dest %s\n",
dest);
free (dest);
@@ -410,7 +413,7 @@ cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface,
return CAIRO_INT_STATUS_TAG_ERROR;
if (link_attrs->page <= (int)_cairo_array_num_elements (&surface->pages)) {
- _cairo_output_stream_printf (surface->output, " /Dest ");
+ _cairo_output_stream_printf (surface->object_stream.stream, " /Dest ");
status = cairo_pdf_interchange_write_explicit_dest (surface,
link_attrs->page,
link_attrs->has_pos,
@@ -428,7 +431,7 @@ cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface,
if (link_res.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /Dest %d 0 R\n",
link_res.id);
@@ -456,7 +459,7 @@ cairo_pdf_interchange_write_link_action (cairo_pdf_surface_t *surface,
return status;
} else if (link_attrs->link_type == TAG_LINK_URI) {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /A <<\n"
" /Type /Action\n"
" /S /URI\n"
@@ -464,7 +467,7 @@ cairo_pdf_interchange_write_link_action (cairo_pdf_surface_t *surface,
" >>\n",
link_attrs->uri);
} else if (link_attrs->link_type == TAG_LINK_FILE) {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /A <<\n"
" /Type /Action\n"
" /S /GoToR\n"
@@ -475,24 +478,24 @@ cairo_pdf_interchange_write_link_action (cairo_pdf_surface_t *surface,
if (unlikely (status))
return status;
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /D %s\n",
dest);
free (dest);
} else {
if (link_attrs->has_pos) {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /D [%d %f %f 0]\n",
link_attrs->page,
link_attrs->pos.x,
link_attrs->pos.y);
} else {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /D [%d null null 0]\n",
link_attrs->page);
}
}
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" >>\n");
}
@@ -522,25 +525,28 @@ cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface,
sp = _cairo_array_num_elements (&ic->parent_tree) - 1;
node->annot_res = _cairo_pdf_surface_new_object (surface);
+ if (node->annot_res.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
status = _cairo_array_append (&surface->page_annots, &node->annot_res);
if (unlikely (status))
return status;
- _cairo_pdf_surface_update_object (surface, node->annot_res);
- _cairo_output_stream_printf (surface->output,
- "%d 0 obj\n"
+ status = _cairo_pdf_surface_object_begin (surface, node->annot_res);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->object_stream.stream,
"<< /Type /Annot\n"
" /Subtype /Link\n"
" /StructParent %d\n",
- node->annot_res.id,
sp);
height = surface->height;
if (num_rects > 0) {
cairo_rectangle_int_t bbox_rect;
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /QuadPoints [ ");
for (i = 0; i < num_rects; i++) {
cairo_rectangle_t rectf;
@@ -553,31 +559,31 @@ cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface,
else
_cairo_rectangle_union (&bbox_rect, &recti);
- write_rect_to_pdf_quad_points (surface->output, &rectf, height);
- _cairo_output_stream_printf (surface->output, " ");
+ write_rect_to_pdf_quad_points (surface->object_stream.stream, &rectf, height);
+ _cairo_output_stream_printf (surface->object_stream.stream, " ");
}
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
"]\n"
" /Rect [ ");
- write_rect_int_to_pdf_bbox (surface->output, &bbox_rect, height);
- _cairo_output_stream_printf (surface->output, " ]\n");
+ write_rect_int_to_pdf_bbox (surface->object_stream.stream, &bbox_rect, height);
+ _cairo_output_stream_printf (surface->object_stream.stream, " ]\n");
} else {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /Rect [ ");
- write_rect_int_to_pdf_bbox (surface->output, &node->extents.extents, height);
- _cairo_output_stream_printf (surface->output, " ]\n");
+ write_rect_int_to_pdf_bbox (surface->object_stream.stream, &node->extents.extents, height);
+ _cairo_output_stream_printf (surface->object_stream.stream, " ]\n");
}
status = cairo_pdf_interchange_write_link_action (surface, &annot->link_attrs);
if (unlikely (status))
return status;
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /BS << /W 0 >>"
- ">>\n"
- "endobj\n");
+ ">>\n");
- status = _cairo_output_stream_get_status (surface->output);
+ _cairo_pdf_surface_object_end (surface);
+ status = _cairo_output_stream_get_status (surface->object_stream.stream);
}
return status;
@@ -614,41 +620,47 @@ cairo_pdf_interchange_write_struct_tree (cairo_pdf_surface_t *surface)
{
cairo_pdf_interchange_t *ic = &surface->interchange;
cairo_pdf_struct_tree_node_t *child;
+ cairo_int_status_t status;
if (cairo_list_is_empty (&ic->struct_root->children))
return CAIRO_STATUS_SUCCESS;
surface->struct_tree_root = _cairo_pdf_surface_new_object (surface);
+ if (surface->struct_tree_root.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
ic->struct_root->res = surface->struct_tree_root;
cairo_pdf_interchange_walk_struct_tree (surface, ic->struct_root, cairo_pdf_interchange_write_node_object);
child = cairo_list_first_entry (&ic->struct_root->children, cairo_pdf_struct_tree_node_t, link);
- _cairo_pdf_surface_update_object (surface, surface->struct_tree_root);
- _cairo_output_stream_printf (surface->output,
- "%d 0 obj\n"
+
+ status = _cairo_pdf_surface_object_begin (surface, surface->struct_tree_root);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->object_stream.stream,
"<< /Type /StructTreeRoot\n"
" /ParentTree %d 0 R\n",
- surface->struct_tree_root.id,
ic->parent_tree_res.id);
if (cairo_list_is_singular (&ic->struct_root->children)) {
child = cairo_list_first_entry (&ic->struct_root->children, cairo_pdf_struct_tree_node_t, link);
- _cairo_output_stream_printf (surface->output, " /K [ %d 0 R ]\n", child->res.id);
+ _cairo_output_stream_printf (surface->object_stream.stream, " /K [ %d 0 R ]\n", child->res.id);
} else {
- _cairo_output_stream_printf (surface->output, " /K [ ");
+ _cairo_output_stream_printf (surface->object_stream.stream, " /K [ ");
cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t,
&ic->struct_root->children, link)
{
- _cairo_output_stream_printf (surface->output, "%d 0 R ", child->res.id);
+ _cairo_output_stream_printf (surface->object_stream.stream, "%d 0 R ", child->res.id);
}
- _cairo_output_stream_printf (surface->output, "]\n");
+ _cairo_output_stream_printf (surface->object_stream.stream, "]\n");
}
- _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_STATUS_SUCCESS;
}
@@ -686,17 +698,23 @@ cairo_pdf_interchange_write_page_parent_elems (cairo_pdf_surface_t *surface)
num_elems = _cairo_array_num_elements (&ic->mcid_to_tree);
if (num_elems > 0) {
res = _cairo_pdf_surface_new_object (surface);
- _cairo_output_stream_printf (surface->output,
- "%d 0 obj\n"
- "[\n",
- res.id);
+ if (res.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_pdf_surface_object_begin (surface, res);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->object_stream.stream,
+ "[\n");
for (i = 0; i < num_elems; i++) {
_cairo_array_copy_element (&ic->mcid_to_tree, i, &node);
- _cairo_output_stream_printf (surface->output, " %d 0 R\n", node->res.id);
+ _cairo_output_stream_printf (surface->object_stream.stream, " %d 0 R\n", node->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_array_append (&ic->parent_tree, &res);
surface->page_parent_tree = _cairo_array_num_elements (&ic->parent_tree) - 1;
}
@@ -710,27 +728,33 @@ cairo_pdf_interchange_write_parent_tree (cairo_pdf_surface_t *surface)
int num_elems, i;
cairo_pdf_resource_t *res;
cairo_pdf_interchange_t *ic = &surface->interchange;
+ cairo_int_status_t status;
num_elems = _cairo_array_num_elements (&ic->parent_tree);
if (num_elems > 0) {
ic->parent_tree_res = _cairo_pdf_surface_new_object (surface);
- _cairo_output_stream_printf (surface->output,
- "%d 0 obj\n"
- "<< /Nums [\n",
- ic->parent_tree_res.id);
+ if (ic->parent_tree_res.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_pdf_surface_object_begin (surface, ic->parent_tree_res);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->object_stream.stream,
+ "<< /Nums [\n");
for (i = 0; i < num_elems; i++) {
res = _cairo_array_index (&ic->parent_tree, i);
if (res->id) {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" %d %d 0 R\n",
i,
res->id);
}
}
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" ]\n"
- ">>\n"
- "endobj\n");
+ ">>\n");
+ _cairo_pdf_surface_object_end (surface);
}
return CAIRO_STATUS_SUCCESS;
@@ -751,19 +775,24 @@ cairo_pdf_interchange_write_outline (cairo_pdf_surface_t *surface)
_cairo_array_copy_element (&ic->outline, 0, &outline);
outline->res = _cairo_pdf_surface_new_object (surface);
+ if (outline->res.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
surface->outlines_dict_res = outline->res;
- _cairo_output_stream_printf (surface->output,
- "%d 0 obj\n"
+ status = _cairo_pdf_surface_object_begin (surface, outline->res);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->object_stream.stream,
"<< /Type /Outlines\n"
" /First %d 0 R\n"
" /Last %d 0 R\n"
" /Count %d\n"
- ">>\n"
- "endobj\n",
- outline->res.id,
+ ">>\n",
outline->first_child->res.id,
outline->last_child->res.id,
outline->count);
+ _cairo_pdf_surface_object_end (surface);
for (i = 1; i < num_elems; i++) {
_cairo_array_copy_element (&ic->outline, i, &outline);
@@ -773,29 +802,31 @@ cairo_pdf_interchange_write_outline (cairo_pdf_surface_t *surface)
if (unlikely (status))
return status;
- _cairo_output_stream_printf (surface->output,
- "%d 0 obj\n"
+ status = _cairo_pdf_surface_object_begin (surface, outline->res);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->object_stream.stream,
"<< /Title %s\n"
" /Parent %d 0 R\n",
- outline->res.id,
name,
outline->parent->res.id);
free (name);
if (outline->prev) {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /Prev %d 0 R\n",
outline->prev->res.id);
}
if (outline->next) {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /Next %d 0 R\n",
outline->next->res.id);
}
if (outline->first_child) {
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /First %d 0 R\n"
" /Last %d 0 R\n"
" /Count %d\n",
@@ -810,7 +841,7 @@ cairo_pdf_interchange_write_outline (cairo_pdf_surface_t *surface)
flags |= 1;
if (outline->flags & CAIRO_PDF_OUTLINE_FLAG_BOLD)
flags |= 2;
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" /F %d\n",
flags);
}
@@ -819,9 +850,9 @@ cairo_pdf_interchange_write_outline (cairo_pdf_surface_t *surface)
if (unlikely (status))
return status;
- _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 status;
@@ -887,6 +918,7 @@ cairo_pdf_interchange_write_forward_links (cairo_pdf_surface_t *surface)
{
int num_elems, i;
cairo_pdf_forward_link_t *link;
+ cairo_int_status_t status;
num_elems = _cairo_array_num_elements (&surface->forward_links);
for (i = 0; i < num_elems; i++) {
@@ -894,10 +926,9 @@ cairo_pdf_interchange_write_forward_links (cairo_pdf_surface_t *surface)
if (link->page > (int)_cairo_array_num_elements (&surface->pages))
return CAIRO_INT_STATUS_TAG_ERROR;
- _cairo_pdf_surface_update_object (surface, link->res);
- _cairo_output_stream_printf (surface->output,
- "%d 0 obj\n",
- link->res.id);
+ status = _cairo_pdf_surface_object_begin (surface, link->res);
+ if (unlikely (status))
+ return status;
cairo_pdf_interchange_write_explicit_dest (surface,
link->page,
@@ -905,8 +936,7 @@ cairo_pdf_interchange_write_forward_links (cairo_pdf_surface_t *surface)
link->pos.x,
link->pos.y);
- _cairo_output_stream_printf (surface->output,
- "endobj\n");
+ _cairo_pdf_surface_object_end (surface);
}
return CAIRO_STATUS_SUCCESS;
@@ -938,10 +968,15 @@ cairo_pdf_interchange_write_page_labels (cairo_pdf_surface_t *surface)
return CAIRO_STATUS_SUCCESS;
surface->page_labels_res = _cairo_pdf_surface_new_object (surface);
- _cairo_output_stream_printf (surface->output,
- "%d 0 obj\n"
- "<< /Nums [\n",
- surface->page_labels_res.id);
+ if (surface->page_labels_res.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_pdf_surface_object_begin (surface, surface->page_labels_res);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->object_stream.stream,
+ "<< /Nums [\n");
prefix = NULL;
prev_prefix = NULL;
num = 0;
@@ -956,10 +991,10 @@ cairo_pdf_interchange_write_page_labels (cairo_pdf_surface_t *surface)
}
if (!strcmp_null (prefix, prev_prefix) || num != prev_num + 1) {
- _cairo_output_stream_printf (surface->output, " %d << ", i);
+ _cairo_output_stream_printf (surface->object_stream.stream, " %d << ", i);
if (num)
- _cairo_output_stream_printf (surface->output, "/S /D /St %d ", num);
+ _cairo_output_stream_printf (surface->object_stream.stream, "/S /D /St %d ", num);
if (prefix) {
char *s;
@@ -967,11 +1002,11 @@ cairo_pdf_interchange_write_page_labels (cairo_pdf_surface_t *surface)
if (unlikely (status))
return status;
- _cairo_output_stream_printf (surface->output, "/P %s ", s);
+ _cairo_output_stream_printf (surface->object_stream.stream, "/P %s ", s);
free (s);
}
- _cairo_output_stream_printf (surface->output, ">>\n");
+ _cairo_output_stream_printf (surface->object_stream.stream, ">>\n");
}
free (prev_prefix);
prev_prefix = prefix;
@@ -980,10 +1015,10 @@ cairo_pdf_interchange_write_page_labels (cairo_pdf_surface_t *surface)
}
free (prefix);
free (prev_prefix);
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" ]\n"
- ">>\n"
- "endobj\n");
+ ">>\n");
+ _cairo_pdf_surface_object_end (surface);
return CAIRO_STATUS_SUCCESS;
}
@@ -1012,6 +1047,7 @@ _cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface)
{
int i;
cairo_pdf_interchange_t *ic = &surface->interchange;
+ cairo_int_status_t status;
if (ic->num_dests == 0) {
ic->dests_res.id = 0;
@@ -1027,10 +1063,15 @@ _cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface)
qsort (ic->sorted_dests, ic->num_dests, sizeof (cairo_pdf_named_dest_t *), _dest_compare);
ic->dests_res = _cairo_pdf_surface_new_object (surface);
- _cairo_output_stream_printf (surface->output,
- "%d 0 obj\n"
- "<< /Names [\n",
- ic->dests_res.id);
+ if (ic->dests_res.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_pdf_surface_object_begin (surface, ic->dests_res);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->object_stream.stream,
+ "<< /Names [\n");
for (i = 0; i < ic->num_dests; i++) {
cairo_pdf_named_dest_t *dest = ic->sorted_dests[i];
cairo_pdf_resource_t page_res;
@@ -1054,17 +1095,17 @@ _cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface)
_cairo_array_copy_element (&surface->pages, dest->page - 1, &page_res);
_cairo_array_copy_element (&surface->page_heights, dest->page - 1, &height);
- _cairo_output_stream_printf (surface->output,
+ _cairo_output_stream_printf (surface->object_stream.stream,
" (%s) [%d 0 R /XYZ %f %f 0]\n",
dest->attrs.name,
page_res.id,
x,
height - y);
}
- _cairo_output_stream_printf (surface->output,
- " ]\n"
- ">>\n"
- "endobj\n");
+ _cairo_output_stream_printf (surface->object_stream.stream,
+ " ]\n"
+ ">>\n");
+ _cairo_pdf_surface_object_end (surface);
return CAIRO_STATUS_SUCCESS;
}
@@ -1082,12 +1123,17 @@ cairo_pdf_interchange_write_names_dict (cairo_pdf_surface_t *surface)
surface->names_dict_res.id = 0;
if (ic->dests_res.id != 0) {
surface->names_dict_res = _cairo_pdf_surface_new_object (surface);
- _cairo_output_stream_printf (surface->output,
- "%d 0 obj\n"
- "<< /Dests %d 0 R >>\n"
- "endobj\n",
- surface->names_dict_res.id,
+ if (surface->names_dict_res.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_pdf_surface_object_begin (surface, surface->names_dict_res);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->object_stream.stream,
+ "<< /Dests %d 0 R >>\n",
ic->dests_res.id);
+ _cairo_pdf_surface_object_end (surface);
}
return CAIRO_STATUS_SUCCESS;
@@ -1097,41 +1143,44 @@ static cairo_int_status_t
cairo_pdf_interchange_write_docinfo (cairo_pdf_surface_t *surface)
{
cairo_pdf_interchange_t *ic = &surface->interchange;
+ cairo_int_status_t status;
surface->docinfo_res = _cairo_pdf_surface_new_object (surface);
if (surface->docinfo_res.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- _cairo_output_stream_printf (surface->output,
- "%d 0 obj\n"
+ status = _cairo_pdf_surface_object_begin (surface, surface->docinfo_res);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->object_stream.stream,
"<< /Producer (cairo %s (https://cairographics.org))\n",
- surface->docinfo_res.id,
cairo_version_string ());
if (ic->docinfo.title)
- _cairo_output_stream_printf (surface->output, " /Title %s\n", ic->docinfo.title);
+ _cairo_output_stream_printf (surface->object_stream.stream, " /Title %s\n", ic->docinfo.title);
if (ic->docinfo.author)
- _cairo_output_stream_printf (surface->output, " /Author %s\n", ic->docinfo.author);
+ _cairo_output_stream_printf (surface->object_stream.stream, " /Author %s\n", ic->docinfo.author);
if (ic->docinfo.subject)
- _cairo_output_stream_printf (surface->output, " /Subject %s\n", ic->docinfo.subject);
+ _cairo_output_stream_printf (surface->object_stream.stream, " /Subject %s\n", ic->docinfo.subject);
if (ic->docinfo.keywords)
- _cairo_output_stream_printf (surface->output, " /Keywords %s\n", ic->docinfo.keywords);
+ _cairo_output_stream_printf (surface->object_stream.stream, " /Keywords %s\n", ic->docinfo.keywords);
if (ic->docinfo.creator)
- _cairo_output_stream_printf (surface->output, " /Creator %s\n", ic->docinfo.creator);
+ _cairo_output_stream_printf (surface->object_stream.stream, " /Creator %s\n", ic->docinfo.creator);
if (ic->docinfo.create_date)
- _cairo_output_stream_printf (surface->output, " /CreationDate %s\n", ic->docinfo.create_date);
+ _cairo_output_stream_printf (surface->object_stream.stream, " /CreationDate %s\n", ic->docinfo.create_date);
if (ic->docinfo.mod_date)
- _cairo_output_stream_printf (surface->output, " /ModDate %s\n", ic->docinfo.mod_date);
+ _cairo_output_stream_printf (surface->object_stream.stream, " /ModDate %s\n", ic->docinfo.mod_date);
- _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_STATUS_SUCCESS;
}
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index b548af032..7decdc7e7 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -2170,6 +2170,7 @@ _cairo_pdf_surface_object_begin (cairo_pdf_surface_t *surface,
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);
@@ -6886,7 +6887,7 @@ _cairo_pdf_surface_write_xref_stream (cairo_pdf_surface_t *surface,
if (!surface->compress_streams) {
/* Adobe Reader requires xref streams to be flate encoded (PDF
- * Reference 1.7, implemenation note 20). This means
+ * 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.
commit bd514f6b08c1b31a75948fd99c147319e5aa649f
Author: Adrian Johnson <ajohnson at redneon.com>
Date: Sun Jul 18 07:16:22 2021 +0930
pdf: add support for object streams for PDF >= 1.5
This allows all objects that were previously emitted uncompressed to
be compressed into a an object stream.
Currently only /Page, /Pages, and /Catalog have been converted to use
object streams.
diff --git a/src/cairo-array-private.h b/src/cairo-array-private.h
index 35b29e5fc..8e779d41a 100644
--- a/src/cairo-array-private.h
+++ b/src/cairo-array-private.h
@@ -85,6 +85,9 @@ _cairo_array_num_elements (const cairo_array_t *array);
cairo_private unsigned int
_cairo_array_size (const cairo_array_t *array);
+cairo_private void
+_cairo_array_sort (const cairo_array_t *array, int (*compar)(const void *, const void *));
+
CAIRO_END_DECLS
#endif /* CAIRO_ARRAY_PRIVATE_H */
diff --git a/src/cairo-array.c b/src/cairo-array.c
index af8b7e982..60f45db4e 100644
--- a/src/cairo-array.c
+++ b/src/cairo-array.c
@@ -532,3 +532,9 @@ _cairo_user_data_array_foreach (cairo_user_data_array_t *array,
func (slots[i].key, slots[i].user_data, closure);
}
}
+
+void
+_cairo_array_sort (const cairo_array_t *array, int (*compar)(const void *, const void *))
+{
+ qsort (array->elements, array->num_elements, array->element_size, compar);
+}
diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
index 65fd8c741..673e20a6d 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -55,6 +55,7 @@ typedef struct _cairo_pdf_resource {
unsigned int id;
} cairo_pdf_resource_t;
+
#define CAIRO_NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY + 1)
typedef struct _cairo_pdf_group_resources {
@@ -315,6 +316,13 @@ struct _cairo_pdf_surface {
cairo_bool_t is_knockout;
} group_stream;
+ struct {
+ cairo_bool_t active;
+ cairo_output_stream_t *stream;
+ cairo_pdf_resource_t resource;
+ cairo_array_t objects;
+ } object_stream;
+
cairo_surface_clipper_t clipper;
cairo_pdf_operators_t pdf_operators;
@@ -377,6 +385,13 @@ _cairo_pdf_interchange_tag_begin (cairo_pdf_surface_t *surface,
const char *name,
const char *attributes);
+cairo_private cairo_int_status_t
+_cairo_pdf_surface_object_begin (cairo_pdf_surface_t *surface,
+ cairo_pdf_resource_t resource);
+
+cairo_private void
+_cairo_pdf_surface_object_end (cairo_pdf_surface_t *surface);
+
cairo_private cairo_int_status_t
_cairo_pdf_interchange_tag_end (cairo_pdf_surface_t *surface,
const char *name);
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 09b2648f1..b548af032 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,11 +306,12 @@ _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);
@@ -296,9 +327,6 @@ static cairo_int_status_t
_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_write_page (cairo_pdf_surface_t *surface);
-
static cairo_int_status_t
_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface);
@@ -315,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)) {
@@ -336,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
@@ -472,6 +504,9 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output,
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;
@@ -2093,6 +2128,181 @@ _cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface,
return status;
}
+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_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,
@@ -2223,6 +2433,10 @@ _cairo_pdf_surface_finish (void *abstract_surface)
_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);
@@ -2230,15 +2444,25 @@ _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);
+
+ status = _cairo_pdf_surface_write_catalog (surface, catalog);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_close_object_stream (surface);
+ if (unlikely (status))
+ return status;
if (surface->pdf_version >= CAIRO_PDF_VERSION_1_5)
{
@@ -2306,6 +2530,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++) {
@@ -5141,34 +5366,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
@@ -6452,55 +6681,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
@@ -6523,7 +6751,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);
}
@@ -6570,7 +6798,7 @@ _cairo_write_xref_stream_entrys (cairo_pdf_surface_t *surface,
/* PDF requires this to be first entry */
_cairo_write_xref_stream_entry (stream,
0,
- 0, /* type 0 == free */
+ PDF_OBJECT_FREE,
field2_size,
0, /* next free object number */
0xffff, /* next generation number */
@@ -6579,13 +6807,31 @@ _cairo_write_xref_stream_entrys (cairo_pdf_surface_t *surface,
num_objects = _cairo_array_num_elements (&surface->objects);
for (i = 0; i < num_objects; i++) {
object = _cairo_array_index (&surface->objects, i);
- _cairo_write_xref_stream_entry (stream,
- i + 1,
- 1, /* type 1 == in use, not in obj stream */
- field2_size,
- object->offset,
- 0, /* generation number */
- write_as_comments);
+ 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);
+ }
}
}
@@ -6614,7 +6860,6 @@ _cairo_pdf_surface_write_xref_stream (cairo_pdf_surface_t *surface,
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);
@@ -6647,7 +6892,7 @@ _cairo_pdf_surface_write_xref_stream (cairo_pdf_surface_t *surface,
* a human readable format of the xref stream as PDF comments.
*/
_cairo_output_stream_printf (surface->output,
- "%% id type offset gen\n");
+ "%% id type offset/obj gen/index\n");
_cairo_write_xref_stream_entrys (surface, surface->output, offset_bytes, TRUE);
}
@@ -6659,7 +6904,8 @@ _cairo_pdf_surface_write_xref_stream (cairo_pdf_surface_t *surface,
return status;
_cairo_output_stream_printf (surface->output,
- "\nendstream\n"
+ "\n"
+ "endstream\n"
"endobj\n");
return _cairo_output_stream_get_status (surface->output);
@@ -7018,6 +7264,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;
@@ -7086,9 +7336,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"
@@ -7100,7 +7353,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,
@@ -7109,39 +7361,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
commit 0f382eb0875f2102ba26e30c6ea1dfbaf1abff96
Author: Adrian Johnson <ajohnson at redneon.com>
Date: Fri Jul 16 19:18:02 2021 +0930
pdf: use cross-reference stream for PDF >= 1.5
This reduces the output size and is required for object streams.
diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
index 3fb8ffaf7..65fd8c741 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -288,7 +288,7 @@ struct _cairo_pdf_surface {
cairo_pdf_resource_t struct_tree_root;
cairo_pdf_version_t pdf_version;
- cairo_bool_t compress_content;
+ cairo_bool_t compress_streams;
cairo_pdf_resource_t content;
cairo_pdf_resource_t content_resources;
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index a9caed8ff..09b2648f1 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -285,6 +285,13 @@ _cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface);
static long
_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface);
+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);
+
static cairo_int_status_t
_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface,
cairo_bool_t finish);
@@ -459,7 +466,7 @@ _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;
@@ -506,7 +513,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 +1961,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 +2010,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 +2064,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;
@@ -2109,7 +2116,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 +2136,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 +2151,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,6 +2219,7 @@ _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);
@@ -2232,18 +2240,26 @@ _cairo_pdf_surface_finish (void *abstract_surface)
if (catalog.id == 0 && status == CAIRO_STATUS_SUCCESS)
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
- 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);
-
+ 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"
@@ -4223,7 +4239,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"
@@ -5338,7 +5354,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 +6237,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;
@@ -6515,6 +6531,140 @@ _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,
+ 0, /* type 0 == 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);
+ _cairo_write_xref_stream_entry (stream,
+ i + 1,
+ 1, /* type 1 == in use, not in obj stream */
+ field2_size,
+ object->offset,
+ 0, /* generation number */
+ 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, implemenation 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 gen\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,
+ "\nendstream\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)
More information about the cairo-commit
mailing list