File indexing completed on 2024-05-19 05:21:46

0001 /*
0002   SPDX-FileCopyrightText: 2019-2020 Montel Laurent <montel@kde.org>
0003 
0004   SPDX-License-Identifier: LGPL-2.0-or-later
0005 
0006 */
0007 
0008 #include "plaintextmarkupbuilder.h"
0009 #include <QDebug>
0010 namespace KPIMTextEdit
0011 {
0012 class PlainTextMarkupBuilderPrivate
0013 {
0014 public:
0015     PlainTextMarkupBuilderPrivate(PlainTextMarkupBuilder *b)
0016         : q_ptr(b)
0017     {
0018     }
0019 
0020     /**
0021     Get a letter string to represent a number.
0022 
0023     The numbers 1-26 are represented by a-z, and 27-52 by aa-az, 53-79 by
0024     ba-bz
0025     etc.
0026 
0027     @param The number to convert
0028     @return The letter string representation of the number.
0029   */
0030     [[nodiscard]] QString getLetterString(int itemNumber);
0031 
0032     [[nodiscard]] QString getRomanString(int itemNumber);
0033 
0034     /**
0035     Gets a block of references in the body of the text.
0036     This is an ordered list of links and images in the text.
0037   */
0038     [[nodiscard]] QString getReferences();
0039 
0040     QStringList mUrls;
0041     QList<QTextListFormat::Style> currentListItemStyles;
0042     QList<int> currentListItemNumbers;
0043 
0044     QString activeLink;
0045 
0046     QString mText;
0047 
0048     QString mQuoteprefix;
0049 
0050     PlainTextMarkupBuilder *const q_ptr;
0051 
0052     Q_DECLARE_PUBLIC(PlainTextMarkupBuilder)
0053 };
0054 }
0055 
0056 using namespace KPIMTextEdit;
0057 PlainTextMarkupBuilder::PlainTextMarkupBuilder()
0058     : d_ptr(new PlainTextMarkupBuilderPrivate(this))
0059 {
0060 }
0061 
0062 QString PlainTextMarkupBuilderPrivate::getRomanString(int item)
0063 {
0064     QString result;
0065     // Code based to gui/text/qtextlist.cpp
0066     if (item < 5000) {
0067         QString romanNumeral;
0068 
0069         // works for up to 4999 items
0070         auto romanSymbols = QStringLiteral("iiivixxxlxcccdcmmmm");
0071         int c[] = {1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000};
0072         auto n = item;
0073         for (auto i = 12; i >= 0; n %= c[i], i--) {
0074             auto q = n / c[i];
0075             if (q > 0) {
0076                 auto startDigit = i + (i + 3) / 4;
0077                 int numDigits;
0078                 if (i % 4) {
0079                     // c[i] == 4|5|9|40|50|90|400|500|900
0080                     if ((i - 2) % 4) {
0081                         // c[i] == 4|9|40|90|400|900 => with subtraction (IV,
0082                         // IX, XL, XC,
0083                         // ...)
0084                         numDigits = 2;
0085                     } else {
0086                         // c[i] == 5|50|500 (V, L, D)
0087                         numDigits = 1;
0088                     }
0089                 } else {
0090                     // c[i] == 1|10|100|1000 (I, II, III, X, XX, ...)
0091                     numDigits = q;
0092                 }
0093 
0094                 romanNumeral.append(QStringView(romanSymbols).mid(startDigit, numDigits).toString());
0095             }
0096         }
0097         result = romanNumeral;
0098     } else {
0099         result = QStringLiteral("?");
0100     }
0101     return result;
0102 }
0103 
0104 QString PlainTextMarkupBuilderPrivate::getLetterString(int itemNumber)
0105 {
0106     QString letterString;
0107     while (true) {
0108         // Create the letter string by prepending one char at a time.
0109         // The itemNumber is converted to a number in the base 36 (number of
0110         // letters
0111         // in the
0112         // alphabet plus 10) after being increased by 10 (to pass out the digits
0113         // 0
0114         // to 9).
0115         letterString.prepend(QStringLiteral("%1").arg((itemNumber % LETTERSINALPHABET) + DIGITSOFFSET,
0116                                                       0, // no padding while building this string.
0117                                                       LETTERSINALPHABET + DIGITSOFFSET));
0118         if ((itemNumber >= LETTERSINALPHABET)) {
0119             itemNumber = itemNumber / LETTERSINALPHABET;
0120             itemNumber--;
0121         } else {
0122             break;
0123         }
0124     }
0125     return letterString;
0126 }
0127 
0128 QString PlainTextMarkupBuilderPrivate::getReferences()
0129 {
0130     QString refs;
0131     if (!mUrls.isEmpty()) {
0132         refs.append(QStringLiteral("\n--------\n"));
0133 
0134         int index = 1;
0135         while (!mUrls.isEmpty()) {
0136             refs.append(QStringLiteral("[%1] %2\n").arg(index++).arg(mUrls.takeFirst()));
0137         }
0138     }
0139     return refs;
0140 }
0141 
0142 PlainTextMarkupBuilder::~PlainTextMarkupBuilder()
0143 {
0144     delete d_ptr;
0145 }
0146 
0147 void PlainTextMarkupBuilder::setQuotePrefix(const QString &prefix)
0148 {
0149     Q_D(PlainTextMarkupBuilder);
0150     d->mQuoteprefix = prefix;
0151 }
0152 
0153 void PlainTextMarkupBuilder::beginStrong()
0154 {
0155     Q_D(PlainTextMarkupBuilder);
0156     d->mText.append(QLatin1Char('*'));
0157 }
0158 
0159 void PlainTextMarkupBuilder::endStrong()
0160 {
0161     Q_D(PlainTextMarkupBuilder);
0162     d->mText.append(QLatin1Char('*'));
0163 }
0164 
0165 void PlainTextMarkupBuilder::beginEmph()
0166 {
0167     Q_D(PlainTextMarkupBuilder);
0168     d->mText.append(QLatin1Char('/'));
0169 }
0170 
0171 void PlainTextMarkupBuilder::endEmph()
0172 {
0173     Q_D(PlainTextMarkupBuilder);
0174     d->mText.append(QLatin1Char('/'));
0175 }
0176 
0177 void PlainTextMarkupBuilder::beginUnderline()
0178 {
0179     Q_D(PlainTextMarkupBuilder);
0180     d->mText.append(QLatin1Char('_'));
0181 }
0182 
0183 void PlainTextMarkupBuilder::endUnderline()
0184 {
0185     Q_D(PlainTextMarkupBuilder);
0186     d->mText.append(QLatin1Char('_'));
0187 }
0188 
0189 void PlainTextMarkupBuilder::beginStrikeout()
0190 {
0191     Q_D(PlainTextMarkupBuilder);
0192     d->mText.append(QLatin1Char('-'));
0193 }
0194 
0195 void PlainTextMarkupBuilder::endStrikeout()
0196 {
0197     Q_D(PlainTextMarkupBuilder);
0198     d->mText.append(QLatin1Char('-'));
0199 }
0200 
0201 void PlainTextMarkupBuilder::beginAnchor(const QString &href, const QString &name)
0202 {
0203     Q_D(PlainTextMarkupBuilder);
0204     Q_UNUSED(name)
0205     if (!d->mUrls.contains(href)) {
0206         d->mUrls.append(href);
0207     }
0208     d->activeLink = href;
0209 }
0210 
0211 void PlainTextMarkupBuilder::endAnchor()
0212 {
0213     Q_D(PlainTextMarkupBuilder);
0214     d->mText.append(QStringLiteral("[%1]").arg(d->mUrls.indexOf(d->activeLink) + 1));
0215 }
0216 
0217 void PlainTextMarkupBuilder::endParagraph()
0218 {
0219     Q_D(PlainTextMarkupBuilder);
0220     d->mText.append(QLatin1Char('\n'));
0221 }
0222 
0223 void PlainTextMarkupBuilder::addNewline()
0224 {
0225     Q_D(PlainTextMarkupBuilder);
0226     d->mText.append(QLatin1Char('\n'));
0227 }
0228 
0229 void PlainTextMarkupBuilder::insertHorizontalRule(int width)
0230 {
0231     Q_UNUSED(width)
0232     Q_D(PlainTextMarkupBuilder);
0233 
0234     d->mText.append(QStringLiteral("--------------------\n"));
0235 }
0236 
0237 int PlainTextMarkupBuilder::addReference(const QString &reference)
0238 {
0239     Q_D(PlainTextMarkupBuilder);
0240 
0241     if (!d->mUrls.contains(reference)) {
0242         d->mUrls.append(reference);
0243     }
0244     return d->mUrls.indexOf(reference) + 1;
0245 }
0246 
0247 void PlainTextMarkupBuilder::insertImage(const QString &src, qreal width, qreal height)
0248 {
0249     Q_D(PlainTextMarkupBuilder);
0250     Q_UNUSED(width)
0251     Q_UNUSED(height)
0252 
0253     const auto ref = addReference(src);
0254 
0255     d->mText.append(QStringLiteral("[%1]").arg(ref));
0256 }
0257 
0258 void PlainTextMarkupBuilder::beginList(QTextListFormat::Style style)
0259 {
0260     Q_D(PlainTextMarkupBuilder);
0261     d->currentListItemStyles.append(style);
0262     d->currentListItemNumbers.append(0);
0263 }
0264 
0265 void PlainTextMarkupBuilder::endList()
0266 {
0267     Q_D(PlainTextMarkupBuilder);
0268     if (!d->currentListItemNumbers.isEmpty()) {
0269         d->currentListItemStyles.removeLast();
0270         d->currentListItemNumbers.removeLast();
0271     }
0272 }
0273 
0274 void PlainTextMarkupBuilder::beginListItem()
0275 {
0276     Q_D(PlainTextMarkupBuilder);
0277     for (int i = 0, total = d->currentListItemNumbers.size(); i < total; ++i) {
0278         d->mText.append(QStringLiteral("    "));
0279     }
0280 
0281     auto itemNumber = d->currentListItemNumbers.last();
0282 
0283     switch (d->currentListItemStyles.last()) {
0284     case QTextListFormat::ListDisc:
0285         d->mText.append(QStringLiteral(" *  "));
0286         break;
0287     case QTextListFormat::ListCircle:
0288         d->mText.append(QStringLiteral(" o  "));
0289         break;
0290     case QTextListFormat::ListSquare:
0291         d->mText.append(QStringLiteral(" -  "));
0292         break;
0293     case QTextListFormat::ListDecimal:
0294         d->mText.append(QStringLiteral(" %1. ").arg(itemNumber + 1));
0295         break;
0296     case QTextListFormat::ListLowerAlpha:
0297         d->mText.append(QStringLiteral(" %1. ").arg(d->getLetterString(itemNumber)));
0298         break;
0299     case QTextListFormat::ListUpperAlpha:
0300         d->mText.append(QStringLiteral(" %1. ").arg(d->getLetterString(itemNumber).toUpper()));
0301         break;
0302     case QTextListFormat::ListLowerRoman:
0303         d->mText.append(QStringLiteral(" %1. ").arg(d->getRomanString(itemNumber + 1)));
0304         break;
0305     case QTextListFormat::ListUpperRoman:
0306         d->mText.append(QStringLiteral(" %1. ").arg(d->getRomanString(itemNumber + 1).toUpper()));
0307         break;
0308     default:
0309         break;
0310     }
0311 }
0312 
0313 void PlainTextMarkupBuilder::endListItem()
0314 {
0315     Q_D(PlainTextMarkupBuilder);
0316     d->currentListItemNumbers.last() = d->currentListItemNumbers.last() + 1;
0317     d->mText.append(QLatin1Char('\n'));
0318 }
0319 
0320 void PlainTextMarkupBuilder::beginSuperscript()
0321 {
0322     Q_D(PlainTextMarkupBuilder);
0323     d->mText.append(QStringLiteral("^{"));
0324 }
0325 
0326 void PlainTextMarkupBuilder::endSuperscript()
0327 {
0328     Q_D(PlainTextMarkupBuilder);
0329     d->mText.append(QLatin1Char('}'));
0330 }
0331 
0332 void PlainTextMarkupBuilder::beginSubscript()
0333 {
0334     Q_D(PlainTextMarkupBuilder);
0335     d->mText.append(QStringLiteral("_{"));
0336 }
0337 
0338 void PlainTextMarkupBuilder::endSubscript()
0339 {
0340     Q_D(PlainTextMarkupBuilder);
0341     d->mText.append(QLatin1Char('}'));
0342 }
0343 
0344 void PlainTextMarkupBuilder::appendLiteralText(const QString &text)
0345 {
0346     Q_D(PlainTextMarkupBuilder);
0347     d->mText.append(text);
0348 }
0349 
0350 void PlainTextMarkupBuilder::appendRawText(const QString &text)
0351 {
0352     Q_D(PlainTextMarkupBuilder);
0353     d->mText.append(text);
0354 }
0355 
0356 QString PlainTextMarkupBuilder::getResult()
0357 {
0358     Q_D(PlainTextMarkupBuilder);
0359     auto ret = d->mText;
0360     ret.append(d->getReferences());
0361     d->mText.clear();
0362     return ret;
0363 }
0364 
0365 void PlainTextMarkupBuilder::beginParagraph(Qt::Alignment a, qreal top, qreal bottom, qreal left, qreal right, bool leftToRightText)
0366 {
0367     Q_UNUSED(a)
0368     Q_UNUSED(leftToRightText)
0369     Q_D(PlainTextMarkupBuilder);
0370     if (isQuoteBlock(top, bottom, left, right)) {
0371         d->mText.append(d->mQuoteprefix);
0372     }
0373 }
0374 
0375 bool PlainTextMarkupBuilder::isQuoteBlock(qreal top, qreal bottom, qreal left, qreal right) const
0376 {
0377     Q_UNUSED(top)
0378     Q_UNUSED(bottom)
0379     return /*(top == 12) && (bottom == 12) &&*/ (left == 40) && (right == 40);
0380 }
0381 
0382 void PlainTextMarkupBuilder::beginBackground(const QBrush &brush)
0383 {
0384     Q_UNUSED(brush)
0385 }
0386 
0387 void PlainTextMarkupBuilder::beginFontFamily(const QString &family)
0388 {
0389     Q_UNUSED(family)
0390 }
0391 
0392 void PlainTextMarkupBuilder::beginFontPointSize(int size)
0393 {
0394     Q_UNUSED(size)
0395 }
0396 
0397 void PlainTextMarkupBuilder::beginForeground(const QBrush &brush)
0398 {
0399     Q_UNUSED(brush)
0400 }
0401 
0402 void PlainTextMarkupBuilder::beginHeader(int level)
0403 {
0404     Q_D(PlainTextMarkupBuilder);
0405     switch (level) {
0406     case 1:
0407         d->mText.append(QStringLiteral("# "));
0408         break;
0409     case 2:
0410         d->mText.append(QStringLiteral("## "));
0411         break;
0412     case 3:
0413         d->mText.append(QStringLiteral("### "));
0414         break;
0415     case 4:
0416         d->mText.append(QStringLiteral("#### "));
0417         break;
0418     case 5:
0419         d->mText.append(QStringLiteral("##### "));
0420         break;
0421     case 6:
0422         d->mText.append(QStringLiteral("###### "));
0423         break;
0424     default:
0425         break;
0426     }
0427 }
0428 
0429 void PlainTextMarkupBuilder::beginTable(qreal cellpadding, qreal cellspacing, const QString &width)
0430 {
0431     Q_UNUSED(cellpadding)
0432     Q_UNUSED(cellspacing)
0433     Q_UNUSED(width)
0434 }
0435 
0436 void PlainTextMarkupBuilder::beginTableCell(const QString &width, int colSpan, int rowSpan)
0437 {
0438     Q_UNUSED(width)
0439     Q_UNUSED(colSpan)
0440     Q_UNUSED(rowSpan)
0441 }
0442 
0443 void PlainTextMarkupBuilder::beginTableHeaderCell(const QString &width, int colSpan, int rowSpan)
0444 {
0445     Q_UNUSED(width)
0446     Q_UNUSED(colSpan)
0447     Q_UNUSED(rowSpan)
0448 }
0449 
0450 void PlainTextMarkupBuilder::beginTableRow()
0451 {
0452 }
0453 
0454 void PlainTextMarkupBuilder::endBackground()
0455 {
0456 }
0457 
0458 void PlainTextMarkupBuilder::endFontFamily()
0459 {
0460 }
0461 
0462 void PlainTextMarkupBuilder::endFontPointSize()
0463 {
0464 }
0465 
0466 void PlainTextMarkupBuilder::endForeground()
0467 {
0468 }
0469 
0470 void PlainTextMarkupBuilder::endHeader(int level)
0471 {
0472     Q_D(PlainTextMarkupBuilder);
0473     qDebug() << " void PlainTextMarkupBuilder::endHeader(int level)" << level;
0474     switch (level) {
0475     case 1:
0476         d->mText.append(QStringLiteral(" #\n"));
0477         break;
0478     case 2:
0479         d->mText.append(QStringLiteral(" ##\n"));
0480         break;
0481     case 3:
0482         d->mText.append(QStringLiteral(" ###\n"));
0483         break;
0484     case 4:
0485         d->mText.append(QStringLiteral(" ####\n"));
0486         break;
0487     case 5:
0488         d->mText.append(QStringLiteral(" #####\n"));
0489         break;
0490     case 6:
0491         d->mText.append(QStringLiteral(" ######\n"));
0492         break;
0493     default:
0494         break;
0495     }
0496 }
0497 
0498 void PlainTextMarkupBuilder::endTable()
0499 {
0500 }
0501 
0502 void PlainTextMarkupBuilder::endTableCell()
0503 {
0504 }
0505 
0506 void PlainTextMarkupBuilder::endTableHeaderCell()
0507 {
0508 }
0509 
0510 void PlainTextMarkupBuilder::endTableRow()
0511 {
0512 }
0513 
0514 void PlainTextMarkupBuilder::addSingleBreakLine()
0515 {
0516     Q_D(PlainTextMarkupBuilder);
0517     d->mText.append(QLatin1Char('\n'));
0518 }