diff options
author | Adrian Johnson <ajohnson@redneon.com> | 2016-10-01 22:28:02 +0930 |
---|---|---|
committer | Adrian Johnson <ajohnson@redneon.com> | 2016-10-01 22:28:02 +0930 |
commit | dfc7b9e6698d5923a858ae9a77345c983ab51e4c (patch) | |
tree | 2f409df3a7bcc4f5769aeb979221d6e71a5f5557 | |
parent | dcbfb726478f5ab2277bd52813b40e565612566a (diff) |
pdf: add document outline API
-rw-r--r-- | doc/public/cairo-sections.txt | 3 | ||||
-rw-r--r-- | src/cairo-pdf-interchange.c | 182 | ||||
-rw-r--r-- | src/cairo-pdf-surface-private.h | 22 | ||||
-rw-r--r-- | src/cairo-pdf-surface.c | 59 | ||||
-rw-r--r-- | src/cairo-pdf.h | 28 |
5 files changed, 293 insertions, 1 deletions
diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt index fdffc033f..7446415ab 100644 --- a/doc/public/cairo-sections.txt +++ b/doc/public/cairo-sections.txt @@ -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> diff --git a/src/cairo-pdf-interchange.c b/src/cairo-pdf-interchange.c index 95c8ba67f..38c5bac09 100644 --- a/src/cairo-pdf-interchange.c +++ b/src/cairo-pdf-interchange.c @@ -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; } diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h index cbe4599a1..010b127de 100644 --- a/src/cairo-pdf-surface-private.h +++ b/src/cairo-pdf-surface-private.h @@ -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 */ diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index a32dfb6bc..98c780b91 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -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", diff --git a/src/cairo-pdf.h b/src/cairo-pdf.h index 1bc8524f2..e8ea80115 100644 --- a/src/cairo-pdf.h +++ b/src/cairo-pdf.h @@ -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 */ |