"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-opendoc-utils.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-opendoc-utils.c:  Handle the application neutral portions of OpenDocument
4  *
5  * Author:  Luciano Wolf (luciano.wolf@indt.org.br)
6  *
7  * Copyright (C) 2006 Jody Goldberg (jody@gnome.org)
8  * Copyright (C) 2005-2006 INdT - Instituto Nokia de Tecnologia
9  * http://www.indt.org.br
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 2.1 of the GNU Lesser General Public
13  * License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
23  * USA
24  */
25
26 #include <gsf-config.h>
27 #include <gsf/gsf-opendoc-utils.h>
28 #include <gsf/gsf-meta-names.h>
29 #include <gsf/gsf-doc-meta-data.h>
30 #include <gsf/gsf-timestamp.h>
31 #include <gsf/gsf-docprop-vector.h>
32 #include <string.h>
33
34
35 #define OFFICE   "office:"
36
37 typedef struct {
38         GsfDocMetaData   *md;
39         GsfDocPropVector *keywords;
40         GError           *err;
41         char             *name;
42         GType            typ;
43 } GsfOOMetaIn;
44
45 G_MODULE_EXPORT char const *
46 get_gsf_odf_version_string (void)
47 {
48         return "1.2";
49 }
50
51 G_MODULE_EXPORT short 
52 get_gsf_odf_version (void)
53 {
54         return 102;
55 }
56
57
58
59 /* Generated based on:
60  * http://www.oasis-open.org/committees/download.php/12572/OpenDocument-v1.0-os.pdf */
61 /* and  OpenDocument-v1.1.pdf */
62 GsfXMLInNS gsf_ooo_ns[] = {
63         /* OOo 1.0.x & 1.1.x */
64         GSF_XML_IN_NS (OO_NS_OFFICE,    "http://openoffice.org/2000/office"),
65         GSF_XML_IN_NS (OO_NS_STYLE,     "http://openoffice.org/2000/style"),
66         GSF_XML_IN_NS (OO_NS_TEXT,      "http://openoffice.org/2000/text"),
67         GSF_XML_IN_NS (OO_NS_TABLE,     "http://openoffice.org/2000/table"),
68         GSF_XML_IN_NS (OO_NS_DRAW,      "http://openoffice.org/2000/drawing"),
69         GSF_XML_IN_NS (OO_NS_NUMBER,    "http://openoffice.org/2000/datastyle"),
70         GSF_XML_IN_NS (OO_NS_CHART,     "http://openoffice.org/2000/chart"),
71         GSF_XML_IN_NS (OO_NS_DR3D,      "http://openoffice.org/2000/dr3d"),
72         GSF_XML_IN_NS (OO_NS_FORM,      "http://openoffice.org/2000/form"),
73         GSF_XML_IN_NS (OO_NS_SCRIPT,    "http://openoffice.org/2000/script"),
74         GSF_XML_IN_NS (OO_NS_CONFIG,    "http://openoffice.org/2001/config"),
75         GSF_XML_IN_NS (OO_NS_MATH,      "http://www.w3.org/1998/Math/MathML"),  /* also in 2.0 */
76         GSF_XML_IN_NS (OO_NS_FO,        "http://www.w3.org/1999/XSL/Format"),
77         GSF_XML_IN_NS (OO_NS_XLINK,     "http://www.w3.org/1999/xlink"),        /* also in 2.0 */
78         GSF_XML_IN_NS (OO_NS_SVG,       "http://www.w3.org/2000/svg"),
79
80         /* OOo 1.9.x & 2.0.x */
81         GSF_XML_IN_NS (OO_NS_OFFICE,    "urn:oasis:names:tc:opendocument:xmlns:office:1.0"),
82         GSF_XML_IN_NS (OO_NS_STYLE,     "urn:oasis:names:tc:opendocument:xmlns:style:1.0"),
83         GSF_XML_IN_NS (OO_NS_TEXT,      "urn:oasis:names:tc:opendocument:xmlns:text:1.0"),
84         GSF_XML_IN_NS (OO_NS_TABLE,     "urn:oasis:names:tc:opendocument:xmlns:table:1.0"),
85         GSF_XML_IN_NS (OO_NS_DRAW,      "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"),
86         GSF_XML_IN_NS (OO_NS_FO,        "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"),
87         GSF_XML_IN_NS (OO_NS_META,      "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"),
88         GSF_XML_IN_NS (OO_NS_NUMBER,    "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"),
89         GSF_XML_IN_NS (OO_NS_SVG,       "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"),
90         GSF_XML_IN_NS (OO_NS_CHART,     "urn:oasis:names:tc:opendocument:xmlns:chart:1.0"),
91         GSF_XML_IN_NS (OO_NS_DR3D,      "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"),
92         GSF_XML_IN_NS (OO_NS_FORM,      "urn:oasis:names:tc:opendocument:xmlns:form:1.0"),
93         GSF_XML_IN_NS (OO_NS_SCRIPT,    "urn:oasis:names:tc:opendocument:xmlns:script:1.0"),
94         GSF_XML_IN_NS (OO_NS_PRESENT,   "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"),
95
96         GSF_XML_IN_NS (OO_NS_DC,        "http://purl.org/dc/elements/1.1/"),
97         GSF_XML_IN_NS (OO_NS_OOO,       "http://openoffice.org/2004/office"),
98         GSF_XML_IN_NS (OO_NS_OOOW,      "http://openoffice.org/2004/writer"),
99         GSF_XML_IN_NS (OO_NS_OOOC,      "http://openoffice.org/2004/calc"),
100         GSF_XML_IN_NS (OO_NS_DOM,       "http://www.w3.org/2001/xml-events"),
101         GSF_XML_IN_NS (OO_NS_XFORMS,    "http://www.w3.org/2002/xforms"),
102         GSF_XML_IN_NS (OO_NS_XSD,       "http://www.w3.org/2001/XMLSchema"),
103         GSF_XML_IN_NS (OO_NS_XSI,       "http://www.w3.org/2001/XMLSchema-instance"),
104
105         /* OOo 3.0.x */
106         GSF_XML_IN_NS (OO_NS_OF,        "urn:oasis:names:tc:opendocument:xmlns:of:1.2"),
107         GSF_XML_IN_NS (OO_NS_FIELD,     "urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0"),
108         GSF_XML_IN_NS (OO_NS_FIELD,     "urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:field:1.0"),
109         GSF_XML_IN_NS (OO_NS_FORMX,     "urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0"),
110
111         GSF_XML_IN_NS (OO_NS_RPT,       "http://openoffice.org/2005/report"),
112         GSF_XML_IN_NS (OO_NS_RDFA,      "http://docs.oasis-open.org/opendocument/meta/rdfa#"),
113
114         /* OOo 3.2.x */
115         GSF_XML_IN_NS (OO_NS_GRDDL,     "http://www.w3.org/2003/g/data-view#"),
116         GSF_XML_IN_NS (OO_NS_XHTML,     "http://www.w3.org/1999/xhtml"),
117         GSF_XML_IN_NS (OO_NS_TABLE_OOO, "http://openoffice.org/2009/table"),
118
119         /* OOo 3.3.x */
120         GSF_XML_IN_NS (OO_NS_CHART_OOO, "http://openoffice.org/2010/chart"),
121
122         /* Other OpenDocument v 1.1 */
123         GSF_XML_IN_NS (OO_NS_CONFIG,    "urn:oasis:names:tc:opendocument:xmlns:config:1.0"),
124         GSF_XML_IN_NS (OO_NS_ANIM,      "urn:oasis:names:tc:opendocument:xmlns:animation:1.0"),
125         GSF_XML_IN_NS (OO_NS_DATASTYLE, "urn:oasis:names:tc:opendocument:xmlns:data style:1.0"),
126         GSF_XML_IN_NS (OO_NS_MANIFEST,  "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"),
127         GSF_XML_IN_NS (OO_NS_SMIL,      "urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0"),
128
129
130         /* Symphony 1.3 */
131         GSF_XML_IN_NS (OO_LOTUS_NS_PRODTOOLS, "http://www.ibm.com/xmlns/prodtools"),
132
133         /* CleverAge ODF Add-in for Microsoft Office 3.0.5224.0 (11.0.8302)*/
134         GSF_XML_IN_NS (OO_CLEVERAGE_NS_DC,      "http://purl.org/dc/terms/"), 
135
136         /* KOffice 1.6.3 */
137         GSF_XML_IN_NS (OO_KDE_NS_KOFFICE, "http://www.koffice.org/2005/"),
138
139         /* Microsoft Excel Formulas in ODF */
140         GSF_XML_IN_NS (OO_MS_NS_MSOXL, "http://schemas.microsoft.com/office/excel/formula"),
141
142         /* Gnumeric ODF extensions */
143         GSF_XML_IN_NS (OO_GNUM_NS_EXT, "http://www.gnumeric.org/odf-extension/1.0"),
144         { NULL, 0 }
145 };
146
147 G_MODULE_EXPORT GsfXMLInNS *get_gsf_ooo_ns (void)
148 {
149         return gsf_ooo_ns;
150 }
151
152
153 static void
154 od_get_meta_prop (GsfXMLIn *xin, char const *prop_name, GType g_type)
155 {
156         GValue *res = g_new0 (GValue, 1);
157         if (gsf_xml_gvalue_from_str (res, g_type, xin->content->str))
158                 gsf_doc_meta_data_insert (((GsfOOMetaIn *)xin->user_state)->md,
159                         g_strdup (prop_name), res);
160         else
161                 g_free (res);
162 }
163
164 /* Avoid duplication */
165 #define OO_PROP(tag, name, type)                                        \
166 static void                                                             \
167 od_meta_ ## tag (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob) \
168 {                                                                       \
169         od_get_meta_prop (xin, name, type);                             \
170 }
171 OO_PROP(generator,              GSF_META_NAME_GENERATOR,        G_TYPE_STRING)
172 OO_PROP(title,                  GSF_META_NAME_TITLE,            G_TYPE_STRING)
173 OO_PROP(description,            GSF_META_NAME_DESCRIPTION,      G_TYPE_STRING)
174 OO_PROP(subject,                GSF_META_NAME_SUBJECT,          G_TYPE_STRING)
175 OO_PROP(initial_creator,        GSF_META_NAME_INITIAL_CREATOR,  G_TYPE_STRING)
176 /* OD considers this the last person to modify the doc, rather than
177  * the DC convention of the person primarilly responsible for its creation */
178 OO_PROP(creator,                GSF_META_NAME_CREATOR,          G_TYPE_STRING)
179 /* last to print */
180 OO_PROP(printed_by,             GSF_META_NAME_PRINTED_BY,       G_TYPE_STRING)
181
182 OO_PROP(date_created,           GSF_META_NAME_DATE_CREATED,     GSF_TIMESTAMP_TYPE)
183 OO_PROP(date_modified,          GSF_META_NAME_DATE_MODIFIED,    GSF_TIMESTAMP_TYPE)
184 OO_PROP(print_date,             GSF_META_NAME_LAST_PRINTED,     GSF_TIMESTAMP_TYPE)
185
186 OO_PROP(language,               GSF_META_NAME_LANGUAGE,         G_TYPE_STRING)
187 OO_PROP(editing_cycles,         GSF_META_NAME_REVISION_COUNT,   G_TYPE_UINT)
188 /* FIXME FIXME FIXME should be durations using format 'PnYnMnDTnHnMnS' */
189 OO_PROP(editing_duration,       GSF_META_NAME_EDITING_DURATION, G_TYPE_STRING)
190
191 /* OD allows multiple keywords, accumulate things and make it an array */
192 static void
193 od_meta_keyword (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
194 {
195         GsfOOMetaIn *mi = (GsfOOMetaIn *)xin->user_state;
196         GValue *v = g_new0 (GValue, 1);
197
198         if (NULL == mi->keywords)
199                 mi->keywords = gsf_docprop_vector_new ();
200
201         g_value_init (v, G_TYPE_STRING);
202         g_value_set_string (v, xin->content->str);
203         gsf_docprop_vector_append (mi->keywords, v);
204         g_value_unset (v);
205         g_free (v);
206 }
207
208 #define CXML2C(s) ((char const *)(s))
209
210 static void
211 od_meta_user_defined (GsfXMLIn *xin,  xmlChar const **attrs)
212 {
213         GsfOOMetaIn *mi = (GsfOOMetaIn *)xin->user_state;
214         mi->typ = G_TYPE_STRING;
215         mi->name = NULL;
216
217         for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
218                 if (!strcmp (CXML2C (attrs[0]), "meta:name"))
219                         mi->name = g_strdup (CXML2C (attrs[1]));
220                 else if (!strcmp (CXML2C (attrs[0]), "meta:value-type") ||
221                          !strcmp (CXML2C (attrs[0]), "meta:type")) {
222                                 /*
223                                  * "meta:type" is a typo on the write 
224                                  * side that was
225                                  * fixed on 20110509.
226                                  */
227                         if (!strcmp (CXML2C (attrs[1]), "boolean")) {
228                                 mi->typ = G_TYPE_BOOLEAN;
229                         } else if (!strcmp (CXML2C (attrs[1]), "float")) {
230                                 mi->typ = G_TYPE_DOUBLE;
231                         } else if (!strcmp (CXML2C (attrs[1]), "string")) {
232                                 mi->typ = G_TYPE_STRING;
233                         } else if (!strcmp (CXML2C (attrs[1]), "date") ||
234                                    !strcmp (CXML2C (attrs[1]), "data")) {
235                                 /*
236                                  * "data" is a typo on the write side that was
237                                  * fixed on 20110311.
238                                  */
239                                 mi->typ = GSF_TIMESTAMP_TYPE;
240                         } else if (!strcmp (CXML2C (attrs[1]), "time")) {
241                                 mi->typ = G_TYPE_STRING;
242                                 /* We should be able to do better */
243                         } else {
244                                 /* What? */
245                         }
246                 }
247         }
248         if (mi->name == NULL) /* This should not happen */
249                 mi->name = g_strdup ("");
250 }
251
252 static void
253 od_meta_user_defined_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
254 {
255         GsfOOMetaIn *mi = (GsfOOMetaIn *)xin->user_state;
256         
257         if (mi->name != NULL) {
258                 GValue *res = g_new0 (GValue, 1);
259                 GType t = mi->typ;
260                 if (t == G_TYPE_NONE) t = G_TYPE_STRING;
261                 if (gsf_xml_gvalue_from_str (res, t, xin->content->str)) {
262                         gsf_doc_meta_data_insert (mi->md, mi->name, res);
263                         mi->name = NULL;
264                 } else {
265                         g_free (res);
266                         g_free (mi->name);
267                         mi->name = NULL;
268                 }
269         }
270 }
271
272 #if 0
273 /* These need special handling for attributes */
274 template
275 auto_reload
276 hl_behavior
277 doc_stats
278 #endif
279
280 static GsfXMLInNode const gsf_opendoc_meta_st_dtd[] = {
281   GSF_XML_IN_NODE (META, META, OO_NS_OFFICE, "meta", FALSE, NULL, NULL),
282     /* OpenDocument TAGS */
283     GSF_XML_IN_NODE (META, META_GENERATOR,      OO_NS_META, "generator", TRUE, NULL, &od_meta_generator),
284     GSF_XML_IN_NODE (META, META_TITLE,          OO_NS_DC, "title", TRUE, NULL, &od_meta_title),
285     GSF_XML_IN_NODE (META, META_DESCRIPTION,    OO_NS_DC, "description", TRUE, NULL, &od_meta_description),
286     GSF_XML_IN_NODE (META, META_SUBJECT,        OO_NS_DC, "subject", TRUE, NULL, &od_meta_subject),
287     GSF_XML_IN_NODE (META, META_KEYWORD,        OO_NS_META, "keyword", TRUE, NULL, &od_meta_keyword),
288     GSF_XML_IN_NODE (META, META_INITIAL_CREATOR, OO_NS_META, "initial-creator", TRUE, NULL, &od_meta_initial_creator),
289     GSF_XML_IN_NODE (META, META_CREATOR,        OO_NS_DC, "creator", TRUE, NULL, &od_meta_creator),
290     GSF_XML_IN_NODE (META, META_PRINTED_BY,     OO_NS_META, "printed-by", TRUE, NULL, &od_meta_printed_by),
291     GSF_XML_IN_NODE (META, META_CREATION_DATE,  OO_NS_META, "creation-date", TRUE, NULL, &od_meta_date_created),
292     GSF_XML_IN_NODE (META, META_DATE_MOD,       OO_NS_DC, "date", TRUE, NULL, &od_meta_date_modified),
293     GSF_XML_IN_NODE (META, META_PRINT_DATE,     OO_NS_META, "print-date", TRUE, NULL, &od_meta_print_date),
294     GSF_XML_IN_NODE (META, META_TEMPLATE,       OO_NS_META, "template", FALSE, NULL, NULL),
295     GSF_XML_IN_NODE (META, META_AUTO_RELOAD,    OO_NS_META, "auto-reload", FALSE, NULL, NULL),
296     GSF_XML_IN_NODE (META, META_HL_BEHAVIOUR,   OO_NS_META, "hyperlink-behaviour", FALSE, NULL, NULL),
297     GSF_XML_IN_NODE (META, META_DOCUMENT_STATS, OO_NS_META, "document-statistic", FALSE, NULL, NULL),
298     GSF_XML_IN_NODE (META, META_LANGUAGE,       OO_NS_DC, "language", TRUE, NULL, &od_meta_language),
299     GSF_XML_IN_NODE (META, META_EDITING_CYCLES, OO_NS_META, "editing-cycles", TRUE, NULL, &od_meta_editing_cycles),
300     GSF_XML_IN_NODE (META, META_EDITING_DURATION, OO_NS_META, "editing-duration", TRUE, NULL, &od_meta_editing_duration),
301     GSF_XML_IN_NODE (META, META_USER_DEFINED, OO_NS_META, "user-defined", GSF_XML_CONTENT, &od_meta_user_defined,  &od_meta_user_defined_end),
302    GSF_XML_IN_NODE_END
303 };
304
305
306 static void 
307 gsf_opendoc_metadata_subtree_free (G_GNUC_UNUSED GsfXMLIn *xin, gpointer old_state)
308 {
309         GsfOOMetaIn *state = old_state;
310
311         if (state->keywords) {
312                 GValue *val = g_new0 (GValue, 1);
313                 g_value_init (val, GSF_DOCPROP_VECTOR_TYPE);
314                 g_value_set_object (val, state->keywords);
315                 gsf_doc_meta_data_insert (state->md,
316                                           g_strdup (GSF_META_NAME_KEYWORDS), val);
317                 g_object_unref (state->keywords);
318         }
319
320         g_object_unref (G_OBJECT (state->md));
321         g_free (state);
322 }
323
324 static GsfXMLInDoc *doc_subtree = NULL;
325
326 /**
327  * gsf_opendoc_metadata_subtree :
328  * @doc : #GsfXMLInDoc
329  * @md  : #GsfDocMetaData
330  *
331  * Extend @xin so that it can parse a subtree in OpenDoc metadata format
332  **/
333 void
334 gsf_opendoc_metadata_subtree (GsfXMLIn *xin, GsfDocMetaData *md)
335 {
336         GsfOOMetaIn *state = NULL;
337
338         g_return_if_fail (md != NULL);
339
340         if (NULL == doc_subtree)
341                 doc_subtree = gsf_xml_in_doc_new (gsf_opendoc_meta_st_dtd, gsf_ooo_ns);
342
343         state = g_new0 (GsfOOMetaIn, 1);
344         state->md = md;
345         state->typ = G_TYPE_NONE;
346         g_object_ref (G_OBJECT (md));
347         gsf_xml_in_push_state (xin, doc_subtree, state, gsf_opendoc_metadata_subtree_free, NULL);
348 }
349
350 /**
351  * gsf_opendoc_metadata_subtree_internal :
352  * @doc : #GsfXMLInDoc
353  *
354  * Extend @xin so that it can parse a subtree in OpenDoc metadata format
355  * The current user_state must be a  GsfOOMetaIn!
356  **/
357 static void
358 gsf_opendoc_metadata_subtree_internal (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
359 {
360         if (NULL == doc_subtree)
361                 doc_subtree = gsf_xml_in_doc_new (gsf_opendoc_meta_st_dtd, gsf_ooo_ns);
362
363         gsf_xml_in_push_state (xin, doc_subtree, NULL, NULL, NULL);
364 }
365
366 static GsfXMLInNode const gsf_opendoc_meta_dtd[] = {
367   GSF_XML_IN_NODE_FULL (START, START, -1, NULL, FALSE, FALSE, TRUE, NULL, NULL, 0),
368   GSF_XML_IN_NODE_FULL (START, META, OO_NS_OFFICE, "meta", FALSE, FALSE, TRUE, &gsf_opendoc_metadata_subtree_internal, NULL, 0),
369    GSF_XML_IN_NODE_END
370 };
371
372 /**
373  * gsf_opendoc_metadata_read :
374  * @input : #GsfInput
375  * @md    : #GsfDocMetaData
376  *
377  * Read an OpenDocument metadata stream from @input and store the properties
378  * into @md.  Overwrite any existing properties with the same id.
379  *
380  * Returns: a GError if there is a problem.
381  **/
382 GError *
383 gsf_opendoc_metadata_read (GsfInput *input, GsfDocMetaData *md)
384 {
385         GsfXMLInDoc     *doc;
386         GsfOOMetaIn      state;
387
388         state.md  = md;
389         state.keywords = NULL;
390         state.err = NULL;
391         state.name = NULL;
392
393         doc = gsf_xml_in_doc_new (gsf_opendoc_meta_dtd, gsf_ooo_ns);
394         gsf_xml_in_doc_parse (doc, input, &state);
395         gsf_xml_in_doc_free (doc);
396
397         if (state.keywords) {
398                 GValue *val = g_new0 (GValue, 1);
399                 g_value_init (val, GSF_DOCPROP_VECTOR_TYPE);
400                 g_value_set_object (val, state.keywords);
401                 gsf_doc_meta_data_insert (md,
402                         g_strdup (GSF_META_NAME_KEYWORDS), val);
403                 g_object_unref (state.keywords);
404         }
405
406         return state.err;
407 }
408
409
410 static char const *
411 od_map_prop_name (char const *name)
412 {
413         /* shared by all instances and never freed */
414         static GHashTable *od_prop_name_map = NULL;
415
416         if (NULL == od_prop_name_map) 
417         {
418                 static struct {
419                         char const *gsf_key;
420                         char const *od_key;
421                 } const map [] = {
422                         { GSF_META_NAME_GENERATOR,      "meta:generator" },
423                         { GSF_META_NAME_TITLE,          "dc:title" },
424                         { GSF_META_NAME_DESCRIPTION,    "dc:description" },
425                         { GSF_META_NAME_SUBJECT,        "dc:subject" },
426                         { GSF_META_NAME_INITIAL_CREATOR,"meta:initial-creator" },
427                         { GSF_META_NAME_CREATOR,        "dc:creator" },
428                         { GSF_META_NAME_PRINTED_BY,     "meta:printed-by" },
429                         { GSF_META_NAME_DATE_CREATED,   "meta:creation-date" },
430                         { GSF_META_NAME_DATE_MODIFIED,  "dc:date" },
431                         { GSF_META_NAME_LAST_PRINTED,   "meta:print-date" },
432                         { GSF_META_NAME_LANGUAGE,       "dc:language" },
433                         { GSF_META_NAME_REVISION_COUNT, "meta:editing-cycles" },
434                         { GSF_META_NAME_EDITING_DURATION, "meta:editing-duration" }
435                 };
436                 int i = G_N_ELEMENTS (map);
437
438                 od_prop_name_map = g_hash_table_new (g_str_hash, g_str_equal);
439                 while (i-- > 0)
440                         g_hash_table_insert (od_prop_name_map,
441                                 (gpointer)map[i].gsf_key,
442                                 (gpointer)map[i].od_key);
443         }
444
445         return g_hash_table_lookup (od_prop_name_map, name);
446 }
447
448 #if 0
449 meta:page-count         GSF_META_NAME_PAGE_COUNT
450 meta:table-count        GSF_META_NAME_TABLE_COUNT:
451 meta:draw-count
452 meta:image-count        GSF_META_NAME_IMAGE_COUNT:
453 meta:ole-object-count   GSF_META_NAME_OBJECT_COUNT:
454 meta:paragraph-count    GSF_META_NAME_PARAGRAPH_COUNT:
455 meta:word-count
456 meta:character-count    GSF_META_NAME_CHARACTER_COUNT
457 meta:row-count          GSF_META_NAME_LINE_COUNT:
458 meta:frame-count
459 meta:sentence-count
460 meta:syllable-count
461 meta:non-whitespace-character-count
462
463 meta:page-count
464         GSF_META_NAME_SPREADSHEET_COUNT
465 meta:table-count
466         GSF_META_NAME_TABLE_COUNT:
467 meta:image-count
468         * GSF_META_NAME_IMAGE_COUNT:
469 meta:cell-count
470         GSF_META_NAME_CELL_COUNT
471 meta:object-count
472         GSF_META_NAME_OBJECT_COUNT:
473
474 meta:page-count
475          GSF_META_NAME_SLIDE_COUNT:
476 meta:image-count
477         GSF_META_NAME_IMAGE_COUNT:
478 meta:object-count
479         GSF_META_NAME_OBJECT_COUNT:
480 #endif
481
482 static void
483 meta_write_props (char const *prop_name, GsfDocProp *prop, GsfXMLOut *output)
484 {
485         char const *mapped_name;
486         GValue const *val = gsf_doc_prop_get_val (prop);
487
488         /* Handle specially */
489         if (0 == strcmp (prop_name, GSF_META_NAME_KEYWORDS)) {
490                 GValueArray *va;
491                 unsigned i;
492                 char *str;
493
494                 /* OLE2 stores a single string, with no obvious
495                  * standard for seperator */
496                 if (G_TYPE_STRING == G_VALUE_TYPE (val)) {
497                         str = g_value_dup_string (val);
498                         if (str && *str) {
499                                 gsf_xml_out_start_element (output, "meta:keyword");
500                                 gsf_xml_out_add_cstr (output, NULL, str);
501                                 gsf_xml_out_end_element (output);
502                         }
503                         g_free (str);
504                 } else if (NULL != (va = gsf_value_get_docprop_varray (val))) {
505                         for (i = 0 ; i < va->n_values; i++) {
506                                 str = g_value_dup_string (g_value_array_get_nth (va, i));
507                                 gsf_xml_out_start_element (output, "meta:keyword");
508                                 gsf_xml_out_add_cstr (output, NULL, str);
509                                 gsf_xml_out_end_element (output);
510                                 g_free (str);
511                         }
512                 }
513                 return;
514         }
515
516         if (NULL == (mapped_name = od_map_prop_name (prop_name))) {
517                 GType t;
518                 char const *type_name = NULL;
519
520                 gsf_xml_out_start_element (output, "meta:user-defined");
521                 gsf_xml_out_add_cstr (output, "meta:name", prop_name);
522
523                 if (NULL == val) {
524                         gsf_xml_out_end_element (output);
525                         return;
526                 }
527
528                 t = G_VALUE_TYPE (val);
529                 switch (t) {
530                 case G_TYPE_CHAR:
531                 case G_TYPE_UCHAR:
532                 case G_TYPE_STRING:
533                 case G_TYPE_ENUM:
534                 case G_TYPE_FLAGS:
535                         type_name = "string";
536                         break;
537                 case G_TYPE_BOOLEAN:
538                         type_name = "boolean";
539                         break;
540                 case G_TYPE_INT:
541                 case G_TYPE_UINT:
542                 case G_TYPE_LONG:
543                 case G_TYPE_ULONG:
544                 case G_TYPE_FLOAT:
545                 case G_TYPE_DOUBLE:
546                         type_name = "float";
547                         break; 
548
549                 default:
550                         if (GSF_TIMESTAMP_TYPE == t)
551                                 type_name = "date";
552                 }
553                 if (NULL != type_name)
554                         gsf_xml_out_add_cstr (output, "meta:value-type", type_name);
555         } else
556                 gsf_xml_out_start_element (output, mapped_name);
557         if (NULL != val)
558                 gsf_xml_out_add_gvalue (output, NULL, val);
559         gsf_xml_out_end_element (output);
560 }
561
562 gboolean
563 gsf_opendoc_metadata_write (GsfXMLOut *output, GsfDocMetaData const *md)
564 {
565         if (output == NULL)
566                 return FALSE;
567
568         gsf_xml_out_start_element (output, OFFICE "document-meta");
569         gsf_xml_out_add_cstr_unchecked (output, "xmlns:office",
570                 "urn:oasis:names:tc:opendocument:xmlns:office:1.0");
571         gsf_xml_out_add_cstr_unchecked (output, "xmlns:xlink",
572                 "http://www.w3.org/1999/xlink");
573         gsf_xml_out_add_cstr_unchecked (output, "xmlns:dc",
574                 "http://purl.org/dc/elements/1.1/");
575         gsf_xml_out_add_cstr_unchecked (output, "xmlns:meta",
576                 "urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
577         gsf_xml_out_add_cstr_unchecked (output, "xmlns:ooo",
578                 "http://openoffice.org/2004/office");
579         gsf_xml_out_add_cstr_unchecked (output, "office:version", 
580                                         get_gsf_odf_version_string ());
581         gsf_xml_out_start_element (output, OFFICE "meta");
582         gsf_doc_meta_data_foreach (md, (GHFunc) meta_write_props, output);
583         gsf_xml_out_end_element (output); /* </office:meta> */
584
585         gsf_xml_out_end_element (output); /* </office:document-meta> */
586
587         return TRUE;
588 }