Commit 3e7cdd80 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Implemented the first version of ASL parser and writer

Implemented:

+ reading of entire ASL file including embedded patterns.
  Supported version is v2 only.
+ writing ASL files without embedded patterns
+ all the ASL parsing happens via intermediate form in XML

Still to be done:

- writing embedded patterns
- connect to actual layer styles

Short description of architecture:

1) KisAslReader reads an ASL file into QDomDocument
2) KisAslXmlParses together with KisAslCallbackObjectCatcher parses
   the prepared XML document and initializes any structures connected
   to it.

3) KisAslXmlWriter is used to create a XML document from internal
   styles representation.
4) KisAslWriter converts XML data prepared by KisAslXmlWriter into
   actual ASL file.
parent a9d5f68d
......@@ -13,6 +13,12 @@ set(kritalayerstylesfilter_PART_SRCS
kis_layer_style_filter_environment.cpp
kis_layer_style_filter_projection_plane.cpp
kis_layer_style_projection_plane.cpp
kis_asl_reader.cpp
kis_asl_xml_parser.cpp
kis_asl_object_catcher.cpp
kis_asl_callback_object_catcher.cpp
kis_asl_xml_writer.cpp
kis_asl_writer.cpp
)
# kde4_add_ui_files(kritalayerstylesfilter_PART_SRCS
......
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_callback_object_catcher.h"
#include <QHash>
#include <QString>
#include <QPointF>
#include <QColor>
#include "kis_debug.h"
typedef QHash<QString, ASLCallbackDouble> MapHashDouble;
typedef QHash<QString, ASLCallbackInteger> MapHashInt;
struct EnumMapping {
EnumMapping(const QString &_typeId, ASLCallbackString _map)
: typeId(_typeId),
map(_map)
{
}
QString typeId;
ASLCallbackString map;
};
typedef QHash<QString, EnumMapping> MapHashEnum;
struct UnitFloatMapping {
UnitFloatMapping(const QString &_unit, ASLCallbackDouble _map)
: unit(_unit),
map(_map)
{
}
QString unit;
ASLCallbackDouble map;
};
typedef QHash<QString, UnitFloatMapping> MapHashUnitFloat;
typedef QHash<QString, ASLCallbackString> MapHashText;
typedef QHash<QString, ASLCallbackBoolean> MapHashBoolean;
typedef QHash<QString, ASLCallbackColor> MapHashColor;
typedef QHash<QString, ASLCallbackPoint> MapHashPoint;
typedef QHash<QString, ASLCallbackCurve> MapHashCurve;
typedef QHash<QString, ASLCallbackPattern> MapHashPattern;
struct KisAslCallbackObjectCatcher::Private
{
MapHashDouble mapDouble;
MapHashInt mapInteger;
MapHashEnum mapEnum;
MapHashUnitFloat mapUnitFloat;
MapHashText mapText;
MapHashBoolean mapBoolean;
MapHashColor mapColor;
MapHashPoint mapPoint;
MapHashCurve mapCurve;
MapHashPattern mapPattern;
};
KisAslCallbackObjectCatcher::KisAslCallbackObjectCatcher()
: m_d(new Private)
{
}
KisAslCallbackObjectCatcher::~KisAslCallbackObjectCatcher()
{
}
template <class HashType, typename T>
inline void passToCallback(const QString &path, const HashType &hash, const T &value)
{
typename HashType::const_iterator it = hash.find(path);
if (it != hash.constEnd()) {
(*it)(value);
}
}
void KisAslCallbackObjectCatcher::addDouble(const QString &path, double value)
{
passToCallback(path, m_d->mapDouble, value);
}
void KisAslCallbackObjectCatcher::addInteger(const QString &path, int value)
{
passToCallback(path, m_d->mapInteger, value);
}
void KisAslCallbackObjectCatcher::addEnum(const QString &path, const QString &typeId, const QString &value)
{
MapHashEnum::const_iterator it = m_d->mapEnum.find(path);
if (it != m_d->mapEnum.constEnd()) {
if (it->typeId == typeId) {
it->map(value);
} else {
qWarning() << "KisAslCallbackObjectCatcher::addEnum: inconsistent typeId" << ppVar(typeId) << ppVar(it->typeId);
}
}
}
void KisAslCallbackObjectCatcher::addUnitFloat(const QString &path, const QString &unit, double value)
{
MapHashUnitFloat::const_iterator it = m_d->mapUnitFloat.find(path);
if (it != m_d->mapUnitFloat.constEnd()) {
if (it->unit == unit) {
it->map(value);
} else {
qWarning() << "KisAslCallbackObjectCatcher::addUnitFloat: inconsistent unit" << ppVar(unit) << ppVar(it->unit);
}
}
}
void KisAslCallbackObjectCatcher::addText(const QString &path, const QString &value)
{
passToCallback(path, m_d->mapText, value);
}
void KisAslCallbackObjectCatcher::addBoolean(const QString &path, bool value)
{
passToCallback(path, m_d->mapBoolean, value);
}
void KisAslCallbackObjectCatcher::addColor(const QString &path, const QColor &value)
{
passToCallback(path, m_d->mapColor, value);
}
void KisAslCallbackObjectCatcher::addPoint(const QString &path, const QPointF &value)
{
passToCallback(path, m_d->mapPoint, value);
}
void KisAslCallbackObjectCatcher::addCurve(const QString &path, const QString &name, const QVector<QPointF> &points)
{
MapHashCurve::const_iterator it = m_d->mapCurve.find(path);
if (it != m_d->mapCurve.constEnd()) {
(*it)(name, points);
}
}
void KisAslCallbackObjectCatcher::addPattern(const QString &path, KoPattern *value) {
passToCallback(path, m_d->mapPattern, value);
}
/*****************************************************************/
/* Subscription methods */
/*****************************************************************/
void KisAslCallbackObjectCatcher::subscribeDouble(const QString &path, ASLCallbackDouble callback)
{
m_d->mapDouble.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeInteger(const QString &path, ASLCallbackInteger callback)
{
m_d->mapInteger.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeEnum(const QString &path, const QString &typeId, ASLCallbackString callback)
{
m_d->mapEnum.insert(path, EnumMapping(typeId, callback));
}
void KisAslCallbackObjectCatcher::subscribeUnitFloat(const QString &path, const QString &unit, ASLCallbackDouble callback)
{
m_d->mapUnitFloat.insert(path, UnitFloatMapping(unit, callback));
}
void KisAslCallbackObjectCatcher::subscribeText(const QString &path, ASLCallbackString callback)
{
m_d->mapText.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeBoolean(const QString &path, ASLCallbackBoolean callback)
{
m_d->mapBoolean.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeColor(const QString &path, ASLCallbackColor callback)
{
m_d->mapColor.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribePoint(const QString &path, ASLCallbackPoint callback)
{
m_d->mapPoint.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribeCurve(const QString &path, ASLCallbackCurve callback)
{
m_d->mapCurve.insert(path, callback);
}
void KisAslCallbackObjectCatcher::subscribePattern(const QString &path, ASLCallbackPattern callback)
{
m_d->mapPattern.insert(path, callback);
}
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_ASL_CALLBACK_OBJECT_CATCHER_H
#define __KIS_ASL_CALLBACK_OBJECT_CATCHER_H
#include "kis_asl_object_catcher.h"
#include <boost/function.hpp>
#include <QScopedPointer>
class KoPattern;
typedef boost::function<void (double)> ASLCallbackDouble;
typedef boost::function<void (int)> ASLCallbackInteger;
typedef boost::function<void (const QString &)> ASLCallbackString;
typedef boost::function<void (bool)> ASLCallbackBoolean;
typedef boost::function<void (const QColor &)> ASLCallbackColor;
typedef boost::function<void (const QPointF &)> ASLCallbackPoint;
typedef boost::function<void (const QString &, const QVector<QPointF> &)> ASLCallbackCurve;
typedef boost::function<void (KoPattern *)> ASLCallbackPattern;
class KRITAIMAGE_EXPORT KisAslCallbackObjectCatcher : public KisAslObjectCatcher
{
public:
KisAslCallbackObjectCatcher();
~KisAslCallbackObjectCatcher();
void addDouble(const QString &path, double value);
void addInteger(const QString &path, int value);
void addEnum(const QString &path, const QString &typeId, const QString &value);
void addUnitFloat(const QString &path, const QString &unit, double value);
void addText(const QString &path, const QString &value);
void addBoolean(const QString &path, bool value);
void addColor(const QString &path, const QColor &value);
void addPoint(const QString &path, const QPointF &value);
void addCurve(const QString &path, const QString &name, const QVector<QPointF> &points);
void addPattern(const QString &path, KoPattern *pattern);
void subscribeDouble(const QString &path, ASLCallbackDouble callback);
void subscribeInteger(const QString &path, ASLCallbackInteger callback);
void subscribeEnum(const QString &path, const QString &typeId, ASLCallbackString callback);
void subscribeUnitFloat(const QString &path, const QString &unit, ASLCallbackDouble callback);
void subscribeText(const QString &path, ASLCallbackString callback);
void subscribeBoolean(const QString &path, ASLCallbackBoolean callback);
void subscribeColor(const QString &path, ASLCallbackColor callback);
void subscribePoint(const QString &path, ASLCallbackPoint callback);
void subscribeCurve(const QString &path, ASLCallbackCurve callback);
void subscribePattern(const QString &path, ASLCallbackPattern callback);
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif /* __KIS_ASL_CALLBACK_OBJECT_CATCHER_H */
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_object_catcher.h"
#include <QString>
#include <QPointF>
#include <QColor>
#include "kis_debug.h"
KisAslObjectCatcher::KisAslObjectCatcher()
: m_arrayMode(false)
{
}
KisAslObjectCatcher::~KisAslObjectCatcher()
{
}
void KisAslObjectCatcher::addDouble(const QString &path, double value) {
qDebug() << (m_arrayMode ? "[A]" : "[ ]") << path << "double" << value;
}
void KisAslObjectCatcher::addInteger(const QString &path, int value) {
qDebug() << (m_arrayMode ? "[A]" : "[ ]") << path << "int" << value;
}
void KisAslObjectCatcher::addEnum(const QString &path, const QString &typeId, const QString &value) {
qDebug() << (m_arrayMode ? "[A]" : "[ ]") << path << "enum" << ppVar(typeId) << ppVar(value);
}
void KisAslObjectCatcher::addUnitFloat(const QString &path, const QString &unit, double value) {
qDebug() << (m_arrayMode ? "[A]" : "[ ]") << path << "unitfloat" << ppVar(unit) << ppVar(value);
}
void KisAslObjectCatcher::addText(const QString &path, const QString &value) {
qDebug() << (m_arrayMode ? "[A]" : "[ ]") << path << "text" << value;
}
void KisAslObjectCatcher::addBoolean(const QString &path, bool value) {
qDebug() << (m_arrayMode ? "[A]" : "[ ]") << path << "bool" << value;
}
void KisAslObjectCatcher::addColor(const QString &path, const QColor &value) {
qDebug() << (m_arrayMode ? "[A]" : "[ ]") << path << "color" << value;
}
void KisAslObjectCatcher::addPoint(const QString &path, const QPointF &value) {
qDebug() << (m_arrayMode ? "[A]" : "[ ]") << path << "point" << value;
}
void KisAslObjectCatcher::addCurve(const QString &path, const QString &name, const QVector<QPointF> &points) {
qDebug() << (m_arrayMode ? "[A]" : "[ ]") << path << "curve" << name << ppVar(points.size());
}
void KisAslObjectCatcher::addPattern(const QString &path, KoPattern *value)
{
qDebug() << (m_arrayMode ? "[A]" : "[ ]") << path << "pattern" << value;
}
void KisAslObjectCatcher::setArrayMode(bool value) {
m_arrayMode = value;
}
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_ASL_OBJECT_CATCHER_H
#define __KIS_ASL_OBJECT_CATCHER_H
#include <QVector>
#include "krita_export.h"
class QString;
class QColor;
class QPointF;
class KoPattern;
class KRITAIMAGE_EXPORT KisAslObjectCatcher
{
public:
KisAslObjectCatcher();
virtual ~KisAslObjectCatcher();
virtual void addDouble(const QString &path, double value);
virtual void addInteger(const QString &path, int value);
virtual void addEnum(const QString &path, const QString &typeId, const QString &value);
virtual void addUnitFloat(const QString &path, const QString &unit, double value);
virtual void addText(const QString &path, const QString &value);
virtual void addBoolean(const QString &path, bool value);
virtual void addColor(const QString &path, const QColor &value);
virtual void addPoint(const QString &path, const QPointF &value);
virtual void addCurve(const QString &path, const QString &name, const QVector<QPointF> &points);
virtual void addPattern(const QString &path, KoPattern *pattern);
void setArrayMode(bool value);
protected:
bool m_arrayMode;
};
#endif /* __KIS_ASL_OBJECT_CATCHER_H */
This diff is collapsed.
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __KIS_ASL_READER_H
#define __KIS_ASL_READER_H
#include "krita_export.h"
class QDomDocument;
class QIODevice;
class KRITAIMAGE_EXPORT KisAslReader
{
public:
QDomDocument readFile(QIODevice *device);
};
#endif /* __KIS_ASL_READER_H */
/*
* Copyright (c) 2015 Dmitry Kazakov <dimula73@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_asl_writer.h"
#include <stdexcept>
#include <string>
#include <QDomDocument>
#include <QIODevice>
#include "kis_dom_utils.h"
#include "kis_debug.h"
#include "psd_utils.h"
namespace Private {
/**
* Exception that is emitted when any write error appear.
*/
struct ASLWriteException : public std::runtime_error
{
ASLWriteException(const QString &msg)
: std::runtime_error(msg.toAscii().data())
{
}
};
#define SAFE_WRITE_EX(device, varname) \
if (!psdwrite(device, varname)) { \
QString msg = QString("Failed to write \'%1\' tag!").arg(#varname); \
throw ASLWriteException(msg); \
}
void writeUnicodeString(const QString &value, QIODevice *device)
{
quint32 len = value.length() + 1;
SAFE_WRITE_EX(device, len);
const quint16 *ptr = value.utf16();
for (quint32 i = 0; i < len; i++) {
SAFE_WRITE_EX(device, ptr[i]);
}
}
void writeVarString(const QString &value, QIODevice *device)
{
quint32 lenTag = value.length() != 4 ? value.length() : 0;
SAFE_WRITE_EX(device, lenTag);
if (!device->write(value.toAscii().data(), value.length())) {
qWarning() << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
return;
}
}
void writeFixedString(const QString &value, QIODevice *device)
{
KIS_ASSERT_RECOVER_RETURN(value.length() == 4);
if (!device->write(value.toAscii().data(), value.length())) {
qWarning() << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
return;
}
}
void parseElement(const QDomElement &el, QIODevice *device, bool forceTypeInfo = false)
{
KIS_ASSERT_RECOVER_RETURN(el.tagName() == "node");
QString type = el.attribute("type", "<unknown>");
QString key = el.attribute("key", "");
if (type == "Descriptor") {
if (!key.isEmpty()) {
writeVarString(key, device);
}
if (!key.isEmpty() || forceTypeInfo) {
writeFixedString("Objc", device);
}
QString classId = el.attribute("classId", "");
QString name = el.attribute("name", "");
writeUnicodeString(name, device);
writeVarString(classId, device);
quint32 numChildren = el.childNodes().size();
SAFE_WRITE_EX(device, numChildren);
QDomNode child = el.firstChild();
while (!child.isNull()) {
parseElement(child.toElement(), device);
child = child.nextSibling();
}
} else if (type == "List") {
writeVarString(key, device);
writeFixedString("VlLs", device);
quint32 numChildren = el.childNodes().size();
SAFE_WRITE_EX(device, numChildren);
QDomNode child = el.firstChild();
while (!child.isNull()) {
parseElement(child.toElement(), device, true);
child = child.nextSibling();
}
} else if (type == "Double") {
double v = KisDomUtils::Private::stringToDouble(el.attribute("value", "0"));
writeVarString(key, device);
writeFixedString("doub", device);
SAFE_WRITE_EX(device, v);
} else if (type == "UnitFloat") {
double v = KisDomUtils::Private::stringToDouble(el.attribute("value", "0"));
QString unit = el.attribute("unit", "#Pxl");
writeVarString(key, device);
writeFixedString("UntF", device);
writeFixedString(unit, device);
SAFE_WRITE_EX(device, v);
} else if (type == "Text") {
QString v = el.attribute("value", "");
writeVarString(key, device);
writeFixedString("TEXT", device);
writeUnicodeString(v, device);
} else if (type == "Enum") {
QString v = el.attribute("value", "");
QString typeId = el.attribute("typeId", "DEAD");
writeVarString(key, device);
writeFixedString("enum", device);
writeVarString(typeId, device);
writeVarString(v, device);
} else if (type == "Integer") {
quint32 v = KisDomUtils::Private::stringToInt(el.attribute("value", "0"));
writeVarString(key, device);
writeFixedString("long", device);
SAFE_WRITE_EX(device, v);
} else if (type == "Boolean") {
quint8 v = KisDomUtils::Private::stringToInt(el.attribute("value", "0"));
writeVarString(key, device);
writeFixedString("bool", device);
SAFE_WRITE_EX(device, v);
} else {
qWarning() << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(key);
}
}
void writeFileImpl(QIODevice *device, const QDomDocument &doc)
{
{
quint16 stylesVersion = 2;
SAFE_WRITE_EX(device, stylesVersion);
}
{
QString signature("8BSL");
if (!device->write(signature.toAscii().data(), 4)) {
throw ASLWriteException("Failed to write ASL signature");
}
}
{
quint16 patternsVersion = 3;
SAFE_WRITE_EX(device, patternsVersion);
}
{
// FIXME: not implemented yet
quint32 patternsSize = 0;
SAFE_WRITE_EX(device, patternsSize);
}
{
// FIXME: real number of styles