Code

A simple layout document as to what, why and how is cppification.
[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  *
10  * Copyright (C) 2004 Kees Cook <kees@outflux.net>
11  * Copyright (C) 2006 Jon Phillips <jon@rejon.org>
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #include "xml/repr.h"
17 #include "rdf.h"
18 #include "sp-item-group.h"
19 #include "inkscape.h"
21 /*
22    Example RDF XML from various places...
23  
24 <rdf:RDF xmlns="http://creativecommons.org/ns#"
25     xmlns:dc="http://purl.org/dc/elements/1.1/"
26     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
27 <Work rdf:about="">
28    <dc:title>title of work</dc:title>
29    <dc:date>year</dc:date>
30    <dc:description>description of work</dc:description>
31    <dc:creator><Agent>
32       <dc:title>creator</dc:title>
33    </Agent></dc:creator>
34    <dc:rights><Agent>
35       <dc:title>holder</dc:title>
36    </Agent></dc:rights>
37    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
38    <dc:source rdf:resource="source"/>
39    <license rdf:resource="http://creativecommons.org/licenses/by/2.0/" 
40 />
41 </Work>
44   <rdf:RDF xmlns="http://creativecommons.org/ns#"
45       xmlns:dc="http://purl.org/dc/elements/1.1/"
46       xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
47   <Work rdf:about="">
48      <dc:title>SVG Road Signs</dc:title>
49      <dc:rights><Agent>
50         <dc:title>John Cliff</dc:title>
51      </Agent></dc:rights>
52      <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
53      <license rdf:resource="http://creativecommons.org/ns#PublicDomain" />
54   </Work>
55   
56   <License rdf:about="http://creativecommons.org/ns#PublicDomain">
57      <permits rdf:resource="http://creativecommons.org/ns#Reproduction" />
58      <permits rdf:resource="http://creativecommons.org/ns#Distribution" />
59      <permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
60   </License>
61   
62 </rdf:RDF>
65 Bag example:
67 <dc:subject>
68 <rdf:Bag>
69 <rdf:li>open clip art logo</rdf:li>
70 <rdf:li>images</rdf:li>
71 <rdf:li>logo</rdf:li>
72 <rdf:li>clip art</rdf:li>
73 <rdf:li>ocal</rdf:li>
74 <rdf:li>logotype</rdf:li>
75 <rdf:li>filetype</rdf:li>
76 </rdf:Bag>
77 </dc:subject>
78 */
80 struct rdf_double_t rdf_license_empty [] = {
81     { NULL, NULL }
82 };
84 struct rdf_double_t rdf_license_cc_a [] = {
85     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
86     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
87     { "cc:requires", "http://creativecommons.org/ns#Notice", },
88     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
89     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
90     { NULL, NULL }
91 };
93 struct rdf_double_t rdf_license_cc_a_sa [] = {
94     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
95     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
96     { "cc:requires", "http://creativecommons.org/ns#Notice", },
97     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
98     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
99     { "cc:requires", "http://creativecommons.org/ns#ShareAlike", },
100     { NULL, NULL }
101 };
103 struct rdf_double_t rdf_license_cc_a_nd [] = {
104     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
105     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
106     { "cc:requires", "http://creativecommons.org/ns#Notice", },
107     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
108     { NULL, NULL }
109 };
111 struct rdf_double_t rdf_license_cc_a_nc [] = {
112     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
113     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
114     { "cc:requires", "http://creativecommons.org/ns#Notice", },
115     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
116     { "cc:prohibits", "http://creativecommons.org/ns#CommercialUse", },
117     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
118     { NULL, NULL }
119 };
121 struct rdf_double_t rdf_license_cc_a_nc_sa [] = {
122     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
123     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
124     { "cc:requires", "http://creativecommons.org/ns#Notice", },
125     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
126     { "cc:prohibits", "http://creativecommons.org/ns#CommercialUse", },
127     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
128     { "cc:requires", "http://creativecommons.org/ns#ShareAlike", },
129     { NULL, NULL }
130 };
132 struct rdf_double_t rdf_license_cc_a_nc_nd [] = {
133     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
134     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
135     { "cc:requires", "http://creativecommons.org/ns#Notice", },
136     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
137     { "cc:prohibits", "http://creativecommons.org/ns#CommercialUse", },
138     { NULL, NULL }
139 };
141 struct rdf_double_t rdf_license_pd [] = {
142     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
143     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
144     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
145     { NULL, NULL }
146 };
148 struct rdf_double_t rdf_license_freeart [] = {
149     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
150     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
151     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
152     { "cc:requires", "http://creativecommons.org/ns#ShareAlike", },
153     { "cc:requires", "http://creativecommons.org/ns#Notice", },
154     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
155     { NULL, NULL }
156 };
158 struct rdf_double_t rdf_license_ofl [] = {
159     { "cc:permits", "http://scripts.sil.org/pub/OFL/Reproduction", },
160     { "cc:permits", "http://scripts.sil.org/pub/OFL/Distribution", },
161     { "cc:permits", "http://scripts.sil.org/pub/OFL/Embedding", },
162     { "cc:permits", "http://scripts.sil.org/pub/OFL/DerivativeWorks", },
163     { "cc:requires", "http://scripts.sil.org/pub/OFL/Notice", },
164     { "cc:requires", "http://scripts.sil.org/pub/OFL/Attribution", },
165     { "cc:requires", "http://scripts.sil.org/pub/OFL/ShareAlike", },
166     { "cc:requires", "http://scripts.sil.org/pub/OFL/DerivativeRenaming", },
167     { "cc:requires", "http://scripts.sil.org/pub/OFL/BundlingWhenSelling", },
168     { NULL, NULL }
169 };
171 struct rdf_license_t rdf_licenses [] = {
172     { N_("CC Attribution"), 
173       "http://creativecommons.org/licenses/by/3.0/",
174       rdf_license_cc_a,
175     },
177     { N_("CC Attribution-ShareAlike"), 
178       "http://creativecommons.org/licenses/by-sa/3.0/",
179       rdf_license_cc_a_sa,
180     },
182     { N_("CC Attribution-NoDerivs"), 
183       "http://creativecommons.org/licenses/by-nd/3.0/",
184       rdf_license_cc_a_nd,
185     },
187     { N_("CC Attribution-NonCommercial"), 
188       "http://creativecommons.org/licenses/by-nc/3.0/",
189       rdf_license_cc_a_nc,
190     },
192     { N_("CC Attribution-NonCommercial-ShareAlike"), 
193       "http://creativecommons.org/licenses/by-nc-sa/3.0/",
194       rdf_license_cc_a_nc_sa,
195     },
197     { N_("CC Attribution-NonCommercial-NoDerivs"), 
198       "http://creativecommons.org/licenses/by-nc-nd/3.0/",
199       rdf_license_cc_a_nc_nd,
200     },
202     { N_("Public Domain"),
203       "http://creativecommons.org/licenses/publicdomain/",
204       rdf_license_pd,
205     },
207     { N_("FreeArt"),
208       "http://artlibre.org/licence/lal",
209       rdf_license_freeart,
210     },
212     { N_("Open Font License"),
213       "http://scripts.sil.org/OFL",
214       rdf_license_ofl,
215     },
217     { NULL, NULL, rdf_license_empty, }
218 };
220 #define XML_TAG_NAME_SVG      "svg:svg"
221 #define XML_TAG_NAME_METADATA "svg:metadata"
222 #define XML_TAG_NAME_RDF      "rdf:RDF"
223 #define XML_TAG_NAME_WORK     "cc:Work"
224 #define XML_TAG_NAME_LICENSE  "cc:License"
226 // Remember when using the "title" and "tip" elements to pass them through
227 // the localization functions when you use them!
228 struct rdf_work_entity_t rdf_work_entities [] = {
229     { "title", N_("Title"), "dc:title", RDF_CONTENT,
230       N_("Name by which this document is formally known."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
231     },
232     { "date", N_("Date"), "dc:date", RDF_CONTENT,
233       N_("Date associated with the creation of this document (YYYY-MM-DD)."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
234     },
235     { "format", N_("Format"), "dc:format", RDF_CONTENT,
236       N_("The physical or digital manifestation of this document (MIME type)."), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
237     },
238     { "type", N_("Type"), "dc:type", RDF_RESOURCE,
239       N_("Type of document (DCMI Type)."), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
240     },
242     { "creator", N_("Creator"), "dc:creator", RDF_AGENT,
243       N_("Name of entity primarily responsible for making the content of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
244     },
245     { "rights", N_("Rights"), "dc:rights", RDF_AGENT,
246       N_("Name of entity with rights to the Intellectual Property of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
247     },
248     { "publisher", N_("Publisher"), "dc:publisher", RDF_AGENT,
249       N_("Name of entity responsible for making this document available."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
250     },
252     { "identifier", N_("Identifier"), "dc:identifier", RDF_CONTENT,
253       N_("Unique URI to reference this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
254     },
255     { "source", N_("Source"), "dc:source", RDF_CONTENT,
256       N_("Unique URI to reference the source of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
257     },
258     { "relation", N_("Relation"), "dc:relation", RDF_CONTENT,
259       N_("Unique URI to a related document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
260     },
261     { "language", N_("Language"), "dc:language", RDF_CONTENT,
262       N_("Two-letter language tag with optional subtags for the language of this document.  (e.g. 'en-GB')"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
263     },
264     { "subject", N_("Keywords"), "dc:subject", RDF_BAG,
265       N_("The topic of this document as comma-separated key words, phrases, or classifications."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
266     },
267     // TRANSLATORS: "Coverage": the spatial or temporal characteristics of the content.
268     // For info, see Appendix D of http://www.w3.org/TR/1998/WD-rdf-schema-19980409/
269     { "coverage", N_("Coverage"), "dc:coverage", RDF_CONTENT,
270       N_("Extent or scope of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
271     },
273     { "description", N_("Description"), "dc:description", RDF_CONTENT,
274       N_("A short account of the content of this document."), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC,
275     },
277     // FIXME: need to handle 1 agent per line of input
278     { "contributor", N_("Contributors"), "dc:contributor", RDF_AGENT,
279       N_("Names of entities responsible for making contributions to the content of this document."), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC,
280     },
282     // TRANSLATORS: URL to a page that defines the license for the document
283     { "license_uri", N_("URI"), "cc:license", RDF_RESOURCE,
284       // TRANSLATORS: this is where you put a URL to a page that defines the license
285       N_("URI to this document's license's namespace definition."), RDF_FORMAT_LINE, RDF_EDIT_SPECIAL,
286     },
288       // TRANSLATORS: fragment of XML representing the license of the document
289     { "license_fragment", N_("Fragment"), "License", RDF_XML,
290       N_("XML fragment for the RDF 'License' section."), RDF_FORMAT_MULTILINE, RDF_EDIT_SPECIAL,
291     },
292     
293     { NULL, NULL, NULL, RDF_CONTENT,
294       NULL, RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
295     }
296 };
298 /**
299  *  \brief   Retrieves a known RDF/Work entity by name
300  *  \return  A pointer to an RDF/Work entity
301  *  \param   name  The desired RDF/Work entity
302  *  
303  */
304 struct rdf_work_entity_t *
305 rdf_find_entity(gchar const * name)
307     struct rdf_work_entity_t *entity;
308     for (entity=rdf_work_entities; entity->name; entity++) {
309         if (strcmp(entity->name,name)==0) break;
310     }
311     if (entity->name) return entity;
312     return NULL;
315 /*
316  * Takes the inkscape rdf struct and spits out a static RDF, which is only
317  * useful for testing.  We must merge the rdf struct into the XML DOM for
318  * changes to be saved.
319  */
320 /*
322    Since g_markup_printf_escaped doesn't exist for most people's glib
323    right now, this function will remain commented out since it's only
324    for generic debug anyway.  --Kees
326 gchar *
327 rdf_string(struct rdf_t * rdf)
329     gulong overall=0;
330     gchar *string=NULL;
332     gchar *rdf_head="\
333 <rdf:RDF xmlns=\"http://creativecommons.org/ns#\"\
334     xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\
335     xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\
336 ";
337     gchar *work_head="\
338 <Work rdf:about=\"\">\
339    <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />\
340 ";
341     gchar *work_title=NULL;
342     gchar *work_date=NULL;
343     gchar *work_description=NULL;
344     gchar *work_creator=NULL;
345     gchar *work_owner=NULL;
346     gchar *work_source=NULL;
347     gchar *work_license=NULL;
348     gchar *license_head=NULL;
349     gchar *license=NULL;
350     gchar *license_end="</License>\n";
351     gchar *work_end="</Work>\n";
352     gchar *rdf_end="</rdf:RDF>\n";
354     if (rdf && rdf->work_title && rdf->work_title[0]) {
355         work_title=g_markup_printf_escaped("   <dc:title>%s</dc:title>\n",
356             rdf->work_title);
357     overall+=strlen(work_title);
358     }
359     if (rdf && rdf->work_date && rdf->work_date[0]) {
360         work_date=g_markup_printf_escaped("   <dc:date>%s</dc:date>\n",
361             rdf->work_date);
362     overall+=strlen(work_date);
363     }
364     if (rdf && rdf->work_description && rdf->work_description[0]) {
365         work_description=g_markup_printf_escaped("   <dc:description>%s</dc:description>\n",
366             rdf->work_description);
367     overall+=strlen(work_description);
368     }
369     if (rdf && rdf->work_creator && rdf->work_creator[0]) {
370         work_creator=g_markup_printf_escaped("   <dc:creator><Agent>\
371       <dc:title>%s</dc:title>\
372    </Agent></dc:creator>\n",
373             rdf->work_creator);
374     overall+=strlen(work_creator);
375     }
376     if (rdf && rdf->work_owner && rdf->work_owner[0]) {
377         work_owner=g_markup_printf_escaped("   <dc:rights><Agent>\
378       <dc:title>%s</dc:title>\
379    </Agent></dc:rights>\n",
380             rdf->work_owner);
381     overall+=strlen(work_owner);
382     }
383     if (rdf && rdf->work_source && rdf->work_source[0]) {
384         work_source=g_markup_printf_escaped("   <dc:source rdf:resource=\"%s\" />\n",
385             rdf->work_source);
386     overall+=strlen(work_source);
387     }
388     if (rdf && rdf->license && rdf->license->work_rdf && rdf->license->work_rdf[0]) {
389         work_license=g_markup_printf_escaped("   <license rdf:resource=\"%s\" />\n",
390             rdf->license->work_rdf);
391     overall+=strlen(work_license);
393     license_head=g_markup_printf_escaped("<License rdf:about=\"%s\">\n",
394             rdf->license->work_rdf);
395     overall+=strlen(license_head);
396     overall+=strlen(rdf->license->license_rdf);
397     overall+=strlen(license_end);
398     }
400     overall+=strlen(rdf_head)+strlen(rdf_end);
401     overall+=strlen(work_head)+strlen(work_end);
403     overall++; // NULL term
405     if (!(string=(gchar*)g_malloc(overall))) {
406         return NULL;
407     }
409     string[0]='\0';
410     strcat(string,rdf_head);
411     strcat(string,work_head);
413     if (work_title)       strcat(string,work_title);
414     if (work_date)        strcat(string,work_date);
415     if (work_description) strcat(string,work_description);
416     if (work_creator)     strcat(string,work_creator);
417     if (work_owner)       strcat(string,work_owner);
418     if (work_source)      strcat(string,work_source);
419     if (work_license)     strcat(string,work_license);
421     strcat(string,work_end);
422     if (license_head) {
423         strcat(string,license_head);
424     strcat(string,rdf->license->license_rdf);
425     strcat(string,license_end);
426     }
427     strcat(string,rdf_end);
429     return string;
431 */
434 /**
435  *  \brief   Pull the text out of an RDF entity, depends on how it's stored
436  *  \return  A pointer to the entity's static contents as a string
437  *  \param   repr    The XML element to extract from
438  *  \param   entity  The desired RDF/Work entity
439  *  
440  */
441 const gchar *
442 rdf_get_repr_text ( Inkscape::XML::Node * repr, struct rdf_work_entity_t * entity )
444     g_return_val_if_fail (repr != NULL, NULL);
445     g_return_val_if_fail (entity != NULL, NULL);
446     static gchar * bag = NULL;
447     gchar * holder = NULL;
449     Inkscape::XML::Node * temp=NULL;
450     switch (entity->datatype) {
451         case RDF_CONTENT:
452             temp = sp_repr_children(repr);
453             if ( temp == NULL ) return NULL;
454             
455             return temp->content();
457         case RDF_AGENT:
458             temp = sp_repr_lookup_name ( repr, "cc:Agent", 1 );
459             if ( temp == NULL ) return NULL;
461             temp = sp_repr_lookup_name ( temp, "dc:title", 1 );
462             if ( temp == NULL ) return NULL;
464             temp = sp_repr_children(temp);
465             if ( temp == NULL ) return NULL;
467             return temp->content();
469         case RDF_RESOURCE:
470             return repr->attribute("rdf:resource");
472         case RDF_XML:
473             return "xml goes here";
475         case RDF_BAG:
476             /* clear the static string.  yucky. */
477             if (bag) g_free(bag);
478             bag = NULL;
480             temp = sp_repr_lookup_name ( repr, "rdf:Bag", 1 );
481             if ( temp == NULL ) {
482                 /* backwards compatible: read contents */
483                 temp = sp_repr_children(repr);
484                 if ( temp == NULL ) return NULL;
485             
486                 return temp->content();
487             }
489             for ( temp = sp_repr_children(temp) ;
490                   temp ;
491                   temp = sp_repr_next(temp) ) {
492                 if (!strcmp(temp->name(),"rdf:li") &&
493                     temp->firstChild()) {
494                     const gchar * str = temp->firstChild()->content();
495                     if (bag) {
496                         holder = bag;
497                         bag = g_strconcat(holder, ", ", str, NULL);
498                         g_free(holder);
499                     }
500                     else {
501                         bag = g_strdup(str);
502                     }
503                 }
504             }
505             return bag;
507         default:
508             break;
509     }
510     return NULL;
513 unsigned int
514 rdf_set_repr_text ( Inkscape::XML::Node * repr,
515                     struct rdf_work_entity_t * entity,
516                     gchar const * text )
518     g_return_val_if_fail ( repr != NULL, 0);
519     g_return_val_if_fail ( entity != NULL, 0);
520     g_return_val_if_fail ( text != NULL, 0);
521     gchar * str = NULL;
522     gchar** strlist = NULL;
523     int i;
525     Inkscape::XML::Node * temp=NULL;
526     Inkscape::XML::Node * child=NULL;
527     Inkscape::XML::Node * parent=repr;
529     Inkscape::XML::Document * xmldoc = parent->document();
530     g_return_val_if_fail (xmldoc != NULL, FALSE);
532     // set document's title element to the RDF title
533     if (!strcmp(entity->name, "title")) {
534         SPDocument *doc = SP_ACTIVE_DOCUMENT;
535         if(doc && doc->root) doc->root->setTitle(text);
536     }
538     switch (entity->datatype) {
539         case RDF_CONTENT:
540             temp = sp_repr_children(parent);
541             if ( temp == NULL ) {
542                 temp = xmldoc->createTextNode( text );
543                 g_return_val_if_fail (temp != NULL, FALSE);
545                 parent->appendChild(temp);
546                 Inkscape::GC::release(temp);
548                 return TRUE;
549             }
550             else {
551                 temp->setContent(text);
552                 return TRUE;
553             }
555         case RDF_AGENT:
556             temp = sp_repr_lookup_name ( parent, "cc:Agent", 1 );
557             if ( temp == NULL ) {
558                 temp = xmldoc->createElement ( "cc:Agent" );
559                 g_return_val_if_fail (temp != NULL, FALSE);
561                 parent->appendChild(temp);
562                 Inkscape::GC::release(temp);
563             }
564             parent = temp;
566             temp = sp_repr_lookup_name ( parent, "dc:title", 1 );
567             if ( temp == NULL ) {
568                 temp = xmldoc->createElement ( "dc:title" );
569                 g_return_val_if_fail (temp != NULL, FALSE);
571                 parent->appendChild(temp);
572                 Inkscape::GC::release(temp);
573             }
574             parent = temp;
576             temp = sp_repr_children(parent);
577             if ( temp == NULL ) {
578                 temp = xmldoc->createTextNode( text );
579                 g_return_val_if_fail (temp != NULL, FALSE);
581                 parent->appendChild(temp);
582                 Inkscape::GC::release(temp);
584                 return TRUE;
585             }
586             else {
587                 temp->setContent(text);
588                                 return TRUE;
589             }
591         case RDF_RESOURCE:
592             parent->setAttribute("rdf:resource", text );
593             return true;
595         case RDF_XML:
596             return 1;
598         case RDF_BAG:
599             /* find/create the rdf:Bag item */
600             temp = sp_repr_lookup_name ( parent, "rdf:Bag", 1 );
601             if ( temp == NULL ) {
602                 /* backward compatibility: drop the dc:subject contents */
603                 while ( (temp = sp_repr_children( parent )) ) {
604                     parent->removeChild(temp);
605                 }
607                 temp = xmldoc->createElement ( "rdf:Bag" );
608                 g_return_val_if_fail (temp != NULL, FALSE);
610                 parent->appendChild(temp);
611                 Inkscape::GC::release(temp);
612             }
613             parent = temp;
615             /* toss all the old list items */
616             while ( (temp = sp_repr_children( parent )) ) {
617                 parent->removeChild(temp);
618             }
620             /* chop our list up on commas */
621             strlist = g_strsplit( text, ",", 0);
623             for (i = 0; (str = strlist[i]); i++) {
624                 temp = xmldoc->createElement ( "rdf:li" );
625                 g_return_val_if_fail (temp != NULL, 0);
627                 parent->appendChild(temp);
628                 Inkscape::GC::release(temp);
630                 child = xmldoc->createTextNode( g_strstrip(str) );
631                 g_return_val_if_fail (child != NULL, 0);
633                 temp->appendChild(child);
634                 Inkscape::GC::release(child);
635             }
636             g_strfreev( strlist );
638             return 1;
640         default:
641             break;
642     }
643     return 0;
646 Inkscape::XML::Node *
647 rdf_get_rdf_root_repr ( SPDocument * doc, bool build )
649     g_return_val_if_fail (doc        != NULL, NULL);
650     g_return_val_if_fail (doc->rroot != NULL, NULL);
652     Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
653     g_return_val_if_fail (xmldoc != NULL, NULL);
655     Inkscape::XML::Node * rdf = sp_repr_lookup_name ( doc->rroot, XML_TAG_NAME_RDF );
657     if (rdf == NULL) {
658         //printf("missing XML '%s'\n",XML_TAG_NAME_RDF);
659         if (!build) return NULL;
661         Inkscape::XML::Node * svg = sp_repr_lookup_name ( doc->rroot, XML_TAG_NAME_SVG );
662         g_return_val_if_fail ( svg != NULL, NULL );
664         Inkscape::XML::Node * parent = sp_repr_lookup_name ( svg, XML_TAG_NAME_METADATA );
665         if ( parent == NULL ) {
666             parent = xmldoc->createElement( XML_TAG_NAME_METADATA );
667             g_return_val_if_fail ( parent != NULL, NULL);
669             svg->appendChild(parent);
670             Inkscape::GC::release(parent);
671         }
673         Inkscape::XML::Document * xmldoc = parent->document();
674         g_return_val_if_fail (xmldoc != NULL, FALSE);
676         rdf = xmldoc->createElement( XML_TAG_NAME_RDF );
677         g_return_val_if_fail (rdf != NULL, NULL);
679         parent->appendChild(rdf);
680         Inkscape::GC::release(rdf);
681     }
683     /*
684      * some implementations do not put RDF stuff inside <metadata>,
685      * so we need to check for it and add it if we don't see it
686      */
687     Inkscape::XML::Node * want_metadata = sp_repr_parent ( rdf );
688     g_return_val_if_fail (want_metadata != NULL, NULL);
689     if (strcmp( want_metadata->name(), XML_TAG_NAME_METADATA )) {
690             Inkscape::XML::Node * metadata = xmldoc->createElement( XML_TAG_NAME_METADATA );
691             g_return_val_if_fail (metadata != NULL, NULL);
693             /* attach the metadata node */
694             want_metadata->appendChild(metadata);
695             Inkscape::GC::release(metadata);
697             /* move the RDF into it */
698             Inkscape::GC::anchor(rdf);
699             sp_repr_unparent ( rdf );
700             metadata->appendChild(rdf);
701             Inkscape::GC::release(rdf);
702     }
703     
704     return rdf;
707 Inkscape::XML::Node *
708 rdf_get_xml_repr( SPDocument * doc, gchar const * name, bool build )
710     g_return_val_if_fail (name       != NULL, NULL);
711     g_return_val_if_fail (doc        != NULL, NULL);
712     g_return_val_if_fail (doc->rroot != NULL, NULL);
714     Inkscape::XML::Node * rdf = rdf_get_rdf_root_repr ( doc, build );
715     if (!rdf) return NULL;
717     Inkscape::XML::Node * xml = sp_repr_lookup_name ( rdf, name );
718     if (xml == NULL) {
719         //printf("missing XML '%s'\n",name);
720         if (!build) return NULL;
722         Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
723         g_return_val_if_fail (xmldoc != NULL, NULL);
725         xml = xmldoc->createElement( name );
726         g_return_val_if_fail (xml != NULL, NULL);
728         xml->setAttribute("rdf:about", "" );
730         rdf->appendChild(xml);
731         Inkscape::GC::release(xml);
732     }
734     return xml;
737 Inkscape::XML::Node *
738 rdf_get_work_repr( SPDocument * doc, gchar const * name, bool build )
740     g_return_val_if_fail (name       != NULL, NULL);
741     g_return_val_if_fail (doc        != NULL, NULL);
742     g_return_val_if_fail (doc->rroot != NULL, NULL);
744     Inkscape::XML::Node * work = rdf_get_xml_repr ( doc, XML_TAG_NAME_WORK, build );
745     if (!work) return NULL;
747     Inkscape::XML::Node * item = sp_repr_lookup_name ( work, name, 1 );
748     if (item == NULL) {
749         //printf("missing XML '%s'\n",name);
750         if (!build) return NULL;
752         Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
753         g_return_val_if_fail (xmldoc != NULL, NULL);
755         item = xmldoc->createElement( name );
756         g_return_val_if_fail (item != NULL, NULL);
758         work->appendChild(item);
759         Inkscape::GC::release(item);
760     }
762     return item;
767 /**
768  *  \brief   Retrieves a known RDF/Work entity's contents from the document XML by name
769  *  \return  A pointer to the entity's static contents as a string, or NULL if no entity exists
770  *  \param   entity  The desired RDF/Work entity
771  *  
772  */
773 const gchar *
774 rdf_get_work_entity(SPDocument * doc, struct rdf_work_entity_t * entity)
776     g_return_val_if_fail (doc    != NULL, NULL);
777     if ( entity == NULL ) return NULL;
778     //printf("want '%s'\n",entity->title);
779     bool bIsTitle = !strcmp(entity->name, "title");
781     Inkscape::XML::Node * item;
782     if ( entity->datatype == RDF_XML ) {
783         item = rdf_get_xml_repr ( doc, entity->tag, FALSE );
784     }
785     else {
786         item = rdf_get_work_repr( doc, entity->tag, bIsTitle ); // build title if necessary
787     }
788     if ( item == NULL ) return NULL;
789     const gchar * result = rdf_get_repr_text ( item, entity );
790     if(!result && bIsTitle && doc->root) {         // if RDF title not set
791         result = doc->root->title();               // get the document's <title>
792         rdf_set_work_entity(doc, entity, result);  // and set the RDF
793     }
794     //printf("found '%s' == '%s'\n", entity->title, result );
795     return result;
798 /**
799  *  \brief   Stores a string into a named RDF/Work entity in the document XML
800  *  \param   entity The desired RDF/Work entity to replace
801  *  \param   string The string to replace the entity contents with
802  *  
803  */
804 unsigned int
805 rdf_set_work_entity(SPDocument * doc, struct rdf_work_entity_t * entity,
806                     const gchar * text)
808     g_return_val_if_fail ( entity != NULL, 0 );
809     if (text == NULL) {
810         // FIXME: on a "NULL" text, delete the entity.  For now, blank it.
811         text="";
812     }
814     /*
815     printf("changing '%s' (%s) to '%s'\n",
816         entity->title,
817         entity->tag,
818         text);
819     */
821     Inkscape::XML::Node * item = rdf_get_work_repr( doc, entity->tag, TRUE );
822     g_return_val_if_fail ( item != NULL, 0 );
824     return rdf_set_repr_text ( item, entity, text );
827 #undef DEBUG_MATCH
829 static bool
830 rdf_match_license(Inkscape::XML::Node const *repr, struct rdf_license_t const *license)
832     g_assert ( repr != NULL );
833     g_assert ( license != NULL );
835     bool result=TRUE;
836 #ifdef DEBUG_MATCH
837     printf("checking against '%s'\n",license->name);
838 #endif
840     int count = 0;
841     for (struct rdf_double_t const *details = license->details;
842          details->name; details++ ) {
843         count++;
844     }
845     bool * matched = (bool*)calloc(count,sizeof(bool));
847     for (Inkscape::XML::Node const *current = sp_repr_children(repr);
848          current;
849          current = sp_repr_next ( current ) ) {
851         gchar const * attr = current->attribute("rdf:resource");
852         if ( attr == NULL ) continue;
854 #ifdef DEBUG_MATCH
855         printf("\texamining '%s' => '%s'\n", current->name(), attr);
856 #endif
858         bool found_match=FALSE;
859         for (int i=0; i<count; i++) {
860             // skip already matched items
861             if (matched[i]) continue;
863 #ifdef DEBUG_MATCH
864             printf("\t\t'%s' vs '%s'\n", current->name(), license->details[i].name);
865             printf("\t\t'%s' vs '%s'\n", attr, license->details[i].resource);
866 #endif
868             if (!strcmp( current->name(),
869                          license->details[i].name ) &&
870                 !strcmp( attr,
871                          license->details[i].resource )) {
872                 matched[i]=TRUE;
873                 found_match=TRUE;
874 #ifdef DEBUG_MATCH
875                 printf("\t\tgood!\n");
876 #endif
877                 break;
878             }
879         }
880         if (!found_match) {
881             // if we checked each known item of the license
882             // and didn't find it, we must abort
883             result=FALSE;
884 #ifdef DEBUG_MATCH
885             printf("\t\tno '%s' element matched XML (bong)!\n",license->name);
886 #endif
887             break;
888         }
889     }
890 #ifdef DEBUG_MATCH
891     if (result) printf("\t\tall XML found matching elements!\n");
892 #endif
893     for (int i=0; result && i<count; i++) {
894         // scan looking for an unmatched item
895         if (matched[i]==0) {
896             result=FALSE;
897 #ifdef DEBUG_MATCH
898             printf("\t\tnot all '%s' elements used to match (bong)!\n", license->name);
899 #endif
900         }
901     }
903 #ifdef DEBUG_MATCH
904     printf("\t\tall '%s' elements used to match!\n",license->name);
905 #endif
907     free(matched);
909 #ifdef DEBUG_MATCH
910     if (result) printf("matched '%s'\n",license->name);
911 #endif
912     return result;
915 /**
916  *  \brief   Attempts to match and retrieve a known RDF/License from the document XML
917  *  \return  A pointer to the static RDF license structure
918  *  
919  */
920 struct rdf_license_t *
921 rdf_get_license(SPDocument * document)
923     Inkscape::XML::Node const *repr = rdf_get_xml_repr ( document, XML_TAG_NAME_LICENSE, FALSE );
924     if (repr) {
925         for (struct rdf_license_t * license = rdf_licenses;
926              license->name; license++ ) {
927             if ( rdf_match_license ( repr, license ) ) return license;
928         }
929     }
930 #ifdef DEBUG_MATCH
931     else {
932         printf("no license XML\n");
933     }
934 #endif
935     return NULL;
938 /**
939  *  \brief   Stores an RDF/License XML in the document XML
940  *  \param   document  Which document to update
941  *  \param   license   The desired RDF/License structure to store; NULL drops old license, so can be used for proprietary license. 
942  *  
943  */
944 void
945 rdf_set_license(SPDocument * doc, struct rdf_license_t const * license)
947     // drop old license section
948     Inkscape::XML::Node * repr = rdf_get_xml_repr ( doc, XML_TAG_NAME_LICENSE, FALSE );
949     if (repr) sp_repr_unparent(repr);
951     if (!license) return;
953     // build new license section
954     repr = rdf_get_xml_repr ( doc, XML_TAG_NAME_LICENSE, TRUE );
955     g_assert ( repr );
957     repr->setAttribute("rdf:about", license->uri );
959     Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
960     g_return_if_fail (xmldoc != NULL);
962     for (struct rdf_double_t const * detail = license->details;
963          detail->name; detail++) {
964         Inkscape::XML::Node * child = xmldoc->createElement( detail->name );
965         g_assert ( child != NULL );
967         child->setAttribute("rdf:resource", detail->resource );
968         repr->appendChild(child);
969         Inkscape::GC::release(child);
970     }
973 struct rdf_entity_default_t {
974     gchar const * name;
975     gchar const * text;
976 };
977 struct rdf_entity_default_t rdf_defaults[] = {
978     { "format",      "image/svg+xml", },
979     { "type",        "http://purl.org/dc/dcmitype/StillImage", },
980     { NULL,          NULL, }
981 };
983 void
984 rdf_set_defaults ( SPDocument * doc )
986     g_assert ( doc != NULL );
988     // Create metadata node if it doesn't already exist
989     if (!sp_item_group_get_child_by_name ((SPGroup *) doc->root, NULL,
990                                           XML_TAG_NAME_METADATA)) {
991         // create repr
992         Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
993         g_return_if_fail (xmldoc != NULL);
994         Inkscape::XML::Node * rnew = xmldoc->createElement (XML_TAG_NAME_METADATA);
995         // insert into the document
996         doc->rroot->addChild(rnew, NULL);
997         // clean up
998         Inkscape::GC::release(rnew);
999     }
1001     /* install defaults */
1002     for ( struct rdf_entity_default_t * rdf_default = rdf_defaults;
1003           rdf_default->name;
1004           rdf_default++) {
1005         struct rdf_work_entity_t * entity = rdf_find_entity ( rdf_default->name );
1006         g_assert ( entity != NULL );
1008         if ( rdf_get_work_entity ( doc, entity ) == NULL ) {
1009             rdf_set_work_entity ( doc, entity, rdf_default->text );
1010         }
1011     }
1014 /*
1015   Local Variables:
1016   mode:c++
1017   c-file-style:"stroustrup"
1018   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1019   indent-tabs-mode:nil
1020   fill-column:99
1021   End:
1022 */
1023 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :