Support for ASF embedded pictures

Patch by Anton Sergunov <setosha@gmail.com>

CCMAIL: setosha@gmail.com


git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@1214788 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
This commit is contained in:
Lukáš Lalinský 2011-01-16 11:57:12 +00:00
parent 393e9c5572
commit fb3cca2c44
14 changed files with 541 additions and 9 deletions

View File

@ -150,6 +150,7 @@ asf/asftag.cpp
asf/asffile.cpp
asf/asfproperties.cpp
asf/asfattribute.cpp
asf/asfpicture.cpp
)
ELSE(WITH_ASF)
SET(asf_SRCS)

View File

@ -1 +1 @@
INSTALL( FILES asffile.h asfproperties.h asftag.h asfattribute.h DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
INSTALL( FILES asffile.h asfproperties.h asftag.h asfattribute.h asfpicture.h DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)

View File

@ -6,7 +6,7 @@ INCLUDES = \
noinst_LTLIBRARIES = libasf.la
libasf_la_SOURCES = asfattribute.cpp asffile.cpp asfproperties.cpp asftag.cpp
libasf_la_SOURCES = asfattribute.cpp asffile.cpp asfproperties.cpp asftag.cpp asfpicture.cpp
taglib_include_HEADERS = asfattribute.h asffile.h asfproperties.h asftag.h
taglib_include_HEADERS = asfattribute.h asffile.h asfproperties.h asftag.h asfpicture.h
taglib_includedir = $(includedir)/taglib

View File

@ -40,11 +40,13 @@ class ASF::Attribute::AttributePrivate : public RefCounter
{
public:
AttributePrivate()
: stream(0),
: pictureValue(ASF::Picture::fromInvalid()),
stream(0),
language(0) {}
AttributeTypes type;
String stringValue;
ByteVector byteVectorValue;
ASF::Picture pictureValue;
union {
unsigned int intValue;
unsigned short shortValue;
@ -101,6 +103,13 @@ ASF::Attribute::Attribute(const ByteVector &value)
d->byteVectorValue = value;
}
ASF::Attribute::Attribute(const ASF::Picture &value)
{
d = new AttributePrivate;
d->type = BytesType;
d->pictureValue = value;
}
ASF::Attribute::Attribute(unsigned int value)
{
d = new AttributePrivate;
@ -144,6 +153,8 @@ ASF::Attribute::toString() const
ByteVector
ASF::Attribute::toByteVector() const
{
if(d->pictureValue.isValid())
return d->pictureValue.render();
return d->byteVectorValue;
}
@ -171,12 +182,18 @@ ASF::Attribute::toULongLong() const
return d->longLongValue;
}
ASF::Picture
ASF::Attribute::toPicture() const
{
return d->pictureValue;
}
String
ASF::Attribute::parse(ASF::File &f, int kind)
{
int size, nameLength;
uint size, nameLength;
String name;
d->pictureValue = Picture::fromInvalid();
// extended content descriptor
if(kind == 0) {
nameLength = f.readWORD();
@ -234,6 +251,13 @@ ASF::Attribute::parse(ASF::File &f, int kind)
break;
}
if(d->type == BytesType && name == "WM/Picture") {
d->pictureValue.parse(d->byteVectorValue);
if(d->pictureValue.isValid()) {
d->byteVectorValue.clear();
}
}
return name;
}
@ -252,6 +276,8 @@ ASF::Attribute::dataSize() const
case UnicodeType:
return d->stringValue.size() * 2 + 2;
case BytesType:
if(d->pictureValue.isValid())
return d->pictureValue.dataSize();
case GuidType:
return d->byteVectorValue.size();
}
@ -290,6 +316,10 @@ ASF::Attribute::render(const String &name, int kind) const
break;
case BytesType:
if(d->pictureValue.isValid()) {
data.append(d->pictureValue.render());
break;
}
case GuidType:
data.append(d->byteVectorValue);
break;

View File

@ -29,6 +29,7 @@
#include "tstring.h"
#include "tbytevector.h"
#include "taglib_export.h"
#include "asfpicture.h"
namespace TagLib
{
@ -37,6 +38,7 @@ namespace TagLib
{
class File;
class Picture;
class TAGLIB_EXPORT Attribute
{
@ -68,7 +70,20 @@ namespace TagLib
/*!
* Constructs an attribute with \a key and a BytesType \a value.
*/
Attribute(const ByteVector &value);
Attribute(const ByteVector &value);
/*!
* Constructs an attribute with \a key and a Picture \a value.
*
* This attribute is compatible with the ID3 frame, APIC. The ID3 specification for the APIC frame stipulates that,
* while there may be any number of APIC frames associated with a file,
* only one may be of type 1 and only one may be of type 2.
*
* The specification also states that the description of the picture can be no longer than 64 characters, but can be empty.
* WM/Picture attributes added with TagLib::ASF are not automatically validated to conform to ID3 specifications.
* You must add code in your application to perform validations if you want to maintain complete compatibility with ID3.
*/
Attribute(const Picture &value);
/*!
* Constructs an attribute with \a key and a DWordType \a value.
@ -140,6 +155,11 @@ namespace TagLib
*/
ByteVector toByteVector() const;
/*!
* Returns the Picture \a value.
*/
Picture toPicture() const;
/*!
* Returns the language number, or 0 is no stream number was set.
*/
@ -176,7 +196,6 @@ namespace TagLib
class AttributePrivate;
AttributePrivate *d;
};
}
}

View File

@ -518,7 +518,7 @@ int ASF::File::readBYTE()
int ASF::File::readWORD()
{
ByteVector v = readBlock(2);
return v.toShort(false);
return v.toUShort(false);
}
unsigned int ASF::File::readDWORD()

View File

@ -97,6 +97,7 @@ namespace TagLib {
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
friend class Attribute;
friend class Picture;
class BaseObject;
class UnknownObject;

185
taglib/asf/asfpicture.cpp Normal file
View File

@ -0,0 +1,185 @@
/**************************************************************************
copyright : (C) 2005-2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
* USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WITH_ASF
#include <taglib.h>
#include <tdebug.h>
#include "asfattribute.h"
#include "asffile.h"
#include "asfpicture.h"
using namespace TagLib;
class ASF::Picture::PicturePriavte : public RefCounter
{
public:
bool valid;
Type type;
String mimeType;
String description;
ByteVector picture;
};
////////////////////////////////////////////////////////////////////////////////
// Picture class members
////////////////////////////////////////////////////////////////////////////////
ASF::Picture::Picture()
{
d = new PicturePriavte();
d->valid = true;
}
ASF::Picture::Picture(const Picture& other)
: d(other.d)
{
d->ref();
}
ASF::Picture::~Picture()
{
if(d->deref())
delete d;
}
bool ASF::Picture::isValid() const
{
return d->valid;
}
String ASF::Picture::mimeType() const
{
return d->mimeType;
}
void ASF::Picture::setMimeType(const String &value)
{
d->mimeType = value;
}
ASF::Picture::Type ASF::Picture::type() const
{
return d->type;
}
void ASF::Picture::setType(const ASF::Picture::Type& t)
{
d->type = t;
}
String ASF::Picture::description() const
{
return d->description;
}
void ASF::Picture::setDescription(const String &desc)
{
d->description = desc;
}
ByteVector ASF::Picture::picture() const
{
return d->picture;
}
void ASF::Picture::setPicture(const ByteVector &p)
{
d->picture = p;
}
int ASF::Picture::dataSize() const
{
return
9 + (d->mimeType.length() + d->description.length()) * 2 +
d->picture.size();
}
ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other)
{
if(other.d != d) {
if(d->deref())
delete d;
d = other.d;
d->ref();
}
return *this;
}
ByteVector ASF::Picture::render() const
{
if(!isValid())
return ByteVector::null;
return
ByteVector((char)d->type) +
ByteVector::fromUInt(d->picture.size(), false) +
ASF::File::renderString(d->mimeType) +
ASF::File::renderString(d->description) +
d->picture;
}
void ASF::Picture::parse(const ByteVector& bytes)
{
d->valid = false;
if(bytes.size() < 9)
return;
int pos = 0;
d->type = (Type)bytes[0]; ++pos;
uint dataLen = bytes.mid(pos, 4).toUInt(false); pos+=4;
const ByteVector nullStringTerminator(2, 0);
int endPos = bytes.find(nullStringTerminator, pos, 2);
if(endPos < 0)
return;
d->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
pos = endPos+2;
endPos = bytes.find(nullStringTerminator, pos, 2);
if(endPos < 0)
return;
d->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
pos = endPos+2;
if(dataLen + pos != bytes.size())
return;
d->picture = bytes.mid(pos, dataLen);
d->valid = true;
return;
}
ASF::Picture ASF::Picture::fromInvalid()
{
Picture ret;
ret.d->valid = false;
return ret;
}
#endif

192
taglib/asf/asfpicture.h Normal file
View File

@ -0,0 +1,192 @@
#ifndef ASFPICTURE_H
#define ASFPICTURE_H
#include "tstring.h"
#include "tbytevector.h"
#include "taglib_export.h"
#include "attachedpictureframe.h"
namespace TagLib
{
namespace ASF
{
//! An ASF attached picture interface implementation
/*!
* This is an implementation of ASF attached pictures interface. Pictures may be
* included in attributes, one per WM/Picture attribute (but there may be multiple WM/Picture
* attribute in a single tag). These pictures are usually in either JPEG or
* PNG format.
* \see Attribute::toPicture()
* \see Attribute::Attribute(const Picture& picture)
*/
class TAGLIB_EXPORT Picture {
public:
/*!
* This describes the function or content of the picture.
*/
enum Type {
//! A type not enumerated below
Other = 0x00,
//! 32x32 PNG image that should be used as the file icon
FileIcon = 0x01,
//! File icon of a different size or format
OtherFileIcon = 0x02,
//! Front cover image of the album
FrontCover = 0x03,
//! Back cover image of the album
BackCover = 0x04,
//! Inside leaflet page of the album
LeafletPage = 0x05,
//! Image from the album itself
Media = 0x06,
//! Picture of the lead artist or soloist
LeadArtist = 0x07,
//! Picture of the artist or performer
Artist = 0x08,
//! Picture of the conductor
Conductor = 0x09,
//! Picture of the band or orchestra
Band = 0x0A,
//! Picture of the composer
Composer = 0x0B,
//! Picture of the lyricist or text writer
Lyricist = 0x0C,
//! Picture of the recording location or studio
RecordingLocation = 0x0D,
//! Picture of the artists during recording
DuringRecording = 0x0E,
//! Picture of the artists during performance
DuringPerformance = 0x0F,
//! Picture from a movie or video related to the track
MovieScreenCapture = 0x10,
//! Picture of a large, coloured fish
ColouredFish = 0x11,
//! Illustration related to the track
Illustration = 0x12,
//! Logo of the band or performer
BandLogo = 0x13,
//! Logo of the publisher (record company)
PublisherLogo = 0x14
};
/*!
* Constructs an empty picture.
*/
Picture();
/*!
* Construct an picture as a copy of \a other.
*/
Picture(const Picture& other);
/*!
* Destroys the picture.
*/
virtual ~Picture();
/*!
* Copies the contents of \a other into this picture.
*/
Picture& operator=(const Picture& other);
/*!
* Returns true if Picture stores valid picture
*/
bool isValid() const;
/*!
* Returns the mime type of the image. This should in most cases be
* "image/png" or "image/jpeg".
* \see setMimeType(const String &)
* \see picture()
* \see setPicture(const ByteArray&)
*/
String mimeType() const;
/*!
* Sets the mime type of the image. This should in most cases be
* "image/png" or "image/jpeg".
* \see setMimeType(const String &)
* \see picture()
* \see setPicture(const ByteArray&)
*/
void setMimeType(const String &value);
/*!
* Returns the type of the image.
*
* \see Type
* \see setType()
*/
Type type() const;
/*!
* Sets the type for the image.
*
* \see Type
* \see type()
*/
void setType(const ASF::Picture::Type& t);
/*!
* Returns a text description of the image.
*
* \see setDescription()
*/
String description() const;
/*!
* Sets a textual description of the image to \a desc.
*
* \see description()
*/
void setDescription(const String &desc);
/*!
* Returns the image data as a ByteVector.
*
* \note ByteVector has a data() method that returns a const char * which
* should make it easy to export this data to external programs.
*
* \see setPicture()
* \see mimeType()
*/
ByteVector picture() const;
/*!
* Sets the image data to \a p. \a p should be of the type specified in
* this frame's mime-type specification.
*
* \see picture()
* \see mimeType()
* \see setMimeType()
*/
void setPicture(const ByteVector &p);
/*!
* Returns picture as binary raw data \a value
*/
ByteVector render() const;
/*!
* Returns picture as binary raw data \a value
*/
int dataSize() const;
#ifndef DO_NOT_DOCUMENT
/* THIS IS PRIVATE, DON'T TOUCH IT! */
void parse(const ByteVector& );
static Picture fromInvalid();
friend class Attribute;
#endif
private:
struct PicturePriavte;
PicturePriavte *d;
};
}
}
#endif // ASFPICTURE_H

View File

@ -63,6 +63,7 @@ HEADERS += audioproperties.h \
ape/apetag.h \
flac/flacfile.h \
flac/flacproperties.h \
flac/flacpicture.h \
mpc/mpcfile.h \
mpc/mpcproperties.h \
mp4/mp4atom.h \
@ -124,10 +125,12 @@ SOURCES += ape/apefooter.cpp \
asf/asffile.cpp \
asf/asfproperties.cpp \
asf/asftag.cpp \
asf/asfpicture.cpp \
audioproperties.cpp \
fileref.cpp \
flac/flacfile.cpp \
flac/flacproperties.cpp \
flac/flacpicture.cpp \
mp4/mp4atom.cpp \
mp4/mp4coverart.cpp \
mp4/mp4file.cpp \
@ -197,6 +200,7 @@ FRAMEWORK_HEADERS.files = \
asf/asffile.h \
asf/asfproperties.h \
asf/asftag.h \
asf/asfpicture.h \
audioproperties.h \
fileref.h \
flac/flacfile.h \

View File

@ -565,6 +565,11 @@ short ByteVector::toShort(bool mostSignificantByteFirst) const
return toNumber<unsigned short>(d->data, mostSignificantByteFirst);
}
unsigned short ByteVector::toUShort(bool mostSignificantByteFirst) const
{
return toNumber<unsigned short>(d->data, mostSignificantByteFirst);
}
long long ByteVector::toLongLong(bool mostSignificantByteFirst) const
{
return toNumber<unsigned long long>(d->data, mostSignificantByteFirst);

View File

@ -266,6 +266,17 @@ namespace TagLib {
*/
short toShort(bool mostSignificantByteFirst = true) const;
/*!
* Converts the first 2 bytes of the vector to a unsigned short.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
* true then $00 $01 == 0x0001 == 1, if false, $01 00 == 0x01000000 == 1.
*
* \see fromShort()
*/
unsigned short toUShort(bool mostSignificantByteFirst = true) const;
/*!
* Converts the first 8 bytes of the vector to a (signed) long long.
*

View File

@ -20,6 +20,8 @@ class TestASF : public CppUnit::TestFixture
CPPUNIT_TEST(testSaveLanguage);
CPPUNIT_TEST(testDWordTrackNumber);
CPPUNIT_TEST(testSaveLargeValue);
CPPUNIT_TEST(testSavePicture);
CPPUNIT_TEST(testSaveMultiplePictures);
CPPUNIT_TEST_SUITE_END();
public:
@ -141,6 +143,78 @@ public:
delete f;
}
void testSavePicture()
{
ScopedFileCopy copy("silence-1", ".wma");
string newname = copy.fileName();
ASF::File *f = new ASF::File(newname.c_str());
ASF::AttributeList values;
ASF::Picture picture;
picture.setMimeType("image/jpeg");
picture.setType(ASF::Picture::FrontCover);
picture.setDescription("description");
picture.setPicture("data");
ASF::Attribute attr(picture);
values.append(attr);
f->tag()->attributeListMap()["WM/Picture"] = values;
f->save();
delete f;
f = new ASF::File(newname.c_str());
ASF::AttributeList values2 = f->tag()->attributeListMap()["WM/Picture"];
CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), values2.size());
ASF::Attribute attr2 = values2.front();
ASF::Picture picture2 = attr2.toPicture();
CPPUNIT_ASSERT(picture2.isValid());
CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), picture2.mimeType());
CPPUNIT_ASSERT_EQUAL(ASF::Picture::FrontCover, picture2.type());
CPPUNIT_ASSERT_EQUAL(String("description"), picture2.description());
CPPUNIT_ASSERT_EQUAL(ByteVector("data"), picture2.picture());
delete f;
}
void testSaveMultiplePictures()
{
ScopedFileCopy copy("silence-1", ".wma");
string newname = copy.fileName();
ASF::File *f = new ASF::File(newname.c_str());
ASF::AttributeList values;
ASF::Picture picture;
picture.setMimeType("image/jpeg");
picture.setType(ASF::Picture::FrontCover);
picture.setDescription("description");
picture.setPicture("data");
values.append(ASF::Attribute(picture));
ASF::Picture picture2;
picture2.setMimeType("image/png");
picture2.setType(ASF::Picture::BackCover);
picture2.setDescription("back cover");
picture2.setPicture("PNG data");
values.append(ASF::Attribute(picture2));
f->tag()->attributeListMap()["WM/Picture"] = values;
f->save();
delete f;
f = new ASF::File(newname.c_str());
ASF::AttributeList values2 = f->tag()->attributeListMap()["WM/Picture"];
CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), values2.size());
ASF::Picture picture3 = values2[1].toPicture();
CPPUNIT_ASSERT(picture3.isValid());
CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), picture3.mimeType());
CPPUNIT_ASSERT_EQUAL(ASF::Picture::FrontCover, picture3.type());
CPPUNIT_ASSERT_EQUAL(String("description"), picture3.description());
CPPUNIT_ASSERT_EQUAL(ByteVector("data"), picture3.picture());
ASF::Picture picture4 = values2[0].toPicture();
CPPUNIT_ASSERT(picture4.isValid());
CPPUNIT_ASSERT_EQUAL(String("image/png"), picture4.mimeType());
CPPUNIT_ASSERT_EQUAL(ASF::Picture::BackCover, picture4.type());
CPPUNIT_ASSERT_EQUAL(String("back cover"), picture4.description());
CPPUNIT_ASSERT_EQUAL(ByteVector("PNG data"), picture4.picture());
delete f;
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestASF);

View File

@ -38,6 +38,7 @@ class TestByteVector : public CppUnit::TestFixture
CPPUNIT_TEST(testRfind1);
CPPUNIT_TEST(testRfind2);
CPPUNIT_TEST(testToHex);
CPPUNIT_TEST(testToUShort);
CPPUNIT_TEST_SUITE_END();
public:
@ -181,6 +182,15 @@ public:
CPPUNIT_ASSERT_EQUAL(ByteVector("f0e1d2c3b4a5968778695a4b3c2d1e0f"), v.toHex());
}
void testToUShort()
{
CPPUNIT_ASSERT_EQUAL((unsigned short)0xFFFF, ByteVector("\xff\xff", 2).toUShort());
CPPUNIT_ASSERT_EQUAL((unsigned short)0x0001, ByteVector("\x00\x01", 2).toUShort());
CPPUNIT_ASSERT_EQUAL((unsigned short)0x0100, ByteVector("\x00\x01", 2).toUShort(false));
CPPUNIT_ASSERT_EQUAL((unsigned short)0xFF01, ByteVector("\xFF\x01", 2).toUShort());
CPPUNIT_ASSERT_EQUAL((unsigned short)0x01FF, ByteVector("\xFF\x01", 2).toUShort(false));
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestByteVector);