2 commits - src/cairo-pdf-interchange.c src/cairo-pdf-surface.c src/cairo-pdf-surface-private.h src/cairo-tag-attributes.c src/cairo-tag-attributes-private.h src/cairo-tag-stack.c src/cairo-tag-stack-private.h test/pdf-tagged-text.c

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Wed May 15 22:12:25 UTC 2024


 src/cairo-pdf-interchange.c        |  177 +++++++++++++------------------------
 src/cairo-pdf-surface-private.h    |    9 -
 src/cairo-pdf-surface.c            |    2 
 src/cairo-tag-attributes-private.h |    2 
 src/cairo-tag-attributes.c         |   19 ++-
 src/cairo-tag-stack-private.h      |    3 
 src/cairo-tag-stack.c              |   14 ++
 test/pdf-tagged-text.c             |   46 ++++++++-
 8 files changed, 135 insertions(+), 137 deletions(-)

New commits:
commit c75997a4eab246b6b5dd164bb30ab497ea17c80a
Merge: cf1b42ca7 754f43164
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed May 15 22:12:24 2024 +0000

    Merge branch 'dest-and-uri' into 'master'
    
    Allow links to specify 'dest' and 'uri'
    
    See merge request cairo/cairo!547

commit 754f431642e9082611f36d7aec96378875b5620a
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat May 11 15:03:17 2024 +0930

    Allow links to specify 'dest' and 'uri'
    
    - If a link has both 'dest' and 'uri', the 'dest' will be used if it
      exists, otherwise it will fallback to using the 'uri'.
    
    - Ensure that a missing 'dest' does not result in an error. Instead a
      warning is printed if CAIRO_DEBUG_TAG is set, and a link to the
      current location is embedded in the PDF. ie the link does
      nothing. Cairo needs to embed a link even if no destination is
      available because when links are embedded at the end of the
      document, the content stream already contains link tags.
    
    - Remove cairo_pdf_interchange_write_forward_links. This code was
      originally used prior to !463 when cairo wrote the links at the end
      of each page. Now the links are written at the end of the document
      so there are no longer any forward links with an unknown
      destination, unless the destination does not exist.
    
    - When 'internal' is not used, use the 'dest' name to reference the
      link. Ensure non ASCII names are correctly encoded.

diff --git a/src/cairo-pdf-interchange.c b/src/cairo-pdf-interchange.c
index 9d7aa42fd..6bda9e8b8 100644
--- a/src/cairo-pdf-interchange.c
+++ b/src/cairo-pdf-interchange.c
@@ -768,13 +768,11 @@ static cairo_int_status_t
 cairo_pdf_interchange_write_dest (cairo_pdf_surface_t *surface,
 				  cairo_link_attrs_t  *link_attrs)
 {
-    cairo_int_status_t status;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
     cairo_pdf_interchange_t *ic = &surface->interchange;
-    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) {
+    if (link_attrs->link_type == TAG_LINK_DEST_AND_URI || link_attrs->link_type == TAG_LINK_DEST) {
 	cairo_pdf_named_dest_t key;
 	cairo_pdf_named_dest_t *named_dest;
 
@@ -797,53 +795,56 @@ 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->object_stream.stream, "   /Dest ");
-	    status = cairo_pdf_interchange_write_explicit_dest (surface,
-                                                                named_dest->page,
-                                                                TRUE,
-                                                                x, y);
-	    return status;
-	}
-    }
+	    if (named_dest->attrs.internal) {
+		_cairo_output_stream_printf (surface->object_stream.stream, "   /Dest ");
+		status = cairo_pdf_interchange_write_explicit_dest (surface,
+								    named_dest->page,
+								    TRUE,
+								    x, y);
+	    } else {
+		char *name = NULL;
 
-    /* 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);
+		status = _cairo_utf8_to_pdf_string (named_dest->attrs.name, &name);
+		if (unlikely (status))
+		    return status;
 
-	if (link_attrs->page <= (int)_cairo_array_num_elements (&surface->pages)) {
-	    _cairo_output_stream_printf (surface->object_stream.stream, "   /Dest ");
-	    return cairo_pdf_interchange_write_explicit_dest (surface,
-							      link_attrs->page,
-							      link_attrs->has_pos,
-							      link_attrs->pos.x,
-							      link_attrs->pos.y);
+		_cairo_output_stream_printf (surface->object_stream.stream, "   /Dest %s\n",
+					     name);
+		free (name);
+	    }
+	    return status;
+	}
+	/* name does not exist */
+	if (link_attrs->link_type == TAG_LINK_DEST_AND_URI) {
+	    /* Don't emit anything. The caller will fallback to emitting a URI destination. */
+	    return CAIRO_INT_STATUS_NOTHING_TO_DO;
 	}
-    }
-
-    /* Link refers to a future or unknown 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);
+	/* Mising destination. Emit a "do nothing" dest that points to the same page and position. */
+	_cairo_tag_warning ("Link to dest=\"%s\" not found", link_attrs->dest);
+	_cairo_output_stream_printf (surface->object_stream.stream, "   /Dest ");
+	status = cairo_pdf_interchange_write_explicit_dest (surface,
+							    link_attrs->link_page,
+							    FALSE,
+							    0, 0);
+	return status;
+    }
 
-    link_res = _cairo_pdf_surface_new_object (surface);
-    if (link_res.id == 0)
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    /* link_attrs->link_type == TAG_LINK_PAGE */
 
-    _cairo_output_stream_printf (surface->object_stream.stream,
-				 "   /Dest %d 0 R\n",
-				 link_res.id);
+    if (link_attrs->page < 1)
+	return _cairo_tag_error ("Link attribute: \"page=%d\" page must be >= 1", link_attrs->page);
 
-    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);
+    if (link_attrs->page > (int)_cairo_array_num_elements (&surface->pages))
+	return _cairo_tag_error ("Link attribute: \"page=%d\" page exceeds page count (%d)",
+				 link_attrs->page, _cairo_array_num_elements (&surface->pages));
 
-    return status;
+    _cairo_output_stream_printf (surface->object_stream.stream, "   /Dest ");
+    return cairo_pdf_interchange_write_explicit_dest (surface,
+						      link_attrs->page,
+						      link_attrs->has_pos,
+						      link_attrs->pos.x,
+						      link_attrs->pos.y);
 }
 
 static cairo_int_status_t
@@ -918,12 +919,20 @@ cairo_pdf_interchange_write_link_action (cairo_pdf_surface_t   *surface,
     cairo_int_status_t status;
     char *dest = NULL;
 
-    if (link_attrs->link_type == TAG_LINK_DEST) {
+    if (link_attrs->link_type == TAG_LINK_DEST_AND_URI ||
+	link_attrs->link_type == TAG_LINK_DEST         ||
+	link_attrs->link_type == TAG_LINK_PAGE)
+    {
 	status = cairo_pdf_interchange_write_dest (surface, link_attrs);
-	if (unlikely (status))
+	if (status != CAIRO_INT_STATUS_NOTHING_TO_DO)
 	    return status;
 
-    } else if (link_attrs->link_type == TAG_LINK_URI) {
+	/* CAIRO_INT_STATUS_NOTHING_TO_DO means that the link type is TAG_LINK_DEST_AND_URI
+	 * and the DEST is missing. Fall through to writing a URI link below.
+	 */
+    }
+
+    if (link_attrs->link_type == TAG_LINK_URI || link_attrs->link_type == TAG_LINK_DEST_AND_URI) {
 	status = _cairo_utf8_to_pdf_string (link_attrs->uri, &dest);
 	if (unlikely (status))
 	    return status;
@@ -1460,67 +1469,6 @@ 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;
-    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++) {
-	link = _cairo_array_index (&surface->forward_links, i);
-	if (link->page > (int)_cairo_array_num_elements (&surface->pages))
-	    return _cairo_tag_error ("Link attribute: \"page=%d\" page exceeds page count (%d)",
-				     link->page, _cairo_array_num_elements (&surface->pages));
-
-
-	status = _cairo_pdf_surface_object_begin (surface, link->res);
-	if (unlikely (status))
-	    return status;
-
-	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);
-    }
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
 static cairo_int_status_t
 cairo_pdf_interchange_write_page_labels (cairo_pdf_surface_t *surface)
 {
@@ -1658,6 +1606,7 @@ _cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface)
 	cairo_pdf_named_dest_t *dest = ic->sorted_dests[i];
 	double x = 0;
 	double y = 0;
+	char *name = NULL;
 
 	if (dest->attrs.internal)
 	    continue;
@@ -1673,13 +1622,19 @@ _cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface)
 	if (dest->attrs.y_valid)
 	    y = dest->attrs.y;
 
+	status = _cairo_utf8_to_pdf_string (dest->attrs.name, &name);
+	if (unlikely (status))
+	    return status;
+
 	page_info = _cairo_array_index (&surface->pages, dest->page - 1);
 	_cairo_output_stream_printf (surface->object_stream.stream,
-				     "   (%s) [%d 0 R /XYZ %f %f 0]\n",
-				     dest->attrs.name,
+				     "   %s [%d 0 R /XYZ %f %f 0]\n",
+				     name,
 				     page_info->page_res.id,
 				     x,
 				     page_info->height - y);
+	free (name);
+
     }
     _cairo_output_stream_printf (surface->object_stream.stream,
 				 "  ]\n"
@@ -2419,10 +2374,6 @@ _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 13ccc5001..bf1d39456 100644
--- a/src/cairo-pdf-surface-private.h
+++ b/src/cairo-pdf-surface-private.h
@@ -266,14 +266,6 @@ typedef struct _cairo_pdf_outline_entry {
     int count;
 } cairo_pdf_outline_entry_t;
 
-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;
-} cairo_pdf_forward_link_t;
-
 struct docinfo {
     char *title;
     char *author;
@@ -468,7 +460,6 @@ 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 713880703..d4c6e39d9 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -555,7 +555,6 @@ _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 *));
@@ -2824,7 +2823,6 @@ _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);
 
      _cairo_hash_table_foreach (surface->color_glyphs,
 				_cairo_pdf_color_glyph_pluck,
diff --git a/src/cairo-tag-attributes-private.h b/src/cairo-tag-attributes-private.h
index 1b770aef9..5853f9a0d 100644
--- a/src/cairo-tag-attributes-private.h
+++ b/src/cairo-tag-attributes-private.h
@@ -47,6 +47,8 @@ typedef enum {
     TAG_LINK_DEST,
     TAG_LINK_URI,
     TAG_LINK_FILE,
+    TAG_LINK_PAGE,
+    TAG_LINK_DEST_AND_URI,
 } cairo_tag_link_type_t;
 
 typedef struct _cairo_content_attrs {
diff --git a/src/cairo-tag-attributes.c b/src/cairo-tag-attributes.c
index 4fde1b5b8..7873bb8da 100644
--- a/src/cairo-tag-attributes.c
+++ b/src/cairo-tag-attributes.c
@@ -655,25 +655,30 @@ _cairo_tag_parse_link_attributes (const char *attributes, cairo_link_attrs_t *li
 	}
     }
 
-    if (link_attrs->uri) {
-	link_attrs->link_type = TAG_LINK_URI;
-	if (link_attrs->dest || link_attrs->page || link_attrs->has_pos || link_attrs->file)
-	    invalid_combination = TRUE;
-
-    } else if (link_attrs->file) {
+    if (link_attrs->file) {
 	link_attrs->link_type = TAG_LINK_FILE;
 	if (link_attrs->uri)
 	    invalid_combination = TRUE;
 	else if (link_attrs->dest && (link_attrs->page || link_attrs->has_pos))
 	    invalid_combination = TRUE;
 
+    } else if (link_attrs->uri && link_attrs->dest) {
+	link_attrs->link_type = TAG_LINK_DEST_AND_URI;
+	if (link_attrs->page || link_attrs->has_pos || link_attrs->file)
+	    invalid_combination = TRUE;
+
+    } else if (link_attrs->uri) {
+	link_attrs->link_type = TAG_LINK_URI;
+	if (link_attrs->dest || link_attrs->page || link_attrs->has_pos || link_attrs->file)
+	    invalid_combination = TRUE;
+
     } else if (link_attrs->dest) {
 	link_attrs->link_type = TAG_LINK_DEST;
 	if (link_attrs->uri || link_attrs->page || link_attrs->has_pos)
 	    invalid_combination = TRUE;
 
     } else if (link_attrs->page) {
-	link_attrs->link_type = TAG_LINK_DEST;
+	link_attrs->link_type = TAG_LINK_PAGE;
 	if (link_attrs->uri || link_attrs->dest)
 	    invalid_combination = TRUE;
 
diff --git a/src/cairo-tag-stack-private.h b/src/cairo-tag-stack-private.h
index 49145bf1d..b3878ddb2 100644
--- a/src/cairo-tag-stack-private.h
+++ b/src/cairo-tag-stack-private.h
@@ -116,4 +116,7 @@ _cairo_tag_get_type (const char *name);
 cairo_private cairo_status_t
 _cairo_tag_error (const char *fmt, ...) CAIRO_PRINTF_FORMAT (1, 2);
 
+cairo_private void
+_cairo_tag_warning (const char *fmt, ...) CAIRO_PRINTF_FORMAT (1, 2);
+
 #endif /* CAIRO_TAG_STACK_PRIVATE_H */
diff --git a/src/cairo-tag-stack.c b/src/cairo-tag-stack.c
index f182b508b..ffe1e483a 100644
--- a/src/cairo-tag-stack.c
+++ b/src/cairo-tag-stack.c
@@ -331,3 +331,17 @@ _cairo_tag_error (const char *fmt, ...)
     }
     return _cairo_error (CAIRO_STATUS_TAG_ERROR);
 }
+
+void
+_cairo_tag_warning (const char *fmt, ...)
+{
+    va_list ap;
+
+    if (getenv ("CAIRO_DEBUG_TAG") != NULL) {
+	printf ("TAG WARNING: ");
+	va_start (ap, fmt);
+	vprintf (fmt, ap);
+	va_end (ap);
+	printf ("\n");
+    }
+}
diff --git a/test/pdf-tagged-text.c b/test/pdf-tagged-text.c
index 3883d418e..094b1c8b0 100644
--- a/test/pdf-tagged-text.c
+++ b/test/pdf-tagged-text.c
@@ -113,6 +113,8 @@ static const char *roman_numerals[] = {
 
 #define MAX_PARAGRAPH_LINES 20
 
+static const char *utf8_destination = "l\xc3\xa4nk";
+
 static int paragraph_num_lines;
 static char *paragraph_text[MAX_PARAGRAPH_LINES];
 static double paragraph_height;
@@ -329,20 +331,20 @@ draw_cover (cairo_surface_t *surface, cairo_t *cr)
 
     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);
+    cairo_move_to (cr, PAGE_WIDTH/3, 0.15*PAGE_HEIGHT);
     cairo_tag_begin (cr, "Span", NULL);
     cairo_show_text (cr, "PDF Features Test");
     cairo_tag_end (cr, "Span");
 
     /* Test URL link using "rect" attribute. The entire rectangle surrounding the URL should be a clickable link.  */
-    cairo_move_to (cr, PAGE_WIDTH/3, 2*PAGE_HEIGHT/3);
+    cairo_move_to (cr, PAGE_WIDTH/3, 0.2*PAGE_HEIGHT);
     cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
     cairo_set_font_size(cr, TEXT_SIZE);
     cairo_set_source_rgb (cr, 0, 0, 1);
     cairo_show_text (cr, cairo_url);
     cairo_text_extents (cr, cairo_url, &text_extents);
     url_box.x = PAGE_WIDTH/3 - url_box_margin;
-    url_box.y = 2*PAGE_HEIGHT/3 - url_box_margin;
+    url_box.y = 0.2*PAGE_HEIGHT - url_box_margin;
     url_box.width = text_extents.width + 2*url_box_margin;
     url_box.height = -text_extents.height + 2*url_box_margin;
     cairo_rectangle(cr, url_box.x, url_box.y, url_box.width, url_box.height);
@@ -355,28 +357,52 @@ draw_cover (cairo_surface_t *surface, cairo_t *cr)
 
     /* 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_move_to (cr, PAGE_WIDTH/3, 0.25*PAGE_HEIGHT);
     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_move_to (cr, PAGE_WIDTH/3, 0.3*PAGE_HEIGHT);
     cairo_show_text (cr, "link to page section 3.3");
     cairo_tag_end (cr, CAIRO_TAG_LINK);
 
     /* Create link to external file */
+    cairo_move_to (cr, PAGE_WIDTH/3, 0.35*PAGE_HEIGHT);
     cairo_tag_begin (cr, CAIRO_TAG_LINK, "file='foo.pdf' page=1");
-    cairo_move_to (cr, PAGE_WIDTH/3, 4.4*PAGE_HEIGHT/5);
     cairo_show_text (cr, "link file 'foo.pdf'");
     cairo_tag_end (cr, CAIRO_TAG_LINK);
 
+    /* Create link to missing dest */
+    cairo_move_to (cr, PAGE_WIDTH/3, 0.4*PAGE_HEIGHT);
+    cairo_tag_begin (cr, CAIRO_TAG_LINK, "dest='I don\\'t exist'");
+    cairo_show_text (cr, "link to missing dest");
+    cairo_tag_end (cr, CAIRO_TAG_LINK);
+
+    /* Create link to missing dest with URI fallback*/
+    cairo_move_to (cr, PAGE_WIDTH/3, 0.45*PAGE_HEIGHT);
+    xasprintf(&attrib, "dest='I also don\\'t exist' uri='%s'", cairo_url);
+    cairo_tag_begin (cr, CAIRO_TAG_LINK, attrib);
+    cairo_show_text (cr, "link to missing dest with uri fallback");
+    cairo_tag_end (cr, CAIRO_TAG_LINK);
+    free (attrib);
+
+    /* Create link to utf8 dest */
+    cairo_move_to (cr, PAGE_WIDTH/3, 0.5*PAGE_HEIGHT);
+    xasprintf(&attrib, "dest='%s'", utf8_destination);
+    cairo_tag_begin (cr, CAIRO_TAG_LINK, attrib);
+    cairo_show_text (cr, "link to utf8 dest");
+    cairo_tag_end (cr, CAIRO_TAG_LINK);
+    free (attrib);
+
     draw_page_num (surface, cr, "cover", 0);
 }
 
 static void
 create_document (cairo_surface_t *surface, cairo_t *cr)
 {
+    char *attrib;
+
     layout_paragraph (cr);
 
     cairo_pdf_surface_set_thumbnail_size (surface, PAGE_WIDTH/10, PAGE_HEIGHT/10);
@@ -491,6 +517,14 @@ create_document (cairo_surface_t *surface, cairo_t *cr)
     cairo_show_text (cr, "link to page 3");
     cairo_tag_end (cr, CAIRO_TAG_LINK);
 
+    /* Create utf8 dest */
+    cairo_move_to (cr, PAGE_WIDTH/3, 4*PAGE_HEIGHT/5);
+    xasprintf(&attrib, "name='%s'", utf8_destination);
+    cairo_tag_begin (cr, CAIRO_TAG_DEST, attrib);
+    cairo_show_text (cr, utf8_destination);
+    cairo_tag_end (cr, CAIRO_TAG_DEST);
+    free (attrib);
+
     cairo_tag_end (cr, "Document");
 }
 


More information about the cairo-commit mailing list