Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / rdf.cpp
1 /** @file
2  * @brief  RDF manipulation functions
3  *
4  * @todo move these to xml/ instead of dialogs/
5  */
6 /* Authors:
7  *   Kees Cook <kees@outflux.net>
8  *   Jon Phillips <jon@rejon.org>
9  *   Jon A. Cruz <jon@joncruz.org>
10  *
11  * Copyright (C) 2004 Kees Cook <kees@outflux.net>
12  * Copyright (C) 2006 Jon Phillips <jon@rejon.org>
13  *
14  * Released under GNU GPL, read the file 'COPYING' for more information
15  */
17 #include "xml/repr.h"
18 #include "rdf.h"
19 #include "sp-item-group.h"
20 #include "inkscape.h"
22 /*
23    Example RDF XML from various places...
24  
25 <rdf:RDF xmlns="http://creativecommons.org/ns#"
26     xmlns:dc="http://purl.org/dc/elements/1.1/"
27     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
28 <Work rdf:about="">
29    <dc:title>title of work</dc:title>
30    <dc:date>year</dc:date>
31    <dc:description>description of work</dc:description>
32    <dc:creator><Agent>
33       <dc:title>creator</dc:title>
34    </Agent></dc:creator>
35    <dc:rights><Agent>
36       <dc:title>holder</dc:title>
37    </Agent></dc:rights>
38    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
39    <dc:source rdf:resource="source"/>
40    <license rdf:resource="http://creativecommons.org/licenses/by/2.0/" 
41 />
42 </Work>
45   <rdf:RDF xmlns="http://creativecommons.org/ns#"
46       xmlns:dc="http://purl.org/dc/elements/1.1/"
47       xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
48   <Work rdf:about="">
49      <dc:title>SVG Road Signs</dc:title>
50      <dc:rights><Agent>
51         <dc:title>John Cliff</dc:title>
52      </Agent></dc:rights>
53      <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
54      <license rdf:resource="http://creativecommons.org/ns#PublicDomain" />
55   </Work>
56   
57   <License rdf:about="http://creativecommons.org/ns#PublicDomain">
58      <permits rdf:resource="http://creativecommons.org/ns#Reproduction" />
59      <permits rdf:resource="http://creativecommons.org/ns#Distribution" />
60      <permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
61   </License>
62   
63 </rdf:RDF>
66 Bag example:
68 <dc:subject>
69 <rdf:Bag>
70 <rdf:li>open clip art logo</rdf:li>
71 <rdf:li>images</rdf:li>
72 <rdf:li>logo</rdf:li>
73 <rdf:li>clip art</rdf:li>
74 <rdf:li>ocal</rdf:li>
75 <rdf:li>logotype</rdf:li>
76 <rdf:li>filetype</rdf:li>
77 </rdf:Bag>
78 </dc:subject>
79 */
81 struct rdf_double_t rdf_license_empty [] = {
82     { NULL, NULL }
83 };
85 struct rdf_double_t rdf_license_cc_a [] = {
86     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
87     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
88     { "cc:requires", "http://creativecommons.org/ns#Notice", },
89     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
90     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
91     { NULL, NULL }
92 };
94 struct rdf_double_t rdf_license_cc_a_sa [] = {
95     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
96     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
97     { "cc:requires", "http://creativecommons.org/ns#Notice", },
98     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
99     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
100     { "cc:requires", "http://creativecommons.org/ns#ShareAlike", },
101     { NULL, NULL }
102 };
104 struct rdf_double_t rdf_license_cc_a_nd [] = {
105     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
106     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
107     { "cc:requires", "http://creativecommons.org/ns#Notice", },
108     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
109     { NULL, NULL }
110 };
112 struct rdf_double_t rdf_license_cc_a_nc [] = {
113     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
114     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
115     { "cc:requires", "http://creativecommons.org/ns#Notice", },
116     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
117     { "cc:prohibits", "http://creativecommons.org/ns#CommercialUse", },
118     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
119     { NULL, NULL }
120 };
122 struct rdf_double_t rdf_license_cc_a_nc_sa [] = {
123     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
124     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
125     { "cc:requires", "http://creativecommons.org/ns#Notice", },
126     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
127     { "cc:prohibits", "http://creativecommons.org/ns#CommercialUse", },
128     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
129     { "cc:requires", "http://creativecommons.org/ns#ShareAlike", },
130     { NULL, NULL }
131 };
133 struct rdf_double_t rdf_license_cc_a_nc_nd [] = {
134     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
135     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
136     { "cc:requires", "http://creativecommons.org/ns#Notice", },
137     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
138     { "cc:prohibits", "http://creativecommons.org/ns#CommercialUse", },
139     { NULL, NULL }
140 };
142 struct rdf_double_t rdf_license_pd [] = {
143     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
144     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
145     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
146     { NULL, NULL }
147 };
149 struct rdf_double_t rdf_license_freeart [] = {
150     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
151     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
152     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
153     { "cc:requires", "http://creativecommons.org/ns#ShareAlike", },
154     { "cc:requires", "http://creativecommons.org/ns#Notice", },
155     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
156     { NULL, NULL }
157 };
159 struct rdf_double_t rdf_license_ofl [] = {
160     { "cc:permits", "http://scripts.sil.org/pub/OFL/Reproduction", },
161     { "cc:permits", "http://scripts.sil.org/pub/OFL/Distribution", },
162     { "cc:permits", "http://scripts.sil.org/pub/OFL/Embedding", },
163     { "cc:permits", "http://scripts.sil.org/pub/OFL/DerivativeWorks", },
164     { "cc:requires", "http://scripts.sil.org/pub/OFL/Notice", },
165     { "cc:requires", "http://scripts.sil.org/pub/OFL/Attribution", },
166     { "cc:requires", "http://scripts.sil.org/pub/OFL/ShareAlike", },
167     { "cc:requires", "http://scripts.sil.org/pub/OFL/DerivativeRenaming", },
168     { "cc:requires", "http://scripts.sil.org/pub/OFL/BundlingWhenSelling", },
169     { NULL, NULL }
170 };
172 struct rdf_license_t rdf_licenses [] = {
173     { N_("CC Attribution"), 
174       "http://creativecommons.org/licenses/by/3.0/",
175       rdf_license_cc_a,
176     },
178     { N_("CC Attribution-ShareAlike"), 
179       "http://creativecommons.org/licenses/by-sa/3.0/",
180       rdf_license_cc_a_sa,
181     },
183     { N_("CC Attribution-NoDerivs"), 
184       "http://creativecommons.org/licenses/by-nd/3.0/",
185       rdf_license_cc_a_nd,
186     },
188     { N_("CC Attribution-NonCommercial"), 
189       "http://creativecommons.org/licenses/by-nc/3.0/",
190       rdf_license_cc_a_nc,
191     },
193     { N_("CC Attribution-NonCommercial-ShareAlike"), 
194       "http://creativecommons.org/licenses/by-nc-sa/3.0/",
195       rdf_license_cc_a_nc_sa,
196     },
198     { N_("CC Attribution-NonCommercial-NoDerivs"), 
199       "http://creativecommons.org/licenses/by-nc-nd/3.0/",
200       rdf_license_cc_a_nc_nd,
201     },
203     { N_("Public Domain"),
204       "http://creativecommons.org/licenses/publicdomain/",
205       rdf_license_pd,
206     },
208     { N_("FreeArt"),
209       "http://artlibre.org/licence/lal",
210       rdf_license_freeart,
211     },
213     { N_("Open Font License"),
214       "http://scripts.sil.org/OFL",
215       rdf_license_ofl,
216     },
218     { NULL, NULL, rdf_license_empty, }
219 };
221 #define XML_TAG_NAME_SVG      "svg:svg"
222 #define XML_TAG_NAME_METADATA "svg:metadata"
223 #define XML_TAG_NAME_RDF      "rdf:RDF"
224 #define XML_TAG_NAME_WORK     "cc:Work"
225 #define XML_TAG_NAME_LICENSE  "cc:License"
227 // Remember when using the "title" and "tip" elements to pass them through
228 // the localization functions when you use them!
229 struct rdf_work_entity_t rdf_work_entities [] = {
230     { "title", N_("Title:"), "dc:title", RDF_CONTENT,
231       N_("Name by which this document is formally known"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
232     },
233     { "date", N_("Date:"), "dc:date", RDF_CONTENT,
234       N_("Date associated with the creation of this document (YYYY-MM-DD)"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
235     },
236     { "format", N_("Format:"), "dc:format", RDF_CONTENT,
237       N_("The physical or digital manifestation of this document (MIME type)"), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
238     },
239     { "type", N_("Type:"), "dc:type", RDF_RESOURCE,
240       N_("Type of document (DCMI Type)"), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
241     },
243     { "creator", N_("Creator:"), "dc:creator", RDF_AGENT,
244       N_("Name of entity primarily responsible for making the content of this document"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
245     },
246     { "rights", N_("Rights:"), "dc:rights", RDF_AGENT,
247       N_("Name of entity with rights to the Intellectual Property of this document"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
248     },
249     { "publisher", N_("Publisher:"), "dc:publisher", RDF_AGENT,
250       N_("Name of entity responsible for making this document available"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
251     },
253     { "identifier", N_("Identifier:"), "dc:identifier", RDF_CONTENT,
254       N_("Unique URI to reference this document"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
255     },
256     { "source", N_("Source:"), "dc:source", RDF_CONTENT,
257       N_("Unique URI to reference the source of this document"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
258     },
259     { "relation", N_("Relation:"), "dc:relation", RDF_CONTENT,
260       N_("Unique URI to a related document"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
261     },
262     { "language", N_("Language:"), "dc:language", RDF_CONTENT,
263       N_("Two-letter language tag with optional subtags for the language of this document (e.g. 'en-GB')"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
264     },
265     { "subject", N_("Keywords:"), "dc:subject", RDF_BAG,
266       N_("The topic of this document as comma-separated key words, phrases, or classifications"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
267     },
268     // TRANSLATORS: "Coverage": the spatial or temporal characteristics of the content.
269     // For info, see Appendix D of http://www.w3.org/TR/1998/WD-rdf-schema-19980409/
270     { "coverage", N_("Coverage:"), "dc:coverage", RDF_CONTENT,
271       N_("Extent or scope of this document"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
272     },
274     { "description", N_("Description:"), "dc:description", RDF_CONTENT,
275       N_("A short account of the content of this document"), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC,
276     },
278     // FIXME: need to handle 1 agent per line of input
279     { "contributor", N_("Contributors:"), "dc:contributor", RDF_AGENT,
280       N_("Names of entities responsible for making contributions to the content of this document"), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC,
281     },
283     // TRANSLATORS: URL to a page that defines the license for the document
284     { "license_uri", N_("URI:"), "cc:license", RDF_RESOURCE,
285       // TRANSLATORS: this is where you put a URL to a page that defines the license
286       N_("URI to this document's license's namespace definition"), RDF_FORMAT_LINE, RDF_EDIT_SPECIAL,
287     },
289       // TRANSLATORS: fragment of XML representing the license of the document
290     { "license_fragment", N_("Fragment:"), "License", RDF_XML,
291       N_("XML fragment for the RDF 'License' section"), RDF_FORMAT_MULTILINE, RDF_EDIT_SPECIAL,
292     },
293     
294     { NULL, NULL, NULL, RDF_CONTENT,
295       NULL, RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
296     }
297 };
300 // Simple start of C++-ification:
301 class RDFImpl
303 public:
304     /**
305      * Some implementations do not put RDF stuff inside <metadata>,
306      * so we need to check for it and add it if we don't see it.
307      */
308     static void ensureParentIsMetadata( SPDocument *doc, Inkscape::XML::Node *node );
310     static Inkscape::XML::Node const *getRdfRootRepr( SPDocument const * doc );
311     static Inkscape::XML::Node *ensureRdfRootRepr( SPDocument * doc );
313     static Inkscape::XML::Node const *getXmlRepr( SPDocument const * doc, gchar const * name );
314     static Inkscape::XML::Node *getXmlRepr( SPDocument * doc, gchar const * name );
315     static Inkscape::XML::Node *ensureXmlRepr( SPDocument * doc, gchar const * name );
317     static Inkscape::XML::Node const *getWorkRepr( SPDocument const * doc, gchar const * name );
318     static Inkscape::XML::Node *ensureWorkRepr( SPDocument * doc, gchar const * name );
320     static const gchar *getWorkEntity(SPDocument const * doc, struct rdf_work_entity_t & entity);
321     static unsigned int setWorkEntity(SPDocument * doc, struct rdf_work_entity_t & entity, gchar const * text);
323     static void setDefaults( SPDocument * doc );
325     /**
326      *  \brief   Pull the text out of an RDF entity, depends on how it's stored
327      *  \return  A pointer to the entity's static contents as a string
328      *  \param   repr    The XML element to extract from
329      *  \param   entity  The desired RDF/Work entity
330      *  
331      */
332     static const gchar *getReprText( Inkscape::XML::Node const * repr, struct rdf_work_entity_t const & entity );
334     static unsigned int setReprText( Inkscape::XML::Node * repr,
335                                      struct rdf_work_entity_t const & entity,
336                                      gchar const * text );
338     static struct rdf_license_t *getLicense(SPDocument const * document);
340     static void setLicense(SPDocument * doc, struct rdf_license_t const * license);
341 };
343 /**
344  *  \brief   Retrieves a known RDF/Work entity by name
345  *  \return  A pointer to an RDF/Work entity
346  *  \param   name  The desired RDF/Work entity
347  *  
348  */
349 struct rdf_work_entity_t *
350 rdf_find_entity(gchar const * name)
352     struct rdf_work_entity_t *entity;
353     for (entity=rdf_work_entities; entity->name; entity++) {
354         if (strcmp(entity->name,name)==0) break;
355     }
356     if (entity->name) return entity;
357     return NULL;
360 /*
361  * Takes the inkscape rdf struct and spits out a static RDF, which is only
362  * useful for testing.  We must merge the rdf struct into the XML DOM for
363  * changes to be saved.
364  */
365 /*
367    Since g_markup_printf_escaped doesn't exist for most people's glib
368    right now, this function will remain commented out since it's only
369    for generic debug anyway.  --Kees
371 gchar *
372 rdf_string(struct rdf_t * rdf)
374     gulong overall=0;
375     gchar *string=NULL;
377     gchar *rdf_head="\
378 <rdf:RDF xmlns=\"http://creativecommons.org/ns#\"\
379     xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\
380     xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\
381 ";
382     gchar *work_head="\
383 <Work rdf:about=\"\">\
384    <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />\
385 ";
386     gchar *work_title=NULL;
387     gchar *work_date=NULL;
388     gchar *work_description=NULL;
389     gchar *work_creator=NULL;
390     gchar *work_owner=NULL;
391     gchar *work_source=NULL;
392     gchar *work_license=NULL;
393     gchar *license_head=NULL;
394     gchar *license=NULL;
395     gchar *license_end="</License>\n";
396     gchar *work_end="</Work>\n";
397     gchar *rdf_end="</rdf:RDF>\n";
399     if (rdf && rdf->work_title && rdf->work_title[0]) {
400         work_title=g_markup_printf_escaped("   <dc:title>%s</dc:title>\n",
401             rdf->work_title);
402     overall+=strlen(work_title);
403     }
404     if (rdf && rdf->work_date && rdf->work_date[0]) {
405         work_date=g_markup_printf_escaped("   <dc:date>%s</dc:date>\n",
406             rdf->work_date);
407     overall+=strlen(work_date);
408     }
409     if (rdf && rdf->work_description && rdf->work_description[0]) {
410         work_description=g_markup_printf_escaped("   <dc:description>%s</dc:description>\n",
411             rdf->work_description);
412     overall+=strlen(work_description);
413     }
414     if (rdf && rdf->work_creator && rdf->work_creator[0]) {
415         work_creator=g_markup_printf_escaped("   <dc:creator><Agent>\
416       <dc:title>%s</dc:title>\
417    </Agent></dc:creator>\n",
418             rdf->work_creator);
419     overall+=strlen(work_creator);
420     }
421     if (rdf && rdf->work_owner && rdf->work_owner[0]) {
422         work_owner=g_markup_printf_escaped("   <dc:rights><Agent>\
423       <dc:title>%s</dc:title>\
424    </Agent></dc:rights>\n",
425             rdf->work_owner);
426     overall+=strlen(work_owner);
427     }
428     if (rdf && rdf->work_source && rdf->work_source[0]) {
429         work_source=g_markup_printf_escaped("   <dc:source rdf:resource=\"%s\" />\n",
430             rdf->work_source);
431     overall+=strlen(work_source);
432     }
433     if (rdf && rdf->license && rdf->license->work_rdf && rdf->license->work_rdf[0]) {
434         work_license=g_markup_printf_escaped("   <license rdf:resource=\"%s\" />\n",
435             rdf->license->work_rdf);
436     overall+=strlen(work_license);
438     license_head=g_markup_printf_escaped("<License rdf:about=\"%s\">\n",
439             rdf->license->work_rdf);
440     overall+=strlen(license_head);
441     overall+=strlen(rdf->license->license_rdf);
442     overall+=strlen(license_end);
443     }
445     overall+=strlen(rdf_head)+strlen(rdf_end);
446     overall+=strlen(work_head)+strlen(work_end);
448     overall++; // NULL term
450     if (!(string=(gchar*)g_malloc(overall))) {
451         return NULL;
452     }
454     string[0]='\0';
455     strcat(string,rdf_head);
456     strcat(string,work_head);
458     if (work_title)       strcat(string,work_title);
459     if (work_date)        strcat(string,work_date);
460     if (work_description) strcat(string,work_description);
461     if (work_creator)     strcat(string,work_creator);
462     if (work_owner)       strcat(string,work_owner);
463     if (work_source)      strcat(string,work_source);
464     if (work_license)     strcat(string,work_license);
466     strcat(string,work_end);
467     if (license_head) {
468         strcat(string,license_head);
469     strcat(string,rdf->license->license_rdf);
470     strcat(string,license_end);
471     }
472     strcat(string,rdf_end);
474     return string;
476 */
479 const gchar *RDFImpl::getReprText( Inkscape::XML::Node const * repr, struct rdf_work_entity_t const & entity )
481     g_return_val_if_fail (repr != NULL, NULL);
482     static gchar * bag = NULL;
483     gchar * holder = NULL;
485     Inkscape::XML::Node const * temp = NULL;
486     switch (entity.datatype) {
487         case RDF_CONTENT:
488             temp = sp_repr_children(repr);
489             if ( temp == NULL ) return NULL;
490             
491             return temp->content();
493         case RDF_AGENT:
494             temp = sp_repr_lookup_name ( repr, "cc:Agent", 1 );
495             if ( temp == NULL ) return NULL;
497             temp = sp_repr_lookup_name ( temp, "dc:title", 1 );
498             if ( temp == NULL ) return NULL;
500             temp = sp_repr_children(temp);
501             if ( temp == NULL ) return NULL;
503             return temp->content();
505         case RDF_RESOURCE:
506             return repr->attribute("rdf:resource");
508         case RDF_XML:
509             return "xml goes here";
511         case RDF_BAG:
512             /* clear the static string.  yucky. */
513             if (bag) g_free(bag);
514             bag = NULL;
516             temp = sp_repr_lookup_name ( repr, "rdf:Bag", 1 );
517             if ( temp == NULL ) {
518                 /* backwards compatible: read contents */
519                 temp = sp_repr_children(repr);
520                 if ( temp == NULL ) return NULL;
521             
522                 return temp->content();
523             }
525             for ( temp = sp_repr_children(temp) ;
526                   temp ;
527                   temp = sp_repr_next(temp) ) {
528                 if (!strcmp(temp->name(),"rdf:li") &&
529                     temp->firstChild()) {
530                     const gchar * str = temp->firstChild()->content();
531                     if (bag) {
532                         holder = bag;
533                         bag = g_strconcat(holder, ", ", str, NULL);
534                         g_free(holder);
535                     }
536                     else {
537                         bag = g_strdup(str);
538                     }
539                 }
540             }
541             return bag;
543         default:
544             break;
545     }
546     return NULL;
549 unsigned int RDFImpl::setReprText( Inkscape::XML::Node * repr,
550                                    struct rdf_work_entity_t const & entity,
551                                    gchar const * text )
553     g_return_val_if_fail ( repr != NULL, 0);
554     g_return_val_if_fail ( text != NULL, 0);
555     gchar * str = NULL;
556     gchar** strlist = NULL;
557     int i;
559     Inkscape::XML::Node * temp=NULL;
560     Inkscape::XML::Node * child=NULL;
561     Inkscape::XML::Node * parent=repr;
563     Inkscape::XML::Document * xmldoc = parent->document();
564     g_return_val_if_fail (xmldoc != NULL, FALSE);
566     // set document's title element to the RDF title
567     if (!strcmp(entity.name, "title")) {
568         SPDocument *doc = SP_ACTIVE_DOCUMENT;
569         if(doc && doc->root) doc->root->setTitle(text);
570     }
572     switch (entity.datatype) {
573         case RDF_CONTENT:
574             temp = sp_repr_children(parent);
575             if ( temp == NULL ) {
576                 temp = xmldoc->createTextNode( text );
577                 g_return_val_if_fail (temp != NULL, FALSE);
579                 parent->appendChild(temp);
580                 Inkscape::GC::release(temp);
582                 return TRUE;
583             }
584             else {
585                 temp->setContent(text);
586                 return TRUE;
587             }
589         case RDF_AGENT:
590             temp = sp_repr_lookup_name ( parent, "cc:Agent", 1 );
591             if ( temp == NULL ) {
592                 temp = xmldoc->createElement ( "cc:Agent" );
593                 g_return_val_if_fail (temp != NULL, FALSE);
595                 parent->appendChild(temp);
596                 Inkscape::GC::release(temp);
597             }
598             parent = temp;
600             temp = sp_repr_lookup_name ( parent, "dc:title", 1 );
601             if ( temp == NULL ) {
602                 temp = xmldoc->createElement ( "dc:title" );
603                 g_return_val_if_fail (temp != NULL, FALSE);
605                 parent->appendChild(temp);
606                 Inkscape::GC::release(temp);
607             }
608             parent = temp;
610             temp = sp_repr_children(parent);
611             if ( temp == NULL ) {
612                 temp = xmldoc->createTextNode( text );
613                 g_return_val_if_fail (temp != NULL, FALSE);
615                 parent->appendChild(temp);
616                 Inkscape::GC::release(temp);
618                 return TRUE;
619             }
620             else {
621                 temp->setContent(text);
622                                 return TRUE;
623             }
625         case RDF_RESOURCE:
626             parent->setAttribute("rdf:resource", text );
627             return true;
629         case RDF_XML:
630             return 1;
632         case RDF_BAG:
633             /* find/create the rdf:Bag item */
634             temp = sp_repr_lookup_name ( parent, "rdf:Bag", 1 );
635             if ( temp == NULL ) {
636                 /* backward compatibility: drop the dc:subject contents */
637                 while ( (temp = sp_repr_children( parent )) ) {
638                     parent->removeChild(temp);
639                 }
641                 temp = xmldoc->createElement ( "rdf:Bag" );
642                 g_return_val_if_fail (temp != NULL, FALSE);
644                 parent->appendChild(temp);
645                 Inkscape::GC::release(temp);
646             }
647             parent = temp;
649             /* toss all the old list items */
650             while ( (temp = sp_repr_children( parent )) ) {
651                 parent->removeChild(temp);
652             }
654             /* chop our list up on commas */
655             strlist = g_strsplit( text, ",", 0);
657             for (i = 0; (str = strlist[i]); i++) {
658                 temp = xmldoc->createElement ( "rdf:li" );
659                 g_return_val_if_fail (temp != NULL, 0);
661                 parent->appendChild(temp);
662                 Inkscape::GC::release(temp);
664                 child = xmldoc->createTextNode( g_strstrip(str) );
665                 g_return_val_if_fail (child != NULL, 0);
667                 temp->appendChild(child);
668                 Inkscape::GC::release(child);
669             }
670             g_strfreev( strlist );
672             return 1;
674         default:
675             break;
676     }
677     return 0;
680 void RDFImpl::ensureParentIsMetadata( SPDocument *doc, Inkscape::XML::Node *node )
682     if ( !node ) {
683         g_critical("Null node passed to ensureParentIsMetadata().");
684     } else if ( !node->parent() ) {
685         g_critical( "No parent node when verifying <metadata> placement." );
686     } else {
687         Inkscape::XML::Node * currentParent = node->parent();
688         if ( strcmp( currentParent->name(), XML_TAG_NAME_METADATA ) != 0 ) {
689             Inkscape::XML::Node * metadata = doc->getReprDoc()->createElement( XML_TAG_NAME_METADATA );
690             if ( !metadata ) {
691                 g_critical("Unable to create metadata element.");
692             } else {
693                 // attach the metadata node
694                 currentParent->appendChild( metadata );
695                 Inkscape::GC::release( metadata );
697                 // move the node into it
698                 Inkscape::GC::anchor( node );
699                 sp_repr_unparent( node );
700                 metadata->appendChild( node );
701                 Inkscape::GC::release( node );
702             }
703         }
704     }
707 Inkscape::XML::Node const *RDFImpl::getRdfRootRepr( SPDocument const * doc )
709     Inkscape::XML::Node const *rdf = 0;
710     if ( !doc ) {
711         g_critical("Null doc passed to getRdfRootRepr()");
712     } else if ( !doc->getReprDoc() ) {
713         g_critical("XML doc is null.");
714     } else {
715         rdf = sp_repr_lookup_name( doc->getReprDoc(), XML_TAG_NAME_RDF );
716     }
718     return rdf;
721 Inkscape::XML::Node *RDFImpl::ensureRdfRootRepr( SPDocument * doc )
723     Inkscape::XML::Node *rdf = 0;
724     if ( !doc ) {
725         g_critical("Null doc passed to ensureRdfRootRepr()");
726     } else if ( !doc->getReprDoc() ) {
727         g_critical("XML doc is null.");
728     } else {
729         rdf = sp_repr_lookup_name( doc->getReprDoc(), XML_TAG_NAME_RDF );
730         if ( !rdf ) {
731             Inkscape::XML::Node * svg = sp_repr_lookup_name( doc->getReprRoot(), XML_TAG_NAME_SVG );
732             if ( !svg ) {
733                 g_critical("Unable to locate svg element.");
734             } else {
735                 Inkscape::XML::Node * parent = sp_repr_lookup_name( svg, XML_TAG_NAME_METADATA );
736                 if ( parent == NULL ) {
737                     parent = doc->getReprDoc()->createElement( XML_TAG_NAME_METADATA );
738                     if ( !parent ) {
739                         g_critical("Unable to create metadata element");
740                     } else {
741                         svg->appendChild(parent);
742                         Inkscape::GC::release(parent);
743                     }
744                 }
746                 if ( parent && !parent->document() ) {
747                     g_critical("Parent has no document");
748                 } else if ( parent ) {
749                     rdf = parent->document()->createElement( XML_TAG_NAME_RDF );
750                     if ( !rdf ) {
751                         g_critical("Unable to create root RDF element.");
752                     } else {
753                         parent->appendChild(rdf);
754                         Inkscape::GC::release(rdf);
755                     }
756                 }
757             }
758         }
759     }
761     if ( rdf ) {
762         ensureParentIsMetadata( doc, rdf );
763     }
765     return rdf;
768 Inkscape::XML::Node const *RDFImpl::getXmlRepr( SPDocument const * doc, gchar const * name )
770     Inkscape::XML::Node const * xml = 0;
771     if ( !doc ) {
772         g_critical("Null doc passed to getXmlRepr()");
773     } else if ( !doc->getReprDoc() ) {
774         g_critical("XML doc is null.");
775     } else if (!name) {
776         g_critical("Null name passed to getXmlRepr()");        
777     } else {
778         Inkscape::XML::Node const * rdf = getRdfRootRepr( doc );
779         if ( rdf ) {
780             xml = sp_repr_lookup_name( rdf, name );
781         }
782     }
783     return xml;
786 Inkscape::XML::Node *RDFImpl::getXmlRepr( SPDocument * doc, gchar const * name )
788     Inkscape::XML::Node const *xml = getXmlRepr( const_cast<SPDocument const *>(doc), name );
790     return const_cast<Inkscape::XML::Node *>(xml);
793 Inkscape::XML::Node *RDFImpl::ensureXmlRepr( SPDocument * doc, gchar const * name )
795     Inkscape::XML::Node * xml = 0;
796     if ( !doc ) {
797         g_critical("Null doc passed to ensureXmlRepr()");
798     } else if ( !doc->getReprDoc() ) {
799         g_critical("XML doc is null.");
800     } else if (!name) {
801         g_critical("Null name passed to ensureXmlRepr()");        
802     } else {
803         Inkscape::XML::Node * rdf = ensureRdfRootRepr( doc );
804         if ( rdf ) {
805             xml = sp_repr_lookup_name( rdf, name );
806             if ( !xml ) {
807                 xml = doc->getReprDoc()->createElement( name );
808                 if ( !xml ) {
809                     g_critical("Unable to create xml element <%s>.", name);
810                 } else {
811                     xml->setAttribute("rdf:about", "" );
813                     rdf->appendChild(xml);
814                     Inkscape::GC::release(xml);
815                 }
816             }
817         }
818     }
819     return xml;
822 Inkscape::XML::Node const *RDFImpl::getWorkRepr( SPDocument const * doc, gchar const * name )
824     Inkscape::XML::Node const * item = 0;
825     if ( !doc ) {
826         g_critical("Null doc passed to getWorkRepr()");
827     } else if ( !doc->getReprDoc() ) {
828         g_critical("XML doc is null.");
829     } else if (!name) {
830         g_critical("Null name passed to getWorkRepr()");        
831     } else {
832         Inkscape::XML::Node const* work = getXmlRepr( doc, XML_TAG_NAME_WORK );
833         if ( work ) {
834             item = sp_repr_lookup_name( work, name, 1 );
835         }
836     }
837     return item;
840 Inkscape::XML::Node *RDFImpl::ensureWorkRepr( SPDocument * doc, gchar const * name )
842     Inkscape::XML::Node * item = 0;
843     if ( !doc ) {
844         g_critical("Null doc passed to ensureWorkRepr()");
845     } else if ( !doc->getReprDoc() ) {
846         g_critical("XML doc is null.");
847     } else if (!name) {
848         g_critical("Null name passed to ensureWorkRepr()");        
849     } else {
850         Inkscape::XML::Node * work = ensureXmlRepr( doc, XML_TAG_NAME_WORK );
851         if ( work ) {
852             item = sp_repr_lookup_name( work, name, 1 );
853             if ( !item ) {
854                 //printf("missing XML '%s'\n",name);
855                 item = doc->getReprDoc()->createElement( name );
856                 if ( !item ) {
857                     g_critical("Unable to create xml element <%s>", name);
858                 } else {
859                     work->appendChild(item);
860                     Inkscape::GC::release(item);
861                 }
862             }
863         }
864     }
865     return item;
869 // Public API:
870 const gchar *rdf_get_work_entity(SPDocument const * doc, struct rdf_work_entity_t * entity)
872     const gchar *result = 0;
873     if ( !doc ) {
874         g_critical("Null doc passed to rdf_get_work_entity()");
875     } else if ( entity ) {
876         //g_message("want '%s'\n",entity->title);
878         result = RDFImpl::getWorkEntity( doc, *entity );
880         //g_message("found '%s' == '%s'\n", entity->title, result );
881     }
882     return result;
885 const gchar *RDFImpl::getWorkEntity(SPDocument const * doc, struct rdf_work_entity_t & entity)
887     gchar const *result = 0;
889     Inkscape::XML::Node const * item = getWorkRepr( doc, entity.tag );
890     if ( item ) {
891         result = getReprText( item, entity );
892         // TODO note that this is the location that used to set the title if needed. Ensure code it not required.
893     }
895     return result;
898 // Public API:
899 unsigned int rdf_set_work_entity(SPDocument * doc, struct rdf_work_entity_t * entity,
900                                  const gchar * text)
902     unsigned int result = 0;
903     if ( !doc ) {
904         g_critical("Null doc passed to rdf_set_work_entity()");
905     } else if ( entity ) {
906         result = RDFImpl::setWorkEntity( doc, *entity, text );
907     }
909     return result;
912 unsigned int RDFImpl::setWorkEntity(SPDocument * doc, struct rdf_work_entity_t & entity, const gchar * text)
914     int result = 0;
915     if ( !text ) {
916         // FIXME: on a "NULL" text, delete the entity.  For now, blank it.
917         text = "";
918     }
920     /*
921       printf("changing '%s' (%s) to '%s'\n",
922       entity->title,
923       entity->tag,
924       text);
925     */
927     Inkscape::XML::Node * item = ensureWorkRepr( doc, entity.tag );
928     if ( !item ) {
929         g_critical("Unable to get work element");
930     } else {
931         result = setReprText( item, entity, text );
932     }
933     return result;
937 #undef DEBUG_MATCH
939 static bool
940 rdf_match_license(Inkscape::XML::Node const *repr, struct rdf_license_t const *license)
942     g_assert ( repr != NULL );
943     g_assert ( license != NULL );
945     bool result=TRUE;
946 #ifdef DEBUG_MATCH
947     printf("checking against '%s'\n",license->name);
948 #endif
950     int count = 0;
951     for (struct rdf_double_t const *details = license->details;
952          details->name; details++ ) {
953         count++;
954     }
955     bool * matched = (bool*)calloc(count,sizeof(bool));
957     for (Inkscape::XML::Node const *current = sp_repr_children(repr);
958          current;
959          current = sp_repr_next ( current ) ) {
961         gchar const * attr = current->attribute("rdf:resource");
962         if ( attr == NULL ) continue;
964 #ifdef DEBUG_MATCH
965         printf("\texamining '%s' => '%s'\n", current->name(), attr);
966 #endif
968         bool found_match=FALSE;
969         for (int i=0; i<count; i++) {
970             // skip already matched items
971             if (matched[i]) continue;
973 #ifdef DEBUG_MATCH
974             printf("\t\t'%s' vs '%s'\n", current->name(), license->details[i].name);
975             printf("\t\t'%s' vs '%s'\n", attr, license->details[i].resource);
976 #endif
978             if (!strcmp( current->name(),
979                          license->details[i].name ) &&
980                 !strcmp( attr,
981                          license->details[i].resource )) {
982                 matched[i]=TRUE;
983                 found_match=TRUE;
984 #ifdef DEBUG_MATCH
985                 printf("\t\tgood!\n");
986 #endif
987                 break;
988             }
989         }
990         if (!found_match) {
991             // if we checked each known item of the license
992             // and didn't find it, we must abort
993             result=FALSE;
994 #ifdef DEBUG_MATCH
995             printf("\t\tno '%s' element matched XML (bong)!\n",license->name);
996 #endif
997             break;
998         }
999     }
1000 #ifdef DEBUG_MATCH
1001     if (result) printf("\t\tall XML found matching elements!\n");
1002 #endif
1003     for (int i=0; result && i<count; i++) {
1004         // scan looking for an unmatched item
1005         if (matched[i]==0) {
1006             result=FALSE;
1007 #ifdef DEBUG_MATCH
1008             printf("\t\tnot all '%s' elements used to match (bong)!\n", license->name);
1009 #endif
1010         }
1011     }
1013 #ifdef DEBUG_MATCH
1014     printf("\t\tall '%s' elements used to match!\n",license->name);
1015 #endif
1017     free(matched);
1019 #ifdef DEBUG_MATCH
1020     if (result) printf("matched '%s'\n",license->name);
1021 #endif
1022     return result;
1025 // Public API:
1026 struct rdf_license_t *rdf_get_license(SPDocument const * document)
1028     return RDFImpl::getLicense(document);
1031 struct rdf_license_t *RDFImpl::getLicense(SPDocument const *document)
1033     Inkscape::XML::Node const *repr = getXmlRepr( document, XML_TAG_NAME_LICENSE );
1034     if (repr) {
1035         for ( struct rdf_license_t * license = rdf_licenses; license->name; license++ ) {
1036             if ( rdf_match_license( repr, license ) ) {
1037                 return license;
1038             }
1039         }
1040     }
1041 #ifdef DEBUG_MATCH
1042     else {
1043         printf("no license XML\n");
1044     }
1045 #endif
1046     return NULL;
1049 // Public API:
1050 void rdf_set_license(SPDocument * doc, struct rdf_license_t const * license)
1052     RDFImpl::setLicense( doc, license );
1055 void RDFImpl::setLicense(SPDocument * doc, struct rdf_license_t const * license)
1057     // drop old license section
1058     Inkscape::XML::Node * repr = getXmlRepr( doc, XML_TAG_NAME_LICENSE );
1059     if (repr) {
1060         sp_repr_unparent(repr);
1061     }
1063     if ( !license ) {
1064         // All done
1065     } else if ( !doc->getReprDoc() ) {
1066         g_critical("XML doc is null.");
1067     } else {
1068         // build new license section
1069         repr = ensureXmlRepr( doc, XML_TAG_NAME_LICENSE );
1070         g_assert( repr );
1072         repr->setAttribute("rdf:about", license->uri );
1074         for (struct rdf_double_t const * detail = license->details; detail->name; detail++) {
1075             Inkscape::XML::Node * child = doc->getReprDoc()->createElement( detail->name );
1076             g_assert ( child != NULL );
1078             child->setAttribute("rdf:resource", detail->resource );
1079             repr->appendChild(child);
1080             Inkscape::GC::release(child);
1081         }
1082     }
1085 struct rdf_entity_default_t {
1086     gchar const * name;
1087     gchar const * text;
1088 };
1089 struct rdf_entity_default_t rdf_defaults[] = {
1090     { "format",      "image/svg+xml", },
1091     { "type",        "http://purl.org/dc/dcmitype/StillImage", },
1092     { NULL,          NULL, }
1093 };
1095 // Public API:
1096 void rdf_set_defaults( SPDocument * doc )
1098     RDFImpl::setDefaults( doc );
1102 void RDFImpl::setDefaults( SPDocument * doc )
1104     g_assert( doc != NULL );
1106     // Create metadata node if it doesn't already exist
1107     if (!sp_item_group_get_child_by_name((SPGroup *) doc->root, NULL,
1108                                           XML_TAG_NAME_METADATA)) {
1109         if ( !doc->getReprDoc()) {
1110             g_critical("XML doc is null.");
1111         } else {
1112             // create repr
1113             Inkscape::XML::Node * rnew = doc->getReprDoc()->createElement(XML_TAG_NAME_METADATA);
1115             // insert into the document
1116             doc->getReprRoot()->addChild(rnew, NULL);
1118             // clean up
1119             Inkscape::GC::release(rnew);
1120         }
1121     }
1123     // install defaults
1124     for ( struct rdf_entity_default_t * rdf_default = rdf_defaults; rdf_default->name; rdf_default++) {
1125         struct rdf_work_entity_t * entity = rdf_find_entity( rdf_default->name );
1126         g_assert( entity != NULL );
1128         if ( getWorkEntity( doc, *entity ) == NULL ) {
1129             setWorkEntity( doc, *entity, rdf_default->text );
1130         }
1131     }
1135 /*
1136   Local Variables:
1137   mode:c++
1138   c-file-style:"stroustrup"
1139   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1140   indent-tabs-mode:nil
1141   fill-column:99
1142   End:
1143 */
1144 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :