* installer/win32/dia.nsi: don't uninstall old version; overwrite
[dia.git] / lib / dia_xml.c
blob6925a1b98f45ba7ad6d8ce419c1b1a2f44f28a7e
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 #define isinf(a) (!_finite(a))
52 #endif
54 #define BUFLEN 1024
56 /** If all files produced by dia were good XML files, we wouldn't have to do
57 * this little gymnastic. Alas, during the libxml1 days, we were outputting
58 * files with no encoding specification (which means UTF-8 if we're in an
59 * asciish encoding) and strings encoded in local charset (so, we wrote
60 * broken files).
62 * The following logic finds if we have a broken file, and attempts to fix
63 * it if it's possible. If the file is correct or is unrecognisable, we pass
64 * it untouched to libxml2.
65 * @param filename The name of the file to check.
66 * @param default_enc The default encoding to use if none is given.
67 * @return The filename given if it seems ok, or the name of a new file
68 * with fixed contents, or NULL if we couldn't read the file. The
69 * caller should free this string and unlink the file if it is not
70 * the same as `filename'.
71 * @bug The many gzclose-g_free-return sequences should be refactored into
72 * an "exception handle" (goto+label). At least for people who think goto is
73 * better than this. I dont. --hb
75 static const gchar *
76 xml_file_check_encoding(const gchar *filename, const gchar *default_enc)
78 gzFile zf = gzopen(filename,"rb");
79 gchar *buf;
80 gchar *p,*pmax;
81 int len;
82 gchar *tmp,*res;
83 int uf;
84 gboolean well_formed_utf8;
86 static char magic_xml[] =
87 {0x3c,0x3f,0x78,0x6d,0x6c,0x00}; /* "<?xml" in ASCII */
89 if (!zf) {
90 /* message_error(_("The file %s can not be opened for reading"),filename); */
91 /* XXX perhaps we can just chicken out to libxml ? -- CC */
92 return NULL;
94 p = buf = g_malloc0(BUFLEN);
95 len = gzread(zf,buf,BUFLEN);
96 pmax = p + len;
98 /* first, we expect the magic <?xml string */
99 if ((0 != strncmp(p,magic_xml,5)) || (len < 5)) {
100 gzclose(zf);
101 g_free(buf);
102 return filename; /* let libxml figure out what this is. */
104 /* now, we're sure we have some asciish XML file. */
105 p += 5;
106 while (((*p == 0x20)||(*p == 0x09)||(*p == 0x0d)||(*p == 0x0a))
107 && (p<pmax)) p++;
108 if (p>=pmax) { /* whoops ? */
109 gzclose(zf);
110 g_free(buf);
111 return filename;
113 if (0 != strncmp(p,"version=\"",9)) {
114 gzclose(zf); /* chicken out. */
115 g_free(buf);
116 return filename;
118 p += 9;
119 /* The header is rather well formed. */
120 if (p>=pmax) { /* whoops ? */
121 gzclose(zf);
122 g_free(buf);
123 return filename;
125 while ((*p != '"') && (p < pmax)) p++;
126 p++;
127 while (((*p == 0x20)||(*p == 0x09)||(*p == 0x0d)||(*p == 0x0a))
128 && (p<pmax)) p++;
129 if (p>=pmax) { /* whoops ? */
130 gzclose(zf);
131 g_free(buf);
132 return filename;
134 if (0 == strncmp(p,"encoding=\"",10)) {
135 gzclose(zf); /* this file has an encoding string. Good. */
136 g_free(buf);
137 return filename;
139 /* now let's read the whole file, to see if there are offending bits.
140 * We can call it well formed UTF-8 if the highest isn't used
142 well_formed_utf8 = TRUE;
143 do {
144 int i;
145 for (i = 0; i < len; i++)
146 if (buf[i] & 0x80 || buf[i] == '&')
147 well_formed_utf8 = FALSE;
148 len = gzread(zf,buf,BUFLEN);
149 } while (len > 0 && well_formed_utf8);
150 if (well_formed_utf8) {
151 gzclose(zf); /* this file is utf-8 compatible */
152 g_free(buf);
153 return filename;
154 } else {
155 gzclose(zf); /* poor man's fseek */
156 zf = gzopen(filename,"rb");
157 len = gzread(zf,buf,BUFLEN);
160 if (0 != strcmp(default_enc,"UTF-8")) {
161 message_warning(_("The file %s has no encoding specification;\n"
162 "assuming it is encoded in %s"),
163 dia_message_filename(filename), default_enc);
164 } else {
165 gzclose(zf); /* we apply the standard here. */
166 g_free(buf);
167 return filename;
170 tmp = getenv("TMP");
171 if (!tmp) tmp = getenv("TEMP");
172 if (!tmp) tmp = "/tmp";
174 res = g_strconcat(tmp,G_DIR_SEPARATOR_S,"dia-xml-fix-encodingXXXXXX",NULL);
175 uf = g_mkstemp(res);
176 write(uf,buf,p-buf);
177 write(uf," encoding=\"",11);
178 write(uf,default_enc,strlen(default_enc));
179 write(uf,"\" ",2);
180 write(uf,p,pmax - p);
182 while (1) {
183 len = gzread(zf,buf,BUFLEN);
184 if (len <= 0) break;
185 write(uf,buf,len);
187 gzclose(zf);
188 close(uf);
189 g_free(buf);
190 return res; /* caller frees the name and unlinks the file. */
193 /** Parse a given file into XML, handling old broken files correctly.
194 * @param filename The name of the file to read.
195 * @returns An XML document parsed from the file.
196 * @see xmlParseFile() in the XML2 library for details on the return value.
198 xmlDocPtr
199 xmlDiaParseFile(const char *filename)
201 G_CONST_RETURN char *local_charset = NULL;
203 if ( !g_get_charset(&local_charset)
204 && local_charset) {
205 /* we're not in an UTF-8 environment. */
206 const gchar *fname = xml_file_check_encoding(filename,local_charset);
207 if (fname != filename) {
208 /* We've got a corrected file to parse. */
209 xmlDocPtr ret = xmlDoParseFile(fname);
210 unlink(fname);
211 /* printf("has read %s instead of %s\n",fname,filename); */
212 g_free((void *)fname);
213 return ret;
214 } else {
215 /* the XML file is good. libxml is "old enough" to handle it correctly.
217 return xmlDoParseFile(filename);
219 } else {
220 return xmlDoParseFile(filename);
224 /** Relic of earlier, unhappier days.
225 * @param filename A file to parse.
226 * @return An XML document.
227 * @bug Could probably be inlined. So what?
229 xmlDocPtr
230 xmlDoParseFile(const char *filename)
232 return xmlParseFile(filename);
235 /** Find a named attribute node in an XML object node.
236 * Note that Dia has a concept of attribute node that is not the same
237 * as an XML attribute.
238 * @param obj_node The node to look in.
239 * @param attrname The name of the attribute node to find.
240 * @return The node matching the given name, or NULL if none found.
242 AttributeNode
243 object_find_attribute(ObjectNode obj_node,
244 const char *attrname)
246 AttributeNode attr;
247 xmlChar *name;
249 while (obj_node && xmlIsBlankNode(obj_node))
250 obj_node = obj_node->next;
251 if (!obj_node) return NULL;
253 attr = obj_node->xmlChildrenNode;
254 while (attr != NULL) {
255 if (xmlIsBlankNode(attr)) {
256 attr = attr->next;
257 continue;
260 name = xmlGetProp(attr, "name");
261 if ( (name!=NULL) && (strcmp(name, attrname)==0) ) {
262 xmlFree(name);
263 return attr;
265 if (name) xmlFree(name);
267 attr = attr->next;
269 return NULL;
272 /** Find an attribute in a composite XML node.
273 * @param composite_node The composite node to search.
274 * @param attrname The name of the attribute node to find.
275 * @return The desired node, or NULL if none exists in `composite_node'.
276 * @bug Describe in more detail how a composite node differs from an
277 * object node.
279 AttributeNode
280 composite_find_attribute(DataNode composite_node,
281 const char *attrname)
283 AttributeNode attr;
284 xmlChar *name;
286 while (composite_node && xmlIsBlankNode(composite_node))
287 composite_node = composite_node->next;
288 if (!composite_node) return NULL;
290 attr = composite_node->xmlChildrenNode;
291 while (attr != NULL) {
292 if (xmlIsBlankNode(attr)) {
293 attr = attr->next;
294 continue;
297 name = xmlGetProp(attr, "name");
298 if ( (name!=NULL) && (strcmp(name, attrname)==0) ) {
299 xmlFree(name);
300 return attr;
302 if (name) xmlFree(name);
304 attr = attr->next;
306 return NULL;
309 /** The number of non-blank data nodes in an attribute node.
310 * @param attribute The attribute node to read from.
311 * @returns The number of non-blank data nodes in the node.
314 attribute_num_data(AttributeNode attribute)
316 xmlNode *data;
317 int nr=0;
319 data = attribute ? attribute->xmlChildrenNode : NULL;
320 while (data != NULL) {
321 if (xmlIsBlankNode(data)) {
322 data = data->next;
323 continue;
325 nr++;
326 data = data->next;
328 return nr;
331 /** Get the first data node in an attribute node.
332 * @param attribute The attribute node to look through.
333 * @return The first non-black data node in the attribute node.
335 DataNode
336 attribute_first_data(AttributeNode attribute)
338 xmlNode *data = attribute ? attribute->xmlChildrenNode : NULL;
339 while (data && xmlIsBlankNode(data)) data = data->next;
340 return (DataNode) data;
343 /** Get the next data node (sibling).
344 * @param data A data node to start from (e.g. just processed)
345 * @returns The next sibling data node.
347 DataNode
348 data_next(DataNode data)
351 if (data) {
352 data = data->next;
353 while (data && xmlIsBlankNode(data)) data = data->next;
355 return (DataNode) data;
358 /** Get the type of a data node.
359 * @param data The data node.
360 * @return The type that the data node defines, or 0 on error. In case of
361 * error, an error message is displayed.
362 * @note This function does a number of strcmp calls, which may not be the
363 * fastest way to check if a node is of the expected type.
364 * @bug Make functions that check quickly if a node is of a specific type
365 * (but profile first).
367 DataType
368 data_type(DataNode data)
370 const char *name;
372 name = data ? (const char *)data->name : (const char *)"";
373 if (strcmp(name, "composite")==0) {
374 return DATATYPE_COMPOSITE;
375 } else if (strcmp(name, "int")==0) {
376 return DATATYPE_INT;
377 } else if (strcmp(name, "enum")==0) {
378 return DATATYPE_ENUM;
379 } else if (strcmp(name, "real")==0) {
380 return DATATYPE_REAL;
381 } else if (strcmp(name, "boolean")==0) {
382 return DATATYPE_BOOLEAN;
383 } else if (strcmp(name, "color")==0) {
384 return DATATYPE_COLOR;
385 } else if (strcmp(name, "point")==0) {
386 return DATATYPE_POINT;
387 } else if (strcmp(name, "rectangle")==0) {
388 return DATATYPE_RECTANGLE;
389 } else if (strcmp(name, "string")==0) {
390 return DATATYPE_STRING;
391 } else if (strcmp(name, "font")==0) {
392 return DATATYPE_FONT;
395 message_error("Unknown type of DataNode");
396 return 0;
399 /** Return the value of an integer-type data node.
400 * @param data The data node to read from.
401 * @returns The integer value found in the node. If the node is not an
402 * integer node, an error message is displayed and 0 is returned.
405 data_int(DataNode data)
407 xmlChar *val;
408 int res;
410 if (data_type(data)!=DATATYPE_INT) {
411 message_error("Taking int value of non-int node.");
412 return 0;
415 val = xmlGetProp(data, "val");
416 res = atoi(val);
417 if (val) xmlFree(val);
419 return res;
422 /** Return the value of an enum-type data node.
423 * @param data The data node to read from.
424 * @returns The enum value found in the node. If the node is not an
425 * enum node, an error message is displayed and 0 is returned.
427 int data_enum(DataNode data)
429 xmlChar *val;
430 int res;
432 if (data_type(data)!=DATATYPE_ENUM) {
433 message_error("Taking enum value of non-enum node.");
434 return 0;
437 val = xmlGetProp(data, "val");
438 res = atoi(val);
439 if (val) xmlFree(val);
441 return res;
444 /** Return the value of a real-type data node.
445 * @param data The data node to read from.
446 * @returns The real value found in the node. If the node is not a
447 * real-type node, an error message is displayed and 0.0 is returned.
449 real
450 data_real(DataNode data)
452 xmlChar *val;
453 real res;
455 if (data_type(data)!=DATATYPE_REAL) {
456 message_error("Taking real value of non-real node.");
457 return 0;
460 val = xmlGetProp(data, "val");
461 res = g_ascii_strtod(val, NULL);
462 if (val) xmlFree(val);
464 return res;
467 /** Return the value of a boolean-type data node.
468 * @param data The data node to read from.
469 * @returns The boolean value found in the node. If the node is not a
470 * boolean node, an error message is displayed and FALSE is returned.
473 data_boolean(DataNode data)
475 xmlChar *val;
476 int res;
478 if (data_type(data)!=DATATYPE_BOOLEAN) {
479 message_error("Taking boolean value of non-boolean node.");
480 return 0;
483 val = xmlGetProp(data, "val");
485 if ((val) && (strcmp(val, "true")==0))
486 res = TRUE;
487 else
488 res = FALSE;
490 if (val) xmlFree(val);
492 return res;
495 /** Return the integer value of a hex digit.
496 * @param c A hex digit, one of 0-9, a-f or A-F.
497 * @returns The value of the digit, i.e. 0-15. If a non-gex digit is given
498 * an error message is displayed to the user, and 0 is returned.
500 static int
501 hex_digit(char c)
503 if ((c>='0') && (c<='9'))
504 return c-'0';
505 if ((c>='a') && (c<='f'))
506 return (c-'a') + 10;
507 if ((c>='A') && (c<='F'))
508 return (c-'A') + 10;
509 message_error("wrong hex digit %c", c);
510 return 0;
513 /** Return the value of a color-type data node.
514 * @param data The XML node to read from
515 * @param col A place to store the resulting RGB values. If the node does
516 * not contain a valid color value, an error message is displayed to the
517 * user, and `col' is unchanged.
518 * @note Could be cool to use RGBA data here, even if we can't display it yet.
520 void
521 data_color(DataNode data, Color *col)
523 xmlChar *val;
524 int r=0, g=0, b=0;
526 if (data_type(data)!=DATATYPE_COLOR) {
527 message_error("Taking color value of non-color node.");
528 return;
531 val = xmlGetProp(data, "val");
533 /* Format #RRGGBB */
534 /* 0123456 */
536 if ((val) && (strlen(val)>=7)) {
537 r = hex_digit(val[1])*16 + hex_digit(val[2]);
538 g = hex_digit(val[3])*16 + hex_digit(val[4]);
539 b = hex_digit(val[5])*16 + hex_digit(val[6]);
542 if (val) xmlFree(val);
544 col->red = ((float)r)/255.0;
545 col->green = ((float)g)/255.0;
546 col->blue = ((float)b)/255.0;
549 /** Return the value of a point-type data node.
550 * @param data The XML node to read from
551 * @param point A place to store the resulting x, y values. If the node does
552 * not contain a valid point value, an error message is displayed to the
553 * user, and `point' is unchanged.
555 void
556 data_point(DataNode data, Point *point)
558 xmlChar *val;
559 gchar *str;
560 real ax,ay;
562 if (data_type(data)!=DATATYPE_POINT) {
563 message_error(_("Taking point value of non-point node."));
564 return;
567 val = xmlGetProp(data, "val");
568 point->x = g_ascii_strtod(val, &str);
569 ax = fabs(point->x);
570 if ((ax > 1e9) || ((ax < 1e-9) && (ax != 0.0)) || isnan(ax) || isinf(ax)) {
571 /* there is no provision to keep values larger when saving,
572 * so do this 'reduction' silent */
573 if (!(ax < 1e-9))
574 g_warning(_("Incorrect x Point value \"%s\" %f; discarding it."),val,point->x);
575 point->x = 0.0;
577 while ((*str != ',') && (*str!=0))
578 str++;
579 if (*str==0){
580 point->y = 0.0;
581 g_warning(_("Error parsing point."));
582 xmlFree(val);
583 return;
585 point->y = g_ascii_strtod(str+1, NULL);
586 ay = fabs(point->y);
587 if ((ay > 1e9) || ((ay < 1e-9) && (ay != 0.0)) || isnan(ay) || isinf(ay)) {
588 if (!(ay < 1e-9)) /* don't bother with useless warnings (see above) */
589 g_warning(_("Incorrect y Point value \"%s\" %f; discarding it."),str+1,point->y);
590 point->y = 0.0;
592 xmlFree(val);
595 /** Return the value of a rectangle-type data node.
596 * @param data The data node to read from.
597 * @param rect A place to store the resulting values. If the node does
598 * not contain a valid rectangle value, an error message is displayed to the
599 * user, and `rect' is unchanged.
601 void
602 data_rectangle(DataNode data, Rectangle *rect)
604 xmlChar *val;
605 gchar *str;
607 if (data_type(data)!=DATATYPE_RECTANGLE) {
608 message_error("Taking rectangle value of non-rectangle node.");
609 return;
612 val = xmlGetProp(data, "val");
614 rect->left = g_ascii_strtod(val, &str);
616 while ((*str != ',') && (*str!=0))
617 str++;
619 if (*str==0){
620 message_error("Error parsing rectangle.");
621 xmlFree(val);
622 return;
625 rect->top = g_ascii_strtod(str+1, &str);
627 while ((*str != ';') && (*str!=0))
628 str++;
630 if (*str==0){
631 message_error("Error parsing rectangle.");
632 xmlFree(val);
633 return;
636 rect->right = g_ascii_strtod(str+1, &str);
638 while ((*str != ',') && (*str!=0))
639 str++;
641 if (*str==0){
642 message_error("Error parsing rectangle.");
643 xmlFree(val);
644 return;
647 rect->bottom = g_ascii_strtod(str+1, NULL);
649 xmlFree(val);
652 /** Return the value of a string-type data node.
653 * @param data The data node to read from.
654 * @returns The string value found in the node. If the node is not a
655 * string node, an error message is displayed and NULL is returned. The
656 * returned valuee should be freed after use.
657 * @note For historical reasons, strings in Dia XML are surrounded by ##.
659 gchar *
660 data_string(DataNode data)
662 xmlChar *val;
663 gchar *str, *p,*str2;
664 int len;
666 if (data_type(data)!=DATATYPE_STRING) {
667 message_error("Taking string value of non-string node.");
668 return NULL;
671 val = xmlGetProp(data, "val");
672 if (val != NULL) { /* Old kind of string. Left for backwards compatibility */
673 str = g_malloc(4 * (sizeof(char)*(strlen(val)+1))); /* extra room
674 for UTF8 */
675 p = str;
676 while (*val) {
677 if (*val == '\\') {
678 val++;
679 switch (*val) {
680 case '0':
681 /* Just skip this. \0 means nothing */
682 break;
683 case 'n':
684 *p++ = '\n';
685 break;
686 case 't':
687 *p++ = '\t';
688 break;
689 case '\\':
690 *p++ = '\\';
691 break;
692 default:
693 message_error("Error in string tag.");
695 } else {
696 *p++ = *val;
698 val++;
700 *p = 0;
701 xmlFree(val);
702 str2 = g_strdup(str); /* to remove the extra space */
703 g_free(str);
704 return str2;
707 if (data->xmlChildrenNode!=NULL) {
708 p = xmlNodeListGetString(data->doc, data->xmlChildrenNode, TRUE);
710 if (*p!='#')
711 message_error("Error in file, string not starting with #\n");
713 len = strlen(p)-1; /* Ignore first '#' */
715 str = g_malloc(len+1);
717 strncpy(str, p+1, len);
718 str[len]=0; /* For safety */
720 str[strlen(str)-1] = 0; /* Remove last '#' */
721 xmlFree(p);
722 return str;
725 return NULL;
728 /** Return the value of a filename-type data node.
729 * @param data The data node to read from.
730 * @return The filename value found in the node. If the node is not a
731 * filename node, an error message is displayed and NULL is returned.
732 * The resulting string is in the local filesystem's encoding rather than
733 * UTF-8, and should be freed after use.
734 * @bug data_string() can return NULL, what does g_filename_from_utf8 do then?
736 char *
737 data_filename(DataNode data)
739 char *utf8 = data_string(data);
740 char *filename = g_filename_from_utf8(utf8, -1, NULL, NULL, NULL);
741 g_free(utf8);
742 return filename;
745 /** Return the value of a font-type data node. This handles both the current
746 * format (family and style) and the old format (name).
747 * @param data The data node to read from.
748 * @return The font value found in the node. If the node is not a
749 * font node, an error message is displayed and NULL is returned. The
750 * resulting value should be freed after use.
752 DiaFont *
753 data_font(DataNode data)
755 xmlChar *family;
756 DiaFont *font;
758 if (data_type(data)!=DATATYPE_FONT) {
759 message_error("Taking font value of non-font node.");
760 return NULL;
763 family = xmlGetProp(data, "family");
764 /* always prefer the new format */
765 if (family) {
766 DiaFontStyle style;
767 xmlChar* style_name = xmlGetProp(data, "style");
768 style = style_name ? atoi(style_name) : 0;
770 font = dia_font_new (family, style, 1.0);
771 if (family) xmlFree(family);
772 if (style_name) xmlFree(style_name);
773 } else {
774 /* Legacy format support */
775 char *name = xmlGetProp(data, "name");
776 font = dia_font_new_from_legacy_name(name);
777 xmlFree(name);
779 return font;
782 /* ***** Saving XML **** */
784 /** Create a new attribute node.
785 * @param obj_node The object node to create the attribute node under.
786 * @param attrname The name of the attribute node.
787 * @return A new attribute node.
788 * @bug Should have utility functions that creates the node and sets
789 * the value based on type.
791 AttributeNode
792 new_attribute(ObjectNode obj_node,
793 const char *attrname)
795 AttributeNode attr;
796 attr = xmlNewChild(obj_node, NULL, "attribute", NULL);
797 xmlSetProp(attr, "name", attrname);
799 return attr;
802 /** Add an attribute node to a composite node.
803 * @param composite_node The composite node.
804 * @param attrname The name of the new attribute node.
805 * @return The attribute node added.
806 * @bug This does exactly the same as new_attribute.
808 AttributeNode
809 composite_add_attribute(DataNode composite_node,
810 const char *attrname)
812 AttributeNode attr;
813 attr = xmlNewChild(composite_node, NULL, "attribute", NULL);
814 xmlSetProp(attr, "name", attrname);
816 return attr;
819 /** Add integer data to an attribute node.
820 * @param attr The attribute node.
821 * @param data The value to set.
823 void
824 data_add_int(AttributeNode attr, int data)
826 DataNode data_node;
827 char buffer[20+1]; /* Enought for 64bit int + zero */
829 g_snprintf(buffer, 20, "%d", data);
831 data_node = xmlNewChild(attr, NULL, "int", NULL);
832 xmlSetProp(data_node, "val", buffer);
835 /** Add enum data to an attribute node.
836 * @param attr The attribute node.
837 * @param data The value to set.
839 void
840 data_add_enum(AttributeNode attr, int data)
842 DataNode data_node;
843 char buffer[20+1]; /* Enought for 64bit int + zero */
845 g_snprintf(buffer, 20, "%d", data);
847 data_node = xmlNewChild(attr, NULL, "enum", NULL);
848 xmlSetProp(data_node, "val", buffer);
851 /** Add real-typed data to an attribute node.
852 * @param attr The attribute node.
853 * @param data The value to set.
855 void
856 data_add_real(AttributeNode attr, real data)
858 DataNode data_node;
859 char buffer[G_ASCII_DTOSTR_BUF_SIZE]; /* Large enought */
861 g_ascii_dtostr(buffer, G_ASCII_DTOSTR_BUF_SIZE, data);
863 data_node = xmlNewChild(attr, NULL, "real", NULL);
864 xmlSetProp(data_node, "val", buffer);
867 /** Add boolean data to an attribute node.
868 * @param attr The attribute node.
869 * @param data The value to set.
871 void
872 data_add_boolean(AttributeNode attr, int data)
874 DataNode data_node;
876 data_node = xmlNewChild(attr, NULL, "boolean", NULL);
877 if (data)
878 xmlSetProp(data_node, "val", "true");
879 else
880 xmlSetProp(data_node, "val", "false");
883 /** Convert a floating-point value to hexadecimal.
884 * @param x The floating point value.
885 * @param str A string to place the result in.
886 * @note Currently only works for 0 <= x <= 255 and will silently cap the value
887 * to those limits. Also expects str to have at least two bytes allocated,
888 * and doesn't null-terminate it. This works well for converting a color
889 * value, but is pretty much useless for other values.
891 static void
892 convert_to_hex(float x, char *str)
894 static const char hex_digit[] = "0123456789abcdef";
895 int val;
897 val = x * 255.0;
898 if (val>255)
899 val = 255;
900 if (val<0)
901 val = 0;
903 str[0] = hex_digit[val/16];
904 str[1] = hex_digit[val%16];
907 /** Add color data to an attribute node.
908 * @param attr The attribute node.
909 * @param col The value to set.
911 void
912 data_add_color(AttributeNode attr, const Color *col)
914 char buffer[1+6+1];
915 DataNode data_node;
917 buffer[0] = '#';
918 convert_to_hex(col->red, &buffer[1]);
919 convert_to_hex(col->green, &buffer[3]);
920 convert_to_hex(col->blue, &buffer[5]);
921 buffer[7] = 0;
923 data_node = xmlNewChild(attr, NULL, "color", NULL);
924 xmlSetProp(data_node, "val", buffer);
927 /** Add point data to an attribute node.
928 * @param attr The attribute node.
929 * @param point The value to set.
931 void
932 data_add_point(AttributeNode attr, const Point *point)
934 DataNode data_node;
935 gchar *buffer;
936 gchar px_buf[G_ASCII_DTOSTR_BUF_SIZE];
937 gchar py_buf[G_ASCII_DTOSTR_BUF_SIZE];
939 g_ascii_formatd(px_buf, sizeof(px_buf), "%g", point->x);
940 g_ascii_formatd(py_buf, sizeof(py_buf), "%g", point->y);
941 buffer = g_strconcat(px_buf, ",", py_buf, NULL);
943 data_node = xmlNewChild(attr, NULL, "point", NULL);
944 xmlSetProp(data_node, "val", buffer);
945 g_free(buffer);
948 /** Add rectangle data to an attribute node.
949 * @param attr The attribute node.
950 * @param rect The value to set.
952 void
953 data_add_rectangle(AttributeNode attr, const Rectangle *rect)
955 DataNode data_node;
956 gchar *buffer;
957 gchar rl_buf[G_ASCII_DTOSTR_BUF_SIZE];
958 gchar rr_buf[G_ASCII_DTOSTR_BUF_SIZE];
959 gchar rt_buf[G_ASCII_DTOSTR_BUF_SIZE];
960 gchar rb_buf[G_ASCII_DTOSTR_BUF_SIZE];
962 g_ascii_formatd(rl_buf, sizeof(rl_buf), "%g", rect->left);
963 g_ascii_formatd(rr_buf, sizeof(rr_buf), "%g", rect->right);
964 g_ascii_formatd(rt_buf, sizeof(rt_buf), "%g", rect->top);
965 g_ascii_formatd(rb_buf, sizeof(rb_buf), "%g", rect->bottom);
967 buffer = g_strconcat(rl_buf, ",", rt_buf, ";", rr_buf, ",", rb_buf, NULL);
969 data_node = xmlNewChild(attr, NULL, "rectangle", NULL);
970 xmlSetProp(data_node, "val", buffer);
972 g_free(buffer);
975 /** Add string data to an attribute node.
976 * @param attr The attribute node.
977 * @param str The value to set.
979 void
980 data_add_string(AttributeNode attr, const char *str)
982 DataNode data_node;
983 xmlChar *escaped_str;
984 xmlChar *sharped_str;
986 if (str==NULL) {
987 data_node = xmlNewChild(attr, NULL, "string", "##");
988 return;
991 escaped_str = xmlEncodeEntitiesReentrant(attr->doc,str);
993 sharped_str = g_strconcat("#", escaped_str, "#", NULL);
995 xmlFree(escaped_str);
997 data_node = xmlNewChild(attr, NULL, "string", sharped_str);
999 g_free(sharped_str);
1002 /** Add filename data to an attribute node.
1003 * @param attr The attribute node.
1004 * @param filename The value to set. This should be n the local filesystem
1005 * encoding, not utf-8.
1007 void
1008 data_add_filename(DataNode data, const char *str)
1010 char *utf8 = g_filename_to_utf8(str, -1, NULL, NULL, NULL);
1012 data_add_string(data, utf8);
1014 g_free(utf8);
1017 /** Add font data to an attribute node.
1018 * @param attr The attribute node.
1019 * @param font The value to set.
1021 void
1022 data_add_font(AttributeNode attr, const DiaFont *font)
1024 DataNode data_node;
1025 DiaFontStyle style;
1026 char buffer[20+1]; /* Enought for 64bit int + zero */
1028 data_node = xmlNewChild(attr, NULL, "font", NULL);
1029 style = dia_font_get_style (font);
1030 xmlSetProp(data_node, "family", dia_font_get_family(font));
1031 g_snprintf(buffer, 20, "%d", dia_font_get_style(font));
1033 xmlSetProp(data_node, "style", buffer);
1034 /* Legacy support: don't crash older Dia on missing 'name' attribute */
1035 xmlSetProp(data_node, "name", dia_font_get_legacy_name(font));
1038 /** Add a new composite node to an attribute node.
1039 * @param attr The attribute node to add to.
1040 * @param type The type of the new node.
1041 * @returns The new child of `attr'.
1043 DataNode
1044 data_add_composite(AttributeNode attr, const char *type)
1046 /* type can be NULL */
1047 DataNode data_node;
1049 data_node = xmlNewChild(attr, NULL, "composite", NULL);
1050 if (type != NULL)
1051 xmlSetProp(data_node, "type", type);
1053 return data_node;
1056 /** Emit a warning to the user that having UTF-8 as local charset doesn't.
1058 void
1059 warn_about_broken_libxml1(void)
1061 message_warning(_("Your local character set is UTF-8. Because of issues"
1062 " with libxml1 and the support of files generated by"
1063 " previous versions of dia, you will encounter "
1064 " problems. Please report to dia-list@gnome.org if you"
1065 " see this message."));
1068 #define BUFSIZE 2048
1069 #define OVERRUN_SAFETY 16
1071 /* diarc option */
1072 int pretty_formated_xml = TRUE;
1074 /** Save an XML document to a file.
1075 * @param filename The file to save to.
1076 * @param cur The XML document structure.
1077 * @return The return value of xmlSaveFormatFileEnc.
1078 * @bug Get the proper defn of the return value from libxml2.
1081 xmlDiaSaveFile(const char *filename,
1082 xmlDocPtr cur)
1084 int old = 0, ret;
1086 if (pretty_formated_xml)
1087 old = xmlKeepBlanksDefault (0);
1088 ret = xmlSaveFormatFileEnc (filename,cur, "UTF-8", pretty_formated_xml ? 1 : 0);
1089 if (pretty_formated_xml)
1090 xmlKeepBlanksDefault (old);
1091 return ret;