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 */