File indexing completed on 2024-05-19 04:35:18
0001 /* 0002 SPDX-FileCopyrightText: 2007 Tobias Koenig <tokoe@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "converter.h" 0008 0009 #include <QAbstractTextDocumentLayout> 0010 #include <QDate> 0011 #include <QDomElement> 0012 #include <QDomText> 0013 #include <QTextCursor> 0014 #include <QTextDocument> 0015 #include <QTextFrame> 0016 #include <QTextTable> 0017 #include <QUrl> 0018 0019 #include <KLocalizedString> 0020 0021 #include <core/action.h> 0022 #include <core/document.h> 0023 0024 #include "document.h" 0025 0026 using namespace FictionBook; 0027 0028 class Converter::TitleInfo 0029 { 0030 public: 0031 QStringList mGenres; 0032 QString mAuthor; 0033 QString mTitle; 0034 QString mAnnotation; 0035 QString mKeywords; 0036 QDate mDate; 0037 QDomElement mCoverPage; 0038 QString mLanguage; 0039 }; 0040 0041 class Converter::DocumentInfo 0042 { 0043 public: 0044 QString mAuthor; 0045 QString mProducer; 0046 QDate mDate; 0047 QString mId; 0048 QString mVersion; 0049 }; 0050 0051 Converter::Converter() 0052 : mTextDocument(nullptr) 0053 , mCursor(nullptr) 0054 , mTitleInfo(nullptr) 0055 , mDocumentInfo(nullptr) 0056 { 0057 } 0058 0059 Converter::~Converter() 0060 { 0061 delete mTitleInfo; 0062 delete mDocumentInfo; 0063 } 0064 0065 QTextDocument *Converter::convert(const QString &fileName) 0066 { 0067 Document fbDocument(fileName); 0068 if (!fbDocument.open()) { 0069 Q_EMIT error(fbDocument.lastErrorString(), -1); 0070 return nullptr; 0071 } 0072 0073 mTextDocument = new QTextDocument; 0074 mCursor = new QTextCursor(mTextDocument); 0075 mSectionCounter = 0; 0076 mLocalLinks.clear(); 0077 mSectionMap.clear(); 0078 0079 const QDomDocument document = fbDocument.content(); 0080 0081 /** 0082 * Set the correct page size 0083 */ 0084 mTextDocument->setPageSize(QSizeF(600, 800)); 0085 0086 QTextFrameFormat frameFormat; 0087 frameFormat.setMargin(20); 0088 0089 QTextFrame *rootFrame = mTextDocument->rootFrame(); 0090 rootFrame->setFrameFormat(frameFormat); 0091 0092 /** 0093 * Parse the content of the document 0094 */ 0095 const QDomElement documentElement = document.documentElement(); 0096 0097 if (documentElement.tagName() != QLatin1String("FictionBook")) { 0098 Q_EMIT error(i18n("Document is not a valid FictionBook"), -1); 0099 delete mCursor; 0100 return nullptr; 0101 } 0102 0103 /** 0104 * First we read all images, so we can calculate the size later. 0105 */ 0106 QDomElement element = documentElement.firstChildElement(); 0107 while (!element.isNull()) { 0108 if (element.tagName() == QLatin1String("binary")) { 0109 if (!convertBinary(element)) { 0110 delete mCursor; 0111 return nullptr; 0112 } 0113 } 0114 0115 element = element.nextSiblingElement(); 0116 } 0117 0118 /** 0119 * Read the rest: description (could be only one) and bodies (one or more) 0120 */ 0121 element = documentElement.firstChildElement(); 0122 while (!element.isNull()) { 0123 if (element.tagName() == QLatin1String("description")) { 0124 if (!convertDescription(element)) { 0125 delete mCursor; 0126 return nullptr; 0127 } 0128 0129 if (mTitleInfo && !mTitleInfo->mCoverPage.isNull()) { 0130 convertCover(mTitleInfo->mCoverPage); 0131 mCursor->insertBlock(); 0132 } 0133 0134 QTextFrame *topFrame = mCursor->currentFrame(); 0135 0136 QTextFrameFormat frameFormat; 0137 frameFormat.setBorder(2); 0138 frameFormat.setPadding(8); 0139 frameFormat.setBackground(Qt::lightGray); 0140 0141 if (mTitleInfo && !mTitleInfo->mTitle.isEmpty()) { 0142 mCursor->insertFrame(frameFormat); 0143 0144 QTextCharFormat charFormat; 0145 charFormat.setFontPointSize(22); 0146 charFormat.setFontWeight(QFont::Bold); 0147 mCursor->insertText(mTitleInfo->mTitle, charFormat); 0148 0149 mCursor->setPosition(topFrame->lastPosition()); 0150 } 0151 0152 if (mTitleInfo && !mTitleInfo->mAuthor.isEmpty()) { 0153 frameFormat.setBorder(1); 0154 mCursor->insertFrame(frameFormat); 0155 0156 QTextCharFormat charFormat; 0157 charFormat.setFontPointSize(10); 0158 mCursor->insertText(mTitleInfo->mAuthor, charFormat); 0159 0160 mCursor->setPosition(topFrame->lastPosition()); 0161 mCursor->insertBlock(); 0162 } 0163 0164 if (mTitleInfo && !mTitleInfo->mAnnotation.isEmpty()) { 0165 frameFormat.setBorder(0); 0166 mCursor->insertFrame(frameFormat); 0167 0168 QTextCharFormat charFormat; 0169 charFormat.setFontPointSize(10); 0170 charFormat.setFontItalic(true); 0171 mCursor->insertText(mTitleInfo->mAnnotation, charFormat); 0172 0173 mCursor->setPosition(topFrame->lastPosition()); 0174 mCursor->insertBlock(); 0175 } 0176 } else if (element.tagName() == QLatin1String("body")) { 0177 mCursor->insertBlock(); 0178 0179 if (!convertBody(element)) { 0180 delete mCursor; 0181 return nullptr; 0182 } 0183 } 0184 0185 element = element.nextSiblingElement(); 0186 } 0187 0188 /** 0189 * Add document info. 0190 */ 0191 if (mTitleInfo) { 0192 if (!mTitleInfo->mTitle.isEmpty()) { 0193 Q_EMIT addMetaData(Okular::DocumentInfo::Title, mTitleInfo->mTitle); 0194 } 0195 0196 if (!mTitleInfo->mAuthor.isEmpty()) { 0197 Q_EMIT addMetaData(Okular::DocumentInfo::Author, mTitleInfo->mAuthor); 0198 } 0199 0200 if (!mTitleInfo->mKeywords.isEmpty()) { 0201 Q_EMIT addMetaData(Okular::DocumentInfo::Keywords, mTitleInfo->mKeywords); 0202 } 0203 } 0204 0205 if (mDocumentInfo) { 0206 if (!mDocumentInfo->mProducer.isEmpty()) { 0207 Q_EMIT addMetaData(Okular::DocumentInfo::Producer, mDocumentInfo->mProducer); 0208 } 0209 0210 if (mDocumentInfo->mDate.isValid()) { 0211 Q_EMIT addMetaData(Okular::DocumentInfo::CreationDate, QLocale().toString(mDocumentInfo->mDate, QLocale::ShortFormat)); 0212 } 0213 } 0214 0215 QMapIterator<QString, QPair<int, int>> it(mLocalLinks); 0216 while (it.hasNext()) { 0217 it.next(); 0218 0219 const QTextBlock block = mSectionMap[it.key()]; 0220 if (!block.isValid()) { // local link without existing target 0221 continue; 0222 } 0223 0224 Okular::DocumentViewport viewport = calculateViewport(mTextDocument, block); 0225 0226 Okular::GotoAction *action = new Okular::GotoAction(QString(), viewport); 0227 0228 Q_EMIT addAction(action, it.value().first, it.value().second); 0229 } 0230 0231 delete mCursor; 0232 0233 return mTextDocument; 0234 } 0235 0236 bool Converter::convertBody(const QDomElement &element) 0237 { 0238 QDomElement child = element.firstChildElement(); 0239 while (!child.isNull()) { 0240 if (child.tagName() == QLatin1String("section")) { 0241 mCursor->insertBlock(); 0242 if (!convertSection(child)) { 0243 return false; 0244 } 0245 } else if (child.tagName() == QLatin1String("image")) { 0246 if (!convertImage(child)) { 0247 return false; 0248 } 0249 } else if (child.tagName() == QLatin1String("title")) { 0250 if (!convertTitle(child)) { 0251 return false; 0252 } 0253 } else if (child.tagName() == QLatin1String("epigraph")) { 0254 if (!convertEpigraph(child)) { 0255 return false; 0256 } 0257 } 0258 0259 child = child.nextSiblingElement(); 0260 } 0261 0262 return true; 0263 } 0264 0265 bool Converter::convertDescription(const QDomElement &element) 0266 { 0267 QDomElement child = element.firstChildElement(); 0268 while (!child.isNull()) { 0269 if (child.tagName() == QLatin1String("title-info")) { 0270 if (!convertTitleInfo(child)) { 0271 return false; 0272 } 0273 } 0274 if (child.tagName() == QLatin1String("document-info")) { 0275 if (!convertDocumentInfo(child)) { 0276 return false; 0277 } 0278 } 0279 0280 child = child.nextSiblingElement(); 0281 } 0282 0283 return true; 0284 } 0285 0286 bool Converter::convertTitleInfo(const QDomElement &element) 0287 { 0288 delete mTitleInfo; 0289 mTitleInfo = new TitleInfo; 0290 0291 QDomElement child = element.firstChildElement(); 0292 while (!child.isNull()) { 0293 if (child.tagName() == QLatin1String("genre")) { 0294 QString genre; 0295 if (!convertTextNode(child, genre)) { 0296 return false; 0297 } 0298 0299 if (!genre.isEmpty()) { 0300 mTitleInfo->mGenres.append(genre); 0301 } 0302 } else if (child.tagName() == QLatin1String("author")) { 0303 QString firstName, middleName, lastName, dummy; 0304 0305 if (!convertAuthor(child, firstName, middleName, lastName, dummy, dummy)) { 0306 return false; 0307 } 0308 0309 if (mTitleInfo->mAuthor.isEmpty()) { 0310 mTitleInfo->mAuthor = QStringLiteral("%1 %2 %3").arg(firstName, middleName, lastName).simplified(); 0311 } else { 0312 mTitleInfo->mAuthor += QStringLiteral(", %1 %2 %3").arg(firstName, middleName, lastName).simplified(); 0313 } 0314 } else if (child.tagName() == QLatin1String("book-title")) { 0315 if (!convertTextNode(child, mTitleInfo->mTitle)) { 0316 return false; 0317 } 0318 } else if (child.tagName() == QLatin1String("keywords")) { 0319 QString keywords; 0320 if (!convertTextNode(child, keywords)) { 0321 return false; 0322 } 0323 0324 mTitleInfo->mKeywords = keywords; 0325 } else if (child.tagName() == QLatin1String("annotation")) { 0326 if (!convertAnnotation(child, mTitleInfo->mAnnotation)) { 0327 return false; 0328 } 0329 } else if (child.tagName() == QLatin1String("date")) { 0330 if (!convertDate(child, mTitleInfo->mDate)) { 0331 return false; 0332 } 0333 } else if (child.tagName() == QLatin1String("coverpage")) { 0334 mTitleInfo->mCoverPage = child; 0335 } else if (child.tagName() == QLatin1String("lang")) { 0336 if (!convertTextNode(child, mTitleInfo->mLanguage)) { 0337 return false; 0338 } 0339 } 0340 child = child.nextSiblingElement(); 0341 } 0342 0343 return true; 0344 } 0345 0346 bool Converter::convertDocumentInfo(const QDomElement &element) 0347 { 0348 delete mDocumentInfo; 0349 mDocumentInfo = new DocumentInfo; 0350 0351 QDomElement child = element.firstChildElement(); 0352 while (!child.isNull()) { 0353 if (child.tagName() == QLatin1String("author")) { 0354 QString firstName, middleName, lastName, email, nickname; 0355 0356 if (!convertAuthor(child, firstName, middleName, lastName, email, nickname)) { 0357 return false; 0358 } 0359 0360 mDocumentInfo->mAuthor = QStringLiteral("%1 %2 %3 <%4> (%5)").arg(firstName, middleName, lastName, email, nickname); 0361 } else if (child.tagName() == QLatin1String("program-used")) { 0362 if (!convertTextNode(child, mDocumentInfo->mProducer)) { 0363 return false; 0364 } 0365 } else if (child.tagName() == QLatin1String("date")) { 0366 if (!convertDate(child, mDocumentInfo->mDate)) { 0367 return false; 0368 } 0369 } else if (child.tagName() == QLatin1String("id")) { 0370 if (!convertTextNode(child, mDocumentInfo->mId)) { 0371 return false; 0372 } 0373 } else if (child.tagName() == QLatin1String("version")) { 0374 if (!convertTextNode(child, mDocumentInfo->mVersion)) { 0375 return false; 0376 } 0377 } 0378 0379 child = child.nextSiblingElement(); 0380 } 0381 0382 return true; 0383 } 0384 bool Converter::convertAuthor(const QDomElement &element, QString &firstName, QString &middleName, QString &lastName, QString &email, QString &nickname) 0385 { 0386 QDomElement child = element.firstChildElement(); 0387 while (!child.isNull()) { 0388 if (child.tagName() == QLatin1String("first-name")) { 0389 if (!convertTextNode(child, firstName)) { 0390 return false; 0391 } 0392 } else if (child.tagName() == QLatin1String("middle-name")) { 0393 if (!convertTextNode(child, middleName)) { 0394 return false; 0395 } 0396 } else if (child.tagName() == QLatin1String("last-name")) { 0397 if (!convertTextNode(child, lastName)) { 0398 return false; 0399 } 0400 } else if (child.tagName() == QLatin1String("email")) { 0401 if (!convertTextNode(child, email)) { 0402 return false; 0403 } 0404 } else if (child.tagName() == QLatin1String("nickname")) { 0405 if (!convertTextNode(child, nickname)) { 0406 return false; 0407 } 0408 } 0409 0410 child = child.nextSiblingElement(); 0411 } 0412 0413 return true; 0414 } 0415 0416 bool Converter::convertTextNode(const QDomElement &element, QString &data) 0417 { 0418 QDomNode child = element.firstChild(); 0419 while (!child.isNull()) { 0420 QDomText text = child.toText(); 0421 if (!text.isNull()) { 0422 data = text.data(); 0423 } 0424 0425 child = child.nextSibling(); 0426 } 0427 0428 return true; 0429 } 0430 0431 bool Converter::convertDate(const QDomElement &element, QDate &date) 0432 { 0433 if (element.hasAttribute(QStringLiteral("value"))) { 0434 date = QDate::fromString(element.attribute(QStringLiteral("value")), Qt::ISODate); 0435 } 0436 0437 return true; 0438 } 0439 0440 bool Converter::convertAnnotation(const QDomElement &element, QString &data) 0441 { 0442 QDomElement child = element.firstChildElement(); 0443 while (!child.isNull()) { 0444 QString text = child.text(); 0445 if (!text.isNull()) { 0446 data = child.text(); 0447 } 0448 0449 child = child.nextSiblingElement(); 0450 } 0451 0452 return true; 0453 } 0454 0455 bool Converter::convertSection(const QDomElement &element) 0456 { 0457 if (element.hasAttribute(QStringLiteral("id"))) { 0458 mSectionMap.insert(element.attribute(QStringLiteral("id")), mCursor->block()); 0459 } 0460 0461 mSectionCounter++; 0462 0463 QDomElement child = element.firstChildElement(); 0464 while (!child.isNull()) { 0465 if (child.tagName() == QLatin1String("title")) { 0466 if (!convertTitle(child)) { 0467 return false; 0468 } 0469 } else if (child.tagName() == QLatin1String("epigraph")) { 0470 if (!convertEpigraph(child)) { 0471 return false; 0472 } 0473 } else if (child.tagName() == QLatin1String("image")) { 0474 if (!convertImage(child)) { 0475 return false; 0476 } 0477 } else if (child.tagName() == QLatin1String("section")) { 0478 if (!convertSection(child)) { 0479 return false; 0480 } 0481 } else if (child.tagName() == QLatin1String("p")) { 0482 QTextBlockFormat format; 0483 format.setTextIndent(10); 0484 mCursor->insertBlock(format); 0485 if (!convertParagraph(child)) { 0486 return false; 0487 } 0488 } else if (child.tagName() == QLatin1String("poem")) { 0489 if (!convertPoem(child)) { 0490 return false; 0491 } 0492 } else if (child.tagName() == QLatin1String("subtitle")) { 0493 if (!convertSubTitle(child)) { 0494 return false; 0495 } 0496 } else if (child.tagName() == QLatin1String("cite")) { 0497 if (!convertCite(child)) { 0498 return false; 0499 } 0500 } else if (child.tagName() == QLatin1String("empty-line")) { 0501 if (!convertEmptyLine(child)) { 0502 return false; 0503 } 0504 } else if (child.tagName() == QLatin1String("code")) { 0505 if (!convertCode(child)) { 0506 return false; 0507 } 0508 } else if (child.tagName() == QLatin1String("table")) { 0509 if (!convertTable(child)) { 0510 return false; 0511 } 0512 } 0513 0514 child = child.nextSiblingElement(); 0515 } 0516 0517 mSectionCounter--; 0518 0519 return true; 0520 } 0521 0522 bool Converter::convertTitle(const QDomElement &element) 0523 { 0524 QTextFrame *topFrame = mCursor->currentFrame(); 0525 0526 QTextFrameFormat frameFormat; 0527 frameFormat.setBorder(1); 0528 frameFormat.setPadding(8); 0529 frameFormat.setBackground(Qt::lightGray); 0530 0531 mCursor->insertFrame(frameFormat); 0532 0533 QDomElement child = element.firstChildElement(); 0534 0535 bool firstParagraph = true; 0536 while (!child.isNull()) { 0537 if (child.tagName() == QLatin1String("p")) { 0538 if (firstParagraph) { 0539 firstParagraph = false; 0540 } else { 0541 mCursor->insertBlock(); 0542 } 0543 0544 QTextCharFormat origFormat = mCursor->charFormat(); 0545 0546 QTextCharFormat titleFormat(origFormat); 0547 titleFormat.setFontPointSize(22 - (mSectionCounter * 2)); 0548 titleFormat.setFontWeight(QFont::Bold); 0549 mCursor->setCharFormat(titleFormat); 0550 0551 if (!convertParagraph(child)) { 0552 return false; 0553 } 0554 0555 mCursor->setCharFormat(origFormat); 0556 0557 Q_EMIT addTitle(mSectionCounter, child.text(), mCursor->block()); 0558 0559 } else if (child.tagName() == QLatin1String("empty-line")) { 0560 if (!convertEmptyLine(child)) { 0561 return false; 0562 } 0563 } 0564 0565 child = child.nextSiblingElement(); 0566 } 0567 0568 mCursor->setPosition(topFrame->lastPosition()); 0569 0570 return true; 0571 } 0572 0573 bool Converter::convertParagraph(const QDomElement &element) 0574 { 0575 QDomNode child = element.firstChild(); 0576 while (!child.isNull()) { 0577 if (child.isElement()) { 0578 const QDomElement childElement = child.toElement(); 0579 if (childElement.tagName() == QLatin1String("emphasis")) { 0580 if (!convertEmphasis(childElement)) { 0581 return false; 0582 } 0583 } else if (childElement.tagName() == QLatin1String("strong")) { 0584 if (!convertStrong(childElement)) { 0585 return false; 0586 } 0587 } else if (childElement.tagName() == QLatin1String("style")) { 0588 if (!convertStyle(childElement)) { 0589 return false; 0590 } 0591 } else if (childElement.tagName() == QLatin1String("a")) { 0592 if (!convertLink(childElement)) { 0593 return false; 0594 } 0595 } else if (childElement.tagName() == QLatin1String("image")) { 0596 if (!convertImage(childElement)) { 0597 return false; 0598 } 0599 } else if (childElement.tagName() == QLatin1String("strikethrough")) { 0600 if (!convertStrikethrough(childElement)) { 0601 return false; 0602 } 0603 } else if (childElement.tagName() == QLatin1String("code")) { 0604 if (!convertCode(childElement)) { 0605 return false; 0606 } 0607 } else if (childElement.tagName() == QLatin1String("sup")) { 0608 if (!convertSuperScript(childElement)) { 0609 return false; 0610 } 0611 } else if (childElement.tagName() == QLatin1String("sub")) { 0612 if (!convertSubScript(childElement)) { 0613 return false; 0614 } 0615 } 0616 } else if (child.isText()) { 0617 const QDomText childText = child.toText(); 0618 mCursor->insertText(childText.data().simplified()); 0619 } 0620 0621 child = child.nextSibling(); 0622 } 0623 0624 return true; 0625 } 0626 0627 bool Converter::convertEmphasis(const QDomElement &element) 0628 { 0629 QTextCharFormat origFormat = mCursor->charFormat(); 0630 0631 QTextCharFormat italicFormat(origFormat); 0632 italicFormat.setFontItalic(true); 0633 mCursor->setCharFormat(italicFormat); 0634 0635 if (!convertParagraph(element)) { 0636 return false; 0637 } 0638 0639 mCursor->setCharFormat(origFormat); 0640 0641 return true; 0642 } 0643 0644 bool Converter::convertStrikethrough(const QDomElement &element) 0645 { 0646 QTextCharFormat origFormat = mCursor->charFormat(); 0647 0648 QTextCharFormat strikeoutFormat(origFormat); 0649 strikeoutFormat.setFontStrikeOut(true); 0650 mCursor->setCharFormat(strikeoutFormat); 0651 0652 if (!convertParagraph(element)) { 0653 return false; 0654 } 0655 0656 mCursor->setCharFormat(origFormat); 0657 0658 return true; 0659 } 0660 0661 bool Converter::convertStrong(const QDomElement &element) 0662 { 0663 QTextCharFormat origFormat = mCursor->charFormat(); 0664 0665 QTextCharFormat boldFormat(origFormat); 0666 boldFormat.setFontWeight(QFont::Bold); 0667 mCursor->setCharFormat(boldFormat); 0668 0669 if (!convertParagraph(element)) { 0670 return false; 0671 } 0672 0673 mCursor->setCharFormat(origFormat); 0674 0675 return true; 0676 } 0677 0678 bool Converter::convertStyle(const QDomElement &element) 0679 { 0680 if (!convertParagraph(element)) { 0681 return false; 0682 } 0683 0684 return true; 0685 } 0686 0687 bool Converter::convertBinary(const QDomElement &element) 0688 { 0689 const QString id = element.attribute(QStringLiteral("id")); 0690 0691 const QDomText textNode = element.firstChild().toText(); 0692 QByteArray data = textNode.data().toLatin1(); 0693 data = QByteArray::fromBase64(data); 0694 0695 mTextDocument->addResource(QTextDocument::ImageResource, QUrl(id), QImage::fromData(data)); 0696 0697 return true; 0698 } 0699 0700 bool Converter::convertCover(const QDomElement &element) 0701 { 0702 QDomElement child = element.firstChildElement(); 0703 while (!child.isNull()) { 0704 if (child.tagName() == QLatin1String("image")) { 0705 if (!convertImage(child)) { 0706 return false; 0707 } 0708 } 0709 0710 child = child.nextSiblingElement(); 0711 } 0712 0713 return true; 0714 } 0715 0716 bool Converter::convertImage(const QDomElement &element) 0717 { 0718 QString href = element.attributeNS(QStringLiteral("http://www.w3.org/1999/xlink"), QStringLiteral("href")); 0719 0720 if (href.startsWith(QLatin1Char('#'))) { 0721 href = href.mid(1); 0722 } 0723 0724 const QImage img = qvariant_cast<QImage>(mTextDocument->resource(QTextDocument::ImageResource, QUrl(href))); 0725 0726 QTextImageFormat format; 0727 format.setName(href); 0728 0729 if (img.width() > 560) { 0730 format.setWidth(560); 0731 } 0732 0733 format.setHeight(img.height()); 0734 0735 mCursor->insertImage(format); 0736 0737 return true; 0738 } 0739 0740 bool Converter::convertEpigraph(const QDomElement &element) 0741 { 0742 QDomElement child = element.firstChildElement(); 0743 while (!child.isNull()) { 0744 if (child.tagName() == QLatin1String("p")) { 0745 QTextBlockFormat format; 0746 format.setTextIndent(10); 0747 mCursor->insertBlock(format); 0748 if (!convertParagraph(child)) { 0749 return false; 0750 } 0751 } else if (child.tagName() == QLatin1String("poem")) { 0752 if (!convertPoem(child)) { 0753 return false; 0754 } 0755 } else if (child.tagName() == QLatin1String("cite")) { 0756 if (!convertCite(child)) { 0757 return false; 0758 } 0759 } else if (child.tagName() == QLatin1String("empty-line")) { 0760 if (!convertEmptyLine(child)) { 0761 return false; 0762 } 0763 } else if (child.tagName() == QLatin1String("text-author")) { 0764 QTextBlockFormat format; 0765 format.setTextIndent(10); 0766 mCursor->insertBlock(format); 0767 if (!convertParagraph(child)) { 0768 return false; 0769 } 0770 } 0771 0772 child = child.nextSiblingElement(); 0773 } 0774 0775 return true; 0776 } 0777 0778 bool Converter::convertPoem(const QDomElement &element) 0779 { 0780 QDomElement child = element.firstChildElement(); 0781 while (!child.isNull()) { 0782 if (child.tagName() == QLatin1String("title")) { 0783 if (!convertTitle(child)) { 0784 return false; 0785 } 0786 } else if (child.tagName() == QLatin1String("epigraph")) { 0787 if (!convertEpigraph(child)) { 0788 return false; 0789 } 0790 } else if (child.tagName() == QLatin1String("empty-line")) { 0791 if (!convertEmptyLine(child)) { 0792 return false; 0793 } 0794 } else if (child.tagName() == QLatin1String("stanza")) { 0795 if (!convertStanza(child)) { 0796 return false; 0797 } 0798 } else if (child.tagName() == QLatin1String("text-author")) { 0799 QTextBlockFormat format; 0800 format.setTextIndent(10); 0801 mCursor->insertBlock(format); 0802 if (!convertParagraph(child)) { 0803 return false; 0804 } 0805 } 0806 0807 child = child.nextSiblingElement(); 0808 } 0809 0810 return true; 0811 } 0812 0813 bool Converter::convertSubTitle(const QDomElement &element) 0814 { 0815 QTextFrame *topFrame = mCursor->currentFrame(); 0816 0817 QTextFrameFormat frameFormat; 0818 frameFormat.setBorder(1); 0819 frameFormat.setPadding(8); 0820 frameFormat.setBackground(Qt::lightGray); 0821 frameFormat.setTopMargin(16); 0822 0823 mCursor->insertFrame(frameFormat); 0824 0825 if (!convertParagraph(element)) { 0826 return false; 0827 } 0828 0829 mCursor->setPosition(topFrame->lastPosition()); 0830 0831 return true; 0832 } 0833 0834 bool Converter::convertCite(const QDomElement &element) 0835 { 0836 QDomElement child = element.firstChildElement(); 0837 while (!child.isNull()) { 0838 if (child.tagName() == QLatin1String("p")) { 0839 QTextBlockFormat format; 0840 format.setTextIndent(10); 0841 mCursor->insertBlock(format); 0842 if (!convertParagraph(child)) { 0843 return false; 0844 } 0845 } else if (child.tagName() == QLatin1String("poem")) { 0846 if (!convertParagraph(child)) { 0847 return false; 0848 } 0849 } else if (child.tagName() == QLatin1String("text-author")) { 0850 QTextBlockFormat format; 0851 format.setTextIndent(10); 0852 mCursor->insertBlock(format); 0853 if (!convertParagraph(child)) { 0854 return false; 0855 } 0856 } else if (child.tagName() == QLatin1String("empty-line")) { 0857 if (!convertEmptyLine(child)) { 0858 return false; 0859 } 0860 } else if (child.tagName() == QLatin1String("subtitle")) { 0861 if (!convertSubTitle(child)) { 0862 return false; 0863 } 0864 } else if (child.tagName() == QLatin1String("table")) { 0865 if (!convertTable(child)) { 0866 return false; 0867 } 0868 } 0869 0870 child = child.nextSiblingElement(); 0871 } 0872 0873 return true; 0874 } 0875 0876 bool Converter::convertEmptyLine(const QDomElement &) 0877 { 0878 mCursor->insertText(QStringLiteral("\n\n")); 0879 return true; 0880 } 0881 0882 bool Converter::convertLink(const QDomElement &element) 0883 { 0884 QString href = element.attributeNS(QStringLiteral("http://www.w3.org/1999/xlink"), QStringLiteral("href")); 0885 QString type = element.attributeNS(QStringLiteral("http://www.w3.org/1999/xlink"), QStringLiteral("type")); 0886 0887 if (type == QLatin1String("note")) { 0888 mCursor->insertText(QStringLiteral("[")); 0889 } 0890 0891 int startPosition = mCursor->position(); 0892 0893 QTextCharFormat origFormat(mCursor->charFormat()); 0894 0895 QTextCharFormat format(mCursor->charFormat()); 0896 format.setForeground(Qt::blue); 0897 format.setFontUnderline(true); 0898 mCursor->setCharFormat(format); 0899 0900 QDomNode child = element.firstChild(); 0901 while (!child.isNull()) { 0902 if (child.isElement()) { 0903 const QDomElement childElement = child.toElement(); 0904 if (childElement.tagName() == QLatin1String("emphasis")) { 0905 if (!convertEmphasis(childElement)) { 0906 return false; 0907 } 0908 } else if (childElement.tagName() == QLatin1String("strong")) { 0909 if (!convertStrong(childElement)) { 0910 return false; 0911 } 0912 } else if (childElement.tagName() == QLatin1String("style")) { 0913 if (!convertStyle(childElement)) { 0914 return false; 0915 } 0916 } 0917 } else if (child.isText()) { 0918 const QDomText text = child.toText(); 0919 if (!text.isNull()) { 0920 mCursor->insertText(text.data()); 0921 } 0922 } 0923 0924 child = child.nextSibling(); 0925 } 0926 mCursor->setCharFormat(origFormat); 0927 0928 int endPosition = mCursor->position(); 0929 0930 if (type == QLatin1String("note")) { 0931 mCursor->insertText(QStringLiteral("]")); 0932 } 0933 0934 if (href.startsWith(QLatin1Char('#'))) { // local link 0935 mLocalLinks.insert(href.mid(1), QPair<int, int>(startPosition, endPosition)); 0936 } else { 0937 // external link 0938 Okular::BrowseAction *action = new Okular::BrowseAction(QUrl(href)); 0939 Q_EMIT addAction(action, startPosition, endPosition); 0940 } 0941 0942 return true; 0943 } 0944 0945 bool Converter::convertStanza(const QDomElement &element) 0946 { 0947 QDomElement child = element.firstChildElement(); 0948 while (!child.isNull()) { 0949 if (child.tagName() == QLatin1String("title")) { 0950 if (!convertTitle(child)) { 0951 return false; 0952 } 0953 } else if (child.tagName() == QLatin1String("subtitle")) { 0954 if (!convertSubTitle(child)) { 0955 return false; 0956 } 0957 } else if (child.tagName() == QLatin1String("v")) { 0958 QTextBlockFormat format; 0959 format.setTextIndent(50); 0960 mCursor->insertBlock(format); 0961 if (!convertParagraph(child)) { 0962 return false; 0963 } 0964 } 0965 0966 child = child.nextSiblingElement(); 0967 } 0968 0969 return true; 0970 } 0971 0972 bool Converter::convertCode(const QDomElement &element) 0973 { 0974 QTextCharFormat origFormat = mCursor->charFormat(); 0975 0976 QTextCharFormat codeFormat(origFormat); 0977 codeFormat.setFontFamilies({QStringLiteral("monospace")}); 0978 mCursor->setCharFormat(codeFormat); 0979 0980 if (!convertParagraph(element)) { 0981 return false; 0982 } 0983 0984 mCursor->setCharFormat(origFormat); 0985 0986 return true; 0987 } 0988 0989 bool Converter::convertSuperScript(const QDomElement &element) 0990 { 0991 QTextCharFormat origFormat = mCursor->charFormat(); 0992 0993 QTextCharFormat superScriptFormat(origFormat); 0994 superScriptFormat.setVerticalAlignment(QTextCharFormat::AlignSuperScript); 0995 mCursor->setCharFormat(superScriptFormat); 0996 0997 if (!convertParagraph(element)) { 0998 return false; 0999 } 1000 1001 mCursor->setCharFormat(origFormat); 1002 1003 return true; 1004 } 1005 1006 bool Converter::convertSubScript(const QDomElement &element) 1007 { 1008 QTextCharFormat origFormat = mCursor->charFormat(); 1009 1010 QTextCharFormat subScriptFormat(origFormat); 1011 subScriptFormat.setVerticalAlignment(QTextCharFormat::AlignSubScript); 1012 mCursor->setCharFormat(subScriptFormat); 1013 1014 if (!convertParagraph(element)) { 1015 return false; 1016 } 1017 1018 mCursor->setCharFormat(origFormat); 1019 1020 return true; 1021 } 1022 1023 bool Converter::convertTable(const QDomElement &element) 1024 { 1025 QTextFrame *topFrame = mCursor->currentFrame(); 1026 1027 QTextTable *table = nullptr; 1028 1029 QDomElement child = element.firstChildElement(); 1030 while (!child.isNull()) { 1031 if (child.tagName() == QLatin1String("tr")) { 1032 if (table) { 1033 table->appendRows(1); 1034 } else { 1035 QTextTableFormat tableFormat; 1036 tableFormat.setBorderStyle(QTextFrameFormat::BorderStyle_None); 1037 table = mCursor->insertTable(1, 1, tableFormat); 1038 } 1039 1040 if (!convertTableRow(child, *table)) { 1041 return false; 1042 } 1043 } 1044 1045 child = child.nextSiblingElement(); 1046 } 1047 1048 mCursor->setPosition(topFrame->lastPosition()); 1049 1050 return true; 1051 } 1052 1053 bool Converter::convertTableRow(const QDomElement &element, QTextTable &table) 1054 { 1055 QDomElement child = element.firstChildElement(); 1056 int column = 0; 1057 while (!child.isNull()) { 1058 if (child.tagName() == QLatin1String("th")) { 1059 if (!convertTableHeaderCell(child, table, column)) { 1060 return false; 1061 } 1062 } else if (child.tagName() == QLatin1String("td")) { 1063 if (!convertTableCell(child, table, column)) { 1064 return false; 1065 } 1066 } 1067 1068 child = child.nextSiblingElement(); 1069 } 1070 1071 return true; 1072 } 1073 1074 bool Converter::convertTableHeaderCell(const QDomElement &element, QTextTable &table, int &column) 1075 { 1076 QTextCharFormat charFormat; 1077 charFormat.setFontWeight(QFont::Bold); 1078 return convertTableCellHelper(element, table, column, charFormat); 1079 } 1080 1081 bool Converter::convertTableCell(const QDomElement &element, QTextTable &table, int &column) 1082 { 1083 QTextCharFormat charFormat; 1084 return convertTableCellHelper(element, table, column, charFormat); 1085 } 1086 1087 bool Converter::convertTableCellHelper(const QDomElement &element, QTextTable &table, int &column, const QTextCharFormat &charFormat) 1088 { 1089 int row = table.rows() - 1; 1090 1091 int colspan = qMax(element.attribute(QStringLiteral("colspan")).toInt(), 1); 1092 // TODO: rowspan 1093 // int rowspan = qMax(element.attribute(QStringLiteral("rowspan")).toInt(), 1); 1094 1095 int columnsToAppend = column + colspan - table.columns(); 1096 if (columnsToAppend > 0) { 1097 table.appendColumns(columnsToAppend); 1098 } 1099 1100 table.mergeCells(row, column, 1, colspan); 1101 1102 int cellCursorPosition = table.cellAt(row, column).firstPosition(); 1103 mCursor->setPosition(cellCursorPosition); 1104 1105 Qt::Alignment alignment; 1106 1107 QString halign = element.attribute(QStringLiteral("halign")); 1108 if (halign == QStringLiteral("center")) { 1109 alignment |= Qt::AlignHCenter; 1110 } else if (halign == QStringLiteral("right")) { 1111 alignment |= Qt::AlignRight; 1112 } else { 1113 alignment |= Qt::AlignLeft; 1114 } 1115 1116 QString valign = element.attribute(QStringLiteral("valign")); 1117 if (valign == QStringLiteral("center")) { 1118 alignment |= Qt::AlignVCenter; 1119 } else if (valign == QStringLiteral("bottom")) { 1120 alignment |= Qt::AlignBottom; 1121 } else { 1122 alignment |= Qt::AlignTop; 1123 } 1124 1125 QTextBlockFormat format; 1126 format.setAlignment(alignment); 1127 mCursor->insertBlock(format, charFormat); 1128 1129 if (!convertParagraph(element)) { 1130 return false; 1131 } 1132 1133 column += colspan; 1134 return true; 1135 }