Krita Source Code Documentation
Loading...
Searching...
No Matches
psd_additional_layer_info_block.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2014 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
9#include "psd.h"
10
11#include <QDomDocument>
12
14
16#include <asl/kis_asl_reader.h>
18#include <asl/kis_asl_writer.h>
20#include <cos/kis_txt2_utls.h>
21
22
24 : m_header(header)
25 , sectionDividerType(psd_other)
26{
27}
28
33
38
40{
41 bool result = true;
42
43 try {
44 switch (m_header.byteOrder) {
46 readImpl<psd_byte_order::psdLittleEndian>(io);
47 break;
48 default:
49 readImpl(io);
50 break;
51 }
53 error = e.what();
54 result = false;
55 }
56
57 return result;
58}
59
60template<psd_byte_order byteOrder>
62{
63 using namespace KisAslReaderUtils;
64
65 QStringList longBlocks;
66 if (m_header.version > 1) {
67 longBlocks << "LMsk"
68 << "Lr16"
69 << "Lr32"
70 << "Layr"
71 << "Mt16"
72 << "Mt32"
73 << "Mtrn"
74 << "Alph"
75 << "FMsk"
76 << "lnk2"
77 << "FEid"
78 << "FXid"
79 << "PxSD";
80 }
81
82 while (!io.atEnd()) {
83 {
84 const std::array<quint8, 4> refSignature1 = {'8', 'B', 'I', 'M'}; // '8BIM' in big-endian
85 const std::array<quint8, 4> refSignature2 = {'8', 'B', '6', '4'}; // '8B64' in big-endian
86
87 if (!TRY_READ_SIGNATURE_2OPS_EX<byteOrder>(io, refSignature1, refSignature2)) {
88 break;
89 }
90 }
91
92 QString key = readFixedString<byteOrder>(io);
93 dbgFile << "found info block with key" << key << "(" << io.pos() << ")";
94
95 quint64 blockSize = GARBAGE_VALUE_MARK;
96 if (longBlocks.contains(key)) {
97 SAFE_READ_EX(byteOrder, io, blockSize);
98 } else {
99 quint32 size32;
100 SAFE_READ_EX(byteOrder, io, size32);
101 blockSize = size32;
102 }
103
104 // Since TIFF headers are padded to multiples of 4,
105 // staving them off here is way easier.
107 if (blockSize % 4U) {
108 dbgFile << "(TIFF) WARNING: current block size is NOT a multiple of 4! Fixing...";
109 blockSize += (4U - blockSize % 4U);
110 }
111 }
112
113 dbgFile << "info block size" << blockSize << "(" << io.pos() << ")";
114
115 if (blockSize == 0)
116 continue;
117
118 // offset verifier will correct the position on the exit from
119 // current namespace, including 'continue', 'return' and
120 // exceptions.
121 SETUP_OFFSET_VERIFIER(infoBlockEndVerifier, io, blockSize, 0);
122
123 if (keys.contains(key)) {
124 error = "Found duplicate entry for key ";
125 continue;
126 }
127 keys << key;
128
129 // TODO: Loading of 32 bit files is not supported yet
130 if (key == "Lr16" /* || key == "Lr32"*/) {
132 int offset = m_header.version > 1 ? 8 : 4;
133 dbgFile << "Offset for block handler: " << io.pos() << offset;
134 io.seek(io.pos() - offset);
136 }
137 } else if (key == "Layr") {
139 int offset = m_header.version > 1 ? 8 : 4;
140 dbgFile << "(TIFF) Offset for block handler: " << io.pos() << offset;
141 io.seek(io.pos() - offset);
143 }
144 } else if (key == "SoCo") {
145 // Solid Color
148 } else if (key == "GdFl") {
149 // Gradient Fill
152 } else if (key == "PtFl") {
153 // Pattern Fill
156 } else if (key == "brit") {
157 } else if (key == "levl") {
158 } else if (key == "curv") {
159 } else if (key == "expA") {
160 } else if (key == "vibA") {
161 } else if (key == "hue") {
162 } else if (key == "hue2") {
163 } else if (key == "blnc") {
164 } else if (key == "blwh") {
165 } else if (key == "phfl") {
166 } else if (key == "mixr") {
167 } else if (key == "clrL") {
168 } else if (key == "nvrt") {
169 } else if (key == "post") {
170 } else if (key == "thrs") {
171 } else if (key == "selc") {
172 } else if (key == "lrFX") {
173 // deprecated! use lfx2 instead!
174 } else if (key == "tySh") {
175 } else if (key == "luni") {
176 // get the unicode layer name
177 unicodeLayerName = readUnicodeString<byteOrder>(io);
178 dbgFile << "\t" << "unicodeLayerName" << unicodeLayerName;
179 } else if (key == "lyid") {
180 quint32 id;
181 psdread<byteOrder>(io, id);
182 dbgFile << "\t" << "layer ID:" << id;
183 } else if (key == "lfx2" || key == "lfxs") {
184 // lfxs is a special variant of layer styles for group layers
186 } else if (key == "Patt" || key == "Pat2" || key == "Pat3") {
187 QDomDocument pattern = KisAslReader::readPsdSectionPattern(io, blockSize, byteOrder);
188 embeddedPatterns << pattern;
189 } else if (key == "Anno") {
190 } else if (key == "clbl") {
191 } else if (key == "infx") {
192 } else if (key == "knko") {
193 } else if (key == "spf") {
194 } else if (key == "lclr") {
195 // layer label color.
196 quint16 col1 = 0;
197 quint16 col2 = 0;
198 quint16 col3 = 0;
199 quint16 col4 = 0;
200 psdread<byteOrder>(io, col1);
201 psdread<byteOrder>(io, col2);
202 psdread<byteOrder>(io, col3);
203 psdread<byteOrder>(io, col4);
204 dbgFile << "\t" << "layer color:" << col1 << col2 << col3 << col4;
205 labelColor = col1;
206 } else if (key == "fxrp") {
207 } else if (key == "grdm") {
208 } else if (key == "lsct") {
209 quint32 dividerType = GARBAGE_VALUE_MARK;
210 SAFE_READ_EX(byteOrder, io, dividerType);
211 this->sectionDividerType = (psd_section_type)dividerType;
212
213 dbgFile << "Reading \"lsct\" block:";
214 dbgFile << ppVar(blockSize);
215 dbgFile << ppVar(dividerType);
216
217 if (blockSize >= 12) {
218 quint32 lsctSignature = GARBAGE_VALUE_MARK;
219 const quint32 refSignature1 = 0x3842494D; // '8BIM' in little-endian
220 SAFE_READ_SIGNATURE_EX(byteOrder, io, lsctSignature, refSignature1);
221
222 this->sectionDividerBlendMode = readFixedString<byteOrder>(io);
223
225 }
226
227 // Animation
228 if (blockSize >= 14) {
233 }
234
235 } else if (key == "brst") {
236 } else if (key == "vmsk" || key == "vsms") { // If key is "vsms" then we are writing for (Photoshop CS6) and the document will have a "vscg" key
237 quint32 version; // ( = 3 for Photoshop 6.0)
238 psdread<byteOrder>(io, version);
239
240 quint32 flags;
241 psdread<byteOrder>(io, flags);
242 // read the flags.
243 vectorMask.invert = flags & 1? true: false;
244 vectorMask.notLink = flags & 2? true: false;
245 vectorMask.disable = flags & 4? true: false;
246
247 quint64 currentPos = 8;
248 psd_path_sub_path currentPath;
249 bool firstPath = true;
250
251 while (currentPos < blockSize) {
252 quint16 recordType;
253 psdread<byteOrder>(io, recordType);
254
255 if (recordType == 6) {
256 io.skip(24);
257 dbgFile << "\trecord" << recordType;
258 } else if (recordType == 7) {
259 QRectF bounds;
260 // unsure if there can be multiple clipboard records...
261 bounds.setTop(psdreadFixedPoint<byteOrder>(io));
262 bounds.setLeft(psdreadFixedPoint<byteOrder>(io));
263 bounds.setBottom(psdreadFixedPoint<byteOrder>(io));
264 bounds.setRight(psdreadFixedPoint<byteOrder>(io));
266 vectorMask.path.clipBoardResolution = psdreadFixedPoint<byteOrder>(io);
267 dbgFile << "\trecord" << recordType << "top" << bounds << "res" << vectorMask.path.clipBoardResolution;
268 io.skip(4);
269 } else if (recordType == 0 || recordType == 3) {
270 quint16 length;
271 psdread<byteOrder>(io, length);
272 dbgFile << "\trecord" << recordType << "length" << length;
273 if (firstPath) {
274 currentPath.isClosed = (recordType == 0);
275 } else {
276 vectorMask.path.subPaths.append(currentPath);
277 currentPath = psd_path_sub_path();
278 currentPath.isClosed = (recordType == 0);
279 }
280 firstPath = false;
281 io.skip(22);
282 } else if (recordType == 8) {
283 quint16 length;
284 psdread<byteOrder>(io, length);
285 dbgFile << "\trecord" << recordType << "length" << length;
287 io.skip(22);
288 } else {
289 psd_path_node node;
290 node.control1.setY(psdreadFixedPoint<byteOrder>(io));
291 node.control1.setX(psdreadFixedPoint<byteOrder>(io));
292 node.node.setY(psdreadFixedPoint<byteOrder>(io));
293 node.node.setX(psdreadFixedPoint<byteOrder>(io));
294 node.control2.setY(psdreadFixedPoint<byteOrder>(io));
295 node.control2.setX(psdreadFixedPoint<byteOrder>(io));
296 node.isSmooth = (recordType == 1 || recordType == 4);
297 dbgFile << "\trecord" << recordType << "c1" << node.control1
298 << "node" << node.node << "c2" << node.control2;
299 currentPath.nodes.append(node);
300 }
301
302 currentPos += 26;
303 }
304 if (!currentPath.nodes.isEmpty()) {
305 vectorMask.path.subPaths.append(currentPath);
306 }
307
308 } else if (key == "TySh") {
310 } else if (key == "ffxi") {
311 } else if (key == "lnsr") {
312 } else if (key == "shpa") {
313 } else if (key == "shmd") {
314 } else if (key == "lyvr") {
315 } else if (key == "tsly") {
316 } else if (key == "lmgm") {
317 } else if (key == "vmgm") {
318 } else if (key == "plLd") { // Replaced by SoLd in CS3
319
320 } else if (key == "linkD" || key == "lnk2" || key == "lnk3") {
321 } else if (key == "CgEd") {
322 } else if (key == "Txt2") { // global text data, basically the same as an Illustrator text object.
323 // Docs say "first 4 are length", this is not true for this particular block, only when in ASL is first 4 length.
324 QByteArray ba = io.read(blockSize);
326 txt2Data = KisTxt2Utils::uncompressKeys(p.parseCosToJson(&ba));
327 } else if (key == "pths") {
328 } else if (key == "anFX") {
329 } else if (key == "FMsk") {
330 } else if (key == "SoLd") {
331 } else if (key == "vstk") { // vector stroke info
333 } else if (key == "vscg") {
334 if (blockSize > 4) {
335 QString vscgKey = readFixedString<byteOrder>(io);
337 if (vscgKey == "SoCo") {
339 } else if (vscgKey == "GdFl") {
340 // Gradient Fill
342 } else if (vscgKey == "PtFl") {
343 // Pattern Fill
345 }
346 }
347 } else if (key == "sn2P") {
348 } else if (key == "vogk") { // Live path shapes, these are similar to parametric shapes.
350 } else if (key == "Mtrn" || key == "Mt16" || key == "Mt32") { // There is no data associated with these keys.
351
352 } else if (key == "LMsk") {
353 // TIFFs store the global mask here.
355 int offset = m_header.version > 1 ? 8 : 4;
356 dbgFile << "(TIFF) Offset for block handler: " << io.pos() << offset;
357 io.seek(io.pos() - offset);
359 }
360 } else if (key == "FXid") {
361 } else if (key == "FEid") {
362 }
363 }
364}
365
366bool PsdAdditionalLayerInfoBlock::write(QIODevice & /*io*/, KisNodeSP /*node*/)
367{
368 return true;
369}
370
372{
373 return true;
374}
375
376void PsdAdditionalLayerInfoBlock::writeLuniBlockEx(QIODevice &io, const QString &layerName)
377{
378 switch (m_header.byteOrder) {
380 writeLuniBlockExImpl<psd_byte_order::psdLittleEndian>(io, layerName);
381 break;
382 default:
383 writeLuniBlockExImpl(io, layerName);
384 break;
385 }
386}
387
388template<psd_byte_order byteOrder>
389void PsdAdditionalLayerInfoBlock::writeLuniBlockExImpl(QIODevice &io, const QString &layerName)
390{
391 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
392 KisAslWriterUtils::writeFixedString<byteOrder>("luni", io);
394 KisAslWriterUtils::writeUnicodeString<byteOrder>(layerName, io);
395}
396
397void PsdAdditionalLayerInfoBlock::writeLsctBlockEx(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey)
398{
399 switch (m_header.byteOrder) {
401 writeLsctBlockExImpl<psd_byte_order::psdLittleEndian>(io, sectionType, isPassThrough, blendModeKey);
402 break;
403 default:
404 writeLsctBlockExImpl(io, sectionType, isPassThrough, blendModeKey);
405 break;
406 }
407}
408
409template<psd_byte_order byteOrder>
410void PsdAdditionalLayerInfoBlock::writeLsctBlockExImpl(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey)
411{
412 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
413 KisAslWriterUtils::writeFixedString<byteOrder>("lsct", io);
415 SAFE_WRITE_EX(byteOrder, io, (quint32)sectionType);
416
417 QString realBlendModeKey = isPassThrough ? QString("pass") : blendModeKey;
418
419 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
420 KisAslWriterUtils::writeFixedString<byteOrder>(realBlendModeKey, io);
421}
422
423void PsdAdditionalLayerInfoBlock::writeLfx2BlockEx(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
424{
425 switch (m_header.byteOrder) {
427 writeLfx2BlockExImpl<psd_byte_order::psdLittleEndian>(io, stylesXmlDoc, useLfxsLayerStyleFormat);
428 break;
429 default:
430 writeLfx2BlockExImpl(io, stylesXmlDoc, useLfxsLayerStyleFormat);
431 break;
432 }
433}
434
435template<psd_byte_order byteOrder>
436void PsdAdditionalLayerInfoBlock::writeLfx2BlockExImpl(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
437{
438 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
439 // 'lfxs' format is used for Group layers in PS
440 KisAslWriterUtils::writeFixedString<byteOrder>(!useLfxsLayerStyleFormat ? "lfx2" : "lfxs", io);
442
443 try {
444 KisAslWriter writer(byteOrder);
445 writer.writePsdLfx2SectionEx(io, stylesXmlDoc);
446
448 warnKrita << "WARNING: Couldn't save layer style lfx2 block:" << PREPEND_METHOD(e.what());
449
450 // TODO: make this error recoverable!
451 throw e;
452 }
453}
454
455void PsdAdditionalLayerInfoBlock::writePattBlockEx(QIODevice &io, const QDomDocument &patternsXmlDoc)
456{
457 switch (m_header.byteOrder) {
459 writePattBlockExImpl<psd_byte_order::psdLittleEndian>(io, patternsXmlDoc);
460 break;
461 default:
462 writePattBlockExImpl(io, patternsXmlDoc);
463 break;
464 }
465}
466
467void PsdAdditionalLayerInfoBlock::writeLclrBlockEx(QIODevice &io, const quint16 &labelColor)
468{
469 switch (m_header.byteOrder) {
471 writeLclrBlockExImpl<psd_byte_order::psdLittleEndian>(io, labelColor);
472 break;
473 default:
475 break;
476 }
477}
478
479void PsdAdditionalLayerInfoBlock::writeFillLayerBlockEx(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type)
480{
481 switch (m_header.byteOrder) {
483 writeFillLayerBlockExImpl<psd_byte_order::psdLittleEndian>(io, fillConfig, type);
484 break;
485 default:
487 break;
488 }
489}
490
492{
493 switch (m_header.byteOrder) {
495 writeVectorMaskImpl<psd_byte_order::psdLittleEndian>(io, mask);
496 break;
497 default:
498 writeVectorMaskImpl(io, mask);
499 break;
500 }
501}
502
504{
505 switch (m_header.byteOrder) {
507 writeTypeToolImpl<psd_byte_order::psdLittleEndian>(io, typeTool);
508 break;
509 default:
510 writeTypeToolImpl(io, typeTool);
511 break;
512 }
513}
514
515void PsdAdditionalLayerInfoBlock::writeVectorStrokeDataEx(QIODevice &io, const QDomDocument &vectorStroke)
516{
517 switch (m_header.byteOrder) {
519 writeVectorStrokeDataImpl<psd_byte_order::psdLittleEndian>(io, vectorStroke);
520 break;
521 default:
523 break;
524 }
525}
526
527void PsdAdditionalLayerInfoBlock::writeVectorOriginationDataEx(QIODevice &io, const QDomDocument &vectorOrigination)
528{
529 switch (m_header.byteOrder) {
531 writeVectorOriginationDataImpl<psd_byte_order::psdLittleEndian>(io, vectorOrigination);
532 break;
533 default:
534 writeVectorOriginationDataImpl(io, vectorOrigination);
535 break;
536 }
537}
538
539template<psd_byte_order byteOrder>
540void PsdAdditionalLayerInfoBlock::writePattBlockExImpl(QIODevice &io, const QDomDocument &patternsXmlDoc)
541{
542 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
543 KisAslWriterUtils::writeFixedString<byteOrder>("Patt", io);
544 const quint32 padding = m_header.tiffStyleLayerBlock ? 4 : 2;
546
547 try {
548 KisAslPatternsWriter writer(patternsXmlDoc, io, byteOrder);
549 writer.writePatterns();
550
552 warnKrita << "WARNING: Couldn't save layer style patterns block:" << PREPEND_METHOD(e.what());
553
554 // TODO: make this error recoverable!
555 throw e;
556 }
557}
558
559template<psd_byte_order byteOrder>
560void PsdAdditionalLayerInfoBlock::writeLclrBlockExImpl(QIODevice &io, const quint16 &lclr)
561{
562 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
563 KisAslWriterUtils::writeFixedString<byteOrder>("lclr", io);
564 // 4x2 quint16
565 const quint32 len = 8;
566 SAFE_WRITE_EX(byteOrder, io, len);
567 quint16 zero = 0;
568 SAFE_WRITE_EX(byteOrder, io, lclr);
569 SAFE_WRITE_EX(byteOrder, io, zero);
570 SAFE_WRITE_EX(byteOrder, io, zero);
571 SAFE_WRITE_EX(byteOrder, io, zero);
572
573
574}
575
576template<psd_byte_order byteOrder>
577void PsdAdditionalLayerInfoBlock::writeFillLayerBlockExImpl(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type)
578{
579 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
580 if (type == psd_fill_solid_color) {
581 KisAslWriterUtils::writeFixedString<byteOrder>("SoCo", io);
582 } else if (type == psd_fill_gradient) {
583 KisAslWriterUtils::writeFixedString<byteOrder>("GdFl", io);
584 } else {
585 KisAslWriterUtils::writeFixedString<byteOrder>("PtFl", io);
586 }
588
589 try {
590 KisAslWriter writer(byteOrder);
591
593
595 warnKrita << "WARNING: Couldn't save fill layer block:" << PREPEND_METHOD(e.what());
596
597 // TODO: make this error recoverable!
598 throw e;
599 }
600}
601
602template<psd_byte_order byteOrder>
604{
605 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
606 KisAslWriterUtils::writeFixedString<byteOrder>("vmsk", io);
607
608 quint32 len = 8; //, 4 version, 4 flags
609 len += 52; // 26 path record, 26 initial fill rule.
610 Q_FOREACH(psd_path_sub_path subPath, mask.path.subPaths) {
611 len += 26; // subpath record;
612 len += subPath.nodes.size() * 26; //and one for each node.
613 }
614
615 SAFE_WRITE_EX(byteOrder, io, len);
616
617 quint32 version = 3;
618 SAFE_WRITE_EX(byteOrder, io, version);
619 quint32 flags = 0;
620 if (mask.invert) {
621 flags |= 1;
622 }
623 if (mask.notLink) {
624 flags |= 2;
625 }
626 if (mask.disable) {
627 flags |= 4;
628 }
629 SAFE_WRITE_EX(byteOrder, io, flags);
630
631 // start path records
632 quint16 recordType = 6;
633 quint32 zero = 0;
634 SAFE_WRITE_EX(byteOrder, io, recordType);
635 // 24 empty bits
636 SAFE_WRITE_EX(byteOrder, io, zero);
637 SAFE_WRITE_EX(byteOrder, io, zero);
638 SAFE_WRITE_EX(byteOrder, io, zero);
639 SAFE_WRITE_EX(byteOrder, io, zero);
640 SAFE_WRITE_EX(byteOrder, io, zero);
641 SAFE_WRITE_EX(byteOrder, io, zero);
642 // initial fill rule record
643 recordType = 8;
644 SAFE_WRITE_EX(byteOrder, io, recordType);
645 quint16 fillType = mask.path.initialFillRecord? 1: 0;
646 SAFE_WRITE_EX(byteOrder, io, fillType);
647 // 22 empty bits
648 const quint16 halfZero = 0;
649 SAFE_WRITE_EX(byteOrder, io, halfZero);
650 SAFE_WRITE_EX(byteOrder, io, zero);
651 SAFE_WRITE_EX(byteOrder, io, zero);
652 SAFE_WRITE_EX(byteOrder, io, zero);
653 SAFE_WRITE_EX(byteOrder, io, zero);
654 SAFE_WRITE_EX(byteOrder, io, zero);
655 // write the subpaths.
656 Q_FOREACH(psd_path_sub_path subPath, mask.path.subPaths) {
657 recordType = subPath.isClosed? 0: 3;
658 quint16 length = subPath.nodes.size();
659 dbgFile << "writing subpath" << subPath.nodes.size();
660 SAFE_WRITE_EX(byteOrder, io, recordType);
661 SAFE_WRITE_EX(byteOrder, io, length);
662 // 22 empty bits
663 SAFE_WRITE_EX(byteOrder, io, halfZero);
664 SAFE_WRITE_EX(byteOrder, io, zero);
665 SAFE_WRITE_EX(byteOrder, io, zero);
666 SAFE_WRITE_EX(byteOrder, io, zero);
667 SAFE_WRITE_EX(byteOrder, io, zero);
668 SAFE_WRITE_EX(byteOrder, io, zero);
669
670 Q_FOREACH(psd_path_node node, subPath.nodes) {
671 if (subPath.isClosed) {
672 recordType = node.isSmooth? 1: 2;
673 } else {
674 recordType = node.isSmooth? 4: 5;
675 }
676 SAFE_WRITE_EX(byteOrder, io, recordType);
677 psdwriteFixedPoint<byteOrder>(io, node.control1.y());
678 psdwriteFixedPoint<byteOrder>(io, node.control1.x());
679 psdwriteFixedPoint<byteOrder>(io, node.node.y());
680 psdwriteFixedPoint<byteOrder>(io, node.node.x());
681 psdwriteFixedPoint<byteOrder>(io, node.control2.y());
682 psdwriteFixedPoint<byteOrder>(io, node.control2.x());
683 }
684 }
685}
686
687template<psd_byte_order byteOrder>
689{
690 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
691 KisAslWriterUtils::writeFixedString<byteOrder>("TySh", io);
692
694
695 try {
696 KisAslWriter writer(byteOrder);
697
698 writer.writeTypeToolObjectSettings(io, tool.textDataASLXML(), tool.textWarpXML(), tool.transform, tool.boundingBox);
699
701 warnKrita << "WARNING: Couldn't save text layer block:" << PREPEND_METHOD(e.what());
702
703 // TODO: make this error recoverable!
704 throw e;
705 }
706}
707
708template<psd_byte_order byteOrder>
709void PsdAdditionalLayerInfoBlock::writeVectorStrokeDataImpl(QIODevice &io, const QDomDocument &vectorStroke)
710{
711 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
712 KisAslWriterUtils::writeFixedString<byteOrder>("vstk", io);
714 try {
715 KisAslWriter writer(byteOrder);
716
718
720 warnKrita << "WARNING: Couldn't save vector stroke layer block:" << PREPEND_METHOD(e.what());
721
722 // TODO: make this error recoverable!
723 throw e;
724 }
725}
726
727void PsdAdditionalLayerInfoBlock::writeTxt2BlockEx(QIODevice &io, const QVariantHash txt2Hash)
728{
729 switch (m_header.byteOrder) {
731 writeTxt2BlockExImpl<psd_byte_order::psdLittleEndian>(io, txt2Hash);
732 break;
733 default:
734 writeTxt2BlockExImpl(io, txt2Hash);
735 break;
736 }
737}
738
739template<psd_byte_order byteOrder>
740void PsdAdditionalLayerInfoBlock::writeTxt2BlockExImpl(QIODevice &io, const QVariantHash txt2Hash)
741{
742 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
743 KisAslWriterUtils::writeFixedString<byteOrder>("Txt2", io);
744
745 QByteArray ba = KisCosWriter::writeTxt2FromVariantHash(txt2Hash);
746 quint32 length = ba.size();
747 SAFE_WRITE_EX(byteOrder, io, length);
748 io.write(ba);
749}
750
751template<psd_byte_order byteOrder>
752void PsdAdditionalLayerInfoBlock::writeVectorOriginationDataImpl(QIODevice &io, const QDomDocument &vectorOrigination)
753{
754 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
755 KisAslWriterUtils::writeFixedString<byteOrder>("vogk", io);
757 try {
758 KisAslWriter writer(byteOrder);
759
760 writer.writeVectorOriginationDataEx(io, vectorOrigination);
761
763 warnKrita << "WARNING: Couldn't save vector stroke layer block:" << PREPEND_METHOD(e.what());
764
765 // TODO: make this error recoverable!
766 throw e;
767 }
768}
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
const Params2D p
static QDomDocument readLfx2PsdSection(QIODevice &device, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readPsdSectionPattern(QIODevice &device, qint64 bytesLeft, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readTypeToolObjectSettings(QIODevice &device, QTransform &transform, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readVectorOriginationData(QIODevice &device, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readVectorStroke(QIODevice &device, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readFillLayer(QIODevice &device, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
void writeFillLayerSectionEx(QIODevice &device, const QDomDocument &doc)
void writePsdLfx2SectionEx(QIODevice &device, const QDomDocument &doc)
void writeVectorStrokeDataEx(QIODevice &device, const QDomDocument &doc)
void writeVectorOriginationDataEx(QIODevice &device, const QDomDocument &doc)
void writeTypeToolObjectSettings(QIODevice &device, const QDomDocument &doc, const QDomDocument &warpDoc, const QTransform tf, const QRectF bounds)
The KisCosParser class.
static QByteArray writeTxt2FromVariantHash(const QVariantHash doc)
static QVariantHash uncompressKeys(QVariantHash doc)
uncompressKeys One of the problems with the Txt2 block is that older versions of photoshop (cs2-cs3 e...
quint16 version
Definition psd_header.h:43
bool tiffStyleLayerBlock
Definition psd_header.h:50
psd_byte_order byteOrder
Definition psd_header.h:49
void writeLfx2BlockEx(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
void writeVectorOriginationDataImpl(QIODevice &io, const QDomDocument &vectorOrigination)
void writeFillLayerBlockEx(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type)
ExtraLayerInfoBlockHandler m_layerInfoBlockHandler
void writeVmskBlockEx(QIODevice &io, psd_vector_mask mask)
void writeVectorOriginationDataEx(QIODevice &io, const QDomDocument &vectorOrigination)
void writeVectorStrokeDataImpl(QIODevice &io, const QDomDocument &vectorStroke)
void writeVectorMaskImpl(QIODevice &io, psd_vector_mask mask)
void writeLsctBlockEx(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey)
void writeVectorStrokeDataEx(QIODevice &io, const QDomDocument &vectorStroke)
void writeLuniBlockEx(QIODevice &io, const QString &layerName)
void writeFillLayerBlockExImpl(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type)
void writeLfx2BlockExImpl(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
PsdAdditionalLayerInfoBlock(const PSDHeader &header)
void writeLclrBlockExImpl(QIODevice &io, const quint16 &lclr)
bool write(QIODevice &io, KisNodeSP node)
void writeLsctBlockExImpl(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey)
void writeLclrBlockEx(QIODevice &io, const quint16 &labelColor)
void writeTxt2BlockEx(QIODevice &io, const QVariantHash txt2Hash)
std::function< bool(QIODevice &)> ExtraLayerInfoBlockHandler
void writePattBlockExImpl(QIODevice &io, const QDomDocument &patternsXmlDoc)
void writePattBlockEx(QIODevice &io, const QDomDocument &patternsXmlDoc)
void writeTxt2BlockExImpl(QIODevice &io, const QVariantHash txt2Hash)
void setExtraLayerInfoBlockHandler(ExtraLayerInfoBlockHandler handler)
void setUserMaskInfoBlockHandler(UserMaskInfoBlockHandler handler)
void writeTypeToolImpl(QIODevice &io, psd_layer_type_shape tool)
void writeLuniBlockExImpl(QIODevice &io, const QString &layerName)
void writeTypeToolBlockEx(QIODevice &io, psd_layer_type_shape typeTool)
std::function< bool(QIODevice &)> UserMaskInfoBlockHandler
#define GARBAGE_VALUE_MARK
#define SAFE_READ_EX(byteOrder, device, varname)
#define SAFE_READ_SIGNATURE_EX(byteOrder, device, varname, expected)
#define SAFE_WRITE_EX(byteOrder, device, varname)
#define bounds(x, a, b)
#define PREPEND_METHOD(msg)
Definition kis_debug.h:172
#define warnKrita
Definition kis_debug.h:87
#define ppVar(var)
Definition kis_debug.h:155
#define dbgFile
Definition kis_debug.h:53
#define SETUP_OFFSET_VERIFIER(name, device, expectedOffset, maxPadding)
psd_section_type
Definition psd.h:144
@ psd_other
Definition psd.h:144
psd_fill_type
Definition psd.h:123
@ psd_fill_solid_color
Definition psd.h:124
@ psd_fill_gradient
Definition psd.h:125
@ psd_fill_pattern
Definition psd.h:126
QList< psd_path_sub_path > subPaths