1c902230fe
* Bulk replaced tabs with spaces * Bulk removed trailing whitespace from lines * Replaced c-style comments with c++-style comments in file banners * Replace nested namespace definitions with single concise definitions (C++17), this keeps the indentation more manageable * Cleanup ordering and spacing of include directives * Bulk renaming of header file extensions from '.h' to '.hpp'. * Update CODING-STYLE.md * Update target_compile_features from cxx_std_11 to cxx_std_20. * Refresh .clang-format file. Still needs a lot of tweaking.
612 lines
23 KiB
C++
612 lines
23 KiB
C++
// XmlUtil.cpp
|
|
//
|
|
// Copyright (C) 2013-2016 Jaye 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 "XmlUtil.hpp"
|
|
|
|
#include <QTextStream>
|
|
#include <QCoreApplication>
|
|
#include <QDebug>
|
|
#include <QIODevice>
|
|
|
|
|
|
namespace glabels::model
|
|
{
|
|
|
|
//
|
|
// Static data
|
|
//
|
|
Units XmlUtil::mUnits = Units(Units::PT);
|
|
|
|
|
|
Units XmlUtil::units()
|
|
{
|
|
return mUnits;
|
|
}
|
|
|
|
|
|
void XmlUtil::setUnits( Units units )
|
|
{
|
|
mUnits = units;
|
|
}
|
|
|
|
|
|
QString XmlUtil::getStringAttr( const QDomElement& node,
|
|
const QString& name,
|
|
const QString& default_value )
|
|
{
|
|
return node.attribute( name, default_value );
|
|
}
|
|
|
|
|
|
double XmlUtil::getDoubleAttr( const QDomElement& node,
|
|
const QString& name,
|
|
double default_value )
|
|
{
|
|
QString valueString = node.attribute( name, "" );
|
|
if ( valueString != "" )
|
|
{
|
|
bool ok;
|
|
double value = valueString.toDouble( &ok );
|
|
|
|
if ( !ok )
|
|
{
|
|
qWarning() << "Error: bad double value in attribute "
|
|
<< node.tagName() << ":" << name << "=" << valueString;
|
|
return default_value;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
return default_value;
|
|
}
|
|
|
|
|
|
bool XmlUtil::getBoolAttr( const QDomElement& node,
|
|
const QString& name,
|
|
bool default_value )
|
|
{
|
|
QString valueString = node.attribute( name, "" );
|
|
if ( valueString != "" )
|
|
{
|
|
bool ok;
|
|
int intValue = valueString.toInt( &ok );
|
|
|
|
if ( (valueString == "True") ||
|
|
(valueString == "TRUE") ||
|
|
(valueString == "true") ||
|
|
(ok && (intValue == 1) ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( (valueString == "False") ||
|
|
(valueString == "FALSE") ||
|
|
(valueString == "false") ||
|
|
(ok && (intValue == 0) ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
qWarning() << "Error: bad boolean value in attribute "
|
|
<< node.tagName() << ":" << name << "=" << valueString;
|
|
return default_value;
|
|
}
|
|
|
|
return default_value;
|
|
}
|
|
|
|
|
|
int XmlUtil::getIntAttr( const QDomElement& node,
|
|
const QString& name,
|
|
int default_value )
|
|
{
|
|
QString valueString = node.attribute( name, "" );
|
|
if ( valueString != "" )
|
|
{
|
|
bool ok;
|
|
int value = valueString.toInt( &ok );
|
|
|
|
if ( !ok )
|
|
{
|
|
qWarning() << "Error: bad integer value in attribute "
|
|
<< node.tagName() << ":" << name << "=" << valueString;
|
|
return default_value;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
return default_value;
|
|
}
|
|
|
|
|
|
uint32_t XmlUtil::getUIntAttr( const QDomElement& node,
|
|
const QString& name,
|
|
uint32_t default_value )
|
|
{
|
|
QString valueString = node.attribute( name, "" );
|
|
if ( valueString != "" )
|
|
{
|
|
bool ok;
|
|
uint32_t value = valueString.toUInt( &ok, 0 );
|
|
|
|
if ( !ok )
|
|
{
|
|
qWarning() << "Error: bad unsigned integer value in attribute "
|
|
<< node.tagName() << ":" << name << "=" << valueString;
|
|
return default_value;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
return default_value;
|
|
}
|
|
|
|
|
|
QString XmlUtil::getI18nAttr( const QDomElement& node,
|
|
const QString& name,
|
|
const QString& default_value )
|
|
{
|
|
QString i18nString = node.attribute( QString("_").append(name), "" );
|
|
|
|
if ( i18nString == "" )
|
|
{
|
|
return node.attribute( name, default_value );
|
|
}
|
|
|
|
return QCoreApplication::translate( "XmlStrings", i18nString.toUtf8().constData() );
|
|
}
|
|
|
|
|
|
Distance XmlUtil::getLengthAttr( const QDomElement& node,
|
|
const QString& name,
|
|
Distance default_value )
|
|
{
|
|
QString valueString = node.attribute( name, "" );
|
|
if ( valueString != "" )
|
|
{
|
|
double value;
|
|
QString unitsString;
|
|
QTextStream valueStream( &valueString, QIODevice::ReadOnly );
|
|
|
|
valueStream >> value >> unitsString;
|
|
|
|
if ( !unitsString.isEmpty() && !Units::isIdValid( unitsString ) )
|
|
{
|
|
qWarning() << "Error: bad length value in attribute "
|
|
<< node.tagName() << ":" << name << "=" << valueString;
|
|
return default_value;
|
|
}
|
|
|
|
return Distance( value, unitsString );
|
|
}
|
|
|
|
return default_value;
|
|
}
|
|
|
|
|
|
QFont::Weight XmlUtil::getWeightAttr( const QDomElement& node,
|
|
const QString& name,
|
|
QFont::Weight default_value )
|
|
{
|
|
QString valueString = node.attribute( name, "" );
|
|
if ( valueString != "" )
|
|
{
|
|
if ( valueString == "bold" )
|
|
{
|
|
return QFont::Bold;
|
|
}
|
|
else if ( valueString == "normal" )
|
|
{
|
|
return QFont::Normal;
|
|
}
|
|
else
|
|
{
|
|
qWarning() << "Error: bad weight value in attribute "
|
|
<< node.tagName() << ":" << name << "=" << valueString;
|
|
return default_value;
|
|
}
|
|
}
|
|
|
|
return default_value;
|
|
}
|
|
|
|
|
|
Qt::Alignment XmlUtil::getAlignmentAttr( const QDomElement& node,
|
|
const QString& name,
|
|
Qt::Alignment default_value )
|
|
{
|
|
QString valueString = node.attribute( name, "" );
|
|
if ( valueString != "" )
|
|
{
|
|
if ( valueString == "right" )
|
|
{
|
|
return Qt::AlignRight;
|
|
}
|
|
else if ( valueString == "hcenter" )
|
|
{
|
|
return Qt::AlignHCenter;
|
|
}
|
|
else if ( valueString == "left" )
|
|
{
|
|
return Qt::AlignLeft;
|
|
}
|
|
else if ( valueString == "bottom" )
|
|
{
|
|
return Qt::AlignBottom;
|
|
}
|
|
else if ( valueString == "vcenter" )
|
|
{
|
|
return Qt::AlignVCenter;
|
|
}
|
|
else if ( valueString == "top" )
|
|
{
|
|
return Qt::AlignTop;
|
|
}
|
|
else
|
|
{
|
|
qWarning() << "Error: bad alignment value in attribute "
|
|
<< node.tagName() << ":" << name << "=" << valueString;
|
|
return default_value;
|
|
}
|
|
}
|
|
|
|
return default_value;
|
|
}
|
|
|
|
|
|
QTextOption::WrapMode XmlUtil::getWrapModeAttr( const QDomElement& node,
|
|
const QString& name,
|
|
QTextOption::WrapMode default_value )
|
|
{
|
|
QString valueString = node.attribute( name, "" );
|
|
if ( valueString != "" )
|
|
{
|
|
if ( valueString == "word" )
|
|
{
|
|
return QTextOption::WordWrap;
|
|
}
|
|
else if ( valueString == "anywhere" )
|
|
{
|
|
return QTextOption::WrapAnywhere;
|
|
}
|
|
else if ( valueString == "none" )
|
|
{
|
|
return QTextOption::NoWrap;
|
|
}
|
|
else
|
|
{
|
|
qWarning() << "Error: bad wrap mode value in attribute "
|
|
<< node.tagName() << ":" << name << "=" << valueString;
|
|
return default_value;
|
|
}
|
|
}
|
|
|
|
return default_value;
|
|
}
|
|
|
|
|
|
Units XmlUtil::getUnitsAttr( const QDomElement& node,
|
|
const QString& name,
|
|
Units default_value )
|
|
{
|
|
QString valueString = node.attribute( name, "" );
|
|
if ( valueString != "" )
|
|
{
|
|
return Units( valueString );
|
|
}
|
|
|
|
return default_value;
|
|
}
|
|
|
|
|
|
QPainterPath XmlUtil::getPathDataAttr( const QDomElement& node,
|
|
const QString& name,
|
|
Units units )
|
|
{
|
|
QPainterPath d;
|
|
|
|
//
|
|
// Simple path data parser
|
|
//
|
|
QStringList tokens = node.attribute( name, "" ).split( " ", Qt::SkipEmptyParts );
|
|
|
|
enum { CMD, MX, MY, MDX, MDY, LX, LY, LDX, LDY, HX, HDX, VY, VDY } state = CMD;
|
|
Distance x = 0;
|
|
Distance y = 0;
|
|
Distance dx = 0;
|
|
Distance dy = 0;
|
|
QPointF c;
|
|
|
|
for ( int i = 0; i < tokens.size(); i++ )
|
|
{
|
|
switch (state)
|
|
{
|
|
case CMD:
|
|
switch (tokens[i][0].unicode())
|
|
{
|
|
case 'M':
|
|
state = MX;
|
|
break;
|
|
case 'm':
|
|
state = MDX;
|
|
break;
|
|
case 'L':
|
|
state = LX;
|
|
break;
|
|
case 'l':
|
|
state = LDX;
|
|
break;
|
|
case 'H':
|
|
state = HX;
|
|
break;
|
|
case 'h':
|
|
state = HDX;
|
|
break;
|
|
case 'V':
|
|
state = VY;
|
|
break;
|
|
case 'v':
|
|
state = VDY;
|
|
break;
|
|
case 'Z':
|
|
case 'z':
|
|
d.closeSubpath();
|
|
state = CMD;
|
|
break;
|
|
}
|
|
break;
|
|
case MX:
|
|
x = Distance( tokens[i].toDouble(), units );
|
|
state = MY;
|
|
break;
|
|
case MY:
|
|
y = Distance( tokens[i].toDouble(), units );
|
|
d.moveTo( x.pt(), y.pt() );
|
|
state = CMD;
|
|
break;
|
|
case MDX:
|
|
dx = Distance( tokens[i].toDouble(), units );
|
|
state = MDY;
|
|
break;
|
|
case MDY:
|
|
dy = Distance( tokens[i].toDouble(), units );
|
|
c = d.currentPosition();
|
|
d.moveTo( c.x()+x.pt(), c.y()+y.pt() );
|
|
state = CMD;
|
|
break;
|
|
case LX:
|
|
x = Distance( tokens[i].toDouble(), units );
|
|
state = LY;
|
|
break;
|
|
case LY:
|
|
y = Distance( tokens[i].toDouble(), units );
|
|
d.lineTo( x.pt(), y.pt() );
|
|
state = CMD;
|
|
break;
|
|
case LDX:
|
|
dx = Distance( tokens[i].toDouble(), units );
|
|
state = LDY;
|
|
break;
|
|
case LDY:
|
|
dy = Distance( tokens[i].toDouble(), units );
|
|
c = d.currentPosition();
|
|
d.lineTo( c.x()+dx.pt(), c.y()+dy.pt() );
|
|
state = CMD;
|
|
break;
|
|
case HX:
|
|
x = Distance( tokens[i].toDouble(), units );
|
|
c = d.currentPosition();
|
|
d.lineTo( x.pt(), c.y() );
|
|
state = CMD;
|
|
break;
|
|
case HDX:
|
|
dx = Distance( tokens[i].toDouble(), units );
|
|
c = d.currentPosition();
|
|
d.lineTo( c.x()+dx.pt(), c.y() );
|
|
state = CMD;
|
|
break;
|
|
case VY:
|
|
y = Distance( tokens[i].toDouble(), units );
|
|
c = d.currentPosition();
|
|
d.lineTo( c.x(), y.pt() );
|
|
state = CMD;
|
|
break;
|
|
case VDY:
|
|
dy = Distance( tokens[i].toDouble(), units );
|
|
c = d.currentPosition();
|
|
d.lineTo( c.x(), c.y()+dy.pt() );
|
|
state = CMD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
|
|
void XmlUtil::setStringAttr( QDomElement& node,
|
|
const QString& name,
|
|
const QString& value )
|
|
{
|
|
node.setAttribute( name, value );
|
|
}
|
|
|
|
|
|
void XmlUtil::setDoubleAttr( QDomElement& node,
|
|
const QString& name,
|
|
double value )
|
|
{
|
|
node.setAttribute( name, QString::number(value) );
|
|
}
|
|
|
|
|
|
void XmlUtil::setBoolAttr( QDomElement& node,
|
|
const QString& name,
|
|
bool value )
|
|
{
|
|
node.setAttribute( name, value ? "true" : "false" );
|
|
}
|
|
|
|
|
|
void XmlUtil::setIntAttr( QDomElement& node,
|
|
const QString& name,
|
|
int value )
|
|
{
|
|
node.setAttribute( name, QString::number(value) );
|
|
}
|
|
|
|
|
|
void XmlUtil::setUIntAttr( QDomElement& node,
|
|
const QString& name,
|
|
uint32_t value )
|
|
{
|
|
node.setAttribute( name, "0x" + QString::number(value, 16) );
|
|
}
|
|
|
|
|
|
void XmlUtil::setLengthAttr( QDomElement& node,
|
|
const QString& name,
|
|
Distance value )
|
|
{
|
|
node.setAttribute( name, QString::number(value.inUnits(mUnits)) + mUnits.toIdString() );
|
|
}
|
|
|
|
|
|
void XmlUtil::setWeightAttr( QDomElement& node,
|
|
const QString& name,
|
|
QFont::Weight value )
|
|
{
|
|
switch (value)
|
|
{
|
|
case QFont::Bold:
|
|
node.setAttribute( name, "bold" );
|
|
break;
|
|
default:
|
|
node.setAttribute( name, "normal" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void XmlUtil::setAlignmentAttr( QDomElement& node,
|
|
const QString& name,
|
|
Qt::Alignment value )
|
|
{
|
|
switch (value)
|
|
{
|
|
case Qt::AlignRight:
|
|
node.setAttribute( name, "right" );
|
|
break;
|
|
case Qt::AlignHCenter:
|
|
node.setAttribute( name, "hcenter" );
|
|
break;
|
|
case Qt::AlignLeft:
|
|
node.setAttribute( name, "left" );
|
|
break;
|
|
case Qt::AlignBottom:
|
|
node.setAttribute( name, "bottom" );
|
|
break;
|
|
case Qt::AlignVCenter:
|
|
node.setAttribute( name, "vcenter" );
|
|
break;
|
|
case Qt::AlignTop:
|
|
node.setAttribute( name, "top" );
|
|
break;
|
|
default:
|
|
node.setAttribute( name, "left" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void XmlUtil::setWrapModeAttr( QDomElement& node,
|
|
const QString& name,
|
|
QTextOption::WrapMode value )
|
|
{
|
|
switch (value)
|
|
{
|
|
case QTextOption::WordWrap:
|
|
node.setAttribute( name, "word" );
|
|
break;
|
|
case QTextOption::WrapAnywhere:
|
|
node.setAttribute( name, "anywhere" );
|
|
break;
|
|
case QTextOption::NoWrap:
|
|
case QTextOption::ManualWrap:
|
|
node.setAttribute( name, "none" );
|
|
break;
|
|
default:
|
|
node.setAttribute( name, "word" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void XmlUtil::setUnitsAttr( QDomElement& node,
|
|
const QString& name,
|
|
Units value )
|
|
{
|
|
node.setAttribute( name, value.toIdString() );
|
|
}
|
|
|
|
|
|
void XmlUtil::setPathDataAttr( QDomElement& node,
|
|
const QString& name,
|
|
const QPainterPath& path,
|
|
Units units )
|
|
{
|
|
QString pathString;
|
|
for ( int i = 0; i < path.elementCount(); i++ )
|
|
{
|
|
auto element = path.elementAt( i );
|
|
|
|
// QPainterPath is natively in pts
|
|
Distance x = Distance::pt( element.x );
|
|
Distance y = Distance::pt( element.y );
|
|
|
|
// Translate desired units for path data
|
|
double xValue = x.inUnits( units );
|
|
double yValue = y.inUnits( units );
|
|
|
|
if ( element.isMoveTo() )
|
|
{
|
|
pathString.append( QString( "M %1 %2" ).arg( xValue ).arg( yValue ) );
|
|
}
|
|
else if ( element.isLineTo() )
|
|
{
|
|
pathString.append( QString( "L %1 %2" ).arg( xValue ).arg( yValue ) );
|
|
}
|
|
|
|
if ( i < (path.elementCount() - 1) )
|
|
{
|
|
pathString.append( " " );
|
|
}
|
|
}
|
|
|
|
node.setAttribute( name, pathString );
|
|
}
|
|
|
|
|
|
}
|