File indexing completed on 2024-04-28 05:50:39

0001 /*
0002     SPDX-FileCopyrightText: 2006-2008 Robert Knight <robertknight@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 // Own
0008 #include "HTMLDecoder.h"
0009 
0010 // Konsole characters
0011 #include <ExtendedCharTable.h>
0012 
0013 // Qt
0014 #include <QTextStream>
0015 
0016 using namespace Konsole;
0017 
0018 HTMLDecoder::HTMLDecoder(const QColor *colorTable, const QFont &profileFont)
0019     : _output(nullptr)
0020     , _profileFont(profileFont)
0021     , _innerSpanOpen(false)
0022     , _lastRendition(DEFAULT_RENDITION)
0023     , _lastForeColor(CharacterColor())
0024     , _lastBackColor(CharacterColor())
0025     , _validProfile(false)
0026 {
0027     Q_ASSERT(colorTable);
0028     std::copy_n(colorTable, TABLE_COLORS, _colorTable);
0029 }
0030 
0031 void HTMLDecoder::begin(QTextStream *output)
0032 {
0033     _output = output;
0034 
0035     if (_validProfile) {
0036         QString style;
0037 
0038         style.append(QStringLiteral("font-family:'%1',monospace;").arg(_profileFont.family()));
0039 
0040         // Prefer point size if set
0041         if (_profileFont.pointSizeF() > 0) {
0042             style.append(QStringLiteral("font-size:%1pt;").arg(_profileFont.pointSizeF()));
0043         } else {
0044             style.append(QStringLiteral("font-size:%1px;").arg(_profileFont.pixelSize()));
0045         }
0046 
0047         style.append(QStringLiteral("color:%1;").arg(_colorTable[DEFAULT_FORE_COLOR].name()));
0048         style.append(QStringLiteral("background-color:%1;").arg(_colorTable[DEFAULT_BACK_COLOR].name()));
0049 
0050         *output << QStringLiteral("<body style=\"%1\">").arg(style);
0051     } else {
0052         QString text;
0053         openSpan(text, QStringLiteral("font-family:monospace"));
0054         *output << text;
0055     }
0056 }
0057 
0058 void HTMLDecoder::end()
0059 {
0060     Q_ASSERT(_output);
0061 
0062     if (_validProfile) {
0063         *_output << QStringLiteral("</body>");
0064     } else {
0065         QString text;
0066         closeSpan(text);
0067         *_output << text;
0068     }
0069 
0070     _output = nullptr;
0071 }
0072 
0073 // TODO: Support for LineProperty (mainly double width , double height)
0074 void HTMLDecoder::decodeLine(const Character *const characters, int count, LineProperty /*properties*/)
0075 {
0076     Q_ASSERT(_output);
0077 
0078     QString text;
0079 
0080     int spaceCount = 0;
0081 
0082     for (int i = 0; i < count; i++) {
0083         // check if appearance of character is different from previous char
0084         if (characters[i].rendition.all != _lastRendition || characters[i].foregroundColor != _lastForeColor
0085             || characters[i].backgroundColor != _lastBackColor) {
0086             if (_innerSpanOpen) {
0087                 closeSpan(text);
0088                 _innerSpanOpen = false;
0089             }
0090 
0091             _lastRendition = characters[i].rendition.all;
0092             _lastForeColor = characters[i].foregroundColor;
0093             _lastBackColor = characters[i].backgroundColor;
0094 
0095             // build up style string
0096             QString style;
0097 
0098             bool useBold = (_lastRendition & RE_BOLD) != 0;
0099             if (useBold) {
0100                 style.append(QLatin1String("font-weight:bold;"));
0101             }
0102 
0103             if ((_lastRendition & RE_UNDERLINE_MASK) != 0) {
0104                 style.append(QLatin1String("font-decoration:underline;"));
0105             }
0106 
0107             style.append(QStringLiteral("color:%1;").arg(_lastForeColor.color(_colorTable).name()));
0108 
0109             style.append(QStringLiteral("background-color:%1;").arg(_lastBackColor.color(_colorTable).name()));
0110 
0111             // open the span with the current style
0112             openSpan(text, style);
0113             _innerSpanOpen = true;
0114         }
0115 
0116         // handle whitespace
0117         if (characters[i].isSpace()) {
0118             spaceCount++;
0119         } else {
0120             spaceCount = 0;
0121         }
0122 
0123         // output current character
0124         if (spaceCount < 2) {
0125             if ((characters[i].rendition.all & RE_EXTENDED_CHAR) != 0) {
0126                 ushort extendedCharLength = 0;
0127                 const char32_t *chars = ExtendedCharTable::instance.lookupExtendedChar(characters[i].character, extendedCharLength);
0128                 if (chars != nullptr) {
0129                     text.append(QString::fromUcs4(chars, extendedCharLength));
0130                 }
0131             } else {
0132                 // escape HTML tag characters and just display others as they are
0133                 const QChar ch(characters[i].character);
0134                 if (ch == QLatin1Char('<')) {
0135                     text.append(QLatin1String("&lt;"));
0136                 } else if (ch == QLatin1Char('>')) {
0137                     text.append(QLatin1String("&gt;"));
0138                 } else if (ch == QLatin1Char('&')) {
0139                     text.append(QLatin1String("&amp;"));
0140                 } else {
0141                     text.append(ch);
0142                 }
0143             }
0144         } else {
0145             // HTML truncates multiple spaces, so use a space marker instead
0146             // Use &#160 instead of &nbsp so xmllint will work.
0147             text.append(QLatin1String("&#160;"));
0148         }
0149     }
0150 
0151     // close any remaining open inner spans
0152     if (_innerSpanOpen) {
0153         closeSpan(text);
0154         _innerSpanOpen = false;
0155     }
0156 
0157     // start new line
0158     text.append(QLatin1String("<br>"));
0159 
0160     *_output << text;
0161 }
0162 
0163 void HTMLDecoder::openSpan(QString &text, const QString &style)
0164 {
0165     text.append(QStringLiteral("<span style=\"%1\">").arg(style));
0166 }
0167 
0168 void HTMLDecoder::closeSpan(QString &text)
0169 {
0170     text.append(QLatin1String("</span>"));
0171 }