KSyntaxHighlighting

htmlhighlighter.cpp
1 /*
2  SPDX-FileCopyrightText: 2016 Volker Krause <[email protected]>
3  SPDX-FileCopyrightText: 2018 Christoph Cullmann <[email protected]>
4 
5  SPDX-License-Identifier: MIT
6 */
7 
8 #include "htmlhighlighter.h"
9 #include "definition.h"
10 #include "format.h"
11 #include "ksyntaxhighlighting_logging.h"
12 #include "state.h"
13 #include "theme.h"
14 
15 #include <QFile>
16 #include <QFileInfo>
17 #include <QTextStream>
18 #include <QVarLengthArray>
19 
20 using namespace KSyntaxHighlighting;
21 
22 class KSyntaxHighlighting::HtmlHighlighterPrivate
23 {
24 public:
25  std::unique_ptr<QTextStream> out;
26  std::unique_ptr<QFile> file;
27  QString currentLine;
28 };
29 
30 HtmlHighlighter::HtmlHighlighter()
31  : d(new HtmlHighlighterPrivate())
32 {
33 }
34 
35 HtmlHighlighter::~HtmlHighlighter()
36 {
37 }
38 
39 void HtmlHighlighter::setOutputFile(const QString &fileName)
40 {
41  d->file.reset(new QFile(fileName));
42  if (!d->file->open(QFile::WriteOnly | QFile::Truncate)) {
43  qCWarning(Log) << "Failed to open output file" << fileName << ":" << d->file->errorString();
44  return;
45  }
46  d->out.reset(new QTextStream(d->file.get()));
47 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
48  d->out->setEncoding(QStringConverter::Utf8);
49 #else
50  d->out->setCodec("UTF-8");
51 #endif
52 }
53 
54 void HtmlHighlighter::setOutputFile(FILE *fileHandle)
55 {
56  d->out.reset(new QTextStream(fileHandle, QIODevice::WriteOnly));
57 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
58  d->out->setEncoding(QStringConverter::Utf8);
59 #else
60  d->out->setCodec("UTF-8");
61 #endif
62 }
63 
64 void HtmlHighlighter::highlightFile(const QString &fileName, const QString &title)
65 {
66  QFileInfo fi(fileName);
67  QFile f(fileName);
68  if (!f.open(QFile::ReadOnly)) {
69  qCWarning(Log) << "Failed to open input file" << fileName << ":" << f.errorString();
70  return;
71  }
72 
73  if (title.isEmpty()) {
74  highlightData(&f, fi.fileName());
75  } else {
76  highlightData(&f, title);
77  }
78 }
79 
80 /**
81  * @brief toHtmlRgba
82  * Converts QColor -> rgba(r, g, b, a) if there is an alpha channel
83  * otherwise it will just return the hexcode. This is because QColor
84  * outputs #AARRGGBB, whereas browser support #RRGGBBAA.
85  *
86  * @param color
87  * @return
88  */
89 static QString toHtmlRgbaString(const QColor &color)
90 {
91  if (color.alpha() == 0xFF) {
92  return color.name();
93  }
94 
95  QString rgba = QStringLiteral("rgba(");
96  rgba.append(QString::number(color.red()));
97  rgba.append(QLatin1Char(','));
98  rgba.append(QString::number(color.green()));
99  rgba.append(QLatin1Char(','));
100  rgba.append(QString::number(color.blue()));
101  rgba.append(QLatin1Char(','));
102  // this must be alphaF
103  rgba.append(QString::number(color.alphaF()));
104  rgba.append(QLatin1Char(')'));
105  return rgba;
106 }
107 
108 void HtmlHighlighter::highlightData(QIODevice *dev, const QString &title)
109 {
110  if (!d->out) {
111  qCWarning(Log) << "No output stream defined!";
112  return;
113  }
114 
115  QString htmlTitle;
116  if (title.isEmpty()) {
117  htmlTitle = QStringLiteral("Kate Syntax Highlighter");
118  } else {
119  htmlTitle = title.toHtmlEscaped();
120  }
121 
122  State state;
123  *d->out << "<!DOCTYPE html>\n";
124  *d->out << "<html><head>\n";
125  *d->out << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n";
126  *d->out << "<title>" << htmlTitle << "</title>\n";
127  *d->out << "<meta name=\"generator\" content=\"KF5::SyntaxHighlighting - Definition (" << definition().name() << ") - Theme (" << theme().name()
128  << ")\"/>\n";
129  *d->out << "</head><body";
130  *d->out << " style=\"background-color:" << toHtmlRgbaString(QColor::fromRgba(theme().editorColor(Theme::BackgroundColor)));
131  if (theme().textColor(Theme::Normal)) {
132  *d->out << ";color:" << toHtmlRgbaString(QColor::fromRgba(theme().textColor(Theme::Normal)));
133  }
134  *d->out << "\"><pre>\n";
135 
136  QTextStream in(dev);
137 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
138  in.setCodec("UTF-8");
139 #endif
140  while (in.readLineInto(&d->currentLine)) {
141  state = highlightLine(d->currentLine, state);
142  *d->out << "\n";
143  }
144 
145  *d->out << "</pre></body></html>\n";
146  d->out->flush();
147 
148  d->out.reset();
149  d->file.reset();
150 }
151 
152 void HtmlHighlighter::applyFormat(int offset, int length, const Format &format)
153 {
154  if (length == 0) {
155  return;
156  }
157 
158  // collect potential output, cheaper than thinking about "is there any?"
159  QVarLengthArray<QString, 16> formatOutput;
160  if (format.hasTextColor(theme())) {
161  formatOutput << QStringLiteral("color:") << toHtmlRgbaString(format.textColor(theme())) << QStringLiteral(";");
162  }
163  if (format.hasBackgroundColor(theme())) {
164  formatOutput << QStringLiteral("background-color:") << toHtmlRgbaString(format.backgroundColor(theme())) << QStringLiteral(";");
165  }
166  if (format.isBold(theme())) {
167  formatOutput << QStringLiteral("font-weight:bold;");
168  }
169  if (format.isItalic(theme())) {
170  formatOutput << QStringLiteral("font-style:italic;");
171  }
172  if (format.isUnderline(theme())) {
173  formatOutput << QStringLiteral("text-decoration:underline;");
174  }
175  if (format.isStrikeThrough(theme())) {
176  formatOutput << QStringLiteral("text-decoration:line-through;");
177  }
178 
179  if (!formatOutput.isEmpty()) {
180  *d->out << "<span style=\"";
181  for (const auto &out : std::as_const(formatOutput)) {
182  *d->out << out;
183  }
184  *d->out << "\">";
185  }
186 
187  *d->out << d->currentLine.mid(offset, length).toHtmlEscaped();
188 
189  if (!formatOutput.isEmpty()) {
190  *d->out << "</span>";
191  }
192 }
Opaque handle to the state of the highlighting engine.
Definition: state.h:25
QString number(int n, int base)
QString toHtmlEscaped() const const
bool hasBackgroundColor(const Theme &theme) const
Returns true if the combination of this format and the theme theme change the background color compar...
Definition: format.cpp:135
QString name() const const
int red() const const
Describes the format to be used for a specific text fragment.
Definition: format.h:33
QColor fromRgba(QRgb rgba)
bool hasTextColor(const Theme &theme) const
Returns true if the combination of this format and the theme theme change the foreground color compar...
Definition: format.cpp:120
bool isItalic(const Theme &theme) const
Returns true if the combination of this format and the given theme results in italic text formatting.
Definition: format.cpp:159
QColor textColor(const Theme &theme) const
Returns the foreground color of the combination of this format and the given theme.
Definition: format.cpp:125
bool isEmpty() const const
int alpha() const const
int green() const const
QColor backgroundColor(const Theme &theme) const
Returns the background color of the combination of this format and the given theme.
Definition: format.cpp:140
bool isEmpty() const const
qreal alphaF() const const
bool isUnderline(const Theme &theme) const
Returns true if the combination of this format and the given theme results in underlined text.
Definition: format.cpp:168
int blue() const const
@ Normal
Default text style for normal text and source code without special highlighting.
Definition: theme.h:83
bool isStrikeThrough(const Theme &theme) const
Returns true if the combination of this format and the given theme results in struck through text.
Definition: format.cpp:177
@ BackgroundColor
Background color for the editing area.
Definition: theme.h:165
bool isBold(const Theme &theme) const
Returns true if the combination of this format and the given theme results in bold text formatting.
Definition: format.cpp:150
QString & append(QChar ch)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Mar 26 2023 04:09:17 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.