[cairo-commit] 2 commits - doc/public src/cairo-pdf.h src/cairo-pdf-interchange.c src/cairo-pdf-surface.c src/cairo-pdf-surface-private.h test/pdf-tagged-text.c

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Thu Sep 16 20:47:52 UTC 2021


 doc/public/cairo-sections.txt   |    1 
 src/cairo-pdf-interchange.c     |  100 ++++++++++++++++++++++++++++++++++++++++
 src/cairo-pdf-surface-private.h |   11 ++++
 src/cairo-pdf-surface.c         |   36 ++++++++++++++
 src/cairo-pdf.h                 |    5 ++
 test/pdf-tagged-text.c          |    6 ++
 6 files changed, 159 insertions(+)

New commits:
commit 4c520fea2124f1d2d200ca86045f1de138809148
Merge: ffa2374b0 0ce4c0fc2
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Thu Sep 16 20:47:50 2021 +0000

    Merge branch 'custom-metadata' into 'master'
    
    Add cairo_pdf_surface_set_custom_metadata()
    
    See merge request cairo/cairo!240

commit 0ce4c0fc29230a15c12e66571dba2dbfa428285c
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Wed Aug 18 19:41:26 2021 +0930

    Add cairo_pdf_surface_set_custom_metadata()

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
@@ -907,6 +907,42 @@ cairo_pdf_surface_set_metadata (cairo_surface_t      *surface,
 	status = _cairo_surface_set_error (surface, status);
 }
 
+/**
+ * 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
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
@@ -143,6 +143,11 @@ cairo_pdf_surface_set_metadata (cairo_surface_t	     *surface,
 				cairo_pdf_metadata_t  metadata,
                                 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);


More information about the cairo-commit mailing list