Bug 1722300 - patch 2 - Pull in cairo MR "pdf links: fix forward references to dest names with 'internal' flag". r=jrmuizel

This is https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/220, required so that
we can use the 'internal' attribute when generating link destinations.

Differential Revision: https://phabricator.services.mozilla.com/D121239
This commit is contained in:
Jonathan Kew 2021-08-04 12:52:37 +00:00
Родитель c0b0f4fc91
Коммит c8767e7306
3 изменённых файлов: 79 добавлений и 49 удалений

Просмотреть файл

@ -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);

Просмотреть файл

@ -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;

Просмотреть файл

@ -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. */