/* Dia -- an diagram creation/manipulation program * Copyright (C) 1998,1999 Alexander Larsson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** \file dia_xml.c Helper function to convert Dia's basic to and from XML */ #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include "intl.h" #include "utils.h" #include "dia_xml_libxml.h" #include "dia_xml.h" #include "geometry.h" /* For isinf() on Solaris */ #include "message.h" #ifdef G_OS_WIN32 #include /* write, close */ #endif #ifdef G_OS_WIN32 /* apparently _MSC_VER and mingw */ #include # ifndef isinf # define isinf(a) (!_finite(a)) # endif #endif #define BUFLEN 1024 /** If all files produced by dia were good XML files, we wouldn't have to do * this little gymnastic. Alas, during the libxml1 days, we were outputting * files with no encoding specification (which means UTF-8 if we're in an * asciish encoding) and strings encoded in local charset (so, we wrote * broken files). * * The following logic finds if we have a broken file, and attempts to fix * it if it's possible. If the file is correct or is unrecognisable, we pass * it untouched to libxml2. * @param filename The name of the file to check. * @param default_enc The default encoding to use if none is given. * @return The filename given if it seems ok, or the name of a new file * with fixed contents, or NULL if we couldn't read the file. The * caller should free this string and unlink the file if it is not * the same as `filename'. * @bug The many gzclose-g_free-return sequences should be refactored into * an "exception handle" (goto+label). At least for people who think goto is * better than this. I dont. --hb */ static const gchar * xml_file_check_encoding(const gchar *filename, const gchar *default_enc) { int fd = g_open (filename, O_RDONLY, 0); gzFile zf = gzdopen(fd,"rb"); gchar *buf; gchar *p,*pmax; int len; gchar *tmp,*res; int uf; gboolean well_formed_utf8; static char magic_xml[] = {0x3c,0x3f,0x78,0x6d,0x6c,0x00}; /* "=pmax) { /* whoops ? */ gzclose(zf); g_free(buf); return filename; } if (0 != strncmp(p,"version=\"",9)) { gzclose(zf); /* chicken out. */ g_free(buf); return filename; } p += 9; /* The header is rather well formed. */ if (p>=pmax) { /* whoops ? */ gzclose(zf); g_free(buf); return filename; } while ((*p != '"') && (p < pmax)) p++; p++; while (((*p == 0x20)||(*p == 0x09)||(*p == 0x0d)||(*p == 0x0a)) && (p=pmax) { /* whoops ? */ gzclose(zf); g_free(buf); return filename; } if (0 == strncmp(p,"encoding=\"",10)) { gzclose(zf); /* this file has an encoding string. Good. */ g_free(buf); return filename; } /* now let's read the whole file, to see if there are offending bits. * We can call it well formed UTF-8 if the highest isn't used */ well_formed_utf8 = TRUE; do { int i; for (i = 0; i < len; i++) if (buf[i] & 0x80 || buf[i] == '&') well_formed_utf8 = FALSE; len = gzread(zf,buf,BUFLEN); } while (len > 0 && well_formed_utf8); if (well_formed_utf8) { gzclose(zf); /* this file is utf-8 compatible */ g_free(buf); return filename; } else { gzclose(zf); /* poor man's fseek */ fd = g_open (filename, O_RDONLY, 0); zf = gzdopen(fd,"rb"); len = gzread(zf,buf,BUFLEN); } if (0 != strcmp(default_enc,"UTF-8")) { message_warning(_("The file %s has no encoding specification;\n" "assuming it is encoded in %s"), dia_message_filename(filename), default_enc); } else { gzclose(zf); /* we apply the standard here. */ g_free(buf); return filename; } tmp = getenv("TMP"); if (!tmp) tmp = getenv("TEMP"); if (!tmp) tmp = "/tmp"; res = g_strconcat(tmp,G_DIR_SEPARATOR_S,"dia-xml-fix-encodingXXXXXX",NULL); uf = g_mkstemp(res); write(uf,buf,p-buf); write(uf," encoding=\"",11); write(uf,default_enc,strlen(default_enc)); write(uf,"\" ",2); write(uf,p,pmax - p); while (1) { len = gzread(zf,buf,BUFLEN); if (len <= 0) break; write(uf,buf,len); } gzclose(zf); close(uf); g_free(buf); return res; /* caller frees the name and unlinks the file. */ } /** Parse a given file into XML, handling old broken files correctly. * @param filename The name of the file to read. * @returns An XML document parsed from the file. * @see xmlParseFile() in the XML2 library for details on the return value. */ xmlDocPtr xmlDiaParseFile(const char *filename) { G_CONST_RETURN char *local_charset = NULL; if ( !g_get_charset(&local_charset) && local_charset) { /* we're not in an UTF-8 environment. */ const gchar *fname = xml_file_check_encoding(filename,local_charset); if (fname != filename) { /* We've got a corrected file to parse. */ xmlDocPtr ret = xmlDoParseFile(fname); unlink(fname); /* printf("has read %s instead of %s\n",fname,filename); */ g_free((void *)fname); return ret; } else { /* the XML file is good. libxml is "old enough" to handle it correctly. */ return xmlDoParseFile(filename); } } else { return xmlDoParseFile(filename); } } /** Parse an xml file from a filename given in Dia's/GLib's filename encoding * @param filename A file to parse. On win32 the filename encoding is utf-8 since GLib 2.6 * @return An XML document. */ xmlDocPtr xmlDoParseFile(const char *filename) { return xmlParseFile(filename); } /** Find a named attribute node in an XML object node. * Note that Dia has a concept of attribute node that is not the same * as an XML attribute. * @param obj_node The node to look in. * @param attrname The name of the attribute node to find. * @return The node matching the given name, or NULL if none found. */ AttributeNode object_find_attribute(ObjectNode obj_node, const char *attrname) { AttributeNode attr; xmlChar *name; while (obj_node && xmlIsBlankNode(obj_node)) obj_node = obj_node->next; if (!obj_node) return NULL; attr = obj_node->xmlChildrenNode; while (attr != NULL) { if (xmlIsBlankNode(attr)) { attr = attr->next; continue; } name = xmlGetProp(attr, "name"); if ( (name!=NULL) && (strcmp(name, attrname)==0) ) { xmlFree(name); return attr; } if (name) xmlFree(name); attr = attr->next; } return NULL; } /** Find an attribute in a composite XML node. * @param composite_node The composite node to search. * @param attrname The name of the attribute node to find. * @return The desired node, or NULL if none exists in `composite_node'. * @bug Describe in more detail how a composite node differs from an * object node. */ AttributeNode composite_find_attribute(DataNode composite_node, const char *attrname) { AttributeNode attr; xmlChar *name; while (composite_node && xmlIsBlankNode(composite_node)) composite_node = composite_node->next; if (!composite_node) return NULL; attr = composite_node->xmlChildrenNode; while (attr != NULL) { if (xmlIsBlankNode(attr)) { attr = attr->next; continue; } name = xmlGetProp(attr, "name"); if ( (name!=NULL) && (strcmp(name, attrname)==0) ) { xmlFree(name); return attr; } if (name) xmlFree(name); attr = attr->next; } return NULL; } /** The number of non-blank data nodes in an attribute node. * @param attribute The attribute node to read from. * @returns The number of non-blank data nodes in the node. */ int attribute_num_data(AttributeNode attribute) { xmlNode *data; int nr=0; data = attribute ? attribute->xmlChildrenNode : NULL; while (data != NULL) { if (xmlIsBlankNode(data)) { data = data->next; continue; } nr++; data = data->next; } return nr; } /** Get the first data node in an attribute node. * @param attribute The attribute node to look through. * @return The first non-black data node in the attribute node. */ DataNode attribute_first_data(AttributeNode attribute) { xmlNode *data = attribute ? attribute->xmlChildrenNode : NULL; while (data && xmlIsBlankNode(data)) data = data->next; return (DataNode) data; } /** Get the next data node (sibling). * @param data A data node to start from (e.g. just processed) * @returns The next sibling data node. */ DataNode data_next(DataNode data) { if (data) { data = data->next; while (data && xmlIsBlankNode(data)) data = data->next; } return (DataNode) data; } /** Get the type of a data node. * @param data The data node. * @return The type that the data node defines, or 0 on error. In case of * error, an error message is displayed. * @note This function does a number of strcmp calls, which may not be the * fastest way to check if a node is of the expected type. * @bug Make functions that check quickly if a node is of a specific type * (but profile first). */ DataType data_type(DataNode data) { const char *name; name = data ? (const char *)data->name : (const char *)""; if (strcmp(name, "composite")==0) { return DATATYPE_COMPOSITE; } else if (strcmp(name, "int")==0) { return DATATYPE_INT; } else if (strcmp(name, "enum")==0) { return DATATYPE_ENUM; } else if (strcmp(name, "real")==0) { return DATATYPE_REAL; } else if (strcmp(name, "boolean")==0) { return DATATYPE_BOOLEAN; } else if (strcmp(name, "color")==0) { return DATATYPE_COLOR; } else if (strcmp(name, "point")==0) { return DATATYPE_POINT; } else if (strcmp(name, "rectangle")==0) { return DATATYPE_RECTANGLE; } else if (strcmp(name, "string")==0) { return DATATYPE_STRING; } else if (strcmp(name, "font")==0) { return DATATYPE_FONT; } message_error("Unknown type of DataNode"); return 0; } /** Return the value of an integer-type data node. * @param data The data node to read from. * @returns The integer value found in the node. If the node is not an * integer node, an error message is displayed and 0 is returned. */ int data_int(DataNode data) { xmlChar *val; int res; if (data_type(data)!=DATATYPE_INT) { message_error("Taking int value of non-int node."); return 0; } val = xmlGetProp(data, "val"); res = atoi(val); if (val) xmlFree(val); return res; } /** Return the value of an enum-type data node. * @param data The data node to read from. * @returns The enum value found in the node. If the node is not an * enum node, an error message is displayed and 0 is returned. */ int data_enum(DataNode data) { xmlChar *val; int res; if (data_type(data)!=DATATYPE_ENUM) { message_error("Taking enum value of non-enum node."); return 0; } val = xmlGetProp(data, "val"); res = atoi(val); if (val) xmlFree(val); return res; } /** Return the value of a real-type data node. * @param data The data node to read from. * @returns The real value found in the node. If the node is not a * real-type node, an error message is displayed and 0.0 is returned. */ real data_real(DataNode data) { xmlChar *val; real res; if (data_type(data)!=DATATYPE_REAL) { message_error("Taking real value of non-real node."); return 0; } val = xmlGetProp(data, "val"); res = g_ascii_strtod(val, NULL); if (val) xmlFree(val); return res; } /** Return the value of a boolean-type data node. * @param data The data node to read from. * @returns The boolean value found in the node. If the node is not a * boolean node, an error message is displayed and FALSE is returned. */ int data_boolean(DataNode data) { xmlChar *val; int res; if (data_type(data)!=DATATYPE_BOOLEAN) { message_error("Taking boolean value of non-boolean node."); return 0; } val = xmlGetProp(data, "val"); if ((val) && (strcmp(val, "true")==0)) res = TRUE; else res = FALSE; if (val) xmlFree(val); return res; } /** Return the integer value of a hex digit. * @param c A hex digit, one of 0-9, a-f or A-F. * @returns The value of the digit, i.e. 0-15. If a non-gex digit is given * an error message is displayed to the user, and 0 is returned. */ static int hex_digit(char c) { if ((c>='0') && (c<='9')) return c-'0'; if ((c>='a') && (c<='f')) return (c-'a') + 10; if ((c>='A') && (c<='F')) return (c-'A') + 10; message_error("wrong hex digit %c", c); return 0; } /** Return the value of a color-type data node. * @param data The XML node to read from * @param col A place to store the resulting RGB values. If the node does * not contain a valid color value, an error message is displayed to the * user, and `col' is unchanged. * @note Could be cool to use RGBA data here, even if we can't display it yet. */ void data_color(DataNode data, Color *col) { xmlChar *val; int r=0, g=0, b=0; if (data_type(data)!=DATATYPE_COLOR) { message_error("Taking color value of non-color node."); return; } val = xmlGetProp(data, "val"); /* Format #RRGGBB */ /* 0123456 */ if ((val) && (strlen(val)>=7)) { r = hex_digit(val[1])*16 + hex_digit(val[2]); g = hex_digit(val[3])*16 + hex_digit(val[4]); b = hex_digit(val[5])*16 + hex_digit(val[6]); } if (val) xmlFree(val); col->red = (float)(r/255.0); col->green = (float)(g/255.0); col->blue = (float)(b/255.0); } /** Return the value of a point-type data node. * @param data The XML node to read from * @param point A place to store the resulting x, y values. If the node does * not contain a valid point value, an error message is displayed to the * user, and `point' is unchanged. */ void data_point(DataNode data, Point *point) { xmlChar *val; gchar *str; real ax,ay; if (data_type(data)!=DATATYPE_POINT) { message_error(_("Taking point value of non-point node.")); return; } val = xmlGetProp(data, "val"); point->x = g_ascii_strtod(val, &str); ax = fabs(point->x); if ((ax > 1e9) || ((ax < 1e-9) && (ax != 0.0)) || isnan(ax) || isinf(ax)) { /* there is no provision to keep values larger when saving, * so do this 'reduction' silent */ if (!(ax < 1e-9)) g_warning(_("Incorrect x Point value \"%s\" %f; discarding it."),val,point->x); point->x = 0.0; } while ((*str != ',') && (*str!=0)) str++; if (*str==0){ point->y = 0.0; g_warning(_("Error parsing point.")); xmlFree(val); return; } point->y = g_ascii_strtod(str+1, NULL); ay = fabs(point->y); if ((ay > 1e9) || ((ay < 1e-9) && (ay != 0.0)) || isnan(ay) || isinf(ay)) { if (!(ay < 1e-9)) /* don't bother with useless warnings (see above) */ g_warning(_("Incorrect y Point value \"%s\" %f; discarding it."),str+1,point->y); point->y = 0.0; } xmlFree(val); } /** Return the value of a rectangle-type data node. * @param data The data node to read from. * @param rect A place to store the resulting values. If the node does * not contain a valid rectangle value, an error message is displayed to the * user, and `rect' is unchanged. */ void data_rectangle(DataNode data, Rectangle *rect) { xmlChar *val; gchar *str; if (data_type(data)!=DATATYPE_RECTANGLE) { message_error("Taking rectangle value of non-rectangle node."); return; } val = xmlGetProp(data, "val"); rect->left = g_ascii_strtod(val, &str); while ((*str != ',') && (*str!=0)) str++; if (*str==0){ message_error("Error parsing rectangle."); xmlFree(val); return; } rect->top = g_ascii_strtod(str+1, &str); while ((*str != ';') && (*str!=0)) str++; if (*str==0){ message_error("Error parsing rectangle."); xmlFree(val); return; } rect->right = g_ascii_strtod(str+1, &str); while ((*str != ',') && (*str!=0)) str++; if (*str==0){ message_error("Error parsing rectangle."); xmlFree(val); return; } rect->bottom = g_ascii_strtod(str+1, NULL); xmlFree(val); } /** Return the value of a string-type data node. * @param data The data node to read from. * @returns The string value found in the node. If the node is not a * string node, an error message is displayed and NULL is returned. The * returned valuee should be freed after use. * @note For historical reasons, strings in Dia XML are surrounded by ##. */ gchar * data_string(DataNode data) { xmlChar *val; gchar *str, *p,*str2; int len; if (data_type(data)!=DATATYPE_STRING) { message_error("Taking string value of non-string node."); return NULL; } val = xmlGetProp(data, "val"); if (val != NULL) { /* Old kind of string. Left for backwards compatibility */ str = g_malloc(4 * (sizeof(char)*(strlen(val)+1))); /* extra room for UTF8 */ p = str; while (*val) { if (*val == '\\') { val++; switch (*val) { case '0': /* Just skip this. \0 means nothing */ break; case 'n': *p++ = '\n'; break; case 't': *p++ = '\t'; break; case '\\': *p++ = '\\'; break; default: message_error("Error in string tag."); } } else { *p++ = *val; } val++; } *p = 0; xmlFree(val); str2 = g_strdup(str); /* to remove the extra space */ g_free(str); return str2; } if (data->xmlChildrenNode!=NULL) { p = xmlNodeListGetString(data->doc, data->xmlChildrenNode, TRUE); if (*p!='#') message_error("Error in file, string not starting with #\n"); len = strlen(p)-1; /* Ignore first '#' */ str = g_malloc(len+1); strncpy(str, p+1, len); str[len]=0; /* For safety */ str[strlen(str)-1] = 0; /* Remove last '#' */ xmlFree(p); return str; } return NULL; } /** Return the value of a filename-type data node. * @param data The data node to read from. * @return The filename value found in the node. If the node is not a * filename node, an error message is displayed and NULL is returned. * The resulting string is in the local filesystem's encoding rather than * UTF-8, and should be freed after use. * @bug data_string() can return NULL, what does g_filename_from_utf8 do then? */ char * data_filename(DataNode data) { char *utf8 = data_string(data); char *filename = g_filename_from_utf8(utf8, -1, NULL, NULL, NULL); g_free(utf8); return filename; } /** Return the value of a font-type data node. This handles both the current * format (family and style) and the old format (name). * @param data The data node to read from. * @return The font value found in the node. If the node is not a * font node, an error message is displayed and NULL is returned. The * resulting value should be freed after use. */ DiaFont * data_font(DataNode data) { xmlChar *family; DiaFont *font; if (data_type(data)!=DATATYPE_FONT) { message_error("Taking font value of non-font node."); return NULL; } family = xmlGetProp(data, "family"); /* always prefer the new format */ if (family) { DiaFontStyle style; xmlChar* style_name = xmlGetProp(data, "style"); style = style_name ? atoi(style_name) : 0; font = dia_font_new (family, style, 1.0); if (family) xmlFree(family); if (style_name) xmlFree(style_name); } else { /* Legacy format support */ char *name = xmlGetProp(data, "name"); font = dia_font_new_from_legacy_name(name); xmlFree(name); } return font; } /* ***** Saving XML **** */ /** Create a new attribute node. * @param obj_node The object node to create the attribute node under. * @param attrname The name of the attribute node. * @return A new attribute node. * @bug Should have utility functions that creates the node and sets * the value based on type. */ AttributeNode new_attribute(ObjectNode obj_node, const char *attrname) { AttributeNode attr; attr = xmlNewChild(obj_node, NULL, "attribute", NULL); xmlSetProp(attr, "name", attrname); return attr; } /** Add an attribute node to a composite node. * @param composite_node The composite node. * @param attrname The name of the new attribute node. * @return The attribute node added. * @bug This does exactly the same as new_attribute. */ AttributeNode composite_add_attribute(DataNode composite_node, const char *attrname) { AttributeNode attr; attr = xmlNewChild(composite_node, NULL, "attribute", NULL); xmlSetProp(attr, "name", attrname); return attr; } /** Add integer data to an attribute node. * @param attr The attribute node. * @param data The value to set. */ void data_add_int(AttributeNode attr, int data) { DataNode data_node; char buffer[20+1]; /* Enought for 64bit int + zero */ g_snprintf(buffer, 20, "%d", data); data_node = xmlNewChild(attr, NULL, "int", NULL); xmlSetProp(data_node, "val", buffer); } /** Add enum data to an attribute node. * @param attr The attribute node. * @param data The value to set. */ void data_add_enum(AttributeNode attr, int data) { DataNode data_node; char buffer[20+1]; /* Enought for 64bit int + zero */ g_snprintf(buffer, 20, "%d", data); data_node = xmlNewChild(attr, NULL, "enum", NULL); xmlSetProp(data_node, "val", buffer); } /** Add real-typed data to an attribute node. * @param attr The attribute node. * @param data The value to set. */ void data_add_real(AttributeNode attr, real data) { DataNode data_node; char buffer[G_ASCII_DTOSTR_BUF_SIZE]; /* Large enought */ g_ascii_dtostr(buffer, G_ASCII_DTOSTR_BUF_SIZE, data); data_node = xmlNewChild(attr, NULL, "real", NULL); xmlSetProp(data_node, "val", buffer); } /** Add boolean data to an attribute node. * @param attr The attribute node. * @param data The value to set. */ void data_add_boolean(AttributeNode attr, int data) { DataNode data_node; data_node = xmlNewChild(attr, NULL, "boolean", NULL); if (data) xmlSetProp(data_node, "val", "true"); else xmlSetProp(data_node, "val", "false"); } /** Convert a floating-point value to hexadecimal. * @param x The floating point value. * @param str A string to place the result in. * @note Currently only works for 0 <= x <= 255 and will silently cap the value * to those limits. Also expects str to have at least two bytes allocated, * and doesn't null-terminate it. This works well for converting a color * value, but is pretty much useless for other values. */ static void convert_to_hex(float x, char *str) { static const char hex_digit[] = "0123456789abcdef"; int val; val = x * 255.0; if (val>255) val = 255; if (val<0) val = 0; str[0] = hex_digit[val/16]; str[1] = hex_digit[val%16]; } /** Add color data to an attribute node. * @param attr The attribute node. * @param col The value to set. */ void data_add_color(AttributeNode attr, const Color *col) { char buffer[1+6+1]; DataNode data_node; buffer[0] = '#'; convert_to_hex(col->red, &buffer[1]); convert_to_hex(col->green, &buffer[3]); convert_to_hex(col->blue, &buffer[5]); buffer[7] = 0; data_node = xmlNewChild(attr, NULL, "color", NULL); xmlSetProp(data_node, "val", buffer); } /** Add point data to an attribute node. * @param attr The attribute node. * @param point The value to set. */ void data_add_point(AttributeNode attr, const Point *point) { DataNode data_node; gchar *buffer; gchar px_buf[G_ASCII_DTOSTR_BUF_SIZE]; gchar py_buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_formatd(px_buf, sizeof(px_buf), "%g", point->x); g_ascii_formatd(py_buf, sizeof(py_buf), "%g", point->y); buffer = g_strconcat(px_buf, ",", py_buf, NULL); data_node = xmlNewChild(attr, NULL, "point", NULL); xmlSetProp(data_node, "val", buffer); g_free(buffer); } /** Add rectangle data to an attribute node. * @param attr The attribute node. * @param rect The value to set. */ void data_add_rectangle(AttributeNode attr, const Rectangle *rect) { DataNode data_node; gchar *buffer; gchar rl_buf[G_ASCII_DTOSTR_BUF_SIZE]; gchar rr_buf[G_ASCII_DTOSTR_BUF_SIZE]; gchar rt_buf[G_ASCII_DTOSTR_BUF_SIZE]; gchar rb_buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_formatd(rl_buf, sizeof(rl_buf), "%g", rect->left); g_ascii_formatd(rr_buf, sizeof(rr_buf), "%g", rect->right); g_ascii_formatd(rt_buf, sizeof(rt_buf), "%g", rect->top); g_ascii_formatd(rb_buf, sizeof(rb_buf), "%g", rect->bottom); buffer = g_strconcat(rl_buf, ",", rt_buf, ";", rr_buf, ",", rb_buf, NULL); data_node = xmlNewChild(attr, NULL, "rectangle", NULL); xmlSetProp(data_node, "val", buffer); g_free(buffer); } /** Add string data to an attribute node. * @param attr The attribute node. * @param str The value to set. */ void data_add_string(AttributeNode attr, const char *str) { DataNode data_node; xmlChar *escaped_str; xmlChar *sharped_str; if (str==NULL) { data_node = xmlNewChild(attr, NULL, "string", "##"); return; } escaped_str = xmlEncodeEntitiesReentrant(attr->doc,str); sharped_str = g_strconcat("#", escaped_str, "#", NULL); xmlFree(escaped_str); data_node = xmlNewChild(attr, NULL, "string", sharped_str); g_free(sharped_str); } /** Add filename data to an attribute node. * @param attr The attribute node. * @param filename The value to set. This should be n the local filesystem * encoding, not utf-8. */ void data_add_filename(DataNode data, const char *str) { char *utf8 = g_filename_to_utf8(str, -1, NULL, NULL, NULL); data_add_string(data, utf8); g_free(utf8); } /** Add font data to an attribute node. * @param attr The attribute node. * @param font The value to set. */ void data_add_font(AttributeNode attr, const DiaFont *font) { DataNode data_node; DiaFontStyle style; char buffer[20+1]; /* Enought for 64bit int + zero */ data_node = xmlNewChild(attr, NULL, "font", NULL); style = dia_font_get_style (font); xmlSetProp(data_node, "family", dia_font_get_family(font)); g_snprintf(buffer, 20, "%d", dia_font_get_style(font)); xmlSetProp(data_node, "style", buffer); /* Legacy support: don't crash older Dia on missing 'name' attribute */ xmlSetProp(data_node, "name", dia_font_get_legacy_name(font)); } /** Add a new composite node to an attribute node. * @param attr The attribute node to add to. * @param type The type of the new node. * @returns The new child of `attr'. */ DataNode data_add_composite(AttributeNode attr, const char *type) { /* type can be NULL */ DataNode data_node; data_node = xmlNewChild(attr, NULL, "composite", NULL); if (type != NULL) xmlSetProp(data_node, "type", type); return data_node; } /** Emit a warning to the user that having UTF-8 as local charset doesn't. */ void warn_about_broken_libxml1(void) { message_warning(_("Your local character set is UTF-8. Because of issues" " with libxml1 and the support of files generated by" " previous versions of dia, you will encounter " " problems. Please report to dia-list@gnome.org if you" " see this message.")); } #define BUFSIZE 2048 #define OVERRUN_SAFETY 16 /* diarc option */ int pretty_formated_xml = TRUE; /** Save an XML document to a file. * @param filename The file to save to. * @param cur The XML document structure. * @return The return value of xmlSaveFormatFileEnc. * @bug Get the proper defn of the return value from libxml2. */ int xmlDiaSaveFile(const char *filename, xmlDocPtr cur) { int old = 0, ret; if (pretty_formated_xml) old = xmlKeepBlanksDefault (0); ret = xmlSaveFormatFileEnc (filename,cur, "UTF-8", pretty_formated_xml ? 1 : 0); if (pretty_formated_xml) xmlKeepBlanksDefault (old); return ret; }