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,50 +397,43 @@ 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 */ - - 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->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); + return cairo_pdf_interchange_write_explicit_dest (surface, + link_attrs->page, + link_attrs->has_pos, + link_attrs->pos.x, + link_attrs->pos.y); } } + /* 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); + + 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;