diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -108,6 +108,10 @@ TEST_NAME "taglibextractortest" LINK_LIBRARIES Qt5::Test KF5::FileMetaData ${TAGLIB_LIBRARIES} ) + ecm_add_test(embeddedimagedatatest.cpp ../src/embeddedimagedata.cpp + TEST_NAME "embeddedimagedatatest" + LINK_LIBRARIES Qt5::Test KF5::FileMetaData ${TAGLIB_LIBRARIES} + ) endif() diff --git a/autotests/embeddedimagedatatest.h b/autotests/embeddedimagedatatest.h new file mode 100644 --- /dev/null +++ b/autotests/embeddedimagedatatest.h @@ -0,0 +1,38 @@ +/* + * EmbeddedImageData tests. + * + * Copyright (C) 2018 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef EMBEDDEDIMAGEDATATEST_H +#define EMBEDDEDIMAGEDATATEST_H + +#include +#include "properties.h" + +class EmbeddedImageDataTest : public QObject +{ + Q_OBJECT +private: + QString testFilePath(const QString& fileName) const; + +private Q_SLOTS: + void test(); +}; + +#endif // EMBEDDEDIMAGEDATATEST_H diff --git a/autotests/embeddedimagedatatest.cpp b/autotests/embeddedimagedatatest.cpp new file mode 100644 --- /dev/null +++ b/autotests/embeddedimagedatatest.cpp @@ -0,0 +1,65 @@ +/* + * EmbeddedImageData tests. + * + * Copyright (C) 2018 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "embeddedimagedatatest.h" +#include "embeddedimagedata.h" +#include "indexerextractortestsconfig.h" + +#include + +using namespace KFileMetaData; + +QString EmbeddedImageDataTest::testFilePath(const QString& fileName) const +{ + return QLatin1String(INDEXER_TESTS_SAMPLE_FILES_PATH) + QDir::separator() + fileName; +} + +void EmbeddedImageDataTest::test() +{ + + QMimeDatabase mimeDatabase; + QByteArray originalImage; + QByteArray embeddedImage; + QFile testFile(testFilePath("test.jpg")); + testFile.open(QIODevice::ReadOnly); + originalImage = testFile.readAll(); + + EmbeddedImageData imageData; + embeddedImage = imageData.imageData(testFilePath("test.opus"), mimeDatabase); + QCOMPARE(embeddedImage,originalImage); + + embeddedImage = imageData.imageData(testFilePath("test.ogg"), mimeDatabase); + QCOMPARE(embeddedImage,originalImage); + + embeddedImage = imageData.imageData(testFilePath("test.flac"), mimeDatabase); + QCOMPARE(embeddedImage,originalImage); + + embeddedImage = imageData.imageData(testFilePath("test.mp3"), mimeDatabase); + QCOMPARE(embeddedImage,originalImage); + + embeddedImage = imageData.imageData(testFilePath("test.m4a"), mimeDatabase); + QCOMPARE(embeddedImage,originalImage); + + embeddedImage = imageData.imageData(testFilePath("test.mpc"), mimeDatabase); + QCOMPARE(embeddedImage,originalImage); +} + +QTEST_GUILESS_MAIN(EmbeddedImageDataTest) diff --git a/autotests/samplefiles/no-meta/test.m4a b/autotests/samplefiles/no-meta/test.m4a index 95b406f98003af2f17adcd81ce5466b50a6a6fcb..5202955569df8f0a0333e904bae98dbf4e66a8e9 GIT binary patch literal 12112 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 diff --git a/autotests/samplefiles/test.mpc b/autotests/samplefiles/test.mpc index 46966621333b3825900e6a2871fc0ab8c6997567..e9195cf5001e6e6528196b0710e8082db9d53a2d GIT binary patch literal 5227 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 TyE+EaPrus#@p2HgeK`IFKJK)A literal 3739 zc${@tc|4R|`yXQ)MrB_j5n{5F6qQ+-VJtC3vKupFUqh57ubnKJiiikVLuJkW*tbv! zE$9)lmVLQ@<9*Wmd;fUv&*!?&eSN>zIrq7~XZf6?Yk<-*!XAfpvh+eiKiYu=p@v#) zbfDi42FIL9CroFg4|#OxA`Bk-mG1;i0=^9o163I~apFn1#7S_@FI>xQB$+pa90z}; zm3ub$MfL#CW&ajEVL7TUmok^I1>~?{?#XIL~&4{SYBwC-n5K-eNhey-ewE zPwB1T2lq&O!P2t`&HMh|3E}!c#+Ymj(;z&7_hqs>up;$bjv3c2`t92OmlL5ctvS>p z04RvAC`!19Q5ulh?t3@Q4ZI7WI>?gQl&&|lMqO@(-xcp|7Yk|XH;ZrHPUC>028c(u4(r3V!)WRqk}cpoOfl)!cmORBh= zxo{IO|3n-)y@q5gxFhIX`CVrq) zugWCqWXJ`wMyuhL>4%z*;slf?&5 zd-}m80f1Efc8|&vaS`uUA>>qEUidct~->^VexF7epfneb|7=_^|QCTnpRtFvezDbs7 zix0b(C9;G4`YW;JrIvMUhyZMg`EGDr{~jP z4;oWiS|kbc?(>c^GWg~D@0SGC>@;6kJzhSo>cKTyuU2ct`A$6kn%p#x5rZ#(!6R|S z=|pG^|7yp-CX8YO1Th!e%U;v;+099*+pQV;X`hNclbIRDZ7s{rA|5KaTc~3ROw61n zD|4iLl@d_zb+z>!yy$#eUaH}&UVP#7(9GOPCk+5-Jem3TTK3rUTUv$Rl(dluGS^jW z(~o>8qd_W)I&!vHdqBP|PIcGkNuZVb^-)Dn1BFjfB)e%}RVD$LqXnoS$Xq$SKi z8+o94osbgqIrWMo0X7Nu$4518j-vnW~B06UUw___=i2yFh*I%P;(^HbM;1F~GOqXtl`$%mOo@k3c?_ znsYNEt2jYhLhTj@gmLg?pg<*?CML7SXZM|Uw}I-g7v;*u0WNEM`%_>w1`eq+hN4MQ zk@nFH`S277XBgNZBr}*S!gZ)-!F;WHNx8KAlW>P|dVwna93kZ$#{Bzy`Kqv91ZT!Z zRw*wt?4G(~-Vlo`4+t(-hlh#j#Ah8lU$&l5OV`D=sg&_{No0tzoOQ1V7y|%($62H8 zx_5Kf%97eQe~0}@=s;FVC>}VwOA6hbKe#(()twx}R&+}#u}FnzFC9Hatrjyzh`#z* zz;Z1214cuAPO~;HKndy~fwM}@DABGn4E115(oPX^J~X%va4;5hdyc0vs}Yy$izQi! zuh4mXlK#ZV>jTl?i0NF}%-wn{bq=KTla|`bu~Oe(8^ufIZDkQFPwk?#YtFs$gQ$-M zf2n_5zA=(HxPIJEEQ4V;gIe4k2k(&U{O-@CLFOw#*lP1oZ@i9XMd2~dEKRYT%;7Bb ze~D&$zytAJsmSZEBjcJpE=rlpHPclJbUDsmnA^OuRbr0X-W^N+8mTf1 z0Px*-kA=yn(p75}cUB}`uxesVxcXnjvZ#Dnmb}zgo%;#fQtJhyVs$2MZzY`~tVKW7 zK7ONO4$*DW2kRR#@zA?fQ+l-@fTzr~wCXMTd9Du+cjV`co{rvv^ysLab-3prC{ZZ6 zb+6i_SocIuhHT^2^rqYhuPTKt$QFc3Tfbj#bQuMjU8p#hMTo9AVU;!D z``DVEfDO%5weE3CV0k9 z%Q_^mDSM));-USPT0ky+9eVLta*tp!RBOmC_b=B*wSHjbMxNg1_F?zCuzR}gc63o! zmV7RKY<^IjtOxN*uTCBY$W4Y|x z;zVW%a5JD!awkQ9U0)1jNai!!Hm+)-StI8#LKZU~248FzkyEGvPeU^q93VX$dmMX? zkYRR*?JecNy>bk*Od_Hs=%61kDRx{IzKqhXD=+2cBtNrm{~(K+(tUR7Lbu)8Xwx|T z))W#vZvomQ7@9XMnhW22Icg(ea|S;d+QW^{@c?lG{lGp5k}=N8_^b3k|2TAeMsqatAP0(^Juavs%ziKr{D z@%f}TdAy3sG5n8ARd|mfVEGa%J6i+E_ZFs+dZ1IZwX}R*>3e8=dDUq+HeLDBn**pB(=LnT`DsC49|$wyBvKm&c9{0HJIf_5eTxJfB_U z(L561l$0LeJegSQ>^G&=2!01)g}%MKJF*IuVbdwhxbsBU-}YdNZeC>%dTXJM~ z>E7!7pD@W7QI|Ib_yPIwFZF_<#a-&W?RW1c)sgW5)=YGUu*Eu718!~l=+E_aTVsSx)+=U_UFX7VDb|Klf{TE zQL$df`*r4`aoyPo&mk<{vik1oCSMwqv-g7K6N2lJtl|9(^pc@;NfiLlWMQfNHn&p+ zCA(s99b_R_+BUV3XsMq&aw&Qvu?ueQfPU*bbo;8S^wsIIT%QC%@C63i38KlP)%;i9 zOrUjgh5j@VKPmBtxRC^cpvli&7Row-HKJx)vB3vnX{j8gUv3%Mg%50&H*C~ zJ;DG(Kxko12be)1T`&m5|Ialc^XR_7_`5{lk;b|pgtM)W3)>NN=!hUZy(wPa|K&iA z0t^iidRiDm5W>^b#+5|$WI7^#*ZFtx2Q(&N^=Yv_-efmV=HDR7&BcZ2>U~r~k6?XW z0~{Vf!0GE9QtlLI8*hr6E95`5+}*s0o_{`A{?yY$8>0xc-=N%F?S1UL-TohCU}lKZ z!V+kdyT2!eMD{+qG@&V8b|A&0K#U$rAB{oFBF#V;S35U*qCL_d@`p3R=pykL8skND zv2`XM$)`nh^(6lDK7#NFBnA({+t?DFA%7q}Eo0hf&`?*Bj}3` + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef KFILEMETADATA_EMBEDDEDIMAGEDATA_H +#define KFILEMETADATA_EMBEDDEDIMAGEDATA_H + +#include "kfilemetadata_export.h" +#include + +namespace KFileMetaData { + +class KFILEMETADATA_EXPORT EmbeddedImageData { +public: + EmbeddedImageData(); + virtual ~EmbeddedImageData(); + enum ImageType { + FrontCover = 0 + }; + QByteArray imageData(const QString &fileUrl, const QMimeDatabase &mimeDatabase, const ImageType imageType = FrontCover) const; + +private: + class Private; + Private *d; + + QByteArray getFrontCover(const QString &fileUrl, const QString &mimeType) const; + +}; + +} + +#endif // KFILEMETADATA_EMBEDDEDIMAGEDATA_H diff --git a/src/embeddedimagedata.cpp b/src/embeddedimagedata.cpp new file mode 100644 --- /dev/null +++ b/src/embeddedimagedata.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2018 Alexander Stippich + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "embeddedimagedata.h" +// Taglib includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace KFileMetaData; + +class Q_DECL_HIDDEN EmbeddedImageData::Private +{ +}; + +EmbeddedImageData::EmbeddedImageData() + : d(new Private) +{ +} + +EmbeddedImageData::~EmbeddedImageData() +{ + delete d; +} + +QByteArray EmbeddedImageData::imageData(const QString &fileUrl, const QMimeDatabase &mimeDatabase, const ImageType imageType) const +{ + QByteArray imageData; + + const auto &fileMimeType = mimeDatabase.mimeTypeForFile(fileUrl); + if (fileMimeType.name().startsWith(QStringLiteral("audio/"))) { + QString mimeType = fileMimeType.name(); + + if (imageType == FrontCover) { + imageData = getFrontCover(fileUrl,mimeType); + } + } + + return imageData; +} + +QByteArray EmbeddedImageData::getFrontCover(const QString &fileUrl, const QString &mimeType) const +{ + TagLib::FileStream stream(fileUrl.toUtf8().constData(), true); + if (!stream.isOpen()) { + qWarning() << "Unable to open file readonly: " << fileUrl; + return QByteArray(); + } + if ((mimeType == QStringLiteral("audio/mpeg")) + || (mimeType == QStringLiteral("audio/mpeg3")) + || (mimeType == QStringLiteral("audio/x-mpeg"))) { + + // Handling multiple tags in mpeg files. + TagLib::MPEG::File mpegFile(&stream, TagLib::ID3v2::FrameFactory::instance(), true); + if (!mpegFile.ID3v2Tag() || mpegFile.ID3v2Tag()->isEmpty()) { + return QByteArray();; + } + + TagLib::ID3v2::FrameList lstID3v2; + // Attached Front Picture. + lstID3v2 = mpegFile.ID3v2Tag()->frameListMap()["APIC"]; + if (!lstID3v2.isEmpty()) { + for (TagLib::ID3v2::FrameList::ConstIterator it = lstID3v2.begin(); it != lstID3v2.end(); ++it) { + TagLib::ID3v2::AttachedPictureFrame *frontCoverFrame = static_cast(*it); + if (frontCoverFrame->type() == frontCoverFrame->FrontCover) { + return QByteArray(frontCoverFrame->picture().data(), frontCoverFrame->picture().size()); + } + } + } + + } else if (mimeType == QStringLiteral("audio/mp4")) { + + TagLib::MP4::File mp4File(&stream, true); + if (!mp4File.tag() || mp4File.tag()->isEmpty()) { + return QByteArray(); + } + TagLib::MP4::Item coverArtItem = mp4File.tag()->item("covr"); + if (coverArtItem.isValid()) + { + TagLib::MP4::CoverArtList coverArtList = coverArtItem.toCoverArtList(); + TagLib::MP4::CoverArt& frontCover = coverArtList.front(); + return QByteArray(frontCover.data().data(), frontCover.data().size()); + } + + } else if (mimeType == QStringLiteral("audio/x-musepack")) { + + TagLib::MPC::File mpcFile(&stream, true); + if (!mpcFile.tag() || mpcFile.tag()->isEmpty()) { + return QByteArray(); + } + + TagLib::APE::ItemListMap lstMusepack = mpcFile.APETag()->itemListMap(); + TagLib::APE::ItemListMap::ConstIterator itMPC; + + /* The cover art tag for APEv2 tags starts with the filename as string + * with zero termination followed by the actual picture data */ + itMPC = lstMusepack.find("COVER ART (FRONT)"); + if (itMPC != lstMusepack.end()) { + TagLib::ByteVector pictureData = (*itMPC).second.binaryData(); + int dataPosition = pictureData.find('\0') + 1; + return QByteArray(pictureData.data() + dataPosition, pictureData.size() - dataPosition); + } + + + } else if (mimeType == QStringLiteral("audio/flac")) { + + TagLib::FLAC::File flacFile(&stream, TagLib::ID3v2::FrameFactory::instance(), true); + TagLib::List lstPic = flacFile.pictureList(); + + if (!lstPic.isEmpty()) { + for (TagLib::List::Iterator it = lstPic.begin(); it != lstPic.end(); ++it) { + TagLib::FLAC::Picture *pictureFlac = *it; + if (pictureFlac->type() == pictureFlac->FrontCover) { + return QByteArray(pictureFlac->data().data(),pictureFlac->data().size()); + } + } + } + + } else { + + TagLib::List lstPic; + if (mimeType == QStringLiteral("audio/ogg") || mimeType == QStringLiteral("audio/x-vorbis+ogg")) { + TagLib::Ogg::Vorbis::File oggFile(&stream, true); + if (oggFile.tag() && !oggFile.tag()->isEmpty()) { + lstPic = oggFile.tag()->pictureList(); + } + } + if (mimeType == QStringLiteral("audio/opus") || mimeType == QStringLiteral("audio/x-opus+ogg")) { + TagLib::Ogg::Opus::File opusFile(&stream, true); + if (opusFile.tag() && !opusFile.tag()->isEmpty()) { + lstPic = opusFile.tag()->pictureList(); + } + } + // Attached Picture. + if (!lstPic.isEmpty()) { + for (TagLib::List::Iterator it = lstPic.begin(); it != lstPic.end(); ++it) { + TagLib::FLAC::Picture *picture = *it; + if (picture->type() == picture->FrontCover) { + return QByteArray(picture->data().data(),picture->data().size()); + } + } + } + } + return QByteArray(); +} diff --git a/src/extractors/taglibextractor.h b/src/extractors/taglibextractor.h --- a/src/extractors/taglibextractor.h +++ b/src/extractors/taglibextractor.h @@ -62,6 +62,7 @@ TagLib::StringList genres; QVariant discNumber; QVariant opus; + QVariant embeddedPicture; }; void extractMP3(TagLib::FileStream& stream, ExtractedData& data); void extractMP4(TagLib::FileStream& stream, ExtractedData& data); diff --git a/src/extractors/taglibextractor.cpp b/src/extractors/taglibextractor.cpp --- a/src/extractors/taglibextractor.cpp +++ b/src/extractors/taglibextractor.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -208,6 +209,17 @@ data.compilation += (*it)->toString(); } } + + // Attached Front Picture. + lstID3v2 = mpegFile.ID3v2Tag()->frameListMap()["APIC"]; + if (!lstID3v2.isEmpty()) { + for (TagLib::ID3v2::FrameList::ConstIterator it = lstID3v2.begin(); it != lstID3v2.end(); ++it) { + TagLib::ID3v2::AttachedPictureFrame *frontCoverFrame = static_cast(*it); + if (frontCoverFrame->type() == frontCoverFrame->FrontCover) { + data.embeddedPicture = QByteArray(frontCoverFrame->picture().data(), frontCoverFrame->picture().size()); + } + } + } //TODO handle TIPL tag } @@ -250,6 +262,13 @@ if (itComposers != allTags.end()) { data.composers = itComposers->second.toStringList().toString(", "); } + + TagLib::MP4::ItemListMap::Iterator itCoverArt = allTags.find("covr"); + if (itCoverArt != allTags.end()) { + TagLib::MP4::CoverArtList coverArtList = allTags["covr"].toCoverArtList(); + TagLib::MP4::CoverArt& frontCover = coverArtList.front(); + data.embeddedPicture = QByteArray(frontCover.data().data(), frontCover.data().size()); + } } void TagLibExtractor::extractMusePack(TagLib::FileStream& stream, ExtractedData& data) @@ -409,26 +428,39 @@ void TagLibExtractor::extractOgg(TagLib::FileStream& stream, const QString& mimeType, ExtractedData& data) { TagLib::Ogg::FieldListMap lstOgg; + TagLib::List lstPic; if (mimeType == QStringLiteral("audio/flac")) { TagLib::FLAC::File flacFile(&stream, TagLib::ID3v2::FrameFactory::instance(), true); if (flacFile.xiphComment() && !flacFile.xiphComment()->isEmpty()) { lstOgg = flacFile.xiphComment()->fieldListMap(); + lstPic = flacFile.pictureList(); + // Attached Picture is different for FLAC. + if (!lstPic.isEmpty()) { + for (TagLib::List::Iterator it = lstPic.begin(); it != lstPic.end(); ++it) { + TagLib::FLAC::Picture *pictureFlac = *it; + if (pictureFlac->type() == pictureFlac->FrontCover) { + data.embeddedPicture = QByteArray(pictureFlac->data().data(),pictureFlac->data().size()); + } + } + } } } // Vorbis files. if (mimeType == QStringLiteral("audio/ogg") || mimeType == QStringLiteral("audio/x-vorbis+ogg")) { TagLib::Ogg::Vorbis::File oggFile(&stream, true); if (oggFile.tag() && !oggFile.tag()->isEmpty()) { lstOgg = oggFile.tag()->fieldListMap(); + lstPic = oggFile.tag()->pictureList(); } } if (mimeType == QStringLiteral("audio/opus") || mimeType == QStringLiteral("audio/x-opus+ogg")) { TagLib::Ogg::Opus::File opusFile(&stream, true); if (opusFile.tag() && !opusFile.tag()->isEmpty()) { lstOgg = opusFile.tag()->fieldListMap(); + lstPic = opusFile.tag()->pictureList(); } } @@ -578,6 +610,16 @@ if (itOgg != lstOgg.end()) { data.opus = (*itOgg).second.toString("").toInt(); } + + // Attached Picture. + if (!data.embeddedPicture.isValid() && !lstPic.isEmpty()) { + for (TagLib::List::Iterator it = lstPic.begin(); it != lstPic.end(); ++it) { + TagLib::FLAC::Picture *picture = *it; + if (picture->type() == picture->FrontCover) { + data.embeddedPicture = QByteArray(picture->data().data(),picture->data().size()); + } + } + } } } @@ -767,6 +809,10 @@ result->add(Property::DiscNumber, data.discNumber); } + if (data.embeddedPicture.isValid()) { + result->add(Property::EmbeddedPicture, data.embeddedPicture); + } + TagLib::AudioProperties* audioProp = file.audioProperties(); if (audioProp) { if (audioProp->length()) { diff --git a/src/properties.h b/src/properties.h --- a/src/properties.h +++ b/src/properties.h @@ -316,6 +316,11 @@ */ License, + /** + * Contains data of an embedded image file, e.g. an album cover. + */ + EmbeddedPicture, + PropertyCount, LastProperty = PropertyCount-1, diff --git a/src/propertyinfo.cpp b/src/propertyinfo.cpp --- a/src/propertyinfo.cpp +++ b/src/propertyinfo.cpp @@ -413,6 +413,12 @@ d->valueType = QVariant::Int; break; + case Property::EmbeddedPicture: + d->name = QStringLiteral("embeddedPicture"); + d->displayName = i18nc("@label", "Embedded Picture"); + d->valueType = QVariant::ByteArray; + break; + case Property::Width: d->name = QStringLiteral("width"); d->displayName = i18nc("@label", "Width"); @@ -591,6 +597,7 @@ { QStringLiteral("copyright"), Property::Copyright }, { QStringLiteral("publisher"), Property::Publisher }, { QStringLiteral("label"), Property::Label }, + { QStringLiteral("embeddedpicture"), Property::EmbeddedPicture }, { QStringLiteral("compilation"), Property::Compilation }, { QStringLiteral("license"), Property::License }, { QStringLiteral("creationdate"), Property::CreationDate },