Implemented compatablility import of glabels-3 project files (#39).
This commit is contained in:
@@ -0,0 +1,828 @@
|
||||
/* 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
|
||||
Reference in New Issue
Block a user