Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_kra_saver.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * Copyright 2008 (C) Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6#include "kis_kra_saver.h"
7
8#include "kis_kra_tags.h"
11
12#include <QApplication>
13#include <QMessageBox>
14#include <QDomDocument>
15#include <QDomElement>
16#include <QString>
17#include <QStringList>
18
19#include <QUrl>
20#include <QBuffer>
21
22#include <KoDocumentInfo.h>
24#include <KoColorSpace.h>
25#include <KoColorProfile.h>
26#include <KoColor.h>
27#include <KoColorSet.h>
28#include <KoStore.h>
29#include <KoStoreDevice.h>
30#include <KisResourceTypes.h>
31#include <KisResourceModel.h>
32#include <kis_annotation.h>
33#include <kis_image.h>
36#include <kis_group_layer.h>
37#include <kis_layer.h>
41#include "kis_png_converter.h"
43#include <kis_time_span.h>
44#include "KisDocument.h"
45#include <string>
46#include "kis_dom_utils.h"
47#include "kis_grid_config.h"
48#include "kis_guides_config.h"
51
52#include <KisMirrorAxisConfig.h>
53
54#include <QFileInfo>
55#include <QDir>
56
57
58using namespace KRA;
59
78
79KisKraSaver::KisKraSaver(KisDocument* document, const QString &filename, bool addMergedImage)
80 : m_d(new Private)
81{
82 m_d->doc = document;
83 m_d->filename = filename;
84 m_d->addMergedImage = addMergedImage;
85 m_d->linkedDocumentResources = document->linkedDocumentResources();
86
87 m_d->imageName = m_d->doc->documentInfo()->aboutInfo("title");
88 if (m_d->imageName.isEmpty()) {
89 m_d->imageName = i18n("Unnamed");
90 }
91}
92
94{
95 delete m_d;
96}
97
98QDomElement KisKraSaver::saveXML(QDomDocument& doc, KisImageSP image)
99{
100 QDomElement imageElement = doc.createElement("IMAGE");
101
102 Q_ASSERT(image);
103 imageElement.setAttribute(NAME, m_d->imageName);
104 imageElement.setAttribute(MIME, NATIVE_MIMETYPE);
105 imageElement.setAttribute(WIDTH, KisDomUtils::toString(image->width()));
106 imageElement.setAttribute(HEIGHT, KisDomUtils::toString(image->height()));
107 imageElement.setAttribute(COLORSPACE_NAME, image->colorSpace()->id());
108 imageElement.setAttribute(DESCRIPTION, m_d->doc->documentInfo()->aboutInfo("comment"));
109 // XXX: Save profile as blob inside the image, instead of the product name.
110 if (image->profile() && image->profile()-> valid()) {
111 imageElement.setAttribute(PROFILE, image->profile()->name());
112 }
113 imageElement.setAttribute(X_RESOLUTION, KisDomUtils::toString(image->xRes()*72.0));
114 imageElement.setAttribute(Y_RESOLUTION, KisDomUtils::toString(image->yRes()*72.0));
115
116 //now the proofing options:
117 if (image->proofingConfiguration()) {
118 imageElement.setAttribute(PROOFINGPROFILENAME,
119 KisDomUtils::toString(image->proofingConfiguration()->proofingProfile));
120 imageElement.setAttribute(PROOFINGMODEL, KisDomUtils::toString(image->proofingConfiguration()->proofingModel));
121 imageElement.setAttribute(PROOFINGDEPTH, KisDomUtils::toString(image->proofingConfiguration()->proofingDepth));
122 imageElement.setAttribute(PROOFINGINTENT,
123 KisDomUtils::toString(image->proofingConfiguration()->conversionIntent));
124 imageElement.setAttribute(PROOFINGDISPLAYINTENT,
125 KisDomUtils::toString(image->proofingConfiguration()->displayIntent));
126 bool bcp = image->proofingConfiguration()->useBlackPointCompensationFirstTransform;
127 imageElement.setAttribute(PROOFINGBLACKPOINTCOMPENSATION, bcp ? "true" : "false");
128 bcp = image->proofingConfiguration()->displayFlags.testFlag(
130 imageElement.setAttribute(PROOFINGDISPLAYBLACKPOINTCOMPENSATION, bcp ? "true" : "false");
131 const QString mode = [&]() {
132 switch (image->proofingConfiguration()->displayMode) {
134 return "monitor";
136 return "paper";
138 return "custom";
139 }
140 Q_UNREACHABLE_RETURN("custom");
141 }();
142 imageElement.setAttribute(PROOFINGDISPLAYMODE, mode);
143 imageElement.setAttribute(PROOFINGADAPTATIONSTATE,
144 KisDomUtils::toString(image->proofingConfiguration()->legacyAdaptationState()));
145 }
146
147 quint32 count = 1; // We don't save the root layer, but it does count
148 KisSaveXmlVisitor visitor(doc, imageElement, count, m_d->filename, true);
150
151 image->rootLayer()->accept(visitor);
152 m_d->errorMessages.append(visitor.errorMessages());
153
154 m_d->nodeFileNames = visitor.nodeFileNames();
156
157 saveBackgroundColor(doc, imageElement, image);
158 saveAssistantsGlobalColor(doc, imageElement);
159 saveWarningColor(doc, imageElement, image);
160 saveCompositions(doc, imageElement, image);
161 saveAssistantsList(doc, imageElement);
162 saveGrid(doc, imageElement);
163 saveGuides(doc, imageElement);
164 saveMirrorAxis(doc, imageElement);
165 saveColorHistory(doc, imageElement);
166 saveResourcesToXML(doc, imageElement);
167
168 // Redundancy -- Save animation metadata in XML to prevent data loss for the time being...
169 QDomElement animationElement = doc.createElement("animation");
170 KisDomUtils::saveValue(&animationElement, "framerate", image->animationInterface()->framerate());
171 KisDomUtils::saveValue(&animationElement, "range", image->animationInterface()->documentPlaybackRange());
172 KisDomUtils::saveValue(&animationElement, "currentTime", image->animationInterface()->currentUITime());
173 imageElement.appendChild(animationElement);
174
175 vKisAnnotationSP_it beginIt = image->beginAnnotations();
176 vKisAnnotationSP_it endIt = image->endAnnotations();
177
178 if (beginIt != endIt) {
179 QDomElement annotationsElement = doc.createElement(ANNOTATIONS);
180 vKisAnnotationSP_it it = beginIt;
181 while (it != endIt) {
182 if (!(*it) || (*it)->type().isEmpty()) {
183 it++;
184 continue;
185 }
186 QString type = (*it)->type();
187
188 if (!m_d->specialAnnotations.contains(type)) {
189
190 QString description = (*it)->description();
191 QDomElement annotationElement = doc.createElement(ANNOTATION);
192 annotationsElement.appendChild(annotationElement);
193 annotationElement.setAttribute("type", type);
194 annotationElement.setAttribute("description", description);
195 }
196 it++;
197 }
198 imageElement.appendChild(annotationsElement);
199 }
200
201
202 return imageElement;
203}
204
205bool KisKraSaver::saveResources(KoStore *store, KisImageSP image, const QString &uri)
206{
207 Q_UNUSED(image);
208 Q_UNUSED(uri);
209
211
212 Q_FOREACH (const KoResourceLoadResult &result, embeddedResources) {
214
215 if (result.type() == KoResourceLoadResult::FailedLink) {
216 m_d->warningMessages << i18nc("Error message when saving a .kra file", "Could not export resource for embedding: %1", result.signature().filename);
217 continue;
218 }
219
220 KoEmbeddedResource resource = result.embeddedResource();
221
222 QString path = RESOURCE_PATH + "/" + resource.signature().type;
223
224 if (resource.signature().type == ResourceType::Palettes) {
225 path = m_d->imageName + PALETTE_PATH;
226 }
227
228 const QString fileName = resource.signature().filename;
229
230 if (!store->open(path + '/' + fileName)) {
231 m_d->warningMessages << i18nc("Error message when saving a .kra file", "Could not write resource: %1", result.signature().filename);
232 continue;
233 }
234
235 // we first read into a buffer to make sure the save operation is transactional,
236 // that is, either resource is saves correctly, or the file is left empty.
237 QByteArray ba = resource.data();
238
239 qint64 nwritten = 0;
240 if (!ba.isEmpty()) {
241 nwritten = store->write(ba);
242 } else {
243 m_d->warningMessages << i18nc("Error message when saving a .kra file", "Written resource is empty: %1", result.signature().filename);
244 }
245
246 store->close();
247
248 if (nwritten != ba.size()) {
249 m_d->warningMessages << i18nc("Error message when saving a .kra file", "Written resource is incomplete: %1", result.signature().filename);
250 }
251 }
252
253 return true;
254}
255
256bool KisKraSaver::saveStoryboard(KoStore *store, KisImageSP image, const QString &uri)
257{
258 Q_UNUSED(image);
259 Q_UNUSED(uri);
260
261 bool success = true;
262 if (m_d->doc->getStoryboardItemList().count() == 0) {
263 return true;
264 } else {
265 if (!store->open(m_d->imageName + STORYBOARD_PATH + "index.xml")) {
266 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save storyboards.");
267 return false;
268 }
269
270 QDomDocument storyboardDocument = m_d->doc->createDomDocument("storyboard-info", "1.1");
271 QDomElement root = storyboardDocument.documentElement();
272 saveStoryboardToXML(storyboardDocument, root);
273
274 QByteArray ba = storyboardDocument.toByteArray();
275 qint64 nwritten = 0;
276 if (!ba.isEmpty()) {
277 nwritten = store->write(ba);
278 } else {
279 success = false;
280 qWarning() << "Could not save storyboard data to a byte array!";
281 }
282
283 bool r = store->close();
284 success = success && r && (nwritten == ba.size());
285 }
286
287 if (!success) {
288 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save storyboards.");
289 return false;
290 }
291
292 return success;
293}
294
295bool KisKraSaver::saveAnimationMetadata(KoStore *store, KisImageSP image, const QString &uri)
296{
297 Q_UNUSED(uri);
298
299 if (!store->open(m_d->imageName + ANIMATION_METADATA_PATH + "index.xml")) {
300 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save animation meta data.");
301 return false;
302 }
303
304 QDomDocument animationDocument = m_d->doc->createDomDocument("animation-metadata", "1.1");
305 QDomElement root = animationDocument.documentElement();
306 saveAnimationMetadataToXML(animationDocument, root, image);
307
308 bool success = true;
309
310 QByteArray ba = animationDocument.toByteArray();
311 qint64 nwritten = 0;
312 if (!ba.isEmpty()) {
313 nwritten = store->write(ba);
314 } else {
315 qWarning() << "Could not save animation meta data to a byte array!";
316 success = false;
317 }
318
319 bool r = store->close();
320
321 success = success && r && (nwritten == ba.size());
322
323 if (!success) {
324 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save animation meta data.");
325 return false;
326 }
327
328 return true;
329}
330
332{
333 if (m_d->doc->getAudioTracks().isEmpty())
334 return true;
335
336 if (!store->open(m_d->imageName + AUDIO_PATH + "index.xml")) {
337 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save audio meta data.");
338 return false;
339 }
340
341 QDomDocument audioDocument = m_d->doc->createDomDocument("audio-info", "1.1");
342 QDomElement root = audioDocument.documentElement();
343 saveAudioXML(audioDocument, root);
344
345 bool success = true;
346 QByteArray byteArray = audioDocument.toByteArray();
347 qint64 bytesWriteCount = 0;
348 if (!byteArray.isEmpty()) {
349 bytesWriteCount = store->write(byteArray);
350 } else {
351 qWarning() << "Could not save audio data to a byte array!";
352 success = false;
353 }
354
355 bool closeOK = store->close();
356
357 success = success && closeOK && (bytesWriteCount == byteArray.size());
358
359 if (!success) {
360 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save audio meta data.");
361 return false;
362 }
363
364 return true;
365}
366
367void KisKraSaver::saveResourcesToXML(QDomDocument &doc, QDomElement &element)
368{
369 QDomElement ePalette = doc.createElement(PALETTES);
370 QDomElement eResources = doc.createElement(RESOURCES);
371
372 Q_FOREACH (const KoResourceLoadResult resource, m_d->linkedDocumentResources) {
373 // all warnings will be issued in KisKraSaver::saveResources()
374 if (resource.type() != KoResourceLoadResult::EmbeddedResource) continue;
375
376 KoResourceSignature sig = resource.signature();
377
378 QDomElement eResource = doc.createElement("resource");
379 eResource.setAttribute("type", sig.type);
380 eResource.setAttribute("name", sig.name);
381 eResource.setAttribute("filename", sig.filename);
382 eResource.setAttribute("md5sum", sig.md5sum);
383
384 if (sig.type == ResourceType::Palettes) {
385 ePalette.appendChild(eResource);
386 }
387 else {
388 eResources.appendChild(eResource);
389
390 }
391 }
392 element.appendChild(ePalette);
393 element.appendChild(eResources);
394}
395
396void KisKraSaver::saveStoryboardToXML(QDomDocument& doc, QDomElement &element)
397{
398 //saving storyboard comments
399 QDomElement eCommentList = doc.createElement("StoryboardCommentList");
401 QDomElement commentElement = doc.createElement("storyboardcomment");
402 commentElement.setAttribute("name", comment.name);
403 commentElement.setAttribute("visibility", comment.visibility);
404 eCommentList.appendChild(commentElement);
405 }
406 element.appendChild(eCommentList);
407
408 //saving storyboard items
409 QDomElement eItemList = doc.createElement("StoryboardItemList");
411 QDomElement eItem = item->toXML(doc);
412 eItemList.appendChild(eItem);
413 }
414 element.appendChild(eItemList);
415}
416
417void KisKraSaver::saveAnimationMetadataToXML(QDomDocument &doc, QDomElement &element, KisImageSP image)
418{
419 KisDomUtils::saveValue(&element, "framerate", image->animationInterface()->framerate());
421 KisDomUtils::saveValue(&element, "currentTime", image->animationInterface()->currentUITime());
422
423 {
424 QDomElement exportItemElem = doc.createElement("export-settings");
425 KisDomUtils::saveValue(&exportItemElem, "sequenceFilePath", image->animationInterface()->exportSequenceFilePath());
426 KisDomUtils::saveValue(&exportItemElem, "sequenceBaseName", image->animationInterface()->exportSequenceBaseName());
427 KisDomUtils::saveValue(&exportItemElem, "sequenceInitialFrameNumber", image->animationInterface()->exportInitialFrameNumber());
428 element.appendChild(exportItemElem);
429 }
430}
431
432bool KisKraSaver::saveKeyframes(KoStore *store, const QString &uri, bool external)
433{
434 QMap<const KisNode*, QString>::iterator it;
435
436 for (it = m_d->keyframeFilenames.begin(); it != m_d->keyframeFilenames.end(); it++) {
437 const KisNode *node = it.key();
438 QString filename = it.value();
439
440 QString location =
441 (external ? QString() : uri)
442 + m_d->imageName + LAYER_PATH + filename;
443
444 if (!saveNodeKeyframes(store, location, node)) {
445 return false;
446 }
447 }
448
449 return true;
450}
451
452bool KisKraSaver::saveNodeKeyframes(KoStore *store, QString location, const KisNode *node)
453{
454 QDomDocument doc = KisDocument::createDomDocument("krita-keyframes", "keyframes", "1.0");
455 QDomElement root = doc.documentElement();
456
457 KisKeyframeChannel *channel;
458 Q_FOREACH (channel, node->keyframeChannels()) {
459 QDomElement element = channel->toXML(doc, m_d->nodeFileNames[node]);
460 root.appendChild(element);
461 }
462
463 bool success = true;
464 if (store->open(location)) {
465 QByteArray xml = doc.toByteArray();
466 qint64 nwritten = store->write(xml);
467 bool r = store->close();
468 success = r && (nwritten == xml.size());
469 } else {
470 success = false;
471 }
472 if (!success) {
473 m_d->errorMessages << i18nc("Error message on saving a .kra file", "Could not save keyframes.");
474 return false;
475 }
476
477 return true;
478}
479
480bool KisKraSaver::saveBinaryData(KoStore* store, KisImageSP image, const QString &uri, bool external, bool addMergedImage)
481{
482 QString location;
483
484 // Save the layers data
486
487 if (external)
488 visitor.setExternalUri(uri);
489
490 image->rootLayer()->accept(visitor);
491
492 m_d->errorMessages.append(visitor.errorMessages());
493 if (!m_d->errorMessages.isEmpty()) {
494 return false;
495 }
496
497 bool success = true;
498 bool r = true;
499 qint64 nwritten = 0;
500
501 // saving annotations
502 bool savingAnnotationsSuccess = true;
503 KisAnnotationSP annotation = image->annotation("exif");
504 if (annotation) {
505 location = external ? QString() : uri;
506 location += m_d->imageName + EXIF_PATH;
507 if (store->open(location)) {
508 nwritten = store->write(annotation->annotation());
509 r = store->close();
510 savingAnnotationsSuccess = savingAnnotationsSuccess && (nwritten == annotation->annotation().size()) && r;
511 } else {
512 savingAnnotationsSuccess = false;
513 }
514 }
515
516 if (!savingAnnotationsSuccess) {
517 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save annotations."));
518 }
519
520 success = success && savingAnnotationsSuccess;
521
522 bool savingImageProfileSuccess = true;
523 if (image->profile()) {
524 const KoColorProfile *profile = image->profile();
525 KisAnnotationSP annotation;
526 if (profile) {
527 QByteArray profileRawData = profile->rawData();
528 if (!profileRawData.isEmpty()) {
529 if (profile->type() == "icc") {
530 annotation = new KisAnnotation(ICC, profile->name(), profile->rawData());
531 } else {
532 annotation = new KisAnnotation(PROFILE, profile->name(), profile->rawData());
533 }
534 }
535 }
536
537 if (annotation) {
538 location = external ? QString() : uri;
539 location += m_d->imageName + ICC_PATH;
540 if (store->open(location)) {
541 nwritten = store->write(annotation->annotation());
542 r = store->close();
543 savingImageProfileSuccess = savingImageProfileSuccess && (nwritten == annotation->annotation().size()) && r;
544 } else {
545 savingImageProfileSuccess = false;
546 }
547 }
548 }
549
550 if (!savingImageProfileSuccess) {
551 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save image profile."));
552 }
553 success = success && savingImageProfileSuccess;
554
555 //This'll embed the profile used for proofing into the kra file.
556 bool savingSoftproofingProfileSuccess = true;
557 if (image->proofingConfiguration()) {
558 const KoColorProfile *proofingProfile =
560 if (proofingProfile && proofingProfile->valid()) {
561 QByteArray proofingProfileRaw = proofingProfile->rawData();
562 if (!proofingProfileRaw.isEmpty()) {
563 annotation = new KisAnnotation(ICCPROOFINGPROFILE, proofingProfile->name(), proofingProfile->rawData());
564 }
565 }
566 if (annotation) {
567 location = external ? QString() : uri;
568 location += m_d->imageName + ICC_PROOFING_PATH;
569 if (store->open(location)) {
570 nwritten = store->write(annotation->annotation());
571 r = store->close();
572 savingSoftproofingProfileSuccess =
573 savingSoftproofingProfileSuccess && (nwritten == annotation->annotation().size()) && r;
574 } else {
575 savingSoftproofingProfileSuccess = false;
576 }
577 }
578 }
579
580 if (!savingSoftproofingProfileSuccess) {
581 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save softproofing color profile."));
582 }
583
584 success = success && savingSoftproofingProfileSuccess;
585
586 // Save the remaining annotations
587 vKisAnnotationSP_it beginIt = image->beginAnnotations();
588 vKisAnnotationSP_it endIt = image->endAnnotations();
589
590 bool savingRemainingAnnotationsSuccess = true;
591 if (beginIt != endIt) {
592 vKisAnnotationSP_it it = beginIt;
593 while (it != endIt) {
594 if (!(*it) || (*it)->type().isEmpty()) {
595 it++;
596 continue;
597 }
598 QString type = (*it)->type();
599
600 if (!m_d->specialAnnotations.contains(type)) {
601 location = external ? QString() : uri;
602 location += m_d->imageName + ANNOTATIONS_PATH + type;
603 if (store->open(location)) {
604 nwritten = store->write((*it)->annotation());
605 r = store->close();
606 savingRemainingAnnotationsSuccess = savingRemainingAnnotationsSuccess && (nwritten == (*it)->annotation().size()) && r;
607 } else {
608 savingRemainingAnnotationsSuccess = false;
609 }
610 }
611 it++;
612 }
613 }
614
615 if (!savingRemainingAnnotationsSuccess) {
616 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save additional annotations."));
617 }
618
619 success = success && savingRemainingAnnotationsSuccess;
620
621 bool savingLayerStylesSuccess = true;
622 {
624 QVector<KisPSDLayerStyleSP> stylesClones = serializer.collectAllLayerStyles(image->root());
625 if (stylesClones.size() > 0) {
626 location = external ? QString() : uri;
627 location += m_d->imageName + LAYER_STYLES_PATH;
628
629 if (store->open(location)) {
630 QBuffer aslBuffer;
631 if (aslBuffer.open(QIODevice::WriteOnly)) {
632 serializer.setStyles(stylesClones);
633 serializer.saveToDevice(aslBuffer);
634 aslBuffer.close();
635 nwritten = store->write(aslBuffer.buffer());
636 savingLayerStylesSuccess = savingLayerStylesSuccess && (nwritten == aslBuffer.buffer().size());
637 } else {
638 savingLayerStylesSuccess = false;
639 }
640 r = store->close();
641 savingLayerStylesSuccess = savingLayerStylesSuccess && r;
642 } else {
643 savingLayerStylesSuccess = false;
644 }
645 }
646 }
647
648 if (!savingLayerStylesSuccess) {
649 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save layer styles."));
650 }
651
652 success = success && savingLayerStylesSuccess;
653
654 bool savingMergedImageSuccess = true;
655 if (addMergedImage) {
656 KisPaintDeviceSP dev = image->projection();
657 store->setCompressionEnabled(false);
658 r = KisPNGConverter::saveDeviceToStore("mergedimage.png", image->bounds(), image->xRes(), image->yRes(), dev, store);
659 savingMergedImageSuccess = savingMergedImageSuccess && r;
660 store->setCompressionEnabled(KisConfig(true).compressKra());
661 }
662
663 if (!savingMergedImageSuccess) {
664 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save merged image."));
665 }
666
667 success = success && savingMergedImageSuccess;
668
669 r = saveAssistants(store, uri,external);
670 success = success && r;
671
672 return success;
673}
674
675
676
681
686
687void KisKraSaver::saveBackgroundColor(QDomDocument& doc, QDomElement& element, KisImageSP image)
688{
689 QDomElement e = doc.createElement(CANVASPROJECTIONCOLOR);
690 KoColor color = image->defaultProjectionColor();
691 QByteArray colorData = QByteArray::fromRawData((const char*)color.data(), color.colorSpace()->pixelSize());
692 e.setAttribute(COLORBYTEDATA, QString(colorData.toBase64()));
693 element.appendChild(e);
694}
695
696void KisKraSaver::saveColorHistory(QDomDocument &doc, QDomElement &element)
697{
698 QDomElement colorsElement = doc.createElement(COLORHISTORY);
699 saveKoColors(doc, colorsElement, m_d->doc->colorHistory());
700
701 element.appendChild(colorsElement);
702}
703
704void KisKraSaver::saveAssistantsGlobalColor(QDomDocument& doc, QDomElement& element)
705{
706 QDomElement e = doc.createElement(GLOBALASSISTANTSCOLOR);
708 e.setAttribute(SIMPLECOLORDATA, QString(colorString));
709 element.appendChild(e);
710}
711
712void KisKraSaver::saveWarningColor(QDomDocument& doc, QDomElement& element, KisImageSP image)
713{
714 if (image->proofingConfiguration()) {
715 QDomElement e = doc.createElement(PROOFINGWARNINGCOLOR);
716 KoColor color = image->proofingConfiguration()->warningColor;
717 color.toXML(doc, e);
718 element.appendChild(e);
719 }
720}
721
722void KisKraSaver::saveCompositions(QDomDocument& doc, QDomElement& element, KisImageSP image)
723{
724 if (!image->compositions().isEmpty()) {
725 QDomElement e = doc.createElement("compositions");
726 Q_FOREACH (KisLayerCompositionSP composition, image->compositions()) {
727 composition->save(doc, e);
728 }
729 element.appendChild(e);
730 }
731}
732
733bool KisKraSaver::saveAssistants(KoStore* store, QString uri, bool external)
734{
735 QString location;
736 QMap<QString, int> assistantcounters;
737 QByteArray data;
738
740 QMap<KisPaintingAssistantHandleSP, int> handlemap;
741
742 bool success = true;
743 if (!assistants.isEmpty()) {
744
745 Q_FOREACH (KisPaintingAssistantSP assist, assistants){
746 if (!assistantcounters.contains(assist->id())){
747 assistantcounters.insert(assist->id(),0);
748 }
749 location = external ? QString() : uri;
750 location += m_d->imageName + ASSISTANTS_PATH;
751 location += QString(assist->id()+"%1.assistant").arg(assistantcounters[assist->id()]);
752
753 data = assist->saveXml(handlemap);
754 if (store->open(location)) {
755 qint64 nwritten = store->write(data);
756 bool r = store->close();
757 success = success && r && (nwritten == data.size());
758 } else {
759 success = false;
760 }
761 assistantcounters[assist->id()]++;
762 }
763 }
764 if (!success) {
765 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save assistants."));
766 }
767 return true;
768}
769
770bool KisKraSaver::saveAssistantsList(QDomDocument& doc, QDomElement& element)
771{
772 int count_ellipse = 0,
773 count_twopoint = 0,
774 count_perspective = 0,
775 count_ruler = 0,
776 count_vanishingpoint = 0,
777 count_infiniteruler = 0,
778 count_parallelruler = 0,
779 count_concentricellipse = 0,
780 count_fisheyepoint = 0,
781 count_spline = 0,
782 count_perspectiveellipse = 0,
783 count_curvilinearperspective = 0;
785 if (!assistants.isEmpty()) {
786 QDomElement assistantsElement = doc.createElement("assistants");
787 Q_FOREACH (KisPaintingAssistantSP assist, assistants){
788 if (assist->id() == "ellipse"){
789 assist->saveXmlList(doc, assistantsElement, count_ellipse);
790 count_ellipse++;
791 }
792 else if (assist->id() == "spline"){
793 assist->saveXmlList(doc, assistantsElement, count_spline);
794 count_spline++;
795 }
796 else if (assist->id() == "perspective"){
797 assist->saveXmlList(doc, assistantsElement, count_perspective);
798 count_perspective++;
799 }
800 else if (assist->id() == "vanishing point"){
801 assist->saveXmlList(doc, assistantsElement, count_vanishingpoint);
802 count_vanishingpoint++;
803 }
804 else if (assist->id() == "infinite ruler"){
805 assist->saveXmlList(doc, assistantsElement, count_infiniteruler);
806 count_infiniteruler++;
807 }
808 else if (assist->id() == "parallel ruler"){
809 assist->saveXmlList(doc, assistantsElement, count_parallelruler);
810 count_parallelruler++;
811 }
812 else if (assist->id() == "concentric ellipse"){
813 assist->saveXmlList(doc, assistantsElement, count_concentricellipse);
814 count_concentricellipse++;
815 }
816 else if (assist->id() == "fisheye-point"){
817 assist->saveXmlList(doc, assistantsElement, count_fisheyepoint);
818 count_fisheyepoint++;
819 }
820 else if (assist->id() == "two point"){
821 assist->saveXmlList(doc, assistantsElement, count_twopoint);
822 count_twopoint++;
823 }
824 else if (assist->id() == "ruler"){
825 assist->saveXmlList(doc, assistantsElement, count_ruler);
826 count_ruler++;
827 }
828 else if (assist->id() == "perspective ellipse"){
829 assist->saveXmlList(doc, assistantsElement, count_perspectiveellipse);
830 count_perspectiveellipse++;
831 }
832 else if (assist->id() == "curvilinear-perspective"){
833 assist->saveXmlList(doc, assistantsElement, count_curvilinearperspective);
834 count_curvilinearperspective++;
835 }
836 }
837 element.appendChild(assistantsElement);
838 }
839 return true;
840}
841
842bool KisKraSaver::saveGrid(QDomDocument& doc, QDomElement& element)
843{
844 KisGridConfig config = m_d->doc->gridConfig();
845
846 if (!config.isDefault()) {
847 QDomElement gridElement = config.saveDynamicDataToXml(doc, "grid");
848 element.appendChild(gridElement);
849 }
850
851 return true;
852}
853
854bool KisKraSaver::saveGuides(QDomDocument& doc, QDomElement& element)
855{
856 KisGuidesConfig guides = m_d->doc->guidesConfig();
857
858 if (!guides.isDefault()) {
859 QDomElement guidesElement = guides.saveToXml(doc, "guides");
860 element.appendChild(guidesElement);
861 }
862
863 return true;
864}
865
866bool KisKraSaver::saveMirrorAxis(QDomDocument &doc, QDomElement &element)
867{
868 KisMirrorAxisConfig mirrorAxisConfig = m_d->doc->mirrorAxisConfig();
869
870 if (!mirrorAxisConfig.isDefault()) {
871 QDomElement mirrorAxisElement = mirrorAxisConfig.saveToXml(doc, MIRROR_AXIS);
872 element.appendChild(mirrorAxisElement);
873 }
874
875 return true;
876}
877
878bool KisKraSaver::saveAudioXML(QDomDocument& doc, QDomElement& element)
879{
881 const qreal volume = m_d->doc->getAudioLevel();
882
883 if (!clips.isEmpty()) {
884 QDomElement audioClips = doc.createElement("audioClips");
885 Q_FOREACH(const QFileInfo& file, clips) {
886 QDomElement clip = doc.createElement(QString("Clip"));
887 clip.setAttribute("filePath", file.absoluteFilePath());
888 clip.setAttribute("volume", volume);
889 audioClips.appendChild(clip);
890 }
891 element.appendChild(audioClips);
892 }
893
894 return true;
895}
896
897bool KisKraSaver::saveKoColors(QDomDocument &doc, QDomElement &colorsElement,
898 const QList<KoColor> &colors) const
899{
900 // Writes like <colors><RGB ../><RGB .. /> ... </colors>
901 Q_FOREACH(const KoColor & color, colors) {
902 color.toXML(doc, colorsElement);
903 }
904 return true;
905}
906
const QString DESCRIPTION
A data extension mechanism for Krita.
static QVector< KisPSDLayerStyleSP > collectAllLayerStyles(KisNodeSP root)
void setStyles(const QVector< KisPSDLayerStyleSP > &styles)
QList< KoColor > colorHistory
KisMirrorAxisConfig mirrorAxisConfig
KoDocumentInfo * documentInfo() const
QDomDocument createDomDocument(const QString &tagName, const QString &version) const
qreal getAudioLevel()
KisGridConfig gridConfig
QColor assistantsGlobalColor()
StoryboardItemList getStoryboardItemList()
returns the list of pointers to storyboard Items for the document
KisNodeWSP preActivatedNode
QList< KisPaintingAssistantSP > assistants
QVector< StoryboardComment > getStoryboardCommentsList()
returns the list of comments for the storyboard docker in the document
QVector< QFileInfo > getAudioTracks() const
KisGuidesConfig guidesConfig
QDomElement saveDynamicDataToXml(QDomDocument &doc, const QString &tag) const
bool isDefault() const
QDomElement saveToXml(QDomDocument &doc, const QString &tag) const
const KisTimeSpan & documentPlaybackRange() const
documentPlaybackRange
vKisAnnotationSP_it endAnnotations()
KisGroupLayerSP rootLayer() const
const KoColorSpace * colorSpace() const
KisImageAnimationInterface * animationInterface() const
KisAnnotationSP annotation(const QString &type)
KisPaintDeviceSP projection() const
qint32 width() const
double xRes() const
double yRes() const
qint32 height() const
QList< KisLayerCompositionSP > compositions()
QRect bounds() const override
KoColor defaultProjectionColor() const
const KoColorProfile * profile() const
vKisAnnotationSP_it beginAnnotations()
KisProofingConfigurationSP proofingConfiguration() const
proofingConfiguration
KisKeyframeChannel stores and manages KisKeyframes. Maps units of time to virtual keyframe values....
virtual QDomElement toXML(QDomDocument doc, const QString &layerFilename)
QStringList errorMessages() const
void setExternalUri(const QString &uri)
bool saveKeyframes(KoStore *store, const QString &uri, bool external)
void saveStoryboardToXML(QDomDocument &doc, QDomElement &element)
Private *const m_d
bool saveNodeKeyframes(KoStore *store, QString location, const KisNode *node)
bool saveMirrorAxis(QDomDocument &doc, QDomElement &element)
bool saveAssistants(KoStore *store, QString uri, bool external)
QStringList errorMessages() const
QDomElement saveXML(QDomDocument &doc, KisImageSP image)
bool saveAnimationMetadata(KoStore *store, KisImageSP image, const QString &uri)
void saveResourcesToXML(QDomDocument &doc, QDomElement &element)
bool saveAudioXML(QDomDocument &doc, QDomElement &element)
bool saveResources(KoStore *store, KisImageSP image, const QString &uri)
bool saveBinaryData(KoStore *store, KisImageSP image, const QString &uri, bool external, bool addMergedImage)
bool saveAudio(KoStore *store)
void saveWarningColor(QDomDocument &doc, QDomElement &element, KisImageSP image)
bool saveKoColors(QDomDocument &doc, QDomElement &element, const QList< KoColor > &colors) const
void saveColorHistory(QDomDocument &doc, QDomElement &element)
bool saveGrid(QDomDocument &doc, QDomElement &element)
bool saveGuides(QDomDocument &doc, QDomElement &element)
KisKraSaver(KisDocument *document, const QString &filename, bool addMergedImage=true)
void saveBackgroundColor(QDomDocument &doc, QDomElement &element, KisImageSP image)
bool saveStoryboard(KoStore *store, KisImageSP image, const QString &uri)
QStringList warningMessages() const
void saveCompositions(QDomDocument &doc, QDomElement &element, KisImageSP image)
bool saveAssistantsList(QDomDocument &doc, QDomElement &element)
void saveAssistantsGlobalColor(QDomDocument &doc, QDomElement &element)
void saveAnimationMetadataToXML(QDomDocument &doc, QDomElement &element, KisImageSP image)
The KisMirrorAxisConfig class stores configuration for the KisMirrorAxis canvas decoration....
QDomElement saveToXml(QDomDocument &doc, const QString &tag) const
saveToXml() function for KisKraSaver
bool isDefault() const
Check whether the config object was changed, or is the class default.
static bool saveDeviceToStore(const QString &filename, const QRect &imageRect, const qreal xRes, const qreal yRes, KisPaintDeviceSP dev, KoStore *store, KisMetaData::Store *metaData=0)
saveDeviceToStore saves the given paint device to the KoStore. If the device is not 8 bits sRGB,...
@ Custom
Let artists configure their own.
@ Paper
Whether to use Paper settings (absolute colorimetric, 0% adaptation.)
@ Monitor
Whether to use monitor rendering intent and flags for the second transform.
QMap< const KisNode *, QString > keyframeFileNames()
QMap< const KisNode *, QString > nodeFileNames()
QStringList errorMessages() const
void setSelectedNodes(vKisNodeSP selectedNodes)
virtual quint32 pixelSize() const =0
void toXML(QDomDocument &doc, QDomElement &colorElt) const
Definition KoColor.cpp:304
quint8 * data()
Definition KoColor.h:144
const KoColorSpace * colorSpace() const
return the current colorSpace
Definition KoColor.h:82
QString aboutInfo(const QString &info) const
const KoResourceSignature & signature() const
QByteArray data() const
KoEmbeddedResource embeddedResource() const noexcept
KoResourceSignature signature() const
A simple wrapper object for the main information about the resource.
qint64 write(const QByteArray &data)
Definition KoStore.cpp:198
bool close()
Definition KoStore.cpp:156
virtual void setCompressionEnabled(bool e)
Definition KoStore.cpp:403
bool open(const QString &name)
Definition KoStore.cpp:109
This file is part of the Krita application in calligra.
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
vKisAnnotationSP::iterator vKisAnnotationSP_it
Definition kis_types.h:181
const QString LAYER_PATH
const QString PALETTES
const QString PROOFINGPROFILENAME
const QString COLORHISTORY
const QString CANVASPROJECTIONCOLOR
const QString COLORBYTEDATA
const QString PROOFINGBLACKPOINTCOMPENSATION
const QString NAME
const QString ICC_PROOFING_PATH
const QString Y_RESOLUTION
const QString HEIGHT
const QString ASSISTANTS_PATH
const QString PROOFINGDEPTH
const QString ANIMATION_METADATA_PATH
const QString NATIVE_MIMETYPE
const QString LAYER_STYLES_PATH
const QString X_RESOLUTION
const QString STORYBOARD_PATH
const QString RESOURCES
const QString ICC
const QString ANNOTATIONS_PATH
const QString PROOFINGDISPLAYBLACKPOINTCOMPENSATION
const QString PROOFINGDISPLAYMODE
const QString ICC_PATH
const QString PROOFINGWARNINGCOLOR
const QString PROOFINGDISPLAYINTENT
const QString WIDTH
const QString EXIF_PATH
const QString MIME
const QString GLOBALASSISTANTSCOLOR
const QString ANNOTATIONS
const QString RESOURCE_PATH
const QString PROOFINGMODEL
const QString AUDIO_PATH
const QString PALETTE_PATH
const QString PROOFINGINTENT
const QString ICCPROOFINGPROFILE
const QString ANNOTATION
const QString MIRROR_AXIS
const QString SIMPLECOLORDATA
const QString PROOFINGADAPTATIONSTATE
const QString PROFILE
const QString COLORSPACE_NAME
void saveValue(QDomElement *parent, const QString &tag, const QSize &size)
QString qColorToQString(QColor color)
QString toString(const QString &value)
const QString Palettes
QMap< QString, KisKeyframeChannel * > keyframeChannels
bool accept(KisNodeVisitor &v) override
QMap< const KisNode *, QString > keyframeFilenames
QStringList warningMessages
QList< KoResourceLoadResult > linkedDocumentResources
QMap< const KisNode *, QString > nodeFileNames
QStringList specialAnnotations
virtual QByteArray rawData() const
virtual QString type() const
virtual bool valid() const =0
const KoColorProfile * profileByName(const QString &name) const
static KoColorSpaceRegistry * instance()