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("\"> </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 }