File indexing completed on 2024-04-28 04:20:13

0001 /*
0002    Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
0003    All rights reserved.
0004 
0005    Redistribution and use in source and binary forms, with or without
0006    modification, are permitted provided that the following conditions
0007    are met:
0008 
0009    1. Redistributions of source code must retain the above copyright
0010       notice, this list of conditions and the following disclaimer.
0011    2. Redistributions in binary form must reproduce the above copyright
0012       notice, this list of conditions and the following disclaimer in the
0013       documentation and/or other materials provided with the distribution.
0014 
0015    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0016    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0017    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0018    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0019    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0020    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0021    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0022    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0023    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0024    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0025 */
0026 
0027 
0028 #define DEBUG_KP_DOCUMENT 0
0029 
0030 
0031 #include "kpDocument.h"
0032 #include "kpDocumentPrivate.h"
0033 
0034 #include "imagelib/kpColor.h"
0035 #include "widgets/toolbars/kpColorToolBar.h"
0036 #include "kpDefs.h"
0037 #include "environments/document/kpDocumentEnvironment.h"
0038 #include "document/kpDocumentSaveOptions.h"
0039 #include "imagelib/kpDocumentMetaInfo.h"
0040 #include "imagelib/effects/kpEffectReduceColors.h"
0041 #include "pixmapfx/kpPixmapFX.h"
0042 #include "tools/kpTool.h"
0043 #include "lgpl/generic/kpUrlFormatter.h"
0044 #include "views/manager/kpViewManager.h"
0045 
0046 
0047 #include <QColor>
0048 #include <QImage>
0049 #include <QMimeDatabase>
0050 #include <QImageReader>
0051 #include <QBuffer>
0052 
0053 #include <KJobWidgets>
0054 #include "kpLogCategories.h"
0055 #include <KLocalizedString>
0056 #include <KIO/StoredTransferJob>
0057 #include <KMessageBox>
0058 
0059 //---------------------------------------------------------------------
0060 
0061 void kpDocument::getDataFromImage(const QImage &image,
0062                                   kpDocumentSaveOptions &saveOptions,
0063                                   kpDocumentMetaInfo &metaInfo)
0064 {
0065   saveOptions.setColorDepth(image.depth());
0066   saveOptions.setDither(false);  // avoid double dithering when saving
0067 
0068   metaInfo.setDotsPerMeterX(image.dotsPerMeterX());
0069   metaInfo.setDotsPerMeterY(image.dotsPerMeterY());
0070   metaInfo.setOffset(image.offset());
0071 
0072   QStringList keys = image.textKeys();
0073   for (int i = 0; i < keys.count(); i++) {
0074       metaInfo.setText(keys[i], image.text(keys[i]));
0075   }
0076 }
0077 
0078 //---------------------------------------------------------------------
0079 
0080 // public static
0081 QImage kpDocument::getPixmapFromFile(const QUrl &url, bool suppressDoesntExistDialog,
0082                                      QWidget *parent,
0083                                      kpDocumentSaveOptions *saveOptions,
0084                                      kpDocumentMetaInfo *metaInfo)
0085 {
0086 #if DEBUG_KP_DOCUMENT
0087     qCDebug(kpLogDocument) << "kpDocument::getPixmapFromFile(" << url << "," << parent << ")";
0088 #endif
0089 
0090     if (saveOptions) {
0091         *saveOptions = kpDocumentSaveOptions ();
0092     }
0093 
0094     if (metaInfo) {
0095         *metaInfo = kpDocumentMetaInfo ();
0096     }
0097 
0098     if (url.isEmpty ()) {
0099         return {};
0100     }
0101 
0102     KIO::StoredTransferJob *job = KIO::storedGet (url);
0103     KJobWidgets::setWindow(job, parent);
0104 
0105     if (!job->exec())
0106     {
0107         if (!suppressDoesntExistDialog)
0108         {
0109             // TODO: Use "Cannot" instead of "Could not" in all dialogs in KolourPaint.
0110             //       Or at least choose one consistently.
0111             //
0112             // TODO: Have captions for all dialogs in KolourPaint.
0113             KMessageBox::error (parent,
0114                                 i18n ("Could not open \"%1\".",
0115                                       kpUrlFormatter::PrettyFilename (url)));
0116         }
0117 
0118         return {};
0119     }
0120     QByteArray data = job->data();
0121 
0122     QMimeDatabase db;
0123     QMimeType mimeType = db.mimeTypeForFileNameAndData(url.fileName(), data);
0124 
0125     if (saveOptions) {
0126         saveOptions->setMimeType(mimeType.name());
0127     }
0128 
0129 #if DEBUG_KP_DOCUMENT
0130     qCDebug(kpLogDocument) << "\tmimetype=" << mimeType.name();
0131     qCDebug(kpLogDocument) << "\tsrc=" << url.path ();
0132 #endif
0133 
0134     QBuffer buffer(&data);
0135     buffer.open(QIODevice::ReadOnly);
0136     QImageReader reader(&buffer);
0137     reader.setAutoTransform(true);
0138     reader.setDecideFormatFromContent(true);
0139 
0140     // Do *NOT* convert to
0141     // QImage image = reader.read();
0142     // this variant is more lenient on errors and we may get something that we would not otherwise
0143     // e.g. image from https://bugs.kde.org/show_bug.cgi?id=441554
0144     QImage image;
0145     reader.read(&image);
0146 
0147     if (image.isNull ())
0148     {
0149         KMessageBox::error (parent,
0150                             i18n ("Could not open \"%1\" - unsupported image format.\n"
0151                                   "The file may be corrupt.",
0152                                   kpUrlFormatter::PrettyFilename (url)));
0153         return {};
0154     }
0155 
0156 #if DEBUG_KP_DOCUMENT
0157     qCDebug(kpLogDocument) << "\tpixmap: depth=" << image.depth ()
0158                 << " hasAlphaChannel=" << image.hasAlphaChannel ();
0159 #endif
0160 
0161     if ( saveOptions && metaInfo ) {
0162         getDataFromImage(image, *saveOptions, *metaInfo);
0163     }
0164 
0165     // make sure we always have Format_ARGB32_Premultiplied as this is the fastest to draw on
0166     // and Qt can not draw onto Format_Indexed8 (Qt-4.7)
0167     if ( image.format() != QImage::Format_ARGB32_Premultiplied ) {
0168         image.convertTo(QImage::Format_ARGB32_Premultiplied);
0169     }
0170 
0171     return image;
0172 }
0173 
0174 //---------------------------------------------------------------------
0175 
0176 void kpDocument::openNew (const QUrl &url)
0177 {
0178 #if DEBUG_KP_DOCUMENT
0179     qCDebug(kpLogDocument) << "kpDocument::openNew (" << url << ")";
0180 #endif
0181 
0182     m_image->fill(QColor(Qt::white).rgb());
0183 
0184     setURL (url, false/*not from url*/);
0185 
0186     *m_saveOptions = kpDocumentSaveOptions ();
0187 
0188     if ( !url.isEmpty() )
0189     {
0190       //  guess the mimetype from url's filename extension.
0191       //
0192       //  That way "kolourpaint doesnotexist.bmp" automatically
0193       //  selects the BMP file format when the save dialog comes up for
0194       //  the first time.
0195 
0196       QMimeDatabase mimeDb;
0197       m_saveOptions->setMimeType(mimeDb.mimeTypeForUrl(url).name());
0198     }
0199 
0200     *m_metaInfo = kpDocumentMetaInfo ();
0201     m_modified = false;
0202 
0203     Q_EMIT documentOpened ();
0204 }
0205 
0206 //---------------------------------------------------------------------
0207 
0208 bool kpDocument::open (const QUrl &url, bool newDocSameNameIfNotExist)
0209 {
0210 #if DEBUG_KP_DOCUMENT
0211     qCDebug(kpLogDocument) << "kpDocument::open (" << url << ")";
0212 #endif
0213 
0214     kpDocumentSaveOptions newSaveOptions;
0215     kpDocumentMetaInfo newMetaInfo;
0216     QImage newPixmap = kpDocument::getPixmapFromFile (url,
0217         newDocSameNameIfNotExist/*suppress "doesn't exist" dialog*/,
0218         d->environ->dialogParent (),
0219         &newSaveOptions,
0220         &newMetaInfo);
0221 
0222     if (!newPixmap.isNull ())
0223     {
0224         delete m_image;
0225         m_image = new kpImage (newPixmap);
0226 
0227         setURL (url, true/*is from url*/);
0228         *m_saveOptions = newSaveOptions;
0229         *m_metaInfo = newMetaInfo;
0230         m_modified = false;
0231 
0232         Q_EMIT documentOpened ();
0233         return true;
0234     }
0235 
0236     if (newDocSameNameIfNotExist)
0237     {
0238         if (urlExists (url)) // not just a permission error?
0239         {
0240             openNew (url);
0241         }
0242         else
0243         {
0244             openNew (QUrl ());
0245         }
0246 
0247         return true;
0248     }
0249 
0250     return false;
0251 
0252 }
0253 
0254 //---------------------------------------------------------------------