/* XmlLabelParser.cpp * * Copyright (C) 2014-2016 Jim Evins * * This file is part of gLabels-qt. * * gLabels-qt 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 3 of the License, or * (at your option) any later version. * * gLabels-qt 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 gLabels-qt. If not, see . */ #include "XmlLabelParser.h" #include "BarcodeBackends.h" #include "EnumUtil.h" #include "LabelModel.h" #include "LabelModelObject.h" #include "LabelModelBarcodeObject.h" #include "LabelModelBoxObject.h" #include "LabelModelEllipseObject.h" #include "LabelModelImageObject.h" #include "LabelModelLineObject.h" #include "LabelModelTextObject.h" #include "XmlTemplateParser.h" #include "XmlUtil.h" #include "DataCache.h" #include "Merge/Factory.h" #include #include #include #include #include #include namespace glabels { LabelModel* XmlLabelParser::readFile( const QString& fileName ) { QFile file( fileName ); if ( !file.open( QFile::ReadOnly ) ) { qWarning() << "Error: Cannot read file " << qPrintable(fileName) << ": " << file.errorString(); return nullptr; } QDomDocument doc; bool success; QString errorString; int errorLine; int errorColumn; QByteArray rawData = file.readAll(); if ( ((rawData[0]&0xFF) == 0x1F) && ((rawData[1]&0xFF) == 0x8b) ) // gzip magic number 0x1F, 0x8B { // gzip compressed format QByteArray unzippedData; gunzip( rawData, unzippedData ); success = doc.setContent( unzippedData, false, &errorString, &errorLine, &errorColumn ); } else { // plain text success = doc.setContent( rawData, false, &errorString, &errorLine, &errorColumn ); } if ( !success ) { qWarning() << "Error: Parse error at line " << errorLine << "column " << errorColumn << ": " << errorString; return nullptr; } QDomElement root = doc.documentElement(); if ( root.tagName() != "Glabels-document" ) { qWarning() << "Error: Not a Glabels-document file"; return nullptr; } return parseRootNode( root ); } LabelModel* XmlLabelParser::readBuffer( const QByteArray& buffer ) { QDomDocument doc; QString errorString; int errorLine; int errorColumn; if ( !doc.setContent( buffer, false, &errorString, &errorLine, &errorColumn ) ) { qWarning() << "Error: Parse error at line " << errorLine << "column " << errorColumn << ": " << errorString; return nullptr; } QDomElement root = doc.documentElement(); if ( root.tagName() != "Glabels-document" ) { qWarning() << "Error: Not a Glabels-document file"; return nullptr; } return parseRootNode( root ); } QList XmlLabelParser::deserializeObjects( const QByteArray& buffer ) { QList list; QDomDocument doc; QString errorString; int errorLine; int errorColumn; if ( !doc.setContent( buffer, false, &errorString, &errorLine, &errorColumn ) ) { qWarning() << "Error: Parse error at line " << errorLine << "column " << errorColumn << ": " << errorString; return list; } QDomElement root = doc.documentElement(); if ( root.tagName() != "Glabels-objects" ) { qWarning() << "Error: Not a Glabels-objects stream"; return list; } /* Pass 1, extract data nodes to pre-load cache. */ DataCache data; for ( QDomNode child = root.firstChild(); !child.isNull(); child = child.nextSibling() ) { if ( child.toElement().tagName() == "Data" ) { parseDataNode( child.toElement(), data ); } } /* Pass 2, now extract objects. */ for ( QDomNode child = root.firstChild(); !child.isNull(); child = child.nextSibling() ) { if ( child.toElement().tagName() == "Objects" ) { list = parseObjectsNode( child.toElement(), data ); } } return list; } void XmlLabelParser::gunzip( const QByteArray& data, QByteArray& result ) { result.clear(); if (data.size() <= 4) { qWarning("XmlLabelParser::gunzip: Input data is truncated"); return; } // setup stream for inflate() z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = data.size(); strm.next_in = (Bytef*)(data.data()); int ret = inflateInit2(&strm, MAX_WBITS + 16); // gzip decoding if (ret != Z_OK) { return; } static const int CHUNK_SIZE = 1024; char out[CHUNK_SIZE]; // run inflate(), one chunk at a time do { strm.avail_out = CHUNK_SIZE; strm.next_out = (Bytef*)(out); ret = inflate(&strm, Z_NO_FLUSH); Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered if ( (ret == Z_NEED_DICT) || (ret == Z_DATA_ERROR) || (ret == Z_MEM_ERROR) ) { // clean up inflateEnd(&strm); return; } result.append(out, CHUNK_SIZE - strm.avail_out); } while (strm.avail_out == 0); // clean up inflateEnd(&strm); } LabelModel* XmlLabelParser::parseRootNode( const QDomElement &node ) { QString version = XmlUtil::getStringAttr( node, "version", "" ); if ( version != "4.0" ) { qWarning() << "TODO: compatability mode."; } LabelModel* label = new LabelModel(); /* Pass 1, extract data nodes to pre-load cache. */ DataCache data; for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) { if ( child.toElement().tagName() == "Data" ) { parseDataNode( child.toElement(), data ); } } /* Pass 2, now extract everything else. */ for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) { QString tagName = child.toElement().tagName(); if ( tagName == "Template" ) { Template* tmplate = XmlTemplateParser().parseTemplateNode( child.toElement() ); if ( tmplate == nullptr ) { qWarning() << "Unable to parse template"; delete label; return nullptr; } label->setTmplate( tmplate ); } else if ( tagName == "Objects" ) { label->setRotate( parseRotateAttr( child.toElement() ) ); QList list = parseObjectsNode( child.toElement(), data ); foreach ( LabelModelObject* object, list ) { label->addObject( object ); } } else if ( tagName == "Merge" ) { parseMergeNode( child.toElement(), label ); } else if ( tagName == "Data" ) { /* Handled in pass 1. */ } else if ( !child.isComment() ) { qWarning() << "Unexpected" << node.tagName() << "child:" << tagName; } } label->clearModified(); return label; } QList XmlLabelParser::parseObjectsNode( const QDomElement &node, const DataCache& data ) { QList list; for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) { QString tagName = child.toElement().tagName(); if ( tagName == "Object-box" ) { list.append( parseObjectBoxNode( child.toElement() ) ); } else if ( tagName == "Object-ellipse" ) { list.append( parseObjectEllipseNode( child.toElement() ) ); } else if ( tagName == "Object-line" ) { list.append( parseObjectLineNode( child.toElement() ) ); } else if ( tagName == "Object-text" ) { list.append( parseObjectTextNode( child.toElement() ) ); } else if ( tagName == "Object-image" ) { list.append( parseObjectImageNode( child.toElement(), data ) ); } else if ( tagName == "Object-barcode" ) { list.append( parseObjectBarcodeNode( child.toElement() ) ); } else if ( !child.isComment() ) { qWarning() << "Unexpected" << node.tagName() << "child:" << tagName; } } return list; } LabelModelBoxObject* XmlLabelParser::parseObjectBoxNode( const QDomElement &node ) { /* position attrs */ Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 ); /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); /* line attrs */ Distance lineWidth = XmlUtil::getLengthAttr( node, "line_width", 1.0 ); QString key = XmlUtil::getStringAttr( node, "line_color_field", "" ); bool field_flag = !key.isEmpty(); uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0 ); ColorNode lineColorNode( field_flag, color, key ); /* fill attrs */ key = XmlUtil::getStringAttr( node, "fill_color_field", "" ); field_flag = !key.isEmpty(); color = XmlUtil::getUIntAttr( node, "fill_color", 0 ); ColorNode fillColorNode( field_flag, color, key ); /* affine attrs */ double a[6]; a[0] = XmlUtil::getDoubleAttr( node, "a0", 1.0 ); a[1] = XmlUtil::getDoubleAttr( node, "a1", 0.0 ); a[2] = XmlUtil::getDoubleAttr( node, "a2", 0.0 ); a[3] = XmlUtil::getDoubleAttr( node, "a3", 1.0 ); a[4] = XmlUtil::getDoubleAttr( node, "a4", 0.0 ); a[5] = XmlUtil::getDoubleAttr( node, "a5", 0.0 ); /* shadow attrs */ bool shadowState = XmlUtil::getBoolAttr( node, "shadow", false ); Distance shadowX = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 ); Distance shadowY = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 ); double shadowOpacity = XmlUtil::getDoubleAttr( node, "shadow_opacity", 1.0 ); key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); ColorNode shadowColorNode( field_flag, color, key ); return new LabelModelBoxObject( x0, y0, w, h, lineWidth, lineColorNode, fillColorNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } LabelModelEllipseObject* XmlLabelParser::parseObjectEllipseNode( const QDomElement &node ) { /* position attrs */ Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 ); /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); /* line attrs */ Distance lineWidth = XmlUtil::getLengthAttr( node, "line_width", 1.0 ); QString key = XmlUtil::getStringAttr( node, "line_color_field", "" ); bool field_flag = !key.isEmpty(); uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0 ); ColorNode lineColorNode( field_flag, color, key ); /* fill attrs */ key = XmlUtil::getStringAttr( node, "fill_color_field", "" ); field_flag = !key.isEmpty(); color = XmlUtil::getUIntAttr( node, "fill_color", 0 ); ColorNode fillColorNode( field_flag, color, key ); /* affine attrs */ double a[6]; a[0] = XmlUtil::getDoubleAttr( node, "a0", 1.0 ); a[1] = XmlUtil::getDoubleAttr( node, "a1", 0.0 ); a[2] = XmlUtil::getDoubleAttr( node, "a2", 0.0 ); a[3] = XmlUtil::getDoubleAttr( node, "a3", 1.0 ); a[4] = XmlUtil::getDoubleAttr( node, "a4", 0.0 ); a[5] = XmlUtil::getDoubleAttr( node, "a5", 0.0 ); /* shadow attrs */ bool shadowState = XmlUtil::getBoolAttr( node, "shadow", false ); Distance shadowX = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 ); Distance shadowY = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 ); double shadowOpacity = XmlUtil::getDoubleAttr( node, "shadow_opacity", 1.0 ); key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); ColorNode shadowColorNode( field_flag, color, key ); return new LabelModelEllipseObject( x0, y0, w, h, lineWidth, lineColorNode, fillColorNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } LabelModelLineObject* XmlLabelParser::parseObjectLineNode( const QDomElement &node ) { /* position attrs */ Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 ); /* size attrs of line */ Distance dx = XmlUtil::getLengthAttr( node, "dx", 0 ); Distance dy = XmlUtil::getLengthAttr( node, "dy", 0 ); /* line attrs */ Distance lineWidth = XmlUtil::getLengthAttr( node, "line_width", 1.0 ); QString key = XmlUtil::getStringAttr( node, "line_color_field", "" ); bool field_flag = !key.isEmpty(); uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0 ); ColorNode lineColorNode( field_flag, color, key ); /* affine attrs */ double a[6]; a[0] = XmlUtil::getDoubleAttr( node, "a0", 1.0 ); a[1] = XmlUtil::getDoubleAttr( node, "a1", 0.0 ); a[2] = XmlUtil::getDoubleAttr( node, "a2", 0.0 ); a[3] = XmlUtil::getDoubleAttr( node, "a3", 1.0 ); a[4] = XmlUtil::getDoubleAttr( node, "a4", 0.0 ); a[5] = XmlUtil::getDoubleAttr( node, "a5", 0.0 ); /* shadow attrs */ bool shadowState = XmlUtil::getBoolAttr( node, "shadow", false ); Distance shadowX = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 ); Distance shadowY = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 ); double shadowOpacity = XmlUtil::getDoubleAttr( node, "shadow_opacity", 1.0 ); key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); ColorNode shadowColorNode( field_flag, color, key ); return new LabelModelLineObject( x0, y0, dx, dy, lineWidth, lineColorNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } LabelModelImageObject* XmlLabelParser::parseObjectImageNode( const QDomElement &node, const DataCache& data ) { /* position attrs */ Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 ); /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); /* file attrs */ QString key = XmlUtil::getStringAttr( node, "src_field", "" ); bool field_flag = !key.isEmpty(); QString filename = XmlUtil::getStringAttr( node, "src", "" ); TextNode filenameNode( field_flag, field_flag ? key : filename ); /* affine attrs */ double a[6]; a[0] = XmlUtil::getDoubleAttr( node, "a0", 1.0 ); a[1] = XmlUtil::getDoubleAttr( node, "a1", 0.0 ); a[2] = XmlUtil::getDoubleAttr( node, "a2", 0.0 ); a[3] = XmlUtil::getDoubleAttr( node, "a3", 1.0 ); a[4] = XmlUtil::getDoubleAttr( node, "a4", 0.0 ); a[5] = XmlUtil::getDoubleAttr( node, "a5", 0.0 ); /* shadow attrs */ bool shadowState = XmlUtil::getBoolAttr( node, "shadow", false ); Distance shadowX = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 ); Distance shadowY = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 ); double shadowOpacity = XmlUtil::getDoubleAttr( node, "shadow_opacity", 1.0 ); key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); uint32_t color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); ColorNode shadowColorNode( field_flag, color, key ); if ( filenameNode.isField() ) { return new LabelModelImageObject( x0, y0, w, h, filenameNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } else { if ( data.hasImage( filename ) ) { return new LabelModelImageObject( x0, y0, w, h, filename, data.getImage( filename ), QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } else if ( data.hasSvg( filename ) ) { return new LabelModelImageObject( x0, y0, w, h, filename, data.getSvg( filename ), QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } else { qWarning() << "Embedded file" << filename << "missing. Trying actual file."; return new LabelModelImageObject( x0, y0, w, h, filenameNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } } } LabelModelBarcodeObject* XmlLabelParser::parseObjectBarcodeNode( const QDomElement &node ) { /* position attrs */ Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 ); /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); /* barcode attrs */ BarcodeStyle bcStyle = BarcodeBackends::style( XmlUtil::getStringAttr( node, "backend", "" ), XmlUtil::getStringAttr( node, "style", "") ); bool bcTextFlag = XmlUtil::getBoolAttr( node, "text", true ); bool bcChecksumFlag = XmlUtil::getBoolAttr( node, "checksum", true ); QString key = XmlUtil::getStringAttr( node, "color_field", "" ); bool field_flag = !key.isEmpty(); uint32_t color = XmlUtil::getUIntAttr( node, "color", 0 ); ColorNode bcColorNode( field_flag, color, key ); QString bcData = XmlUtil::getStringAttr( node, "data", "" ); /* affine attrs */ double a[6]; a[0] = XmlUtil::getDoubleAttr( node, "a0", 1.0 ); a[1] = XmlUtil::getDoubleAttr( node, "a1", 0.0 ); a[2] = XmlUtil::getDoubleAttr( node, "a2", 0.0 ); a[3] = XmlUtil::getDoubleAttr( node, "a3", 1.0 ); a[4] = XmlUtil::getDoubleAttr( node, "a4", 0.0 ); a[5] = XmlUtil::getDoubleAttr( node, "a5", 0.0 ); return new LabelModelBarcodeObject( x0, y0, w, h, bcStyle, bcTextFlag, bcChecksumFlag, bcData, bcColorNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ) ); } LabelModelTextObject* XmlLabelParser::parseObjectTextNode( const QDomElement &node ) { /* position attrs */ Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 ); /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); /* color attr */ QString key = XmlUtil::getStringAttr( node, "color_field", "" ); bool field_flag = !key.isEmpty(); uint32_t color = XmlUtil::getUIntAttr( node, "color", 0 ); ColorNode textColorNode( field_flag, color, key ); /* font attrs */ QString fontFamily = XmlUtil::getStringAttr( node, "font_family", "Sans" ); double fontSize = XmlUtil::getDoubleAttr( node, "font_size", 10 ); QFont::Weight fontWeight = EnumUtil::stringToWeight( XmlUtil::getStringAttr( node, "font_weight", "normal" ) ); bool fontItalicFlag = XmlUtil::getBoolAttr( node, "font_italic", false ); bool fontUnderlineFlag = XmlUtil::getBoolAttr( node, "font_underline", false ); /* text attrs */ double textLineSpacing = XmlUtil::getDoubleAttr( node, "line_spacing", 1 ); Qt::Alignment textHAlign = EnumUtil::stringToHAlign( XmlUtil::getStringAttr( node, "align", "left" ) ); Qt::Alignment textVAlign = EnumUtil::stringToVAlign( XmlUtil::getStringAttr( node, "valign", "top" ) ); /* affine attrs */ double a[6]; a[0] = XmlUtil::getDoubleAttr( node, "a0", 1.0 ); a[1] = XmlUtil::getDoubleAttr( node, "a1", 0.0 ); a[2] = XmlUtil::getDoubleAttr( node, "a2", 0.0 ); a[3] = XmlUtil::getDoubleAttr( node, "a3", 1.0 ); a[4] = XmlUtil::getDoubleAttr( node, "a4", 0.0 ); a[5] = XmlUtil::getDoubleAttr( node, "a5", 0.0 ); /* shadow attrs */ bool shadowState = XmlUtil::getBoolAttr( node, "shadow", false ); Distance shadowX = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 ); Distance shadowY = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 ); double shadowOpacity = XmlUtil::getDoubleAttr( node, "shadow_opacity", 1.0 ); key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); ColorNode shadowColorNode( field_flag, color, key ); /* deserialize contents. */ QTextDocument document; QTextCursor cursor( &document ); bool firstBlock = true; for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) { QString tagName = child.toElement().tagName(); if ( tagName == "p" ) { if ( !firstBlock ) { cursor.insertBlock(); } firstBlock = false; cursor.insertText( parsePNode( child.toElement() ) ); } else if ( !child.isComment() ) { qWarning() << "Unexpected" << node.tagName() << "child:" << tagName; } } QString text = document.toPlainText(); return new LabelModelTextObject( x0, y0, w, h, text, fontFamily, fontSize, fontWeight, fontItalicFlag, fontUnderlineFlag, textColorNode, textHAlign, textVAlign, textLineSpacing, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } QString XmlLabelParser::parsePNode( const QDomElement &node ) { return node.text(); } bool XmlLabelParser::parseRotateAttr( const QDomElement &node ) { return XmlUtil::getBoolAttr( node, "rotate", false ); } void XmlLabelParser::parseMergeNode( const QDomElement &node, LabelModel* label ) { QString type = XmlUtil::getStringAttr( node, "type", "None" ); QString src = XmlUtil::getStringAttr( node, "src", "" ); merge::Merge* merge = merge::Factory::createMerge( type ); merge->setSource( src ); label->setMerge( merge ); } void XmlLabelParser::parseDataNode( const QDomElement &node, DataCache& data ) { for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) { QString tagName = child.toElement().tagName(); if ( tagName == "File" ) { parseFileNode( child.toElement(), data ); } else if ( !child.isComment() ) { qWarning() << "Unexpected" << node.tagName() << "child:" << tagName; } } } void XmlLabelParser::parsePixdataNode( const QDomElement& node, DataCache& data ) { // TODO, compatability with glabels-3 } void XmlLabelParser::parseFileNode( const QDomElement& node, DataCache& data ) { QString name = XmlUtil::getStringAttr( node, "name", "" ); QString mimetype = XmlUtil::getStringAttr( node, "mimetype", "image/png" ); QString encoding = XmlUtil::getStringAttr( node, "encoding", "base64" ); if ( mimetype == "image/png" ) { QByteArray ba64 = node.text().toUtf8(); QByteArray ba = QByteArray::fromBase64( ba64 ); QImage image; image.loadFromData( ba, "PNG" ); data.addImage( name, image ); } else if ( mimetype == "image/svg+xml" ) { data.addSvg( name, node.text().toUtf8() ); } } } // namespace glabels