File indexing completed on 2025-04-27 03:58:26

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2006-06-23
0007  * Description : a tab widget to display ICC profile infos
0008  *
0009  * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "iccprofilewidget.h"
0016 
0017 // Qt includes
0018 
0019 #include <QComboBox>
0020 #include <QFile>
0021 #include <QGroupBox>
0022 #include <QLabel>
0023 #include <QMap>
0024 #include <QPushButton>
0025 
0026 // KDE includes
0027 
0028 #include <klocalizedstring.h>
0029 
0030 // Local includes
0031 
0032 #include "iccprofile.h"
0033 #include "digikam_debug.h"
0034 #include "cietonguewidget.h"
0035 #include "metadatalistview.h"
0036 
0037 namespace
0038 {
0039 
0040 static const char* ICCHumanList[] =
0041 {
0042     "Icc.Header.ColorSpace",
0043     "Icc.Header.Copyright",
0044     "Icc.Header.DeviceClass",
0045     "Icc.Header.Name",
0046     "Icc.Header.Description",
0047     "Icc.Header.RenderingIntent",
0048     "-1"
0049 };
0050 
0051 /**
0052  * NOTE: This entry list is only require for compatibility with MetadataWidget implementation.
0053  */
0054 static const char* ICCEntryList[] =
0055 {
0056     "Header",
0057     "-1"
0058 };
0059 
0060 } // namespace
0061 
0062 namespace Digikam
0063 {
0064 
0065 class Q_DECL_HIDDEN ICCTagInfo
0066 {
0067 
0068 public:
0069 
0070     ICCTagInfo()
0071     {
0072     }
0073 
0074     ICCTagInfo(const QString& title, const QString& description)
0075         : m_title      (title),
0076           m_description(description)
0077     {
0078     }
0079 
0080     QString title()       const
0081     {
0082         return m_title;
0083     }
0084 
0085     QString description() const
0086     {
0087         return m_description;
0088     }
0089 
0090 private:
0091 
0092     QString m_title;
0093     QString m_description;
0094 };
0095 
0096 typedef QMap<QString, ICCTagInfo> ICCTagInfoMap;
0097 
0098 // ---------------------------------------------------------------------------------------
0099 
0100 class Q_DECL_HIDDEN ICCProfileWidget::Private
0101 {
0102 
0103 public:
0104 
0105     explicit Private()
0106       : cieTongue(nullptr)
0107     {
0108     }
0109 
0110     IccProfile       profile;
0111 
0112     QStringList      keysFilter;
0113 
0114     CIETongueWidget* cieTongue;
0115 
0116     ICCTagInfoMap    iccTagsDescription;
0117 };
0118 
0119 ICCProfileWidget::ICCProfileWidget(QWidget* const parent, int w, int h)
0120     : MetadataWidget(parent),
0121       d             (new Private)
0122 {
0123     setup();
0124     dkCmsErrorAction(LCMS_ERROR_SHOW);
0125 
0126     // Set the translated ICC tags titles/descriptions list
0127 
0128     d->iccTagsDescription[QLatin1String("Icc.Header.Name")]            = ICCTagInfo(i18nc("@item: icc profile properties", "Name"),
0129                                                                                     i18nc("@info: icc profile properties", "The ICC profile product name"));
0130     d->iccTagsDescription[QLatin1String("Icc.Header.Description")]     = ICCTagInfo(i18nc("@item: icc profile properties", "Description"),
0131                                                                                     i18nc("@info: icc profile properties", "The ICC profile product description"));
0132     d->iccTagsDescription[QLatin1String("Icc.Header.Information")]     = ICCTagInfo(i18nc("@item: icc profile properties", "Information"),
0133                                                                                     i18nc("@info: icc profile properties", "Additional ICC profile information"));
0134     d->iccTagsDescription[QLatin1String("Icc.Header.Manufacturer")]    = ICCTagInfo(i18nc("@item: icc profile properties", "Manufacturer"),
0135                                                                                     i18nc("@info: icc profile properties", "Raw information about the ICC profile manufacturer"));
0136     d->iccTagsDescription[QLatin1String("Icc.Header.Model")]           = ICCTagInfo(i18nc("@item: icc profile properties", "Model"),
0137                                                                                     i18nc("@info: icc profile properties", "Raw information about the ICC profile model"));
0138     d->iccTagsDescription[QLatin1String("Icc.Header.Copyright")]       = ICCTagInfo(i18nc("@item: icc profile properties", "Copyright"),
0139                                                                                     i18nc("@info: icc profile properties", "Raw information about the ICC profile copyright"));
0140     d->iccTagsDescription[QLatin1String("Icc.Header.ProfileID")]       = ICCTagInfo(i18nc("@item: icc profile properties", "Profile ID"),
0141                                                                                     i18nc("@info: icc profile properties", "The ICC profile ID number"));
0142     d->iccTagsDescription[QLatin1String("Icc.Header.ColorSpace")]      = ICCTagInfo(i18nc("@item: icc profile properties", "Color Space"),
0143                                                                                     i18nc("@info: icc profile properties", "The color space used by the ICC profile"));
0144     d->iccTagsDescription[QLatin1String("Icc.Header.ConnectionSpace")] = ICCTagInfo(i18nc("@item: icc profile properties", "Connection Space"),
0145                                                                                     i18nc("@info: icc profile properties", "The connection space used by the ICC profile"));
0146     d->iccTagsDescription[QLatin1String("Icc.Header.DeviceClass")]     = ICCTagInfo(i18nc("@item: icc profile properties", "Device Class"),
0147                                                                                     i18nc("@info: icc profile properties", "The ICC profile device class"));
0148     d->iccTagsDescription[QLatin1String("Icc.Header.RenderingIntent")] = ICCTagInfo(i18nc("@item: icc profile properties", "Rendering Intent"),
0149                                                                                     i18nc("@info: icc profile properties", "The ICC profile rendering intent"));
0150     d->iccTagsDescription[QLatin1String("Icc.Header.ProfileVersion")]  = ICCTagInfo(i18nc("@item: icc profile properties", "Profile Version"),
0151                                                                                     i18nc("@info: icc profile properties", "The ICC version used to record the profile"));
0152     d->iccTagsDescription[QLatin1String("Icc.Header.CMMFlags")]        = ICCTagInfo(i18nc("@item: icc profile properties", "CMM Flags"),
0153                                                                                     i18nc("@info: icc profile properties", "The ICC profile color management flags"));
0154 
0155     // Set the list of keys and tags filters.
0156 
0157     for (int i = 0 ; QLatin1String(ICCEntryList[i]) != QLatin1String("-1") ; ++i)
0158     {
0159         d->keysFilter << QLatin1String(ICCEntryList[i]);
0160     }
0161 
0162     QStringList tagsFilter;
0163 
0164     for (int i = 0 ; QLatin1String(ICCHumanList[i]) != QLatin1String("-1") ; ++i)
0165     {
0166         tagsFilter << QLatin1String(ICCHumanList[i]);
0167     }
0168 
0169     setTagsFilter(tagsFilter);
0170 
0171     // Add CIE tongue graph to the widget area
0172 
0173     d->cieTongue = new CIETongueWidget(w, h, this);
0174     d->cieTongue->setWhatsThis(i18nc("@info: icc profile properties",
0175                                      "This area contains a CIE or chromaticity diagram.\n"
0176                                      "A CIE diagram is a representation of all the colors\n"
0177                                      "that a person with normal vision can see. This is represented\n"
0178                                      "by the colored sail-shaped area. In addition you will see a\n"
0179                                      "triangle that is superimposed on the diagram outlined in white.\n"
0180                                      "This triangle represents the outer boundaries of the color space\n"
0181                                      "of the device that is characterized by the inspected profile.\n"
0182                                      "This is called the device gamut.\n"
0183                                      "In addition there are black dots and yellow lines on the diagram.\n"
0184                                      "Each black dot represents one of the measurement points that were\n"
0185                                      "used to create this profile. The yellow line represents the\n"
0186                                      "amount that each point is corrected by the profile, and the\n"
0187                                      "direction of this correction."));
0188 
0189     setUserAreaWidget(d->cieTongue);
0190     this->decodeMetadata();
0191 }
0192 
0193 ICCProfileWidget::~ICCProfileWidget()
0194 {
0195     delete d;
0196 }
0197 
0198 bool ICCProfileWidget::setProfile(const IccProfile& profile)
0199 {
0200     // Cleanup all metadata contents.
0201 
0202     setMetadataMap();
0203 
0204     d->profile = profile;
0205 
0206     if (!d->profile.open())
0207     {
0208         setMetadataEmpty();
0209         d->cieTongue->setProfileData();
0210         d->profile = IccProfile();
0211 
0212         return false;
0213     }
0214 
0215     // Try to decode current metadata.
0216 
0217     enabledToolButtons(decodeMetadata());
0218 
0219     // Refresh view using decoded metadata.
0220 
0221     buildView();
0222 
0223     return true;
0224 }
0225 
0226 IccProfile ICCProfileWidget::getProfile() const
0227 {
0228     return d->profile;
0229 }
0230 
0231 void ICCProfileWidget::setDataLoading()
0232 {
0233     d->cieTongue->loadingStarted();
0234 }
0235 
0236 void ICCProfileWidget::setLoadingFailed()
0237 {
0238     d->cieTongue->loadingFailed();
0239 }
0240 
0241 void ICCProfileWidget::setUncalibratedColor()
0242 {
0243     d->cieTongue->uncalibratedColor();
0244 }
0245 
0246 QString ICCProfileWidget::getMetadataTitle() const
0247 {
0248     return i18nc("@title: icc profile properties", "ICC Color Profile Information");
0249 }
0250 
0251 bool ICCProfileWidget::loadFromURL(const QUrl& url)
0252 {
0253     setFileName(url.toLocalFile());
0254 
0255     if (url.isEmpty())
0256     {
0257         setProfile(IccProfile());
0258         d->cieTongue->setProfileData();
0259 
0260         return false;
0261     }
0262     else
0263     {
0264         IccProfile profile(url.toLocalFile());
0265 
0266         if (!setProfile(profile))
0267         {
0268             setProfile(IccProfile());
0269             d->cieTongue->setProfileData();
0270 
0271             return false;
0272         }
0273     }
0274 
0275     return true;
0276 }
0277 
0278 bool ICCProfileWidget::loadFromProfileData(const QString& fileName, const QByteArray& data)
0279 {
0280     setFileName(fileName);
0281 
0282     return(setProfile(IccProfile(data)));
0283 }
0284 
0285 bool ICCProfileWidget::loadProfile(const QString& fileName, const IccProfile& profile)
0286 {
0287     setFileName(fileName);
0288 
0289     return(setProfile(profile));
0290 }
0291 
0292 bool ICCProfileWidget::decodeMetadata()
0293 {
0294     if (!d->profile.isOpen())
0295     {
0296         return false;
0297     }
0298 
0299     d->cieTongue->setProfileData(d->profile.data());
0300 
0301     LcmsLock lock;
0302     cmsHPROFILE hProfile = d->profile.handle();
0303 
0304     if (!hProfile)
0305     {
0306         qCDebug(DIGIKAM_WIDGETS_LOG) << "Cannot parse ICC profile tags using LCMS";
0307         return false;
0308     }
0309 
0310     DMetadata::MetaDataMap metaDataMap;
0311 
0312     if (!QString(dkCmsTakeProductName(hProfile)).isEmpty())
0313     {
0314         metaDataMap.insert(QLatin1String("Icc.Header.Name"), dkCmsTakeProductName(hProfile).replace(QLatin1Char('\n'), QLatin1Char(' ')));
0315     }
0316 
0317     if (!dkCmsTakeProductDesc(hProfile).isEmpty())
0318     {
0319         metaDataMap.insert(QLatin1String("Icc.Header.Description"), dkCmsTakeProductDesc(hProfile).replace(QLatin1Char('\n'), QLatin1Char(' ')));
0320     }
0321 
0322     if (!dkCmsTakeProductInfo(hProfile).isEmpty())
0323     {
0324         metaDataMap.insert(QLatin1String("Icc.Header.Information"), dkCmsTakeProductInfo(hProfile).replace(QLatin1Char('\n'), QLatin1Char(' ')));
0325     }
0326 
0327     if (!dkCmsTakeManufacturer(hProfile).isEmpty())
0328     {
0329         metaDataMap.insert(QLatin1String("Icc.Header.Manufacturer"), dkCmsTakeManufacturer(hProfile).replace(QLatin1Char('\n'), QLatin1Char(' ')));
0330     }
0331 
0332     if (!dkCmsTakeModel(hProfile).isEmpty())
0333     {
0334         metaDataMap.insert(QLatin1String("Icc.Header.Model"), dkCmsTakeModel(hProfile).replace(QLatin1Char('\n'), QLatin1Char(' ')));
0335     }
0336 
0337     if (!dkCmsTakeCopyright(hProfile).isEmpty())
0338     {
0339         metaDataMap.insert(QLatin1String("Icc.Header.Copyright"), dkCmsTakeCopyright(hProfile).replace(QLatin1Char('\n'), QLatin1Char(' ')));
0340     }
0341 
0342     metaDataMap.insert(QLatin1String("Icc.Header.ProfileID"),      QString::number((uint)*dkCmsTakeProfileID(hProfile)));
0343     metaDataMap.insert(QLatin1String("Icc.Header.ProfileVersion"), QString::number((uint)dkCmsGetProfileICCversion(hProfile)));
0344     metaDataMap.insert(QLatin1String("Icc.Header.CMMFlags"),       QString::number((uint)dkCmsTakeHeaderFlags(hProfile)));
0345 
0346     QString colorSpace;
0347 
0348     switch (dkCmsGetColorSpace(hProfile))
0349     {
0350         case icSigLabData:
0351         {
0352             colorSpace = i18nc("@info: icc profile color space", "Lab");
0353             break;
0354         }
0355 
0356         case icSigLuvData:
0357         {
0358             colorSpace = i18nc("@info: icc profile color space", "Luv");
0359             break;
0360         }
0361 
0362         case icSigRgbData:
0363         {
0364             colorSpace = i18nc("@info: icc profile color space", "RGB");
0365             break;
0366         }
0367 
0368         case icSigGrayData:
0369         {
0370             colorSpace = i18nc("@info: icc profile color space", "GRAY");
0371             break;
0372         }
0373 
0374         case icSigHsvData:
0375         {
0376             colorSpace = i18nc("@info: icc profile color space", "HSV");
0377             break;
0378         }
0379 
0380         case icSigHlsData:
0381         {
0382             colorSpace = i18nc("@info: icc profile color space", "HLS");
0383             break;
0384         }
0385 
0386         case icSigCmykData:
0387         {
0388             colorSpace = i18nc("@info: icc profile color space", "CMYK");
0389             break;
0390         }
0391 
0392         case icSigCmyData:
0393         {
0394             colorSpace= i18nc("@info: icc profile color space", "CMY");
0395             break;
0396         }
0397 
0398         default:
0399         {
0400             colorSpace = i18nc("@info: icc profile color space", "Unknown");
0401             break;
0402         }
0403     }
0404 
0405     metaDataMap.insert(QLatin1String("Icc.Header.ColorSpace"), colorSpace);
0406 
0407     QString connectionSpace;
0408 
0409     switch (dkCmsGetPCS(hProfile))
0410     {
0411         case icSigLabData:
0412         {
0413             connectionSpace = i18nc("@info: icc profile color space", "Lab");
0414             break;
0415         }
0416 
0417         case icSigLuvData:
0418         {
0419             connectionSpace = i18nc("@info: icc profile color space", "Luv");
0420             break;
0421         }
0422 
0423         case icSigRgbData:
0424         {
0425             connectionSpace = i18nc("@info: icc profile color space", "RGB");
0426             break;
0427         }
0428 
0429         case icSigGrayData:
0430         {
0431             connectionSpace = i18nc("@info: icc profile color space", "GRAY");
0432             break;
0433         }
0434 
0435         case icSigHsvData:
0436         {
0437             connectionSpace = i18nc("@info: icc profile color space", "HSV");
0438             break;
0439         }
0440 
0441         case icSigHlsData:
0442         {
0443             connectionSpace = i18nc("@info: icc profile color space", "HLS");
0444             break;
0445         }
0446 
0447         case icSigCmykData:
0448         {
0449             connectionSpace = i18nc("@info: icc profile color space", "CMYK");
0450             break;
0451         }
0452 
0453         case icSigCmyData:
0454         {
0455             connectionSpace= i18nc("@info: icc profile color space", "CMY");
0456             break;
0457         }
0458 
0459         default:
0460         {
0461             connectionSpace = i18nc("@info: icc profile color space", "Unknown");
0462             break;
0463         }
0464     }
0465 
0466     metaDataMap.insert(QLatin1String("Icc.Header.ConnectionSpace"), connectionSpace);
0467 
0468     QString device;
0469 
0470     switch ((int)dkCmsGetDeviceClass(hProfile))
0471     {
0472         case icSigInputClass:
0473         {
0474             device = i18nc("@info: icc profile class", "Input device");
0475             break;
0476         }
0477 
0478         case icSigDisplayClass:
0479         {
0480             device = i18nc("@info: icc profile class", "Display device");
0481             break;
0482         }
0483 
0484         case icSigOutputClass:
0485         {
0486             device = i18nc("@info: icc profile class", "Output device");
0487             break;
0488         }
0489 
0490         case icSigColorSpaceClass:
0491         {
0492             device = i18nc("@info: icc profile class", "Color space");
0493             break;
0494         }
0495 
0496         case icSigLinkClass:
0497         {
0498             device = i18nc("@info: icc profile class", "Link device");
0499             break;
0500         }
0501 
0502         case icSigAbstractClass:
0503         {
0504             device = i18nc("@info: icc profile class", "Abstract");
0505             break;
0506         }
0507 
0508         case icSigNamedColorClass:
0509         {
0510             device = i18nc("@info: icc profile class", "Named color");
0511             break;
0512         }
0513 
0514         default:
0515         {
0516             device = i18nc("@info: icc profile class", "Unknown");
0517             break;
0518         }
0519     }
0520 
0521     metaDataMap.insert(QLatin1String("Icc.Header.DeviceClass"), device);
0522 
0523     QString intent;
0524 
0525     switch (dkCmsTakeRenderingIntent(hProfile))
0526     {
0527         case 0:
0528         {
0529             intent = i18nc("@info: icc profile redering", "Perceptual");
0530             break;
0531         }
0532 
0533         case 1:
0534         {
0535             intent = i18nc("@info: icc profile redering", "Relative Colorimetric");
0536             break;
0537         }
0538 
0539         case 2:
0540         {
0541             intent = i18nc("@info: icc profile redering", "Saturation");
0542             break;
0543         }
0544 
0545         case 3:
0546         {
0547             intent = i18nc("@info: icc profile redering", "Absolute Colorimetric");
0548             break;
0549         }
0550 
0551         default:
0552         {
0553             intent = i18nc("@info: icc profile redering", "Unknown");
0554             break;
0555         }
0556     }
0557 
0558     metaDataMap.insert(QLatin1String("Icc.Header.RenderingIntent"), intent);
0559 
0560     // Update all metadata contents.
0561 
0562     setMetadataMap(metaDataMap);
0563 
0564     return true;
0565 }
0566 
0567 void ICCProfileWidget::buildView()
0568 {
0569     if (getMode() == CUSTOM)
0570     {
0571         setIfdList(getMetadataMap(), d->keysFilter, getTagsFilter());
0572     }
0573     else
0574     {
0575         setIfdList(getMetadataMap(), d->keysFilter, QStringList() << QLatin1String("FULL"));
0576     }
0577 
0578     MetadataWidget::buildView();
0579 }
0580 
0581 QString ICCProfileWidget::getTagTitle(const QString& key)
0582 {
0583     ICCTagInfoMap::const_iterator it = d->iccTagsDescription.constFind(key);
0584 
0585     if (it != d->iccTagsDescription.constEnd())
0586     {
0587         return(it.value().title());
0588     }
0589 
0590     return key.section(QLatin1Char('.'), 2, 2);
0591 }
0592 
0593 void ICCProfileWidget::slotSaveMetadataToFile()
0594 {
0595     QUrl url = saveMetadataToFile(i18nc("@title: file open dialog", "ICC color profile File to Save"),
0596                                   QString(QLatin1String("*.icc *.icm|") +
0597                                   i18nc("@info: file open filters", "ICC Files (*.icc; *.icm)")));
0598     storeMetadataToFile(url, d->profile.data());
0599 }
0600 
0601 QString ICCProfileWidget::getTagDescription(const QString& key)
0602 {
0603     ICCTagInfoMap::const_iterator it = d->iccTagsDescription.constFind(key);
0604 
0605     if (it != d->iccTagsDescription.constEnd())
0606     {
0607         return(it.value().description());
0608     }
0609 
0610     return key.section(QLatin1Char('.'), 2, 2);
0611 }
0612 
0613 } // namespace Digikam
0614 
0615 #include "moc_iccprofilewidget.cpp"