File indexing completed on 2024-05-19 15:40:00

0001 /*
0002     SPDX-FileCopyrightText: 2008 Jakub Stachowski <qbast@go2.pl>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 #include "mobidocument.h"
0007 #include <QApplication> // Because of the HACK
0008 #include <QColor>
0009 #include <QDebug>
0010 #include <QFile>
0011 #include <QPalette> // Because of the HACK
0012 #include <QRegularExpression>
0013 #include <qmobipocket/mobipocket.h>
0014 #include <qmobipocket/qfilestream.h>
0015 
0016 using namespace Mobi;
0017 
0018 MobiDocument::MobiDocument(const QString &fileName)
0019     : QTextDocument()
0020 {
0021     file = new Mobipocket::QFileStream(fileName);
0022     doc = new Mobipocket::Document(file);
0023     if (doc->isValid()) {
0024         QString text = doc->text();
0025         QString header = text.left(1024);
0026         if (header.contains(QStringLiteral("<html>")) || header.contains(QStringLiteral("<HTML>"))) {
0027             // HACK BEGIN Get the links without CSS to be blue
0028             //            Remove if Qt ever gets fixed and the code in textdocumentgenerator.cpp works
0029             const QPalette orig = qApp->palette();
0030             QPalette p = orig;
0031             p.setColor(QPalette::Link, Qt::blue);
0032             qApp->setPalette(p);
0033             // HACK END
0034 
0035             setHtml(fixMobiMarkup(text));
0036 
0037             // HACK BEGIN
0038             qApp->setPalette(orig);
0039             // HACK END
0040         } else {
0041             setPlainText(text);
0042         }
0043     }
0044 }
0045 
0046 MobiDocument::~MobiDocument()
0047 {
0048     delete doc;
0049     delete file;
0050 }
0051 
0052 QVariant MobiDocument::loadResource(int type, const QUrl &name)
0053 {
0054     if (type != QTextDocument::ImageResource || name.scheme() != QString(QStringLiteral("pdbrec"))) {
0055         return QVariant();
0056     }
0057     bool ok;
0058     quint16 recnum = QStringView {name.path()}.mid(1).toUShort(&ok);
0059     if (!ok || recnum >= doc->imageCount()) {
0060         return QVariant();
0061     }
0062 
0063     QVariant resource;
0064     resource.setValue(doc->getImage(recnum - 1));
0065     addResource(type, name, resource);
0066 
0067     return resource;
0068 }
0069 
0070 // starting from 'pos', find position in the string that is not inside a tag
0071 int outsideTag(const QString &data, int pos)
0072 {
0073     for (int i = pos - 1; i >= 0; i--) {
0074         if (data[i] == QLatin1Char('>')) {
0075             return pos;
0076         }
0077         if (data[i] == QLatin1Char('<')) {
0078             return i;
0079         }
0080     }
0081     return pos;
0082 }
0083 
0084 QString MobiDocument::fixMobiMarkup(const QString &data)
0085 {
0086     QString ret = data;
0087     QMap<int, QString> anchorPositions;
0088     static QRegularExpression anchors(QStringLiteral("<a(?: href=\"[^\"]*\"){0,1}[\\s]+filepos=['\"]{0,1}([\\d]+)[\"']{0,1}"), QRegularExpression::CaseInsensitiveOption);
0089 
0090     // find all link destinations
0091     auto matcher = anchors.globalMatch(data);
0092     while (matcher.hasNext()) {
0093         auto match = matcher.next();
0094         int filepos = match.captured(1).toUInt();
0095         if (filepos) {
0096             anchorPositions[filepos] = match.captured(1);
0097         }
0098     }
0099 
0100     // put HTML anchors in all link destinations
0101     int offset = 0;
0102     QMapIterator<int, QString> it(anchorPositions);
0103     while (it.hasNext()) {
0104         it.next();
0105         // link pointing outside the document, ignore
0106         if ((it.key() + offset) >= ret.size()) {
0107             continue;
0108         }
0109         int fixedpos = outsideTag(ret, it.key() + offset);
0110         ret.insert(fixedpos, QStringLiteral("<a name=\"") + it.value() + QStringLiteral("\">&nbsp;</a>"));
0111         // inserting anchor shifts all offsets after the anchor
0112         offset += 21 + it.value().size();
0113     }
0114 
0115     // replace links referencing filepos with normal internal links
0116     ret.replace(anchors, QStringLiteral("<a href=\"#\\1\""));
0117     // Mobipocket uses strange variang of IMG tags: <img recindex="3232"> where recindex is number of
0118     // record containing image
0119     static QRegularExpression imgs(QStringLiteral("<img.*?recindex=\"([\\d]*)\".*?>"), QRegularExpression::CaseInsensitiveOption);
0120 
0121     ret.replace(imgs, QStringLiteral("<img src=\"pdbrec:/\\1\">"));
0122     ret.replace(QStringLiteral("<mbp:pagebreak/>"), QStringLiteral("<p style=\"page-break-after:always\"></p>"));
0123 
0124     return ret;
0125 }