202 lines
6.6 KiB
C#
202 lines
6.6 KiB
C#
|
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
|||
|
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
|||
|
|
|
|||
|
|
using System;
|
|||
|
|
using System.Diagnostics;
|
|||
|
|
using System.Globalization;
|
|||
|
|
using System.IO;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Windows;
|
|||
|
|
|
|||
|
|
using ICSharpCode.AvalonEdit.Document;
|
|||
|
|
|
|||
|
|
namespace ICSharpCode.AvalonEdit.Highlighting
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// Allows copying HTML text to the clipboard.
|
|||
|
|
/// </summary>
|
|||
|
|
public static class HtmlClipboard
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// Builds a header for the CF_HTML clipboard format.
|
|||
|
|
/// </summary>
|
|||
|
|
static string BuildHeader(int startHTML, int endHTML, int startFragment, int endFragment)
|
|||
|
|
{
|
|||
|
|
StringBuilder b = new StringBuilder();
|
|||
|
|
b.AppendLine("Version:0.9");
|
|||
|
|
b.AppendLine("StartHTML:" + startHTML.ToString("d8", CultureInfo.InvariantCulture));
|
|||
|
|
b.AppendLine("EndHTML:" + endHTML.ToString("d8", CultureInfo.InvariantCulture));
|
|||
|
|
b.AppendLine("StartFragment:" + startFragment.ToString("d8", CultureInfo.InvariantCulture));
|
|||
|
|
b.AppendLine("EndFragment:" + endFragment.ToString("d8", CultureInfo.InvariantCulture));
|
|||
|
|
return b.ToString();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Sets the TextDataFormat.Html on the data object to the specified html fragment.
|
|||
|
|
/// This helper methods takes care of creating the necessary CF_HTML header.
|
|||
|
|
/// </summary>
|
|||
|
|
public static void SetHtml(DataObject dataObject, string htmlFragment)
|
|||
|
|
{
|
|||
|
|
if (dataObject == null)
|
|||
|
|
throw new ArgumentNullException("dataObject");
|
|||
|
|
if (htmlFragment == null)
|
|||
|
|
throw new ArgumentNullException("htmlFragment");
|
|||
|
|
|
|||
|
|
string htmlStart = @"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Transitional//EN"">" + Environment.NewLine
|
|||
|
|
+ "<HTML>" + Environment.NewLine
|
|||
|
|
+ "<BODY>" + Environment.NewLine
|
|||
|
|
+ "<!--StartFragment-->" + Environment.NewLine;
|
|||
|
|
string htmlEnd = "<!--EndFragment-->" + Environment.NewLine + "</BODY>" + Environment.NewLine + "</HTML>" + Environment.NewLine;
|
|||
|
|
string dummyHeader = BuildHeader(0, 0, 0, 0);
|
|||
|
|
// the offsets are stored as UTF-8 bytes (see CF_HTML documentation)
|
|||
|
|
int startHTML = dummyHeader.Length;
|
|||
|
|
int startFragment = startHTML + htmlStart.Length;
|
|||
|
|
int endFragment = startFragment + Encoding.UTF8.GetByteCount(htmlFragment);
|
|||
|
|
int endHTML = endFragment + htmlEnd.Length;
|
|||
|
|
string cf_html = BuildHeader(startHTML, endHTML, startFragment, endFragment) + htmlStart + htmlFragment + htmlEnd;
|
|||
|
|
Debug.WriteLine(cf_html);
|
|||
|
|
dataObject.SetText(cf_html, TextDataFormat.Html);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Creates a HTML fragment from a part of a document.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="document">The document to create HTML from.</param>
|
|||
|
|
/// <param name="highlighter">The highlighter used to highlight the document. <c>null</c> is valid and will create HTML without any highlighting.</param>
|
|||
|
|
/// <param name="segment">The part of the document to create HTML for. You can pass <c>null</c> to create HTML for the whole document.</param>
|
|||
|
|
/// <param name="options">The options for the HTML creation.</param>
|
|||
|
|
/// <returns>HTML code for the document part.</returns>
|
|||
|
|
public static string CreateHtmlFragment(TextDocument document, IHighlighter highlighter, ISegment segment, HtmlOptions options)
|
|||
|
|
{
|
|||
|
|
if (document == null)
|
|||
|
|
throw new ArgumentNullException("document");
|
|||
|
|
if (options == null)
|
|||
|
|
throw new ArgumentNullException("options");
|
|||
|
|
if (highlighter != null && highlighter.Document != document)
|
|||
|
|
throw new ArgumentException("Highlighter does not belong to the specified document.");
|
|||
|
|
if (segment == null)
|
|||
|
|
segment = new SimpleSegment(0, document.TextLength);
|
|||
|
|
|
|||
|
|
StringBuilder html = new StringBuilder();
|
|||
|
|
int segmentEndOffset = segment.EndOffset;
|
|||
|
|
DocumentLine line = document.GetLineByOffset(segment.Offset);
|
|||
|
|
while (line != null && line.Offset < segmentEndOffset) {
|
|||
|
|
HighlightedLine highlightedLine;
|
|||
|
|
if (highlighter != null)
|
|||
|
|
highlightedLine = highlighter.HighlightLine(line.LineNumber);
|
|||
|
|
else
|
|||
|
|
highlightedLine = new HighlightedLine(document, line);
|
|||
|
|
SimpleSegment s = segment.GetOverlap(line);
|
|||
|
|
if (html.Length > 0)
|
|||
|
|
html.AppendLine("<br>");
|
|||
|
|
html.Append(highlightedLine.ToHtml(s.Offset, s.EndOffset, options));
|
|||
|
|
line = line.NextLine;
|
|||
|
|
}
|
|||
|
|
return html.ToString();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Escapes text and writes the result to the StringBuilder.
|
|||
|
|
/// </summary>
|
|||
|
|
internal static void EscapeHtml(StringWriter w, string text, HtmlOptions options)
|
|||
|
|
{
|
|||
|
|
int spaceCount = -1;
|
|||
|
|
foreach (char c in text) {
|
|||
|
|
if (c == ' ') {
|
|||
|
|
if (spaceCount < 0)
|
|||
|
|
w.Write(" ");
|
|||
|
|
else
|
|||
|
|
spaceCount++;
|
|||
|
|
} else if (c == '\t') {
|
|||
|
|
if (spaceCount < 0)
|
|||
|
|
spaceCount = 0;
|
|||
|
|
spaceCount += options.TabSize;
|
|||
|
|
} else {
|
|||
|
|
if (spaceCount == 1) {
|
|||
|
|
w.Write(' ');
|
|||
|
|
} else if (spaceCount >= 1) {
|
|||
|
|
for (int i = 0; i < spaceCount; i++) {
|
|||
|
|
w.Write(" ");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
spaceCount = 0;
|
|||
|
|
switch (c) {
|
|||
|
|
case '<':
|
|||
|
|
w.Write("<");
|
|||
|
|
break;
|
|||
|
|
case '>':
|
|||
|
|
w.Write(">");
|
|||
|
|
break;
|
|||
|
|
case '&':
|
|||
|
|
w.Write("&");
|
|||
|
|
break;
|
|||
|
|
case '"':
|
|||
|
|
w.Write(""");
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
w.Write(c);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
for (int i = 0; i < spaceCount; i++) {
|
|||
|
|
w.Write(" ");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Holds options for converting text to HTML.
|
|||
|
|
/// </summary>
|
|||
|
|
public class HtmlOptions
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// Creates a default HtmlOptions instance.
|
|||
|
|
/// </summary>
|
|||
|
|
public HtmlOptions()
|
|||
|
|
{
|
|||
|
|
this.TabSize = 4;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Creates a new HtmlOptions instance that copies applicable options from the <see cref="TextEditorOptions"/>.
|
|||
|
|
/// </summary>
|
|||
|
|
public HtmlOptions(TextEditorOptions options)
|
|||
|
|
: this()
|
|||
|
|
{
|
|||
|
|
if (options == null)
|
|||
|
|
throw new ArgumentNullException("options");
|
|||
|
|
this.TabSize = options.IndentationSize;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// The amount of spaces a tab gets converted to.
|
|||
|
|
/// </summary>
|
|||
|
|
public int TabSize { get; set; }
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Writes the HTML attribute for the style to the text writer.
|
|||
|
|
/// </summary>
|
|||
|
|
public virtual void WriteStyleAttributeForColor(TextWriter writer, HighlightingColor color)
|
|||
|
|
{
|
|||
|
|
if (writer == null)
|
|||
|
|
throw new ArgumentNullException("writer");
|
|||
|
|
if (color == null)
|
|||
|
|
throw new ArgumentNullException("color");
|
|||
|
|
writer.Write(" style=\"");
|
|||
|
|
writer.Write(color.ToCss());
|
|||
|
|
writer.Write("\"");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Gets whether the color needs to be written out to HTML.
|
|||
|
|
/// </summary>
|
|||
|
|
public virtual bool ColorNeedsSpanForStyling(HighlightingColor color)
|
|||
|
|
{
|
|||
|
|
if (color == null)
|
|||
|
|
throw new ArgumentNullException("color");
|
|||
|
|
return !string.IsNullOrEmpty(color.ToCss());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|