[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