2
* \file pictureframe.cpp
3
* Frame containing picture.
9
* Copyright (C) 2008-2009 Urs Fleisch
11
* This file is part of Kid3.
13
* Kid3 is free software; you can redistribute it and/or modify
14
* it under the terms of the GNU General Public License as published by
15
* the Free Software Foundation; either version 2 of the License, or
16
* (at your option) any later version.
18
* Kid3 is distributed in the hope that it will be useful,
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
* GNU General Public License for more details.
23
* You should have received a copy of the GNU General Public License
24
* along with this program. If not, see <http://www.gnu.org/licenses/>.
27
#include "pictureframe.h"
31
#if QT_VERSION < 0x040000 && defined CONFIG_USE_KDE
34
#include "qtcompatmac.h"
39
* @param data binary picture data
40
* @param description description
41
* @param pictureType picture type
42
* @param mimeType MIME type
43
* @param enc text encoding
44
* @param imgFormat image format
46
PictureFrame::PictureFrame(
47
const QByteArray& data,
48
const QString& description,
49
PictureType pictureType,
50
const QString& mimeType,
51
Field::TextEncoding enc,
52
const QString& imgFormat)
55
setFields(*this, enc, imgFormat, mimeType, pictureType, description, data);
61
* @param frame general frame
63
PictureFrame::PictureFrame(const Frame& frame)
65
*(static_cast<Frame*>(this)) = frame;
68
// Make sure all fields are available in the correct order
69
Field::TextEncoding enc = Field::TE_ISO8859_1;
70
PictureType pictureType = PT_CoverFront;
71
QString imgFormat("JPG"), mimeType("image/jpeg"), description;
73
getFields(*this, enc, imgFormat, mimeType, pictureType, description, data);
74
setFields(*this, enc, imgFormat, mimeType, pictureType, description, data);
80
PictureFrame::~PictureFrame()
87
* @param frame frame to set
88
* @param enc text encoding
89
* @param imgFormat image format
90
* @param mimeType MIME type
91
* @param pictureType picture type
92
* @param description description
93
* @param data binary picture data
95
void PictureFrame::setFields(Frame& frame,
96
Field::TextEncoding enc, const QString& imgFormat,
97
const QString& mimeType, PictureType pictureType,
98
const QString& description, const QByteArray& data)
101
FieldList& fields = frame.fieldList();
104
field.m_id = Field::ID_TextEnc;
106
fields.push_back(field);
108
field.m_id = Field::ID_ImageFormat;
109
field.m_value = imgFormat;
110
fields.push_back(field);
112
field.m_id = Field::ID_MimeType;
113
field.m_value = mimeType;
114
fields.push_back(field);
116
field.m_id = Field::ID_PictureType;
117
field.m_value = pictureType;
118
fields.push_back(field);
120
field.m_id = Field::ID_Description;
121
field.m_value = description;
122
fields.push_back(field);
124
field.m_id = Field::ID_Data;
125
field.m_value = data;
126
fields.push_back(field);
128
frame.setValue(description);
132
* Get all properties.
133
* Unavailable fields are not set.
135
* @param frame frame to get
136
* @param enc text encoding
137
* @param imgFormat image format
138
* @param mimeType MIME type
139
* @param pictureType picture type
140
* @param description description
141
* @param data binary picture data
143
void PictureFrame::getFields(const Frame& frame,
144
Field::TextEncoding& enc, QString& imgFormat,
145
QString& mimeType, PictureType& pictureType,
146
QString& description, QByteArray& data)
148
for (Frame::FieldList::const_iterator it = frame.getFieldList().begin();
149
it != frame.getFieldList().end();
151
switch ((*it).m_id) {
152
case Field::ID_TextEnc:
153
enc = static_cast<Field::TextEncoding>((*it).m_value.toInt());
155
case Field::ID_ImageFormat:
156
imgFormat = (*it).m_value.toString();
158
case Field::ID_MimeType:
159
mimeType = (*it).m_value.toString();
161
case Field::ID_PictureType:
162
pictureType = static_cast<PictureType>((*it).m_value.toInt());
164
case Field::ID_Description:
165
description = (*it).m_value.toString();
168
data = (*it).m_value.toByteArray();
171
qDebug("Unknown picture field ID");
177
* Set value of a field.
179
* @param frame frame to set
181
* @param value field value
183
* @return true if field found and set.
185
bool PictureFrame::setField(Frame& frame, Field::Id id, const QVariant& value)
187
for (Frame::FieldList::iterator it = frame.fieldList().begin();
188
it != frame.fieldList().end();
190
if ((*it).m_id == id) {
191
(*it).m_value = value;
192
if (id == Field::ID_Description) frame.setValue(value.toString());
200
* Get value of a field.
202
* @param frame frame to get
205
* @return field value, invalid if not found.
207
QVariant PictureFrame::getField(const Frame& frame, Field::Id id)
210
if (!frame.getFieldList().empty()) {
211
for (Frame::FieldList::const_iterator it = frame.getFieldList().begin();
212
it != frame.getFieldList().end();
214
if ((*it).m_id == id) {
215
result = (*it).m_value;
226
* @param frame frame to set
227
* @param enc text encoding
229
* @return true if field found and set.
231
bool PictureFrame::setTextEncoding(Frame& frame, Field::TextEncoding enc)
233
return setField(frame, Field::ID_TextEnc, enc);
239
* @param frame frame to get
240
* @param enc the text encoding is returned here
242
* @return true if field found.
244
bool PictureFrame::getTextEncoding(const Frame& frame, Field::TextEncoding& enc)
246
QVariant var(getField(frame, Field::ID_TextEnc));
248
enc = static_cast<Field::TextEncoding>(var.toInt());
257
* @param frame frame to set
258
* @param imgFormat image format
260
* @return true if field found and set.
262
bool PictureFrame::setImageFormat(Frame& frame, const QString& imgFormat)
264
return setField(frame, Field::ID_ImageFormat, imgFormat);
270
* @param frame frame to get
271
* @param imgFormat the image format is returned here
273
* @return true if field found.
275
bool PictureFrame::getImageFormat(const Frame& frame, QString& imgFormat)
277
QVariant var(getField(frame, Field::ID_ImageFormat));
279
imgFormat = var.toString();
288
* @param frame frame to set
289
* @param mimeType MIME type
291
* @return true if field found and set.
293
bool PictureFrame::setMimeType(Frame& frame, const QString& mimeType)
295
return setField(frame, Field::ID_MimeType, mimeType);
301
* @param frame frame to get
302
* @param mimeType the MIME type is returned here
304
* @return true if field found.
306
bool PictureFrame::getMimeType(const Frame& frame, QString& mimeType)
308
QVariant var(getField(frame, Field::ID_MimeType));
310
mimeType = var.toString();
319
* @param frame frame to set
320
* @param pictureType picture type
322
* @return true if field found and set.
324
bool PictureFrame::setPictureType(Frame& frame, PictureType pictureType)
326
return setField(frame, Field::ID_PictureType, pictureType);
332
* @param frame frame to get
333
* @param pictureType the picture type is returned here
335
* @return true if field found.
337
bool PictureFrame::getPictureType(const Frame& frame, PictureType& pictureType)
339
QVariant var(getField(frame, Field::ID_PictureType));
341
pictureType = static_cast<PictureType>(var.toInt());
350
* @param frame frame to set
351
* @param description description
353
* @return true if field found and set.
355
bool PictureFrame::setDescription(Frame& frame, const QString& description)
357
return setField(frame, Field::ID_Description, description);
363
* @param frame frame to get
364
* @param description the description is returned here
366
* @return true if field found.
368
bool PictureFrame::getDescription(const Frame& frame, QString& description)
370
QVariant var(getField(frame, Field::ID_Description));
372
description = var.toString();
381
* @param frame frame to set
382
* @param data binary data
384
* @return true if field found and set.
386
bool PictureFrame::setData(Frame& frame, const QByteArray& data)
388
return setField(frame, Field::ID_Data, data);
394
* @param frame frame to get
395
* @param data the binary data is returned here
397
* @return true if field found.
399
bool PictureFrame::getData(const Frame& frame, QByteArray& data)
401
QVariant var(getField(frame, Field::ID_Data));
403
data = var.toByteArray();
410
* Read binary data from file.
412
* @param frame frame to set
413
* @param fileName name of data file
415
* @return true if file read, field found and set.
417
bool PictureFrame::setDataFromFile(Frame& frame, const QString& fileName)
420
if (!fileName.isEmpty()) {
421
QFile file(fileName);
422
if (file.open(QCM_ReadOnly)) {
423
size_t size = file.size();
424
char* data = new char[size];
426
QDataStream stream(&file);
427
stream.QCM_readRawData(data, size);
429
QCM_duplicate(ba, data, size);
430
result = setData(frame, ba);
440
* Get binary data from image.
442
* @param frame frame to set
445
* @return true if field found and set.
447
bool PictureFrame::setDataFromImage(Frame& frame, const QImage& image)
450
#if QT_VERSION >= 0x040000
455
buffer.open(QCM_WriteOnly);
456
image.save(&buffer, "JPG");
457
return setData(frame, ba);
461
* Save binary data to a file.
464
* @param fileName name of data file to save
466
* @return true if field found and saved.
468
bool PictureFrame::writeDataToFile(const Frame& frame, const QString& fileName)
471
if (getData(frame, ba)) {
472
QFile file(fileName);
473
if (file.open(QCM_WriteOnly)) {
474
QDataStream stream(&file);
475
stream.QCM_writeRawData(ba.data(), ba.size());
484
* Set the MIME type and image format from the file name extension.
486
* @param frame frame to set
487
* @param fileName name of data file
489
* @return true if field found and set.
491
bool PictureFrame::setMimeTypeFromFileName(Frame& frame, const QString& fileName)
493
if (fileName.endsWith(".jpg", QCM_CaseInsensitive) ||
494
fileName.endsWith(".jpeg", QCM_CaseInsensitive)) {
495
return setMimeType(frame, "image/jpeg") && setImageFormat(frame, "JPG");
496
} else if (fileName.endsWith(".png", QCM_CaseInsensitive)) {
497
return setMimeType(frame, "image/png") && setImageFormat(frame, "PNG");
502
#ifdef HAVE_BASE64_ENCODING
504
* Get a 32-bit number from a byte array stored in big-endian order.
506
* @param data byte array
507
* @param index index of first byte in data
509
* @return big endian 32-bit value.
511
static unsigned long getBigEndianULongFromByteArray(const QByteArray& data,
515
((unsigned char)data[index + 3] & 0xff) |
516
(((unsigned char)data[index + 2] & 0xff) << 8) |
517
(((unsigned char)data[index + 1] & 0xff) << 16) |
518
(((unsigned char)data[index + 0] & 0xff) << 24);
522
* Render a 32-bit number to a byte array in big-endian order.
524
* @param value 32-bit value
525
* @param data byte array
526
* @param index index of first byte in data
528
static void renderBigEndianULongToByteArray(unsigned long value,
529
QByteArray& data, int index)
531
data[index + 3] = value & 0xff;
533
data[index + 2] = value & 0xff;
535
data[index + 1] = value & 0xff;
537
data[index + 0] = value & 0xff;
541
* Copy characters into a byte array.
543
* @param str source string
544
* @param data destination byte array
545
* @param index index of first byte in data
546
* @param len number of bytes to copy
548
static void renderCharsToByteArray(const char* str, QByteArray& data,
551
for (int i = 0; i < len; ++i) {
552
data[index++] = *str++;
557
* Set picture from a base64 string.
559
* @param frame frame to set
560
* @param base64Value base64 string
562
void PictureFrame::setFieldsFromBase64(Frame& frame, const QString& base64Value)
564
#if QT_VERSION >= 0x040000
565
QByteArray ba = QByteArray::fromBase64(base64Value.toAscii());
566
#elif defined CONFIG_USE_KDE
568
QCString baBase64(base64Value.ascii());
569
KCodecs::base64Decode(baBase64, ba);
571
PictureFrame::PictureType pictureType = PictureFrame::PT_CoverFront;
572
QString mimeType("image/jpeg");
573
QString description("");
574
if (frame.getName(true) == "METADATA_BLOCK_PICTURE") {
575
unsigned long baSize = static_cast<unsigned long>(ba.size());
576
if (baSize < 32) return;
578
pictureType = static_cast<PictureFrame::PictureType>(
579
getBigEndianULongFromByteArray(ba, index));
581
unsigned long mimeLen = getBigEndianULongFromByteArray(ba, index);
583
if (baSize < index + mimeLen + 24) return;
584
mimeType = QString::fromAscii(ba.data() + index, mimeLen);
586
unsigned long descLen = getBigEndianULongFromByteArray(ba, index);
588
if (baSize < index + descLen + 20) return;
589
description = QString::fromUtf8(ba.data() + index, descLen);
591
index += 16; // width, height, depth, number of colors
592
unsigned long picLen = getBigEndianULongFromByteArray(ba, index);
594
if (baSize < index + picLen) return;
595
#if QT_VERSION >= 0x040000
598
ba.duplicate(ba.data() + index, picLen);
601
PictureFrame::setFields(
602
frame, Frame::Field::TE_UTF8, "", mimeType,
603
pictureType, description, ba);
607
* Get picture to a base64 string.
609
* @param frame frame to get
610
* @param base64Value base64 string to set
612
void PictureFrame::getFieldsToBase64(const Frame& frame, QString& base64Value)
614
Frame::Field::TextEncoding enc;
615
PictureFrame::PictureType pictureType = PictureFrame::PT_CoverFront;
616
QString imgFormat, mimeType, description;
618
PictureFrame::getFields(frame, enc, imgFormat, mimeType,
619
pictureType, description, pic);
620
if (frame.getName(true) == "METADATA_BLOCK_PICTURE") {
621
QCM_QCString mimeStr = mimeType.QCM_toAscii();
622
QCM_QCString descStr = description.QCM_toUtf8();
623
int mimeLen = mimeStr.length();
624
int descLen = descStr.length();
625
int picLen = pic.size();
626
QByteArray ba(32 + mimeLen + descLen + picLen
627
#if QT_VERSION >= 0x040000
632
renderBigEndianULongToByteArray(pictureType, ba, index);
634
renderBigEndianULongToByteArray(mimeLen, ba, index);
636
renderCharsToByteArray(mimeStr, ba, index, mimeLen);
638
renderBigEndianULongToByteArray(descLen, ba, index);
640
renderCharsToByteArray(descStr, ba, index, descLen);
643
int width = 0, height = 0, depth = 0, numColors = 0;
645
if (image.loadFromData(pic)) {
646
width = image.width();
647
height = image.height();
648
depth = image.depth();
649
numColors = image.numColors();
651
renderBigEndianULongToByteArray(width, ba, index);
653
renderBigEndianULongToByteArray(height, ba, index);
655
renderBigEndianULongToByteArray(depth, ba, index);
657
renderBigEndianULongToByteArray(numColors, ba, index);
660
renderBigEndianULongToByteArray(picLen, ba, index);
662
renderCharsToByteArray(pic.data(), ba, index, picLen);
665
#if QT_VERSION >= 0x040000
666
base64Value = pic.toBase64();
667
#elif defined CONFIG_USE_KDE
668
QByteArray picBase64;
669
KCodecs::base64Encode(pic, picBase64);
670
base64Value = QString(picBase64);
673
#endif // HAVE_BASE64_ENCODING