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>&nbsp;"));
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("&nbsp;");
0442             } else {
0443                 if (i + 1 < textEscaped.length() && (textEscaped.at(i + 1) == QLatin1Char(' '))) {
0444                     textEscapedResult += QStringLiteral("&nbsp;");
0445                 } else {
0446                     textEscapedResult += c;
0447                 }
0448             }
0449         } else if (c == QLatin1Char('\t')) {
0450             textEscapedResult += QStringLiteral("&nbsp;&nbsp;&nbsp; ");
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 }