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...
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>
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>
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.php/lalgb.html",
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 },
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)
306 {
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;
313 }
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)
328 {
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;
430 }
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 )
443 {
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;
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;
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;
511 }
513 unsigned int
514 rdf_set_repr_text ( Inkscape::XML::Node * repr,
515 struct rdf_work_entity_t * entity,
516 gchar const * text )
517 {
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;
644 }
646 Inkscape::XML::Node *
647 rdf_get_rdf_root_repr ( SPDocument * doc, bool build )
648 {
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 }
704 return rdf;
705 }
707 Inkscape::XML::Node *
708 rdf_get_xml_repr( SPDocument * doc, gchar const * name, bool build )
709 {
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;
735 }
737 Inkscape::XML::Node *
738 rdf_get_work_repr( SPDocument * doc, gchar const * name, bool build )
739 {
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;
763 }
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)
775 {
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;
796 }
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)
807 {
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 );
825 }
827 #undef DEBUG_MATCH
829 static bool
830 rdf_match_license(Inkscape::XML::Node const *repr, struct rdf_license_t const *license)
831 {
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;
913 }
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)
922 {
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;
936 }
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)
946 {
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 }
971 }
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 )
985 {
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 }
1012 }
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 :