summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Johnson <ajohnson@redneon.com>2021-08-18 19:41:26 +0930
committerAdrian Johnson <ajohnson@redneon.com>2021-08-22 13:43:34 +0930
commit0ce4c0fc29230a15c12e66571dba2dbfa428285c (patch)
tree96ee813d5a8a25d7d563f92efaba22aad74e3966
parent2dec76ddfe9499b5a6efc1ccc8e52536a314543b (diff)
downloadcairo-0ce4c0fc29230a15c12e66571dba2dbfa428285c.tar.gz
Add cairo_pdf_surface_set_custom_metadata()
-rw-r--r--doc/public/cairo-sections.txt1
-rw-r--r--src/cairo-pdf-interchange.c100
-rw-r--r--src/cairo-pdf-surface-private.h11
-rw-r--r--src/cairo-pdf-surface.c36
-rw-r--r--src/cairo-pdf.h5
-rw-r--r--test/pdf-tagged-text.c6
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);