mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-05 22:48:07 +02:00
pdf: add document outline API
This commit is contained in:
parent
dcbfb72647
commit
dfc7b9e669
5 changed files with 293 additions and 1 deletions
|
|
@ -68,6 +68,8 @@ cairo_image_surface_get_stride
|
|||
<SECTION>
|
||||
<FILE>cairo-pdf</FILE>
|
||||
CAIRO_HAS_PDF_SURFACE
|
||||
CAIRO_PDF_OUTLINE_ROOT
|
||||
cairo_pdf_outline_flags_t
|
||||
cairo_pdf_surface_create
|
||||
cairo_pdf_surface_create_for_stream
|
||||
cairo_pdf_surface_restrict_to_version
|
||||
|
|
@ -75,6 +77,7 @@ cairo_pdf_version_t
|
|||
cairo_pdf_get_versions
|
||||
cairo_pdf_version_to_string
|
||||
cairo_pdf_surface_set_size
|
||||
cairo_pdf_surface_add_outline
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
|
|
|
|||
|
|
@ -506,6 +506,99 @@ cairo_pdf_interchange_write_parent_tree (cairo_pdf_surface_t *surface)
|
|||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static cairo_int_status_t
|
||||
cairo_pdf_interchange_write_outline (cairo_pdf_surface_t *surface)
|
||||
{
|
||||
int num_elems, i;
|
||||
cairo_pdf_outline_entry_t *outline;
|
||||
cairo_pdf_interchange_t *ic = &surface->interchange;
|
||||
cairo_int_status_t status;
|
||||
char *name = NULL;
|
||||
char *dest = NULL;
|
||||
|
||||
num_elems = _cairo_array_num_elements (&ic->outline);
|
||||
if (num_elems < 2)
|
||||
return CAIRO_INT_STATUS_SUCCESS;
|
||||
|
||||
_cairo_array_copy_element (&ic->outline, 0, &outline);
|
||||
outline->res = _cairo_pdf_surface_new_object (surface);
|
||||
surface->outlines_dict_res = outline->res;
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
"%d 0 obj\n"
|
||||
"<< /Type /Outlines\n"
|
||||
" /First %d 0 R\n"
|
||||
" /Last %d 0 R\n"
|
||||
" /Count %d\n"
|
||||
">>\n"
|
||||
"endobj\n",
|
||||
outline->res.id,
|
||||
outline->first_child->res.id,
|
||||
outline->last_child->res.id,
|
||||
outline->count);
|
||||
|
||||
for (i = 1; i < num_elems; i++) {
|
||||
_cairo_array_copy_element (&ic->outline, i, &outline);
|
||||
_cairo_pdf_surface_update_object (surface, outline->res);
|
||||
status = _cairo_utf8_to_pdf_string (outline->name, &name);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
status = _cairo_utf8_to_pdf_string (outline->dest, &dest);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
"%d 0 obj\n"
|
||||
"<< /Title %s\n"
|
||||
" /Parent %d 0 R\n",
|
||||
outline->res.id,
|
||||
name,
|
||||
outline->parent->res.id);
|
||||
|
||||
if (outline->prev) {
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
" /Prev %d 0 R\n",
|
||||
outline->prev->res.id);
|
||||
}
|
||||
|
||||
if (outline->next) {
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
" /Next %d 0 R\n",
|
||||
outline->next->res.id);
|
||||
}
|
||||
|
||||
if (outline->first_child) {
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
" /First %d 0 R\n"
|
||||
" /Last %d 0 R\n"
|
||||
" /Count %d\n",
|
||||
outline->first_child->res.id,
|
||||
outline->last_child->res.id,
|
||||
outline->count);
|
||||
}
|
||||
|
||||
if (outline->flags) {
|
||||
int flags = 0;
|
||||
if (outline->flags & CAIRO_BOOKMARK_FLAG_ITALIC)
|
||||
flags |= 1;
|
||||
if (outline->flags & CAIRO_BOOKMARK_FLAG_BOLD)
|
||||
flags |= 2;
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
" /F %d\n",
|
||||
flags);
|
||||
}
|
||||
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
" /Dest %s\n"
|
||||
">>\n"
|
||||
"endobj\n",
|
||||
dest);
|
||||
free (dest);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
_collect_dest (void *entry, void *closure)
|
||||
{
|
||||
|
|
@ -937,6 +1030,10 @@ _cairo_pdf_interchange_write_document_objects (cairo_pdf_surface_t *surface)
|
|||
if (_cairo_tag_stack_get_structure_type (&ic->analysis_tag_stack) == TAG_TREE_TYPE_TAGGED)
|
||||
surface->tagged = TRUE;
|
||||
|
||||
status = cairo_pdf_interchange_write_outline (surface);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
status = cairo_pdf_interchange_write_names_dict (surface);
|
||||
|
||||
return status;
|
||||
|
|
@ -946,6 +1043,8 @@ cairo_int_status_t
|
|||
_cairo_pdf_interchange_init (cairo_pdf_surface_t *surface)
|
||||
{
|
||||
cairo_pdf_interchange_t *ic = &surface->interchange;
|
||||
cairo_pdf_outline_entry_t *outline_root;
|
||||
cairo_int_status_t status;
|
||||
|
||||
_cairo_tag_stack_init (&ic->analysis_tag_stack);
|
||||
_cairo_tag_stack_init (&ic->render_tag_stack);
|
||||
|
|
@ -971,13 +1070,21 @@ _cairo_pdf_interchange_init (cairo_pdf_surface_t *surface)
|
|||
ic->sorted_dests = NULL;
|
||||
ic->dests_res.id = 0;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
_cairo_array_init (&ic->outline, sizeof(cairo_pdf_outline_entry_t *));
|
||||
outline_root = calloc (1, sizeof(cairo_pdf_outline_entry_t));
|
||||
if (unlikely (outline_root == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
status = _cairo_array_append (&ic->outline, &outline_root);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
cairo_int_status_t
|
||||
_cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface)
|
||||
{
|
||||
cairo_pdf_interchange_t *ic = &surface->interchange;
|
||||
unsigned i;
|
||||
|
||||
_cairo_tag_stack_fini (&ic->analysis_tag_stack);
|
||||
_cairo_tag_stack_fini (&ic->render_tag_stack);
|
||||
|
|
@ -989,5 +1096,78 @@ _cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface)
|
|||
_cairo_hash_table_destroy (ic->named_dests);
|
||||
free (ic->sorted_dests);
|
||||
|
||||
for (i = 0; i < _cairo_array_num_elements (&ic->outline); i++) {
|
||||
cairo_pdf_outline_entry_t *outline;
|
||||
|
||||
_cairo_array_copy_element (&ic->outline, i, &outline);
|
||||
free (outline);
|
||||
}
|
||||
_cairo_array_fini (&ic->outline);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
cairo_int_status_t
|
||||
_cairo_pdf_interchange_add_outline (cairo_pdf_surface_t *surface,
|
||||
int parent_id,
|
||||
const char *name,
|
||||
const char *dest,
|
||||
cairo_pdf_outline_flags_t flags,
|
||||
int *id)
|
||||
{
|
||||
cairo_pdf_interchange_t *ic = &surface->interchange;
|
||||
cairo_pdf_outline_entry_t *outline;
|
||||
cairo_pdf_outline_entry_t *parent;
|
||||
cairo_int_status_t status;
|
||||
|
||||
if (parent_id < 0 || parent_id >= (int)_cairo_array_num_elements (&ic->outline))
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
outline = _cairo_malloc (sizeof(cairo_pdf_outline_entry_t));
|
||||
if (unlikely (outline == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
outline->res = _cairo_pdf_surface_new_object (surface);
|
||||
if (outline->res.id == 0)
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
outline->name = strdup (name);
|
||||
outline->dest = strdup (dest);
|
||||
outline->flags = flags;
|
||||
outline->count = 0;
|
||||
|
||||
_cairo_array_copy_element (&ic->outline, parent_id, &parent);
|
||||
|
||||
outline->parent = parent;
|
||||
outline->first_child = NULL;
|
||||
outline->last_child = NULL;
|
||||
outline->next = NULL;
|
||||
if (parent->last_child) {
|
||||
parent->last_child->next = outline;
|
||||
outline->prev = parent->last_child;
|
||||
parent->last_child = outline;
|
||||
} else {
|
||||
parent->first_child = outline;
|
||||
parent->last_child = outline;
|
||||
outline->prev = NULL;
|
||||
}
|
||||
|
||||
*id = _cairo_array_num_elements (&ic->outline);
|
||||
status = _cairo_array_append (&ic->outline, &outline);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
/* Update Count */
|
||||
outline = outline->parent;
|
||||
while (outline) {
|
||||
if (outline->flags & CAIRO_BOOKMARK_FLAG_OPEN) {
|
||||
outline->count++;
|
||||
} else {
|
||||
outline->count--;
|
||||
break;
|
||||
}
|
||||
outline = outline->parent;
|
||||
}
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,6 +193,19 @@ typedef struct _cairo_pdf_named_dest {
|
|||
cairo_bool_t referenced;
|
||||
} cairo_pdf_named_dest_t;
|
||||
|
||||
typedef struct _cairo_pdf_outline_entry {
|
||||
char *name;
|
||||
char *dest;
|
||||
cairo_pdf_outline_flags_t flags;
|
||||
cairo_pdf_resource_t res;
|
||||
struct _cairo_pdf_outline_entry *parent;
|
||||
struct _cairo_pdf_outline_entry *first_child;
|
||||
struct _cairo_pdf_outline_entry *last_child;
|
||||
struct _cairo_pdf_outline_entry *next;
|
||||
struct _cairo_pdf_outline_entry *prev;
|
||||
int count;
|
||||
} cairo_pdf_outline_entry_t;
|
||||
|
||||
typedef struct _cairo_pdf_interchange {
|
||||
cairo_tag_stack_t analysis_tag_stack;
|
||||
cairo_tag_stack_t render_tag_stack;
|
||||
|
|
@ -211,6 +224,7 @@ typedef struct _cairo_pdf_interchange {
|
|||
cairo_pdf_named_dest_t **sorted_dests;
|
||||
cairo_pdf_resource_t dests_res;
|
||||
int annot_page;
|
||||
cairo_array_t outline; /* array of pointers to cairo_pdf_outline_entry_t; */
|
||||
|
||||
} cairo_pdf_interchange_t;
|
||||
|
||||
|
|
@ -298,6 +312,7 @@ struct _cairo_pdf_surface {
|
|||
int page_parent_tree; /* -1 if not used */
|
||||
cairo_array_t page_annots;
|
||||
cairo_bool_t tagged;
|
||||
cairo_pdf_resource_t outlines_dict_res;
|
||||
cairo_pdf_resource_t names_dict_res;
|
||||
|
||||
cairo_surface_t *paginated_surface;
|
||||
|
|
@ -344,5 +359,12 @@ _cairo_pdf_interchange_write_page_objects (cairo_pdf_surface_t *surface);
|
|||
cairo_private cairo_int_status_t
|
||||
_cairo_pdf_interchange_write_document_objects (cairo_pdf_surface_t *surface);
|
||||
|
||||
cairo_private cairo_int_status_t
|
||||
_cairo_pdf_interchange_add_outline (cairo_pdf_surface_t *surface,
|
||||
int parent_id,
|
||||
const char *name,
|
||||
const char *dest,
|
||||
cairo_pdf_outline_flags_t flags,
|
||||
int *id);
|
||||
|
||||
#endif /* CAIRO_PDF_SURFACE_PRIVATE_H */
|
||||
|
|
|
|||
|
|
@ -450,6 +450,7 @@ _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));
|
||||
surface->tagged = FALSE;
|
||||
surface->outlines_dict_res.id = 0;
|
||||
surface->names_dict_res.id = 0;
|
||||
|
||||
surface->paginated_surface = _cairo_paginated_surface_create (
|
||||
|
|
@ -720,6 +721,58 @@ cairo_pdf_surface_set_size (cairo_surface_t *surface,
|
|||
status = _cairo_surface_set_error (surface, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* CAIRO_PDF_OUTLINE_ROOT:
|
||||
*
|
||||
* The root outline item in cairo_pdf_surface_add_outline().
|
||||
*
|
||||
* Since: 1.16
|
||||
**/
|
||||
|
||||
/**
|
||||
* cairo_pdf_surface_add_outline:
|
||||
* @surface: a PDF #cairo_surface_t
|
||||
* @parent_id: the id of the parent item or %CAIRO_PDF_OUTLINE_ROOT if this is a top level item.
|
||||
* @utf8: the name of the outline
|
||||
* @dest: the name of the destination
|
||||
* @flags: outline item flags
|
||||
*
|
||||
* Add an item to the document outline hierarchy with the name @utf8 that links to the
|
||||
* destinaton @dest. Destinations are created using
|
||||
* cairo_tag_begin()/cairo_tag_end() with the
|
||||
* %CAIRO_TAG_DEST. The item will be a child of the item with id @parent_id. Use %CAIRO_PDF_OUTLINE_ROOT
|
||||
* as the parent id of top level items.
|
||||
*
|
||||
* Return value: the id for the added item.
|
||||
*
|
||||
* Since: 1.16
|
||||
**/
|
||||
int
|
||||
cairo_pdf_surface_add_outline (cairo_surface_t *surface,
|
||||
int parent_id,
|
||||
const char *utf8,
|
||||
const char *dest,
|
||||
cairo_pdf_outline_flags_t flags)
|
||||
{
|
||||
cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
|
||||
cairo_status_t status;
|
||||
int id = 0;
|
||||
|
||||
if (! _extract_pdf_surface (surface, &pdf_surface))
|
||||
return 0;
|
||||
|
||||
status = _cairo_pdf_interchange_add_outline (pdf_surface,
|
||||
parent_id,
|
||||
utf8,
|
||||
dest,
|
||||
flags,
|
||||
&id);
|
||||
if (status)
|
||||
status = _cairo_surface_set_error (surface, status);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static void
|
||||
_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface)
|
||||
{
|
||||
|
|
@ -6107,6 +6160,12 @@ _cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface)
|
|||
}
|
||||
}
|
||||
|
||||
if (surface->outlines_dict_res.id != 0) {
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
" /Outlines %d 0 R\n",
|
||||
surface->outlines_dict_res.id);
|
||||
}
|
||||
|
||||
if (surface->names_dict_res.id != 0) {
|
||||
_cairo_output_stream_printf (surface->output,
|
||||
" /Names %d 0 R\n",
|
||||
|
|
|
|||
|
|
@ -85,6 +85,34 @@ cairo_pdf_surface_set_size (cairo_surface_t *surface,
|
|||
double width_in_points,
|
||||
double height_in_points);
|
||||
|
||||
/**
|
||||
* cairo_pdf_outline_flags_t:
|
||||
* @CAIRO_BOOKMARK_FLAG_OPEN: The outline item defaults to open in the PDF viewer (Since 1.16)
|
||||
* @CAIRO_BOOKMARK_FLAG_BOLD: The outline item is displayed by the viewer in bold text (Since 1.16)
|
||||
* @CAIRO_BOOKMARK_FLAG_ITALIC: The outline item is displayed by the viewer in italic text (Since 1.16)
|
||||
*
|
||||
* #cairo_pdf_outline_flags_t is used by the
|
||||
* cairo_pdf_surface_add_outline() function specify the attributes of
|
||||
* an outline item. These flags may be bitwise-or'd to produce any
|
||||
* combination of flags.
|
||||
*
|
||||
* Since: 1.16
|
||||
**/
|
||||
typedef enum _cairo_pdf_outline_flags {
|
||||
CAIRO_BOOKMARK_FLAG_OPEN = 0x1,
|
||||
CAIRO_BOOKMARK_FLAG_BOLD = 0x2,
|
||||
CAIRO_BOOKMARK_FLAG_ITALIC = 0x4,
|
||||
} cairo_pdf_outline_flags_t;
|
||||
|
||||
#define CAIRO_PDF_OUTLINE_ROOT 0
|
||||
|
||||
cairo_public int
|
||||
cairo_pdf_surface_add_outline (cairo_surface_t *surface,
|
||||
int parent_id,
|
||||
const char *utf8,
|
||||
const char *dest,
|
||||
cairo_pdf_outline_flags_t flags);
|
||||
|
||||
CAIRO_END_DECLS
|
||||
|
||||
#else /* CAIRO_HAS_PDF_SURFACE */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue