[cairo-commit] 2 commits - 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
Thu Jul 29 20:29:12 UTC 2021


 src/cairo-pdf-interchange.c     |  119 +++++++++++++++++++++++-----------------
 src/cairo-pdf-surface-private.h |    1 
 src/cairo-pdf-surface.c         |    6 ++
 test/pdf-tagged-text.c          |   11 ++-
 4 files changed, 84 insertions(+), 53 deletions(-)

New commits:
commit 814162d9769fc3b63946f8a1a0ab9ac2ffdcf1e8
Merge: 47e6764de ca1fb44dd
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Thu Jul 29 20:29:10 2021 +0000

    Merge branch 'pdf-links-fixes' into 'master'
    
    pdf links: fix forward references to dest names with 'internal' flag
    
    See merge request cairo/cairo!220

commit ca1fb44dd367eaa1a544a2655425b0313c758ca2
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Jul 28 07:18:25 2021 +0930

    pdf links: fix forward references to dest names with 'internal' flag

diff --git a/src/cairo-pdf-interchange.c b/src/cairo-pdf-interchange.c
index 45c9139b4..921952afb 100644
--- a/src/cairo-pdf-interchange.c
+++ b/src/cairo-pdf-interchange.c
@@ -361,21 +361,19 @@ cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface,
 {
     cairo_int_status_t status;
     cairo_pdf_interchange_t *ic = &surface->interchange;
-    char *dest = NULL;
     cairo_pdf_forward_link_t *link;
     cairo_pdf_resource_t link_res;
 
+    /* If the dest is known, emit an explicit dest */
     if (link_attrs->dest) {
 	cairo_pdf_named_dest_t key;
 	cairo_pdf_named_dest_t *named_dest;
 
-	/* check if this is a link to an internal named dest */
+	/* check if we already have this dest */
 	key.attrs.name = link_attrs->dest;
 	init_named_dest_key (&key);
 	named_dest = _cairo_hash_table_lookup (ic->named_dests, &key.base);
-	if (named_dest && named_dest->attrs.internal) {
-	    /* if dests exists and has internal attribute, use a direct
-	     * reference instead of the name */
+	if (named_dest) {
 	    double x = 0;
 	    double y = 0;
 
@@ -399,49 +397,42 @@ cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface,
 	}
     }
 
-    if (link_attrs->dest) {
-	status = _cairo_utf8_to_pdf_string (link_attrs->dest, &dest);
-	if (unlikely (status))
-	    return status;
-
-	_cairo_output_stream_printf (surface->object_stream.stream,
-				     "   /Dest %s\n",
-				     dest);
-	free (dest);
-    } else {
+    /* If the page is known, emit an explicit dest */
+    if (!link_attrs->dest) {
 	if (link_attrs->page < 1)
 	    return _cairo_tag_error ("Link attribute: \"page=%d\" page must be >= 1", link_attrs->page);
 
 	if (link_attrs->page <= (int)_cairo_array_num_elements (&surface->pages)) {
 	    _cairo_output_stream_printf (surface->object_stream.stream, "   /Dest ");
-	    status = cairo_pdf_interchange_write_explicit_dest (surface,
-								link_attrs->page,
-								link_attrs->has_pos,
-								link_attrs->pos.x,
-								link_attrs->pos.y);
-	} else {
-	    /* Link refers to a future page. Use an indirect object and
-	     * write the link at the end of the document */
+	    return cairo_pdf_interchange_write_explicit_dest (surface,
+							      link_attrs->page,
+							      link_attrs->has_pos,
+							      link_attrs->pos.x,
+							      link_attrs->pos.y);
+	}
+    }
 
-	    link = _cairo_malloc (sizeof (cairo_pdf_forward_link_t));
-	    if (unlikely (link == NULL))
-		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    /* Link refers to a future or unknown page. Use an indirect object
+     * and write the link at the end of the document */
 
-	    link_res = _cairo_pdf_surface_new_object (surface);
-	    if (link_res.id == 0)
-		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    link = _cairo_malloc (sizeof (cairo_pdf_forward_link_t));
+    if (unlikely (link == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-	    _cairo_output_stream_printf (surface->object_stream.stream,
-					 "   /Dest %d 0 R\n",
-					 link_res.id);
-
-	    link->res = link_res;
-	    link->page = link_attrs->page;
-	    link->has_pos = link_attrs->has_pos;
-	    link->pos = link_attrs->pos;
-	    status = _cairo_array_append (&surface->forward_links, link);
-	}
-    }
+    link_res = _cairo_pdf_surface_new_object (surface);
+    if (link_res.id == 0)
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+    _cairo_output_stream_printf (surface->object_stream.stream,
+				 "   /Dest %d 0 R\n",
+				 link_res.id);
+
+    link->res = link_res;
+    link->dest = link_attrs->dest ? strdup (link_attrs->dest) : NULL;
+    link->page = link_attrs->page;
+    link->has_pos = link_attrs->has_pos;
+    link->pos = link_attrs->pos;
+    status = _cairo_array_append (&surface->forward_links, link);
 
     return status;
 }
@@ -919,6 +910,9 @@ 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;
+    cairo_pdf_named_dest_t key;
+    cairo_pdf_named_dest_t *named_dest;
+    cairo_pdf_interchange_t *ic = &surface->interchange;
 
     num_elems = _cairo_array_num_elements (&surface->forward_links);
     for (i = 0; i < num_elems; i++) {
@@ -932,12 +926,39 @@ cairo_pdf_interchange_write_forward_links (cairo_pdf_surface_t *surface)
 	if (unlikely (status))
 	    return status;
 
-	cairo_pdf_interchange_write_explicit_dest (surface,
-						   link->page,
-						   link->has_pos,
-						   link->pos.x,
-						   link->pos.y);
+	if (link->dest) {
+	    key.attrs.name = link->dest;
+	    init_named_dest_key (&key);
+	    named_dest = _cairo_hash_table_lookup (ic->named_dests, &key.base);
+	    if (named_dest) {
+		double x = 0;
+		double y = 0;
+
+		if (named_dest->extents.valid) {
+		    x = named_dest->extents.extents.x;
+		    y = named_dest->extents.extents.y;
+		}
+
+		if (named_dest->attrs.x_valid)
+		    x = named_dest->attrs.x;
+
+		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);
+	    } else {
+		return _cairo_tag_error ("Link to dest=\"%s\" not found", link->dest);
+	    }
+	} else {
+	    cairo_pdf_interchange_write_explicit_dest (surface,
+						       link->page,
+						       link->has_pos,
+						       link->pos.x,
+						       link->pos.y);
+	}
 	_cairo_pdf_surface_object_end (surface);
     }
 
@@ -1026,13 +1047,14 @@ cairo_pdf_interchange_write_page_labels (cairo_pdf_surface_t *surface)
 }
 
 static void
-_collect_dest (void *entry, void *closure)
+_collect_external_dest (void *entry, void *closure)
 {
     cairo_pdf_named_dest_t *dest = entry;
     cairo_pdf_surface_t *surface = closure;
     cairo_pdf_interchange_t *ic = &surface->interchange;
 
-    ic->sorted_dests[ic->num_dests++] = dest;
+    if (!dest->attrs.internal)
+	ic->sorted_dests[ic->num_dests++] = dest;
 }
 
 static int
@@ -1061,7 +1083,8 @@ _cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface)
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
     ic->num_dests = 0;
-    _cairo_hash_table_foreach (ic->named_dests, _collect_dest, surface);
+    _cairo_hash_table_foreach (ic->named_dests, _collect_external_dest, 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);
diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h
index 673e20a6d..b2d857550 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -211,6 +211,7 @@ typedef struct _cairo_pdf_outline_entry {
 
 typedef struct _cairo_pdf_forward_link {
     cairo_pdf_resource_t res;
+    char *dest;
     int page;
     cairo_bool_t has_pos;
     cairo_point_double_t pos;
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 2600c3595..3c9d12471 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -2432,6 +2432,10 @@ _cairo_pdf_surface_finish (void *abstract_surface)
     char *label;
     cairo_pdf_resource_t xref_res;
 
+    /* Some of the data may be in an inconistent state if there is an error status. */
+    if (surface->base.status != CAIRO_STATUS_SUCCESS)
+	goto CLEANUP;
+
     _cairo_pdf_surface_clear (surface);
 
     status = _cairo_pdf_surface_open_object_stream (surface);
@@ -2491,6 +2495,8 @@ _cairo_pdf_surface_finish (void *abstract_surface)
 				 "%%%%EOF\n",
 				 offset);
 
+  CLEANUP:
+
     /* pdf_operators has already been flushed when the last stream was
      * closed so we should never be writing anything here - however,
      * the stream may itself be in an error state. */
diff --git a/test/pdf-tagged-text.c b/test/pdf-tagged-text.c
index 5bfb55886..1e94edf1e 100644
--- a/test/pdf-tagged-text.c
+++ b/test/pdf-tagged-text.c
@@ -352,6 +352,12 @@ draw_cover (cairo_surface_t *surface, cairo_t *cr)
     cairo_show_text (cr, "link to page 5");
     cairo_tag_end (cr, CAIRO_TAG_LINK);
 
+    /* Create link to not yet emmited destination */
+    cairo_tag_begin (cr, CAIRO_TAG_LINK, "dest='Section 3.3'");
+    cairo_move_to (cr, PAGE_WIDTH/3, 4.2*PAGE_HEIGHT/5);
+    cairo_show_text (cr, "link to page section 3.3");
+    cairo_tag_end (cr, CAIRO_TAG_LINK);
+
     draw_page_num (surface, cr, "cover", 0);
 }
 
@@ -561,12 +567,7 @@ create_pdf (cairo_test_context_t *ctx, cairo_bool_t check_output)
 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;


More information about the cairo-commit mailing list