Code

Merge from trunk
[inkscape.git] / src / dialogs / rdf.cpp
1 /**
2  * \brief  RDF manipulation functions
3  *
4  * FIXME: 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  *
15  */
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
23 #include "xml/repr.h"
24 #include "rdf.h"
25 #include "sp-item-group.h"
26 #include "inkscape.h"
28 /*
30    Example RDF XML from various places...
31  
32 <rdf:RDF xmlns="http://creativecommons.org/ns#"
33     xmlns:dc="http://purl.org/dc/elements/1.1/"
34     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
35 <Work rdf:about="">
36    <dc:title>title of work</dc:title>
37    <dc:date>year</dc:date>
38    <dc:description>description of work</dc:description>
39    <dc:creator><Agent>
40       <dc:title>creator</dc:title>
41    </Agent></dc:creator>
42    <dc:rights><Agent>
43       <dc:title>holder</dc:title>
44    </Agent></dc:rights>
45    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
46    <dc:source rdf:resource="source"/>
47    <license rdf:resource="http://creativecommons.org/licenses/by/2.0/" 
48 />
49 </Work>
52   <rdf:RDF xmlns="http://creativecommons.org/ns#"
53       xmlns:dc="http://purl.org/dc/elements/1.1/"
54       xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
55   <Work rdf:about="">
56      <dc:title>SVG Road Signs</dc:title>
57      <dc:rights><Agent>
58         <dc:title>John Cliff</dc:title>
59      </Agent></dc:rights>
60      <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
61      <license rdf:resource="http://creativecommons.org/ns#PublicDomain" />
62   </Work>
63   
64   <License rdf:about="http://creativecommons.org/ns#PublicDomain">
65      <permits rdf:resource="http://creativecommons.org/ns#Reproduction" />
66      <permits rdf:resource="http://creativecommons.org/ns#Distribution" />
67      <permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
68   </License>
69   
70 </rdf:RDF>
73 Bag example:
75 <dc:subject>
76 <rdf:Bag>
77 <rdf:li>open clip art logo</rdf:li>
78 <rdf:li>images</rdf:li>
79 <rdf:li>logo</rdf:li>
80 <rdf:li>clip art</rdf:li>
81 <rdf:li>ocal</rdf:li>
82 <rdf:li>logotype</rdf:li>
83 <rdf:li>filetype</rdf:li>
84 </rdf:Bag>
85 </dc:subject>
88 */
90 struct rdf_double_t rdf_license_empty [] = {
91     { NULL, NULL }
92 };
94 struct rdf_double_t rdf_license_cc_a [] = {
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     { NULL, NULL }
101 };
103 struct rdf_double_t rdf_license_cc_a_sa [] = {
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     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
109     { "cc:requires", "http://creativecommons.org/ns#ShareAlike", },
110     { NULL, NULL }
111 };
113 struct rdf_double_t rdf_license_cc_a_nd [] = {
114     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
115     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
116     { "cc:requires", "http://creativecommons.org/ns#Notice", },
117     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
118     { NULL, NULL }
119 };
121 struct rdf_double_t rdf_license_cc_a_nc [] = {
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     { NULL, NULL }
129 };
131 struct rdf_double_t rdf_license_cc_a_nc_sa [] = {
132     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
133     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
134     { "cc:requires", "http://creativecommons.org/ns#Notice", },
135     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
136     { "cc:prohibits", "http://creativecommons.org/ns#CommercialUse", },
137     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
138     { "cc:requires", "http://creativecommons.org/ns#ShareAlike", },
139     { NULL, NULL }
140 };
142 struct rdf_double_t rdf_license_cc_a_nc_nd [] = {
143     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
144     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
145     { "cc:requires", "http://creativecommons.org/ns#Notice", },
146     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
147     { "cc:prohibits", "http://creativecommons.org/ns#CommercialUse", },
148     { NULL, NULL }
149 };
151 struct rdf_double_t rdf_license_pd [] = {
152     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
153     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
154     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
155     { NULL, NULL }
156 };
158 struct rdf_double_t rdf_license_freeart [] = {
159     { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
160     { "cc:permits", "http://creativecommons.org/ns#Distribution", },
161     { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
162     { "cc:requires", "http://creativecommons.org/ns#ShareAlike", },
163     { "cc:requires", "http://creativecommons.org/ns#Notice", },
164     { "cc:requires", "http://creativecommons.org/ns#Attribution", },
165     { NULL, NULL }
166 };
168 struct rdf_double_t rdf_license_ofl [] = {
169     { "cc:permits", "http://scripts.sil.org/pub/OFL/Reproduction", },
170     { "cc:permits", "http://scripts.sil.org/pub/OFL/Distribution", },
171     { "cc:permits", "http://scripts.sil.org/pub/OFL/Embedding", },
172     { "cc:permits", "http://scripts.sil.org/pub/OFL/DerivativeWorks", },
173     { "cc:requires", "http://scripts.sil.org/pub/OFL/Notice", },
174     { "cc:requires", "http://scripts.sil.org/pub/OFL/Attribution", },
175     { "cc:requires", "http://scripts.sil.org/pub/OFL/ShareAlike", },
176     { "cc:requires", "http://scripts.sil.org/pub/OFL/DerivativeRenaming", },
177     { "cc:requires", "http://scripts.sil.org/pub/OFL/BundlingWhenSelling", },
178     { NULL, NULL }
179 };
181 struct rdf_license_t rdf_licenses [] = {
182     { N_("CC Attribution"), 
183       "http://creativecommons.org/licenses/by/3.0/",
184       rdf_license_cc_a,
185     },
187     { N_("CC Attribution-ShareAlike"), 
188       "http://creativecommons.org/licenses/by-sa/3.0/",
189       rdf_license_cc_a_sa,
190     },
192     { N_("CC Attribution-NoDerivs"), 
193       "http://creativecommons.org/licenses/by-nd/3.0/",
194       rdf_license_cc_a_nd,
195     },
197     { N_("CC Attribution-NonCommercial"), 
198       "http://creativecommons.org/licenses/by-nc/3.0/",
199       rdf_license_cc_a_nc,
200     },
202     { N_("CC Attribution-NonCommercial-ShareAlike"), 
203       "http://creativecommons.org/licenses/by-nc-sa/3.0/",
204       rdf_license_cc_a_nc_sa,
205     },
207     { N_("CC Attribution-NonCommercial-NoDerivs"), 
208       "http://creativecommons.org/licenses/by-nc-nd/3.0/",
209       rdf_license_cc_a_nc_nd,
210     },
212     { N_("Public Domain"),
213       "http://creativecommons.org/licenses/publicdomain/",
214       rdf_license_pd,
215     },
217     { N_("FreeArt"),
218       "http://artlibre.org/licence.php/lalgb.html",
219       rdf_license_freeart,
220     },
222     { N_("Open Font License"),
223       "http://scripts.sil.org/OFL",
224       rdf_license_ofl,
225     },
227     { NULL, NULL, rdf_license_empty, }
228 };
230 #define XML_TAG_NAME_SVG      "svg:svg"
231 #define XML_TAG_NAME_METADATA "svg:metadata"
232 #define XML_TAG_NAME_RDF      "rdf:RDF"
233 #define XML_TAG_NAME_WORK     "cc:Work"
234 #define XML_TAG_NAME_LICENSE  "cc:License"
236 // Remember when using the "title" and "tip" elements to pass them through
237 // the localization functions when you use them!
238 struct rdf_work_entity_t rdf_work_entities [] = {
239     { "title", N_("Title"), "dc:title", RDF_CONTENT,
240       N_("Name by which this document is formally known."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
241     },
242     { "date", N_("Date"), "dc:date", RDF_CONTENT,
243       N_("Date associated with the creation of this document (YYYY-MM-DD)."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
244     },
245     { "format", N_("Format"), "dc:format", RDF_CONTENT,
246       N_("The physical or digital manifestation of this document (MIME type)."), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
247     },
248     { "type", N_("Type"), "dc:type", RDF_RESOURCE,
249       N_("Type of document (DCMI Type)."), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
250     },
252     { "creator", N_("Creator"), "dc:creator", RDF_AGENT,
253       N_("Name of entity primarily responsible for making the content of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
254     },
255     { "rights", N_("Rights"), "dc:rights", RDF_AGENT,
256       N_("Name of entity with rights to the Intellectual Property of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
257     },
258     { "publisher", N_("Publisher"), "dc:publisher", RDF_AGENT,
259       N_("Name of entity responsible for making this document available."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
260     },
262     { "identifier", N_("Identifier"), "dc:identifier", RDF_CONTENT,
263       N_("Unique URI to reference this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
264     },
265     { "source", N_("Source"), "dc:source", RDF_CONTENT,
266       N_("Unique URI to reference the source of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
267     },
268     { "relation", N_("Relation"), "dc:relation", RDF_CONTENT,
269       N_("Unique URI to a related document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
270     },
271     { "language", N_("Language"), "dc:language", RDF_CONTENT,
272       N_("Two-letter language tag with optional subtags for the language of this document.  (e.g. 'en-GB')"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
273     },
274     { "subject", N_("Keywords"), "dc:subject", RDF_BAG,
275       N_("The topic of this document as comma-separated key words, phrases, or classifications."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
276     },
277     // TRANSLATORS: "Coverage": the spatial or temporal characteristics of the content.
278     // For info, see Appendix D of http://www.w3.org/TR/1998/WD-rdf-schema-19980409/
279     { "coverage", N_("Coverage"), "dc:coverage", RDF_CONTENT,
280       N_("Extent or scope of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
281     },
283     { "description", N_("Description"), "dc:description", RDF_CONTENT,
284       N_("A short account of the content of this document."), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC,
285     },
287     // FIXME: need to handle 1 agent per line of input
288     { "contributor", N_("Contributors"), "dc:contributor", RDF_AGENT,
289       N_("Names of entities responsible for making contributions to the content of this document."), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC,
290     },
292     // TRANSLATORS: URL to a page that defines the license for the document
293     { "license_uri", N_("URI"), "cc:license", RDF_RESOURCE,
294       // TRANSLATORS: this is where you put a URL to a page that defines the license
295       N_("URI to this document's license's namespace definition."), RDF_FORMAT_LINE, RDF_EDIT_SPECIAL,
296     },
298       // TRANSLATORS: fragment of XML representing the license of the document
299     { "license_fragment", N_("Fragment"), "License", RDF_XML,
300       N_("XML fragment for the RDF 'License' section."), RDF_FORMAT_MULTILINE, RDF_EDIT_SPECIAL,
301     },
302     
303     { NULL, NULL, NULL, RDF_CONTENT,
304       NULL, RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
305     }
306 };
308 /**
309  *  \brief   Retrieves a known RDF/Work entity by name
310  *  \return  A pointer to an RDF/Work entity
311  *  \param   name  The desired RDF/Work entity
312  *  
313  */
314 struct rdf_work_entity_t *
315 rdf_find_entity(gchar const * name)
317     struct rdf_work_entity_t *entity;
318     for (entity=rdf_work_entities; entity->name; entity++) {
319         if (strcmp(entity->name,name)==0) break;
320     }
321     if (entity->name) return entity;
322     return NULL;
325 /*
326  * Takes the inkscape rdf struct and spits out a static RDF, which is only
327  * useful for testing.  We must merge the rdf struct into the XML DOM for
328  * changes to be saved.
329  */
330 /*
332    Since g_markup_printf_escaped doesn't exist for most people's glib
333    right now, this function will remain commented out since it's only
334    for generic debug anyway.  --Kees
336 gchar *
337 rdf_string(struct rdf_t * rdf)
339     gulong overall=0;
340     gchar *string=NULL;
342     gchar *rdf_head="\
343 <rdf:RDF xmlns=\"http://creativecommons.org/ns#\"\
344     xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\
345     xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\
346 ";
347     gchar *work_head="\
348 <Work rdf:about=\"\">\
349    <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />\
350 ";
351     gchar *work_title=NULL;
352     gchar *work_date=NULL;
353     gchar *work_description=NULL;
354     gchar *work_creator=NULL;
355     gchar *work_owner=NULL;
356     gchar *work_source=NULL;
357     gchar *work_license=NULL;
358     gchar *license_head=NULL;
359     gchar *license=NULL;
360     gchar *license_end="</License>\n";
361     gchar *work_end="</Work>\n";
362     gchar *rdf_end="</rdf:RDF>\n";
364     if (rdf && rdf->work_title && rdf->work_title[0]) {
365         work_title=g_markup_printf_escaped("   <dc:title>%s</dc:title>\n",
366             rdf->work_title);
367     overall+=strlen(work_title);
368     }
369     if (rdf && rdf->work_date && rdf->work_date[0]) {
370         work_date=g_markup_printf_escaped("   <dc:date>%s</dc:date>\n",
371             rdf->work_date);
372     overall+=strlen(work_date);
373     }
374     if (rdf && rdf->work_description && rdf->work_description[0]) {
375         work_description=g_markup_printf_escaped("   <dc:description>%s</dc:description>\n",
376             rdf->work_description);
377     overall+=strlen(work_description);
378     }
379     if (rdf && rdf->work_creator && rdf->work_creator[0]) {
380         work_creator=g_markup_printf_escaped("   <dc:creator><Agent>\
381       <dc:title>%s</dc:title>\
382    </Agent></dc:creator>\n",
383             rdf->work_creator);
384     overall+=strlen(work_creator);
385     }
386     if (rdf && rdf->work_owner && rdf->work_owner[0]) {
387         work_owner=g_markup_printf_escaped("   <dc:rights><Agent>\
388       <dc:title>%s</dc:title>\
389    </Agent></dc:rights>\n",
390             rdf->work_owner);
391     overall+=strlen(work_owner);
392     }
393     if (rdf && rdf->work_source && rdf->work_source[0]) {
394         work_source=g_markup_printf_escaped("   <dc:source rdf:resource=\"%s\" />\n",
395             rdf->work_source);
396     overall+=strlen(work_source);
397     }
398     if (rdf && rdf->license && rdf->license->work_rdf && rdf->license->work_rdf[0]) {
399         work_license=g_markup_printf_escaped("   <license rdf:resource=\"%s\" />\n",
400             rdf->license->work_rdf);
401     overall+=strlen(work_license);
403     license_head=g_markup_printf_escaped("<License rdf:about=\"%s\">\n",
404             rdf->license->work_rdf);
405     overall+=strlen(license_head);
406     overall+=strlen(rdf->license->license_rdf);
407     overall+=strlen(license_end);
408     }
410     overall+=strlen(rdf_head)+strlen(rdf_end);
411     overall+=strlen(work_head)+strlen(work_end);
413     overall++; // NULL term
415     if (!(string=(gchar*)g_malloc(overall))) {
416         return NULL;
417     }
419     string[0]='\0';
420     strcat(string,rdf_head);
421     strcat(string,work_head);
423     if (work_title)       strcat(string,work_title);
424     if (work_date)        strcat(string,work_date);
425     if (work_description) strcat(string,work_description);
426     if (work_creator)     strcat(string,work_creator);
427     if (work_owner)       strcat(string,work_owner);
428     if (work_source)      strcat(string,work_source);
429     if (work_license)     strcat(string,work_license);
431     strcat(string,work_end);
432     if (license_head) {
433         strcat(string,license_head);
434     strcat(string,rdf->license->license_rdf);
435     strcat(string,license_end);
436     }
437     strcat(string,rdf_end);
439     return string;
441 */
444 /**
445  *  \brief   Pull the text out of an RDF entity, depends on how it's stored
446  *  \return  A pointer to the entity's static contents as a string
447  *  \param   repr    The XML element to extract from
448  *  \param   entity  The desired RDF/Work entity
449  *  
450  */
451 const gchar *
452 rdf_get_repr_text ( Inkscape::XML::Node * repr, struct rdf_work_entity_t * entity )
454     g_return_val_if_fail (repr != NULL, NULL);
455     g_return_val_if_fail (entity != NULL, NULL);
456     static gchar * bag = NULL;
457     gchar * holder = NULL;
459     Inkscape::XML::Node * temp=NULL;
460     switch (entity->datatype) {
461         case RDF_CONTENT:
462             temp = sp_repr_children(repr);
463             if ( temp == NULL ) return NULL;
464             
465             return temp->content();
467         case RDF_AGENT:
468             temp = sp_repr_lookup_name ( repr, "cc:Agent", 1 );
469             if ( temp == NULL ) return NULL;
471             temp = sp_repr_lookup_name ( temp, "dc:title", 1 );
472             if ( temp == NULL ) return NULL;
474             temp = sp_repr_children(temp);
475             if ( temp == NULL ) return NULL;
477             return temp->content();
479         case RDF_RESOURCE:
480             return repr->attribute("rdf:resource");
482         case RDF_XML:
483             return "xml goes here";
485         case RDF_BAG:
486             /* clear the static string.  yucky. */
487             if (bag) g_free(bag);
488             bag = NULL;
490             temp = sp_repr_lookup_name ( repr, "rdf:Bag", 1 );
491             if ( temp == NULL ) {
492                 /* backwards compatible: read contents */
493                 temp = sp_repr_children(repr);
494                 if ( temp == NULL ) return NULL;
495             
496                 return temp->content();
497             }
499             for ( temp = sp_repr_children(temp) ;
500                   temp ;
501                   temp = sp_repr_next(temp) ) {
502                 if (!strcmp(temp->name(),"rdf:li") &&
503                     temp->firstChild()) {
504                     const gchar * str = temp->firstChild()->content();
505                     if (bag) {
506                         holder = bag;
507                         bag = g_strconcat(holder, ", ", str, NULL);
508                         g_free(holder);
509                     }
510                     else {
511                         bag = g_strdup(str);
512                     }
513                 }
514             }
515             return bag;
517         default:
518             break;
519     }
520     return NULL;
523 unsigned int
524 rdf_set_repr_text ( Inkscape::XML::Node * repr,
525                     struct rdf_work_entity_t * entity,
526                     gchar const * text )
528     g_return_val_if_fail ( repr != NULL, 0);
529     g_return_val_if_fail ( entity != NULL, 0);
530     g_return_val_if_fail ( text != NULL, 0);
531     gchar * str = NULL;
532     gchar** strlist = NULL;
533     int i;
535     Inkscape::XML::Node * temp=NULL;
536     Inkscape::XML::Node * child=NULL;
537     Inkscape::XML::Node * parent=repr;
539     Inkscape::XML::Document * xmldoc = parent->document();
540     g_return_val_if_fail (xmldoc != NULL, FALSE);
542     // set document's title element to the RDF title
543     if (!strcmp(entity->name, "title")) {
544         SPDocument *doc = SP_ACTIVE_DOCUMENT;
545         if(doc && doc->root) doc->root->setTitle(text);
546     }
548     switch (entity->datatype) {
549         case RDF_CONTENT:
550             temp = sp_repr_children(parent);
551             if ( temp == NULL ) {
552                 temp = xmldoc->createTextNode( text );
553                 g_return_val_if_fail (temp != NULL, FALSE);
555                 parent->appendChild(temp);
556                 Inkscape::GC::release(temp);
558                 return TRUE;
559             }
560             else {
561                 temp->setContent(text);
562                 return TRUE;
563             }
565         case RDF_AGENT:
566             temp = sp_repr_lookup_name ( parent, "cc:Agent", 1 );
567             if ( temp == NULL ) {
568                 temp = xmldoc->createElement ( "cc:Agent" );
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_lookup_name ( parent, "dc:title", 1 );
577             if ( temp == NULL ) {
578                 temp = xmldoc->createElement ( "dc:title" );
579                 g_return_val_if_fail (temp != NULL, FALSE);
581                 parent->appendChild(temp);
582                 Inkscape::GC::release(temp);
583             }
584             parent = temp;
586             temp = sp_repr_children(parent);
587             if ( temp == NULL ) {
588                 temp = xmldoc->createTextNode( text );
589                 g_return_val_if_fail (temp != NULL, FALSE);
591                 parent->appendChild(temp);
592                 Inkscape::GC::release(temp);
594                 return TRUE;
595             }
596             else {
597                 temp->setContent(text);
598                                 return TRUE;
599             }
601         case RDF_RESOURCE:
602             parent->setAttribute("rdf:resource", text );
603             return true;
605         case RDF_XML:
606             return 1;
608         case RDF_BAG:
609             /* find/create the rdf:Bag item */
610             temp = sp_repr_lookup_name ( parent, "rdf:Bag", 1 );
611             if ( temp == NULL ) {
612                 /* backward compatibility: drop the dc:subject contents */
613                 while ( (temp = sp_repr_children( parent )) ) {
614                     parent->removeChild(temp);
615                 }
617                 temp = xmldoc->createElement ( "rdf:Bag" );
618                 g_return_val_if_fail (temp != NULL, FALSE);
620                 parent->appendChild(temp);
621                 Inkscape::GC::release(temp);
622             }
623             parent = temp;
625             /* toss all the old list items */
626             while ( (temp = sp_repr_children( parent )) ) {
627                 parent->removeChild(temp);
628             }
630             /* chop our list up on commas */
631             strlist = g_strsplit( text, ",", 0);
633             for (i = 0; (str = strlist[i]); i++) {
634                 temp = xmldoc->createElement ( "rdf:li" );
635                 g_return_val_if_fail (temp != NULL, 0);
637                 parent->appendChild(temp);
638                 Inkscape::GC::release(temp);
640                 child = xmldoc->createTextNode( g_strstrip(str) );
641                 g_return_val_if_fail (child != NULL, 0);
643                 temp->appendChild(child);
644                 Inkscape::GC::release(child);
645             }
646             g_strfreev( strlist );
648             return 1;
650         default:
651             break;
652     }
653     return 0;
656 Inkscape::XML::Node *
657 rdf_get_rdf_root_repr ( SPDocument * doc, bool build )
659     g_return_val_if_fail (doc        != NULL, NULL);
660     g_return_val_if_fail (doc->rroot != NULL, NULL);
662     Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
663     g_return_val_if_fail (xmldoc != NULL, NULL);
665     Inkscape::XML::Node * rdf = sp_repr_lookup_name ( doc->rroot, XML_TAG_NAME_RDF );
667     if (rdf == NULL) {
668         //printf("missing XML '%s'\n",XML_TAG_NAME_RDF);
669         if (!build) return NULL;
671         Inkscape::XML::Node * svg = sp_repr_lookup_name ( doc->rroot, XML_TAG_NAME_SVG );
672         g_return_val_if_fail ( svg != NULL, NULL );
674         Inkscape::XML::Node * parent = sp_repr_lookup_name ( svg, XML_TAG_NAME_METADATA );
675         if ( parent == NULL ) {
676             parent = xmldoc->createElement( XML_TAG_NAME_METADATA );
677             g_return_val_if_fail ( parent != NULL, NULL);
679             svg->appendChild(parent);
680             Inkscape::GC::release(parent);
681         }
683         Inkscape::XML::Document * xmldoc = parent->document();
684         g_return_val_if_fail (xmldoc != NULL, FALSE);
686         rdf = xmldoc->createElement( XML_TAG_NAME_RDF );
687         g_return_val_if_fail (rdf != NULL, NULL);
689         parent->appendChild(rdf);
690         Inkscape::GC::release(rdf);
691     }
693     /*
694      * some implementations do not put RDF stuff inside <metadata>,
695      * so we need to check for it and add it if we don't see it
696      */
697     Inkscape::XML::Node * want_metadata = sp_repr_parent ( rdf );
698     g_return_val_if_fail (want_metadata != NULL, NULL);
699     if (strcmp( want_metadata->name(), XML_TAG_NAME_METADATA )) {
700             Inkscape::XML::Node * metadata = xmldoc->createElement( XML_TAG_NAME_METADATA );
701             g_return_val_if_fail (metadata != NULL, NULL);
703             /* attach the metadata node */
704             want_metadata->appendChild(metadata);
705             Inkscape::GC::release(metadata);
707             /* move the RDF into it */
708             Inkscape::GC::anchor(rdf);
709             sp_repr_unparent ( rdf );
710             metadata->appendChild(rdf);
711             Inkscape::GC::release(rdf);
712     }
713     
714     return rdf;
717 Inkscape::XML::Node *
718 rdf_get_xml_repr( SPDocument * doc, gchar const * name, bool build )
720     g_return_val_if_fail (name       != NULL, NULL);
721     g_return_val_if_fail (doc        != NULL, NULL);
722     g_return_val_if_fail (doc->rroot != NULL, NULL);
724     Inkscape::XML::Node * rdf = rdf_get_rdf_root_repr ( doc, build );
725     if (!rdf) return NULL;
727     Inkscape::XML::Node * xml = sp_repr_lookup_name ( rdf, name );
728     if (xml == NULL) {
729         //printf("missing XML '%s'\n",name);
730         if (!build) return NULL;
732         Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
733         g_return_val_if_fail (xmldoc != NULL, NULL);
735         xml = xmldoc->createElement( name );
736         g_return_val_if_fail (xml != NULL, NULL);
738         xml->setAttribute("rdf:about", "" );
740         rdf->appendChild(xml);
741         Inkscape::GC::release(xml);
742     }
744     return xml;
747 Inkscape::XML::Node *
748 rdf_get_work_repr( SPDocument * doc, gchar const * name, bool build )
750     g_return_val_if_fail (name       != NULL, NULL);
751     g_return_val_if_fail (doc        != NULL, NULL);
752     g_return_val_if_fail (doc->rroot != NULL, NULL);
754     Inkscape::XML::Node * work = rdf_get_xml_repr ( doc, XML_TAG_NAME_WORK, build );
755     if (!work) return NULL;
757     Inkscape::XML::Node * item = sp_repr_lookup_name ( work, name, 1 );
758     if (item == NULL) {
759         //printf("missing XML '%s'\n",name);
760         if (!build) return NULL;
762         Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
763         g_return_val_if_fail (xmldoc != NULL, NULL);
765         item = xmldoc->createElement( name );
766         g_return_val_if_fail (item != NULL, NULL);
768         work->appendChild(item);
769         Inkscape::GC::release(item);
770     }
772     return item;
777 /**
778  *  \brief   Retrieves a known RDF/Work entity's contents from the document XML by name
779  *  \return  A pointer to the entity's static contents as a string, or NULL if no entity exists
780  *  \param   entity  The desired RDF/Work entity
781  *  
782  */
783 const gchar *
784 rdf_get_work_entity(SPDocument * doc, struct rdf_work_entity_t * entity)
786     g_return_val_if_fail (doc    != NULL, NULL);
787     if ( entity == NULL ) return NULL;
788     //printf("want '%s'\n",entity->title);
789     bool bIsTitle = !strcmp(entity->name, "title");
791     Inkscape::XML::Node * item;
792     if ( entity->datatype == RDF_XML ) {
793         item = rdf_get_xml_repr ( doc, entity->tag, FALSE );
794     }
795     else {
796         item = rdf_get_work_repr( doc, entity->tag, bIsTitle ); // build title if necessary
797     }
798     if ( item == NULL ) return NULL;
799     const gchar * result = rdf_get_repr_text ( item, entity );
800     if(!result && bIsTitle && doc->root) {         // if RDF title not set
801         result = doc->root->title();               // get the document's <title>
802         rdf_set_work_entity(doc, entity, result);  // and set the RDF
803     }
804     //printf("found '%s' == '%s'\n", entity->title, result );
805     return result;
808 /**
809  *  \brief   Stores a string into a named RDF/Work entity in the document XML
810  *  \param   entity The desired RDF/Work entity to replace
811  *  \param   string The string to replace the entity contents with
812  *  
813  */
814 unsigned int
815 rdf_set_work_entity(SPDocument * doc, struct rdf_work_entity_t * entity,
816                     const gchar * text)
818     g_return_val_if_fail ( entity != NULL, 0 );
819     if (text == NULL) {
820         // FIXME: on a "NULL" text, delete the entity.  For now, blank it.
821         text="";
822     }
824     /*
825     printf("changing '%s' (%s) to '%s'\n",
826         entity->title,
827         entity->tag,
828         text);
829     */
831     Inkscape::XML::Node * item = rdf_get_work_repr( doc, entity->tag, TRUE );
832     g_return_val_if_fail ( item != NULL, 0 );
834     return rdf_set_repr_text ( item, entity, text );
837 #undef DEBUG_MATCH
839 static bool
840 rdf_match_license(Inkscape::XML::Node const *repr, struct rdf_license_t const *license)
842     g_assert ( repr != NULL );
843     g_assert ( license != NULL );
845     bool result=TRUE;
846 #ifdef DEBUG_MATCH
847     printf("checking against '%s'\n",license->name);
848 #endif
850     int count = 0;
851     for (struct rdf_double_t const *details = license->details;
852          details->name; details++ ) {
853         count++;
854     }
855     bool * matched = (bool*)calloc(count,sizeof(bool));
857     for (Inkscape::XML::Node const *current = sp_repr_children(repr);
858          current;
859          current = sp_repr_next ( current ) ) {
861         gchar const * attr = current->attribute("rdf:resource");
862         if ( attr == NULL ) continue;
864 #ifdef DEBUG_MATCH
865         printf("\texamining '%s' => '%s'\n", current->name(), attr);
866 #endif
868         bool found_match=FALSE;
869         for (int i=0; i<count; i++) {
870             // skip already matched items
871             if (matched[i]) continue;
873 #ifdef DEBUG_MATCH
874             printf("\t\t'%s' vs '%s'\n", current->name(), license->details[i].name);
875             printf("\t\t'%s' vs '%s'\n", attr, license->details[i].resource);
876 #endif
878             if (!strcmp( current->name(),
879                          license->details[i].name ) &&
880                 !strcmp( attr,
881                          license->details[i].resource )) {
882                 matched[i]=TRUE;
883                 found_match=TRUE;
884 #ifdef DEBUG_MATCH
885                 printf("\t\tgood!\n");
886 #endif
887                 break;
888             }
889         }
890         if (!found_match) {
891             // if we checked each known item of the license
892             // and didn't find it, we must abort
893             result=FALSE;
894 #ifdef DEBUG_MATCH
895             printf("\t\tno '%s' element matched XML (bong)!\n",license->name);
896 #endif
897             break;
898         }
899     }
900 #ifdef DEBUG_MATCH
901     if (result) printf("\t\tall XML found matching elements!\n");
902 #endif
903     for (int i=0; result && i<count; i++) {
904         // scan looking for an unmatched item
905         if (matched[i]==0) {
906             result=FALSE;
907 #ifdef DEBUG_MATCH
908             printf("\t\tnot all '%s' elements used to match (bong)!\n", license->name);
909 #endif
910         }
911     }
913 #ifdef DEBUG_MATCH
914     printf("\t\tall '%s' elements used to match!\n",license->name);
915 #endif
917     free(matched);
919 #ifdef DEBUG_MATCH
920     if (result) printf("matched '%s'\n",license->name);
921 #endif
922     return result;
925 /**
926  *  \brief   Attempts to match and retrieve a known RDF/License from the document XML
927  *  \return  A pointer to the static RDF license structure
928  *  
929  */
930 struct rdf_license_t *
931 rdf_get_license(SPDocument * document)
933     Inkscape::XML::Node const *repr = rdf_get_xml_repr ( document, XML_TAG_NAME_LICENSE, FALSE );
934     if (repr) {
935         for (struct rdf_license_t * license = rdf_licenses;
936              license->name; license++ ) {
937             if ( rdf_match_license ( repr, license ) ) return license;
938         }
939     }
940 #ifdef DEBUG_MATCH
941     else {
942         printf("no license XML\n");
943     }
944 #endif
945     return NULL;
948 /**
949  *  \brief   Stores an RDF/License XML in the document XML
950  *  \param   document  Which document to update
951  *  \param   license   The desired RDF/License structure to store; NULL drops old license, so can be used for proprietary license. 
952  *  
953  */
954 void
955 rdf_set_license(SPDocument * doc, struct rdf_license_t const * license)
957     // drop old license section
958     Inkscape::XML::Node * repr = rdf_get_xml_repr ( doc, XML_TAG_NAME_LICENSE, FALSE );
959     if (repr) sp_repr_unparent(repr);
961     if (!license) return;
963     // build new license section
964     repr = rdf_get_xml_repr ( doc, XML_TAG_NAME_LICENSE, TRUE );
965     g_assert ( repr );
967     repr->setAttribute("rdf:about", license->uri );
969     Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
970     g_return_if_fail (xmldoc != NULL);
972     for (struct rdf_double_t const * detail = license->details;
973          detail->name; detail++) {
974         Inkscape::XML::Node * child = xmldoc->createElement( detail->name );
975         g_assert ( child != NULL );
977         child->setAttribute("rdf:resource", detail->resource );
978         repr->appendChild(child);
979         Inkscape::GC::release(child);
980     }
983 struct rdf_entity_default_t {
984     gchar const * name;
985     gchar const * text;
986 };
987 struct rdf_entity_default_t rdf_defaults[] = {
988     { "format",      "image/svg+xml", },
989     { "type",        "http://purl.org/dc/dcmitype/StillImage", },
990     { NULL,          NULL, }
991 };
993 void
994 rdf_set_defaults ( SPDocument * doc )
996     g_assert ( doc != NULL );
998     // Create metadata node if it doesn't already exist
999     if (!sp_item_group_get_child_by_name ((SPGroup *) doc->root, NULL,
1000                                           XML_TAG_NAME_METADATA)) {
1001         // create repr
1002         Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
1003         g_return_if_fail (xmldoc != NULL);
1004         Inkscape::XML::Node * rnew = xmldoc->createElement (XML_TAG_NAME_METADATA);
1005         // insert into the document
1006         doc->rroot->addChild(rnew, NULL);
1007         // clean up
1008         Inkscape::GC::release(rnew);
1009     }
1011     /* install defaults */
1012     for ( struct rdf_entity_default_t * rdf_default = rdf_defaults;
1013           rdf_default->name;
1014           rdf_default++) {
1015         struct rdf_work_entity_t * entity = rdf_find_entity ( rdf_default->name );
1016         g_assert ( entity != NULL );
1018         if ( rdf_get_work_entity ( doc, entity ) == NULL ) {
1019             rdf_set_work_entity ( doc, entity, rdf_default->text );
1020         }
1021     }
1025 /*
1026   Local Variables:
1027   mode:c++
1028   c-file-style:"stroustrup"
1029   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1030   indent-tabs-mode:nil
1031   fill-column:99
1032   End:
1033 */
1034 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :