[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
Sat Jul 24 12:58:18 UTC 2021


 src/cairo-pdf-interchange.c     |   93 +++++++++++++++++++++++++++++++++-------
 src/cairo-pdf-surface-private.h |    8 +++
 src/cairo-pdf-surface.c         |    2 
 test/pdf-tagged-text.c          |   23 +++++++++
 4 files changed, 109 insertions(+), 17 deletions(-)

New commits:
commit 099d71fb9f267153da8b45adc288f9715fbb4611
Merge: 994eccefc f7c7bcb60
Author: Uli Schlachter <psychon at znc.in>
Date:   Sat Jul 24 12:58:17 2021 +0000

    Merge branch 'issue-336' into 'master'
    
    tags: allow links to page numbers not yet created
    
    Closes #336
    
    See merge request cairo/cairo!211

commit f7c7bcb6030aa36677adcc4887c8f9f8964431d6
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Jul 24 21:26:22 2021 +0930

    tags: allow links to page numbers not yet created
    
    Previously, forward references were required to use named destinations.
    
    This patch is based on the patch in #336 by Guillaume Ayoub <guillaume.ayoub at kozea.fr>
    that converted all links to indirect objects written at the end of the document.
    
    I have reworked the patch so that only forward references to future page numbers are
    written as indirect objects. Backward references and named destinations remain as they
    are. This is to minimize the number of objects written to the PDF file.
    
    Fixes #336

diff --git a/src/cairo-pdf-interchange.c b/src/cairo-pdf-interchange.c
index ad4072e63..8158161fb 100644
--- a/src/cairo-pdf-interchange.c
+++ b/src/cairo-pdf-interchange.c
@@ -335,20 +335,17 @@ cairo_pdf_interchange_write_explicit_dest (cairo_pdf_surface_t *surface,
     cairo_pdf_resource_t res;
     double height;
 
-    if (page < 1 || 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",
+                                    "[%d 0 R /XYZ %f %f 0]\n",
                                     res.id,
                                     x,
                                     height - y);
     } else {
        _cairo_output_stream_printf (surface->output,
-                                    "   /Dest [%d 0 R /XYZ null null 0]\n",
+                                    "[%d 0 R /XYZ null null 0]\n",
                                     res.id);
     }
 
@@ -362,6 +359,8 @@ 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 (link_attrs->dest) {
 	cairo_pdf_named_dest_t key;
@@ -388,10 +387,11 @@ 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 ");
 	    status = cairo_pdf_interchange_write_explicit_dest (surface,
-								named_dest->page,
-								TRUE,
-								x, y);
+                                                                named_dest->page,
+                                                                TRUE,
+                                                                x, y);
 	    return status;
 	}
     }
@@ -406,14 +406,41 @@ 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);
+	if (link_attrs->page < 1)
+	    return CAIRO_INT_STATUS_TAG_ERROR;
+
+	if (link_attrs->page <= (int)_cairo_array_num_elements (&surface->pages)) {
+	    _cairo_output_stream_printf (surface->output, "   /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 */
+
+	    link = _cairo_malloc (sizeof (cairo_pdf_forward_link_t));
+	    if (unlikely (link == NULL))
+		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	    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->output,
+					 "   /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);
+	}
     }
 
-    return CAIRO_STATUS_SUCCESS;
+    return status;
 }
 
 static cairo_int_status_t
@@ -855,6 +882,36 @@ strcmp_null (const char *s1, const char *s2)
     return FALSE;
 }
 
+static cairo_int_status_t
+cairo_pdf_interchange_write_forward_links (cairo_pdf_surface_t *surface)
+{
+    int num_elems, i;
+    cairo_pdf_forward_link_t *link;
+
+    num_elems = _cairo_array_num_elements (&surface->forward_links);
+    for (i = 0; i < num_elems; i++) {
+	link = _cairo_array_index (&surface->forward_links, i);
+	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);
+
+	cairo_pdf_interchange_write_explicit_dest (surface,
+						   link->page,
+						   link->has_pos,
+						   link->pos.x,
+						   link->pos.y);
+
+	_cairo_output_stream_printf (surface->output,
+				     "endobj\n");
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 static cairo_int_status_t
 cairo_pdf_interchange_write_page_labels (cairo_pdf_surface_t *surface)
 {
@@ -1364,10 +1421,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);
 }
@@ -1403,6 +1460,10 @@ _cairo_pdf_interchange_write_document_objects (cairo_pdf_surface_t *surface)
     if (unlikely (status))
 	return status;
 
+    status = cairo_pdf_interchange_write_forward_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 49776b90f..3fb8ffaf7 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -208,6 +208,13 @@ typedef struct _cairo_pdf_outline_entry {
     int count;
 } cairo_pdf_outline_entry_t;
 
+typedef struct _cairo_pdf_forward_link {
+    cairo_pdf_resource_t res;
+    int page;
+    cairo_bool_t has_pos;
+    cairo_point_double_t pos;
+} cairo_pdf_forward_link_t;
+
 struct docinfo {
     char *title;
     char *author;
@@ -327,6 +334,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 forward_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 f2463a8c3..a9caed8ff 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->forward_links, sizeof (cairo_pdf_forward_link_t));
     surface->tagged = FALSE;
     surface->current_page_label = NULL;
     _cairo_array_init (&surface->page_labels, sizeof (char *));
@@ -2304,6 +2305,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->forward_links);
 
     if (surface->font_subsets) {
 	_cairo_scaled_font_subsets_destroy (surface->font_subsets);
diff --git a/test/pdf-tagged-text.c b/test/pdf-tagged-text.c
index 69dc6b928..95b779306 100644
--- a/test/pdf-tagged-text.c
+++ b/test/pdf-tagged-text.c
@@ -318,6 +318,9 @@ draw_cover (cairo_surface_t *surface, cairo_t *cr)
     const char *cairo_url = "https://www.cairographics.org/";
     const double url_box_margin = 20.0;
 
+    cairo_tag_begin (cr, CAIRO_TAG_DEST, "name='cover'  internal");
+    cairo_tag_end (cr, CAIRO_TAG_DEST);
+
     cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
     cairo_set_font_size(cr, 16);
     cairo_move_to (cr, PAGE_WIDTH/3, PAGE_HEIGHT/3);
@@ -343,6 +346,12 @@ draw_cover (cairo_surface_t *surface, cairo_t *cr)
     cairo_tag_begin (cr, CAIRO_TAG_LINK, buf);
     cairo_tag_end (cr, CAIRO_TAG_LINK);
 
+    /* Create link to not yet emmited page number */
+    cairo_tag_begin (cr, CAIRO_TAG_LINK, "page=5");
+    cairo_move_to (cr, PAGE_WIDTH/3, 4*PAGE_HEIGHT/5);
+    cairo_show_text (cr, "link to page 5");
+    cairo_tag_end (cr, CAIRO_TAG_LINK);
+
     draw_page_num (surface, cr, "cover", 0);
 }
 
@@ -417,6 +426,18 @@ create_document (cairo_surface_t *surface, cairo_t *cr)
 	sect++;
     }
 
+    cairo_show_page (cr);
+
+    cairo_tag_begin (cr, CAIRO_TAG_LINK, "dest='cover'");
+    cairo_move_to (cr, PAGE_WIDTH/3, 2*PAGE_HEIGHT/5);
+    cairo_show_text (cr, "link to cover");
+    cairo_tag_end (cr, CAIRO_TAG_LINK);
+
+    cairo_tag_begin (cr, CAIRO_TAG_LINK, "page=3");
+    cairo_move_to (cr, PAGE_WIDTH/3, 3*PAGE_HEIGHT/5);
+    cairo_show_text (cr, "link to page 3");
+    cairo_tag_end (cr, CAIRO_TAG_LINK);
+
     cairo_tag_end (cr, "Document");
 }
 
@@ -511,7 +532,7 @@ preamble (cairo_test_context_t *ctx)
     cairo_destroy (cr);
     cairo_surface_finish (surface);
     status2 = cairo_surface_status (surface);
-    if (status != CAIRO_STATUS_SUCCESS)
+    if (status == CAIRO_STATUS_SUCCESS)
 	status = status2;
 
     cairo_surface_destroy (surface);


More information about the cairo-commit mailing list