[cairo] [PATCH] Fix links whose targets are on a page that is not created yet

Guillaume Ayoub guillaume.ayoub at kozea.fr
Tue Aug 21 13:55:35 UTC 2018


As explained on the mailing list[1], links are broken when their targets are on
a page that has not been created yet.

This commit fixes the problem by using indirect references for /Dest objects,
and by creating the references when the file has been totally drawn.

I'm not sure whether the "links" property should be in "surface" or in
"interchange". I may also have added some memory leaks even if I did my best to
avoid them, as I'm not a regular C developer.

[1] https://lists.cairographics.org/archives/cairo/2018-August/028715.html

---
 src/cairo-pdf-interchange.c     | 100 +++++++++++++++++++++++---------
 src/cairo-pdf-surface-private.h |   9 +++
 src/cairo-pdf-surface.c         |   2 +
 3 files changed, 84 insertions(+), 27 deletions(-)

diff --git a/src/cairo-pdf-interchange.c b/src/cairo-pdf-interchange.c
index 9aa6934dc..d9b1e19fc 100644
--- a/src/cairo-pdf-interchange.c
+++ b/src/cairo-pdf-interchange.c
@@ -327,29 +327,34 @@ _named_dest_pluck (void *entry, void *closure)
 
 static cairo_int_status_t
 cairo_pdf_interchange_write_explicit_dest (cairo_pdf_surface_t *surface,
-                                          int                  page,
-                                          cairo_bool_t         has_pos,
-                                          double               x,
-                                          double               y)
+					   cairo_pdf_link_t    *link)
 {
     cairo_pdf_resource_t res;
     double height;
 
-    if (page < 1 || page > (int)_cairo_array_num_elements (&surface->pages))
+    if (link->page < 1 || link->page > (int)_cairo_array_num_elements (&surface->pages))
        return CAIRO_INT_STATUS_TAG_ERROR;
 
-    _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,
-                                    "   /Dest [%d 0 R /XYZ %f %f 0]\n",
-                                    res.id,
-                                    x,
-                                    height - y);
+    _cairo_pdf_surface_update_object (surface, link->res);
+
+    _cairo_array_copy_element (&surface->page_heights, link->page - 1, &height);
+    _cairo_array_copy_element (&surface->pages, link->page - 1, &res);
+    if (link->has_pos) {
+	_cairo_output_stream_printf (surface->output,
+				     "%d 0 obj\n"
+				     "   [%d 0 R /XYZ %f %f 0]\n"
+				     "endobj",
+				     link->res.id,
+				     res.id,
+				     link->x,
+				     height - link->y);
     } else {
-       _cairo_output_stream_printf (surface->output,
-                                    "   /Dest [%d 0 R /XYZ null null 0]\n",
-                                    res.id);
+	_cairo_output_stream_printf (surface->output,
+				     "%d 0 obj\n"
+				     "   [%d 0 R /XYZ null null 0]\n"
+				     "endobj",
+				     link->res.id,
+				     res.id);
     }
 
     return CAIRO_STATUS_SUCCESS;
@@ -376,6 +381,10 @@ cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface,
 	     * reference instead of the name */
 	    double x = 0;
 	    double y = 0;
+	    cairo_pdf_resource_t res;
+	    cairo_pdf_link_t *link;
+
+	    link = _cairo_malloc (sizeof (cairo_pdf_link_t));
 
 	    if (named_dest->extents.valid) {
 		x = named_dest->extents.extents.x;
@@ -388,10 +397,16 @@ cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface,
 	    if (named_dest->attrs.y_valid)
 		y = named_dest->attrs.y;
 
-	    status = cairo_pdf_interchange_write_explicit_dest (surface,
-								named_dest->page,
-								TRUE,
-								x, y);
+	    res = _cairo_pdf_surface_new_object (surface);
+	    _cairo_output_stream_printf (surface->output,
+					 "   /Dest %d 0 R\n",
+					 res.id);
+	    link->res = res;
+	    link->page = named_dest->page;
+	    link->has_pos = TRUE;
+	    link->x = x;
+	    link->y = y;
+	    status = _cairo_array_append (&surface->links, link);
 	    return status;
 	}
     }
@@ -406,11 +421,24 @@ cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface,
 				     dest);
 	free (dest);
     } else {
-	status = cairo_pdf_interchange_write_explicit_dest (surface,
-							    link_attrs->page,
-							    link_attrs->has_pos,
-							    link_attrs->pos.x,
-							    link_attrs->pos.y);
+	cairo_pdf_resource_t res;
+	cairo_pdf_link_t *link;
+
+	link = _cairo_malloc (sizeof (cairo_pdf_link_t));
+	res = _cairo_pdf_surface_new_object (surface);
+	_cairo_output_stream_printf (surface->output,
+				     "   /Dest %d 0 R\n",
+				     res.id);
+	link->res = res;
+	link->page = link_attrs->page;
+	link->has_pos = link_attrs->has_pos;
+	if (link->has_pos) {
+	    link->x = link_attrs->pos.x;
+	    link->y = link_attrs->pos.y;
+	}
+	status = _cairo_array_append (&surface->links, link);
+	if (unlikely (status))
+	    return status;
     }
 
     return CAIRO_STATUS_SUCCESS;
@@ -855,6 +883,20 @@ strcmp_null (const char *s1, const char *s2)
     return FALSE;
 }
 
+static cairo_int_status_t
+cairo_pdf_interchange_write_links (cairo_pdf_surface_t *surface)
+{
+    int num_elems, i;
+    cairo_pdf_link_t link;
+
+    num_elems = _cairo_array_num_elements (&surface->links);
+    for (i = 0; i < num_elems; i++) {
+	_cairo_array_copy_element (&surface->links, i, &link);
+	cairo_pdf_interchange_write_explicit_dest (surface, &link);
+    }
+    return CAIRO_STATUS_SUCCESS;
+}
+
 static cairo_int_status_t
 cairo_pdf_interchange_write_page_labels (cairo_pdf_surface_t *surface)
 {
@@ -1357,10 +1399,10 @@ _cairo_pdf_interchange_write_page_objects (cairo_pdf_surface_t *surface)
     cairo_int_status_t status;
 
     status = cairo_pdf_interchange_write_page_annots (surface);
-     if (unlikely (status))
+    if (unlikely (status))
 	return status;
 
-     cairo_pdf_interchange_clear_annotations (surface);
+    cairo_pdf_interchange_clear_annotations (surface);
 
     return cairo_pdf_interchange_write_page_parent_elems (surface);
 }
@@ -1395,6 +1437,10 @@ _cairo_pdf_interchange_write_document_objects (cairo_pdf_surface_t *surface)
     if (unlikely (status))
 	return status;
 
+    status = cairo_pdf_interchange_write_links (surface);
+    if (unlikely (status))
+	return status;
+
     status = cairo_pdf_interchange_write_names_dict (surface);
     if (unlikely (status))
 	return status;
diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
index 3793332ce..64dd300f6 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -208,6 +208,14 @@ typedef struct _cairo_pdf_outline_entry {
     int count;
 } cairo_pdf_outline_entry_t;
 
+typedef struct _cairo_pdf_link {
+    cairo_pdf_resource_t res;
+    int page;
+    cairo_bool_t has_pos;
+    double x;
+    double y;
+} cairo_pdf_link_t;
+
 struct docinfo {
     char *title;
     char *author;
@@ -327,6 +335,7 @@ struct _cairo_pdf_surface {
     cairo_pdf_interchange_t interchange;
     int page_parent_tree; /* -1 if not used */
     cairo_array_t page_annots;
+    cairo_array_t links;
     cairo_bool_t tagged;
     char *current_page_label;
     cairo_array_t page_labels;
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index ab6781340..9fd48265d 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -493,6 +493,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t	*output,
 
     surface->page_parent_tree = -1;
     _cairo_array_init (&surface->page_annots, sizeof (cairo_pdf_resource_t));
+    _cairo_array_init (&surface->links, sizeof (cairo_pdf_link_t));
     surface->tagged = FALSE;
     surface->current_page_label = NULL;
     _cairo_array_init (&surface->page_labels, sizeof (char *));
@@ -2296,6 +2297,7 @@ _cairo_pdf_surface_finish (void *abstract_surface)
     _cairo_array_fini (&surface->fonts);
     _cairo_array_fini (&surface->knockout_group);
     _cairo_array_fini (&surface->page_annots);
+    _cairo_array_fini (&surface->links);
 
     if (surface->font_subsets) {
 	_cairo_scaled_font_subsets_destroy (surface->font_subsets);
-- 
2.18.0



More information about the cairo mailing list