forked from ArthurHub/HTML-Renderer
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHtmlSyntaxHighlighter.cs
More file actions
287 lines (263 loc) · 11.2 KB
/
Copy pathHtmlSyntaxHighlighter.cs
File metadata and controls
287 lines (263 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
// "Therefore those skilled at the unorthodox
// are infinite as heaven and earth,
// inexhaustible as the great rivers.
// When they come to an end,
// they begin again,
// like the days and months;
// they die and are reborn,
// like the four seasons."
//
// - Sun Tsu,
// "The Art of War"
using System;
using System.Drawing;
using System.Text;
namespace TheArtOfDev.HtmlRenderer.Demo.Common
{
/// <summary>
/// HTML syntax highlighting using Rich-Text formatting.<br/>
/// - Handle plain input or already in RTF format.<br/>
/// - Handle if input already contains RTF color table.<br/>
/// - Rich coloring adjusted to Visual Studio HTML coloring.<br/>
/// - Support to provide custom colors.<br/>
/// - High performance (as much as RTF format allows).<br/>
/// </summary>
/// <remarks>
/// The MIT License (MIT) Copyright (c) 2014 Arthur Teplitzki.<br/>
/// Based on work by Alun Evans 2006 (http://www.codeproject.com/Articles/15038/C-Formatting-Text-in-a-RichTextBox-by-Parsing-the).
/// </remarks>
public static class HtmlSyntaxHighlighter
{
#region Fields/Consts
/// <summary>
/// RTF header field
/// </summary>
private const string Header = "\\rtf";
/// <summary>
/// RTF color table
/// </summary>
private const string ColorTbl = "\\colortbl";
/// <summary>
/// cf0 = default
/// cf1 = dark red
/// cf2 = bright red
/// cf3 = green
/// cf4 = blue
/// cf5 = blue
/// cf6 = purple
/// </summary>
private const string DefaultColorScheme = "\\red128\\green0\\blue0;\\red240\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;\\red0\\green0\\blue255;\\red128\\green0\\blue171;";
/// <summary>
/// Used to test if a char requires more than 1 byte
/// </summary>
private static readonly char[] _unicodeTest = new char[1];
#endregion
/// <summary>
/// Process the given text to create RTF text with HTML syntax highlighting using default Visual Studio colors.<br/>
/// The given text can be plain HTML or already parsed RTF format.
/// </summary>
/// <param name="text">the text to create color RTF text from</param>
/// <returns>text with RTF formatting for HTML syntax</returns>
public static string Process(string text)
{
return Process(text, DefaultColorScheme);
}
/// <summary>
/// Process the given text to create RTF text with HTML syntax highlighting using custom colors.<br/>
/// The given text can be plain HTML or already parsed RTF format.
/// </summary>
/// <param name="text">the text to create color RTF text from</param>
/// <param name="element">the color for HTML elements</param>
/// <param name="attribute">the color for HTML attributes</param>
/// <param name="comment">the color for HTML comments</param>
/// <param name="chars">the color for HTML special chars: (<![CDATA[<,>,",',=,:]]>)</param>
/// <param name="values">the color for HTML attribute or styles values</param>
/// <param name="style">the color for HTML style attribute</param>
/// <returns>text with RTF formatting for HTML syntax</returns>
public static string Process(string text, Color element, Color attribute, Color comment, Color chars, Color values, Color style)
{
return Process(text, CreateColorScheme(element, attribute, comment, chars, values, style));
}
#region Private/Protected methods
/// <summary>
/// Process the given text to create RTF text with HTML syntax highlighting.
/// </summary>
/// <param name="text">the text to create color RTF text from</param>
/// <param name="colorScheme">the color scheme to add to RTF color table</param>
/// <returns>text with RTF formatting for HTML syntax</returns>
private static string Process(string text, string colorScheme)
{
var sb = new StringBuilder(text.Length * 2);
// add color table used to set color in RTL formatted text
bool rtfFormated;
int i = AddColorTable(sb, text, colorScheme, out rtfFormated);
// Scan through RTF data adding RTF color tags
bool inComment = false;
bool inHtmlTag = false;
bool inAttributeVal = false;
for (; i < text.Length; i++)
{
var c = text[i];
var c2 = text.Length > i + 1 ? text[i + 1] : (char)0;
if (!inComment && c == '<')
{
if (text.Length > i + 3 && c2 == '!' && text[i + 2] == '-' && text[i + 3] == '-')
{
// Comments tag
sb.Append("\\cf3").Append(c);
inComment = true;
}
else
{
// Html start/end tag
sb.Append("\\cf4").Append(c);
if (c2 == '/')
{
sb.Append(c2);
i++;
}
sb.Append("\\cf1 ");
inHtmlTag = true;
}
}
else if (c == '>')
{
//Check for comments tags
if (inComment && text[i - 1] == '-' && text[i - 2] == '-')
{
sb.Append(c).Append("\\cf0 ");
inComment = false;
}
else if (!inComment)
{
sb.Append("\\cf4").Append(c).Append("\\cf0 ");
inHtmlTag = false;
inAttributeVal = false;
}
}
else if (inHtmlTag && !inComment && c == '/' && c2 == '>')
{
sb.Append("\\cf4").Append(c).Append(c2).Append("\\cf0 ");
inHtmlTag = false;
i++;
}
else if (inHtmlTag && !inComment && !inAttributeVal && c == ' ')
{
sb.Append(c).Append("\\cf2 ");
}
else if (inHtmlTag && !inComment && c == '=')
{
sb.Append("\\cf4").Append(c).Append("\\cf6 ");
}
else if (inHtmlTag && !inComment && inAttributeVal && c == ':')
{
sb.Append("\\cf0").Append(c).Append("\\cf5 ");
}
else if (inHtmlTag && !inComment && inAttributeVal && c == ';')
{
sb.Append("\\cf0").Append(c).Append("\\cf6 ");
}
else if (inHtmlTag && !inComment && (c == '"' || c == '\''))
{
sb.Append("\\cf4").Append(c).Append("\\cf6 ");
inAttributeVal = !inAttributeVal;
}
else if (!rtfFormated && c == '\n')
{
sb.Append(c).Append("\\par ");
}
else if (!rtfFormated && (c == '{' || c == '}'))
{
sb.Append('\\').Append(c);
}
else if (!rtfFormated)
{
_unicodeTest[0] = c;
if (Encoding.UTF8.GetByteCount(_unicodeTest, 0, 1) > 1)
sb.Append("\\u" + Convert.ToUInt32(c) + "?");
else
sb.Append(c);
}
else
{
sb.Append(c);
}
}
// close the RTF if we added the header ourselves
if (!rtfFormated)
sb.Append('}');
// return the created colored RTF
return sb.ToString();
}
/// <summary>
/// Add color table used to set color in RTL formatted text.
/// </summary>
/// <param name="sb">the builder to add the RTF string to</param>
/// <param name="text">the original RTF text to build color RTF from</param>
/// <param name="colorScheme">the color scheme to add to RTF color table</param>
/// <param name="rtfFormated">return if the given text is already in RTF format</param>
/// <returns>the index in the given RTF text to start scan from</returns>
private static int AddColorTable(StringBuilder sb, string text, string colorScheme, out bool rtfFormated)
{
// Search for color table, if exists replace it, otherwise add our
rtfFormated = true;
int idx = text.IndexOf(ColorTbl, StringComparison.OrdinalIgnoreCase);
if (idx != -1)
{
sb.Append(text, 0, idx);
// insert our color table at our chosen location
sb.Append(ColorTbl).Append(";").Append(colorScheme).Append("}");
// skip the existing color table
idx = text.IndexOf('}', idx);
}
else
{
// find index of start of header if exists
idx = text.IndexOf(Header, StringComparison.OrdinalIgnoreCase);
if (idx != -1)
{
// append the existing header
idx += Header.Length;
sb.Append(text, 0, idx);
while (text[idx] != '\\' && text[idx] != '{' && text[idx] != '}')
sb.Append(text[idx++]);
}
else
{
// not RTF text, add the RTF header as well
idx = 0;
sb.Append("{").Append(Header);
rtfFormated = false;
}
// insert the color table at our chosen location
sb.Append("{").Append(ColorTbl).Append(";").Append(colorScheme).Append("}");
}
return idx;
}
/// <summary>
/// Create RTF colortbl formatted string for the given colors.
/// </summary>
private static string CreateColorScheme(Color element, Color attribute, Color comment, Color chars, Color values, Color style)
{
var sb = new StringBuilder(DefaultColorScheme.Length);
AppendColorValue(sb, element);
AppendColorValue(sb, attribute);
AppendColorValue(sb, comment);
AppendColorValue(sb, chars);
AppendColorValue(sb, values);
AppendColorValue(sb, style);
return sb.ToString();
}
/// <summary>
/// Append single color in RTF colortbl format.
/// </summary>
private static void AppendColorValue(StringBuilder sb, Color color)
{
sb.Append("\\red").Append(color.R)
.Append("\\green").Append(color.R)
.Append("\\blue").Append(color.R)
.Append(';');
}
#endregion
}
}