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"