diff options
author | Adrian Johnson <ajohnson@redneon.com> | 2021-08-18 19:41:26 +0930 |
---|---|---|
committer | Adrian Johnson <ajohnson@redneon.com> | 2021-08-22 13:43:34 +0930 |
commit | 0ce4c0fc29230a15c12e66571dba2dbfa428285c (patch) | |
tree | 96ee813d5a8a25d7d563f92efaba22aad74e3966 | |
parent | 2dec76ddfe9499b5a6efc1ccc8e52536a314543b (diff) | |
download | cairo-0ce4c0fc29230a15c12e66571dba2dbfa428285c.tar.gz |
Add cairo_pdf_surface_set_custom_metadata()
-rw-r--r-- | doc/public/cairo-sections.txt | 1 | ||||
-rw-r--r-- | src/cairo-pdf-interchange.c | 100 | ||||
-rw-r--r-- | src/cairo-pdf-surface-private.h | 11 | ||||
-rw-r--r-- | src/cairo-pdf-surface.c | 36 | ||||
-rw-r--r-- | src/cairo-pdf.h | 5 | ||||
-rw-r--r-- | test/pdf-tagged-text.c | 6 |
6 files changed, 159 insertions, 0 deletions
diff --git a/doc/public/cairo-sections.txt b/doc/public/cairo-sections.txt index be958eef1..6f9f86d98 100644 --- a/doc/public/cairo-sections.txt +++ b/doc/public/cairo-sections.txt @@ -82,6 +82,7 @@ cairo_pdf_version_to_string cairo_pdf_surface_set_size cairo_pdf_surface_add_outline cairo_pdf_surface_set_metadata +cairo_pdf_surface_set_custom_metadata cairo_pdf_surface_set_page_label cairo_pdf_surface_set_thumbnail_size </SECTION> diff --git a/src/cairo-pdf-interchange.c b/src/cairo-pdf-interchange.c index 921952afb..38bec977f 100644 --- a/src/cairo-pdf-interchange.c +++ b/src/cairo-pdf-interchange.c @@ -1169,6 +1169,9 @@ cairo_pdf_interchange_write_docinfo (cairo_pdf_surface_t *surface) { cairo_pdf_interchange_t *ic = &surface->interchange; cairo_int_status_t status; + unsigned int i, num_elems; + struct metadata *data; + unsigned char *p; surface->docinfo_res = _cairo_pdf_surface_new_object (surface); if (surface->docinfo_res.id == 0) @@ -1203,6 +1206,26 @@ cairo_pdf_interchange_write_docinfo (cairo_pdf_surface_t *surface) if (ic->docinfo.mod_date) _cairo_output_stream_printf (surface->object_stream.stream, " /ModDate %s\n", ic->docinfo.mod_date); + num_elems = _cairo_array_num_elements (&ic->custom_metadata); + for (i = 0; i < num_elems; i++) { + data = _cairo_array_index (&ic->custom_metadata, i); + if (data->value) { + _cairo_output_stream_printf (surface->object_stream.stream, " /"); + /* The name can be any utf8 string. Use hex codes as + * specified in section 7.3.5 of PDF reference + */ + p = (unsigned char *)data->name; + while (*p) { + if (*p < 0x21 || *p > 0x7e || *p == '#' || *p == '/') + _cairo_output_stream_printf (surface->object_stream.stream, "#%02x", *p); + else + _cairo_output_stream_printf (surface->object_stream.stream, "%c", *p); + p++; + } + _cairo_output_stream_printf (surface->object_stream.stream, " %s\n", data->value); + } + } + _cairo_output_stream_printf (surface->object_stream.stream, ">>\n"); _cairo_pdf_surface_object_end (surface); @@ -1624,6 +1647,7 @@ _cairo_pdf_interchange_init (cairo_pdf_surface_t *surface) return _cairo_error (CAIRO_STATUS_NO_MEMORY); memset (&ic->docinfo, 0, sizeof (ic->docinfo)); + _cairo_array_init (&ic->custom_metadata, sizeof(struct metadata)); _cairo_pdf_interchange_set_create_date (surface); status = _cairo_array_append (&ic->outline, &outline_root); @@ -1654,6 +1678,8 @@ void _cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface) { cairo_pdf_interchange_t *ic = &surface->interchange; + unsigned int i, num_elems; + struct metadata *data; _cairo_tag_stack_fini (&ic->analysis_tag_stack); _cairo_tag_stack_fini (&ic->render_tag_stack); @@ -1674,6 +1700,14 @@ _cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface) free (ic->docinfo.creator); free (ic->docinfo.create_date); free (ic->docinfo.mod_date); + + num_elems = _cairo_array_num_elements (&ic->custom_metadata); + for (i = 0; i < num_elems; i++) { + data = _cairo_array_index (&ic->custom_metadata, i); + free (data->name); + free (data->value); + } + _cairo_array_fini (&ic->custom_metadata); } cairo_int_status_t @@ -1868,3 +1902,69 @@ _cairo_pdf_interchange_set_metadata (cairo_pdf_surface_t *surface, return CAIRO_STATUS_SUCCESS; } + +static const char *reserved_metadata_names[] = { + "", + "Title", + "Author", + "Subject", + "Keywords", + "Creator", + "Producer", + "CreationDate", + "ModDate", + "Trapped", +}; + +cairo_int_status_t +_cairo_pdf_interchange_set_custom_metadata (cairo_pdf_surface_t *surface, + const char *name, + const char *value) +{ + cairo_pdf_interchange_t *ic = &surface->interchange; + struct metadata *data; + struct metadata new_data; + int i, num_elems; + cairo_int_status_t status; + char *s = NULL; + + if (name == NULL) + return CAIRO_STATUS_NULL_POINTER; + + for (i = 0; i < ARRAY_LENGTH (reserved_metadata_names); i++) { + if (strcmp(name, reserved_metadata_names[i]) == 0) + return CAIRO_STATUS_INVALID_STRING; + } + + /* First check if we already have an entry for this name. If so, + * update the value. A NULL value means the entry has been removed + * and will not be emitted. */ + num_elems = _cairo_array_num_elements (&ic->custom_metadata); + for (i = 0; i < num_elems; i++) { + data = _cairo_array_index (&ic->custom_metadata, i); + if (strcmp(name, data->name) == 0) { + free (data->value); + data->value = NULL; + if (value && strlen(value)) { + status = _cairo_utf8_to_pdf_string (value, &s); + if (unlikely (status)) + return status; + data->value = s; + } + return CAIRO_STATUS_SUCCESS; + } + } + + /* Add new entry */ + status = CAIRO_STATUS_SUCCESS; + if (value && strlen(value)) { + new_data.name = strdup (name); + status = _cairo_utf8_to_pdf_string (value, &s); + if (unlikely (status)) + return status; + new_data.value = s; + status = _cairo_array_append (&ic->custom_metadata, &new_data); + } + + return status; +} diff --git a/src/cairo-pdf-surface-private.h b/src/cairo-pdf-surface-private.h index b2d857550..473229f26 100644 --- a/src/cairo-pdf-surface-private.h +++ b/src/cairo-pdf-surface-private.h @@ -227,6 +227,11 @@ struct docinfo { char *mod_date; }; +struct metadata { + char *name; + char *value; +}; + typedef struct _cairo_pdf_interchange { cairo_tag_stack_t analysis_tag_stack; cairo_tag_stack_t render_tag_stack; @@ -248,6 +253,7 @@ typedef struct _cairo_pdf_interchange { int annot_page; cairo_array_t outline; /* array of pointers to cairo_pdf_outline_entry_t; */ struct docinfo docinfo; + cairo_array_t custom_metadata; /* array of struct metadata */ } cairo_pdf_interchange_t; @@ -420,4 +426,9 @@ _cairo_pdf_interchange_set_metadata (cairo_pdf_surface_t *surface, cairo_pdf_metadata_t metadata, const char *utf8); +cairo_private cairo_int_status_t +_cairo_pdf_interchange_set_custom_metadata (cairo_pdf_surface_t *surface, + const char *name, + const char *value); + #endif /* CAIRO_PDF_SURFACE_PRIVATE_H */ diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index 3c9d12471..a19bb8aca 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -908,6 +908,42 @@ cairo_pdf_surface_set_metadata (cairo_surface_t *surface, } /** + * cairo_pdf_surface_set_custom_metadata: + * @surface: a PDF #cairo_surface_t + * @name: The name of the custom metadata item to set (utf8). + * @value: The value of the metadata (utf8). + * + * Set custom document metadata. @name may be any string except for + * the following names reserved by PDF: "Title", "Author", "Subject", + * "Keywords", "Creator", "Producer", "CreationDate", "ModDate", + * "Trapped". + * + * If @value is NULL or an empty string, the @name metadata will not be set. + * + * For example: + * <informalexample><programlisting> + * cairo_pdf_surface_set_custom_metadata (surface, "ISBN", "978-0123456789"); + * </programlisting></informalexample> + * + * Since: 1.18 + **/ +void +cairo_pdf_surface_set_custom_metadata (cairo_surface_t *surface, + const char *name, + const char *value) +{ + cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */ + cairo_status_t status; + + if (! _extract_pdf_surface (surface, &pdf_surface)) + return; + + status = _cairo_pdf_interchange_set_custom_metadata (pdf_surface, name, value); + if (status) + status = _cairo_surface_set_error (surface, status); +} + +/** * cairo_pdf_surface_set_page_label: * @surface: a PDF #cairo_surface_t * @utf8: The page label. diff --git a/src/cairo-pdf.h b/src/cairo-pdf.h index 5be0b3f1b..49afb687e 100644 --- a/src/cairo-pdf.h +++ b/src/cairo-pdf.h @@ -144,6 +144,11 @@ cairo_pdf_surface_set_metadata (cairo_surface_t *surface, const char *utf8); cairo_public void +cairo_pdf_surface_set_custom_metadata (cairo_surface_t *surface, + const char *name, + const char *value); + +cairo_public void cairo_pdf_surface_set_page_label (cairo_surface_t *surface, const char *utf8); diff --git a/test/pdf-tagged-text.c b/test/pdf-tagged-text.c index 8908eb25d..40c83b466 100644 --- a/test/pdf-tagged-text.c +++ b/test/pdf-tagged-text.c @@ -377,6 +377,12 @@ create_document (cairo_surface_t *surface, cairo_t *cr) cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_CREATE_DATE, "2016-01-01T12:34:56+10:30"); cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_MOD_DATE, "2016-06-21T05:43:21Z"); + cairo_pdf_surface_set_custom_metadata (surface, "DocumentNumber", "12345"); + /* Include some non ASCII characters */ + cairo_pdf_surface_set_custom_metadata (surface, "Document Name", "\xc2\xab""cairo test\xc2\xbb"); + /* Test unsetting custom metadata. "DocumentNumber" should not be emitted. */ + cairo_pdf_surface_set_custom_metadata (surface, "DocumentNumber", ""); + cairo_tag_begin (cr, "Document", NULL); draw_cover (surface, cr); |