/*  XmlLabelParser_3.cpp
 *
 *  Copyright (C) 2014-2016  Jim Evins <evins@snaught.com>
 *
 *  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 <http://www.gnu.org/licenses/>.
 */
#include "XmlLabelParser_3.h"

#include "DataCache.h"
#include "Model.h"
#include "ModelBarcodeObject.h"
#include "ModelBoxObject.h"
#include "ModelEllipseObject.h"
#include "ModelImageObject.h"
#include "ModelLineObject.h"
#include "ModelObject.h"
#include "ModelTextObject.h"
#include "XmlTemplateParser.h"
#include "XmlUtil.h"
#include "Size.h"

#include "barcode/Backends.h"
#include "merge/Factory.h"

#include <QByteArray>
#include <QFile>
#include <QTextCursor>
#include <QTextDocument>
#include <QtDebug>

#if HAVE_ZLIB
#include <zlib.h>
#endif


namespace
{
	const uint32_t GDK_PIXBUF_MAGIC_NUMBER {0x47646b50};
	const double FONT_SCALE_FACTOR {0.75};

	typedef enum
	{
		/* colorspace + alpha */
		GDK_PIXDATA_COLOR_TYPE_RGB    = 0x01,
		GDK_PIXDATA_COLOR_TYPE_RGBA   = 0x02,
		GDK_PIXDATA_COLOR_TYPE_MASK   = 0xff,
		/* width, support 8bits only currently */
		GDK_PIXDATA_SAMPLE_WIDTH_8    = 0x01 << 16,
		GDK_PIXDATA_SAMPLE_WIDTH_MASK = 0x0f << 16,
		/* encoding */
		GDK_PIXDATA_ENCODING_RAW      = 0x01 << 24,
		GDK_PIXDATA_ENCODING_RLE      = 0x02 << 24,
		GDK_PIXDATA_ENCODING_MASK     = 0x0f << 24
	} GdkPixdataType;
}


namespace glabels
{

	namespace model
	{


		Model*
		XmlLabelParser_3::parseRootNode( const QDomElement &node )
		{
			auto* label = new Model();

			/* Pass 1, extract data nodes to pre-load cache. */
			DataCache data;
			for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() )
			{
				auto element = child.toElement();
				if (!element.isNull() && element.tagName() == "Data" )
				{
					parseDataNode( element, data );
				}
			}

			/* Pass 2, now extract everything else. */
			for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() )
			{
				const auto childElement = child.toElement();
				if(childElement.isNull())
				{
					qCritical()<<"Can't convert the node to an element. Try to continue.";
					continue;
				}
				const QString tagName = childElement.tagName();
		
				if ( tagName == "Template" )
				{
					Template* tmplate = XmlTemplateParser().parseTemplateNode( childElement );
					if ( tmplate == nullptr )
					{
						qWarning() << "Unable to parse template";
						delete label;
						return nullptr;
					}
					label->setTmplate( tmplate );
				}
				else if ( tagName == "Objects" )
				{
					label->setRotate( parseRotateAttr( childElement ) );
					QList<ModelObject*> list = parseObjectsNode( childElement, data );
					foreach ( ModelObject* object, list )
					{
						label->addObject( object );
					}
				}
				else if ( tagName == "Merge" )
				{
					parseMergeNode( childElement, label );
				}
				else if ( tagName == "Data" )
				{
					/* Handled in pass 1. */
				}
				else if ( !child.isComment() )
				{
					qWarning() << "Unexpected" << node.tagName() << "child:" << tagName;
				}
			}

			label->clearModified();
			return label;
		}


		QList<ModelObject*>
		XmlLabelParser_3::parseObjectsNode( const QDomElement &node, const DataCache& data )
		{
			QList<ModelObject*> list;

			for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() )
			{
				const auto childElement = child.toElement();
				if(childElement.isNull())
				{
					qCritical()<<"Can't convert the node to an element. Try to continue.";
					continue;
				}
				const QString tagName = childElement.tagName();
		
				if ( tagName == "Object-box" )
				{
					list.append( parseObjectBoxNode( childElement ) );
				}
				else if ( tagName == "Object-ellipse" )
				{
					list.append( parseObjectEllipseNode( childElement ) );
				}
				else if ( tagName == "Object-line" )
				{
					list.append( parseObjectLineNode( childElement ) );
				}
				else if ( tagName == "Object-text" )
				{
					list.append( parseObjectTextNode( childElement ) );
				}
				else if ( tagName == "Object-image" )
				{
					list.append( parseObjectImageNode( childElement, data ) );
				}
				else if ( tagName == "Object-barcode" )
				{
					list.append( parseObjectBarcodeNode( childElement ) );
				}
				else if ( !child.isComment() )
				{
					qWarning() << "Unexpected" << node.tagName() << "child:" << tagName;
				}
			}

			return list;
		}


		ModelBoxObject*
		XmlLabelParser_3::parseObjectBoxNode( const QDomElement &node )
		{
			/* position attrs */
			const Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 );
			const Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 );

			/* size attrs */
			const Distance w = XmlUtil::getLengthAttr( node, "w", 0 );
			const Distance h = XmlUtil::getLengthAttr( node, "h", 0 );

			/* line attrs */
			const 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 );
			const 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 );
			const ColorNode fillColorNode( field_flag, color, key );

			/* affine attrs */
			const auto affineTransformation = parseAffineTransformation(node);

			/* shadow attrs */
			const bool     shadowState   = XmlUtil::getBoolAttr( node, "shadow", false );
			const Distance shadowX       = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 );
			const Distance shadowY       = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 );
			const 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 );
			const ColorNode shadowColorNode( field_flag, color, key );

			return new ModelBoxObject( x0, y0, w, h,
			                           lineWidth, lineColorNode,
			                           fillColorNode,
						   affineTransformation,
			                           shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode );
		}


		ModelEllipseObject*
		XmlLabelParser_3::parseObjectEllipseNode( const QDomElement &node )
		{
			/* position attrs */
			const Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 );
			const Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 );

			/* size attrs */
			const Distance w = XmlUtil::getLengthAttr( node, "w", 0 );
			const Distance h = XmlUtil::getLengthAttr( node, "h", 0 );

			/* line attrs */
			const 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 );
			const 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 );
			const ColorNode fillColorNode( field_flag, color, key );

			/* affine attrs */
			const auto affineTransformation = parseAffineTransformation(node);

			/* shadow attrs */
			const bool     shadowState   = XmlUtil::getBoolAttr( node, "shadow", false );
			const Distance shadowX       = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 );
			const Distance shadowY       = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 );
			const 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 );
			const ColorNode shadowColorNode( field_flag, color, key );

			return new ModelEllipseObject( x0, y0, w, h,
			                               lineWidth, lineColorNode,
			                               fillColorNode,
						       affineTransformation,
			                               shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode );
		}


		ModelLineObject*
		XmlLabelParser_3::parseObjectLineNode( const QDomElement &node )
		{
			/* position attrs */
			const Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 );
			const Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 );

			/* size attrs of line */
			const Distance dx = XmlUtil::getLengthAttr( node, "dx", 0 );
			const Distance dy = XmlUtil::getLengthAttr( node, "dy", 0 );

			/* line attrs */
			const 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 );
			const ColorNode lineColorNode( field_flag, color, key );

			/* affine attrs */
			const auto affineTransformation = parseAffineTransformation(node);

			/* shadow attrs */
			const bool     shadowState   = XmlUtil::getBoolAttr( node, "shadow", false );
			const Distance shadowX       = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 );
			const Distance shadowY       = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 );
			const 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 );
			const ColorNode shadowColorNode( field_flag, color, key );

			return new ModelLineObject( x0, y0, dx, dy,
			                            lineWidth, lineColorNode,
						    affineTransformation,
			                            shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode );
		}


		ModelImageObject*
		XmlLabelParser_3::parseObjectImageNode( const QDomElement &node, const DataCache& data )
		{
			/* position attrs */
			const Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 );
			const Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 );

			/* size attrs */
			const Distance w = XmlUtil::getLengthAttr( node, "w", 0 );
			const Distance h = XmlUtil::getLengthAttr( node, "h", 0 );

			/* file attrs */
			QString key        = XmlUtil::getStringAttr( node, "field", "" );
			bool    field_flag = !key.isEmpty();
			const QString filename   = XmlUtil::getStringAttr( node, "src", "" );
			const TextNode filenameNode( field_flag, field_flag ? key : filename );

			/* affine attrs */
			const auto affineTransformation = parseAffineTransformation(node);

			/* shadow attrs */
			const bool     shadowState   = XmlUtil::getBoolAttr( node, "shadow", false );
			const Distance shadowX       = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 );
			const Distance shadowY       = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 );
			const 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 );
			const ColorNode shadowColorNode( field_flag, color, key );

			if ( filenameNode.isField() )
			{
				return new ModelImageObject( x0, y0, w, h,
				                             filenameNode,
							     affineTransformation,
				                             shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode );
			}
			if ( data.hasImage( filename ) )
			{
				return new ModelImageObject( x0, y0, w, h,
							     filename, data.getImage( filename ),
							     affineTransformation,
							     shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode );
			}
			if ( data.hasSvg( filename ) )
			{
				return new ModelImageObject( x0, y0, w, h,
							     filename, data.getSvg( filename ),
							     affineTransformation,
							     shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode );
			}
			qWarning() << "Embedded file" << filename << "missing. Trying actual file.";
			return new ModelImageObject( x0, y0, w, h,
						     filenameNode,
						     affineTransformation,
						     shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode );
		}


		ModelBarcodeObject*
		XmlLabelParser_3::parseObjectBarcodeNode( const QDomElement &node )
		{
			/* position attrs */
			const Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 );
			const Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 );

			/* size attrs */
			const Distance w = XmlUtil::getLengthAttr( node, "w", 0 );
			const Distance h = XmlUtil::getLengthAttr( node, "h", 0 );

			/* barcode attrs */
			const auto backend = XmlUtil::getStringAttr( node, "backend", "");
			// one major difference between glabels-3.0.dtd and glabels-4.0.dtd
			// is the lowercase of the style names
			const auto style = XmlUtil::getStringAttr( node, "style", "").toLower();

			const barcode::Style bcStyle = barcode::Backends::style( backend, style );
			const bool bcTextFlag = XmlUtil::getBoolAttr( node, "text", true );
			const bool bcChecksumFlag = XmlUtil::getBoolAttr( node, "checksum", true );

			const QString  key        = XmlUtil::getStringAttr( node, "color_field", "" );
			const bool     field_flag = !key.isEmpty();
			const uint32_t color      = XmlUtil::getUIntAttr( node, "color", 0 );
			const ColorNode bcColorNode( field_flag, color, key );

			QString bcData = XmlUtil::getStringAttr( node, "data", "" );
			if(bcData.isEmpty())
			{
				bcData = "${" + XmlUtil::getStringAttr( node, "field", "" ) + "}";
			}

			/* affine attrs */
			const auto affineTransformation = parseAffineTransformation(node);

			return new ModelBarcodeObject( x0, y0, w, h,
			                               bcStyle, bcTextFlag, bcChecksumFlag, bcData, bcColorNode,
						       affineTransformation );
		}


		QMatrix
		XmlLabelParser_3::parseAffineTransformation(const QDomElement &node)
		{
			return {XmlUtil::getDoubleAttr( node, "a0", 1.0 ),
				XmlUtil::getDoubleAttr( node, "a1", 0.0 ),
				XmlUtil::getDoubleAttr( node, "a2", 0.0 ),
				XmlUtil::getDoubleAttr( node, "a3", 1.0 ),
				XmlUtil::getDoubleAttr( node, "a4", 0.0 ),
				XmlUtil::getDoubleAttr( node, "a5", 0.0 )};
		}


		ModelTextObject*
		XmlLabelParser_3::parseObjectTextNode( const QDomElement &node )
		{
			/* position attrs */
			const Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 );
			const Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 );

			/* size attrs */
			const Distance w = XmlUtil::getLengthAttr( node, "w", 0 );
			const Distance h = XmlUtil::getLengthAttr( node, "h", 0 );

			/* justify attr */
			const Qt::Alignment textHAlign = getHAlignmentAttr( node, "justify", Qt::AlignLeft );

			/* valign attr */
			const Qt::Alignment textVAlign = getVAlignmentAttr( node, "valign", Qt::AlignTop );

			/* auto_shrink attr */
			const bool textAutoShrink = XmlUtil::getBoolAttr( node, "auto_shrink", false );

			/* affine attrs */
			const auto affineTransformation = parseAffineTransformation(node);

			/* shadow attrs */
			const bool     shadowState   = XmlUtil::getBoolAttr( node, "shadow", false );
			const Distance shadowX       = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 );
			const Distance shadowY       = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 );
			const double   shadowOpacity = XmlUtil::getDoubleAttr( node, "shadow_opacity", 1.0 );

			QString key            = XmlUtil::getStringAttr( node, "shadow_color_field", "" );
			bool field_flag        = !key.isEmpty();
			uint32_t color         = XmlUtil::getUIntAttr( node, "shadow_color", 0 );
			const ColorNode shadowColorNode( field_flag, color, key );

			/* font attrs */
			QString       fontFamily = "Sans";
			double        fontSize = 10.;
			QFont::Weight fontWeight = QFont::Normal;
			bool          fontItalicFlag = false;

			/* text attrs */
			double textLineSpacing = 1.;
			QTextOption::WrapMode textWrapMode = QTextOption::WordWrap;

			ColorNode textColorNode;

			/* deserialize contents. */
			QTextDocument document;
			QTextCursor cursor( &document );
			bool firstBlock = true;
			for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() )
			{
				const auto element = child.toElement();
				if(element.isNull())
				{
					qCritical()<<"Can't convert the node to an element. Try to continue.";
					continue;
				}

				if(element.tagName() == "Span")
				{
					QString text;
					for(QDomNode textPartElement = element.firstChild()
					    ; !textPartElement.isNull()
					    ; textPartElement = textPartElement.nextSibling())
					{
						const QDomText textpart = textPartElement.toText();
						if (!textpart.isNull())
						{
							text += textpart.data();
						}
						else
						{
							if(textPartElement.toElement().tagName() == "NL")
							{
								text += "\n";
							}
							else if (textPartElement.toElement().tagName() == "Field")
							{
								text += "${" + XmlUtil::getStringAttr( textPartElement.toElement(), "name", "" ) + "}";
							}
						}
					}

					if ( !firstBlock )
					{
						cursor.insertBlock();
					}
					firstBlock = false;

					cursor.insertText( text );

					/* font attrs */
					fontFamily        = XmlUtil::getStringAttr( element, "font_family", "Sans" );
					fontSize          = XmlUtil::getDoubleAttr( element, "font_size", 10 ) * FONT_SCALE_FACTOR;
					fontWeight        = getWeightAttr( element, "font_weight", QFont::Normal );
					fontItalicFlag    = XmlUtil::getBoolAttr( element, "font_italic", false );

					/* color attr */
					key        = XmlUtil::getStringAttr( element, "color_field", "" );
					field_flag = !key.isEmpty();
					color      = XmlUtil::getUIntAttr( element, "color", 0 );

					textColorNode = ColorNode(field_flag, color, key );

					/* text attrs */
					textLineSpacing   = XmlUtil::getDoubleAttr( element, "line_spacing", 1 );
					textWrapMode = QTextOption::WordWrap;

				}
				else if ( !child.isComment() )
				{
					qWarning() << "Unexpected" << node.tagName() << "child:" << node.tagName();
				}
			}
			const QString text = document.toPlainText();

			auto textNode = new ModelTextObject( x0, y0, w, h, text,
							     fontFamily, fontSize, fontWeight, fontItalicFlag, false,
							     textColorNode, textHAlign, textVAlign, textWrapMode, textLineSpacing,
							     textAutoShrink,
							     affineTransformation,
							     shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode );

			// The size of the textnode does not fit the qt world. So it's needed to
			// recalculate the size depending on the data.
			textNode->setSize(textNode->naturalSize());

			return textNode;
		}


		bool
		XmlLabelParser_3::parseRotateAttr( const QDomElement &node )
		{
			return XmlUtil::getBoolAttr( node, "rotate", false );
		}


		void
		XmlLabelParser_3::parseMergeNode( const QDomElement &node, Model* label )
		{
			const QString type = XmlUtil::getStringAttr( node, "type", "None" );
			const QString src  = XmlUtil::getStringAttr( node, "src", "" );

			merge::Merge* merge = merge::Factory::createMerge( type );
			merge->setSource( src );

			label->setMerge( merge );
		}


		void
		XmlLabelParser_3::parseDataNode( const QDomElement &node, DataCache& data )
		{
			for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() )
			{
				const auto childElement = child.toElement();
				if(childElement.isNull())
				{
					qCritical()<<"Can't convert the node to an element. Try to continue.";
					continue;
				}
				const QString tagName = childElement.tagName();

				if ( tagName == "File" )
				{
					parseFileNode( childElement, data );
				}
				else if (tagName == "Pixdata")
				{
					parsePixdataNode( childElement, data);
				}
				else if ( !child.isComment() )
				{
					qWarning() << "Unexpected" << node.tagName() << "child:" << tagName;
				}
			}
		}


		void
		XmlLabelParser_3::parsePixdataNode( const QDomElement& node, DataCache& data )
		{
			const QString name     = XmlUtil::getStringAttr( node, "name", "" );
			const QString encoding = XmlUtil::getStringAttr( node, "encoding", "base64" );

			/*
			 * Struct of the GdkPixdata from the header file
			 */
			struct _GdkPixdata
			{
				uint32_t magic;        /* GDK_PIXBUF_MAGIC_NUMBER */
				int32_t  length;       /* <1 to disable length checks, otherwise:
							* GDK_PIXDATA_HEADER_LENGTH + pixel_data length*/
				uint32_t pixdata_type; /* GdkPixdataType */
				uint32_t rowstride;
				uint32_t width;
				uint32_t height;
				//uint8_t *pixel_data;
			} mypixdata{};

			if ( encoding.toLower() == "base64" )
			{

				QByteArray ba = QByteArray::fromBase64( node.text().toUtf8() );

				// Use the QDataStream to import the header cause it is in big endian
				QDataStream ds(ba);
				ds.setByteOrder(QDataStream::ByteOrder::BigEndian);

				ds >> mypixdata.magic
				   >> mypixdata.length
				   >> mypixdata.pixdata_type
				   >> mypixdata.rowstride
				   >> mypixdata.width
				   >> mypixdata.height;

				if(mypixdata.magic !=GDK_PIXBUF_MAGIC_NUMBER)
				{
					qCritical() << "GDK pixbuf magic is not correct. Abort reading of pixdata. "
						    << "Node:" << node.tagName();
					return;
				}

				// check if the data fits in a sigend int
				if(mypixdata.width > INT32_MAX || mypixdata.height > INT32_MAX || mypixdata.rowstride > INT32_MAX)
				{
					qCritical() << "rowstride, width or height is to large. Abort reading of pixdata. "
						    << "Node:" << node.tagName();;
					return;
				}

				const auto width = static_cast<int32_t>(mypixdata.width);
				const auto height = static_cast<int32_t>(mypixdata.height);
				const auto rowstride = static_cast<int32_t>(mypixdata.rowstride);

				QImage::Format pixformat;
				int32_t rawpadding;
				if((mypixdata.pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB)
				{
					pixformat = QImage::Format_RGB888;
					rawpadding = rowstride - (3 * width);
				}
				else if((mypixdata.pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA)
				{
					pixformat = QImage::Format_RGBA8888;
					rawpadding = rowstride - (4 * width);
				}
				else
				{
					qCritical() << "pixdata color type is unknown. Abort reading of pixdata. "
						    << "Node:" << node.tagName();
					return;
				}

				if(rawpadding < 0){
					qCritical() << "padding to is negativ. Abort reading of pixdata. "
						    << "Node:" << node.tagName();
					return;
				}

				QImage image(width, height, pixformat);

				const auto padding = static_cast<int32_t>(rawpadding);

				int x = 0;
				int y = 0;
				uint8_t r,g,b,a;
				for(y = 0; y < height; y++)
				{
					for(x = 0; x < width; x++)
					{
						if(pixformat == QImage::Format_RGB888)
						{
							ds >> r >> g >> b;
							image.setPixelColor(x, y, QColor(r, g, b));
						}
						else
						{
							ds >> r >> g >> b >> a;
							image.setPixelColor(x, y, QColor(r, g, b, a));
						}

					}
					ds.skipRawData(padding);
				}

				data.addImage( name, image );
			}
			else
			{
				qWarning() << "Unexpected encoding:" << encoding << "node:" << node.tagName();
			}
		}


		void
		XmlLabelParser_3::parseFileNode( const QDomElement& node, DataCache& data )
		{
			const QString name     = XmlUtil::getStringAttr( node, "name", "" );
			const QString format = XmlUtil::getStringAttr( node, "format", "invalid" );

			if ( format == "SVG" )
			{
				data.addSvg( name, node.text().toUtf8() );
			}
			else
			{
				qCritical() << "Unknown embedded file format:" << format;
			}
		}


		Qt::Alignment
		XmlLabelParser_3::getHAlignmentAttr( const QDomElement& node, const QString& name,
						const Qt::Alignment default_value )
		{

			const QString valueString = node.attribute( name, "" );
			if ( !valueString.isEmpty())
			{
				if ( valueString == "Right" )
				{
					return Qt::AlignRight;
				}
				if ( valueString == "Center")
				{
					return Qt::AlignHCenter;
				}
				if ( valueString == "Left" )
				{
					return Qt::AlignLeft;
				}
			}
			return default_value;
		}


		Qt::Alignment
		XmlLabelParser_3::getVAlignmentAttr( const QDomElement& node, const QString& name,
						     const Qt::Alignment default_value )
		{

			const QString valueString = node.attribute( name, "" );
			if ( ! valueString.isEmpty() )
			{
				if ( valueString == "Bottom" )
				{
					return Qt::AlignBottom;
				}
				if ( valueString == "Center" )
				{
					return Qt::AlignVCenter;
				}
				if ( valueString == "Top" )
				{
					return Qt::AlignTop;
				}
			}

			return default_value;
		}


		QFont::Weight
		XmlLabelParser_3::getWeightAttr( const QDomElement& node, const QString&     name,
						 const QFont::Weight default_value )
		{
			const QString valueString = node.attribute( name, "" );
			if ( !valueString.isEmpty() )
			{
				if ( valueString == "Bold" )
				{
					return QFont::Bold;
				}
				if ( valueString == "Regular" )
				{
					return QFont::Normal;
				}
			}

			return default_value;
		}


	}  // namespace model

}  // namespace glabels
