You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
7.2 KiB
263 lines
7.2 KiB
/* |
|
SPDX-FileCopyrightText: 2007 Tobias Koenig <tokoe@kde.org> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "document.h" |
|
|
|
#include <QBuffer> |
|
#include <QImage> |
|
#include <QImageReader> |
|
#include <QScopedPointer> |
|
|
|
#include <KLocalizedString> |
|
#include <KTar> |
|
#include <QMimeDatabase> |
|
#include <QMimeType> |
|
#include <kzip.h> |
|
#if WITH_K7ZIP |
|
#include <K7Zip> |
|
#endif |
|
|
|
#include <memory> |
|
|
|
#include <core/page.h> |
|
|
|
#include "debug_comicbook.h" |
|
#include "directory.h" |
|
#include "qnatsort.h" |
|
#include "unrar.h" |
|
|
|
using namespace ComicBook; |
|
|
|
static void imagesInArchive(const QString &prefix, const KArchiveDirectory *dir, QStringList *entries) |
|
{ |
|
const QStringList entryList = dir->entries(); |
|
for (const QString &file : entryList) { |
|
const KArchiveEntry *e = dir->entry(file); |
|
if (e->isDirectory()) { |
|
imagesInArchive(prefix + file + QLatin1Char('/'), static_cast<const KArchiveDirectory *>(e), entries); |
|
} else if (e->isFile()) { |
|
entries->append(prefix + file); |
|
} |
|
} |
|
} |
|
|
|
Document::Document() |
|
: mDirectory(nullptr) |
|
, mUnrar(nullptr) |
|
, mArchive(nullptr) |
|
{ |
|
} |
|
|
|
Document::~Document() |
|
{ |
|
} |
|
|
|
bool Document::open(const QString &fileName) |
|
{ |
|
close(); |
|
|
|
QMimeDatabase db; |
|
const QMimeType mime = db.mimeTypeForFile(fileName, QMimeDatabase::MatchContent); |
|
|
|
/** |
|
* We have a zip archive |
|
*/ |
|
if (mime.inherits(QStringLiteral("application/x-cbz")) || mime.inherits(QStringLiteral("application/zip"))) { |
|
mArchive = new KZip(fileName); |
|
|
|
if (!processArchive()) { |
|
return false; |
|
} |
|
/** |
|
* We have a TAR archive |
|
*/ |
|
} else if (mime.inherits(QStringLiteral("application/x-cbt")) || mime.inherits(QStringLiteral("application/x-gzip")) || mime.inherits(QStringLiteral("application/x-tar")) || mime.inherits(QStringLiteral("application/x-bzip"))) { |
|
mArchive = new KTar(fileName); |
|
|
|
if (!processArchive()) { |
|
return false; |
|
} |
|
#ifdef WITH_K7ZIP |
|
/** |
|
* We have a 7z archive |
|
*/ |
|
} else if (mime.inherits(QStringLiteral("application/x-cb7")) || mime.inherits(QStringLiteral("application/x-7z-compressed"))) { |
|
mArchive = new K7Zip(fileName); |
|
|
|
if (!processArchive()) { |
|
return false; |
|
} |
|
#endif |
|
} else if (mime.inherits(QStringLiteral("application/x-cbr")) || mime.inherits(QStringLiteral("application/x-rar")) || mime.inherits(QStringLiteral("application/vnd.rar"))) { |
|
if (!Unrar::isAvailable()) { |
|
mLastErrorString = i18n("Cannot open document, neither unrar nor unarchiver were found."); |
|
return false; |
|
} |
|
|
|
if (!Unrar::isSuitableVersionAvailable()) { |
|
mLastErrorString = i18n("The version of unrar on your system is not suitable for opening comicbooks."); |
|
return false; |
|
} |
|
|
|
/** |
|
* We have a rar archive |
|
*/ |
|
mUnrar = new Unrar(); |
|
|
|
if (!mUnrar->open(fileName)) { |
|
delete mUnrar; |
|
mUnrar = nullptr; |
|
|
|
return false; |
|
} |
|
|
|
mEntries = mUnrar->list(); |
|
} else if (mime.inherits(QStringLiteral("inode/directory"))) { |
|
mDirectory = new Directory(); |
|
|
|
if (!mDirectory->open(fileName)) { |
|
delete mDirectory; |
|
mDirectory = nullptr; |
|
|
|
return false; |
|
} |
|
|
|
mEntries = mDirectory->list(); |
|
} else { |
|
mLastErrorString = i18n("Unknown ComicBook format."); |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void Document::close() |
|
{ |
|
mLastErrorString.clear(); |
|
|
|
if (!(mArchive || mUnrar || mDirectory)) { |
|
return; |
|
} |
|
|
|
delete mArchive; |
|
mArchive = nullptr; |
|
delete mDirectory; |
|
mDirectory = nullptr; |
|
delete mUnrar; |
|
mUnrar = nullptr; |
|
mPageMap.clear(); |
|
mEntries.clear(); |
|
} |
|
|
|
bool Document::processArchive() |
|
{ |
|
if (!mArchive->open(QIODevice::ReadOnly)) { |
|
delete mArchive; |
|
mArchive = nullptr; |
|
|
|
return false; |
|
} |
|
|
|
const KArchiveDirectory *directory = mArchive->directory(); |
|
if (!directory) { |
|
delete mArchive; |
|
mArchive = nullptr; |
|
|
|
return false; |
|
} |
|
|
|
mArchiveDir = directory; |
|
|
|
imagesInArchive(QString(), mArchiveDir, &mEntries); |
|
|
|
return true; |
|
} |
|
|
|
void Document::pages(QVector<Okular::Page *> *pagesVector) |
|
{ |
|
std::sort(mEntries.begin(), mEntries.end(), caseSensitiveNaturalOrderLessThen); |
|
QScopedPointer<QIODevice> dev; |
|
|
|
int count = 0; |
|
pagesVector->clear(); |
|
pagesVector->resize(mEntries.size()); |
|
QImageReader reader; |
|
reader.setAutoTransform(true); |
|
for (const QString &file : qAsConst(mEntries)) { |
|
if (mArchive) { |
|
const KArchiveFile *entry = static_cast<const KArchiveFile *>(mArchiveDir->entry(file)); |
|
if (entry) { |
|
dev.reset(entry->createDevice()); |
|
} |
|
} else if (mDirectory) { |
|
dev.reset(mDirectory->createDevice(file)); |
|
} else { |
|
dev.reset(mUnrar->createDevice(file)); |
|
} |
|
|
|
if (!dev.isNull()) { |
|
reader.setDevice(dev.data()); |
|
if (reader.canRead()) { |
|
QSize pageSize = reader.size(); |
|
if (reader.transformation() & QImageIOHandler::TransformationRotate90) { |
|
pageSize.transpose(); |
|
} |
|
if (!pageSize.isValid()) { |
|
const QImage i = reader.read(); |
|
if (!i.isNull()) { |
|
pageSize = i.size(); |
|
} |
|
} |
|
if (pageSize.isValid()) { |
|
pagesVector->replace(count, new Okular::Page(count, pageSize.width(), pageSize.height(), Okular::Rotation0)); |
|
mPageMap.append(file); |
|
count++; |
|
} else { |
|
qCDebug(OkularComicbookDebug) << "Ignoring" << file << "doesn't seem to be an image even if QImageReader::canRead returned true"; |
|
} |
|
} |
|
} |
|
} |
|
pagesVector->resize(count); |
|
} |
|
|
|
QStringList Document::pageTitles() const |
|
{ |
|
return QStringList(); |
|
} |
|
|
|
QImage Document::pageImage(int page) const |
|
{ |
|
if (mArchive) { |
|
const KArchiveFile *entry = static_cast<const KArchiveFile *>(mArchiveDir->entry(mPageMap[page])); |
|
if (entry) { |
|
std::unique_ptr<QIODevice> dev(entry->createDevice()); |
|
// This could simply be |
|
// QImageReader reader(dev.get()); |
|
// but due to https://codereview.qt-project.org/c/qt/qtbase/+/349174 and https://invent.kde.org/frameworks/karchive/-/merge_requests/14 |
|
// it can not, so it will have to be like this at least until Qt6 |
|
// Test with https://bugs.kde.org/attachment.cgi?id=74039 (it's a cbz with a png inside) |
|
QBuffer b; |
|
b.setData(dev->readAll()); |
|
QImageReader reader(&b); |
|
reader.setAutoTransform(true); |
|
return reader.read(); |
|
} |
|
} else if (mDirectory) { |
|
return QImage(mPageMap[page]); |
|
} else { |
|
return QImage::fromData(mUnrar->contentOf(mPageMap[page])); |
|
} |
|
|
|
return QImage(); |
|
} |
|
|
|
QString Document::lastErrorString() const |
|
{ |
|
return mLastErrorString; |
|
} |
|
|
|
Q_LOGGING_CATEGORY(OkularComicbookDebug, "org.kde.okular.generators.comicbook", QtWarningMsg)
|
|
|