/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* gsf-opendoc-utils.c: Handle the application neutral portions of OpenDocument
*
* Author: Luciano Wolf (luciano.wolf@indt.org.br)
*
* Copyright (C) 2006 Jody Goldberg (jody@gnome.org)
* Copyright (C) 2005-2006 INdT - Instituto Nokia de Tecnologia
* http://www.indt.org.br
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2.1 of the GNU Lesser General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <gsf-config.h>
#include <gsf/gsf-opendoc-utils.h>
#include <gsf/gsf-meta-names.h>
#include <gsf/gsf-doc-meta-data.h>
#include <gsf/gsf-timestamp.h>
#include <gsf/gsf-docprop-vector.h>
#include <string.h>
#define OFFICE "office:"
typedef struct {
GsfDocMetaData *md;
GsfDocPropVector *keywords;
GError *err;
} GsfOOMetaIn;
/* Generated based on:
* http://www.oasis-open.org/committees/download.php/12572/OpenDocument-v1.0-os.pdf */
GsfXMLInNS gsf_ooo_ns[] = {
/* OOo 1.0.x & 1.1.x */
GSF_XML_IN_NS (OO_NS_OFFICE, "http://openoffice.org/2000/office"),
GSF_XML_IN_NS (OO_NS_STYLE, "http://openoffice.org/2000/style"),
GSF_XML_IN_NS (OO_NS_TEXT, "http://openoffice.org/2000/text"),
GSF_XML_IN_NS (OO_NS_TABLE, "http://openoffice.org/2000/table"),
GSF_XML_IN_NS (OO_NS_DRAW, "http://openoffice.org/2000/drawing"),
GSF_XML_IN_NS (OO_NS_NUMBER, "http://openoffice.org/2000/datastyle"),
GSF_XML_IN_NS (OO_NS_CHART, "http://openoffice.org/2000/chart"),
GSF_XML_IN_NS (OO_NS_DR3D, "http://openoffice.org/2000/dr3d"),
GSF_XML_IN_NS (OO_NS_FORM, "http://openoffice.org/2000/form"),
GSF_XML_IN_NS (OO_NS_SCRIPT, "http://openoffice.org/2000/script"),
GSF_XML_IN_NS (OO_NS_CONFIG, "http://openoffice.org/2001/config"),
GSF_XML_IN_NS (OO_NS_MATH, "http://www.w3.org/1998/Math/MathML"), /* also in 2.0 */
GSF_XML_IN_NS (OO_NS_FO, "http://www.w3.org/1999/XSL/Format"),
GSF_XML_IN_NS (OO_NS_XLINK, "http://www.w3.org/1999/xlink"), /* also in 2.0 */
GSF_XML_IN_NS (OO_NS_SVG, "http://www.w3.org/2000/svg"),
/* OOo 1.9.x & 2.0.x */
GSF_XML_IN_NS (OO_NS_OFFICE, "urn:oasis:names:tc:opendocument:xmlns:office:1.0"),
GSF_XML_IN_NS (OO_NS_STYLE, "urn:oasis:names:tc:opendocument:xmlns:style:1.0"),
GSF_XML_IN_NS (OO_NS_TEXT, "urn:oasis:names:tc:opendocument:xmlns:text:1.0"),
GSF_XML_IN_NS (OO_NS_TABLE, "urn:oasis:names:tc:opendocument:xmlns:table:1.0"),
GSF_XML_IN_NS (OO_NS_DRAW, "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"),
GSF_XML_IN_NS (OO_NS_FO, "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"),
GSF_XML_IN_NS (OO_NS_META, "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"),
GSF_XML_IN_NS (OO_NS_NUMBER, "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"),
GSF_XML_IN_NS (OO_NS_SVG, "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"),
GSF_XML_IN_NS (OO_NS_CHART, "urn:oasis:names:tc:opendocument:xmlns:chart:1.0"),
GSF_XML_IN_NS (OO_NS_DR3D, "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"),
GSF_XML_IN_NS (OO_NS_FORM, "urn:oasis:names:tc:opendocument:xmlns:form:1.0"),
GSF_XML_IN_NS (OO_NS_SCRIPT, "urn:oasis:names:tc:opendocument:xmlns:script:1.0"),
GSF_XML_IN_NS (OO_NS_DC, "http://purl.org/dc/elements/1.1/"),
GSF_XML_IN_NS (OO_NS_OOO, "http://openoffice.org/2004/office"),
GSF_XML_IN_NS (OO_NS_OOOW, "http://openoffice.org/2004/writer"),
GSF_XML_IN_NS (OO_NS_OOOC, "http://openoffice.org/2004/calc"),
GSF_XML_IN_NS (OO_NS_DOM, "http://www.w3.org/2001/xml-events"),
GSF_XML_IN_NS (OO_NS_XFORMS, "http://www.w3.org/2002/xforms"),
GSF_XML_IN_NS (OO_NS_XSD, "http://www.w3.org/2001/XMLSchema"),
GSF_XML_IN_NS (OO_NS_XSI, "http://www.w3.org/2001/XMLSchema-instance"),
{ NULL, 0 }
};
static void
od_get_meta_prop (GsfXMLIn *xin, char const *prop_name, GType g_type)
{
GValue *res = g_new0 (GValue, 1);
if (gsf_xml_gvalue_from_str (res, g_type, xin->content->str))
gsf_doc_meta_data_insert (((GsfOOMetaIn *)xin->user_state)->md,
g_strdup (prop_name), res);
else
g_free (res);
}
/* Avoid duplication */
#define OO_PROP(tag, name, type) \
static void \
od_meta_ ## tag (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob) \
{ \
od_get_meta_prop (xin, name, type); \
}
OO_PROP(generator, GSF_META_NAME_GENERATOR, G_TYPE_STRING)
OO_PROP(title, GSF_META_NAME_TITLE, G_TYPE_STRING)
OO_PROP(description, GSF_META_NAME_DESCRIPTION, G_TYPE_STRING)
OO_PROP(subject, GSF_META_NAME_SUBJECT, G_TYPE_STRING)
OO_PROP(initial_creator, GSF_META_NAME_INITIAL_CREATOR, G_TYPE_STRING)
/* OD considers this the last person to modify the doc, rather than
* the DC convention of the person primarilly responsible for its creation */
OO_PROP(creator, GSF_META_NAME_CREATOR, G_TYPE_STRING)
/* last to print */
OO_PROP(printed_by, GSF_META_NAME_PRINTED_BY, G_TYPE_STRING)
OO_PROP(date_created, GSF_META_NAME_DATE_CREATED, GSF_TIMESTAMP_TYPE)
OO_PROP(date_modified, GSF_META_NAME_DATE_MODIFIED, GSF_TIMESTAMP_TYPE)
OO_PROP(print_date, GSF_META_NAME_LAST_PRINTED, GSF_TIMESTAMP_TYPE)
OO_PROP(language, GSF_META_NAME_LANGUAGE, G_TYPE_STRING)
OO_PROP(editing_cycles, GSF_META_NAME_REVISION_COUNT, G_TYPE_UINT)
/* FIXME FIXME FIXME should be durations using format 'PnYnMnDTnHnMnS' */
OO_PROP(editing_duration, GSF_META_NAME_EDITING_DURATION, G_TYPE_STRING)
/* OD allows multiple keywords, accumulate things and make it an array */
static void
od_meta_keyword (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
{
GsfOOMetaIn *mi = (GsfOOMetaIn *)xin->user_state;
GValue *v = g_new0 (GValue, 1);
if (NULL == mi->keywords)
mi->keywords = gsf_docprop_vector_new ();
g_value_init (v, G_TYPE_STRING);
g_value_set_string (v, xin->content->str);
gsf_docprop_vector_append (mi->keywords, v);
g_value_unset (v);
g_free (v);
}
static void
od_meta_user_defined (G_GNUC_UNUSED GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
{
}
#if 0
/* These need special handling for attributes */
template
auto_reload
hl_behavior
doc_stats
#endif
static GsfXMLInNode const gsf_opendoc_meta_dtd[] = {
GSF_XML_IN_NODE_FULL (START, START, -1, NULL, FALSE, FALSE, TRUE, NULL, NULL, 0),
GSF_XML_IN_NODE (START, META, OO_NS_OFFICE, "meta", FALSE, NULL, NULL),
/* OpenDocument TAGS */
GSF_XML_IN_NODE (META, META_GENERATOR, OO_NS_META, "generator", TRUE, NULL, &od_meta_generator),
GSF_XML_IN_NODE (META, META_TITLE, OO_NS_DC, "title", TRUE, NULL, &od_meta_title),
GSF_XML_IN_NODE (META, META_DESCRIPTION, OO_NS_DC, "description", TRUE, NULL, &od_meta_description),
GSF_XML_IN_NODE (META, META_SUBJECT, OO_NS_DC, "subject", TRUE, NULL, &od_meta_subject),
GSF_XML_IN_NODE (META, META_KEYWORD, OO_NS_META, "keyword", TRUE, NULL, &od_meta_keyword),
GSF_XML_IN_NODE (META, META_INITIAL_CREATOR, OO_NS_META, "initial-creator", TRUE, NULL, &od_meta_initial_creator),
GSF_XML_IN_NODE (META, META_CREATOR, OO_NS_DC, "creator", TRUE, NULL, &od_meta_creator),
GSF_XML_IN_NODE (META, META_PRINTED_BY, OO_NS_META, "printed-by", TRUE, NULL, &od_meta_printed_by),
GSF_XML_IN_NODE (META, META_CREATION_DATE, OO_NS_META, "creation-date", TRUE, NULL, &od_meta_date_created),
GSF_XML_IN_NODE (META, META_DATE_MOD, OO_NS_DC, "date", TRUE, NULL, &od_meta_date_modified),
GSF_XML_IN_NODE (META, META_PRINT_DATE, OO_NS_META, "print-date", TRUE, NULL, &od_meta_print_date),
GSF_XML_IN_NODE (META, META_TEMPLATE, OO_NS_META, "template", FALSE, NULL, NULL),
GSF_XML_IN_NODE (META, META_AUTO_RELOAD, OO_NS_META, "auto-reload", FALSE, NULL, NULL),
GSF_XML_IN_NODE (META, META_HL_BEHAVIOUR, OO_NS_META, "hyperlink-behaviour", FALSE, NULL, NULL),
GSF_XML_IN_NODE (META, META_DOCUMENT_STATS, OO_NS_META, "document-statistic", FALSE, NULL, NULL),
GSF_XML_IN_NODE (META, META_LANGUAGE, OO_NS_DC, "language", TRUE, NULL, &od_meta_language),
GSF_XML_IN_NODE (META, META_EDITING_CYCLES, OO_NS_META, "editing-cycles", TRUE, NULL, &od_meta_editing_cycles),
GSF_XML_IN_NODE (META, META_EDITING_DURATION, OO_NS_META, "editing-duration", TRUE, NULL, &od_meta_editing_duration),
GSF_XML_IN_NODE (META, META_USER_DEFINED, OO_NS_META, "user-defined", TRUE, NULL, &od_meta_user_defined),
GSF_XML_IN_NODE_END
};
/**
* gsf_opendoc_metadata_read :
* @input : #GsfInput
* @md : #GsfDocMetaData
*
* Read an OpenDocument metadata stream from @input and store the properties
* into @md. Overwrite any existing properties with the same id.
*
* Returns: a GError if there is a problem.
**/
GError *
gsf_opendoc_metadata_read (GsfInput *input, GsfDocMetaData *md)
{
GsfXMLInDoc *doc;
GsfOOMetaIn state;
state.md = md;
state.keywords = NULL;
state.err = NULL;
doc = gsf_xml_in_doc_new (gsf_opendoc_meta_dtd, gsf_ooo_ns);
gsf_xml_in_doc_parse (doc, input, &state);
gsf_xml_in_doc_free (doc);
if (state.keywords) {
GValue *val = g_new0 (GValue, 1);
g_value_init (val, GSF_DOCPROP_VECTOR_TYPE);
g_value_set_object (val, state.keywords);
gsf_doc_meta_data_insert (md,
g_strdup (GSF_META_NAME_KEYWORDS), val);
g_object_unref (state.keywords);
}
return state.err;
}
static void
gsf_opendoc_metadata_subtree_free (GsfXMLIn *xin, G_GNUC_UNUSED gpointer old_state)
{
gsf_xml_in_doc_free (xin->user_state);
}
/**
* gsf_opendoc_metadata_subtree :
* @doc : #GsfXMLInDoc
* @md : #GsfDocMetaData
*
* Extend @xin so that it can parse a subtree in OpenDoc metadata format
**/
void
gsf_opendoc_metadata_subtree (GsfXMLIn *xin, GsfDocMetaData *md)
{
GsfXMLInDoc *doc = gsf_xml_in_doc_new (gsf_opendoc_meta_dtd+1, gsf_ooo_ns);
gsf_xml_in_push_state (xin, doc, md, &gsf_opendoc_metadata_subtree_free,
NULL);
}
static char const *
od_map_prop_name (char const *name)
{
/* shared by all instances and never freed */
static GHashTable *od_prop_name_map = NULL;
if (NULL == od_prop_name_map)
{
static struct {
char const *gsf_key;
char const *od_key;
} const map [] = {
{ GSF_META_NAME_GENERATOR, "meta:generator" },
{ GSF_META_NAME_TITLE, "dc:title" },
{ GSF_META_NAME_DESCRIPTION, "dc:description" },
{ GSF_META_NAME_SUBJECT, "dc:subject" },
{ GSF_META_NAME_INITIAL_CREATOR,"meta:initial-creator" },
{ GSF_META_NAME_CREATOR, "dc:creator" },
{ GSF_META_NAME_PRINTED_BY, "meta:printed-by" },
{ GSF_META_NAME_DATE_CREATED, "meta:creation-date" },
{ GSF_META_NAME_DATE_MODIFIED, "dc:date" },
{ GSF_META_NAME_LAST_PRINTED, "meta:print-date" },
{ GSF_META_NAME_LANGUAGE, "dc:language" },
{ GSF_META_NAME_REVISION_COUNT, "meta:editing-cycles" },
{ GSF_META_NAME_EDITING_DURATION, "meta:editing-duration" }
};
int i = G_N_ELEMENTS (map);
od_prop_name_map = g_hash_table_new (g_str_hash, g_str_equal);
while (i-- > 0)
g_hash_table_insert (od_prop_name_map,
(gpointer)map[i].gsf_key,
(gpointer)map[i].od_key);
}
return g_hash_table_lookup (od_prop_name_map, name);
}
#if 0
meta:page-count GSF_META_NAME_PAGE_COUNT
meta:table-count GSF_META_NAME_TABLE_COUNT:
meta:draw-count
meta:image-count GSF_META_NAME_IMAGE_COUNT:
meta:ole-object-count GSF_META_NAME_OBJECT_COUNT:
meta:paragraph-count GSF_META_NAME_PARAGRAPH_COUNT:
meta:word-count
meta:character-count GSF_META_NAME_CHARACTER_COUNT
meta:row-count GSF_META_NAME_LINE_COUNT:
meta:frame-count
meta:sentence-count
meta:syllable-count
meta:non-whitespace-character-count
meta:page-count
GSF_META_NAME_SPREADSHEET_COUNT
meta:table-count
GSF_META_NAME_TABLE_COUNT:
meta:image-count
* GSF_META_NAME_IMAGE_COUNT:
meta:cell-count
GSF_META_NAME_CELL_COUNT
meta:object-count
GSF_META_NAME_OBJECT_COUNT:
meta:page-count
GSF_META_NAME_SLIDE_COUNT:
meta:image-count
GSF_META_NAME_IMAGE_COUNT:
meta:object-count
GSF_META_NAME_OBJECT_COUNT:
#endif
static void
meta_write_props (char const *prop_name, GsfDocProp *prop, GsfXMLOut *output)
{
char const *mapped_name;
GValue const *val = gsf_doc_prop_get_val (prop);
/* Handle specially */
if (0 == strcmp (prop_name, GSF_META_NAME_KEYWORDS)) {
GValueArray *va;
unsigned i;
char *str;
/* OLE2 stores a single string, with no obvious
* standard for seperator */
if (G_TYPE_STRING == G_VALUE_TYPE (val)) {
str = g_value_dup_string (val);
if (str && *str) {
gsf_xml_out_start_element (output, "meta:keyword");
gsf_xml_out_add_cstr (output, NULL, str);
gsf_xml_out_end_element (output);
}
g_free (str);
} else if (NULL != (va = gsf_value_get_docprop_varray (val))) {
for (i = 0 ; i < va->n_values; i++) {
str = g_value_dup_string (g_value_array_get_nth (va, i));
gsf_xml_out_start_element (output, "meta:keyword");
gsf_xml_out_add_cstr (output, NULL, str);
gsf_xml_out_end_element (output);
g_free (str);
}
}
return;
}
if (NULL == (mapped_name = od_map_prop_name (prop_name))) {
GType t;
char const *type_name = NULL;
gsf_xml_out_start_element (output, "meta:user-defined");
gsf_xml_out_add_cstr (output, "meta:name", prop_name);
if (NULL == val) {
gsf_xml_out_end_element (output);
return;
}
switch ((t = G_VALUE_TYPE (val))) {
case G_TYPE_CHAR:
case G_TYPE_UCHAR:
case G_TYPE_STRING:
case G_TYPE_ENUM:
case G_TYPE_FLAGS:
type_name = "string";
break;
case G_TYPE_BOOLEAN:
type_name = "boolean";
break;
case G_TYPE_INT:
case G_TYPE_UINT:
case G_TYPE_LONG:
case G_TYPE_ULONG:
case G_TYPE_FLOAT:
case G_TYPE_DOUBLE:
type_name = "float";
break;
default:
if (GSF_TIMESTAMP_TYPE == t)
type_name = "data";
}
if (NULL != type_name)
gsf_xml_out_add_cstr (output, "meta:type", type_name);
} else
gsf_xml_out_start_element (output, mapped_name);
if (NULL != val)
gsf_xml_out_add_gvalue (output, NULL, val);
gsf_xml_out_end_element (output);
}
gboolean
gsf_opendoc_metadata_write (GsfXMLOut *output, GsfDocMetaData const *md)
{
if (output == NULL)
return FALSE;
gsf_xml_out_start_element (output, OFFICE "document-meta");
gsf_xml_out_add_cstr_unchecked (output, "xmlns:office",
"urn:oasis:names:tc:opendocument:xmlns:office:1.0");
gsf_xml_out_add_cstr_unchecked (output, "xmlns:xlink",
"http://www.w3.org/1999/xlink");
gsf_xml_out_add_cstr_unchecked (output, "xmlns:dc",
"http://purl.org/dc/elements/1.1/");
gsf_xml_out_add_cstr_unchecked (output, "xmlns:meta",
"urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
gsf_xml_out_add_cstr_unchecked (output, "xmlns:ooo",
"http://openoffice.org/2004/office");
gsf_xml_out_add_cstr_unchecked (output, "office:version", "1.0");
gsf_xml_out_start_element (output, OFFICE "meta");
gsf_doc_meta_data_foreach (md, (GHFunc) meta_write_props, output);
gsf_xml_out_end_element (output); /* </office:meta> */
gsf_xml_out_end_element (output); /* </office:document-meta> */
return TRUE;
}
syntax highlighted by Code2HTML, v. 0.9.1