File indexing completed on 2024-05-19 05:21:47
0001 /* 0002 SPDX-FileCopyrightText: 2020-2024 Laurent Montel <montel@kde.org> 0003 based on code from Stephen Kelly <steveire@gmail.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "texthtmlbuilder.h" 0009 0010 #include <QDebug> 0011 #include <QList> 0012 #include <QTextDocument> 0013 0014 namespace KPIMTextEdit 0015 { 0016 class TextHTMLBuilderPrivate 0017 { 0018 public: 0019 TextHTMLBuilderPrivate(TextHTMLBuilder *b) 0020 : q_ptr(b) 0021 { 0022 } 0023 0024 QList<QTextListFormat::Style> currentListItemStyles; 0025 QString mText; 0026 0027 TextHTMLBuilder *const q_ptr; 0028 0029 Q_DECLARE_PUBLIC(TextHTMLBuilder) 0030 }; 0031 } 0032 0033 using namespace KPIMTextEdit; 0034 TextHTMLBuilder::TextHTMLBuilder() 0035 : AbstractMarkupBuilder() 0036 , d_ptr(new TextHTMLBuilderPrivate(this)) 0037 { 0038 } 0039 0040 TextHTMLBuilder::~TextHTMLBuilder() 0041 { 0042 delete d_ptr; 0043 } 0044 0045 void TextHTMLBuilder::beginStrong() 0046 { 0047 Q_D(TextHTMLBuilder); 0048 d->mText.append(QStringLiteral("<strong>")); 0049 } 0050 0051 void TextHTMLBuilder::endStrong() 0052 { 0053 Q_D(TextHTMLBuilder); 0054 d->mText.append(QStringLiteral("</strong>")); 0055 } 0056 0057 void TextHTMLBuilder::beginEmph() 0058 { 0059 Q_D(TextHTMLBuilder); 0060 d->mText.append(QStringLiteral("<em>")); 0061 } 0062 0063 void TextHTMLBuilder::endEmph() 0064 { 0065 Q_D(TextHTMLBuilder); 0066 d->mText.append(QStringLiteral("</em>")); 0067 } 0068 0069 void TextHTMLBuilder::beginUnderline() 0070 { 0071 Q_D(TextHTMLBuilder); 0072 d->mText.append(QStringLiteral("<u>")); 0073 } 0074 0075 void TextHTMLBuilder::endUnderline() 0076 { 0077 Q_D(TextHTMLBuilder); 0078 d->mText.append(QStringLiteral("</u>")); 0079 } 0080 0081 void TextHTMLBuilder::beginStrikeout() 0082 { 0083 Q_D(TextHTMLBuilder); 0084 d->mText.append(QStringLiteral("<s>")); 0085 } 0086 0087 void TextHTMLBuilder::endStrikeout() 0088 { 0089 Q_D(TextHTMLBuilder); 0090 d->mText.append(QStringLiteral("</s>")); 0091 } 0092 0093 void TextHTMLBuilder::beginForeground(const QBrush &brush) 0094 { 0095 Q_D(TextHTMLBuilder); 0096 d->mText.append(QStringLiteral("<span style=\"color:%1;\">").arg(brush.color().name())); 0097 } 0098 0099 void TextHTMLBuilder::endForeground() 0100 { 0101 Q_D(TextHTMLBuilder); 0102 d->mText.append(QStringLiteral("</span>")); 0103 } 0104 0105 void TextHTMLBuilder::beginBackground(const QBrush &brush) 0106 { 0107 Q_D(TextHTMLBuilder); 0108 d->mText.append(QStringLiteral("<span style=\"background-color:%1;\">").arg(brush.color().name())); 0109 } 0110 0111 void TextHTMLBuilder::endBackground() 0112 { 0113 Q_D(TextHTMLBuilder); 0114 d->mText.append(QStringLiteral("</span>")); 0115 } 0116 0117 void TextHTMLBuilder::beginAnchor(const QString &href, const QString &name) 0118 { 0119 Q_D(TextHTMLBuilder); 0120 if (!href.isEmpty()) { 0121 if (!name.isEmpty()) { 0122 d->mText.append(QStringLiteral("<a href=\"%1\" name=\"%2\">").arg(href, name)); 0123 } else { 0124 d->mText.append(QStringLiteral("<a href=\"%1\">").arg(href)); 0125 } 0126 } else { 0127 if (!name.isEmpty()) { 0128 d->mText.append(QStringLiteral("<a name=\"%1\">").arg(name)); 0129 } 0130 } 0131 } 0132 0133 void TextHTMLBuilder::endAnchor() 0134 { 0135 Q_D(TextHTMLBuilder); 0136 d->mText.append(QStringLiteral("</a>")); 0137 } 0138 0139 void TextHTMLBuilder::beginFontFamily(const QString &family) 0140 { 0141 Q_D(TextHTMLBuilder); 0142 d->mText.append(QStringLiteral("<span style=\"font-family:%1;\">").arg(family)); 0143 } 0144 0145 void TextHTMLBuilder::endFontFamily() 0146 { 0147 Q_D(TextHTMLBuilder); 0148 d->mText.append(QStringLiteral("</span>")); 0149 } 0150 0151 void TextHTMLBuilder::beginFontPointSize(int size) 0152 { 0153 Q_D(TextHTMLBuilder); 0154 d->mText.append(QStringLiteral("<span style=\"font-size:%1pt;\">").arg(QString::number(size))); 0155 } 0156 0157 void TextHTMLBuilder::endFontPointSize() 0158 { 0159 Q_D(TextHTMLBuilder); 0160 d->mText.append(QStringLiteral("</span>")); 0161 } 0162 0163 void TextHTMLBuilder::beginParagraph(Qt::Alignment al, qreal topMargin, qreal bottomMargin, qreal leftMargin, qreal rightMargin, bool leftToRightText) 0164 { 0165 Q_D(TextHTMLBuilder); 0166 // Don't put paragraph tags inside li tags. Qt bug reported. 0167 // if (currentListItemStyles.size() != 0) 0168 // { 0169 QString styleString; 0170 styleString.append(QStringLiteral("margin-top:%1;").arg(topMargin)); 0171 styleString.append(QStringLiteral("margin-bottom:%1;").arg(bottomMargin)); 0172 styleString.append(QStringLiteral("margin-left:%1;").arg(leftMargin)); 0173 styleString.append(QStringLiteral("margin-right:%1;").arg(rightMargin)); 0174 0175 // Using == doesn't work here. 0176 // Using bitwise comparison because an alignment can contain a vertical and 0177 // a 0178 // horizontal part. 0179 if (al & Qt::AlignRight) { 0180 d->mText.append(QStringLiteral("<p align=\"right\" ")); 0181 } else if (al & Qt::AlignHCenter) { 0182 d->mText.append(QStringLiteral("<p align=\"center\" ")); 0183 } else if (al & Qt::AlignJustify) { 0184 d->mText.append(QStringLiteral("<p align=\"justify\" ")); 0185 } else if (al & Qt::AlignLeft) { 0186 d->mText.append(QStringLiteral("<p")); 0187 } else { 0188 d->mText.append(QStringLiteral("<p")); 0189 } 0190 // Bug in grantlee => style is not defined 0191 if (!styleString.isEmpty()) { 0192 d->mText.append(QStringLiteral(" style=\"") + styleString + QLatin1Char('"')); 0193 } 0194 if (leftToRightText) { 0195 d->mText.append(QStringLiteral(" dir='rtl'")); 0196 } 0197 d->mText.append(QLatin1Char('>')); 0198 // } 0199 } 0200 0201 void TextHTMLBuilder::beginHeader(int level) 0202 { 0203 Q_D(TextHTMLBuilder); 0204 switch (level) { 0205 case 1: 0206 d->mText.append(QStringLiteral("<h1>")); 0207 break; 0208 case 2: 0209 d->mText.append(QStringLiteral("<h2>")); 0210 break; 0211 case 3: 0212 d->mText.append(QStringLiteral("<h3>")); 0213 break; 0214 case 4: 0215 d->mText.append(QStringLiteral("<h4>")); 0216 break; 0217 case 5: 0218 d->mText.append(QStringLiteral("<h5>")); 0219 break; 0220 case 6: 0221 d->mText.append(QStringLiteral("<h6>")); 0222 break; 0223 default: 0224 break; 0225 } 0226 } 0227 0228 void TextHTMLBuilder::endHeader(int level) 0229 { 0230 Q_D(TextHTMLBuilder); 0231 switch (level) { 0232 case 1: 0233 d->mText.append(QStringLiteral("</h1>")); 0234 break; 0235 case 2: 0236 d->mText.append(QStringLiteral("</h2>")); 0237 break; 0238 case 3: 0239 d->mText.append(QStringLiteral("</h3>")); 0240 break; 0241 case 4: 0242 d->mText.append(QStringLiteral("</h4>")); 0243 break; 0244 case 5: 0245 d->mText.append(QStringLiteral("</h5>")); 0246 break; 0247 case 6: 0248 d->mText.append(QStringLiteral("</h6>")); 0249 break; 0250 default: 0251 break; 0252 } 0253 } 0254 0255 void TextHTMLBuilder::endParagraph() 0256 { 0257 Q_D(TextHTMLBuilder); 0258 d->mText.append(QStringLiteral("</p>\n")); 0259 } 0260 0261 void TextHTMLBuilder::addNewline() 0262 { 0263 Q_D(TextHTMLBuilder); 0264 d->mText.append(QStringLiteral("<p> ")); 0265 } 0266 0267 void TextHTMLBuilder::insertHorizontalRule(int width) 0268 { 0269 Q_D(TextHTMLBuilder); 0270 if (width != -1) { 0271 d->mText.append(QStringLiteral("<hr width=\"%1\" />\n").arg(width)); 0272 } 0273 d->mText.append(QStringLiteral("<hr />\n")); 0274 } 0275 0276 void TextHTMLBuilder::insertImage(const QString &src, qreal width, qreal height) 0277 { 0278 Q_D(TextHTMLBuilder); 0279 d->mText.append(QStringLiteral("<img src=\"%1\" ").arg(src)); 0280 if (width != 0) { 0281 d->mText.append(QStringLiteral("width=\"%2\" ").arg(width)); 0282 } 0283 if (height != 0) { 0284 d->mText.append(QStringLiteral("height=\"%2\" ").arg(height)); 0285 } 0286 d->mText.append(QStringLiteral("/>")); 0287 } 0288 0289 void TextHTMLBuilder::beginList(QTextListFormat::Style type) 0290 { 0291 Q_D(TextHTMLBuilder); 0292 d->currentListItemStyles.append(type); 0293 switch (type) { 0294 case QTextListFormat::ListDisc: 0295 d->mText.append(QStringLiteral("<ul type=\"disc\">\n")); 0296 break; 0297 case QTextListFormat::ListCircle: 0298 d->mText.append(QStringLiteral("\n<ul type=\"circle\">\n")); 0299 break; 0300 case QTextListFormat::ListSquare: 0301 d->mText.append(QStringLiteral("\n<ul type=\"square\">\n")); 0302 break; 0303 case QTextListFormat::ListDecimal: 0304 d->mText.append(QStringLiteral("\n<ol type=\"1\">\n")); 0305 break; 0306 case QTextListFormat::ListLowerAlpha: 0307 d->mText.append(QStringLiteral("\n<ol type=\"a\">\n")); 0308 break; 0309 case QTextListFormat::ListUpperAlpha: 0310 d->mText.append(QStringLiteral("\n<ol type=\"A\">\n")); 0311 break; 0312 case QTextListFormat::ListLowerRoman: 0313 d->mText.append(QStringLiteral("\n<ol type=\"i\">\n")); 0314 break; 0315 case QTextListFormat::ListUpperRoman: 0316 d->mText.append(QStringLiteral("\n<ol type=\"I\">\n")); 0317 break; 0318 default: 0319 break; 0320 } 0321 } 0322 void TextHTMLBuilder::endList() 0323 { 0324 Q_D(TextHTMLBuilder); 0325 switch (d->currentListItemStyles.last()) { 0326 case QTextListFormat::ListDisc: 0327 case QTextListFormat::ListCircle: 0328 case QTextListFormat::ListSquare: 0329 d->mText.append(QStringLiteral("</ul>\n")); 0330 break; 0331 case QTextListFormat::ListDecimal: 0332 case QTextListFormat::ListLowerAlpha: 0333 case QTextListFormat::ListUpperAlpha: 0334 case QTextListFormat::ListLowerRoman: 0335 case QTextListFormat::ListUpperRoman: 0336 d->mText.append(QStringLiteral("</ol>\n")); 0337 break; 0338 default: 0339 break; 0340 } 0341 d->currentListItemStyles.removeLast(); 0342 } 0343 void TextHTMLBuilder::beginListItem() 0344 { 0345 Q_D(TextHTMLBuilder); 0346 d->mText.append(QStringLiteral("<li>")); 0347 } 0348 0349 void TextHTMLBuilder::endListItem() 0350 { 0351 Q_D(TextHTMLBuilder); 0352 d->mText.append(QStringLiteral("</li>\n")); 0353 } 0354 0355 void TextHTMLBuilder::beginSuperscript() 0356 { 0357 Q_D(TextHTMLBuilder); 0358 d->mText.append(QStringLiteral("<sup>")); 0359 } 0360 0361 void TextHTMLBuilder::endSuperscript() 0362 { 0363 Q_D(TextHTMLBuilder); 0364 d->mText.append(QStringLiteral("</sup>")); 0365 } 0366 0367 void TextHTMLBuilder::beginSubscript() 0368 { 0369 Q_D(TextHTMLBuilder); 0370 d->mText.append(QStringLiteral("<sub>")); 0371 } 0372 0373 void TextHTMLBuilder::endSubscript() 0374 { 0375 Q_D(TextHTMLBuilder); 0376 d->mText.append(QStringLiteral("</sub>")); 0377 } 0378 0379 void TextHTMLBuilder::beginTable(qreal cellpadding, qreal cellspacing, const QString &width) 0380 { 0381 Q_D(TextHTMLBuilder); 0382 d->mText.append(QStringLiteral("<table cellpadding=\"%1\" cellspacing=\"%2\" " 0383 "width=\"%3\" border=\"1\">") 0384 .arg(cellpadding) 0385 .arg(cellspacing) 0386 .arg(width)); 0387 } 0388 0389 void TextHTMLBuilder::beginTableRow() 0390 { 0391 Q_D(TextHTMLBuilder); 0392 d->mText.append(QStringLiteral("<tr>")); 0393 } 0394 0395 void TextHTMLBuilder::beginTableHeaderCell(const QString &width, int colspan, int rowspan) 0396 { 0397 Q_D(TextHTMLBuilder); 0398 d->mText.append(QStringLiteral("<th width=\"%1\" colspan=\"%2\" rowspan=\"%3\">").arg(width).arg(colspan).arg(rowspan)); 0399 } 0400 0401 void TextHTMLBuilder::beginTableCell(const QString &width, int colspan, int rowspan) 0402 { 0403 Q_D(TextHTMLBuilder); 0404 d->mText.append(QStringLiteral("<td width=\"%1\" colspan=\"%2\" rowspan=\"%3\">").arg(width).arg(colspan).arg(rowspan)); 0405 } 0406 0407 void TextHTMLBuilder::endTable() 0408 { 0409 Q_D(TextHTMLBuilder); 0410 d->mText.append(QStringLiteral("</table>")); 0411 } 0412 0413 void TextHTMLBuilder::endTableRow() 0414 { 0415 Q_D(TextHTMLBuilder); 0416 d->mText.append(QStringLiteral("</tr>")); 0417 } 0418 0419 void TextHTMLBuilder::endTableHeaderCell() 0420 { 0421 Q_D(TextHTMLBuilder); 0422 d->mText.append(QStringLiteral("</th>")); 0423 } 0424 0425 void TextHTMLBuilder::endTableCell() 0426 { 0427 Q_D(TextHTMLBuilder); 0428 d->mText.append(QStringLiteral("</td>")); 0429 } 0430 0431 void TextHTMLBuilder::appendLiteralText(const QString &text) 0432 { 0433 Q_D(TextHTMLBuilder); 0434 const QString textEscaped = text.toHtmlEscaped(); 0435 QString textEscapedResult; 0436 for (int i = 0, total = textEscaped.length(); i < total; ++i) { 0437 const QChar c = textEscaped.at(i); 0438 0439 if (c == QLatin1Char(' ')) { 0440 if (i == 0) { 0441 textEscapedResult += QStringLiteral(" "); 0442 } else { 0443 if (i + 1 < textEscaped.length() && (textEscaped.at(i + 1) == QLatin1Char(' '))) { 0444 textEscapedResult += QStringLiteral(" "); 0445 } else { 0446 textEscapedResult += c; 0447 } 0448 } 0449 } else if (c == QLatin1Char('\t')) { 0450 textEscapedResult += QStringLiteral(" "); 0451 } else { 0452 textEscapedResult += c; 0453 } 0454 } 0455 d->mText.append(textEscapedResult); 0456 } 0457 0458 void TextHTMLBuilder::appendRawText(const QString &text) 0459 { 0460 Q_D(TextHTMLBuilder); 0461 d->mText.append(text); 0462 } 0463 0464 QString TextHTMLBuilder::getResult() 0465 { 0466 Q_D(TextHTMLBuilder); 0467 auto ret = d->mText; 0468 d->mText.clear(); 0469 return ret; 0470 } 0471 0472 void KPIMTextEdit::TextHTMLBuilder::addSingleBreakLine() 0473 { 0474 Q_D(TextHTMLBuilder); 0475 d->mText.append(QLatin1StringView("<br />")); 0476 }