File indexing completed on 2024-05-26 04:33:33
0001 /* This file is part of the KDE project 0002 * Copyright 2008 (C) Boudewijn Rempt <boud@valdyas.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 #include "kis_kra_saver.h" 0007 0008 #include "kis_kra_tags.h" 0009 #include "kis_kra_save_visitor.h" 0010 #include "kis_kra_savexml_visitor.h" 0011 0012 #include <QApplication> 0013 #include <QMessageBox> 0014 #include <QDomDocument> 0015 #include <QDomElement> 0016 #include <QString> 0017 #include <QStringList> 0018 #include <QScopedPointer> 0019 0020 #include <QUrl> 0021 #include <QBuffer> 0022 0023 #include <KoDocumentInfo.h> 0024 #include <KoColorSpaceRegistry.h> 0025 #include <KoColorSpace.h> 0026 #include <KoColorProfile.h> 0027 #include <KoColor.h> 0028 #include <KoColorSet.h> 0029 #include <KoStore.h> 0030 #include <KoStoreDevice.h> 0031 #include <KisResourceTypes.h> 0032 #include <KisResourceModel.h> 0033 #include <kis_annotation.h> 0034 #include <kis_image.h> 0035 #include <kis_image_animation_interface.h> 0036 #include <KisImportExportManager.h> 0037 #include <kis_group_layer.h> 0038 #include <kis_layer.h> 0039 #include <kis_adjustment_layer.h> 0040 #include <kis_layer_composition.h> 0041 #include <kis_painting_assistants_decoration.h> 0042 #include "kis_png_converter.h" 0043 #include "kis_keyframe_channel.h" 0044 #include <kis_time_span.h> 0045 #include "KisDocument.h" 0046 #include <string> 0047 #include "kis_dom_utils.h" 0048 #include "kis_grid_config.h" 0049 #include "kis_guides_config.h" 0050 #include "KisProofingConfiguration.h" 0051 #include "kis_asl_layer_style_serializer.h" 0052 0053 #include <KisMirrorAxisConfig.h> 0054 0055 #include <QFileInfo> 0056 #include <QDir> 0057 0058 0059 using namespace KRA; 0060 0061 struct KisKraSaver::Private 0062 { 0063 KisDocument* doc {nullptr}; 0064 QMap<const KisNode*, QString> nodeFileNames; 0065 QMap<const KisNode*, QString> keyframeFilenames; 0066 QString imageName; 0067 QString filename; 0068 QStringList errorMessages; 0069 QStringList warningMessages; 0070 QStringList specialAnnotations; 0071 bool addMergedImage {false}; 0072 QList<KoResourceLoadResult> linkedDocumentResources; 0073 0074 Private() { 0075 specialAnnotations << "exif" << "icc"; 0076 } 0077 0078 }; 0079 0080 KisKraSaver::KisKraSaver(KisDocument* document, const QString &filename, bool addMergedImage) 0081 : m_d(new Private) 0082 { 0083 m_d->doc = document; 0084 m_d->filename = filename; 0085 m_d->addMergedImage = addMergedImage; 0086 m_d->linkedDocumentResources = document->linkedDocumentResources(); 0087 0088 m_d->imageName = m_d->doc->documentInfo()->aboutInfo("title"); 0089 if (m_d->imageName.isEmpty()) { 0090 m_d->imageName = i18n("Unnamed"); 0091 } 0092 } 0093 0094 KisKraSaver::~KisKraSaver() 0095 { 0096 delete m_d; 0097 } 0098 0099 QDomElement KisKraSaver::saveXML(QDomDocument& doc, KisImageSP image) 0100 { 0101 QDomElement imageElement = doc.createElement("IMAGE"); 0102 0103 Q_ASSERT(image); 0104 imageElement.setAttribute(NAME, m_d->imageName); 0105 imageElement.setAttribute(MIME, NATIVE_MIMETYPE); 0106 imageElement.setAttribute(WIDTH, KisDomUtils::toString(image->width())); 0107 imageElement.setAttribute(HEIGHT, KisDomUtils::toString(image->height())); 0108 imageElement.setAttribute(COLORSPACE_NAME, image->colorSpace()->id()); 0109 imageElement.setAttribute(DESCRIPTION, m_d->doc->documentInfo()->aboutInfo("comment")); 0110 // XXX: Save profile as blob inside the image, instead of the product name. 0111 if (image->profile() && image->profile()-> valid()) { 0112 imageElement.setAttribute(PROFILE, image->profile()->name()); 0113 } 0114 imageElement.setAttribute(X_RESOLUTION, KisDomUtils::toString(image->xRes()*72.0)); 0115 imageElement.setAttribute(Y_RESOLUTION, KisDomUtils::toString(image->yRes()*72.0)); 0116 //now the proofing options: 0117 if (image->proofingConfiguration()) { 0118 if (image->proofingConfiguration()->storeSoftproofingInsideImage) { 0119 imageElement.setAttribute(PROOFINGPROFILENAME, KisDomUtils::toString(image->proofingConfiguration()->proofingProfile)); 0120 imageElement.setAttribute(PROOFINGMODEL, KisDomUtils::toString(image->proofingConfiguration()->proofingModel)); 0121 imageElement.setAttribute(PROOFINGDEPTH, KisDomUtils::toString(image->proofingConfiguration()->proofingDepth)); 0122 imageElement.setAttribute(PROOFINGINTENT, KisDomUtils::toString(image->proofingConfiguration()->intent)); 0123 imageElement.setAttribute(PROOFINGADAPTATIONSTATE, KisDomUtils::toString(image->proofingConfiguration()->adaptationState)); 0124 } 0125 } 0126 0127 quint32 count = 1; // We don't save the root layer, but it does count 0128 KisSaveXmlVisitor visitor(doc, imageElement, count, m_d->filename, true); 0129 visitor.setSelectedNodes({m_d->doc->preActivatedNode()}); 0130 0131 image->rootLayer()->accept(visitor); 0132 m_d->errorMessages.append(visitor.errorMessages()); 0133 0134 m_d->nodeFileNames = visitor.nodeFileNames(); 0135 m_d->keyframeFilenames = visitor.keyframeFileNames(); 0136 0137 saveBackgroundColor(doc, imageElement, image); 0138 saveAssistantsGlobalColor(doc, imageElement); 0139 saveWarningColor(doc, imageElement, image); 0140 saveCompositions(doc, imageElement, image); 0141 saveAssistantsList(doc, imageElement); 0142 saveGrid(doc, imageElement); 0143 saveGuides(doc, imageElement); 0144 saveMirrorAxis(doc, imageElement); 0145 saveResourcesToXML(doc, imageElement); 0146 0147 // Redundancy -- Save animation metadata in XML to prevent data loss for the time being... 0148 QDomElement animationElement = doc.createElement("animation"); 0149 KisDomUtils::saveValue(&animationElement, "framerate", image->animationInterface()->framerate()); 0150 KisDomUtils::saveValue(&animationElement, "range", image->animationInterface()->documentPlaybackRange()); 0151 KisDomUtils::saveValue(&animationElement, "currentTime", image->animationInterface()->currentUITime()); 0152 imageElement.appendChild(animationElement); 0153 0154 vKisAnnotationSP_it beginIt = image->beginAnnotations(); 0155 vKisAnnotationSP_it endIt = image->endAnnotations(); 0156 0157 if (beginIt != endIt) { 0158 QDomElement annotationsElement = doc.createElement(ANNOTATIONS); 0159 vKisAnnotationSP_it it = beginIt; 0160 while (it != endIt) { 0161 if (!(*it) || (*it)->type().isEmpty()) { 0162 it++; 0163 continue; 0164 } 0165 QString type = (*it)->type(); 0166 0167 if (!m_d->specialAnnotations.contains(type)) { 0168 0169 QString description = (*it)->description(); 0170 QDomElement annotationElement = doc.createElement(ANNOTATION); 0171 annotationsElement.appendChild(annotationElement); 0172 annotationElement.setAttribute("type", type); 0173 annotationElement.setAttribute("description", description); 0174 } 0175 it++; 0176 } 0177 imageElement.appendChild(annotationsElement); 0178 } 0179 0180 0181 return imageElement; 0182 } 0183 0184 bool KisKraSaver::saveResources(KoStore *store, KisImageSP image, const QString &uri) 0185 { 0186 Q_UNUSED(image); 0187 Q_UNUSED(uri); 0188 0189 QList<KoResourceLoadResult> embeddedResources = m_d->linkedDocumentResources; 0190 0191 Q_FOREACH (const KoResourceLoadResult &result, embeddedResources) { 0192 KIS_SAFE_ASSERT_RECOVER(result.type() != KoResourceLoadResult::ExistingResource) { continue; } 0193 0194 if (result.type() == KoResourceLoadResult::FailedLink) { 0195 m_d->warningMessages << i18nc("Error message when saving a .kra file", "Could not export resource for embedding: %1", result.signature().filename); 0196 continue; 0197 } 0198 0199 KoEmbeddedResource resource = result.embeddedResource(); 0200 0201 QString path = RESOURCE_PATH + "/" + resource.signature().type; 0202 0203 if (resource.signature().type == ResourceType::Palettes) { 0204 path = m_d->imageName + PALETTE_PATH; 0205 } 0206 0207 const QString fileName = resource.signature().filename; 0208 0209 if (!store->open(path + '/' + fileName)) { 0210 m_d->warningMessages << i18nc("Error message when saving a .kra file", "Could not write resource: %1", result.signature().filename); 0211 continue; 0212 } 0213 0214 // we first read into a buffer to make sure the save operation is transactional, 0215 // that is, either resource is saves correctly, or the file is left empty. 0216 QByteArray ba = resource.data(); 0217 0218 qint64 nwritten = 0; 0219 if (!ba.isEmpty()) { 0220 nwritten = store->write(ba); 0221 } else { 0222 m_d->warningMessages << i18nc("Error message when saving a .kra file", "Written resource is empty: %1", result.signature().filename); 0223 } 0224 0225 store->close(); 0226 0227 if (nwritten != ba.size()) { 0228 m_d->warningMessages << i18nc("Error message when saving a .kra file", "Written resource is incomplete: %1", result.signature().filename); 0229 } 0230 } 0231 0232 return true; 0233 } 0234 0235 bool KisKraSaver::saveStoryboard(KoStore *store, KisImageSP image, const QString &uri) 0236 { 0237 Q_UNUSED(image); 0238 Q_UNUSED(uri); 0239 0240 bool success = true; 0241 if (m_d->doc->getStoryboardItemList().count() == 0) { 0242 return true; 0243 } else { 0244 if (!store->open(m_d->imageName + STORYBOARD_PATH + "index.xml")) { 0245 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save storyboards."); 0246 return false; 0247 } 0248 0249 QDomDocument storyboardDocument = m_d->doc->createDomDocument("storyboard-info", "1.1"); 0250 QDomElement root = storyboardDocument.documentElement(); 0251 saveStoryboardToXML(storyboardDocument, root); 0252 0253 QByteArray ba = storyboardDocument.toByteArray(); 0254 qint64 nwritten = 0; 0255 if (!ba.isEmpty()) { 0256 nwritten = store->write(ba); 0257 } else { 0258 success = false; 0259 qWarning() << "Could not save storyboard data to a byte array!"; 0260 } 0261 0262 bool r = store->close(); 0263 success = success && r && (nwritten == ba.size()); 0264 } 0265 0266 if (!success) { 0267 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save storyboards."); 0268 return false; 0269 } 0270 0271 return success; 0272 } 0273 0274 bool KisKraSaver::saveAnimationMetadata(KoStore *store, KisImageSP image, const QString &uri) 0275 { 0276 Q_UNUSED(uri); 0277 0278 if (!store->open(m_d->imageName + ANIMATION_METADATA_PATH + "index.xml")) { 0279 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save animation meta data."); 0280 return false; 0281 } 0282 0283 QDomDocument animationDocument = m_d->doc->createDomDocument("animation-metadata", "1.1"); 0284 QDomElement root = animationDocument.documentElement(); 0285 saveAnimationMetadataToXML(animationDocument, root, image); 0286 0287 bool success = true; 0288 0289 QByteArray ba = animationDocument.toByteArray(); 0290 qint64 nwritten = 0; 0291 if (!ba.isEmpty()) { 0292 nwritten = store->write(ba); 0293 } else { 0294 qWarning() << "Could not save animation meta data to a byte array!"; 0295 success = false; 0296 } 0297 0298 bool r = store->close(); 0299 0300 success = success && r && (nwritten == ba.size()); 0301 0302 if (!success) { 0303 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save animation meta data."); 0304 return false; 0305 } 0306 0307 return true; 0308 } 0309 0310 bool KisKraSaver::saveAudio(KoStore *store) 0311 { 0312 if (m_d->doc->getAudioTracks().isEmpty()) 0313 return true; 0314 0315 if (!store->open(m_d->imageName + AUDIO_PATH + "index.xml")) { 0316 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save audio meta data."); 0317 return false; 0318 } 0319 0320 QDomDocument audioDocument = m_d->doc->createDomDocument("audio-info", "1.1"); 0321 QDomElement root = audioDocument.documentElement(); 0322 saveAudioXML(audioDocument, root); 0323 0324 bool success = true; 0325 QByteArray byteArray = audioDocument.toByteArray(); 0326 qint64 bytesWriteCount = 0; 0327 if (!byteArray.isEmpty()) { 0328 bytesWriteCount = store->write(byteArray); 0329 } else { 0330 qWarning() << "Could not save audio data to a byte array!"; 0331 success = false; 0332 } 0333 0334 bool closeOK = store->close(); 0335 0336 success = success && closeOK && (bytesWriteCount == byteArray.size()); 0337 0338 if (!success) { 0339 m_d->errorMessages << i18nc("Error message when saving a .kra file", "Could not save audio meta data."); 0340 return false; 0341 } 0342 0343 return true; 0344 } 0345 0346 void KisKraSaver::saveResourcesToXML(QDomDocument &doc, QDomElement &element) 0347 { 0348 QDomElement ePalette = doc.createElement(PALETTES); 0349 QDomElement eResources = doc.createElement(RESOURCES); 0350 0351 Q_FOREACH (const KoResourceLoadResult resource, m_d->linkedDocumentResources) { 0352 // all warnings will be issued in KisKraSaver::saveResources() 0353 if (resource.type() != KoResourceLoadResult::EmbeddedResource) continue; 0354 0355 KoResourceSignature sig = resource.signature(); 0356 0357 QDomElement eResource = doc.createElement("resource"); 0358 eResource.setAttribute("type", sig.type); 0359 eResource.setAttribute("name", sig.name); 0360 eResource.setAttribute("filename", sig.filename); 0361 eResource.setAttribute("md5sum", sig.md5sum); 0362 0363 if (sig.type == ResourceType::Palettes) { 0364 ePalette.appendChild(eResource); 0365 } 0366 else { 0367 eResources.appendChild(eResource); 0368 0369 } 0370 } 0371 element.appendChild(ePalette); 0372 element.appendChild(eResources); 0373 } 0374 0375 void KisKraSaver::saveStoryboardToXML(QDomDocument& doc, QDomElement &element) 0376 { 0377 //saving storyboard comments 0378 QDomElement eCommentList = doc.createElement("StoryboardCommentList"); 0379 for (StoryboardComment comment: m_d->doc->getStoryboardCommentsList()) { 0380 QDomElement commentElement = doc.createElement("storyboardcomment"); 0381 commentElement.setAttribute("name", comment.name); 0382 commentElement.setAttribute("visibility", comment.visibility); 0383 eCommentList.appendChild(commentElement); 0384 } 0385 element.appendChild(eCommentList); 0386 0387 //saving storyboard items 0388 QDomElement eItemList = doc.createElement("StoryboardItemList"); 0389 for (StoryboardItemSP item : m_d->doc->getStoryboardItemList()) { 0390 QDomElement eItem = item->toXML(doc); 0391 eItemList.appendChild(eItem); 0392 } 0393 element.appendChild(eItemList); 0394 } 0395 0396 void KisKraSaver::saveAnimationMetadataToXML(QDomDocument &doc, QDomElement &element, KisImageSP image) 0397 { 0398 KisDomUtils::saveValue(&element, "framerate", image->animationInterface()->framerate()); 0399 KisDomUtils::saveValue(&element, "range", image->animationInterface()->documentPlaybackRange()); 0400 KisDomUtils::saveValue(&element, "currentTime", image->animationInterface()->currentUITime()); 0401 0402 { 0403 QDomElement exportItemElem = doc.createElement("export-settings"); 0404 KisDomUtils::saveValue(&exportItemElem, "sequenceFilePath", image->animationInterface()->exportSequenceFilePath()); 0405 KisDomUtils::saveValue(&exportItemElem, "sequenceBaseName", image->animationInterface()->exportSequenceBaseName()); 0406 KisDomUtils::saveValue(&exportItemElem, "sequenceInitialFrameNumber", image->animationInterface()->exportInitialFrameNumber()); 0407 element.appendChild(exportItemElem); 0408 } 0409 } 0410 0411 bool KisKraSaver::saveKeyframes(KoStore *store, const QString &uri, bool external) 0412 { 0413 QMap<const KisNode*, QString>::iterator it; 0414 0415 for (it = m_d->keyframeFilenames.begin(); it != m_d->keyframeFilenames.end(); it++) { 0416 const KisNode *node = it.key(); 0417 QString filename = it.value(); 0418 0419 QString location = 0420 (external ? QString() : uri) 0421 + m_d->imageName + LAYER_PATH + filename; 0422 0423 if (!saveNodeKeyframes(store, location, node)) { 0424 return false; 0425 } 0426 } 0427 0428 return true; 0429 } 0430 0431 bool KisKraSaver::saveNodeKeyframes(KoStore *store, QString location, const KisNode *node) 0432 { 0433 QDomDocument doc = KisDocument::createDomDocument("krita-keyframes", "keyframes", "1.0"); 0434 QDomElement root = doc.documentElement(); 0435 0436 KisKeyframeChannel *channel; 0437 Q_FOREACH (channel, node->keyframeChannels()) { 0438 QDomElement element = channel->toXML(doc, m_d->nodeFileNames[node]); 0439 root.appendChild(element); 0440 } 0441 0442 bool success = true; 0443 if (store->open(location)) { 0444 QByteArray xml = doc.toByteArray(); 0445 qint64 nwritten = store->write(xml); 0446 bool r = store->close(); 0447 success = r && (nwritten == xml.size()); 0448 } else { 0449 success = false; 0450 } 0451 if (!success) { 0452 m_d->errorMessages << i18nc("Error message on saving a .kra file", "Could not save keyframes."); 0453 return false; 0454 } 0455 0456 return true; 0457 } 0458 0459 bool KisKraSaver::saveBinaryData(KoStore* store, KisImageSP image, const QString &uri, bool external, bool addMergedImage) 0460 { 0461 QString location; 0462 0463 // Save the layers data 0464 KisKraSaveVisitor visitor(store, m_d->imageName, m_d->nodeFileNames); 0465 0466 if (external) 0467 visitor.setExternalUri(uri); 0468 0469 image->rootLayer()->accept(visitor); 0470 0471 m_d->errorMessages.append(visitor.errorMessages()); 0472 if (!m_d->errorMessages.isEmpty()) { 0473 return false; 0474 } 0475 0476 bool success = true; 0477 bool r = true; 0478 qint64 nwritten = 0; 0479 0480 // saving annotations 0481 bool savingAnnotationsSuccess = true; 0482 KisAnnotationSP annotation = image->annotation("exif"); 0483 if (annotation) { 0484 location = external ? QString() : uri; 0485 location += m_d->imageName + EXIF_PATH; 0486 if (store->open(location)) { 0487 nwritten = store->write(annotation->annotation()); 0488 r = store->close(); 0489 savingAnnotationsSuccess = savingAnnotationsSuccess && (nwritten == annotation->annotation().size()) && r; 0490 } else { 0491 savingAnnotationsSuccess = false; 0492 } 0493 } 0494 0495 if (!savingAnnotationsSuccess) { 0496 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save annotations.")); 0497 } 0498 0499 success = success && savingAnnotationsSuccess; 0500 0501 bool savingImageProfileSuccess = true; 0502 if (image->profile()) { 0503 const KoColorProfile *profile = image->profile(); 0504 KisAnnotationSP annotation; 0505 if (profile) { 0506 QByteArray profileRawData = profile->rawData(); 0507 if (!profileRawData.isEmpty()) { 0508 if (profile->type() == "icc") { 0509 annotation = new KisAnnotation(ICC, profile->name(), profile->rawData()); 0510 } else { 0511 annotation = new KisAnnotation(PROFILE, profile->name(), profile->rawData()); 0512 } 0513 } 0514 } 0515 0516 if (annotation) { 0517 location = external ? QString() : uri; 0518 location += m_d->imageName + ICC_PATH; 0519 if (store->open(location)) { 0520 nwritten = store->write(annotation->annotation()); 0521 r = store->close(); 0522 savingImageProfileSuccess = savingImageProfileSuccess && (nwritten == annotation->annotation().size()) && r; 0523 } else { 0524 savingImageProfileSuccess = false; 0525 } 0526 } 0527 } 0528 0529 if (!savingImageProfileSuccess) { 0530 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save image profile.")); 0531 } 0532 success = success && savingImageProfileSuccess; 0533 0534 //This'll embed the profile used for proofing into the kra file. 0535 bool savingSoftproofingProfileSuccess = true; 0536 if (image->proofingConfiguration()) { 0537 if (image->proofingConfiguration()->storeSoftproofingInsideImage) { 0538 const KoColorProfile *proofingProfile = KoColorSpaceRegistry::instance()->profileByName(image->proofingConfiguration()->proofingProfile); 0539 if (proofingProfile && proofingProfile->valid()) { 0540 QByteArray proofingProfileRaw = proofingProfile->rawData(); 0541 if (!proofingProfileRaw.isEmpty()) { 0542 annotation = new KisAnnotation(ICCPROOFINGPROFILE, proofingProfile->name(), proofingProfile->rawData()); 0543 } 0544 } 0545 if (annotation) { 0546 location = external ? QString() : uri; 0547 location += m_d->imageName + ICC_PROOFING_PATH; 0548 if (store->open(location)) { 0549 nwritten = store->write(annotation->annotation()); 0550 r = store->close(); 0551 savingSoftproofingProfileSuccess = savingSoftproofingProfileSuccess && (nwritten == annotation->annotation().size()) && r; 0552 } else { 0553 savingSoftproofingProfileSuccess = false; 0554 } 0555 } 0556 } 0557 } 0558 0559 if (!savingSoftproofingProfileSuccess) { 0560 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save softproofing color profile.")); 0561 } 0562 0563 success = success && savingSoftproofingProfileSuccess; 0564 0565 // Save the remaining annotations 0566 vKisAnnotationSP_it beginIt = image->beginAnnotations(); 0567 vKisAnnotationSP_it endIt = image->endAnnotations(); 0568 0569 bool savingRemainingAnnotationsSuccess = true; 0570 if (beginIt != endIt) { 0571 vKisAnnotationSP_it it = beginIt; 0572 while (it != endIt) { 0573 if (!(*it) || (*it)->type().isEmpty()) { 0574 it++; 0575 continue; 0576 } 0577 QString type = (*it)->type(); 0578 0579 if (!m_d->specialAnnotations.contains(type)) { 0580 location = external ? QString() : uri; 0581 location += m_d->imageName + ANNOTATIONS_PATH + type; 0582 if (store->open(location)) { 0583 nwritten = store->write((*it)->annotation()); 0584 r = store->close(); 0585 savingRemainingAnnotationsSuccess = savingRemainingAnnotationsSuccess && (nwritten == (*it)->annotation().size()) && r; 0586 } else { 0587 savingRemainingAnnotationsSuccess = false; 0588 } 0589 } 0590 it++; 0591 } 0592 } 0593 0594 if (!savingRemainingAnnotationsSuccess) { 0595 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save additional annotations.")); 0596 } 0597 0598 success = success && savingRemainingAnnotationsSuccess; 0599 0600 bool savingLayerStylesSuccess = true; 0601 { 0602 KisAslLayerStyleSerializer serializer; 0603 QVector<KisPSDLayerStyleSP> stylesClones = serializer.collectAllLayerStyles(image->root()); 0604 if (stylesClones.size() > 0) { 0605 location = external ? QString() : uri; 0606 location += m_d->imageName + LAYER_STYLES_PATH; 0607 0608 if (store->open(location)) { 0609 QBuffer aslBuffer; 0610 if (aslBuffer.open(QIODevice::WriteOnly)) { 0611 serializer.setStyles(stylesClones); 0612 serializer.saveToDevice(aslBuffer); 0613 aslBuffer.close(); 0614 nwritten = store->write(aslBuffer.buffer()); 0615 savingLayerStylesSuccess = savingLayerStylesSuccess && (nwritten == aslBuffer.buffer().size()); 0616 } else { 0617 savingLayerStylesSuccess = false; 0618 } 0619 r = store->close(); 0620 savingLayerStylesSuccess = savingLayerStylesSuccess && r; 0621 } else { 0622 savingLayerStylesSuccess = false; 0623 } 0624 } 0625 } 0626 0627 if (!savingLayerStylesSuccess) { 0628 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save layer styles.")); 0629 } 0630 0631 success = success && savingLayerStylesSuccess; 0632 0633 bool savingMergedImageSuccess = true; 0634 if (addMergedImage) { 0635 KisPaintDeviceSP dev = image->projection(); 0636 store->setCompressionEnabled(false); 0637 r = KisPNGConverter::saveDeviceToStore("mergedimage.png", image->bounds(), image->xRes(), image->yRes(), dev, store); 0638 savingMergedImageSuccess = savingMergedImageSuccess && r; 0639 store->setCompressionEnabled(KisConfig(true).compressKra()); 0640 } 0641 0642 if (!savingMergedImageSuccess) { 0643 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save merged image.")); 0644 } 0645 0646 success = success && savingMergedImageSuccess; 0647 0648 r = saveAssistants(store, uri,external); 0649 success = success && r; 0650 0651 return success; 0652 } 0653 0654 0655 0656 QStringList KisKraSaver::errorMessages() const 0657 { 0658 return m_d->errorMessages; 0659 } 0660 0661 QStringList KisKraSaver::warningMessages() const 0662 { 0663 return m_d->warningMessages; 0664 } 0665 0666 void KisKraSaver::saveBackgroundColor(QDomDocument& doc, QDomElement& element, KisImageSP image) 0667 { 0668 QDomElement e = doc.createElement(CANVASPROJECTIONCOLOR); 0669 KoColor color = image->defaultProjectionColor(); 0670 QByteArray colorData = QByteArray::fromRawData((const char*)color.data(), color.colorSpace()->pixelSize()); 0671 e.setAttribute(COLORBYTEDATA, QString(colorData.toBase64())); 0672 element.appendChild(e); 0673 } 0674 0675 void KisKraSaver::saveAssistantsGlobalColor(QDomDocument& doc, QDomElement& element) 0676 { 0677 QDomElement e = doc.createElement(GLOBALASSISTANTSCOLOR); 0678 QString colorString = KisDomUtils::qColorToQString(m_d->doc->assistantsGlobalColor()); 0679 e.setAttribute(SIMPLECOLORDATA, QString(colorString)); 0680 element.appendChild(e); 0681 } 0682 0683 void KisKraSaver::saveWarningColor(QDomDocument& doc, QDomElement& element, KisImageSP image) 0684 { 0685 if (image->proofingConfiguration()) { 0686 if (image->proofingConfiguration()->storeSoftproofingInsideImage) { 0687 QDomElement e = doc.createElement(PROOFINGWARNINGCOLOR); 0688 KoColor color = image->proofingConfiguration()->warningColor; 0689 color.toXML(doc, e); 0690 element.appendChild(e); 0691 } 0692 } 0693 } 0694 0695 void KisKraSaver::saveCompositions(QDomDocument& doc, QDomElement& element, KisImageSP image) 0696 { 0697 if (!image->compositions().isEmpty()) { 0698 QDomElement e = doc.createElement("compositions"); 0699 Q_FOREACH (KisLayerCompositionSP composition, image->compositions()) { 0700 composition->save(doc, e); 0701 } 0702 element.appendChild(e); 0703 } 0704 } 0705 0706 bool KisKraSaver::saveAssistants(KoStore* store, QString uri, bool external) 0707 { 0708 QString location; 0709 QMap<QString, int> assistantcounters; 0710 QByteArray data; 0711 0712 QList<KisPaintingAssistantSP> assistants = m_d->doc->assistants(); 0713 QMap<KisPaintingAssistantHandleSP, int> handlemap; 0714 0715 bool success = true; 0716 if (!assistants.isEmpty()) { 0717 0718 Q_FOREACH (KisPaintingAssistantSP assist, assistants){ 0719 if (!assistantcounters.contains(assist->id())){ 0720 assistantcounters.insert(assist->id(),0); 0721 } 0722 location = external ? QString() : uri; 0723 location += m_d->imageName + ASSISTANTS_PATH; 0724 location += QString(assist->id()+"%1.assistant").arg(assistantcounters[assist->id()]); 0725 0726 data = assist->saveXml(handlemap); 0727 if (store->open(location)) { 0728 qint64 nwritten = store->write(data); 0729 bool r = store->close(); 0730 success = success && r && (nwritten == data.size()); 0731 } else { 0732 success = false; 0733 } 0734 assistantcounters[assist->id()]++; 0735 } 0736 } 0737 if (!success) { 0738 m_d->errorMessages.append(i18nc("Saving .kra file error message", "Could not save assistants.")); 0739 } 0740 return true; 0741 } 0742 0743 bool KisKraSaver::saveAssistantsList(QDomDocument& doc, QDomElement& element) 0744 { 0745 int count_ellipse = 0, 0746 count_twopoint = 0, 0747 count_perspective = 0, 0748 count_ruler = 0, 0749 count_vanishingpoint = 0, 0750 count_infiniteruler = 0, 0751 count_parallelruler = 0, 0752 count_concentricellipse = 0, 0753 count_fisheyepoint = 0, 0754 count_spline = 0, 0755 count_perspectiveellipse = 0, 0756 count_curvilinearperspective = 0; 0757 QList<KisPaintingAssistantSP> assistants = m_d->doc->assistants(); 0758 if (!assistants.isEmpty()) { 0759 QDomElement assistantsElement = doc.createElement("assistants"); 0760 Q_FOREACH (KisPaintingAssistantSP assist, assistants){ 0761 if (assist->id() == "ellipse"){ 0762 assist->saveXmlList(doc, assistantsElement, count_ellipse); 0763 count_ellipse++; 0764 } 0765 else if (assist->id() == "spline"){ 0766 assist->saveXmlList(doc, assistantsElement, count_spline); 0767 count_spline++; 0768 } 0769 else if (assist->id() == "perspective"){ 0770 assist->saveXmlList(doc, assistantsElement, count_perspective); 0771 count_perspective++; 0772 } 0773 else if (assist->id() == "vanishing point"){ 0774 assist->saveXmlList(doc, assistantsElement, count_vanishingpoint); 0775 count_vanishingpoint++; 0776 } 0777 else if (assist->id() == "infinite ruler"){ 0778 assist->saveXmlList(doc, assistantsElement, count_infiniteruler); 0779 count_infiniteruler++; 0780 } 0781 else if (assist->id() == "parallel ruler"){ 0782 assist->saveXmlList(doc, assistantsElement, count_parallelruler); 0783 count_parallelruler++; 0784 } 0785 else if (assist->id() == "concentric ellipse"){ 0786 assist->saveXmlList(doc, assistantsElement, count_concentricellipse); 0787 count_concentricellipse++; 0788 } 0789 else if (assist->id() == "fisheye-point"){ 0790 assist->saveXmlList(doc, assistantsElement, count_fisheyepoint); 0791 count_fisheyepoint++; 0792 } 0793 else if (assist->id() == "two point"){ 0794 assist->saveXmlList(doc, assistantsElement, count_twopoint); 0795 count_twopoint++; 0796 } 0797 else if (assist->id() == "ruler"){ 0798 assist->saveXmlList(doc, assistantsElement, count_ruler); 0799 count_ruler++; 0800 } 0801 else if (assist->id() == "perspective ellipse"){ 0802 assist->saveXmlList(doc, assistantsElement, count_perspectiveellipse); 0803 count_perspectiveellipse++; 0804 } 0805 else if (assist->id() == "curvilinear-perspective"){ 0806 assist->saveXmlList(doc, assistantsElement, count_curvilinearperspective); 0807 count_curvilinearperspective++; 0808 } 0809 } 0810 element.appendChild(assistantsElement); 0811 } 0812 return true; 0813 } 0814 0815 bool KisKraSaver::saveGrid(QDomDocument& doc, QDomElement& element) 0816 { 0817 KisGridConfig config = m_d->doc->gridConfig(); 0818 0819 if (!config.isDefault()) { 0820 QDomElement gridElement = config.saveDynamicDataToXml(doc, "grid"); 0821 element.appendChild(gridElement); 0822 } 0823 0824 return true; 0825 } 0826 0827 bool KisKraSaver::saveGuides(QDomDocument& doc, QDomElement& element) 0828 { 0829 KisGuidesConfig guides = m_d->doc->guidesConfig(); 0830 0831 if (!guides.isDefault()) { 0832 QDomElement guidesElement = guides.saveToXml(doc, "guides"); 0833 element.appendChild(guidesElement); 0834 } 0835 0836 return true; 0837 } 0838 0839 bool KisKraSaver::saveMirrorAxis(QDomDocument &doc, QDomElement &element) 0840 { 0841 KisMirrorAxisConfig mirrorAxisConfig = m_d->doc->mirrorAxisConfig(); 0842 0843 if (!mirrorAxisConfig.isDefault()) { 0844 QDomElement mirrorAxisElement = mirrorAxisConfig.saveToXml(doc, MIRROR_AXIS); 0845 element.appendChild(mirrorAxisElement); 0846 } 0847 0848 return true; 0849 } 0850 0851 bool KisKraSaver::saveAudioXML(QDomDocument& doc, QDomElement& element) 0852 { 0853 QVector<QFileInfo> clips = m_d->doc->getAudioTracks(); 0854 const qreal volume = m_d->doc->getAudioLevel(); 0855 0856 if (!clips.isEmpty()) { 0857 QDomElement audioClips = doc.createElement("audioClips"); 0858 Q_FOREACH(const QFileInfo& file, clips) { 0859 QDomElement clip = doc.createElement(QString("Clip")); 0860 clip.setAttribute("filePath", file.absoluteFilePath()); 0861 clip.setAttribute("volume", volume); 0862 audioClips.appendChild(clip); 0863 } 0864 element.appendChild(audioClips); 0865 } 0866 0867 return true; 0868 } 0869