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 }