kis_asl_writer.cpp 7.78 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 *  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 <QDomDocument>
#include <QIODevice>

#include "kis_dom_utils.h"

#include "kis_debug.h"
#include "psd_utils.h"

29 30
#include "kis_asl_patterns_writer.h"
#include "kis_asl_writer_utils.h"
31 32


33
namespace Private {
34

35
using namespace KisAslWriterUtils;
36 37 38 39 40 41 42 43

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", "");

luz paz's avatar
luz paz committed
44
    // should be filtered on a higher level
45 46
    KIS_ASSERT_RECOVER_RETURN(key != "Patterns");

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
    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") {
85
        double v = KisDomUtils::toDouble(el.attribute("value", "0"));
86 87 88 89 90 91

        writeVarString(key, device);
        writeFixedString("doub", device);
        SAFE_WRITE_EX(device, v);

    } else if (type == "UnitFloat") {
92
        double v = KisDomUtils::toDouble(el.attribute("value", "0"));
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
        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") {
112
        quint32 v = KisDomUtils::toInt(el.attribute("value", "0"));
113 114 115 116
        writeVarString(key, device);
        writeFixedString("long", device);
        SAFE_WRITE_EX(device, v);
    } else if (type == "Boolean") {
117
        quint8 v = KisDomUtils::toInt(el.attribute("value", "0"));
118 119 120 121 122

        writeVarString(key, device);
        writeFixedString("bool", device);
        SAFE_WRITE_EX(device, v);
    } else {
123
        warnKrita << "WARNING: XML (ASL) Unknown element type:" << type << ppVar(key);
124 125 126
    }
}

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
int calculateNumStyles(const QDomElement &root)
{
    int numStyles = 0;
    QDomNode child = root.firstChild();

    while (!child.isNull()) {
        QDomElement el = child.toElement();
        QString classId = el.attribute("classId", "");

        if (classId == "null") {
            numStyles++;
        }

        child = child.nextSibling();
    }

    return numStyles;
}

146 147 148 149 150 151 152 153 154
void writeFileImpl(QIODevice *device, const QDomDocument &doc)
{
    {
        quint16 stylesVersion = 2;
        SAFE_WRITE_EX(device, stylesVersion);
    }

    {
        QString signature("8BSL");
155
        if (!device->write(signature.toLatin1().data(), 4)) {
156 157 158 159 160 161 162 163 164 165
            throw ASLWriteException("Failed to write ASL signature");
        }
    }

    {
        quint16 patternsVersion = 3;
        SAFE_WRITE_EX(device, patternsVersion);
    }

    {
166 167 168 169
        KisAslWriterUtils::OffsetStreamPusher<quint32> patternsSizeField(device);

        KisAslPatternsWriter patternsWriter(doc, device);
        patternsWriter.writePatterns();
170 171
    }

172 173 174 175 176 177
    QDomElement root = doc.documentElement();
    KIS_ASSERT_RECOVER_RETURN(root.tagName() == "asl");

    int numStyles = calculateNumStyles(root);
    KIS_ASSERT_RECOVER_RETURN(numStyles > 0);

178
    {
179 180
        quint32 numStylesTag = numStyles;
        SAFE_WRITE_EX(device, numStylesTag);
181 182
    }

183
    QDomNode child = root.firstChild();
184

185
    for (int styleIndex = 0; styleIndex < numStyles; styleIndex++) {
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
        KisAslWriterUtils::OffsetStreamPusher<quint32> theOnlyStyleSizeField(device);

        KIS_ASSERT_RECOVER_RETURN(!child.isNull());

        {
            quint32 stylesFormatVersion = 16;
            SAFE_WRITE_EX(device, stylesFormatVersion);
        }

        while (!child.isNull()) {
            QDomElement el = child.toElement();
            QString key = el.attribute("key", "");

            if (key != "Patterns") break;

            child = child.nextSibling();
        }

        parseElement(child.toElement(), device);
        child = child.nextSibling();

        {
            quint32 stylesFormatVersion = 16;
            SAFE_WRITE_EX(device, stylesFormatVersion);
        }

        parseElement(child.toElement(), device);
        child = child.nextSibling();

        // ASL files' size should be 4-bytes aligned
        const qint64 paddingSize = 4 - (device->pos() & 0x3);
        if (paddingSize != 4) {
            QByteArray padding(paddingSize, '\0');
            device->write(padding);
        }
221 222
    }
}
223

224 225 226 227
void writePsdLfx2SectionImpl(QIODevice *device, const QDomDocument &doc)
{
    QDomElement root = doc.documentElement();
    KIS_ASSERT_RECOVER_RETURN(root.tagName() == "asl");
228

229 230
    int numStyles = calculateNumStyles(root);
    KIS_ASSERT_RECOVER_RETURN(numStyles == 1);
231

232 233 234 235
    {
        quint32 objectEffectsVersion = 0;
        SAFE_WRITE_EX(device, objectEffectsVersion);
    }
236

237 238 239 240
    {
        quint32 descriptorVersion = 16;
        SAFE_WRITE_EX(device, descriptorVersion);
    }
241

242
    QDomNode child = root.firstChild();
243

244 245 246
    while (!child.isNull()) {
        QDomElement el = child.toElement();
        QString key = el.attribute("key", "");
247

248
        if (key != "Patterns") break;
249

250
        child = child.nextSibling();
251
    }
252

253 254 255 256 257 258 259 260
    parseElement(child.toElement(), device);
    child = child.nextSibling();

    // ASL files' size should be 4-bytes aligned
    const qint64 paddingSize = 4 - (device->pos() & 0x3);
    if (paddingSize != 4) {
        QByteArray padding(paddingSize, '\0');
        device->write(padding);
261
    }
262 263 264 265 266 267 268 269 270
}

} // namespace

void KisAslWriter::writeFile(QIODevice *device, const QDomDocument &doc)
{
    try {
        Private::writeFileImpl(device, doc);
    } catch (Private::ASLWriteException &e) {
271
        warnKrita << "WARNING: ASL:" << e.what();
272 273
    }
}
274 275 276 277 278

void KisAslWriter::writePsdLfx2SectionEx(QIODevice *device, const QDomDocument &doc)
{
    Private::writePsdLfx2SectionImpl(device, doc);
}