Add cairo_pdf_surface_set_custom_metadata()

This commit is contained in:
Adrian Johnson 2021-08-18 19:41:26 +09:30
parent 2dec76ddfe
commit 0ce4c0fc29
6 changed files with 159 additions and 0 deletions

View file

@ -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>

View file

@ -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;
}

View file

@ -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 */

View file

@ -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

View file

@ -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);

View file

@ -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);