summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Johnson <ajohnson@redneon.com>2016-10-01 22:28:02 +0930
committerAdrian Johnson <ajohnson@redneon.com>2016-10-01 22:28:02 +0930
commitdfc7b9e6698d5923a858ae9a77345c983ab51e4c (patch)
tree2f409df3a7bcc4f5769aeb979221d6e71a5f5557
parentdcbfb726478f5ab2277bd52813b40e565612566a (diff)
pdf: add document outline API
-rw-r--r--doc/public/cairo-sections.txt3
-rw-r--r--src/cairo-pdf-interchange.c182
-rw-r--r--src/cairo-pdf-surface-private.h22
-rw-r--r--src/cairo-pdf-surface.c59
-rw-r--r--src/cairo-pdf.h28
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 */