pdf: metadata API

This commit is contained in:
Adrian Johnson 2016-10-01 22:41:36 +09:30
parent dfc7b9e669
commit 5bfadd5530
5 changed files with 265 additions and 31 deletions

View file

@ -70,6 +70,7 @@ cairo_image_surface_get_stride
CAIRO_HAS_PDF_SURFACE
CAIRO_PDF_OUTLINE_ROOT
cairo_pdf_outline_flags_t
cairo_pdf_metadata_t
cairo_pdf_surface_create
cairo_pdf_surface_create_for_stream
cairo_pdf_surface_restrict_to_version
@ -78,6 +79,7 @@ cairo_pdf_get_versions
cairo_pdf_version_to_string
cairo_pdf_surface_set_size
cairo_pdf_surface_add_outline
cairo_pdf_surface_set_metadata
</SECTION>
<SECTION>

View file

@ -699,6 +699,49 @@ cairo_pdf_interchange_write_names_dict (cairo_pdf_surface_t *surface)
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
cairo_pdf_interchange_write_docinfo (cairo_pdf_surface_t *surface)
{
cairo_pdf_interchange_t *ic = &surface->interchange;
surface->docinfo_res = _cairo_pdf_surface_new_object (surface);
if (surface->docinfo_res.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Producer (cairo %s (http://cairographics.org))\n",
surface->docinfo_res.id,
cairo_version_string ());
if (ic->docinfo.title)
_cairo_output_stream_printf (surface->output, " /Title %s\n", ic->docinfo.title);
if (ic->docinfo.author)
_cairo_output_stream_printf (surface->output, " /Author %s\n", ic->docinfo.author);
if (ic->docinfo.subject)
_cairo_output_stream_printf (surface->output, " /Subject %s\n", ic->docinfo.subject);
if (ic->docinfo.keywords)
_cairo_output_stream_printf (surface->output, " /Keywords %s\n", ic->docinfo.keywords);
if (ic->docinfo.creator)
_cairo_output_stream_printf (surface->output, " /Creator %s\n", ic->docinfo.creator);
if (ic->docinfo.create_date)
_cairo_output_stream_printf (surface->output, " /CreationDate %s\n", ic->docinfo.create_date);
if (ic->docinfo.mod_date)
_cairo_output_stream_printf (surface->output, " /ModDate %s\n", ic->docinfo.mod_date);
_cairo_output_stream_printf (surface->output,
">>\n"
"endobj\n");
return CAIRO_STATUS_SUCCESS;
}
static void
init_named_dest_key (cairo_pdf_named_dest_t *dest)
{
@ -1035,6 +1078,10 @@ _cairo_pdf_interchange_write_document_objects (cairo_pdf_surface_t *surface)
return status;
status = cairo_pdf_interchange_write_names_dict (surface);
if (unlikely (status))
return status;
status = cairo_pdf_interchange_write_docinfo (surface);
return status;
}
@ -1075,6 +1122,7 @@ _cairo_pdf_interchange_init (cairo_pdf_surface_t *surface)
if (unlikely (outline_root == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
memset (&ic->docinfo, 0, sizeof (ic->docinfo));
status = _cairo_array_append (&ic->outline, &outline_root);
return status;
@ -1103,6 +1151,13 @@ _cairo_pdf_interchange_fini (cairo_pdf_surface_t *surface)
free (outline);
}
_cairo_array_fini (&ic->outline);
free (ic->docinfo.title);
free (ic->docinfo.author);
free (ic->docinfo.subject);
free (ic->docinfo.keywords);
free (ic->docinfo.creator);
free (ic->docinfo.create_date);
free (ic->docinfo.mod_date);
return CAIRO_STATUS_SUCCESS;
}
@ -1171,3 +1226,125 @@ _cairo_pdf_interchange_add_outline (cairo_pdf_surface_t *surface,
return CAIRO_STATUS_SUCCESS;
}
/*
* Date must be in the following format:
*
* YYYY-MM-DDThh:mm:ss[Z+-]hh:mm
*
* Only the year is required. If a field is included all preceding
* fields must be included.
*/
static char *
iso8601_to_pdf_date_string (const char *iso)
{
char buf[40];
const char *p;
int i;
/* Check that utf8 contains only the characters "0123456789-T:Z+" */
p = iso;
while (*p) {
if (!_cairo_isdigit (*p) && *p != '-' && *p != 'T' &&
*p != ':' && *p != 'Z' && *p != '+')
return NULL;
p++;
}
p = iso;
strcpy (buf, "(");
/* YYYY (required) */
if (strlen (p) < 4)
return NULL;
strncat (buf, p, 4);
p += 4;
/* -MM, -DD, Thh, :mm, :ss */
for (i = 0; i < 5; i++) {
if (strlen (p) < 3)
goto finish;
strncat (buf, p + 1, 2);
p += 3;
}
/* Z, +, - */
if (strlen (p) < 1)
goto finish;
strncat (buf, p, 1);
p += 1;
/* hh */
if (strlen (p) < 2)
goto finish;
strncat (buf, p, 2);
strcat (buf, "'");
p += 2;
/* :mm */
if (strlen (p) < 3)
goto finish;
strncat (buf, p + 1, 3);
finish:
strcat (buf, ")");
return strdup (buf);
}
cairo_int_status_t
_cairo_pdf_interchange_set_metadata (cairo_pdf_surface_t *surface,
cairo_pdf_metadata_t metadata,
const char *utf8)
{
cairo_pdf_interchange_t *ic = &surface->interchange;
cairo_status_t status;
char *s = NULL;
if (utf8) {
if (metadata == CAIRO_PDF_METADATA_CREATE_DATE ||
metadata == CAIRO_PDF_METADATA_MOD_DATE) {
s = iso8601_to_pdf_date_string (utf8);
} else {
status = _cairo_utf8_to_pdf_string (utf8, &s);
if (unlikely (status))
return status;
}
}
switch (metadata) {
case CAIRO_PDF_METADATA_TITLE:
free (ic->docinfo.title);
ic->docinfo.title = s;
break;
case CAIRO_PDF_METADATA_AUTHOR:
free (ic->docinfo.author);
ic->docinfo.author = s;
break;
case CAIRO_PDF_METADATA_SUBJECT:
free (ic->docinfo.subject);
ic->docinfo.subject = s;
break;
case CAIRO_PDF_METADATA_KEYWORDS:
free (ic->docinfo.keywords);
ic->docinfo.keywords = s;
break;
case CAIRO_PDF_METADATA_CREATOR:
free (ic->docinfo.creator);
ic->docinfo.creator = s;
break;
case CAIRO_PDF_METADATA_CREATE_DATE:
free (ic->docinfo.create_date);
ic->docinfo.create_date = s;
break;
case CAIRO_PDF_METADATA_MOD_DATE:
free (ic->docinfo.mod_date);
ic->docinfo.mod_date = s;
break;
}
return CAIRO_STATUS_SUCCESS;
}

View file

@ -206,6 +206,16 @@ typedef struct _cairo_pdf_outline_entry {
int count;
} cairo_pdf_outline_entry_t;
struct docinfo {
char *title;
char *author;
char *subject;
char *keywords;
char *creator;
char *create_date;
char *mod_date;
};
typedef struct _cairo_pdf_interchange {
cairo_tag_stack_t analysis_tag_stack;
cairo_tag_stack_t render_tag_stack;
@ -225,6 +235,7 @@ typedef struct _cairo_pdf_interchange {
cairo_pdf_resource_t dests_res;
int annot_page;
cairo_array_t outline; /* array of pointers to cairo_pdf_outline_entry_t; */
struct docinfo docinfo;
} cairo_pdf_interchange_t;
@ -314,6 +325,7 @@ struct _cairo_pdf_surface {
cairo_bool_t tagged;
cairo_pdf_resource_t outlines_dict_res;
cairo_pdf_resource_t names_dict_res;
cairo_pdf_resource_t docinfo_res;
cairo_surface_t *paginated_surface;
};
@ -367,4 +379,9 @@ _cairo_pdf_interchange_add_outline (cairo_pdf_surface_t *surface,
cairo_pdf_outline_flags_t flags,
int *id);
cairo_private cairo_int_status_t
_cairo_pdf_interchange_set_metadata (cairo_pdf_surface_t *surface,
cairo_pdf_metadata_t metadata,
const char *utf8);
#endif /* CAIRO_PDF_SURFACE_PRIVATE_H */

View file

@ -238,9 +238,6 @@ _cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface);
static void
_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface);
static cairo_pdf_resource_t
_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface);
static cairo_pdf_resource_t
_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface);
@ -773,6 +770,42 @@ cairo_pdf_surface_add_outline (cairo_surface_t *surface,
return id;
}
/**
* cairo_pdf_surface_set_metadata:
* @surface: a PDF #cairo_surface_t
* @metadata: The metadata item to set.
* @utf8: metadata value
*
* Set document metadata. The %CAIRO_PDF_METADATA_CREATE_DATE and
* %CAIRO_PDF_METADATA_MOD_DATE values must be in ISO-8601 format:
* YYYY-MM-DDThh:mm:ss. An optional timezone of the form "[+/-]hh:mm"
* or "Z" for UTC time can be appended. All other metadata values can be any UTF-8
* string.
*
* For example:
* <informalexample><programlisting>
* cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_TITLE, "My Document");
* cairo_pdf_surface_set_metadata (surface, CAIRO_PDF_METADATA_CREATE_DATE, "2015-12-31T23:59+02:00");
* </programlisting></informalexample>
*
* Since: 1.16
**/
void
cairo_pdf_surface_set_metadata (cairo_surface_t *surface,
cairo_pdf_metadata_t metadata,
const char *utf8)
{
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_metadata (pdf_surface, metadata, utf8);
if (status)
status = _cairo_surface_set_error (surface, status);
}
static void
_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface)
{
@ -2121,7 +2154,7 @@ _cairo_pdf_surface_finish (void *abstract_surface)
{
cairo_pdf_surface_t *surface = abstract_surface;
long offset;
cairo_pdf_resource_t info, catalog;
cairo_pdf_resource_t catalog;
cairo_status_t status, status2;
int size, i;
cairo_pdf_jbig2_global_t *global;
@ -2136,10 +2169,6 @@ _cairo_pdf_surface_finish (void *abstract_surface)
if (unlikely (status))
return status;
info = _cairo_pdf_surface_write_info (surface);
if (info.id == 0 && status == CAIRO_STATUS_SUCCESS)
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
catalog = _cairo_pdf_surface_write_catalog (surface);
if (catalog.id == 0 && status == CAIRO_STATUS_SUCCESS)
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
@ -2154,7 +2183,7 @@ _cairo_pdf_surface_finish (void *abstract_surface)
">>\n",
surface->next_available_resource.id,
catalog.id,
info.id);
surface->docinfo_res.id);
_cairo_output_stream_printf (surface->output,
"startxref\n"
@ -4797,28 +4826,6 @@ _cairo_pdf_surface_get_font_options (void *abstract_surface,
_cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
}
static cairo_pdf_resource_t
_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface)
{
cairo_pdf_resource_t info;
info = _cairo_pdf_surface_new_object (surface);
if (info.id == 0)
return info;
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Creator (cairo %s (http://cairographics.org))\n"
" /Producer (cairo %s (http://cairographics.org))\n"
">>\n"
"endobj\n",
info.id,
cairo_version_string (),
cairo_version_string ());
return info;
}
static void
_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface)
{

View file

@ -113,6 +113,37 @@ cairo_pdf_surface_add_outline (cairo_surface_t *surface,
const char *dest,
cairo_pdf_outline_flags_t flags);
/**
* cairo_pdf_metadata_t:
* @CAIRO_PDF_METADATA_TITLE: The document title (Since 1.16)
* @CAIRO_PDF_METADATA_AUTHOR: The document author (Since 1.16)
* @CAIRO_PDF_METADATA_SUBJECT: The document subject (Since 1.16)
* @CAIRO_PDF_METADATA_KEYWORDS: The document keywords (Since 1.16)
* @CAIRO_PDF_METADATA_CREATOR: The document creator (Since 1.16)
* @CAIRO_PDF_METADATA_TITLE: The document title (Since 1.16)
* @CAIRO_PDF_METADATA_CREATE_DATE: The document creation date (Since 1.16)
* @CAIRO_PDF_METADATA_MOD_DATE: The document modification date (Since 1.16)
*
* #cairo_pdf_metadata_t is used by the
* cairo_pdf_surface_set_metadata() function specify the metadata to set.
*
* Since: 1.16
**/
typedef enum _cairo_pdf_metadata {
CAIRO_PDF_METADATA_TITLE,
CAIRO_PDF_METADATA_AUTHOR,
CAIRO_PDF_METADATA_SUBJECT,
CAIRO_PDF_METADATA_KEYWORDS,
CAIRO_PDF_METADATA_CREATOR,
CAIRO_PDF_METADATA_CREATE_DATE,
CAIRO_PDF_METADATA_MOD_DATE,
} cairo_pdf_metadata_t;
void
cairo_pdf_surface_set_metadata (cairo_surface_t *surface,
cairo_pdf_metadata_t metadata,
const char *utf8);
CAIRO_END_DECLS
#else /* CAIRO_HAS_PDF_SURFACE */