KSyntaxHighlighting

htmlhighlighter.cpp
1/*
2 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org>
3 SPDX-FileCopyrightText: 2018 Christoph Cullmann <cullmann@kde.org>
4
5 SPDX-License-Identifier: MIT
6*/
7
8#include "htmlhighlighter.h"
9#include "abstracthighlighter_p.h"
10#include "definition.h"
11#include "definition_p.h"
12#include "format.h"
13#include "ksyntaxhighlighting_logging.h"
14#include "state.h"
15#include "theme.h"
16
17#include <QFile>
18#include <QFileInfo>
19#include <QIODevice>
20#include <QTextStream>
21
22using namespace KSyntaxHighlighting;
23
24class KSyntaxHighlighting::HtmlHighlighterPrivate : public AbstractHighlighterPrivate
25{
26public:
27 std::unique_ptr<QTextStream> out;
28 std::unique_ptr<QFile> file;
29 QString currentLine;
30 std::vector<QString> htmlStyles;
31};
32
33HtmlHighlighter::HtmlHighlighter()
34 : AbstractHighlighter(new HtmlHighlighterPrivate())
35{
36}
37
38HtmlHighlighter::~HtmlHighlighter()
39{
40}
41
42void HtmlHighlighter::setOutputFile(const QString &fileName)
43{
44 Q_D(HtmlHighlighter);
45 d->file.reset(new QFile(fileName));
46 if (!d->file->open(QFile::WriteOnly | QFile::Truncate)) {
47 qCWarning(Log) << "Failed to open output file" << fileName << ":" << d->file->errorString();
48 return;
49 }
50 d->out.reset(new QTextStream(d->file.get()));
51 d->out->setEncoding(QStringConverter::Utf8);
52}
53
54void HtmlHighlighter::setOutputFile(FILE *fileHandle)
55{
56 Q_D(HtmlHighlighter);
57 d->out.reset(new QTextStream(fileHandle, QIODevice::WriteOnly));
58 d->out->setEncoding(QStringConverter::Utf8);
59}
60
61void HtmlHighlighter::highlightFile(const QString &fileName, const QString &title)
62{
63 QFileInfo fi(fileName);
64 QFile f(fileName);
65 if (!f.open(QFile::ReadOnly)) {
66 qCWarning(Log) << "Failed to open input file" << fileName << ":" << f.errorString();
67 return;
68 }
69
70 if (title.isEmpty()) {
71 highlightData(&f, fi.fileName());
72 } else {
73 highlightData(&f, title);
74 }
75}
76
77/**
78 * @brief toHtmlRgba
79 * Converts QColor -> #RRGGBBAA if there is an alpha channel
80 * otherwise it will just return the hexcode. This is because QColor
81 * outputs #AARRGGBB, whereas browser support #RRGGBBAA.
82 *
83 * @param color
84 * @return
85 */
86static QString toHtmlRgbaString(const QColor &color)
87{
88 if (color.alpha() == 0xFF) {
89 return color.name();
90 }
91 static const char16_t digits[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
92 QChar hexcode[9];
93 hexcode[0] = QLatin1Char('#');
94 hexcode[1] = digits[color.red() >> 4];
95 hexcode[2] = digits[color.red() & 0xf];
96 hexcode[3] = digits[color.green() >> 4];
97 hexcode[4] = digits[color.green() & 0xf];
98 hexcode[5] = digits[color.blue() >> 4];
99 hexcode[6] = digits[color.blue() & 0xf];
100 hexcode[7] = digits[color.alpha() >> 4];
101 hexcode[8] = digits[color.alpha() & 0xf];
102 return QString(hexcode, 9);
103}
104
105void HtmlHighlighter::highlightData(QIODevice *dev, const QString &title)
106{
107 Q_D(HtmlHighlighter);
108
109 if (!d->out) {
110 qCWarning(Log) << "No output stream defined!";
111 return;
112 }
113
114 QString htmlTitle;
115 if (title.isEmpty()) {
116 htmlTitle = QStringLiteral("KSyntaxHighlighter");
117 } else {
118 htmlTitle = title.toHtmlEscaped();
119 }
120
121 const auto &theme = d->m_theme;
122 const auto &definition = d->m_definition;
123
124 auto definitions = definition.includedDefinitions();
125 definitions.append(definition);
126
127 int maxId = 0;
128 for (const auto &definition : std::as_const(definitions)) {
129 for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) {
130 maxId = qMax(maxId, format.id());
131 }
132 }
133 d->htmlStyles.clear();
134 // htmlStyles must not be empty for applyFormat to work even with a definition without any context
135 d->htmlStyles.resize(maxId + 1);
136
137 // initialize htmlStyles
138 for (const auto &definition : std::as_const(definitions)) {
139 for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) {
140 auto &buffer = d->htmlStyles[format.id()];
141 if (format.hasTextColor(theme)) {
142 buffer += QStringLiteral("color:") + toHtmlRgbaString(format.textColor(theme)) + QStringLiteral(";");
143 }
144 if (format.hasBackgroundColor(theme)) {
145 buffer += QStringLiteral("background-color:") + toHtmlRgbaString(format.backgroundColor(theme)) + QStringLiteral(";");
146 }
147 if (format.isBold(theme)) {
148 buffer += QStringLiteral("font-weight:bold;");
149 }
150 if (format.isItalic(theme)) {
151 buffer += QStringLiteral("font-style:italic;");
152 }
153 if (format.isUnderline(theme)) {
154 buffer += QStringLiteral("text-decoration:underline;");
155 }
156 if (format.isStrikeThrough(theme)) {
157 buffer += QStringLiteral("text-decoration:line-through;");
158 }
159
160 if (!buffer.isEmpty()) {
161 buffer.insert(0, QStringLiteral("<span style=\""));
162 // replace last ';'
163 buffer.back() = u'"';
164 buffer += u'>';
165 }
166 }
167 }
168
169 State state;
170 *d->out << "<!DOCTYPE html>\n";
171 *d->out << "<html><head>\n";
172 *d->out << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n";
173 *d->out << "<title>" << htmlTitle << "</title>\n";
174 *d->out << "<meta name=\"generator\" content=\"KF5::SyntaxHighlighting - Definition (" << definition.name() << ") - Theme (" << theme.name() << ")\"/>\n";
175 *d->out << "</head><body";
176 *d->out << " style=\"background-color:" << toHtmlRgbaString(QColor::fromRgba(theme.editorColor(Theme::BackgroundColor)));
178 *d->out << ";color:" << toHtmlRgbaString(QColor::fromRgba(theme.textColor(Theme::Normal)));
179 }
180 *d->out << "\"><pre>\n";
181
182 QTextStream in(dev);
183 while (in.readLineInto(&d->currentLine)) {
184 state = highlightLine(d->currentLine, state);
185 *d->out << "\n";
186 }
187
188 *d->out << "</pre></body></html>\n";
189 d->out->flush();
190
191 d->out.reset();
192 d->file.reset();
193}
194
195void HtmlHighlighter::applyFormat(int offset, int length, const Format &format)
196{
197 if (length == 0) {
198 return;
199 }
200
201 Q_D(HtmlHighlighter);
202
203 auto const &htmlStyle = d->htmlStyles[format.id()];
204
205 if (!htmlStyle.isEmpty()) {
206 *d->out << htmlStyle;
207 }
208
209 for (QChar ch : QStringView(d->currentLine).mid(offset, length)) {
210 if (ch == u'<')
211 *d->out << QStringLiteral("&lt;");
212 else if (ch == u'&')
213 *d->out << QStringLiteral("&amp;");
214 else
215 *d->out << ch;
216 }
217
218 if (!htmlStyle.isEmpty()) {
219 *d->out << QStringLiteral("</span>");
220 }
221}
Abstract base class for highlighters.
Theme theme() const
Returns the currently selected theme for highlighting.
Definition definition() const
Returns the syntax definition used for highlighting.
State highlightLine(QStringView text, const State &state)
Highlight the given line.
QList< Definition > includedDefinitions() const
Returns a list of Definitions that are referenced with the IncludeRules rule.
Describes the format to be used for a specific text fragment.
Definition format.h:28
int id() const
Returns a unique identifier of this format.
Definition format.cpp:101
QRgb textColor(TextStyle style) const
Returns the text color to be used for style.
Definition theme.cpp:62
@ BackgroundColor
Background color for the editing area.
Definition theme.h:160
@ Normal
Default text style for normal text and source code without special highlighting.
Definition theme.h:78
QRgb editorColor(EditorColorRole role) const
Returns the editor color for the requested role.
Definition theme.cpp:102
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
Syntax highlighting engine for Kate syntax definitions.
int alpha() const const
int blue() const const
QColor fromRgba(QRgb rgba)
int green() const const
QString name(NameFormat format) const const
int red() const const
bool isEmpty() const const
QString toHtmlEscaped() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:29 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.