From 3cfcceae43a152dbed336679733745c13e3d6228 Mon Sep 17 00:00:00 2001 From: Konstantin Ritt Date: Tue, 8 May 2012 14:15:04 +0300 Subject: [PATCH] QJpegHandler: add an embedded text support texts for the textKeys are stored each in a separate COM(ment) section so that the maximum text's size is almost 65KB Task-number: QTBUG-10568 Task-number: QTBUG-111 Change-Id: I7d693741e10e5d78d497cb0af448160077350bb2 Reviewed-by: aavit --- src/gui/image/qjpeghandler.cpp | 92 +++++++++++++++++++-- tests/auto/gui/image/qimagereader/images/txts.jpg | Bin 0 -> 2357 bytes tests/auto/gui/image/qimagereader/qimagereader.qrc | 1 + .../gui/image/qimagereader/tst_qimagereader.cpp | 56 ++++++++----- 4 files changed, 119 insertions(+), 30 deletions(-) create mode 100644 tests/auto/gui/image/qimagereader/images/txts.jpg diff --git a/src/gui/image/qjpeghandler.cpp b/src/gui/image/qjpeghandler.cpp index 7dcbcf5..c42977e 100644 --- a/src/gui/image/qjpeghandler.cpp +++ b/src/gui/image/qjpeghandler.cpp @@ -528,7 +528,40 @@ inline my_jpeg_destination_mgr::my_jpeg_destination_mgr(QIODevice *device) } -static bool write_jpeg_image(const QImage &image, QIODevice *device, int sourceQuality) +static inline void set_text(const QImage &image, j_compress_ptr cinfo, const QString &description) +{ + QMap text; + foreach (const QString &key, image.textKeys()) { + if (!key.isEmpty()) + text.insert(key, image.text(key)); + } + foreach (const QString &pair, description.split(QLatin1String("\n\n"))) { + int index = pair.indexOf(QLatin1Char(':')); + if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) { + QString s = pair.simplified(); + if (!s.isEmpty()) + text.insert(QLatin1String("Description"), s); + } else { + QString key = pair.left(index); + if (!key.simplified().isEmpty()) + text.insert(key, pair.mid(index + 2).simplified()); + } + } + if (text.isEmpty()) + return; + + for (QMap::ConstIterator it = text.constBegin(); it != text.constEnd(); ++it) { + QByteArray comment = it.key().toLatin1(); + if (!comment.isEmpty()) + comment += ": "; + comment += it.value().toLatin1(); + if (comment.length() > 65530) + comment.truncate(65530); + jpeg_write_marker(cinfo, JPEG_COM, (JOCTET *)comment.constData(), comment.size()); + } +} + +static bool write_jpeg_image(const QImage &image, QIODevice *device, int sourceQuality, const QString &description) { bool success = false; const QVector cmap = image.colorTable(); @@ -600,6 +633,8 @@ static bool write_jpeg_image(const QImage &image, QIODevice *device, int sourceQ jpeg_start_compress(&cinfo, true); #endif + set_text(image, &cinfo, description); + row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components]; int w = cinfo.image_width; while (cinfo.next_scanline < cinfo.image_height) { @@ -734,6 +769,9 @@ public: QSize scaledSize; QRect scaledClipRect; QRect clipRect; + QString description; + QStringList readTexts; + struct jpeg_decompress_struct info; struct my_jpeg_source_mgr * iod_src; struct my_error_mgr err; @@ -760,6 +798,8 @@ bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device) err.output_message = my_output_message; if (!setjmp(err.setjmp_buffer)) { + jpeg_save_markers(&info, JPEG_COM, 0xFFFF); + #if defined(Q_OS_UNIXWARE) (void) jpeg_read_header(&info, B_TRUE); #else @@ -773,6 +813,27 @@ bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device) format = QImage::Format_Invalid; read_jpeg_format(format, &info); + + for (jpeg_saved_marker_ptr marker = info.marker_list; marker != NULL; marker = marker->next) { + if (marker->marker == JPEG_COM) { + QString key, value; + QString s = QString::fromLatin1((const char *)marker->data, marker->data_length); + int index = s.indexOf(QLatin1String(": ")); + if (index == -1 || s.indexOf(QLatin1Char(' ')) < index) { + key = QLatin1String("Description"); + value = s; + } else { + key = s.left(index); + value = s.mid(index + 2); + } + if (!description.isEmpty()) + description += QLatin1String("\n\n"); + description += key + QLatin1String(": ") + value.simplified(); + readTexts.append(key); + readTexts.append(value); + } + } + state = ReadHeader; return true; } @@ -793,9 +854,16 @@ bool QJpegHandlerPrivate::read(QImage *image) if(state == ReadHeader) { - bool success = read_jpeg_image(image, scaledSize, scaledClipRect, clipRect, quality, &info, &err); - state = success ? Ready : Error; - return success; + bool success = read_jpeg_image(image, scaledSize, scaledClipRect, clipRect, quality, &info, &err); + if (success) { + for (int i = 0; i < readTexts.size()-1; i+=2) + image->setText(readTexts.at(i), readTexts.at(i+1)); + + state = Ready; + return true; + } + + state = Error; } return false; @@ -863,7 +931,7 @@ bool QJpegHandler::read(QImage *image) bool QJpegHandler::write(const QImage &image) { - return write_jpeg_image(image, device(), d->quality); + return write_jpeg_image(image, device(), d->quality, d->description); } bool QJpegHandler::supportsOption(ImageOption option) const @@ -872,6 +940,7 @@ bool QJpegHandler::supportsOption(ImageOption option) const || option == ScaledSize || option == ScaledClipRect || option == ClipRect + || option == Description || option == Size || option == ImageFormat; } @@ -887,6 +956,9 @@ QVariant QJpegHandler::option(ImageOption option) const return d->scaledClipRect; case ClipRect: return d->clipRect; + case Description: + d->readJpegHeader(device()); + return d->description; case Size: d->readJpegHeader(device()); return d->size; @@ -894,8 +966,10 @@ QVariant QJpegHandler::option(ImageOption option) const d->readJpegHeader(device()); return d->format; default: - return QVariant(); + break; } + + return QVariant(); } void QJpegHandler::setOption(ImageOption option, const QVariant &value) @@ -913,6 +987,9 @@ void QJpegHandler::setOption(ImageOption option, const QVariant &value) case ClipRect: d->clipRect = value.toRect(); break; + case Description: + d->description = value.toString(); + break; default: break; } @@ -923,7 +1000,4 @@ QByteArray QJpegHandler::name() const return "jpeg"; } - - - QT_END_NAMESPACE diff --git a/tests/auto/gui/image/qimagereader/images/txts.jpg b/tests/auto/gui/image/qimagereader/images/txts.jpg new file mode 100644 index 0000000000000000000000000000000000000000..34e77fef9dddce22d51e2e540ac92a268890e1cb GIT binary patch literal 2357 zcmbW0c{tSV8pnTQX3Wr3#!@QFFk@e0C`)Mgj!D6ADoT zgwPNu8p7uQ@&EvV3zodO=YJMN2nvG>BakAZTLgxBF+d0cg$lu-a5xMmaGwqg)xVpJ}P-uSOasL2%Ad4Ll85JE9d-hyH;`s|n$(OHO&A67Cm7Q}Zzo4+F zxTN%M?Y+AC`wfjv51zJi+S)swbw2Oy>mPVC_;zS`{Nu!@$o5t}w{1TjvaS$N{|N%Q9(?&*`~r)HNb)40;?8|v>!bra0U>Y@4b2cE0@*dJWST%>`SZi zFH&#v0dksn<4-iJ`HQ^~PvcS*xgkdT#6D1YGJZ5KtmFE`uJSS)a(#9ovfb?geri@J zp)LKE-Q%oknWQHQ4tY;t+v9@dt^2D?)J`fr8f~1%1(U8K<`8Rc#|mCDeq6(O6}hO> zv|3RTy=8%O`CR3l7i>`_dxEw%_tO^3U5Mr2U4sn$3d4Zbc8W2VVjL(oUa56SCaspR z9^O6U9bbgvI?^h?E_hc2MM*LV0V7z&I?bLC?>fhWM5jC&Qqx4T`M}Wh&%XK!{PzW~KO-;U4j<5gO{L6r^ybp@hOuQ{yOhZT zQuqDmS}J_+Tr0w}7zKXb+$-LyL%Y)VA_(d3T|dbZy%+Qq)LHL82Y zb*FP}HC+Ffn}MBs{LZ`_mSG_<)Yn*c8 zthKjVzt+>(2^l`H_gUJa>VlSm1(O`N{BrO$qp-TZ|Cry6aCvdVxg?z&9qYeCK`OYx2G4 zZdktEU)q>oH1N%a+ko>A`fY9zv%3IukZ799&f&m^w5gE95jh9@Fyf@g?~1+_RO=7l z<=sN5YTWQ;Kd_897Jx%EyPxkD%jy$6t^wp8ha*C`Rz~R$Y8e6Yck` zURx61;*t=9K$LW=%n+7FD2&{rq?L;z(uSRjd_ck#oG*9v&@K?A?V_#sd^tFi&|fdI z&vs0AwrqtQ3zxc%?c${d9zNs|5*Z6`9a}x&VDy3zIq_EL_Njwq*>jc&^*-nCTInsB z2!6q|)>7(r>mFLoFOTevg$vHrT_r;&UhPgEL>pi4JV+OQce7dJ2C^q%rU5UD?F~{Fo{qYyaJu~GrXV*A> zgqtKCGlCuK^vH5oP}i5z45R@w|H5^b)JLJ52Ff#c^9x#Hyz1gXlbcIP1gUc0JLM^p zDk_mueZ#B8zr;Bd`WrM>=jG++lP@kC&n2rCs+I$!A3PFMnj||nV<`oB%A%K<@rN@1AXx!u>b%7 literal 0 HcmV?d00001 diff --git a/tests/auto/gui/image/qimagereader/qimagereader.qrc b/tests/auto/gui/image/qimagereader/qimagereader.qrc index 3ff41d3..7eb70c8 100644 --- a/tests/auto/gui/image/qimagereader/qimagereader.qrc +++ b/tests/auto/gui/image/qimagereader/qimagereader.qrc @@ -62,6 +62,7 @@ images/corrupt.svg images/corrupt.svgz images/qtbug13653-no_eoi.jpg + images/txts.jpg images/txts.png diff --git a/tests/auto/gui/image/qimagereader/tst_qimagereader.cpp b/tests/auto/gui/image/qimagereader/tst_qimagereader.cpp index a1d050c..82226ef 100644 --- a/tests/auto/gui/image/qimagereader/tst_qimagereader.cpp +++ b/tests/auto/gui/image/qimagereader/tst_qimagereader.cpp @@ -75,8 +75,8 @@ public: virtual ~tst_QImageReader(); public slots: - void init(); - void cleanup(); + void initTestCase(); + void cleanupTestCase(); private slots: void getSetCheck(); @@ -201,13 +201,15 @@ tst_QImageReader::~tst_QImageReader() } -void tst_QImageReader::init() +void tst_QImageReader::initTestCase() { prefix = QFINDTESTDATA("images/"); + if (prefix.isEmpty()) + QFAIL("Can't find images directory!"); QVERIFY(m_temporaryDir.isValid()); } -void tst_QImageReader::cleanup() +void tst_QImageReader::cleanupTestCase() { } @@ -1772,6 +1774,11 @@ void tst_QImageReader::readText_data() QTest::newRow("png, zTXt before img") << "txts.png" << "Comment" << "Some compressed text."; QTest::newRow("png, tEXt after img") << "txts.png" << "Disclaimer" << "For testing only."; QTest::newRow("png, zTXt after img") << "txts.png" << "Description" << "Rendered by Persistence of Vision (tm) Ray Tracer"; + + QTest::newRow("jpg, JPEG_COM Title") << "txts.jpg" << "Title" << "JPG"; + QTest::newRow("jpg, JPEG_COM Comment") << "txts.jpg" << "Comment" << "Some non-compressed text."; + QTest::newRow("jpg, JPEG_COM Disclaimer") << "txts.jpg" << "Disclaimer" << "For testing only."; + QTest::newRow("jpg, JPEG_COM Description") << "txts.jpg" << "Description" << "Rendered by Persistence of Vision (tm) Ray Tracer"; } @@ -1789,30 +1796,37 @@ void tst_QImageReader::readText() void tst_QImageReader::preserveTexts_data() { + QTest::addColumn("fileName"); QTest::addColumn("text"); - QTest::newRow("Simple") << "simpletext"; - QTest::newRow("Whitespace") << " A text with whitespace "; - QTest::newRow("Newline") << "A text\nwith newlines\n"; - QTest::newRow("Double newlines") << "A text\n\nwith double newlines\n\n"; - QTest::newRow("Long") << QString("A rather long text, at least after many repetitions. ").repeated(100); QString latin1set; - int c; - for(c = 0x20; c <= 0x7e; c++) + for (int c = 0x20; c <= 0x7e; c++) latin1set.append(QLatin1Char(c)); - for(c = 0xa0; c <= 0xff; c++) + for (int c = 0xa0; c <= 0xff; c++) latin1set.append(QLatin1Char(c)); - QTest::newRow("All Latin1 chars") << latin1set; + QStringList fileNames; + fileNames << QLatin1String(":/images/kollada.png") + << QLatin1String(":/images/txts.jpg"); + foreach (const QString &fileName, fileNames) { + QTest::newRow("Simple") << fileName << "simpletext"; + QTest::newRow("Whitespace") << fileName << " A text with whitespace "; + QTest::newRow("Newline") << fileName << "A text\nwith newlines\n"; + QTest::newRow("Double newlines") << fileName << "A text\n\nwith double newlines\n\n"; + QTest::newRow("Long") << fileName << QString("A rather long text, at least after many repetitions. ").repeated(100); + QTest::newRow("All Latin1 chars") << fileName << latin1set; #if 0 - // Depends on iTXt support in libpng - QTest::newRow("Multibyte string") << QString::fromUtf8("\341\233\222\341\233\226\341\232\251\341\232\271\341\232\242\341\233\232\341\232\240"); + // Depends on iTXt support in libpng + QTest::newRow("Multibyte string") << fileName << QString::fromUtf8("\341\233\222\341\233\226\341\232\251\341\232\271\341\232\242\341\233\232\341\232\240"); #endif + } } void tst_QImageReader::preserveTexts() { + QFETCH(QString, fileName); + QByteArray format = fileName.right(3).toLatin1(); QFETCH(QString, text); QString key("testkey"); QString key2("testkey2"); @@ -1820,26 +1834,26 @@ void tst_QImageReader::preserveTexts() QString key3("testkey3"); QString text3("Some more other text."); - QImage img(":/images/kollada.png"); + QImage img(fileName); img.setText(key, text); img.setText(key2, text2); QBuffer buf; buf.open(QIODevice::WriteOnly); - QVERIFY(img.save(&buf, "png")); + QVERIFY(img.save(&buf, format.constData())); buf.close(); - QImage stored = QImage::fromData(buf.buffer(), "png"); + QImage stored = QImage::fromData(buf.buffer(), format.constData()); QCOMPARE(stored.text(key), text); QCOMPARE(stored.text(key2), text2); - QImage img2(":/images/kollada.png"); + QImage img2(fileName); img2.setText(key3, text3); QBuffer buf2; - QImageWriter w(&buf2, "png"); + QImageWriter w(&buf2, format); w.setText(key, text); w.setText(key2, text2); QVERIFY(w.write(img2)); buf2.close(); - QImageReader r(&buf2, "png"); + QImageReader r(&buf2, format); QCOMPARE(r.text(key), text.simplified()); QCOMPARE(r.text(key2), text2.simplified()); QCOMPARE(r.text(key3), text3.simplified()); -- 2.7.4