pdf: fix link positions

Converting the link position from cairo to pdf coordinates requires
the page height. Since the link may point to a different page, build
an array of the height of each page and use the target page height for
the conversion.

Don't default to a [0,0] position if "pos" is not specified. PDF
allows a null destination position to be specified which means don't
change the position if the page is already displayed or show top left
if switching to a different page. This is more useful default
particularly for external files where the coordinates (which must be
in PDF coordinates as we don't know the page height) of the top left
corner may not be known.
This commit is contained in:
Adrian Johnson 2017-08-26 09:58:48 +09:30
parent 74c6e3ae1d
commit df37baf789
6 changed files with 50 additions and 22 deletions

View file

@ -239,6 +239,7 @@ cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface,
int sp;
char *dest = NULL;
int i, num_rects;
double height;
num_rects = _cairo_array_num_elements (&node->annot.link_attrs.rects);
if (strcmp (node->name, CAIRO_TAG_LINK) == 0 &&
@ -266,6 +267,7 @@ cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface,
res.id,
sp);
_cairo_array_copy_element (&surface->page_heights, node->annot.src_page - 1, &height);
if (num_rects > 0) {
cairo_rectangle_int_t bbox_rect;
@ -282,18 +284,18 @@ cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface,
else
_cairo_rectangle_union (&bbox_rect, &recti);
write_rect_to_pdf_quad_points (surface->output, &rectf, node->annot.page_height);
write_rect_to_pdf_quad_points (surface->output, &rectf, height);
_cairo_output_stream_printf (surface->output, " ");
}
_cairo_output_stream_printf (surface->output,
"]\n"
" /Rect [ ");
write_rect_int_to_pdf_bbox (surface->output, &bbox_rect, node->annot.page_height);
write_rect_int_to_pdf_bbox (surface->output, &bbox_rect, height);
_cairo_output_stream_printf (surface->output, " ]\n");
} else {
_cairo_output_stream_printf (surface->output,
" /Rect [ ");
write_rect_int_to_pdf_bbox (surface->output, &node->annot.extents.extents, node->annot.page_height);
write_rect_int_to_pdf_bbox (surface->output, &node->annot.extents.extents, height);
_cairo_output_stream_printf (surface->output, " ]\n");
}
@ -315,12 +317,19 @@ cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface,
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);
_cairo_output_stream_printf (surface->output,
" /Dest [%d 0 R /XYZ %f %f 0]\n",
res.id,
node->annot.link_attrs.pos.x,
node->annot.page_height - node->annot.link_attrs.pos.y);
if (node->annot.link_attrs.has_pos) {
_cairo_output_stream_printf (surface->output,
" /Dest [%d 0 R /XYZ %f %f 0]\n",
res.id,
node->annot.link_attrs.pos.x,
height - node->annot.link_attrs.pos.y);
} else {
_cairo_output_stream_printf (surface->output,
" /Dest [%d 0 R /XYZ null null 0]\n",
res.id);
}
}
} else if (node->annot.link_attrs.link_type == TAG_LINK_URI) {
_cairo_output_stream_printf (surface->output,
@ -342,11 +351,17 @@ cairo_pdf_interchange_write_annot (cairo_pdf_surface_t *surface,
" /D %s\n",
dest);
} else {
_cairo_output_stream_printf (surface->output,
" /D [%d %f %f ]\n",
node->annot.link_attrs.page,
node->annot.link_attrs.pos.x,
node->annot.link_attrs.pos.y);
if (node->annot.link_attrs.has_pos) {
_cairo_output_stream_printf (surface->output,
" /D [%d %f %f 0]\n",
node->annot.link_attrs.page,
node->annot.link_attrs.pos.x,
node->annot.link_attrs.pos.y);
} else {
_cairo_output_stream_printf (surface->output,
" /D [%d null null 0]\n",
node->annot.link_attrs.page);
}
}
_cairo_output_stream_printf (surface->output,
" >>\n");
@ -769,6 +784,7 @@ _cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface)
cairo_pdf_resource_t page_res;
double x = 0;
double y = 0;
double height;
if (dest->extents.valid) {
x = dest->extents.extents.x;
@ -782,12 +798,13 @@ _cairo_pdf_interchange_write_document_dests (cairo_pdf_surface_t *surface)
y = dest->attrs.y;
_cairo_array_copy_element (&surface->pages, dest->page - 1, &page_res);
_cairo_array_copy_element (&surface->page_heights, dest->page - 1, &height);
_cairo_output_stream_printf (surface->output,
" (%s) [ %d 0 R /XYZ %f %f ]\n",
" (%s) [%d 0 R /XYZ %f %f 0]\n",
dest->attrs.name,
page_res.id,
x,
surface->height - y);
height - y);
}
_cairo_output_stream_printf (surface->output,
" ]\n"
@ -914,7 +931,7 @@ _cairo_pdf_interchange_begin_structure_tag (cairo_pdf_surface_t *surface,
if (unlikely (status))
return status;
ic->current_node->annot.page_height = surface->height;
ic->current_node->annot.src_page = _cairo_array_num_elements (&surface->pages);;
cairo_list_add_tail (&ic->current_node->annot.extents.link, &ic->extents_list);
}
@ -950,7 +967,6 @@ _cairo_pdf_interchange_begin_dest_tag (cairo_pdf_surface_t *surface,
if (unlikely (status))
return status;
dest->page_height = surface->height;
dest->page = _cairo_array_num_elements (&surface->pages);
init_named_dest_key (dest);
status = _cairo_hash_table_insert (ic->named_dests, &dest->base);

View file

@ -179,7 +179,7 @@ typedef struct _cairo_pdf_struct_tree_node {
struct tag_extents extents;
cairo_pdf_resource_t res;
cairo_link_attrs_t link_attrs;
double page_height;
int src_page; /* page number containing the link */
} annot;
cairo_list_t link;
} cairo_pdf_struct_tree_node_t;
@ -189,7 +189,6 @@ typedef struct _cairo_pdf_named_dest {
struct tag_extents extents;
cairo_dest_attrs_t attrs;
int page;
double page_height;
cairo_bool_t referenced;
} cairo_pdf_named_dest_t;
@ -267,6 +266,7 @@ struct _cairo_pdf_surface {
cairo_array_t smask_groups;
cairo_array_t knockout_group;
cairo_array_t jbig2_global;
cairo_array_t page_heights;
cairo_scaled_font_subsets_t *font_subsets;
cairo_array_t fonts;

View file

@ -387,6 +387,7 @@ _cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output,
_cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t));
_cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t));
_cairo_array_init (&surface->jbig2_global, sizeof (cairo_pdf_jbig2_global_t));
_cairo_array_init (&surface->page_heights, sizeof (double));
surface->all_surfaces = _cairo_hash_table_create (_cairo_pdf_source_surface_equal);
if (unlikely (surface->all_surfaces == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
@ -2313,6 +2314,7 @@ _cairo_pdf_surface_finish (void *abstract_surface)
return _cairo_error (CAIRO_STATUS_JBIG2_GLOBAL_MISSING);
}
_cairo_array_fini (&surface->jbig2_global);
_cairo_array_fini (&surface->page_heights);
size = _cairo_array_num_elements (&surface->page_labels);
for (i = 0; i < size; i++) {
@ -4876,7 +4878,14 @@ _cairo_pdf_surface_show_page (void *abstract_surface)
cairo_pdf_surface_t *surface = abstract_surface;
cairo_int_status_t status;
status = _cairo_array_append (&surface->page_heights, &surface->height);
if (unlikely (status))
return status;
status = _cairo_array_append (&surface->page_labels, &surface->current_page_label);
if (unlikely (status))
return status;
surface->current_page_label = NULL;
status = _cairo_pdf_interchange_end_page_content (surface);

View file

@ -55,6 +55,7 @@ typedef struct _cairo_link_attrs {
char *uri;
char *file;
int page;
cairo_bool_t has_pos;
cairo_point_double_t pos;
} cairo_link_attrs_t;

View file

@ -495,6 +495,7 @@ _cairo_tag_parse_link_attributes (const char *attributes, cairo_link_attrs_t *li
link_attrs->pos.x = val.f;
_cairo_array_copy_element (&attr->array, 1, &val);
link_attrs->pos.y = val.f;
link_attrs->has_pos = TRUE;
} else if (strcmp (attr->name, "rect") == 0) {
cairo_rectangle_t rect;
int i;

View file

@ -193,7 +193,7 @@
* @page: An integer specifying the page number in the PDF file to link to.
*
* @pos: [optional] An array of two floats specifying the x,y position
* on the page. Default is 0,0.
* on the page.
*
* An example of the link attributes to link to a page and x,y position:
* <programlisting>
@ -227,8 +227,9 @@
*
* @page: An integer specifying the page number in the PDF file.
*
* @pos: [optional] An array of two floats specifying the x,y position
* on the page. Default is 0,0.
* @pos: [optional] An array of two floats specifying the x,y
* position on the page. Position coordinates in external files are in PDF
* coordinates (0,0 at bottom left).
*
* An example of the link attributes to PDF file:
* <programlisting>