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"
27 /*
29 Example RDF XML from various places...
31 <rdf:RDF xmlns="http://web.resource.org/cc/"
32 xmlns:dc="http://purl.org/dc/elements/1.1/"
33 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
34 <Work rdf:about="">
35 <dc:title>title of work</dc:title>
36 <dc:date>year</dc:date>
37 <dc:description>description of work</dc:description>
38 <dc:creator><Agent>
39 <dc:title>creator</dc:title>
40 </Agent></dc:creator>
41 <dc:rights><Agent>
42 <dc:title>holder</dc:title>
43 </Agent></dc:rights>
44 <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
45 <dc:source rdf:resource="source"/>
46 <license rdf:resource="http://creativecommons.org/licenses/by/2.0/"
47 />
48 </Work>
51 <rdf:RDF xmlns="http://web.resource.org/cc/"
52 xmlns:dc="http://purl.org/dc/elements/1.1/"
53 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
54 <Work rdf:about="">
55 <dc:title>SVG Road Signs</dc:title>
56 <dc:rights><Agent>
57 <dc:title>John Cliff</dc:title>
58 </Agent></dc:rights>
59 <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
60 <license rdf:resource="http://web.resource.org/cc/PublicDomain" />
61 </Work>
63 <License rdf:about="http://web.resource.org/cc/PublicDomain">
64 <permits rdf:resource="http://web.resource.org/cc/Reproduction" />
65 <permits rdf:resource="http://web.resource.org/cc/Distribution" />
66 <permits rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
67 </License>
69 </rdf:RDF>
72 Bag example:
74 <dc:subject>
75 <rdf:Bag>
76 <rdf:li>open clip art logo</rdf:li>
77 <rdf:li>images</rdf:li>
78 <rdf:li>logo</rdf:li>
79 <rdf:li>clip art</rdf:li>
80 <rdf:li>ocal</rdf:li>
81 <rdf:li>logotype</rdf:li>
82 <rdf:li>filetype</rdf:li>
83 </rdf:Bag>
84 </dc:subject>
87 */
89 struct rdf_double_t rdf_license_empty [] = {
90 { NULL, NULL }
91 };
93 struct rdf_double_t rdf_license_cc_a [] = {
94 { "cc:permits", "http://web.resource.org/cc/Reproduction", },
95 { "cc:permits", "http://web.resource.org/cc/Distribution", },
96 { "cc:requires", "http://web.resource.org/cc/Notice", },
97 { "cc:requires", "http://web.resource.org/cc/Attribution", },
98 { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", },
99 { NULL, NULL }
100 };
102 struct rdf_double_t rdf_license_cc_a_sa [] = {
103 { "cc:permits", "http://web.resource.org/cc/Reproduction", },
104 { "cc:permits", "http://web.resource.org/cc/Distribution", },
105 { "cc:requires", "http://web.resource.org/cc/Notice", },
106 { "cc:requires", "http://web.resource.org/cc/Attribution", },
107 { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", },
108 { "cc:requires", "http://web.resource.org/cc/ShareAlike", },
109 { NULL, NULL }
110 };
112 struct rdf_double_t rdf_license_cc_a_nd [] = {
113 { "cc:permits", "http://web.resource.org/cc/Reproduction", },
114 { "cc:permits", "http://web.resource.org/cc/Distribution", },
115 { "cc:requires", "http://web.resource.org/cc/Notice", },
116 { "cc:requires", "http://web.resource.org/cc/Attribution", },
117 { NULL, NULL }
118 };
120 struct rdf_double_t rdf_license_cc_a_nc [] = {
121 { "cc:permits", "http://web.resource.org/cc/Reproduction", },
122 { "cc:permits", "http://web.resource.org/cc/Distribution", },
123 { "cc:requires", "http://web.resource.org/cc/Notice", },
124 { "cc:requires", "http://web.resource.org/cc/Attribution", },
125 { "cc:prohibits", "http://web.resource.org/cc/CommercialUse", },
126 { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", },
127 { NULL, NULL }
128 };
130 struct rdf_double_t rdf_license_cc_a_nc_sa [] = {
131 { "cc:permits", "http://web.resource.org/cc/Reproduction", },
132 { "cc:permits", "http://web.resource.org/cc/Distribution", },
133 { "cc:requires", "http://web.resource.org/cc/Notice", },
134 { "cc:requires", "http://web.resource.org/cc/Attribution", },
135 { "cc:prohibits", "http://web.resource.org/cc/CommercialUse", },
136 { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", },
137 { "cc:requires", "http://web.resource.org/cc/ShareAlike", },
138 { NULL, NULL }
139 };
141 struct rdf_double_t rdf_license_cc_a_nc_nd [] = {
142 { "cc:permits", "http://web.resource.org/cc/Reproduction", },
143 { "cc:permits", "http://web.resource.org/cc/Distribution", },
144 { "cc:requires", "http://web.resource.org/cc/Notice", },
145 { "cc:requires", "http://web.resource.org/cc/Attribution", },
146 { "cc:prohibits", "http://web.resource.org/cc/CommercialUse", },
147 { NULL, NULL }
148 };
150 struct rdf_double_t rdf_license_pd [] = {
151 { "cc:permits", "http://web.resource.org/cc/Reproduction", },
152 { "cc:permits", "http://web.resource.org/cc/Distribution", },
153 { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", },
154 { NULL, NULL }
155 };
157 struct rdf_double_t rdf_license_freeart [] = {
158 { "cc:permits", "http://web.resource.org/cc/Reproduction", },
159 { "cc:permits", "http://web.resource.org/cc/Distribution", },
160 { "cc:permits", "http://web.resource.org/cc/DerivativeWorks", },
161 { "cc:requires", "http://web.resource.org/cc/ShareAlike", },
162 { "cc:requires", "http://web.resource.org/cc/Notice", },
163 { "cc:requires", "http://web.resource.org/cc/Attribution", },
164 { NULL, NULL }
165 };
167 struct rdf_double_t rdf_license_ofl [] = {
168 { "cc:permits", "http://scripts.sil.org/pub/OFL/Reproduction", },
169 { "cc:permits", "http://scripts.sil.org/pub/OFL/Distribution", },
170 { "cc:permits", "http://scripts.sil.org/pub/OFL/Embedding", },
171 { "cc:permits", "http://scripts.sil.org/pub/OFL/DerivativeWorks", },
172 { "cc:requires", "http://scripts.sil.org/pub/OFL/Notice", },
173 { "cc:requires", "http://scripts.sil.org/pub/OFL/Attribution", },
174 { "cc:requires", "http://scripts.sil.org/pub/OFL/ShareAlike", },
175 { "cc:requires", "http://scripts.sil.org/pub/OFL/DerivativeRenaming", },
176 { "cc:requires", "http://scripts.sil.org/pub/OFL/BundlingWhenSelling", },
177 { NULL, NULL }
178 };
180 struct rdf_license_t rdf_licenses [] = {
181 { N_("CC Attribution"),
182 "http://creativecommons.org/licenses/by/3.0/",
183 rdf_license_cc_a,
184 },
186 { N_("CC Attribution-ShareAlike"),
187 "http://creativecommons.org/licenses/by-sa/3.0/",
188 rdf_license_cc_a_sa,
189 },
191 { N_("CC Attribution-NoDerivs"),
192 "http://creativecommons.org/licenses/by-nd/3.0/",
193 rdf_license_cc_a_nd,
194 },
196 { N_("CC Attribution-NonCommercial"),
197 "http://creativecommons.org/licenses/by-nc/3.0/",
198 rdf_license_cc_a_nc,
199 },
201 { N_("CC Attribution-NonCommercial-ShareAlike"),
202 "http://creativecommons.org/licenses/by-nc-sa/3.0/",
203 rdf_license_cc_a_nc_sa,
204 },
206 { N_("CC Attribution-NonCommercial-NoDerivs"),
207 "http://creativecommons.org/licenses/by-nc-nd/3.0/",
208 rdf_license_cc_a_nc_nd,
209 },
211 { N_("Public Domain"),
212 "http://creativecommons.org/licenses/publicdomain/",
213 rdf_license_pd,
214 },
216 { N_("FreeArt"),
217 "http://artlibre.org/licence.php/lalgb.html",
218 rdf_license_freeart,
219 },
221 { N_("Open Font License"),
222 "http://scripts.sil.org/OFL",
223 rdf_license_ofl,
224 },
226 { NULL, NULL, rdf_license_empty, }
227 };
229 #define XML_TAG_NAME_SVG "svg:svg"
230 #define XML_TAG_NAME_METADATA "svg:metadata"
231 #define XML_TAG_NAME_RDF "rdf:RDF"
232 #define XML_TAG_NAME_WORK "cc:Work"
233 #define XML_TAG_NAME_LICENSE "cc:License"
235 // Remember when using the "title" and "tip" elements to pass them through
236 // the localization functions when you use them!
237 struct rdf_work_entity_t rdf_work_entities [] = {
238 { "title", N_("Title"), "dc:title", RDF_CONTENT,
239 N_("Name by which this document is formally known."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
240 },
241 { "date", N_("Date"), "dc:date", RDF_CONTENT,
242 N_("Date associated with the creation of this document (YYYY-MM-DD)."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
243 },
244 { "format", N_("Format"), "dc:format", RDF_CONTENT,
245 N_("The physical or digital manifestation of this document (MIME type)."), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
246 },
247 { "type", N_("Type"), "dc:type", RDF_RESOURCE,
248 N_("Type of document (DCMI Type)."), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
249 },
251 { "creator", N_("Creator"), "dc:creator", RDF_AGENT,
252 N_("Name of entity primarily responsible for making the content of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
253 },
254 { "rights", N_("Rights"), "dc:rights", RDF_AGENT,
255 N_("Name of entity with rights to the Intellectual Property of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
256 },
257 { "publisher", N_("Publisher"), "dc:publisher", RDF_AGENT,
258 N_("Name of entity responsible for making this document available."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
259 },
261 { "identifier", N_("Identifier"), "dc:identifier", RDF_CONTENT,
262 N_("Unique URI to reference this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
263 },
264 { "source", N_("Source"), "dc:source", RDF_CONTENT,
265 N_("Unique URI to reference the source of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
266 },
267 { "relation", N_("Relation"), "dc:relation", RDF_CONTENT,
268 N_("Unique URI to a related document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
269 },
270 { "language", N_("Language"), "dc:language", RDF_CONTENT,
271 N_("Two-letter language tag with optional subtags for the language of this document. (e.g. 'en-GB')"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
272 },
273 { "subject", N_("Keywords"), "dc:subject", RDF_BAG,
274 N_("The topic of this document as comma-separated key words, phrases, or classifications."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
275 },
276 // TRANSLATORS: "Coverage": the spatial or temporal characteristics of the content.
277 // For info, see Appendix D of http://www.w3.org/TR/1998/WD-rdf-schema-19980409/
278 { "coverage", N_("Coverage"), "dc:coverage", RDF_CONTENT,
279 N_("Extent or scope of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
280 },
282 { "description", N_("Description"), "dc:description", RDF_CONTENT,
283 N_("A short account of the content of this document."), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC,
284 },
286 // FIXME: need to handle 1 agent per line of input
287 { "contributor", N_("Contributors"), "dc:contributor", RDF_AGENT,
288 N_("Names of entities responsible for making contributions to the content of this document."), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC,
289 },
291 // TRANSLATORS: URL to a page that defines the license for the document
292 { "license_uri", N_("URI"), "cc:license", RDF_RESOURCE,
293 // TRANSLATORS: this is where you put a URL to a page that defines the license
294 N_("URI to this document's license's namespace definition."), RDF_FORMAT_LINE, RDF_EDIT_SPECIAL,
295 },
297 // TRANSLATORS: fragment of XML representing the license of the document
298 { "license_fragment", N_("Fragment"), "License", RDF_XML,
299 N_("XML fragment for the RDF 'License' section."), RDF_FORMAT_MULTILINE, RDF_EDIT_SPECIAL,
300 },
302 { NULL, NULL, NULL, RDF_CONTENT,
303 NULL, RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
304 }
305 };
307 /**
308 * \brief Retrieves a known RDF/Work entity by name
309 * \return A pointer to an RDF/Work entity
310 * \param name The desired RDF/Work entity
311 *
312 */
313 struct rdf_work_entity_t *
314 rdf_find_entity(gchar const * name)
315 {
316 struct rdf_work_entity_t *entity;
317 for (entity=rdf_work_entities; entity->name; entity++) {
318 if (strcmp(entity->name,name)==0) break;
319 }
320 if (entity->name) return entity;
321 return NULL;
322 }
324 /*
325 * Takes the inkscape rdf struct and spits out a static RDF, which is only
326 * useful for testing. We must merge the rdf struct into the XML DOM for
327 * changes to be saved.
328 */
329 /*
331 Since g_markup_printf_escaped doesn't exist for most people's glib
332 right now, this function will remain commented out since it's only
333 for generic debug anyway. --Kees
335 gchar *
336 rdf_string(struct rdf_t * rdf)
337 {
338 gulong overall=0;
339 gchar *string=NULL;
341 gchar *rdf_head="\
342 <rdf:RDF xmlns=\"http://web.resource.org/cc/\"\
343 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\
344 xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\
345 ";
346 gchar *work_head="\
347 <Work rdf:about=\"\">\
348 <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />\
349 ";
350 gchar *work_title=NULL;
351 gchar *work_date=NULL;
352 gchar *work_description=NULL;
353 gchar *work_creator=NULL;
354 gchar *work_owner=NULL;
355 gchar *work_source=NULL;
356 gchar *work_license=NULL;
357 gchar *license_head=NULL;
358 gchar *license=NULL;
359 gchar *license_end="</License>\n";
360 gchar *work_end="</Work>\n";
361 gchar *rdf_end="</rdf:RDF>\n";
363 if (rdf && rdf->work_title && rdf->work_title[0]) {
364 work_title=g_markup_printf_escaped(" <dc:title>%s</dc:title>\n",
365 rdf->work_title);
366 overall+=strlen(work_title);
367 }
368 if (rdf && rdf->work_date && rdf->work_date[0]) {
369 work_date=g_markup_printf_escaped(" <dc:date>%s</dc:date>\n",
370 rdf->work_date);
371 overall+=strlen(work_date);
372 }
373 if (rdf && rdf->work_description && rdf->work_description[0]) {
374 work_description=g_markup_printf_escaped(" <dc:description>%s</dc:description>\n",
375 rdf->work_description);
376 overall+=strlen(work_description);
377 }
378 if (rdf && rdf->work_creator && rdf->work_creator[0]) {
379 work_creator=g_markup_printf_escaped(" <dc:creator><Agent>\
380 <dc:title>%s</dc:title>\
381 </Agent></dc:creator>\n",
382 rdf->work_creator);
383 overall+=strlen(work_creator);
384 }
385 if (rdf && rdf->work_owner && rdf->work_owner[0]) {
386 work_owner=g_markup_printf_escaped(" <dc:rights><Agent>\
387 <dc:title>%s</dc:title>\
388 </Agent></dc:rights>\n",
389 rdf->work_owner);
390 overall+=strlen(work_owner);
391 }
392 if (rdf && rdf->work_source && rdf->work_source[0]) {
393 work_source=g_markup_printf_escaped(" <dc:source rdf:resource=\"%s\" />\n",
394 rdf->work_source);
395 overall+=strlen(work_source);
396 }
397 if (rdf && rdf->license && rdf->license->work_rdf && rdf->license->work_rdf[0]) {
398 work_license=g_markup_printf_escaped(" <license rdf:resource=\"%s\" />\n",
399 rdf->license->work_rdf);
400 overall+=strlen(work_license);
402 license_head=g_markup_printf_escaped("<License rdf:about=\"%s\">\n",
403 rdf->license->work_rdf);
404 overall+=strlen(license_head);
405 overall+=strlen(rdf->license->license_rdf);
406 overall+=strlen(license_end);
407 }
409 overall+=strlen(rdf_head)+strlen(rdf_end);
410 overall+=strlen(work_head)+strlen(work_end);
412 overall++; // NULL term
414 if (!(string=(gchar*)g_malloc(overall))) {
415 return NULL;
416 }
418 string[0]='\0';
419 strcat(string,rdf_head);
420 strcat(string,work_head);
422 if (work_title) strcat(string,work_title);
423 if (work_date) strcat(string,work_date);
424 if (work_description) strcat(string,work_description);
425 if (work_creator) strcat(string,work_creator);
426 if (work_owner) strcat(string,work_owner);
427 if (work_source) strcat(string,work_source);
428 if (work_license) strcat(string,work_license);
430 strcat(string,work_end);
431 if (license_head) {
432 strcat(string,license_head);
433 strcat(string,rdf->license->license_rdf);
434 strcat(string,license_end);
435 }
436 strcat(string,rdf_end);
438 return string;
439 }
440 */
443 /**
444 * \brief Pull the text out of an RDF entity, depends on how it's stored
445 * \return A pointer to the entity's static contents as a string
446 * \param repr The XML element to extract from
447 * \param entity The desired RDF/Work entity
448 *
449 */
450 const gchar *
451 rdf_get_repr_text ( Inkscape::XML::Node * repr, struct rdf_work_entity_t * entity )
452 {
453 g_return_val_if_fail (repr != NULL, NULL);
454 g_return_val_if_fail (entity != NULL, NULL);
455 static gchar * bag = NULL;
456 gchar * holder = NULL;
458 Inkscape::XML::Node * temp=NULL;
459 switch (entity->datatype) {
460 case RDF_CONTENT:
461 temp = sp_repr_children(repr);
462 if ( temp == NULL ) return NULL;
464 return temp->content();
466 case RDF_AGENT:
467 temp = sp_repr_lookup_name ( repr, "cc:Agent", 1 );
468 if ( temp == NULL ) return NULL;
470 temp = sp_repr_lookup_name ( temp, "dc:title", 1 );
471 if ( temp == NULL ) return NULL;
473 temp = sp_repr_children(temp);
474 if ( temp == NULL ) return NULL;
476 return temp->content();
478 case RDF_RESOURCE:
479 return repr->attribute("rdf:resource");
481 case RDF_XML:
482 return "xml goes here";
484 case RDF_BAG:
485 /* clear the static string. yucky. */
486 if (bag) g_free(bag);
487 bag = NULL;
489 temp = sp_repr_lookup_name ( repr, "rdf:Bag", 1 );
490 if ( temp == NULL ) {
491 /* backwards compatible: read contents */
492 temp = sp_repr_children(repr);
493 if ( temp == NULL ) return NULL;
495 return temp->content();
496 }
498 for ( temp = sp_repr_children(temp) ;
499 temp ;
500 temp = sp_repr_next(temp) ) {
501 if (!strcmp(temp->name(),"rdf:li") &&
502 temp->firstChild()) {
503 const gchar * str = temp->firstChild()->content();
504 if (bag) {
505 holder = bag;
506 bag = g_strconcat(holder, ", ", str, NULL);
507 g_free(holder);
508 }
509 else {
510 bag = g_strdup(str);
511 }
512 }
513 }
514 return bag;
516 default:
517 break;
518 }
519 return NULL;
520 }
522 unsigned int
523 rdf_set_repr_text ( Inkscape::XML::Node * repr,
524 struct rdf_work_entity_t * entity,
525 gchar const * text )
526 {
527 g_return_val_if_fail ( repr != NULL, 0);
528 g_return_val_if_fail ( entity != NULL, 0);
529 g_return_val_if_fail ( text != NULL, 0);
530 gchar * str = NULL;
531 gchar** strlist = NULL;
532 int i;
534 Inkscape::XML::Node * temp=NULL;
535 Inkscape::XML::Node * child=NULL;
536 Inkscape::XML::Node * parent=repr;
538 Inkscape::XML::Document * xmldoc = parent->document();
539 g_return_val_if_fail (xmldoc != NULL, FALSE);
541 switch (entity->datatype) {
542 case RDF_CONTENT:
543 temp = sp_repr_children(parent);
544 if ( temp == NULL ) {
545 temp = xmldoc->createTextNode( text );
546 g_return_val_if_fail (temp != NULL, FALSE);
548 parent->appendChild(temp);
549 Inkscape::GC::release(temp);
551 return TRUE;
552 }
553 else {
554 temp->setContent(text);
555 return TRUE;
556 }
558 case RDF_AGENT:
559 temp = sp_repr_lookup_name ( parent, "cc:Agent", 1 );
560 if ( temp == NULL ) {
561 temp = xmldoc->createElement ( "cc:Agent" );
562 g_return_val_if_fail (temp != NULL, FALSE);
564 parent->appendChild(temp);
565 Inkscape::GC::release(temp);
566 }
567 parent = temp;
569 temp = sp_repr_lookup_name ( parent, "dc:title", 1 );
570 if ( temp == NULL ) {
571 temp = xmldoc->createElement ( "dc:title" );
572 g_return_val_if_fail (temp != NULL, FALSE);
574 parent->appendChild(temp);
575 Inkscape::GC::release(temp);
576 }
577 parent = temp;
579 temp = sp_repr_children(parent);
580 if ( temp == NULL ) {
581 temp = xmldoc->createTextNode( text );
582 g_return_val_if_fail (temp != NULL, FALSE);
584 parent->appendChild(temp);
585 Inkscape::GC::release(temp);
587 return TRUE;
588 }
589 else {
590 temp->setContent(text);
591 return TRUE;
592 }
594 case RDF_RESOURCE:
595 parent->setAttribute("rdf:resource", text );
596 return true;
598 case RDF_XML:
599 return 1;
601 case RDF_BAG:
602 /* find/create the rdf:Bag item */
603 temp = sp_repr_lookup_name ( parent, "rdf:Bag", 1 );
604 if ( temp == NULL ) {
605 /* backward compatibility: drop the dc:subject contents */
606 while ( (temp = sp_repr_children( parent )) ) {
607 parent->removeChild(temp);
608 }
610 temp = xmldoc->createElement ( "rdf:Bag" );
611 g_return_val_if_fail (temp != NULL, FALSE);
613 parent->appendChild(temp);
614 Inkscape::GC::release(temp);
615 }
616 parent = temp;
618 /* toss all the old list items */
619 while ( (temp = sp_repr_children( parent )) ) {
620 parent->removeChild(temp);
621 }
623 /* chop our list up on commas */
624 strlist = g_strsplit( text, ",", 0);
626 for (i = 0; (str = strlist[i]); i++) {
627 temp = xmldoc->createElement ( "rdf:li" );
628 g_return_val_if_fail (temp != NULL, 0);
630 parent->appendChild(temp);
631 Inkscape::GC::release(temp);
633 child = xmldoc->createTextNode( g_strstrip(str) );
634 g_return_val_if_fail (child != NULL, 0);
636 temp->appendChild(child);
637 Inkscape::GC::release(child);
638 }
639 g_strfreev( strlist );
641 return 1;
643 default:
644 break;
645 }
646 return 0;
647 }
649 Inkscape::XML::Node *
650 rdf_get_rdf_root_repr ( SPDocument * doc, bool build )
651 {
652 g_return_val_if_fail (doc != NULL, NULL);
653 g_return_val_if_fail (doc->rroot != NULL, NULL);
655 Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
656 g_return_val_if_fail (xmldoc != NULL, NULL);
658 Inkscape::XML::Node * rdf = sp_repr_lookup_name ( doc->rroot, XML_TAG_NAME_RDF );
660 if (rdf == NULL) {
661 //printf("missing XML '%s'\n",XML_TAG_NAME_RDF);
662 if (!build) return NULL;
664 Inkscape::XML::Node * svg = sp_repr_lookup_name ( doc->rroot, XML_TAG_NAME_SVG );
665 g_return_val_if_fail ( svg != NULL, NULL );
667 Inkscape::XML::Node * parent = sp_repr_lookup_name ( svg, XML_TAG_NAME_METADATA );
668 if ( parent == NULL ) {
669 parent = xmldoc->createElement( XML_TAG_NAME_METADATA );
670 g_return_val_if_fail ( parent != NULL, NULL);
672 svg->appendChild(parent);
673 Inkscape::GC::release(parent);
674 }
676 Inkscape::XML::Document * xmldoc = parent->document();
677 g_return_val_if_fail (xmldoc != NULL, FALSE);
679 rdf = xmldoc->createElement( XML_TAG_NAME_RDF );
680 g_return_val_if_fail (rdf != NULL, NULL);
682 parent->appendChild(rdf);
683 Inkscape::GC::release(rdf);
684 }
686 /*
687 * some implementations do not put RDF stuff inside <metadata>,
688 * so we need to check for it and add it if we don't see it
689 */
690 Inkscape::XML::Node * want_metadata = sp_repr_parent ( rdf );
691 g_return_val_if_fail (want_metadata != NULL, NULL);
692 if (strcmp( want_metadata->name(), XML_TAG_NAME_METADATA )) {
693 Inkscape::XML::Node * metadata = xmldoc->createElement( XML_TAG_NAME_METADATA );
694 g_return_val_if_fail (metadata != NULL, NULL);
696 /* attach the metadata node */
697 want_metadata->appendChild(metadata);
698 Inkscape::GC::release(metadata);
700 /* move the RDF into it */
701 Inkscape::GC::anchor(rdf);
702 sp_repr_unparent ( rdf );
703 metadata->appendChild(rdf);
704 Inkscape::GC::release(rdf);
705 }
707 return rdf;
708 }
710 Inkscape::XML::Node *
711 rdf_get_xml_repr( SPDocument * doc, gchar const * name, bool build )
712 {
713 g_return_val_if_fail (name != NULL, NULL);
714 g_return_val_if_fail (doc != NULL, NULL);
715 g_return_val_if_fail (doc->rroot != NULL, NULL);
717 Inkscape::XML::Node * rdf = rdf_get_rdf_root_repr ( doc, build );
718 if (!rdf) return NULL;
720 Inkscape::XML::Node * xml = sp_repr_lookup_name ( rdf, name );
721 if (xml == NULL) {
722 //printf("missing XML '%s'\n",name);
723 if (!build) return NULL;
725 Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
726 g_return_val_if_fail (xmldoc != NULL, NULL);
728 xml = xmldoc->createElement( name );
729 g_return_val_if_fail (xml != NULL, NULL);
731 xml->setAttribute("rdf:about", "" );
733 rdf->appendChild(xml);
734 Inkscape::GC::release(xml);
735 }
737 return xml;
738 }
740 Inkscape::XML::Node *
741 rdf_get_work_repr( SPDocument * doc, gchar const * name, bool build )
742 {
743 g_return_val_if_fail (name != NULL, NULL);
744 g_return_val_if_fail (doc != NULL, NULL);
745 g_return_val_if_fail (doc->rroot != NULL, NULL);
747 Inkscape::XML::Node * work = rdf_get_xml_repr ( doc, XML_TAG_NAME_WORK, build );
748 if (!work) return NULL;
750 Inkscape::XML::Node * item = sp_repr_lookup_name ( work, name, 1 );
751 if (item == NULL) {
752 //printf("missing XML '%s'\n",name);
753 if (!build) return NULL;
755 Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
756 g_return_val_if_fail (xmldoc != NULL, NULL);
758 item = xmldoc->createElement( name );
759 g_return_val_if_fail (item != NULL, NULL);
761 work->appendChild(item);
762 Inkscape::GC::release(item);
763 }
765 return item;
766 }
770 /**
771 * \brief Retrieves a known RDF/Work entity's contents from the document XML by name
772 * \return A pointer to the entity's static contents as a string, or NULL if no entity exists
773 * \param entity The desired RDF/Work entity
774 *
775 */
776 const gchar *
777 rdf_get_work_entity(SPDocument * doc, struct rdf_work_entity_t * entity)
778 {
779 g_return_val_if_fail (doc != NULL, NULL);
780 if ( entity == NULL ) return NULL;
781 //printf("want '%s'\n",entity->title);
783 Inkscape::XML::Node * item;
784 if ( entity->datatype == RDF_XML ) {
785 item = rdf_get_xml_repr ( doc, entity->tag, FALSE );
786 }
787 else {
788 item = rdf_get_work_repr( doc, entity->tag, FALSE );
789 }
790 if ( item == NULL ) return NULL;
792 const gchar * result = rdf_get_repr_text ( item, entity );
793 //printf("found '%s' == '%s'\n", entity->title, result );
794 return result;
795 }
797 /**
798 * \brief Stores a string into a named RDF/Work entity in the document XML
799 * \param entity The desired RDF/Work entity to replace
800 * \param string The string to replace the entity contents with
801 *
802 */
803 unsigned int
804 rdf_set_work_entity(SPDocument * doc, struct rdf_work_entity_t * entity,
805 const gchar * text)
806 {
807 g_return_val_if_fail ( entity != NULL, 0 );
808 if (text == NULL) {
809 // FIXME: on a "NULL" text, delete the entity. For now, blank it.
810 text="";
811 }
813 /*
814 printf("changing '%s' (%s) to '%s'\n",
815 entity->title,
816 entity->tag,
817 text);
818 */
820 Inkscape::XML::Node * item = rdf_get_work_repr( doc, entity->tag, TRUE );
821 g_return_val_if_fail ( item != NULL, 0 );
823 return rdf_set_repr_text ( item, entity, text );
824 }
826 #undef DEBUG_MATCH
828 bool
829 rdf_match_license ( Inkscape::XML::Node * repr, struct rdf_license_t * license )
830 {
831 g_assert ( repr != NULL );
832 g_assert ( license != NULL );
834 bool result=TRUE;
835 #ifdef DEBUG_MATCH
836 printf("checking against '%s'\n",license->name);
837 #endif
839 int count = 0;
840 for (struct rdf_double_t * details = license->details;
841 details->name; details++ ) {
842 count++;
843 }
844 bool * matched = (bool*)calloc(count,sizeof(bool));
846 for (Inkscape::XML::Node * current = sp_repr_children ( repr );
847 current;
848 current = sp_repr_next ( current ) ) {
850 gchar const * attr = current->attribute("rdf:resource");
851 if ( attr == NULL ) continue;
853 #ifdef DEBUG_MATCH
854 printf("\texamining '%s' => '%s'\n", current->name(), attr);
855 #endif
857 bool found_match=FALSE;
858 for (int i=0; i<count; i++) {
859 // skip already matched items
860 if (matched[i]) continue;
862 #ifdef DEBUG_MATCH
863 printf("\t\t'%s' vs '%s'\n", current->name(), license->details[i].name);
864 printf("\t\t'%s' vs '%s'\n", attr, license->details[i].resource);
865 #endif
867 if (!strcmp( current->name(),
868 license->details[i].name ) &&
869 !strcmp( attr,
870 license->details[i].resource )) {
871 matched[i]=TRUE;
872 found_match=TRUE;
873 #ifdef DEBUG_MATCH
874 printf("\t\tgood!\n");
875 #endif
876 break;
877 }
878 }
879 if (!found_match) {
880 // if we checked each known item of the license
881 // and didn't find it, we must abort
882 result=FALSE;
883 #ifdef DEBUG_MATCH
884 printf("\t\tno '%s' element matched XML (bong)!\n",license->name);
885 #endif
886 break;
887 }
888 }
889 #ifdef DEBUG_MATCH
890 if (result) printf("\t\tall XML found matching elements!\n");
891 #endif
892 for (int i=0; result && i<count; i++) {
893 // scan looking for an unmatched item
894 if (matched[i]==0) {
895 result=FALSE;
896 #ifdef DEBUG_MATCH
897 printf("\t\tnot all '%s' elements used to match (bong)!\n", license->name);
898 #endif
899 }
900 }
902 #ifdef DEBUG_MATCH
903 printf("\t\tall '%s' elements used to match!\n",license->name);
904 #endif
906 free(matched);
908 #ifdef DEBUG_MATCH
909 if (result) printf("matched '%s'\n",license->name);
910 #endif
911 return result;
912 }
914 /**
915 * \brief Attempts to match and retrieve a known RDF/License from the document XML
916 * \return A pointer to the static RDF license structure
917 *
918 */
919 struct rdf_license_t *
920 rdf_get_license(SPDocument * document)
921 {
922 Inkscape::XML::Node * repr = rdf_get_xml_repr ( document, XML_TAG_NAME_LICENSE, FALSE );
923 if (repr) {
924 for (struct rdf_license_t * license = rdf_licenses;
925 license->name; license++ ) {
926 if ( rdf_match_license ( repr, license ) ) return license;
927 }
928 }
929 #ifdef DEBUG_MATCH
930 else {
931 printf("no license XML\n");
932 }
933 #endif
934 return NULL;
935 }
937 /**
938 * \brief Stores an RDF/License XML in the document XML
939 * \param document Which document to update
940 * \param license The desired RDF/License structure to store; NULL drops old license, so can be used for proprietary license.
941 *
942 */
943 void
944 rdf_set_license(SPDocument * doc, struct rdf_license_t const * license)
945 {
946 // drop old license section
947 Inkscape::XML::Node * repr = rdf_get_xml_repr ( doc, XML_TAG_NAME_LICENSE, FALSE );
948 if (repr) sp_repr_unparent(repr);
950 if (!license) return;
952 // build new license section
953 repr = rdf_get_xml_repr ( doc, XML_TAG_NAME_LICENSE, TRUE );
954 g_assert ( repr );
956 repr->setAttribute("rdf:about", license->uri );
958 Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
959 g_return_if_fail (xmldoc != NULL);
961 for (struct rdf_double_t * detail = license->details;
962 detail->name; detail++) {
963 Inkscape::XML::Node * child = xmldoc->createElement( detail->name );
964 g_assert ( child != NULL );
966 child->setAttribute("rdf:resource", detail->resource );
967 repr->appendChild(child);
968 Inkscape::GC::release(child);
969 }
970 }
972 struct rdf_entity_default_t {
973 gchar const * name;
974 gchar const * text;
975 };
976 struct rdf_entity_default_t rdf_defaults[] = {
977 { "format", "image/svg+xml", },
978 { "type", "http://purl.org/dc/dcmitype/StillImage", },
979 { NULL, NULL, }
980 };
982 void
983 rdf_set_defaults ( SPDocument * doc )
984 {
985 g_assert ( doc != NULL );
987 // Create metadata node if it doesn't already exist
988 if (!sp_item_group_get_child_by_name ((SPGroup *) doc->root, NULL,
989 XML_TAG_NAME_METADATA)) {
990 // create repr
991 Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
992 g_return_if_fail (xmldoc != NULL);
993 Inkscape::XML::Node * rnew = xmldoc->createElement (XML_TAG_NAME_METADATA);
994 // insert into the document
995 doc->rroot->addChild(rnew, NULL);
996 // clean up
997 Inkscape::GC::release(rnew);
998 }
1000 /* install defaults */
1001 for ( struct rdf_entity_default_t * rdf_default = rdf_defaults;
1002 rdf_default->name;
1003 rdf_default++) {
1004 struct rdf_work_entity_t * entity = rdf_find_entity ( rdf_default->name );
1005 g_assert ( entity != NULL );
1007 if ( rdf_get_work_entity ( doc, entity ) == NULL ) {
1008 rdf_set_work_entity ( doc, entity, rdf_default->text );
1009 }
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 :