File indexing completed on 2024-06-23 04:23:33

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
0003  *  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #ifndef __KIS_ASL_WRITER_UTILS_H
0009 #define __KIS_ASL_WRITER_UTILS_H
0010 
0011 #include "kritapsdutils_export.h"
0012 
0013 #include <stdexcept>
0014 #include <string>
0015 
0016 #include <QIODevice>
0017 #include <QUuid>
0018 
0019 #include <kis_debug.h>
0020 #include <resources/KoPattern.h>
0021 
0022 #include "psd.h"
0023 #include "psd_utils.h"
0024 
0025 namespace KisAslWriterUtils
0026 {
0027 /**
0028  * Exception that is emitted when any write error appear.
0029  */
0030 struct KRITAPSDUTILS_EXPORT ASLWriteException : public std::runtime_error {
0031     ASLWriteException(const QString &msg)
0032         : std::runtime_error(msg.toLatin1().data())
0033     {
0034     }
0035 };
0036 
0037 }
0038 
0039 #define SAFE_WRITE_EX(byteOrder, device, varname)                                                                                                              \
0040     if (!psdwrite<byteOrder>(device, varname)) {                                                                                                               \
0041         QString msg = QString("Failed to write \'%1\' tag!").arg(#varname);                                                                                    \
0042         throw KisAslWriterUtils::ASLWriteException(msg);                                                                                                       \
0043     }
0044 
0045 namespace KisAslWriterUtils
0046 {
0047 // XXX: rect uses variable-sized type, is this correct?
0048 template<psd_byte_order byteOrder>
0049 inline void writeRect(const QRect &rect, QIODevice &device)
0050 {
0051     {
0052         const qint32 rectY0 = static_cast<qint32>(rect.y());
0053         SAFE_WRITE_EX(byteOrder, device, rectY0);
0054     }
0055     {
0056         const qint32 rectX0 = static_cast<qint32>(rect.x());
0057         SAFE_WRITE_EX(byteOrder, device, rectX0);
0058     }
0059     {
0060         const qint32 rectY1 = static_cast<qint32>(rect.y() + rect.height());
0061         SAFE_WRITE_EX(byteOrder, device, rectY1);
0062     }
0063     {
0064         const qint32 rectX1 = static_cast<qint32>(rect.x() + rect.width());
0065         SAFE_WRITE_EX(byteOrder, device, rectX1);
0066     }
0067 }
0068 
0069 template<psd_byte_order byteOrder>
0070 inline void writeUnicodeString(const QString &value, QIODevice &device)
0071 {
0072     const quint32 len = static_cast<quint32>(value.length() + 1);
0073     SAFE_WRITE_EX(byteOrder, device, len);
0074 
0075     const quint16 *ptr = value.utf16();
0076     for (quint32 i = 0; i < len; i++) {
0077         SAFE_WRITE_EX(byteOrder, device, ptr[i]);
0078     }
0079 }
0080 
0081 template<psd_byte_order byteOrder>
0082 inline void writeVarString(const QString &value, QIODevice &device)
0083 {
0084     const quint32 lenTag = static_cast<quint32>(value.length() != 4 ? value.length() : 0);
0085     SAFE_WRITE_EX(byteOrder, device, lenTag);
0086 
0087     if (!device.write(value.toLatin1().data(), value.length())) {
0088         warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
0089         return;
0090     }
0091 }
0092 
0093 template<psd_byte_order byteOrder>
0094 inline void writePascalString(const QString &value, QIODevice &device)
0095 {
0096     KIS_ASSERT_RECOVER_RETURN(value.length() < 256);
0097     KIS_ASSERT_RECOVER_RETURN(value.length() >= 0);
0098     const quint8 lenTag = static_cast<quint8>(value.length());
0099     SAFE_WRITE_EX(byteOrder, device, lenTag);
0100 
0101     if (!device.write(value.toLatin1().data(), value.length())) {
0102         warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
0103         return;
0104     }
0105 }
0106 
0107 template<psd_byte_order byteOrder>
0108 inline void writeFixedString(const QString &value, QIODevice &device)
0109 {
0110     KIS_ASSERT_RECOVER_RETURN(value.length() == 4);
0111 
0112     QByteArray data = value.toLatin1();
0113 
0114     if (byteOrder == psd_byte_order::psdLittleEndian) {
0115         std::reverse(data.begin(), data.end());
0116     }
0117 
0118     if (!device.write(data.data(), value.length())) {
0119         warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value);
0120         return;
0121     }
0122 }
0123 
0124 // Write UUID fetched from the file name or generate
0125 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0126 inline QString getPatternUuidLazy(const KoPatternSP pattern)
0127 {
0128     QUuid uuid;
0129     QString patternFileName = pattern->filename();
0130 
0131     if (patternFileName.endsWith(".pat", Qt::CaseInsensitive)) {
0132         QString strUuid = patternFileName.left(patternFileName.size() - 4);
0133 
0134         uuid = QUuid(strUuid);
0135     }
0136 
0137     if (uuid.isNull()) {
0138         warnKrita << "WARNING: Saved pattern doesn't have a UUID, generating...";
0139         warnKrita << ppVar(patternFileName) << ppVar(pattern->name());
0140         uuid = QUuid::createUuid();
0141     }
0142 
0143     return uuid.toString().mid(1, 36);
0144 }
0145 
0146 /**
0147  * Align the pointer \p pos by alignment. Grow the pointer
0148  * if needed.
0149  *
0150  * \return the lowest integer not smaller than \p pos that divides by
0151  *         alignment
0152  */
0153 inline qint64 alignOffsetCeil(qint64 pos, qint64 alignment)
0154 {
0155     qint64 mask = alignment - 1;
0156     return (pos + mask) & ~mask;
0157 }
0158 
0159 template<class OffsetType, psd_byte_order byteOrder>
0160 class OffsetStreamPusher
0161 {
0162 public:
0163     OffsetStreamPusher(QIODevice &device, qint64 alignOnExit = 0, qint64 externalSizeTagOffset = -1)
0164         : m_device(device)
0165         , m_alignOnExit(alignOnExit)
0166         , m_externalSizeTagOffset(externalSizeTagOffset)
0167     {
0168         m_chunkStartPos = m_device.pos();
0169 
0170         if (externalSizeTagOffset < 0) {
0171             const OffsetType fakeObjectSize = OffsetType(0xdeadbeef);
0172             SAFE_WRITE_EX(byteOrder, m_device, fakeObjectSize);
0173         }
0174     }
0175 
0176     ~OffsetStreamPusher()
0177     {
0178         try {
0179             if (m_alignOnExit) {
0180                 qint64 currentPos = m_device.pos();
0181                 const qint64 alignedPos = alignOffsetCeil(currentPos, m_alignOnExit);
0182 
0183                 for (; currentPos < alignedPos; currentPos++) {
0184                     quint8 padding = 0;
0185                     SAFE_WRITE_EX(byteOrder, m_device, padding);
0186                 }
0187             }
0188 
0189             const qint64 currentPos = m_device.pos();
0190 
0191             qint64 writtenDataSize = 0;
0192             qint64 sizeFiledOffset = 0;
0193 
0194             if (m_externalSizeTagOffset >= 0) {
0195                 writtenDataSize = currentPos - m_chunkStartPos;
0196                 sizeFiledOffset = m_externalSizeTagOffset;
0197             } else {
0198                 writtenDataSize = currentPos - m_chunkStartPos - sizeof(OffsetType);
0199                 sizeFiledOffset = m_chunkStartPos;
0200             }
0201 
0202             m_device.seek(sizeFiledOffset);
0203             const OffsetType realObjectSize = writtenDataSize;
0204             SAFE_WRITE_EX(byteOrder, m_device, realObjectSize);
0205             m_device.seek(currentPos);
0206         } catch (ASLWriteException &e) {
0207             warnKrita << PREPEND_METHOD(e.what());
0208         }
0209     }
0210 
0211 private:
0212     qint64 m_chunkStartPos;
0213     QIODevice &m_device;
0214     qint64 m_alignOnExit;
0215     qint64 m_externalSizeTagOffset;
0216 };
0217 
0218 }
0219 
0220 #endif /* __KIS_ASL_WRITER_UTILS_H */