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 * Jon A. Cruz <jon@joncruz.org>
10 *
11 * Copyright (C) 2004 Kees Cook <kees@outflux.net>
12 * Copyright (C) 2006 Jon Phillips <jon@rejon.org>
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 #include "xml/repr.h"
18 #include "rdf.h"
19 #include "sp-item-group.h"
20 #include "inkscape.h"
22 /*
23 Example RDF XML from various places...
25 <rdf:RDF xmlns="http://creativecommons.org/ns#"
26 xmlns:dc="http://purl.org/dc/elements/1.1/"
27 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
28 <Work rdf:about="">
29 <dc:title>title of work</dc:title>
30 <dc:date>year</dc:date>
31 <dc:description>description of work</dc:description>
32 <dc:creator><Agent>
33 <dc:title>creator</dc:title>
34 </Agent></dc:creator>
35 <dc:rights><Agent>
36 <dc:title>holder</dc:title>
37 </Agent></dc:rights>
38 <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
39 <dc:source rdf:resource="source"/>
40 <license rdf:resource="http://creativecommons.org/licenses/by/2.0/"
41 />
42 </Work>
45 <rdf:RDF xmlns="http://creativecommons.org/ns#"
46 xmlns:dc="http://purl.org/dc/elements/1.1/"
47 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
48 <Work rdf:about="">
49 <dc:title>SVG Road Signs</dc:title>
50 <dc:rights><Agent>
51 <dc:title>John Cliff</dc:title>
52 </Agent></dc:rights>
53 <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
54 <license rdf:resource="http://creativecommons.org/ns#PublicDomain" />
55 </Work>
57 <License rdf:about="http://creativecommons.org/ns#PublicDomain">
58 <permits rdf:resource="http://creativecommons.org/ns#Reproduction" />
59 <permits rdf:resource="http://creativecommons.org/ns#Distribution" />
60 <permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
61 </License>
63 </rdf:RDF>
66 Bag example:
68 <dc:subject>
69 <rdf:Bag>
70 <rdf:li>open clip art logo</rdf:li>
71 <rdf:li>images</rdf:li>
72 <rdf:li>logo</rdf:li>
73 <rdf:li>clip art</rdf:li>
74 <rdf:li>ocal</rdf:li>
75 <rdf:li>logotype</rdf:li>
76 <rdf:li>filetype</rdf:li>
77 </rdf:Bag>
78 </dc:subject>
79 */
81 struct rdf_double_t rdf_license_empty [] = {
82 { NULL, NULL }
83 };
85 struct rdf_double_t rdf_license_cc_a [] = {
86 { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
87 { "cc:permits", "http://creativecommons.org/ns#Distribution", },
88 { "cc:requires", "http://creativecommons.org/ns#Notice", },
89 { "cc:requires", "http://creativecommons.org/ns#Attribution", },
90 { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
91 { NULL, NULL }
92 };
94 struct rdf_double_t rdf_license_cc_a_sa [] = {
95 { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
96 { "cc:permits", "http://creativecommons.org/ns#Distribution", },
97 { "cc:requires", "http://creativecommons.org/ns#Notice", },
98 { "cc:requires", "http://creativecommons.org/ns#Attribution", },
99 { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
100 { "cc:requires", "http://creativecommons.org/ns#ShareAlike", },
101 { NULL, NULL }
102 };
104 struct rdf_double_t rdf_license_cc_a_nd [] = {
105 { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
106 { "cc:permits", "http://creativecommons.org/ns#Distribution", },
107 { "cc:requires", "http://creativecommons.org/ns#Notice", },
108 { "cc:requires", "http://creativecommons.org/ns#Attribution", },
109 { NULL, NULL }
110 };
112 struct rdf_double_t rdf_license_cc_a_nc [] = {
113 { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
114 { "cc:permits", "http://creativecommons.org/ns#Distribution", },
115 { "cc:requires", "http://creativecommons.org/ns#Notice", },
116 { "cc:requires", "http://creativecommons.org/ns#Attribution", },
117 { "cc:prohibits", "http://creativecommons.org/ns#CommercialUse", },
118 { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
119 { NULL, NULL }
120 };
122 struct rdf_double_t rdf_license_cc_a_nc_sa [] = {
123 { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
124 { "cc:permits", "http://creativecommons.org/ns#Distribution", },
125 { "cc:requires", "http://creativecommons.org/ns#Notice", },
126 { "cc:requires", "http://creativecommons.org/ns#Attribution", },
127 { "cc:prohibits", "http://creativecommons.org/ns#CommercialUse", },
128 { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
129 { "cc:requires", "http://creativecommons.org/ns#ShareAlike", },
130 { NULL, NULL }
131 };
133 struct rdf_double_t rdf_license_cc_a_nc_nd [] = {
134 { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
135 { "cc:permits", "http://creativecommons.org/ns#Distribution", },
136 { "cc:requires", "http://creativecommons.org/ns#Notice", },
137 { "cc:requires", "http://creativecommons.org/ns#Attribution", },
138 { "cc:prohibits", "http://creativecommons.org/ns#CommercialUse", },
139 { NULL, NULL }
140 };
142 struct rdf_double_t rdf_license_pd [] = {
143 { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
144 { "cc:permits", "http://creativecommons.org/ns#Distribution", },
145 { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
146 { NULL, NULL }
147 };
149 struct rdf_double_t rdf_license_freeart [] = {
150 { "cc:permits", "http://creativecommons.org/ns#Reproduction", },
151 { "cc:permits", "http://creativecommons.org/ns#Distribution", },
152 { "cc:permits", "http://creativecommons.org/ns#DerivativeWorks", },
153 { "cc:requires", "http://creativecommons.org/ns#ShareAlike", },
154 { "cc:requires", "http://creativecommons.org/ns#Notice", },
155 { "cc:requires", "http://creativecommons.org/ns#Attribution", },
156 { NULL, NULL }
157 };
159 struct rdf_double_t rdf_license_ofl [] = {
160 { "cc:permits", "http://scripts.sil.org/pub/OFL/Reproduction", },
161 { "cc:permits", "http://scripts.sil.org/pub/OFL/Distribution", },
162 { "cc:permits", "http://scripts.sil.org/pub/OFL/Embedding", },
163 { "cc:permits", "http://scripts.sil.org/pub/OFL/DerivativeWorks", },
164 { "cc:requires", "http://scripts.sil.org/pub/OFL/Notice", },
165 { "cc:requires", "http://scripts.sil.org/pub/OFL/Attribution", },
166 { "cc:requires", "http://scripts.sil.org/pub/OFL/ShareAlike", },
167 { "cc:requires", "http://scripts.sil.org/pub/OFL/DerivativeRenaming", },
168 { "cc:requires", "http://scripts.sil.org/pub/OFL/BundlingWhenSelling", },
169 { NULL, NULL }
170 };
172 struct rdf_license_t rdf_licenses [] = {
173 { N_("CC Attribution"),
174 "http://creativecommons.org/licenses/by/3.0/",
175 rdf_license_cc_a,
176 },
178 { N_("CC Attribution-ShareAlike"),
179 "http://creativecommons.org/licenses/by-sa/3.0/",
180 rdf_license_cc_a_sa,
181 },
183 { N_("CC Attribution-NoDerivs"),
184 "http://creativecommons.org/licenses/by-nd/3.0/",
185 rdf_license_cc_a_nd,
186 },
188 { N_("CC Attribution-NonCommercial"),
189 "http://creativecommons.org/licenses/by-nc/3.0/",
190 rdf_license_cc_a_nc,
191 },
193 { N_("CC Attribution-NonCommercial-ShareAlike"),
194 "http://creativecommons.org/licenses/by-nc-sa/3.0/",
195 rdf_license_cc_a_nc_sa,
196 },
198 { N_("CC Attribution-NonCommercial-NoDerivs"),
199 "http://creativecommons.org/licenses/by-nc-nd/3.0/",
200 rdf_license_cc_a_nc_nd,
201 },
203 { N_("Public Domain"),
204 "http://creativecommons.org/licenses/publicdomain/",
205 rdf_license_pd,
206 },
208 { N_("FreeArt"),
209 "http://artlibre.org/licence/lal",
210 rdf_license_freeart,
211 },
213 { N_("Open Font License"),
214 "http://scripts.sil.org/OFL",
215 rdf_license_ofl,
216 },
218 { NULL, NULL, rdf_license_empty, }
219 };
221 #define XML_TAG_NAME_SVG "svg:svg"
222 #define XML_TAG_NAME_METADATA "svg:metadata"
223 #define XML_TAG_NAME_RDF "rdf:RDF"
224 #define XML_TAG_NAME_WORK "cc:Work"
225 #define XML_TAG_NAME_LICENSE "cc:License"
227 // Remember when using the "title" and "tip" elements to pass them through
228 // the localization functions when you use them!
229 struct rdf_work_entity_t rdf_work_entities [] = {
230 { "title", N_("Title:"), "dc:title", RDF_CONTENT,
231 N_("Name by which this document is formally known"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
232 },
233 { "date", N_("Date:"), "dc:date", RDF_CONTENT,
234 N_("Date associated with the creation of this document (YYYY-MM-DD)"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
235 },
236 { "format", N_("Format:"), "dc:format", RDF_CONTENT,
237 N_("The physical or digital manifestation of this document (MIME type)"), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
238 },
239 { "type", N_("Type:"), "dc:type", RDF_RESOURCE,
240 N_("Type of document (DCMI Type)"), RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
241 },
243 { "creator", N_("Creator:"), "dc:creator", RDF_AGENT,
244 N_("Name of entity primarily responsible for making the content of this document"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
245 },
246 { "rights", N_("Rights:"), "dc:rights", RDF_AGENT,
247 N_("Name of entity with rights to the Intellectual Property of this document"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
248 },
249 { "publisher", N_("Publisher:"), "dc:publisher", RDF_AGENT,
250 N_("Name of entity responsible for making this document available"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
251 },
253 { "identifier", N_("Identifier:"), "dc:identifier", RDF_CONTENT,
254 N_("Unique URI to reference this document"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
255 },
256 { "source", N_("Source:"), "dc:source", RDF_CONTENT,
257 N_("Unique URI to reference the source of this document"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
258 },
259 { "relation", N_("Relation:"), "dc:relation", RDF_CONTENT,
260 N_("Unique URI to a related document"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
261 },
262 { "language", N_("Language:"), "dc:language", RDF_CONTENT,
263 N_("Two-letter language tag with optional subtags for the language of this document (e.g. 'en-GB')"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
264 },
265 { "subject", N_("Keywords:"), "dc:subject", RDF_BAG,
266 N_("The topic of this document as comma-separated key words, phrases, or classifications"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
267 },
268 // TRANSLATORS: "Coverage": the spatial or temporal characteristics of the content.
269 // For info, see Appendix D of http://www.w3.org/TR/1998/WD-rdf-schema-19980409/
270 { "coverage", N_("Coverage:"), "dc:coverage", RDF_CONTENT,
271 N_("Extent or scope of this document"), RDF_FORMAT_LINE, RDF_EDIT_GENERIC,
272 },
274 { "description", N_("Description:"), "dc:description", RDF_CONTENT,
275 N_("A short account of the content of this document"), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC,
276 },
278 // FIXME: need to handle 1 agent per line of input
279 { "contributor", N_("Contributors:"), "dc:contributor", RDF_AGENT,
280 N_("Names of entities responsible for making contributions to the content of this document"), RDF_FORMAT_MULTILINE, RDF_EDIT_GENERIC,
281 },
283 // TRANSLATORS: URL to a page that defines the license for the document
284 { "license_uri", N_("URI:"), "cc:license", RDF_RESOURCE,
285 // TRANSLATORS: this is where you put a URL to a page that defines the license
286 N_("URI to this document's license's namespace definition"), RDF_FORMAT_LINE, RDF_EDIT_SPECIAL,
287 },
289 // TRANSLATORS: fragment of XML representing the license of the document
290 { "license_fragment", N_("Fragment:"), "License", RDF_XML,
291 N_("XML fragment for the RDF 'License' section"), RDF_FORMAT_MULTILINE, RDF_EDIT_SPECIAL,
292 },
294 { NULL, NULL, NULL, RDF_CONTENT,
295 NULL, RDF_FORMAT_LINE, RDF_EDIT_HARDCODED,
296 }
297 };
300 // Simple start of C++-ification:
301 class RDFImpl
302 {
303 public:
304 /**
305 * Some implementations do not put RDF stuff inside <metadata>,
306 * so we need to check for it and add it if we don't see it.
307 */
308 static void ensureParentIsMetadata( SPDocument *doc, Inkscape::XML::Node *node );
310 static Inkscape::XML::Node const *getRdfRootRepr( SPDocument const * doc );
311 static Inkscape::XML::Node *ensureRdfRootRepr( SPDocument * doc );
313 static Inkscape::XML::Node const *getXmlRepr( SPDocument const * doc, gchar const * name );
314 static Inkscape::XML::Node *getXmlRepr( SPDocument * doc, gchar const * name );
315 static Inkscape::XML::Node *ensureXmlRepr( SPDocument * doc, gchar const * name );
317 static Inkscape::XML::Node const *getWorkRepr( SPDocument const * doc, gchar const * name );
318 static Inkscape::XML::Node *ensureWorkRepr( SPDocument * doc, gchar const * name );
320 static const gchar *getWorkEntity(SPDocument const * doc, struct rdf_work_entity_t & entity);
321 static unsigned int setWorkEntity(SPDocument * doc, struct rdf_work_entity_t & entity, gchar const * text);
323 static void setDefaults( SPDocument * doc );
325 /**
326 * \brief Pull the text out of an RDF entity, depends on how it's stored
327 * \return A pointer to the entity's static contents as a string
328 * \param repr The XML element to extract from
329 * \param entity The desired RDF/Work entity
330 *
331 */
332 static const gchar *getReprText( Inkscape::XML::Node const * repr, struct rdf_work_entity_t const & entity );
334 static unsigned int setReprText( Inkscape::XML::Node * repr,
335 struct rdf_work_entity_t const & entity,
336 gchar const * text );
338 static struct rdf_license_t *getLicense(SPDocument const * document);
340 static void setLicense(SPDocument * doc, struct rdf_license_t const * license);
341 };
343 /**
344 * \brief Retrieves a known RDF/Work entity by name
345 * \return A pointer to an RDF/Work entity
346 * \param name The desired RDF/Work entity
347 *
348 */
349 struct rdf_work_entity_t *
350 rdf_find_entity(gchar const * name)
351 {
352 struct rdf_work_entity_t *entity;
353 for (entity=rdf_work_entities; entity->name; entity++) {
354 if (strcmp(entity->name,name)==0) break;
355 }
356 if (entity->name) return entity;
357 return NULL;
358 }
360 /*
361 * Takes the inkscape rdf struct and spits out a static RDF, which is only
362 * useful for testing. We must merge the rdf struct into the XML DOM for
363 * changes to be saved.
364 */
365 /*
367 Since g_markup_printf_escaped doesn't exist for most people's glib
368 right now, this function will remain commented out since it's only
369 for generic debug anyway. --Kees
371 gchar *
372 rdf_string(struct rdf_t * rdf)
373 {
374 gulong overall=0;
375 gchar *string=NULL;
377 gchar *rdf_head="\
378 <rdf:RDF xmlns=\"http://creativecommons.org/ns#\"\
379 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\
380 xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\
381 ";
382 gchar *work_head="\
383 <Work rdf:about=\"\">\
384 <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />\
385 ";
386 gchar *work_title=NULL;
387 gchar *work_date=NULL;
388 gchar *work_description=NULL;
389 gchar *work_creator=NULL;
390 gchar *work_owner=NULL;
391 gchar *work_source=NULL;
392 gchar *work_license=NULL;
393 gchar *license_head=NULL;
394 gchar *license=NULL;
395 gchar *license_end="</License>\n";
396 gchar *work_end="</Work>\n";
397 gchar *rdf_end="</rdf:RDF>\n";
399 if (rdf && rdf->work_title && rdf->work_title[0]) {
400 work_title=g_markup_printf_escaped(" <dc:title>%s</dc:title>\n",
401 rdf->work_title);
402 overall+=strlen(work_title);
403 }
404 if (rdf && rdf->work_date && rdf->work_date[0]) {
405 work_date=g_markup_printf_escaped(" <dc:date>%s</dc:date>\n",
406 rdf->work_date);
407 overall+=strlen(work_date);
408 }
409 if (rdf && rdf->work_description && rdf->work_description[0]) {
410 work_description=g_markup_printf_escaped(" <dc:description>%s</dc:description>\n",
411 rdf->work_description);
412 overall+=strlen(work_description);
413 }
414 if (rdf && rdf->work_creator && rdf->work_creator[0]) {
415 work_creator=g_markup_printf_escaped(" <dc:creator><Agent>\
416 <dc:title>%s</dc:title>\
417 </Agent></dc:creator>\n",
418 rdf->work_creator);
419 overall+=strlen(work_creator);
420 }
421 if (rdf && rdf->work_owner && rdf->work_owner[0]) {
422 work_owner=g_markup_printf_escaped(" <dc:rights><Agent>\
423 <dc:title>%s</dc:title>\
424 </Agent></dc:rights>\n",
425 rdf->work_owner);
426 overall+=strlen(work_owner);
427 }
428 if (rdf && rdf->work_source && rdf->work_source[0]) {
429 work_source=g_markup_printf_escaped(" <dc:source rdf:resource=\"%s\" />\n",
430 rdf->work_source);
431 overall+=strlen(work_source);
432 }
433 if (rdf && rdf->license && rdf->license->work_rdf && rdf->license->work_rdf[0]) {
434 work_license=g_markup_printf_escaped(" <license rdf:resource=\"%s\" />\n",
435 rdf->license->work_rdf);
436 overall+=strlen(work_license);
438 license_head=g_markup_printf_escaped("<License rdf:about=\"%s\">\n",
439 rdf->license->work_rdf);
440 overall+=strlen(license_head);
441 overall+=strlen(rdf->license->license_rdf);
442 overall+=strlen(license_end);
443 }
445 overall+=strlen(rdf_head)+strlen(rdf_end);
446 overall+=strlen(work_head)+strlen(work_end);
448 overall++; // NULL term
450 if (!(string=(gchar*)g_malloc(overall))) {
451 return NULL;
452 }
454 string[0]='\0';
455 strcat(string,rdf_head);
456 strcat(string,work_head);
458 if (work_title) strcat(string,work_title);
459 if (work_date) strcat(string,work_date);
460 if (work_description) strcat(string,work_description);
461 if (work_creator) strcat(string,work_creator);
462 if (work_owner) strcat(string,work_owner);
463 if (work_source) strcat(string,work_source);
464 if (work_license) strcat(string,work_license);
466 strcat(string,work_end);
467 if (license_head) {
468 strcat(string,license_head);
469 strcat(string,rdf->license->license_rdf);
470 strcat(string,license_end);
471 }
472 strcat(string,rdf_end);
474 return string;
475 }
476 */
479 const gchar *RDFImpl::getReprText( Inkscape::XML::Node const * repr, struct rdf_work_entity_t const & entity )
480 {
481 g_return_val_if_fail (repr != NULL, NULL);
482 static gchar * bag = NULL;
483 gchar * holder = NULL;
485 Inkscape::XML::Node const * temp = NULL;
486 switch (entity.datatype) {
487 case RDF_CONTENT:
488 temp = sp_repr_children(repr);
489 if ( temp == NULL ) return NULL;
491 return temp->content();
493 case RDF_AGENT:
494 temp = sp_repr_lookup_name ( repr, "cc:Agent", 1 );
495 if ( temp == NULL ) return NULL;
497 temp = sp_repr_lookup_name ( temp, "dc:title", 1 );
498 if ( temp == NULL ) return NULL;
500 temp = sp_repr_children(temp);
501 if ( temp == NULL ) return NULL;
503 return temp->content();
505 case RDF_RESOURCE:
506 return repr->attribute("rdf:resource");
508 case RDF_XML:
509 return "xml goes here";
511 case RDF_BAG:
512 /* clear the static string. yucky. */
513 if (bag) g_free(bag);
514 bag = NULL;
516 temp = sp_repr_lookup_name ( repr, "rdf:Bag", 1 );
517 if ( temp == NULL ) {
518 /* backwards compatible: read contents */
519 temp = sp_repr_children(repr);
520 if ( temp == NULL ) return NULL;
522 return temp->content();
523 }
525 for ( temp = sp_repr_children(temp) ;
526 temp ;
527 temp = sp_repr_next(temp) ) {
528 if (!strcmp(temp->name(),"rdf:li") &&
529 temp->firstChild()) {
530 const gchar * str = temp->firstChild()->content();
531 if (bag) {
532 holder = bag;
533 bag = g_strconcat(holder, ", ", str, NULL);
534 g_free(holder);
535 }
536 else {
537 bag = g_strdup(str);
538 }
539 }
540 }
541 return bag;
543 default:
544 break;
545 }
546 return NULL;
547 }
549 unsigned int RDFImpl::setReprText( Inkscape::XML::Node * repr,
550 struct rdf_work_entity_t const & entity,
551 gchar const * text )
552 {
553 g_return_val_if_fail ( repr != NULL, 0);
554 g_return_val_if_fail ( text != NULL, 0);
555 gchar * str = NULL;
556 gchar** strlist = NULL;
557 int i;
559 Inkscape::XML::Node * temp=NULL;
560 Inkscape::XML::Node * child=NULL;
561 Inkscape::XML::Node * parent=repr;
563 Inkscape::XML::Document * xmldoc = parent->document();
564 g_return_val_if_fail (xmldoc != NULL, FALSE);
566 // set document's title element to the RDF title
567 if (!strcmp(entity.name, "title")) {
568 SPDocument *doc = SP_ACTIVE_DOCUMENT;
569 if(doc && doc->root) doc->root->setTitle(text);
570 }
572 switch (entity.datatype) {
573 case RDF_CONTENT:
574 temp = sp_repr_children(parent);
575 if ( temp == NULL ) {
576 temp = xmldoc->createTextNode( text );
577 g_return_val_if_fail (temp != NULL, FALSE);
579 parent->appendChild(temp);
580 Inkscape::GC::release(temp);
582 return TRUE;
583 }
584 else {
585 temp->setContent(text);
586 return TRUE;
587 }
589 case RDF_AGENT:
590 temp = sp_repr_lookup_name ( parent, "cc:Agent", 1 );
591 if ( temp == NULL ) {
592 temp = xmldoc->createElement ( "cc:Agent" );
593 g_return_val_if_fail (temp != NULL, FALSE);
595 parent->appendChild(temp);
596 Inkscape::GC::release(temp);
597 }
598 parent = temp;
600 temp = sp_repr_lookup_name ( parent, "dc:title", 1 );
601 if ( temp == NULL ) {
602 temp = xmldoc->createElement ( "dc:title" );
603 g_return_val_if_fail (temp != NULL, FALSE);
605 parent->appendChild(temp);
606 Inkscape::GC::release(temp);
607 }
608 parent = temp;
610 temp = sp_repr_children(parent);
611 if ( temp == NULL ) {
612 temp = xmldoc->createTextNode( text );
613 g_return_val_if_fail (temp != NULL, FALSE);
615 parent->appendChild(temp);
616 Inkscape::GC::release(temp);
618 return TRUE;
619 }
620 else {
621 temp->setContent(text);
622 return TRUE;
623 }
625 case RDF_RESOURCE:
626 parent->setAttribute("rdf:resource", text );
627 return true;
629 case RDF_XML:
630 return 1;
632 case RDF_BAG:
633 /* find/create the rdf:Bag item */
634 temp = sp_repr_lookup_name ( parent, "rdf:Bag", 1 );
635 if ( temp == NULL ) {
636 /* backward compatibility: drop the dc:subject contents */
637 while ( (temp = sp_repr_children( parent )) ) {
638 parent->removeChild(temp);
639 }
641 temp = xmldoc->createElement ( "rdf:Bag" );
642 g_return_val_if_fail (temp != NULL, FALSE);
644 parent->appendChild(temp);
645 Inkscape::GC::release(temp);
646 }
647 parent = temp;
649 /* toss all the old list items */
650 while ( (temp = sp_repr_children( parent )) ) {
651 parent->removeChild(temp);
652 }
654 /* chop our list up on commas */
655 strlist = g_strsplit( text, ",", 0);
657 for (i = 0; (str = strlist[i]); i++) {
658 temp = xmldoc->createElement ( "rdf:li" );
659 g_return_val_if_fail (temp != NULL, 0);
661 parent->appendChild(temp);
662 Inkscape::GC::release(temp);
664 child = xmldoc->createTextNode( g_strstrip(str) );
665 g_return_val_if_fail (child != NULL, 0);
667 temp->appendChild(child);
668 Inkscape::GC::release(child);
669 }
670 g_strfreev( strlist );
672 return 1;
674 default:
675 break;
676 }
677 return 0;
678 }
680 void RDFImpl::ensureParentIsMetadata( SPDocument *doc, Inkscape::XML::Node *node )
681 {
682 if ( !node ) {
683 g_critical("Null node passed to ensureParentIsMetadata().");
684 } else if ( !node->parent() ) {
685 g_critical( "No parent node when verifying <metadata> placement." );
686 } else {
687 Inkscape::XML::Node * currentParent = node->parent();
688 if ( strcmp( currentParent->name(), XML_TAG_NAME_METADATA ) != 0 ) {
689 Inkscape::XML::Node * metadata = doc->getReprDoc()->createElement( XML_TAG_NAME_METADATA );
690 if ( !metadata ) {
691 g_critical("Unable to create metadata element.");
692 } else {
693 // attach the metadata node
694 currentParent->appendChild( metadata );
695 Inkscape::GC::release( metadata );
697 // move the node into it
698 Inkscape::GC::anchor( node );
699 sp_repr_unparent( node );
700 metadata->appendChild( node );
701 Inkscape::GC::release( node );
702 }
703 }
704 }
705 }
707 Inkscape::XML::Node const *RDFImpl::getRdfRootRepr( SPDocument const * doc )
708 {
709 Inkscape::XML::Node const *rdf = 0;
710 if ( !doc ) {
711 g_critical("Null doc passed to getRdfRootRepr()");
712 } else if ( !doc->getReprDoc() ) {
713 g_critical("XML doc is null.");
714 } else {
715 rdf = sp_repr_lookup_name( doc->getReprDoc(), XML_TAG_NAME_RDF );
716 }
718 return rdf;
719 }
721 Inkscape::XML::Node *RDFImpl::ensureRdfRootRepr( SPDocument * doc )
722 {
723 Inkscape::XML::Node *rdf = 0;
724 if ( !doc ) {
725 g_critical("Null doc passed to ensureRdfRootRepr()");
726 } else if ( !doc->getReprDoc() ) {
727 g_critical("XML doc is null.");
728 } else {
729 rdf = sp_repr_lookup_name( doc->getReprDoc(), XML_TAG_NAME_RDF );
730 if ( !rdf ) {
731 Inkscape::XML::Node * svg = sp_repr_lookup_name( doc->getReprRoot(), XML_TAG_NAME_SVG );
732 if ( !svg ) {
733 g_critical("Unable to locate svg element.");
734 } else {
735 Inkscape::XML::Node * parent = sp_repr_lookup_name( svg, XML_TAG_NAME_METADATA );
736 if ( parent == NULL ) {
737 parent = doc->getReprDoc()->createElement( XML_TAG_NAME_METADATA );
738 if ( !parent ) {
739 g_critical("Unable to create metadata element");
740 } else {
741 svg->appendChild(parent);
742 Inkscape::GC::release(parent);
743 }
744 }
746 if ( parent && !parent->document() ) {
747 g_critical("Parent has no document");
748 } else if ( parent ) {
749 rdf = parent->document()->createElement( XML_TAG_NAME_RDF );
750 if ( !rdf ) {
751 g_critical("Unable to create root RDF element.");
752 } else {
753 parent->appendChild(rdf);
754 Inkscape::GC::release(rdf);
755 }
756 }
757 }
758 }
759 }
761 if ( rdf ) {
762 ensureParentIsMetadata( doc, rdf );
763 }
765 return rdf;
766 }
768 Inkscape::XML::Node const *RDFImpl::getXmlRepr( SPDocument const * doc, gchar const * name )
769 {
770 Inkscape::XML::Node const * xml = 0;
771 if ( !doc ) {
772 g_critical("Null doc passed to getXmlRepr()");
773 } else if ( !doc->getReprDoc() ) {
774 g_critical("XML doc is null.");
775 } else if (!name) {
776 g_critical("Null name passed to getXmlRepr()");
777 } else {
778 Inkscape::XML::Node const * rdf = getRdfRootRepr( doc );
779 if ( rdf ) {
780 xml = sp_repr_lookup_name( rdf, name );
781 }
782 }
783 return xml;
784 }
786 Inkscape::XML::Node *RDFImpl::getXmlRepr( SPDocument * doc, gchar const * name )
787 {
788 Inkscape::XML::Node const *xml = getXmlRepr( const_cast<SPDocument const *>(doc), name );
790 return const_cast<Inkscape::XML::Node *>(xml);
791 }
793 Inkscape::XML::Node *RDFImpl::ensureXmlRepr( SPDocument * doc, gchar const * name )
794 {
795 Inkscape::XML::Node * xml = 0;
796 if ( !doc ) {
797 g_critical("Null doc passed to ensureXmlRepr()");
798 } else if ( !doc->getReprDoc() ) {
799 g_critical("XML doc is null.");
800 } else if (!name) {
801 g_critical("Null name passed to ensureXmlRepr()");
802 } else {
803 Inkscape::XML::Node * rdf = ensureRdfRootRepr( doc );
804 if ( rdf ) {
805 xml = sp_repr_lookup_name( rdf, name );
806 if ( !xml ) {
807 xml = doc->getReprDoc()->createElement( name );
808 if ( !xml ) {
809 g_critical("Unable to create xml element <%s>.", name);
810 } else {
811 xml->setAttribute("rdf:about", "" );
813 rdf->appendChild(xml);
814 Inkscape::GC::release(xml);
815 }
816 }
817 }
818 }
819 return xml;
820 }
822 Inkscape::XML::Node const *RDFImpl::getWorkRepr( SPDocument const * doc, gchar const * name )
823 {
824 Inkscape::XML::Node const * item = 0;
825 if ( !doc ) {
826 g_critical("Null doc passed to getWorkRepr()");
827 } else if ( !doc->getReprDoc() ) {
828 g_critical("XML doc is null.");
829 } else if (!name) {
830 g_critical("Null name passed to getWorkRepr()");
831 } else {
832 Inkscape::XML::Node const* work = getXmlRepr( doc, XML_TAG_NAME_WORK );
833 if ( work ) {
834 item = sp_repr_lookup_name( work, name, 1 );
835 }
836 }
837 return item;
838 }
840 Inkscape::XML::Node *RDFImpl::ensureWorkRepr( SPDocument * doc, gchar const * name )
841 {
842 Inkscape::XML::Node * item = 0;
843 if ( !doc ) {
844 g_critical("Null doc passed to ensureWorkRepr()");
845 } else if ( !doc->getReprDoc() ) {
846 g_critical("XML doc is null.");
847 } else if (!name) {
848 g_critical("Null name passed to ensureWorkRepr()");
849 } else {
850 Inkscape::XML::Node * work = ensureXmlRepr( doc, XML_TAG_NAME_WORK );
851 if ( work ) {
852 item = sp_repr_lookup_name( work, name, 1 );
853 if ( !item ) {
854 //printf("missing XML '%s'\n",name);
855 item = doc->getReprDoc()->createElement( name );
856 if ( !item ) {
857 g_critical("Unable to create xml element <%s>", name);
858 } else {
859 work->appendChild(item);
860 Inkscape::GC::release(item);
861 }
862 }
863 }
864 }
865 return item;
866 }
869 // Public API:
870 const gchar *rdf_get_work_entity(SPDocument const * doc, struct rdf_work_entity_t * entity)
871 {
872 const gchar *result = 0;
873 if ( !doc ) {
874 g_critical("Null doc passed to rdf_get_work_entity()");
875 } else if ( entity ) {
876 //g_message("want '%s'\n",entity->title);
878 result = RDFImpl::getWorkEntity( doc, *entity );
880 //g_message("found '%s' == '%s'\n", entity->title, result );
881 }
882 return result;
883 }
885 const gchar *RDFImpl::getWorkEntity(SPDocument const * doc, struct rdf_work_entity_t & entity)
886 {
887 gchar const *result = 0;
889 Inkscape::XML::Node const * item = getWorkRepr( doc, entity.tag );
890 if ( item ) {
891 result = getReprText( item, entity );
892 // TODO note that this is the location that used to set the title if needed. Ensure code it not required.
893 }
895 return result;
896 }
898 // Public API:
899 unsigned int rdf_set_work_entity(SPDocument * doc, struct rdf_work_entity_t * entity,
900 const gchar * text)
901 {
902 unsigned int result = 0;
903 if ( !doc ) {
904 g_critical("Null doc passed to rdf_set_work_entity()");
905 } else if ( entity ) {
906 result = RDFImpl::setWorkEntity( doc, *entity, text );
907 }
909 return result;
910 }
912 unsigned int RDFImpl::setWorkEntity(SPDocument * doc, struct rdf_work_entity_t & entity, const gchar * text)
913 {
914 int result = 0;
915 if ( !text ) {
916 // FIXME: on a "NULL" text, delete the entity. For now, blank it.
917 text = "";
918 }
920 /*
921 printf("changing '%s' (%s) to '%s'\n",
922 entity->title,
923 entity->tag,
924 text);
925 */
927 Inkscape::XML::Node * item = ensureWorkRepr( doc, entity.tag );
928 if ( !item ) {
929 g_critical("Unable to get work element");
930 } else {
931 result = setReprText( item, entity, text );
932 }
933 return result;
934 }
937 #undef DEBUG_MATCH
939 static bool
940 rdf_match_license(Inkscape::XML::Node const *repr, struct rdf_license_t const *license)
941 {
942 g_assert ( repr != NULL );
943 g_assert ( license != NULL );
945 bool result=TRUE;
946 #ifdef DEBUG_MATCH
947 printf("checking against '%s'\n",license->name);
948 #endif
950 int count = 0;
951 for (struct rdf_double_t const *details = license->details;
952 details->name; details++ ) {
953 count++;
954 }
955 bool * matched = (bool*)calloc(count,sizeof(bool));
957 for (Inkscape::XML::Node const *current = sp_repr_children(repr);
958 current;
959 current = sp_repr_next ( current ) ) {
961 gchar const * attr = current->attribute("rdf:resource");
962 if ( attr == NULL ) continue;
964 #ifdef DEBUG_MATCH
965 printf("\texamining '%s' => '%s'\n", current->name(), attr);
966 #endif
968 bool found_match=FALSE;
969 for (int i=0; i<count; i++) {
970 // skip already matched items
971 if (matched[i]) continue;
973 #ifdef DEBUG_MATCH
974 printf("\t\t'%s' vs '%s'\n", current->name(), license->details[i].name);
975 printf("\t\t'%s' vs '%s'\n", attr, license->details[i].resource);
976 #endif
978 if (!strcmp( current->name(),
979 license->details[i].name ) &&
980 !strcmp( attr,
981 license->details[i].resource )) {
982 matched[i]=TRUE;
983 found_match=TRUE;
984 #ifdef DEBUG_MATCH
985 printf("\t\tgood!\n");
986 #endif
987 break;
988 }
989 }
990 if (!found_match) {
991 // if we checked each known item of the license
992 // and didn't find it, we must abort
993 result=FALSE;
994 #ifdef DEBUG_MATCH
995 printf("\t\tno '%s' element matched XML (bong)!\n",license->name);
996 #endif
997 break;
998 }
999 }
1000 #ifdef DEBUG_MATCH
1001 if (result) printf("\t\tall XML found matching elements!\n");
1002 #endif
1003 for (int i=0; result && i<count; i++) {
1004 // scan looking for an unmatched item
1005 if (matched[i]==0) {
1006 result=FALSE;
1007 #ifdef DEBUG_MATCH
1008 printf("\t\tnot all '%s' elements used to match (bong)!\n", license->name);
1009 #endif
1010 }
1011 }
1013 #ifdef DEBUG_MATCH
1014 printf("\t\tall '%s' elements used to match!\n",license->name);
1015 #endif
1017 free(matched);
1019 #ifdef DEBUG_MATCH
1020 if (result) printf("matched '%s'\n",license->name);
1021 #endif
1022 return result;
1023 }
1025 // Public API:
1026 struct rdf_license_t *rdf_get_license(SPDocument const * document)
1027 {
1028 return RDFImpl::getLicense(document);
1029 }
1031 struct rdf_license_t *RDFImpl::getLicense(SPDocument const *document)
1032 {
1033 Inkscape::XML::Node const *repr = getXmlRepr( document, XML_TAG_NAME_LICENSE );
1034 if (repr) {
1035 for ( struct rdf_license_t * license = rdf_licenses; license->name; license++ ) {
1036 if ( rdf_match_license( repr, license ) ) {
1037 return license;
1038 }
1039 }
1040 }
1041 #ifdef DEBUG_MATCH
1042 else {
1043 printf("no license XML\n");
1044 }
1045 #endif
1046 return NULL;
1047 }
1049 // Public API:
1050 void rdf_set_license(SPDocument * doc, struct rdf_license_t const * license)
1051 {
1052 RDFImpl::setLicense( doc, license );
1053 }
1055 void RDFImpl::setLicense(SPDocument * doc, struct rdf_license_t const * license)
1056 {
1057 // drop old license section
1058 Inkscape::XML::Node * repr = getXmlRepr( doc, XML_TAG_NAME_LICENSE );
1059 if (repr) {
1060 sp_repr_unparent(repr);
1061 }
1063 if ( !license ) {
1064 // All done
1065 } else if ( !doc->getReprDoc() ) {
1066 g_critical("XML doc is null.");
1067 } else {
1068 // build new license section
1069 repr = ensureXmlRepr( doc, XML_TAG_NAME_LICENSE );
1070 g_assert( repr );
1072 repr->setAttribute("rdf:about", license->uri );
1074 for (struct rdf_double_t const * detail = license->details; detail->name; detail++) {
1075 Inkscape::XML::Node * child = doc->getReprDoc()->createElement( detail->name );
1076 g_assert ( child != NULL );
1078 child->setAttribute("rdf:resource", detail->resource );
1079 repr->appendChild(child);
1080 Inkscape::GC::release(child);
1081 }
1082 }
1083 }
1085 struct rdf_entity_default_t {
1086 gchar const * name;
1087 gchar const * text;
1088 };
1089 struct rdf_entity_default_t rdf_defaults[] = {
1090 { "format", "image/svg+xml", },
1091 { "type", "http://purl.org/dc/dcmitype/StillImage", },
1092 { NULL, NULL, }
1093 };
1095 // Public API:
1096 void rdf_set_defaults( SPDocument * doc )
1097 {
1098 RDFImpl::setDefaults( doc );
1100 }
1102 void RDFImpl::setDefaults( SPDocument * doc )
1103 {
1104 g_assert( doc != NULL );
1106 // Create metadata node if it doesn't already exist
1107 if (!sp_item_group_get_child_by_name((SPGroup *) doc->root, NULL,
1108 XML_TAG_NAME_METADATA)) {
1109 if ( !doc->getReprDoc()) {
1110 g_critical("XML doc is null.");
1111 } else {
1112 // create repr
1113 Inkscape::XML::Node * rnew = doc->getReprDoc()->createElement(XML_TAG_NAME_METADATA);
1115 // insert into the document
1116 doc->getReprRoot()->addChild(rnew, NULL);
1118 // clean up
1119 Inkscape::GC::release(rnew);
1120 }
1121 }
1123 // install defaults
1124 for ( struct rdf_entity_default_t * rdf_default = rdf_defaults; rdf_default->name; rdf_default++) {
1125 struct rdf_work_entity_t * entity = rdf_find_entity( rdf_default->name );
1126 g_assert( entity != NULL );
1128 if ( getWorkEntity( doc, *entity ) == NULL ) {
1129 setWorkEntity( doc, *entity, rdf_default->text );
1130 }
1131 }
1132 }
1135 /*
1136 Local Variables:
1137 mode:c++
1138 c-file-style:"stroustrup"
1139 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1140 indent-tabs-mode:nil
1141 fill-column:99
1142 End:
1143 */
1144 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :