File indexing completed on 2024-05-19 04:56:12
0001 /** 0002 * \file pictureframe.cpp 0003 * Frame containing picture. 0004 * 0005 * \b Project: Kid3 0006 * \author Urs Fleisch 0007 * \date 03 Mar 2008 0008 * 0009 * Copyright (C) 2008-2018 Urs Fleisch 0010 * 0011 * This file is part of Kid3. 0012 * 0013 * Kid3 is free software; you can redistribute it and/or modify 0014 * it under the terms of the GNU General Public License as published by 0015 * the Free Software Foundation; either version 2 of the License, or 0016 * (at your option) any later version. 0017 * 0018 * Kid3 is distributed in the hope that it will be useful, 0019 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0021 * GNU General Public License for more details. 0022 * 0023 * You should have received a copy of the GNU General Public License 0024 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0025 */ 0026 0027 #include "pictureframe.h" 0028 #include <QFile> 0029 #include <QDataStream> 0030 #include <QCoreApplication> 0031 #include <QMimeDatabase> 0032 #include <QMimeType> 0033 #include <QtEndian> 0034 0035 namespace { 0036 0037 /** 0038 * List of picture type strings, NULL terminated. 0039 */ 0040 const char* const pictureTypeNames[] = { 0041 QT_TRANSLATE_NOOP("@default", "Other"), 0042 QT_TRANSLATE_NOOP("@default", "32x32 pixels PNG file icon"), 0043 QT_TRANSLATE_NOOP("@default", "Other file icon"), 0044 QT_TRANSLATE_NOOP("@default", "Cover (front)"), 0045 QT_TRANSLATE_NOOP("@default", "Cover (back)"), 0046 QT_TRANSLATE_NOOP("@default", "Leaflet page"), 0047 QT_TRANSLATE_NOOP("@default", "Media"), 0048 QT_TRANSLATE_NOOP("@default", "Lead artist/lead performer/soloist"), 0049 QT_TRANSLATE_NOOP("@default", "Artist/performer"), 0050 QT_TRANSLATE_NOOP("@default", "Conductor"), 0051 QT_TRANSLATE_NOOP("@default", "Band/Orchestra"), 0052 QT_TRANSLATE_NOOP("@default", "Composer"), 0053 QT_TRANSLATE_NOOP("@default", "Lyricist/text writer"), 0054 QT_TRANSLATE_NOOP("@default", "Recording Location"), 0055 QT_TRANSLATE_NOOP("@default", "During recording"), 0056 QT_TRANSLATE_NOOP("@default", "During performance"), 0057 QT_TRANSLATE_NOOP("@default", "Movie/video screen capture"), 0058 QT_TRANSLATE_NOOP("@default", "A bright coloured fish"), 0059 QT_TRANSLATE_NOOP("@default", "Illustration"), 0060 QT_TRANSLATE_NOOP("@default", "Band/artist logotype"), 0061 QT_TRANSLATE_NOOP("@default", "Publisher/Studio logotype"), 0062 nullptr 0063 }; 0064 0065 /** 0066 * List of untranslated picture type strings, NULL terminated. 0067 */ 0068 const char* const pictureTypeStrings[] = { 0069 "Other", 0070 "Png Icon", 0071 "Icon", 0072 "Front", 0073 "Back", 0074 "Leaflet", 0075 "Media", 0076 "Lead Artist", 0077 "Artist", 0078 "Conductor", 0079 "Band", 0080 "Composer", 0081 "Lyricist", 0082 "Recording Location", 0083 "During Recording", 0084 "During Performance", 0085 "Video Capture", 0086 "Fish", 0087 "Illustration", 0088 "Band Logotype", 0089 "Publisher Logotype", 0090 nullptr 0091 }; 0092 0093 } 0094 0095 0096 /** 0097 * Construct properties from a new image. 0098 * @param data image data 0099 */ 0100 PictureFrame::ImageProperties::ImageProperties(const QByteArray& data) 0101 { 0102 if (loadFromData(data)) { 0103 m_imageHash = qHash(data); 0104 } else { 0105 m_width = 0; 0106 m_height = 0; 0107 m_depth = 0; 0108 m_numColors = 0; 0109 m_imageHash = 0; 0110 } 0111 } 0112 0113 bool PictureFrame::ImageProperties::loadFromData(const QByteArray& data) 0114 { 0115 if (const int len = data.size(); 0116 len > 2 && data.at(0) == '\xff' && data.at(1) == '\xd8') { 0117 // JPEG 0118 int i = 2; 0119 while (i + 3 < len) { 0120 if (data.at(i)== '\xff') { 0121 quint8 marker = static_cast<quint8>(data.at(i + 1)); 0122 quint16 sectionLen = qFromBigEndian<quint16>( 0123 reinterpret_cast<const uchar*>(data.constData()) + i + 2); 0124 if (marker == 0xda) { 0125 break; // start of scan 0126 } 0127 if ((marker == 0xc0 || marker == 0xc2) && i + 9 < len && sectionLen >= 8) { 0128 quint8 precision = static_cast<quint8>(data.at(i + 4)); 0129 quint16 height = qFromBigEndian<quint16>( 0130 reinterpret_cast<const uchar*>(data.constData()) + i + 5); 0131 quint16 width = qFromBigEndian<quint16>( 0132 reinterpret_cast<const uchar*>(data.constData()) + i + 7); 0133 quint8 components = static_cast<quint8>(data.at(i + 9)); 0134 m_width = width; 0135 m_height = height; 0136 m_depth = precision * components; 0137 m_numColors = 0; 0138 return true; 0139 } 0140 i += sectionLen + 2; 0141 } else { 0142 break; 0143 } 0144 } 0145 } else if (len > 8 && data.startsWith("\x89PNG\r\n\x1a\n")) { 0146 // PNG 0147 int i = 8; 0148 while (i + 8 < len) { 0149 quint32 chunkLen = qFromBigEndian<quint32>( 0150 reinterpret_cast<const uchar*>(data.constData()) + i); 0151 if (QByteArray chunkName = data.mid(i + 4, 4); 0152 chunkName == "IHDR" && i + 20 < len && chunkLen >= 13) { 0153 quint32 width = qFromBigEndian<quint32>( 0154 reinterpret_cast<const uchar*>(data.constData()) + i + 8); 0155 quint32 height = qFromBigEndian<quint32>( 0156 reinterpret_cast<const uchar*>(data.constData()) + i + 12); 0157 quint8 depth = static_cast<quint8>(data.at(i + 16)); 0158 quint8 color = static_cast<quint8>(data.at(i + 17)); 0159 // The next three bytes are compression, filter, interlace. 0160 m_width = static_cast<int>(width); 0161 m_height = static_cast<int>(height); 0162 m_numColors = 0; 0163 if (color == 0 || color == 3) { 0164 m_depth = depth; 0165 } else if (color == 2) { 0166 m_depth = depth * 3; 0167 } else if (color == 4 || color == 6) { 0168 m_depth = depth * 4; 0169 } 0170 if (!(color & 1)) { 0171 return true; // not indexed, no need to read the PLTE chunk 0172 } 0173 } else if (chunkName == "PLTE") { 0174 m_numColors = chunkLen / 3; 0175 return true; 0176 } 0177 i += chunkLen + 12; 0178 } 0179 } 0180 return false; 0181 } 0182 0183 0184 /** 0185 * Constructor. 0186 * 0187 * @param data binary picture data 0188 * @param description description 0189 * @param pictureType picture type 0190 * @param mimeType MIME type 0191 * @param enc text encoding 0192 * @param imgFormat image format 0193 */ 0194 PictureFrame::PictureFrame( 0195 const QByteArray& data, 0196 const QString& description, 0197 PictureType pictureType, 0198 const QString& mimeType, 0199 TextEncoding enc, 0200 const QString& imgFormat) 0201 { 0202 setType(FT_Picture); 0203 setFields(*this, enc, imgFormat, mimeType, pictureType, description, data); 0204 } 0205 0206 /** 0207 * Constructor. 0208 * 0209 * @param frame general frame 0210 */ 0211 PictureFrame::PictureFrame(const Frame& frame) 0212 { 0213 *static_cast<Frame*>(this) = frame; // clazy:exclude=unneeded-cast 0214 setType(FT_Picture); 0215 0216 // Make sure all fields are available in the correct order 0217 TextEncoding enc = TE_ISO8859_1; 0218 PictureType pictureType = PT_CoverFront; 0219 QString imgFormat(QLatin1String("JPG")), mimeType(QLatin1String("image/jpeg")), description; 0220 QByteArray data; 0221 getFields(*this, enc, imgFormat, mimeType, pictureType, description, data); 0222 setFields(*this, enc, imgFormat, mimeType, pictureType, description, data); 0223 } 0224 0225 /** 0226 * Set all properties. 0227 * 0228 * @param frame frame to set 0229 * @param enc text encoding 0230 * @param imgFormat image format 0231 * @param mimeType MIME type 0232 * @param pictureType picture type 0233 * @param description description 0234 * @param data binary picture data 0235 * @param imgProps optional METADATA_BLOCK_PICTURE image properties 0236 */ 0237 void PictureFrame::setFields(Frame& frame, 0238 TextEncoding enc, const QString& imgFormat, 0239 const QString& mimeType, PictureType pictureType, 0240 const QString& description, const QByteArray& data, 0241 const ImageProperties* imgProps) 0242 { 0243 Field field; 0244 FieldList& fields = frame.fieldList(); 0245 fields.clear(); 0246 0247 field.m_id = ID_TextEnc; 0248 field.m_value = enc; 0249 fields.push_back(field); 0250 0251 field.m_id = ID_ImageFormat; 0252 field.m_value = imgFormat; 0253 fields.push_back(field); 0254 0255 field.m_id = ID_MimeType; 0256 field.m_value = mimeType; 0257 fields.push_back(field); 0258 0259 field.m_id = ID_PictureType; 0260 field.m_value = pictureType; 0261 fields.push_back(field); 0262 0263 field.m_id = ID_Description; 0264 field.m_value = description; 0265 fields.push_back(field); 0266 0267 field.m_id = ID_Data; 0268 field.m_value = data; 0269 fields.push_back(field); 0270 0271 if (imgProps && !imgProps->isNull()) { 0272 field.m_id = ID_ImageProperties; 0273 field.m_value.setValue(*imgProps); 0274 fields.push_back(field); 0275 } 0276 0277 frame.setValue(description); 0278 } 0279 0280 /** 0281 * Set all properties of a GEOB frame. 0282 * 0283 * @param frame frame to set 0284 * @param enc text encoding 0285 * @param mimeType MIME type 0286 * @param fileName file name 0287 * @param description description 0288 * @param data binary data 0289 */ 0290 void PictureFrame::setGeobFields( 0291 Frame& frame, TextEncoding enc, const QString& mimeType, 0292 const QString& fileName, const QString& description, const QByteArray& data) 0293 { 0294 Field field; 0295 FieldList& fields = frame.fieldList(); 0296 fields.clear(); 0297 0298 field.m_id = ID_TextEnc; 0299 field.m_value = enc; 0300 fields.push_back(field); 0301 0302 field.m_id = ID_MimeType; 0303 field.m_value = mimeType; 0304 fields.push_back(field); 0305 0306 field.m_id = ID_Filename; 0307 field.m_value = fileName; 0308 fields.push_back(field); 0309 0310 field.m_id = ID_Description; 0311 field.m_value = description; 0312 fields.push_back(field); 0313 0314 field.m_id = ID_Data; 0315 field.m_value = data; 0316 fields.push_back(field); 0317 0318 frame.setValue(description); 0319 } 0320 0321 /** 0322 * Get all properties. 0323 * Unavailable fields are not set. 0324 * 0325 * @param frame frame to get 0326 * @param enc text encoding 0327 * @param imgFormat image format 0328 * @param mimeType MIME type 0329 * @param pictureType picture type 0330 * @param description description 0331 * @param data binary picture data 0332 * @param imgProps optional METADATA_BLOCK_PICTURE image properties 0333 */ 0334 void PictureFrame::getFields(const Frame& frame, 0335 TextEncoding& enc, QString& imgFormat, 0336 QString& mimeType, PictureType& pictureType, 0337 QString& description, QByteArray& data, 0338 ImageProperties* imgProps) 0339 { 0340 for (auto it = frame.getFieldList().constBegin(); 0341 it != frame.getFieldList().constEnd(); 0342 ++it) { 0343 switch (it->m_id) { 0344 case ID_TextEnc: 0345 enc = static_cast<TextEncoding>(it->m_value.toInt()); 0346 break; 0347 case ID_ImageFormat: 0348 imgFormat = it->m_value.toString(); 0349 break; 0350 case ID_MimeType: 0351 mimeType = it->m_value.toString(); 0352 break; 0353 case ID_PictureType: 0354 pictureType = static_cast<PictureType>(it->m_value.toInt()); 0355 break; 0356 case ID_Description: 0357 description = it->m_value.toString(); 0358 break; 0359 case ID_Data: 0360 data = it->m_value.toByteArray(); 0361 break; 0362 case ID_ImageProperties: 0363 if (imgProps) { 0364 *imgProps = it->m_value.value<ImageProperties>(); 0365 } 0366 break; 0367 default: 0368 qDebug("Unknown picture field ID"); 0369 } 0370 } 0371 } 0372 0373 /** 0374 * Check if all the fields of two picture frames are equal. 0375 * @param f1 first picture frame 0376 * @param f2 second picture frame 0377 * @return true if equal. 0378 */ 0379 bool PictureFrame::areFieldsEqual(const Frame& f1, const Frame& f2) 0380 { 0381 TextEncoding enc1, enc2; 0382 QString imgFormat1, imgFormat2; 0383 QString mimeType1, mimeType2; 0384 PictureType pictureType1, pictureType2; 0385 QString description1, description2; 0386 QByteArray data1, data2; 0387 getFields(f1, enc1, imgFormat1, mimeType1, pictureType1, description1, data1); 0388 getFields(f2, enc2, imgFormat2, mimeType2, pictureType2, description2, data2); 0389 return data1 == data2 && description1 == description2 && 0390 mimeType1 == mimeType2 && pictureType1 == pictureType2 && 0391 imgFormat1 == imgFormat2 && enc1 == enc2; 0392 } 0393 0394 /** 0395 * Set text encoding. 0396 * 0397 * @param frame frame to set 0398 * @param enc text encoding 0399 * 0400 * @return true if field found and set. 0401 */ 0402 bool PictureFrame::setTextEncoding(Frame& frame, TextEncoding enc) 0403 { 0404 return setField(frame, ID_TextEnc, enc); 0405 } 0406 0407 /** 0408 * Get text encoding. 0409 * 0410 * @param frame frame to get 0411 * @param enc the text encoding is returned here 0412 * 0413 * @return true if field found. 0414 */ 0415 bool PictureFrame::getTextEncoding(const Frame& frame, TextEncoding& enc) 0416 { 0417 if (QVariant var(getField(frame, ID_TextEnc)); var.isValid()) { 0418 enc = static_cast<TextEncoding>(var.toInt()); 0419 return true; 0420 } 0421 return false; 0422 } 0423 0424 /** 0425 * Set image format. 0426 * 0427 * @param frame frame to set 0428 * @param imgFormat image format 0429 * 0430 * @return true if field found and set. 0431 */ 0432 bool PictureFrame::setImageFormat(Frame& frame, const QString& imgFormat) 0433 { 0434 return setField(frame, ID_ImageFormat, imgFormat); 0435 } 0436 0437 /** 0438 * Get image format. 0439 * 0440 * @param frame frame to get 0441 * @param imgFormat the image format is returned here 0442 * 0443 * @return true if field found. 0444 */ 0445 bool PictureFrame::getImageFormat(const Frame& frame, QString& imgFormat) 0446 { 0447 if (QVariant var(getField(frame, ID_ImageFormat)); var.isValid()) { 0448 imgFormat = var.toString(); 0449 return true; 0450 } 0451 return false; 0452 } 0453 0454 /** 0455 * Set MIME type. 0456 * 0457 * @param frame frame to set 0458 * @param mimeType MIME type 0459 * 0460 * @return true if field found and set. 0461 */ 0462 bool PictureFrame::setMimeType(Frame& frame, const QString& mimeType) 0463 { 0464 return setField(frame, ID_MimeType, mimeType); 0465 } 0466 0467 /** 0468 * Get MIME type. 0469 * 0470 * @param frame frame to get 0471 * @param mimeType the MIME type is returned here 0472 * 0473 * @return true if field found. 0474 */ 0475 bool PictureFrame::getMimeType(const Frame& frame, QString& mimeType) 0476 { 0477 if (QVariant var(getField(frame, ID_MimeType)); var.isValid()) { 0478 mimeType = var.toString(); 0479 return true; 0480 } 0481 return false; 0482 } 0483 0484 /** 0485 * Set picture type. 0486 * 0487 * @param frame frame to set 0488 * @param pictureType picture type 0489 * 0490 * @return true if field found and set. 0491 */ 0492 bool PictureFrame::setPictureType(Frame& frame, PictureType pictureType) 0493 { 0494 return setField(frame, ID_PictureType, pictureType); 0495 } 0496 0497 /** 0498 * Get picture type. 0499 * 0500 * @param frame frame to get 0501 * @param pictureType the picture type is returned here 0502 * 0503 * @return true if field found. 0504 */ 0505 bool PictureFrame::getPictureType(const Frame& frame, PictureType& pictureType) 0506 { 0507 if (QVariant var(getField(frame, ID_PictureType)); var.isValid()) { 0508 pictureType = static_cast<PictureType>(var.toInt()); 0509 return true; 0510 } 0511 return false; 0512 } 0513 0514 /** 0515 * Set description. 0516 * 0517 * @param frame frame to set 0518 * @param description description 0519 * 0520 * @return true if field found and set. 0521 */ 0522 bool PictureFrame::setDescription(Frame& frame, const QString& description) 0523 { 0524 return setField(frame, ID_Description, description); 0525 } 0526 0527 /** 0528 * Get description. 0529 * 0530 * @param frame frame to get 0531 * @param description the description is returned here 0532 * 0533 * @return true if field found. 0534 */ 0535 bool PictureFrame::getDescription(const Frame& frame, QString& description) 0536 { 0537 if (QVariant var(getField(frame, ID_Description)); var.isValid()) { 0538 description = var.toString(); 0539 return true; 0540 } 0541 return false; 0542 } 0543 0544 /** 0545 * Set binary data. 0546 * 0547 * @param frame frame to set 0548 * @param data binary data 0549 * 0550 * @return true if field found and set. 0551 */ 0552 bool PictureFrame::setData(Frame& frame, const QByteArray& data) 0553 { 0554 return setField(frame, ID_Data, data); 0555 } 0556 0557 /** 0558 * Get binary data. 0559 * 0560 * @param frame frame to get 0561 * @param data the binary data is returned here 0562 * 0563 * @return true if field found. 0564 */ 0565 bool PictureFrame::getData(const Frame& frame, QByteArray& data) 0566 { 0567 if (QVariant var(getField(frame, ID_Data)); var.isValid()) { 0568 data = var.toByteArray(); 0569 return true; 0570 } 0571 return false; 0572 } 0573 0574 /** 0575 * Read binary data from file. 0576 * 0577 * @param frame frame to set 0578 * @param fileName name of data file 0579 * 0580 * @return true if file read, field found and set. 0581 */ 0582 bool PictureFrame::setDataFromFile(Frame& frame, const QString& fileName) 0583 { 0584 bool result = false; 0585 if (!fileName.isEmpty()) { 0586 QFile file(fileName); 0587 if (file.open(QIODevice::ReadOnly)) { 0588 int size = file.size(); 0589 auto data = new char[size]; 0590 QDataStream stream(&file); 0591 stream.readRawData(data, size); 0592 auto ba = QByteArray(data, size); 0593 result = setData(frame, ba); 0594 delete [] data; 0595 file.close(); 0596 } 0597 } 0598 return result; 0599 } 0600 0601 /** 0602 * Save binary data to a file. 0603 * 0604 * @param frame frame 0605 * @param fileName name of data file to save 0606 * 0607 * @return true if field found and saved. 0608 */ 0609 bool PictureFrame::writeDataToFile(const Frame& frame, const QString& fileName) 0610 { 0611 QByteArray ba; 0612 if (getData(frame, ba)) { 0613 QFile file(fileName); 0614 if (file.open(QIODevice::WriteOnly)) { 0615 QDataStream stream(&file); 0616 stream.writeRawData(ba.data(), ba.size()); 0617 file.close(); 0618 return true; 0619 } 0620 } 0621 return false; 0622 } 0623 0624 /** 0625 * Get the MIME type and image format from a file. 0626 * 0627 * @param fileName name of data file 0628 * @param imgFormat if not null, the ID3v2.2 PIC image format ("JGP" or "PNG") 0629 * is set here 0630 * 0631 * @return mime type of file, null if not recognized. 0632 */ 0633 QString PictureFrame::getMimeTypeForFile(const QString& fileName, 0634 QString* imgFormat) 0635 { 0636 QMimeDatabase mimeDb; 0637 QString mimeType = mimeDb.mimeTypeForFile(fileName).name(); 0638 if (imgFormat) { 0639 if (mimeType == QLatin1String("image/jpeg")) { 0640 *imgFormat = QLatin1String("JPG"); 0641 } else if (mimeType == QLatin1String("image/png")) { 0642 *imgFormat = QLatin1String("PNG"); 0643 } 0644 } 0645 return mimeType; 0646 } 0647 0648 /** 0649 * Set the MIME type and image format from a file. 0650 * 0651 * @param frame frame to set 0652 * @param fileName name of data file 0653 * 0654 * @return true if field found and set. 0655 */ 0656 bool PictureFrame::setMimeTypeFromFileName(Frame& frame, const QString& fileName) 0657 { 0658 QString imgFormat; 0659 if (QString mimeType = getMimeTypeForFile(fileName, &imgFormat); 0660 !mimeType.isEmpty()) { 0661 return setMimeType(frame, mimeType) && setImageFormat(frame, imgFormat); 0662 } 0663 return false; 0664 } 0665 0666 namespace { 0667 0668 /** 0669 * Get a 32-bit number from a byte array stored in big-endian order. 0670 * 0671 * @param data byte array 0672 * @param index index of first byte in data 0673 * 0674 * @return big endian 32-bit value. 0675 */ 0676 unsigned long getBigEndianULongFromByteArray(const QByteArray& data, int index) 0677 { 0678 return 0679 (static_cast<unsigned char>(data[index + 3]) & 0xff) | 0680 ((static_cast<unsigned char>(data[index + 2]) & 0xff) << 8) | 0681 ((static_cast<unsigned char>(data[index + 1]) & 0xff) << 16) | 0682 ((static_cast<unsigned char>(data[index + 0]) & 0xff) << 24); 0683 } 0684 0685 /** 0686 * Render a 32-bit number to a byte array in big-endian order. 0687 * 0688 * @param value 32-bit value 0689 * @param data byte array 0690 * @param index index of first byte in data 0691 */ 0692 void renderBigEndianULongToByteArray(unsigned long value, 0693 QByteArray& data, int index) 0694 { 0695 data[index + 3] = value & 0xff; 0696 value >>= 8; 0697 data[index + 2] = value & 0xff; 0698 value >>= 8; 0699 data[index + 1] = value & 0xff; 0700 value >>= 8; 0701 data[index + 0] = value & 0xff; 0702 } 0703 0704 /** 0705 * Copy characters into a byte array. 0706 * 0707 * @param str source string 0708 * @param data destination byte array 0709 * @param index index of first byte in data 0710 * @param len number of bytes to copy 0711 */ 0712 void renderCharsToByteArray(const char* str, QByteArray& data, 0713 int index, int len) 0714 { 0715 for (int i = 0; i < len; ++i) { 0716 data[index++] = *str++; 0717 } 0718 } 0719 0720 } 0721 0722 /** 0723 * Set picture from a base64 string. 0724 * 0725 * @param frame frame to set 0726 * @param base64Value base64 string 0727 */ 0728 void PictureFrame::setFieldsFromBase64(Frame& frame, const QString& base64Value) 0729 { 0730 QByteArray ba = QByteArray::fromBase64(base64Value.toLatin1()); 0731 PictureFrame::PictureType pictureType = PictureFrame::PT_CoverFront; 0732 QString mimeType(QLatin1String("image/jpeg")); 0733 QString description(QLatin1String("")); 0734 ImageProperties imgProps; 0735 if (frame.getInternalName() == QLatin1String("METADATA_BLOCK_PICTURE")) { 0736 auto baSize = static_cast<unsigned long>(ba.size()); 0737 if (baSize < 32) return; 0738 int index = 0; 0739 pictureType = static_cast<PictureFrame::PictureType>( 0740 getBigEndianULongFromByteArray(ba, index)); 0741 index += 4; 0742 unsigned long mimeLen = getBigEndianULongFromByteArray(ba, index); 0743 index += 4; 0744 if (baSize < index + mimeLen + 24) return; 0745 mimeType = QString::fromLatin1(ba.data() + index, mimeLen); 0746 index += mimeLen; 0747 unsigned long descLen = getBigEndianULongFromByteArray(ba, index); 0748 index += 4; 0749 if (baSize < index + descLen + 20) return; 0750 description = QString::fromUtf8(ba.data() + index, descLen); 0751 index += descLen; 0752 uint width = getBigEndianULongFromByteArray(ba, index); 0753 index += 4; 0754 uint height = getBigEndianULongFromByteArray(ba, index); 0755 index += 4; 0756 uint depth = getBigEndianULongFromByteArray(ba, index); 0757 index += 4; 0758 uint numColors = getBigEndianULongFromByteArray(ba, index); 0759 index += 4; 0760 unsigned long picLen = getBigEndianULongFromByteArray(ba, index); 0761 index += 4; 0762 if (baSize < index + picLen) return; 0763 ba = ba.mid(index); 0764 imgProps = ImageProperties(width, height, depth, numColors, ba); 0765 } 0766 PictureFrame::setFields( 0767 frame, TE_UTF8, QLatin1String(""), mimeType, 0768 pictureType, description, ba, &imgProps); 0769 } 0770 0771 /** 0772 * Get picture to a base64 string. 0773 * 0774 * @param frame frame to get 0775 * @param base64Value base64 string to set 0776 */ 0777 void PictureFrame::getFieldsToBase64(const Frame& frame, QString& base64Value) 0778 { 0779 TextEncoding enc; 0780 PictureFrame::PictureType pictureType = PictureFrame::PT_CoverFront; 0781 QString imgFormat, mimeType, description; 0782 QByteArray pic; 0783 ImageProperties imgProps; 0784 PictureFrame::getFields(frame, enc, imgFormat, mimeType, 0785 pictureType, description, pic, &imgProps); 0786 if (frame.getInternalName() == QLatin1String("METADATA_BLOCK_PICTURE")) { 0787 QByteArray mimeStr = mimeType.toLatin1(); 0788 QByteArray descStr = description.toUtf8(); 0789 int mimeLen = mimeStr.length(); 0790 int descLen = descStr.length(); 0791 int picLen = pic.size(); 0792 QByteArray ba(32 + mimeLen + descLen + picLen, '\0'); 0793 int index = 0; 0794 renderBigEndianULongToByteArray(pictureType, ba, index); 0795 index += 4; 0796 renderBigEndianULongToByteArray(mimeLen, ba, index); 0797 index += 4; 0798 renderCharsToByteArray(mimeStr, ba, index, mimeLen); 0799 index += mimeLen; 0800 renderBigEndianULongToByteArray(descLen, ba, index); 0801 index += 4; 0802 renderCharsToByteArray(descStr, ba, index, descLen); 0803 index += descLen; 0804 0805 if (!imgProps.isValidForImage(pic)) { 0806 imgProps = ImageProperties(pic); 0807 } 0808 0809 renderBigEndianULongToByteArray(imgProps.width(), ba, index); 0810 index += 4; 0811 renderBigEndianULongToByteArray(imgProps.height(), ba, index); 0812 index += 4; 0813 renderBigEndianULongToByteArray(imgProps.depth(), ba, index); 0814 index += 4; 0815 renderBigEndianULongToByteArray(imgProps.numColors(), ba, index); 0816 index += 4; 0817 0818 renderBigEndianULongToByteArray(picLen, ba, index); 0819 index += 4; 0820 renderCharsToByteArray(pic.data(), ba, index, picLen); 0821 pic = ba; 0822 } 0823 base64Value = QString::fromLatin1(pic.toBase64()); 0824 } 0825 0826 /** 0827 * Get a translated string for a picture type. 0828 * 0829 * @param type picture type 0830 * 0831 * @return picture type, null string if unknown. 0832 */ 0833 QString PictureFrame::getPictureTypeName(PictureType type) 0834 { 0835 if (static_cast<int>(type) >= 0 && 0836 static_cast<int>(type) < static_cast<int>( 0837 std::size(pictureTypeNames) - 1)) { 0838 return QCoreApplication::translate("@default", pictureTypeNames[type]); 0839 } 0840 return QString(); 0841 } 0842 0843 /** 0844 * List of picture type strings, NULL terminated. 0845 */ 0846 const char* const* PictureFrame::getPictureTypeNames() 0847 { 0848 return pictureTypeNames; 0849 } 0850 0851 /** 0852 * Get an untranslated string for a picture type. 0853 * 0854 * @param type picture type 0855 * 0856 * @return picture type, 0 if unknown. 0857 */ 0858 const char* PictureFrame::getPictureTypeString(PictureType type) 0859 { 0860 return static_cast<int>(type) >= 0 && 0861 static_cast<int>(type) < static_cast<int>( 0862 std::size(pictureTypeStrings) - 1) 0863 ? pictureTypeStrings[type] : nullptr; 0864 } 0865 0866 /** 0867 * List of untranslated picture type strings, NULL terminated. 0868 */ 0869 const char* const* PictureFrame::getPictureTypeStrings() 0870 { 0871 return pictureTypeStrings; 0872 } 0873 0874 /** 0875 * Get picture type from an untranslated string. 0876 * 0877 * @param str untranslated picture type string 0878 * 0879 * @return picture type, PT_Other if unknown. 0880 */ 0881 PictureFrame::PictureType PictureFrame::getPictureTypeFromString(const char* str) 0882 { 0883 for (unsigned int i = 0; 0884 i < std::size(pictureTypeStrings) - 1; 0885 ++i) { 0886 if (qstricmp(str, pictureTypeStrings[i]) == 0) { 0887 return static_cast<PictureType>(i); 0888 } 0889 } 0890 return PT_Other; 0891 }