2006-12-03 Dimitris Glezos <dimitris@glezos.com>
[dia.git] / lib / dia_xml.c
blob110fdb8caea5ec4f427e306f42fc2645043f8d00
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998,1999 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 /** \file dia_xml.c Helper function to convert Dia's basic to and from XML */
19 #include <config.h>
21 #include <glib.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <math.h>
26 #include <fcntl.h>
28 #include <libxml/parser.h>
29 #include <libxml/parserInternals.h>
30 #include <libxml/xmlmemory.h>
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
36 #include <zlib.h>
38 #include "intl.h"
39 #include "utils.h"
40 #include "dia_xml_libxml.h"
41 #include "dia_xml.h"
42 #include "geometry.h" /* For isinf() on Solaris */
43 #include "message.h"
45 #ifdef G_OS_WIN32
46 #include <io.h> /* write, close */
47 #endif
49 #ifdef G_OS_WIN32 /* apparently _MSC_VER and mingw */
50 #include <float.h>
51 # ifndef isinf
52 # define isinf(a) (!_finite(a))
53 # endif
54 #endif
56 #define BUFLEN 1024
58 /** If all files produced by dia were good XML files, we wouldn't have to do
59 * this little gymnastic. Alas, during the libxml1 days, we were outputting
60 * files with no encoding specification (which means UTF-8 if we're in an
61 * asciish encoding) and strings encoded in local charset (so, we wrote
62 * broken files).
64 * The following logic finds if we have a broken file, and attempts to fix
65 * it if it's possible. If the file is correct or is unrecognisable, we pass
66 * it untouched to libxml2.
67 * @param filename The name of the file to check.
68 * @param default_enc The default encoding to use if none is given.
69 * @return The filename given if it seems ok, or the name of a new file
70 * with fixed contents, or NULL if we couldn't read the file. The
71 * caller should free this string and unlink the file if it is not
72 * the same as `filename'.
73 * @bug The many gzclose-g_free-return sequences should be refactored into
74 * an "exception handle" (goto+label). At least for people who think goto is
75 * better than this. I dont. --hb
77 static const gchar *
78 xml_file_check_encoding(const gchar *filename, const gchar *default_enc)
80 gzFile zf = gzopen(filename,"rb");
81 gchar *buf;
82 gchar *p,*pmax;
83 int len;
84 gchar *tmp,*res;
85 int uf;
86 gboolean well_formed_utf8;
88 static char magic_xml[] =
89 {0x3c,0x3f,0x78,0x6d,0x6c,0x00}; /* "<?xml" in ASCII */
91 if (!zf) {
92 /* message_error(_("The file %s can not be opened for reading"),filename); */
93 /* XXX perhaps we can just chicken out to libxml ? -- CC */
94 return NULL;
96 p = buf = g_malloc0(BUFLEN);
97 len = gzread(zf,buf,BUFLEN);
98 pmax = p + len;
100 /* first, we expect the magic <?xml string */
101 if ((0 != strncmp(p,magic_xml,5)) || (len < 5)) {
102 gzclose(zf);
103 g_free(buf);
104 return filename; /* let libxml figure out what this is. */
106 /* now, we're sure we have some asciish XML file. */
107 p += 5;
108 while (((*p == 0x20)||(*p == 0x09)||(*p == 0x0d)||(*p == 0x0a))
109 && (p<pmax)) p++;
110 if (p>=pmax) { /* whoops ? */
111 gzclose(zf);
112 g_free(buf);
113 return filename;
115 if (0 != strncmp(p,"version=\"",9)) {
116 gzclose(zf); /* chicken out. */
117 g_free(buf);
118 return filename;
120 p += 9;
121 /* The header is rather well formed. */
122 if (p>=pmax) { /* whoops ? */
123 gzclose(zf);
124 g_free(buf);
125 return filename;
127 while ((*p != '"') && (p < pmax)) p++;
128 p++;
129 while (((*p == 0x20)||(*p == 0x09)||(*p == 0x0d)||(*p == 0x0a))
130 && (p<pmax)) p++;
131 if (p>=pmax) { /* whoops ? */
132 gzclose(zf);
133 g_free(buf);
134 return filename;
136 if (0 == strncmp(p,"encoding=\"",10)) {
137 gzclose(zf); /* this file has an encoding string. Good. */
138 g_free(buf);
139 return filename;
141 /* now let's read the whole file, to see if there are offending bits.
142 * We can call it well formed UTF-8 if the highest isn't used
144 well_formed_utf8 = TRUE;
145 do {
146 int i;
147 for (i = 0; i < len; i++)
148 if (buf[i] & 0x80 || buf[i] == '&')
149 well_formed_utf8 = FALSE;
150 len = gzread(zf,buf,BUFLEN);
151 } while (len > 0 && well_formed_utf8);
152 if (well_formed_utf8) {
153 gzclose(zf); /* this file is utf-8 compatible */
154 g_free(buf);
155 return filename;
156 } else {
157 gzclose(zf); /* poor man's fseek */
158 zf = gzopen(filename,"rb");
159 len = gzread(zf,buf,BUFLEN);
162 if (0 != strcmp(default_enc,"UTF-8")) {
163 message_warning(_("The file %s has no encoding specification;\n"
164 "assuming it is encoded in %s"),
165 dia_message_filename(filename), default_enc);
166 } else {
167 gzclose(zf); /* we apply the standard here. */
168 g_free(buf);
169 return filename;
172 tmp = getenv("TMP");
173 if (!tmp) tmp = getenv("TEMP");
174 if (!tmp) tmp = "/tmp";
176 res = g_strconcat(tmp,G_DIR_SEPARATOR_S,"dia-xml-fix-encodingXXXXXX",NULL);
177 uf = g_mkstemp(res);
178 write(uf,buf,p-buf);
179 write(uf," encoding=\"",11);
180 write(uf,default_enc,strlen(default_enc));
181 write(uf,"\" ",2);
182 write(uf,p,pmax - p);
184 while (1) {
185 len = gzread(zf,buf,BUFLEN);
186 if (len <= 0) break;
187 write(uf,buf,len);
189 gzclose(zf);
190 close(uf);
191 g_free(buf);
192 return res; /* caller frees the name and unlinks the file. */
195 /** Parse a given file into XML, handling old broken files correctly.
196 * @param filename The name of the file to read.
197 * @returns An XML document parsed from the file.
198 * @see xmlParseFile() in the XML2 library for details on the return value.
200 xmlDocPtr
201 xmlDiaParseFile(const char *filename)
203 G_CONST_RETURN char *local_charset = NULL;
205 if ( !g_get_charset(&local_charset)
206 && local_charset) {
207 /* we're not in an UTF-8 environment. */
208 const gchar *fname = xml_file_check_encoding(filename,local_charset);
209 if (fname != filename) {
210 /* We've got a corrected file to parse. */
211 xmlDocPtr ret = xmlDoParseFile(fname);
212 unlink(fname);
213 /* printf("has read %s instead of %s\n",fname,filename); */
214 g_free((void *)fname);
215 return ret;
216 } else {
217 /* the XML file is good. libxml is "old enough" to handle it correctly.
219 return xmlDoParseFile(filename);
221 } else {
222 return xmlDoParseFile(filename);
226 /** Relic of earlier, unhappier days.
227 * @param filename A file to parse.
228 * @return An XML document.
229 * @bug Could probably be inlined. So what?
231 xmlDocPtr
232 xmlDoParseFile(const char *filename)
234 return xmlParseFile(filename);
237 /** Find a named attribute node in an XML object node.
238 * Note that Dia has a concept of attribute node that is not the same
239 * as an XML attribute.
240 * @param obj_node The node to look in.
241 * @param attrname The name of the attribute node to find.
242 * @return The node matching the given name, or NULL if none found.
244 AttributeNode
245 object_find_attribute(ObjectNode obj_node,
246 const char *attrname)
248 AttributeNode attr;
249 xmlChar *name;
251 while (obj_node && xmlIsBlankNode(obj_node))
252 obj_node = obj_node->next;
253 if (!obj_node) return NULL;
255 attr = obj_node->xmlChildrenNode;
256 while (attr != NULL) {
257 if (xmlIsBlankNode(attr)) {
258 attr = attr->next;
259 continue;
262 name = xmlGetProp(attr, "name");
263 if ( (name!=NULL) && (strcmp(name, attrname)==0) ) {
264 xmlFree(name);
265 return attr;
267 if (name) xmlFree(name);
269 attr = attr->next;
271 return NULL;
274 /** Find an attribute in a composite XML node.
275 * @param composite_node The composite node to search.
276 * @param attrname The name of the attribute node to find.
277 * @return The desired node, or NULL if none exists in `composite_node'.
278 * @bug Describe in more detail how a composite node differs from an
279 * object node.
281 AttributeNode
282 composite_find_attribute(DataNode composite_node,
283 const char *attrname)
285 AttributeNode attr;
286 xmlChar *name;
288 while (composite_node && xmlIsBlankNode(composite_node))
289 composite_node = composite_node->next;
290 if (!composite_node) return NULL;
292 attr = composite_node->xmlChildrenNode;
293 while (attr != NULL) {
294 if (xmlIsBlankNode(attr)) {
295 attr = attr->next;
296 continue;
299 name = xmlGetProp(attr, "name");
300 if ( (name!=NULL) && (strcmp(name, attrname)==0) ) {
301 xmlFree(name);
302 return attr;
304 if (name) xmlFree(name);
306 attr = attr->next;
308 return NULL;
311 /** The number of non-blank data nodes in an attribute node.
312 * @param attribute The attribute node to read from.
313 * @returns The number of non-blank data nodes in the node.
316 attribute_num_data(AttributeNode attribute)
318 xmlNode *data;
319 int nr=0;
321 data = attribute ? attribute->xmlChildrenNode : NULL;
322 while (data != NULL) {
323 if (xmlIsBlankNode(data)) {
324 data = data->next;
325 continue;
327 nr++;
328 data = data->next;
330 return nr;
333 /** Get the first data node in an attribute node.
334 * @param attribute The attribute node to look through.
335 * @return The first non-black data node in the attribute node.
337 DataNode
338 attribute_first_data(AttributeNode attribute)
340 xmlNode *data = attribute ? attribute->xmlChildrenNode : NULL;
341 while (data && xmlIsBlankNode(data)) data = data->next;
342 return (DataNode) data;
345 /** Get the next data node (sibling).
346 * @param data A data node to start from (e.g. just processed)
347 * @returns The next sibling data node.
349 DataNode
350 data_next(DataNode data)
353 if (data) {
354 data = data->next;
355 while (data && xmlIsBlankNode(data)) data = data->next;
357 return (DataNode) data;
360 /** Get the type of a data node.
361 * @param data The data node.
362 * @return The type that the data node defines, or 0 on error. In case of
363 * error, an error message is displayed.
364 * @note This function does a number of strcmp calls, which may not be the
365 * fastest way to check if a node is of the expected type.
366 * @bug Make functions that check quickly if a node is of a specific type
367 * (but profile first).
369 DataType
370 data_type(DataNode data)
372 const char *name;
374 name = data ? (const char *)data->name : (const char *)"";
375 if (strcmp(name, "composite")==0) {
376 return DATATYPE_COMPOSITE;
377 } else if (strcmp(name, "int")==0) {
378 return DATATYPE_INT;
379 } else if (strcmp(name, "enum")==0) {
380 return DATATYPE_ENUM;
381 } else if (strcmp(name, "real")==0) {
382 return DATATYPE_REAL;
383 } else if (strcmp(name, "boolean")==0) {
384 return DATATYPE_BOOLEAN;
385 } else if (strcmp(name, "color")==0) {
386 return DATATYPE_COLOR;
387 } else if (strcmp(name, "point")==0) {
388 return DATATYPE_POINT;
389 } else if (strcmp(name, "rectangle")==0) {
390 return DATATYPE_RECTANGLE;
391 } else if (strcmp(name, "string")==0) {
392 return DATATYPE_STRING;
393 } else if (strcmp(name, "font")==0) {
394 return DATATYPE_FONT;
397 message_error("Unknown type of DataNode");
398 return 0;
401 /** Return the value of an integer-type data node.
402 * @param data The data node to read from.
403 * @returns The integer value found in the node. If the node is not an
404 * integer node, an error message is displayed and 0 is returned.
407 data_int(DataNode data)
409 xmlChar *val;
410 int res;
412 if (data_type(data)!=DATATYPE_INT) {
413 message_error("Taking int value of non-int node.");
414 return 0;
417 val = xmlGetProp(data, "val");
418 res = atoi(val);
419 if (val) xmlFree(val);
421 return res;
424 /** Return the value of an enum-type data node.
425 * @param data The data node to read from.
426 * @returns The enum value found in the node. If the node is not an
427 * enum node, an error message is displayed and 0 is returned.
429 int data_enum(DataNode data)
431 xmlChar *val;
432 int res;
434 if (data_type(data)!=DATATYPE_ENUM) {
435 message_error("Taking enum value of non-enum node.");
436 return 0;
439 val = xmlGetProp(data, "val");
440 res = atoi(val);
441 if (val) xmlFree(val);
443 return res;
446 /** Return the value of a real-type data node.
447 * @param data The data node to read from.
448 * @returns The real value found in the node. If the node is not a
449 * real-type node, an error message is displayed and 0.0 is returned.
451 real
452 data_real(DataNode data)
454 xmlChar *val;
455 real res;
457 if (data_type(data)!=DATATYPE_REAL) {
458 message_error("Taking real value of non-real node.");
459 return 0;
462 val = xmlGetProp(data, "val");
463 res = g_ascii_strtod(val, NULL);
464 if (val) xmlFree(val);
466 return res;
469 /** Return the value of a boolean-type data node.
470 * @param data The data node to read from.
471 * @returns The boolean value found in the node. If the node is not a
472 * boolean node, an error message is displayed and FALSE is returned.
475 data_boolean(DataNode data)
477 xmlChar *val;
478 int res;
480 if (data_type(data)!=DATATYPE_BOOLEAN) {
481 message_error("Taking boolean value of non-boolean node.");
482 return 0;
485 val = xmlGetProp(data, "val");
487 if ((val) && (strcmp(val, "true")==0))
488 res = TRUE;
489 else
490 res = FALSE;
492 if (val) xmlFree(val);
494 return res;
497 /** Return the integer value of a hex digit.
498 * @param c A hex digit, one of 0-9, a-f or A-F.
499 * @returns The value of the digit, i.e. 0-15. If a non-gex digit is given
500 * an error message is displayed to the user, and 0 is returned.
502 static int
503 hex_digit(char c)
505 if ((c>='0') && (c<='9'))
506 return c-'0';
507 if ((c>='a') && (c<='f'))
508 return (c-'a') + 10;
509 if ((c>='A') && (c<='F'))
510 return (c-'A') + 10;
511 message_error("wrong hex digit %c", c);
512 return 0;
515 /** Return the value of a color-type data node.
516 * @param data The XML node to read from
517 * @param col A place to store the resulting RGB values. If the node does
518 * not contain a valid color value, an error message is displayed to the
519 * user, and `col' is unchanged.
520 * @note Could be cool to use RGBA data here, even if we can't display it yet.
522 void
523 data_color(DataNode data, Color *col)
525 xmlChar *val;
526 int r=0, g=0, b=0;
528 if (data_type(data)!=DATATYPE_COLOR) {
529 message_error("Taking color value of non-color node.");
530 return;
533 val = xmlGetProp(data, "val");
535 /* Format #RRGGBB */
536 /* 0123456 */
538 if ((val) && (strlen(val)>=7)) {
539 r = hex_digit(val[1])*16 + hex_digit(val[2]);
540 g = hex_digit(val[3])*16 + hex_digit(val[4]);
541 b = hex_digit(val[5])*16 + hex_digit(val[6]);
544 if (val) xmlFree(val);
546 col->red = (float)(r/255.0);
547 col->green = (float)(g/255.0);
548 col->blue = (float)(b/255.0);
551 /** Return the value of a point-type data node.
552 * @param data The XML node to read from
553 * @param point A place to store the resulting x, y values. If the node does
554 * not contain a valid point value, an error message is displayed to the
555 * user, and `point' is unchanged.
557 void
558 data_point(DataNode data, Point *point)
560 xmlChar *val;
561 gchar *str;
562 real ax,ay;
564 if (data_type(data)!=DATATYPE_POINT) {
565 message_error(_("Taking point value of non-point node."));
566 return;
569 val = xmlGetProp(data, "val");
570 point->x = g_ascii_strtod(val, &str);
571 ax = fabs(point->x);
572 if ((ax > 1e9) || ((ax < 1e-9) && (ax != 0.0)) || isnan(ax) || isinf(ax)) {
573 /* there is no provision to keep values larger when saving,
574 * so do this 'reduction' silent */
575 if (!(ax < 1e-9))
576 g_warning(_("Incorrect x Point value \"%s\" %f; discarding it."),val,point->x);
577 point->x = 0.0;
579 while ((*str != ',') && (*str!=0))
580 str++;
581 if (*str==0){
582 point->y = 0.0;
583 g_warning(_("Error parsing point."));
584 xmlFree(val);
585 return;
587 point->y = g_ascii_strtod(str+1, NULL);
588 ay = fabs(point->y);
589 if ((ay > 1e9) || ((ay < 1e-9) && (ay != 0.0)) || isnan(ay) || isinf(ay)) {
590 if (!(ay < 1e-9)) /* don't bother with useless warnings (see above) */
591 g_warning(_("Incorrect y Point value \"%s\" %f; discarding it."),str+1,point->y);
592 point->y = 0.0;
594 xmlFree(val);
597 /** Return the value of a rectangle-type data node.
598 * @param data The data node to read from.
599 * @param rect A place to store the resulting values. If the node does
600 * not contain a valid rectangle value, an error message is displayed to the
601 * user, and `rect' is unchanged.
603 void
604 data_rectangle(DataNode data, Rectangle *rect)
606 xmlChar *val;
607 gchar *str;
609 if (data_type(data)!=DATATYPE_RECTANGLE) {
610 message_error("Taking rectangle value of non-rectangle node.");
611 return;
614 val = xmlGetProp(data, "val");
616 rect->left = g_ascii_strtod(val, &str);
618 while ((*str != ',') && (*str!=0))
619 str++;
621 if (*str==0){
622 message_error("Error parsing rectangle.");
623 xmlFree(val);
624 return;
627 rect->top = g_ascii_strtod(str+1, &str);
629 while ((*str != ';') && (*str!=0))
630 str++;
632 if (*str==0){
633 message_error("Error parsing rectangle.");
634 xmlFree(val);
635 return;
638 rect->right = g_ascii_strtod(str+1, &str);
640 while ((*str != ',') && (*str!=0))
641 str++;
643 if (*str==0){
644 message_error("Error parsing rectangle.");
645 xmlFree(val);
646 return;
649 rect->bottom = g_ascii_strtod(str+1, NULL);
651 xmlFree(val);
654 /** Return the value of a string-type data node.
655 * @param data The data node to read from.
656 * @returns The string value found in the node. If the node is not a
657 * string node, an error message is displayed and NULL is returned. The
658 * returned valuee should be freed after use.
659 * @note For historical reasons, strings in Dia XML are surrounded by ##.
661 gchar *
662 data_string(DataNode data)
664 xmlChar *val;
665 gchar *str, *p,*str2;
666 int len;
668 if (data_type(data)!=DATATYPE_STRING) {
669 message_error("Taking string value of non-string node.");
670 return NULL;
673 val = xmlGetProp(data, "val");
674 if (val != NULL) { /* Old kind of string. Left for backwards compatibility */
675 str = g_malloc(4 * (sizeof(char)*(strlen(val)+1))); /* extra room
676 for UTF8 */
677 p = str;
678 while (*val) {
679 if (*val == '\\') {
680 val++;
681 switch (*val) {
682 case '0':
683 /* Just skip this. \0 means nothing */
684 break;
685 case 'n':
686 *p++ = '\n';
687 break;
688 case 't':
689 *p++ = '\t';
690 break;
691 case '\\':
692 *p++ = '\\';
693 break;
694 default:
695 message_error("Error in string tag.");
697 } else {
698 *p++ = *val;
700 val++;
702 *p = 0;
703 xmlFree(val);
704 str2 = g_strdup(str); /* to remove the extra space */
705 g_free(str);
706 return str2;
709 if (data->xmlChildrenNode!=NULL) {
710 p = xmlNodeListGetString(data->doc, data->xmlChildrenNode, TRUE);
712 if (*p!='#')
713 message_error("Error in file, string not starting with #\n");
715 len = strlen(p)-1; /* Ignore first '#' */
717 str = g_malloc(len+1);
719 strncpy(str, p+1, len);
720 str[len]=0; /* For safety */
722 str[strlen(str)-1] = 0; /* Remove last '#' */
723 xmlFree(p);
724 return str;
727 return NULL;
730 /** Return the value of a filename-type data node.
731 * @param data The data node to read from.
732 * @return The filename value found in the node. If the node is not a
733 * filename node, an error message is displayed and NULL is returned.
734 * The resulting string is in the local filesystem's encoding rather than
735 * UTF-8, and should be freed after use.
736 * @bug data_string() can return NULL, what does g_filename_from_utf8 do then?
738 char *
739 data_filename(DataNode data)
741 char *utf8 = data_string(data);
742 char *filename = g_filename_from_utf8(utf8, -1, NULL, NULL, NULL);
743 g_free(utf8);
744 return filename;
747 /** Return the value of a font-type data node. This handles both the current
748 * format (family and style) and the old format (name).
749 * @param data The data node to read from.
750 * @return The font value found in the node. If the node is not a
751 * font node, an error message is displayed and NULL is returned. The
752 * resulting value should be freed after use.
754 DiaFont *
755 data_font(DataNode data)
757 xmlChar *family;
758 DiaFont *font;
760 if (data_type(data)!=DATATYPE_FONT) {
761 message_error("Taking font value of non-font node.");
762 return NULL;
765 family = xmlGetProp(data, "family");
766 /* always prefer the new format */
767 if (family) {
768 DiaFontStyle style;
769 xmlChar* style_name = xmlGetProp(data, "style");
770 style = style_name ? atoi(style_name) : 0;
772 font = dia_font_new (family, style, 1.0);
773 if (family) xmlFree(family);
774 if (style_name) xmlFree(style_name);
775 } else {
776 /* Legacy format support */
777 char *name = xmlGetProp(data, "name");
778 font = dia_font_new_from_legacy_name(name);
779 xmlFree(name);
781 return font;
784 /* ***** Saving XML **** */
786 /** Create a new attribute node.
787 * @param obj_node The object node to create the attribute node under.
788 * @param attrname The name of the attribute node.
789 * @return A new attribute node.
790 * @bug Should have utility functions that creates the node and sets
791 * the value based on type.
793 AttributeNode
794 new_attribute(ObjectNode obj_node,
795 const char *attrname)
797 AttributeNode attr;
798 attr = xmlNewChild(obj_node, NULL, "attribute", NULL);
799 xmlSetProp(attr, "name", attrname);
801 return attr;
804 /** Add an attribute node to a composite node.
805 * @param composite_node The composite node.
806 * @param attrname The name of the new attribute node.
807 * @return The attribute node added.
808 * @bug This does exactly the same as new_attribute.
810 AttributeNode
811 composite_add_attribute(DataNode composite_node,
812 const char *attrname)
814 AttributeNode attr;
815 attr = xmlNewChild(composite_node, NULL, "attribute", NULL);
816 xmlSetProp(attr, "name", attrname);
818 return attr;
821 /** Add integer data to an attribute node.
822 * @param attr The attribute node.
823 * @param data The value to set.
825 void
826 data_add_int(AttributeNode attr, int data)
828 DataNode data_node;
829 char buffer[20+1]; /* Enought for 64bit int + zero */
831 g_snprintf(buffer, 20, "%d", data);
833 data_node = xmlNewChild(attr, NULL, "int", NULL);
834 xmlSetProp(data_node, "val", buffer);
837 /** Add enum data to an attribute node.
838 * @param attr The attribute node.
839 * @param data The value to set.
841 void
842 data_add_enum(AttributeNode attr, int data)
844 DataNode data_node;
845 char buffer[20+1]; /* Enought for 64bit int + zero */
847 g_snprintf(buffer, 20, "%d", data);
849 data_node = xmlNewChild(attr, NULL, "enum", NULL);
850 xmlSetProp(data_node, "val", buffer);
853 /** Add real-typed data to an attribute node.
854 * @param attr The attribute node.
855 * @param data The value to set.
857 void
858 data_add_real(AttributeNode attr, real data)
860 DataNode data_node;
861 char buffer[G_ASCII_DTOSTR_BUF_SIZE]; /* Large enought */
863 g_ascii_dtostr(buffer, G_ASCII_DTOSTR_BUF_SIZE, data);
865 data_node = xmlNewChild(attr, NULL, "real", NULL);
866 xmlSetProp(data_node, "val", buffer);
869 /** Add boolean data to an attribute node.
870 * @param attr The attribute node.
871 * @param data The value to set.
873 void
874 data_add_boolean(AttributeNode attr, int data)
876 DataNode data_node;
878 data_node = xmlNewChild(attr, NULL, "boolean", NULL);
879 if (data)
880 xmlSetProp(data_node, "val", "true");
881 else
882 xmlSetProp(data_node, "val", "false");
885 /** Convert a floating-point value to hexadecimal.
886 * @param x The floating point value.
887 * @param str A string to place the result in.
888 * @note Currently only works for 0 <= x <= 255 and will silently cap the value
889 * to those limits. Also expects str to have at least two bytes allocated,
890 * and doesn't null-terminate it. This works well for converting a color
891 * value, but is pretty much useless for other values.
893 static void
894 convert_to_hex(float x, char *str)
896 static const char hex_digit[] = "0123456789abcdef";
897 int val;
899 val = x * 255.0;
900 if (val>255)
901 val = 255;
902 if (val<0)
903 val = 0;
905 str[0] = hex_digit[val/16];
906 str[1] = hex_digit[val%16];
909 /** Add color data to an attribute node.
910 * @param attr The attribute node.
911 * @param col The value to set.
913 void
914 data_add_color(AttributeNode attr, const Color *col)
916 char buffer[1+6+1];
917 DataNode data_node;
919 buffer[0] = '#';
920 convert_to_hex(col->red, &buffer[1]);
921 convert_to_hex(col->green, &buffer[3]);
922 convert_to_hex(col->blue, &buffer[5]);
923 buffer[7] = 0;
925 data_node = xmlNewChild(attr, NULL, "color", NULL);
926 xmlSetProp(data_node, "val", buffer);
929 /** Add point data to an attribute node.
930 * @param attr The attribute node.
931 * @param point The value to set.
933 void
934 data_add_point(AttributeNode attr, const Point *point)
936 DataNode data_node;
937 gchar *buffer;
938 gchar px_buf[G_ASCII_DTOSTR_BUF_SIZE];
939 gchar py_buf[G_ASCII_DTOSTR_BUF_SIZE];
941 g_ascii_formatd(px_buf, sizeof(px_buf), "%g", point->x);
942 g_ascii_formatd(py_buf, sizeof(py_buf), "%g", point->y);
943 buffer = g_strconcat(px_buf, ",", py_buf, NULL);
945 data_node = xmlNewChild(attr, NULL, "point", NULL);
946 xmlSetProp(data_node, "val", buffer);
947 g_free(buffer);
950 /** Add rectangle data to an attribute node.
951 * @param attr The attribute node.
952 * @param rect The value to set.
954 void
955 data_add_rectangle(AttributeNode attr, const Rectangle *rect)
957 DataNode data_node;
958 gchar *buffer;
959 gchar rl_buf[G_ASCII_DTOSTR_BUF_SIZE];
960 gchar rr_buf[G_ASCII_DTOSTR_BUF_SIZE];
961 gchar rt_buf[G_ASCII_DTOSTR_BUF_SIZE];
962 gchar rb_buf[G_ASCII_DTOSTR_BUF_SIZE];
964 g_ascii_formatd(rl_buf, sizeof(rl_buf), "%g", rect->left);
965 g_ascii_formatd(rr_buf, sizeof(rr_buf), "%g", rect->right);
966 g_ascii_formatd(rt_buf, sizeof(rt_buf), "%g", rect->top);
967 g_ascii_formatd(rb_buf, sizeof(rb_buf), "%g", rect->bottom);
969 buffer = g_strconcat(rl_buf, ",", rt_buf, ";", rr_buf, ",", rb_buf, NULL);
971 data_node = xmlNewChild(attr, NULL, "rectangle", NULL);
972 xmlSetProp(data_node, "val", buffer);
974 g_free(buffer);
977 /** Add string data to an attribute node.
978 * @param attr The attribute node.
979 * @param str The value to set.
981 void
982 data_add_string(AttributeNode attr, const char *str)
984 DataNode data_node;
985 xmlChar *escaped_str;
986 xmlChar *sharped_str;
988 if (str==NULL) {
989 data_node = xmlNewChild(attr, NULL, "string", "##");
990 return;
993 escaped_str = xmlEncodeEntitiesReentrant(attr->doc,str);
995 sharped_str = g_strconcat("#", escaped_str, "#", NULL);
997 xmlFree(escaped_str);
999 data_node = xmlNewChild(attr, NULL, "string", sharped_str);
1001 g_free(sharped_str);
1004 /** Add filename data to an attribute node.
1005 * @param attr The attribute node.
1006 * @param filename The value to set. This should be n the local filesystem
1007 * encoding, not utf-8.
1009 void
1010 data_add_filename(DataNode data, const char *str)
1012 char *utf8 = g_filename_to_utf8(str, -1, NULL, NULL, NULL);
1014 data_add_string(data, utf8);
1016 g_free(utf8);
1019 /** Add font data to an attribute node.
1020 * @param attr The attribute node.
1021 * @param font The value to set.
1023 void
1024 data_add_font(AttributeNode attr, const DiaFont *font)
1026 DataNode data_node;
1027 DiaFontStyle style;
1028 char buffer[20+1]; /* Enought for 64bit int + zero */
1030 data_node = xmlNewChild(attr, NULL, "font", NULL);
1031 style = dia_font_get_style (font);
1032 xmlSetProp(data_node, "family", dia_font_get_family(font));
1033 g_snprintf(buffer, 20, "%d", dia_font_get_style(font));
1035 xmlSetProp(data_node, "style", buffer);
1036 /* Legacy support: don't crash older Dia on missing 'name' attribute */
1037 xmlSetProp(data_node, "name", dia_font_get_legacy_name(font));
1040 /** Add a new composite node to an attribute node.
1041 * @param attr The attribute node to add to.
1042 * @param type The type of the new node.
1043 * @returns The new child of `attr'.
1045 DataNode
1046 data_add_composite(AttributeNode attr, const char *type)
1048 /* type can be NULL */
1049 DataNode data_node;
1051 data_node = xmlNewChild(attr, NULL, "composite", NULL);
1052 if (type != NULL)
1053 xmlSetProp(data_node, "type", type);
1055 return data_node;
1058 /** Emit a warning to the user that having UTF-8 as local charset doesn't.
1060 void
1061 warn_about_broken_libxml1(void)
1063 message_warning(_("Your local character set is UTF-8. Because of issues"
1064 " with libxml1 and the support of files generated by"
1065 " previous versions of dia, you will encounter "
1066 " problems. Please report to dia-list@gnome.org if you"
1067 " see this message."));
1070 #define BUFSIZE 2048
1071 #define OVERRUN_SAFETY 16
1073 /* diarc option */
1074 int pretty_formated_xml = TRUE;
1076 /** Save an XML document to a file.
1077 * @param filename The file to save to.
1078 * @param cur The XML document structure.
1079 * @return The return value of xmlSaveFormatFileEnc.
1080 * @bug Get the proper defn of the return value from libxml2.
1083 xmlDiaSaveFile(const char *filename,
1084 xmlDocPtr cur)
1086 int old = 0, ret;
1088 if (pretty_formated_xml)
1089 old = xmlKeepBlanksDefault (0);
1090 ret = xmlSaveFormatFileEnc (filename,cur, "UTF-8", pretty_formated_xml ? 1 : 0);
1091 if (pretty_formated_xml)
1092 xmlKeepBlanksDefault (old);
1093 return ret;