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_license_t rdf_licenses [] = {
168 { N_("CC Attribution"),
169 "http://creativecommons.org/licenses/by/3.0/",
170 rdf_license_cc_a,
171 },
173 { N_("CC Attribution-ShareAlike"),
174 "http://creativecommons.org/licenses/by-sa/3.0/",
175 rdf_license_cc_a_sa,
176 },
178 { N_("CC Attribution-NoDerivs"),
179 "http://creativecommons.org/licenses/by-nd/3.0/",
180 rdf_license_cc_a_nd,
181 },
183 { N_("CC Attribution-NonCommercial"),
184 "http://creativecommons.org/licenses/by-nc/3.0/",
185 rdf_license_cc_a_nc,
186 },
188 { N_("CC Attribution-NonCommercial-ShareAlike"),
189 "http://creativecommons.org/licenses/by-nc-sa/3.0/",
190 rdf_license_cc_a_nc_sa,
191 },
193 { N_("CC Attribution-NonCommercial-NoDerivs"),
194 "http://creativecommons.org/licenses/by-nc-nd/3.0/",
195 rdf_license_cc_a_nc_nd,
196 },
198 { N_("Public Domain"),
199 "http://creativecommons.org/licenses/publicdomain/",
200 rdf_license_pd,
201 },
203 { N_("FreeArt"),
204 "http://artlibre.org/licence.php/lalgb.html",
205 rdf_license_freeart,
206 },
208 { NULL, NULL, rdf_license_empty, }
209 };
211 #define XML_TAG_NAME_SVG "svg:svg"
212 #define XML_TAG_NAME_METADATA "svg:metadata"
213 #define XML_TAG_NAME_RDF "rdf:RDF"
214 #define XML_TAG_NAME_WORK "cc:Work"
215 #define XML_TAG_NAME_LICENSE "cc:License"
217 // Remember when using the "title" and "tip" elements to pass them through
218 // the localization functions when you use them!
219 struct rdf_work_entity_t rdf_work_entities [] = {
220 { "title", N_("Title"), "dc:title", RDF_CONTENT,
221 N_("Name by which this document is formally known."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
222 },
223 { "date", N_("Date"), "dc:date", RDF_CONTENT,
224 N_("Date associated with the creation of this document (YYYY-MM-DD)."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
225 },
226 { "format", N_("Format"), "dc:format", RDF_CONTENT,
227 N_("The physical or digital manifestation of this document (MIME type)."), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
228 },
229 { "type", N_("Type"), "dc:type", RDF_RESOURCE,
230 N_("Type of document (DCMI Type)."), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
231 },
233 { "creator", N_("Creator"), "dc:creator", RDF_AGENT,
234 N_("Name of entity primarily responsible for making the content of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
235 },
236 { "rights", N_("Rights"), "dc:rights", RDF_AGENT,
237 N_("Name of entity with rights to the Intellectual Property of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
238 },
239 { "publisher", N_("Publisher"), "dc:publisher", RDF_AGENT,
240 N_("Name of entity responsible for making this document available."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
241 },
243 { "identifier", N_("Identifier"), "dc:identifier", RDF_CONTENT,
244 N_("Unique URI to reference this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
245 },
246 { "source", N_("Source"), "dc:source", RDF_CONTENT,
247 N_("Unique URI to reference the source of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
248 },
249 { "relation", N_("Relation"), "dc:relation", RDF_CONTENT,
250 N_("Unique URI to a related document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
251 },
252 { "language", N_("Language"), "dc:language", RDF_CONTENT,
253 N_("Two-letter language tag with optional subtags for the language of this document. (e.g. 'en-GB')"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
254 },
255 { "subject", N_("Keywords"), "dc:subject", RDF_BAG,
256 N_("The topic of this document as comma-separated key words, phrases, or classifications."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
257 },
258 // TRANSLATORS: "Coverage": the spatial or temporal characteristics of the content.
259 // For info, see Appendix D of http://www.w3.org/TR/1998/WD-rdf-schema-19980409/
260 { "coverage", N_("Coverage"), "dc:coverage", RDF_CONTENT,
261 N_("Extent or scope of this document."), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
262 },
264 { "description", N_("Description"), "dc:description", RDF_CONTENT,
265 N_("A short account of the content of this document."), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC,
266 },
268 // FIXME: need to handle 1 agent per line of input
269 { "contributor", N_("Contributors"), "dc:contributor", RDF_AGENT,
270 N_("Names of entities responsible for making contributions to the content of this document."), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC,
271 },
273 // TRANSLATORS: URL to a page that defines the license for the document
274 { "license_uri", N_("URI"), "cc:license", RDF_RESOURCE,
275 // TRANSLATORS: this is where you put a URL to a page that defines the license
276 N_("URI to this document's license's namespace definition."), RDF_FORMAT_LINE, RDF_EDIT_SPECIAL,
277 },
279 // TRANSLATORS: fragment of XML representing the license of the document
280 { "license_fragment", N_("Fragment"), "License", RDF_XML,
281 N_("XML fragment for the RDF 'License' section."), RDF_FORMAT_MULTILINE, RDF_EDIT_SPECIAL,
282 },
284 { NULL, NULL, NULL, RDF_CONTENT,
285 NULL, RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
286 }
287 };
289 /**
290 * \brief Retrieves a known RDF/Work entity by name
291 * \return A pointer to an RDF/Work entity
292 * \param name The desired RDF/Work entity
293 *
294 */
295 struct rdf_work_entity_t *
296 rdf_find_entity(gchar const * name)
297 {
298 struct rdf_work_entity_t *entity;
299 for (entity=rdf_work_entities; entity->name; entity++) {
300 if (strcmp(entity->name,name)==0) break;
301 }
302 if (entity->name) return entity;
303 return NULL;
304 }
306 /*
307 * Takes the inkscape rdf struct and spits out a static RDF, which is only
308 * useful for testing. We must merge the rdf struct into the XML DOM for
309 * changes to be saved.
310 */
311 /*
313 Since g_markup_printf_escaped doesn't exist for most people's glib
314 right now, this function will remain commented out since it's only
315 for generic debug anyway. --Kees
317 gchar *
318 rdf_string(struct rdf_t * rdf)
319 {
320 gulong overall=0;
321 gchar *string=NULL;
323 gchar *rdf_head="\
324 <rdf:RDF xmlns=\"http://web.resource.org/cc/\"\
325 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\
326 xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\
327 ";
328 gchar *work_head="\
329 <Work rdf:about=\"\">\
330 <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />\
331 ";
332 gchar *work_title=NULL;
333 gchar *work_date=NULL;
334 gchar *work_description=NULL;
335 gchar *work_creator=NULL;
336 gchar *work_owner=NULL;
337 gchar *work_source=NULL;
338 gchar *work_license=NULL;
339 gchar *license_head=NULL;
340 gchar *license=NULL;
341 gchar *license_end="</License>\n";
342 gchar *work_end="</Work>\n";
343 gchar *rdf_end="</rdf:RDF>\n";
345 if (rdf && rdf->work_title && rdf->work_title[0]) {
346 work_title=g_markup_printf_escaped(" <dc:title>%s</dc:title>\n",
347 rdf->work_title);
348 overall+=strlen(work_title);
349 }
350 if (rdf && rdf->work_date && rdf->work_date[0]) {
351 work_date=g_markup_printf_escaped(" <dc:date>%s</dc:date>\n",
352 rdf->work_date);
353 overall+=strlen(work_date);
354 }
355 if (rdf && rdf->work_description && rdf->work_description[0]) {
356 work_description=g_markup_printf_escaped(" <dc:description>%s</dc:description>\n",
357 rdf->work_description);
358 overall+=strlen(work_description);
359 }
360 if (rdf && rdf->work_creator && rdf->work_creator[0]) {
361 work_creator=g_markup_printf_escaped(" <dc:creator><Agent>\
362 <dc:title>%s</dc:title>\
363 </Agent></dc:creator>\n",
364 rdf->work_creator);
365 overall+=strlen(work_creator);
366 }
367 if (rdf && rdf->work_owner && rdf->work_owner[0]) {
368 work_owner=g_markup_printf_escaped(" <dc:rights><Agent>\
369 <dc:title>%s</dc:title>\
370 </Agent></dc:rights>\n",
371 rdf->work_owner);
372 overall+=strlen(work_owner);
373 }
374 if (rdf && rdf->work_source && rdf->work_source[0]) {
375 work_source=g_markup_printf_escaped(" <dc:source rdf:resource=\"%s\" />\n",
376 rdf->work_source);
377 overall+=strlen(work_source);
378 }
379 if (rdf && rdf->license && rdf->license->work_rdf && rdf->license->work_rdf[0]) {
380 work_license=g_markup_printf_escaped(" <license rdf:resource=\"%s\" />\n",
381 rdf->license->work_rdf);
382 overall+=strlen(work_license);
384 license_head=g_markup_printf_escaped("<License rdf:about=\"%s\">\n",
385 rdf->license->work_rdf);
386 overall+=strlen(license_head);
387 overall+=strlen(rdf->license->license_rdf);
388 overall+=strlen(license_end);
389 }
391 overall+=strlen(rdf_head)+strlen(rdf_end);
392 overall+=strlen(work_head)+strlen(work_end);
394 overall++; // NULL term
396 if (!(string=(gchar*)g_malloc(overall))) {
397 return NULL;
398 }
400 string[0]='\0';
401 strcat(string,rdf_head);
402 strcat(string,work_head);
404 if (work_title) strcat(string,work_title);
405 if (work_date) strcat(string,work_date);
406 if (work_description) strcat(string,work_description);
407 if (work_creator) strcat(string,work_creator);
408 if (work_owner) strcat(string,work_owner);
409 if (work_source) strcat(string,work_source);
410 if (work_license) strcat(string,work_license);
412 strcat(string,work_end);
413 if (license_head) {
414 strcat(string,license_head);
415 strcat(string,rdf->license->license_rdf);
416 strcat(string,license_end);
417 }
418 strcat(string,rdf_end);
420 return string;
421 }
422 */
425 /**
426 * \brief Pull the text out of an RDF entity, depends on how it's stored
427 * \return A pointer to the entity's static contents as a string
428 * \param repr The XML element to extract from
429 * \param entity The desired RDF/Work entity
430 *
431 */
432 const gchar *
433 rdf_get_repr_text ( Inkscape::XML::Node * repr, struct rdf_work_entity_t * entity )
434 {
435 g_return_val_if_fail (repr != NULL, NULL);
436 g_return_val_if_fail (entity != NULL, NULL);
437 static gchar * bag = NULL;
438 gchar * holder = NULL;
440 Inkscape::XML::Node * temp=NULL;
441 switch (entity->datatype) {
442 case RDF_CONTENT:
443 temp = sp_repr_children(repr);
444 if ( temp == NULL ) return NULL;
446 return temp->content();
448 case RDF_AGENT:
449 temp = sp_repr_lookup_name ( repr, "cc:Agent", 1 );
450 if ( temp == NULL ) return NULL;
452 temp = sp_repr_lookup_name ( temp, "dc:title", 1 );
453 if ( temp == NULL ) return NULL;
455 temp = sp_repr_children(temp);
456 if ( temp == NULL ) return NULL;
458 return temp->content();
460 case RDF_RESOURCE:
461 return repr->attribute("rdf:resource");
463 case RDF_XML:
464 return "xml goes here";
466 case RDF_BAG:
467 /* clear the static string. yucky. */
468 if (bag) g_free(bag);
469 bag = NULL;
471 temp = sp_repr_lookup_name ( repr, "rdf:Bag", 1 );
472 if ( temp == NULL ) {
473 /* backwards compatible: read contents */
474 temp = sp_repr_children(repr);
475 if ( temp == NULL ) return NULL;
477 return temp->content();
478 }
480 for ( temp = sp_repr_children(temp) ;
481 temp ;
482 temp = sp_repr_next(temp) ) {
483 if (!strcmp(temp->name(),"rdf:li") &&
484 temp->firstChild()) {
485 const gchar * str = temp->firstChild()->content();
486 if (bag) {
487 holder = bag;
488 bag = g_strconcat(holder, ", ", str, NULL);
489 g_free(holder);
490 }
491 else {
492 bag = g_strdup(str);
493 }
494 }
495 }
496 return bag;
498 default:
499 break;
500 }
501 return NULL;
502 }
504 unsigned int
505 rdf_set_repr_text ( Inkscape::XML::Node * repr,
506 struct rdf_work_entity_t * entity,
507 gchar const * text )
508 {
509 g_return_val_if_fail ( repr != NULL, 0);
510 g_return_val_if_fail ( entity != NULL, 0);
511 g_return_val_if_fail ( text != NULL, 0);
512 gchar * str = NULL;
513 gchar** strlist = NULL;
514 int i;
516 Inkscape::XML::Node * temp=NULL;
517 Inkscape::XML::Node * child=NULL;
518 Inkscape::XML::Node * parent=repr;
520 Inkscape::XML::Document * xmldoc = parent->document();
521 g_return_val_if_fail (xmldoc != NULL, FALSE);
523 switch (entity->datatype) {
524 case RDF_CONTENT:
525 temp = sp_repr_children(parent);
526 if ( temp == NULL ) {
527 temp = xmldoc->createTextNode( text );
528 g_return_val_if_fail (temp != NULL, FALSE);
530 parent->appendChild(temp);
531 Inkscape::GC::release(temp);
533 return TRUE;
534 }
535 else {
536 temp->setContent(text);
537 return TRUE;
538 }
540 case RDF_AGENT:
541 temp = sp_repr_lookup_name ( parent, "cc:Agent", 1 );
542 if ( temp == NULL ) {
543 temp = xmldoc->createElement ( "cc:Agent" );
544 g_return_val_if_fail (temp != NULL, FALSE);
546 parent->appendChild(temp);
547 Inkscape::GC::release(temp);
548 }
549 parent = temp;
551 temp = sp_repr_lookup_name ( parent, "dc:title", 1 );
552 if ( temp == NULL ) {
553 temp = xmldoc->createElement ( "dc:title" );
554 g_return_val_if_fail (temp != NULL, FALSE);
556 parent->appendChild(temp);
557 Inkscape::GC::release(temp);
558 }
559 parent = temp;
561 temp = sp_repr_children(parent);
562 if ( temp == NULL ) {
563 temp = xmldoc->createTextNode( text );
564 g_return_val_if_fail (temp != NULL, FALSE);
566 parent->appendChild(temp);
567 Inkscape::GC::release(temp);
569 return TRUE;
570 }
571 else {
572 temp->setContent(text);
573 return TRUE;
574 }
576 case RDF_RESOURCE:
577 parent->setAttribute("rdf:resource", text );
578 return true;
580 case RDF_XML:
581 return 1;
583 case RDF_BAG:
584 /* find/create the rdf:Bag item */
585 temp = sp_repr_lookup_name ( parent, "rdf:Bag", 1 );
586 if ( temp == NULL ) {
587 /* backward compatibility: drop the dc:subject contents */
588 while ( (temp = sp_repr_children( parent )) ) {
589 parent->removeChild(temp);
590 }
592 temp = xmldoc->createElement ( "rdf:Bag" );
593 g_return_val_if_fail (temp != NULL, FALSE);
595 parent->appendChild(temp);
596 Inkscape::GC::release(temp);
597 }
598 parent = temp;
600 /* toss all the old list items */
601 while ( (temp = sp_repr_children( parent )) ) {
602 parent->removeChild(temp);
603 }
605 /* chop our list up on commas */
606 strlist = g_strsplit( text, ",", 0);
608 for (i = 0; (str = strlist[i]); i++) {
609 temp = xmldoc->createElement ( "rdf:li" );
610 g_return_val_if_fail (temp != NULL, 0);
612 parent->appendChild(temp);
613 Inkscape::GC::release(temp);
615 child = xmldoc->createTextNode( g_strstrip(str) );
616 g_return_val_if_fail (child != NULL, 0);
618 temp->appendChild(child);
619 Inkscape::GC::release(child);
620 }
621 g_strfreev( strlist );
623 return 1;
625 default:
626 break;
627 }
628 return 0;
629 }
631 Inkscape::XML::Node *
632 rdf_get_rdf_root_repr ( SPDocument * doc, bool build )
633 {
634 g_return_val_if_fail (doc != NULL, NULL);
635 g_return_val_if_fail (doc->rroot != NULL, NULL);
637 Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
638 g_return_val_if_fail (xmldoc != NULL, NULL);
640 Inkscape::XML::Node * rdf = sp_repr_lookup_name ( doc->rroot, XML_TAG_NAME_RDF );
642 if (rdf == NULL) {
643 //printf("missing XML '%s'\n",XML_TAG_NAME_RDF);
644 if (!build) return NULL;
646 Inkscape::XML::Node * svg = sp_repr_lookup_name ( doc->rroot, XML_TAG_NAME_SVG );
647 g_return_val_if_fail ( svg != NULL, NULL );
649 Inkscape::XML::Node * parent = sp_repr_lookup_name ( svg, XML_TAG_NAME_METADATA );
650 if ( parent == NULL ) {
651 parent = xmldoc->createElement( XML_TAG_NAME_METADATA );
652 g_return_val_if_fail ( parent != NULL, NULL);
654 svg->appendChild(parent);
655 Inkscape::GC::release(parent);
656 }
658 Inkscape::XML::Document * xmldoc = parent->document();
659 g_return_val_if_fail (xmldoc != NULL, FALSE);
661 rdf = xmldoc->createElement( XML_TAG_NAME_RDF );
662 g_return_val_if_fail (rdf != NULL, NULL);
664 parent->appendChild(rdf);
665 Inkscape::GC::release(rdf);
666 }
668 /*
669 * some implementations do not put RDF stuff inside <metadata>,
670 * so we need to check for it and add it if we don't see it
671 */
672 Inkscape::XML::Node * want_metadata = sp_repr_parent ( rdf );
673 g_return_val_if_fail (want_metadata != NULL, NULL);
674 if (strcmp( want_metadata->name(), XML_TAG_NAME_METADATA )) {
675 Inkscape::XML::Node * metadata = xmldoc->createElement( XML_TAG_NAME_METADATA );
676 g_return_val_if_fail (metadata != NULL, NULL);
678 /* attach the metadata node */
679 want_metadata->appendChild(metadata);
680 Inkscape::GC::release(metadata);
682 /* move the RDF into it */
683 Inkscape::GC::anchor(rdf);
684 sp_repr_unparent ( rdf );
685 metadata->appendChild(rdf);
686 Inkscape::GC::release(rdf);
687 }
689 return rdf;
690 }
692 Inkscape::XML::Node *
693 rdf_get_xml_repr( SPDocument * doc, gchar const * name, bool build )
694 {
695 g_return_val_if_fail (name != NULL, NULL);
696 g_return_val_if_fail (doc != NULL, NULL);
697 g_return_val_if_fail (doc->rroot != NULL, NULL);
699 Inkscape::XML::Node * rdf = rdf_get_rdf_root_repr ( doc, build );
700 if (!rdf) return NULL;
702 Inkscape::XML::Node * xml = sp_repr_lookup_name ( rdf, name );
703 if (xml == NULL) {
704 //printf("missing XML '%s'\n",name);
705 if (!build) return NULL;
707 Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
708 g_return_val_if_fail (xmldoc != NULL, NULL);
710 xml = xmldoc->createElement( name );
711 g_return_val_if_fail (xml != NULL, NULL);
713 xml->setAttribute("rdf:about", "" );
715 rdf->appendChild(xml);
716 Inkscape::GC::release(xml);
717 }
719 return xml;
720 }
722 Inkscape::XML::Node *
723 rdf_get_work_repr( SPDocument * doc, gchar const * name, bool build )
724 {
725 g_return_val_if_fail (name != NULL, NULL);
726 g_return_val_if_fail (doc != NULL, NULL);
727 g_return_val_if_fail (doc->rroot != NULL, NULL);
729 Inkscape::XML::Node * work = rdf_get_xml_repr ( doc, XML_TAG_NAME_WORK, build );
730 if (!work) return NULL;
732 Inkscape::XML::Node * item = sp_repr_lookup_name ( work, name, 1 );
733 if (item == NULL) {
734 //printf("missing XML '%s'\n",name);
735 if (!build) return NULL;
737 Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
738 g_return_val_if_fail (xmldoc != NULL, NULL);
740 item = xmldoc->createElement( name );
741 g_return_val_if_fail (item != NULL, NULL);
743 work->appendChild(item);
744 Inkscape::GC::release(item);
745 }
747 return item;
748 }
752 /**
753 * \brief Retrieves a known RDF/Work entity's contents from the document XML by name
754 * \return A pointer to the entity's static contents as a string, or NULL if no entity exists
755 * \param entity The desired RDF/Work entity
756 *
757 */
758 const gchar *
759 rdf_get_work_entity(SPDocument * doc, struct rdf_work_entity_t * entity)
760 {
761 g_return_val_if_fail (doc != NULL, NULL);
762 if ( entity == NULL ) return NULL;
763 //printf("want '%s'\n",entity->title);
765 Inkscape::XML::Node * item;
766 if ( entity->datatype == RDF_XML ) {
767 item = rdf_get_xml_repr ( doc, entity->tag, FALSE );
768 }
769 else {
770 item = rdf_get_work_repr( doc, entity->tag, FALSE );
771 }
772 if ( item == NULL ) return NULL;
774 const gchar * result = rdf_get_repr_text ( item, entity );
775 //printf("found '%s' == '%s'\n", entity->title, result );
776 return result;
777 }
779 /**
780 * \brief Stores a string into a named RDF/Work entity in the document XML
781 * \param entity The desired RDF/Work entity to replace
782 * \param string The string to replace the entity contents with
783 *
784 */
785 unsigned int
786 rdf_set_work_entity(SPDocument * doc, struct rdf_work_entity_t * entity,
787 const gchar * text)
788 {
789 g_return_val_if_fail ( entity != NULL, 0 );
790 if (text == NULL) {
791 // FIXME: on a "NULL" text, delete the entity. For now, blank it.
792 text="";
793 }
795 /*
796 printf("changing '%s' (%s) to '%s'\n",
797 entity->title,
798 entity->tag,
799 text);
800 */
802 Inkscape::XML::Node * item = rdf_get_work_repr( doc, entity->tag, TRUE );
803 g_return_val_if_fail ( item != NULL, 0 );
805 return rdf_set_repr_text ( item, entity, text );
806 }
808 #undef DEBUG_MATCH
810 bool
811 rdf_match_license ( Inkscape::XML::Node * repr, struct rdf_license_t * license )
812 {
813 g_assert ( repr != NULL );
814 g_assert ( license != NULL );
816 bool result=TRUE;
817 #ifdef DEBUG_MATCH
818 printf("checking against '%s'\n",license->name);
819 #endif
821 int count = 0;
822 for (struct rdf_double_t * details = license->details;
823 details->name; details++ ) {
824 count++;
825 }
826 bool * matched = (bool*)calloc(count,sizeof(bool));
828 for (Inkscape::XML::Node * current = sp_repr_children ( repr );
829 current;
830 current = sp_repr_next ( current ) ) {
832 gchar const * attr = current->attribute("rdf:resource");
833 if ( attr == NULL ) continue;
835 #ifdef DEBUG_MATCH
836 printf("\texamining '%s' => '%s'\n", current->name(), attr);
837 #endif
839 bool found_match=FALSE;
840 for (int i=0; i<count; i++) {
841 // skip already matched items
842 if (matched[i]) continue;
844 #ifdef DEBUG_MATCH
845 printf("\t\t'%s' vs '%s'\n", current->name(), license->details[i].name);
846 printf("\t\t'%s' vs '%s'\n", attr, license->details[i].resource);
847 #endif
849 if (!strcmp( current->name(),
850 license->details[i].name ) &&
851 !strcmp( attr,
852 license->details[i].resource )) {
853 matched[i]=TRUE;
854 found_match=TRUE;
855 #ifdef DEBUG_MATCH
856 printf("\t\tgood!\n");
857 #endif
858 break;
859 }
860 }
861 if (!found_match) {
862 // if we checked each known item of the license
863 // and didn't find it, we must abort
864 result=FALSE;
865 #ifdef DEBUG_MATCH
866 printf("\t\tno '%s' element matched XML (bong)!\n",license->name);
867 #endif
868 break;
869 }
870 }
871 #ifdef DEBUG_MATCH
872 if (result) printf("\t\tall XML found matching elements!\n");
873 #endif
874 for (int i=0; result && i<count; i++) {
875 // scan looking for an unmatched item
876 if (matched[i]==0) {
877 result=FALSE;
878 #ifdef DEBUG_MATCH
879 printf("\t\tnot all '%s' elements used to match (bong)!\n", license->name);
880 #endif
881 }
882 }
884 #ifdef DEBUG_MATCH
885 printf("\t\tall '%s' elements used to match!\n",license->name);
886 #endif
888 free(matched);
890 #ifdef DEBUG_MATCH
891 if (result) printf("matched '%s'\n",license->name);
892 #endif
893 return result;
894 }
896 /**
897 * \brief Attempts to match and retrieve a known RDF/License from the document XML
898 * \return A pointer to the static RDF license structure
899 *
900 */
901 struct rdf_license_t *
902 rdf_get_license(SPDocument * document)
903 {
904 Inkscape::XML::Node * repr = rdf_get_xml_repr ( document, XML_TAG_NAME_LICENSE, FALSE );
905 if (repr) {
906 for (struct rdf_license_t * license = rdf_licenses;
907 license->name; license++ ) {
908 if ( rdf_match_license ( repr, license ) ) return license;
909 }
910 }
911 #ifdef DEBUG_MATCH
912 else {
913 printf("no license XML\n");
914 }
915 #endif
916 return NULL;
917 }
919 /**
920 * \brief Stores an RDF/License XML in the document XML
921 * \param document Which document to update
922 * \param license The desired RDF/License structure to store; NULL drops old license, so can be used for proprietary license.
923 *
924 */
925 void
926 rdf_set_license(SPDocument * doc, struct rdf_license_t const * license)
927 {
928 // drop old license section
929 Inkscape::XML::Node * repr = rdf_get_xml_repr ( doc, XML_TAG_NAME_LICENSE, FALSE );
930 if (repr) sp_repr_unparent(repr);
932 if (!license) return;
934 // build new license section
935 repr = rdf_get_xml_repr ( doc, XML_TAG_NAME_LICENSE, TRUE );
936 g_assert ( repr );
938 repr->setAttribute("rdf:about", license->uri );
940 Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
941 g_return_if_fail (xmldoc != NULL);
943 for (struct rdf_double_t * detail = license->details;
944 detail->name; detail++) {
945 Inkscape::XML::Node * child = xmldoc->createElement( detail->name );
946 g_assert ( child != NULL );
948 child->setAttribute("rdf:resource", detail->resource );
949 repr->appendChild(child);
950 Inkscape::GC::release(child);
951 }
952 }
954 struct rdf_entity_default_t {
955 gchar const * name;
956 gchar const * text;
957 };
958 struct rdf_entity_default_t rdf_defaults[] = {
959 { "format", "image/svg+xml", },
960 { "type", "http://purl.org/dc/dcmitype/StillImage", },
961 { NULL, NULL, }
962 };
964 void
965 rdf_set_defaults ( SPDocument * doc )
966 {
967 g_assert ( doc != NULL );
969 // Create metadata node if it doesn't already exist
970 if (!sp_item_group_get_child_by_name ((SPGroup *) doc->root, NULL,
971 XML_TAG_NAME_METADATA)) {
972 // create repr
973 Inkscape::XML::Document * xmldoc = sp_document_repr_doc(doc);
974 g_return_if_fail (xmldoc != NULL);
975 Inkscape::XML::Node * rnew = xmldoc->createElement (XML_TAG_NAME_METADATA);
976 // insert into the document
977 doc->rroot->addChild(rnew, NULL);
978 // clean up
979 Inkscape::GC::release(rnew);
980 }
982 /* install defaults */
983 for ( struct rdf_entity_default_t * rdf_default = rdf_defaults;
984 rdf_default->name;
985 rdf_default++) {
986 struct rdf_work_entity_t * entity = rdf_find_entity ( rdf_default->name );
987 g_assert ( entity != NULL );
989 if ( rdf_get_work_entity ( doc, entity ) == NULL ) {
990 rdf_set_work_entity ( doc, entity, rdf_default->text );
991 }
992 }
993 }
996 /*
997 Local Variables:
998 mode:c++
999 c-file-style:"stroustrup"
1000 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1001 indent-tabs-mode:nil
1002 fill-column:99
1003 End:
1004 */
1005 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :