Skip to content

Instantly share code, notes, and snippets.

@UweKeim
Last active January 28, 2024 21:31
Show Gist options
  • Save UweKeim/038cbcdc6299030eb672663d2169dfd4 to your computer and use it in GitHub Desktop.
Save UweKeim/038cbcdc6299030eb672663d2169dfd4 to your computer and use it in GitHub Desktop.
namespace ZetaProducer.RuntimeGui.ExtendedWebBrowser
{
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Zeta.VoyagerLibrary.Common.IO;
using ZetaLongPaths;
public static class HtmlClipboardHelper
{
public static string GetClipboardHtmlText()
{
if (Clipboard.ContainsText(TextDataFormat.Html))
{
getHtmlFromClipboard(out var htmlCode, out var originalBuffer);
if (string.IsNullOrEmpty(htmlCode)) return null;
//split Html to htmlInfo (and htmlSource)
var htmlInfo = htmlCode.Substring(0, htmlCode.IndexOf('<') - 1);
const string startFragmentText = @"StartFragment:";
const string endFragmentText = @"EndFragment:";
//get Fragment positions
var tmp = htmlInfo.Substring(htmlInfo.IndexOf(startFragmentText, StringComparison.Ordinal) +
startFragmentText.Length);
var ir = tmp.IndexOf('\r');
if (ir < 0) return null;
tmp = tmp.Substring(0, ir);
var posStartSelection = Convert.ToInt32(tmp);
tmp = htmlInfo.Substring(htmlInfo.IndexOf(endFragmentText, StringComparison.Ordinal) +
endFragmentText.Length);
ir = tmp.IndexOf('\r');
if (ir < 0) return null;
tmp = tmp.Substring(0, ir);
var posEndSelection = Convert.ToInt32(tmp);
// Get Fragment. Always UTF-8 as of spec.
return Encoding.UTF8.GetString(
originalBuffer,
posStartSelection,
posEndSelection - posStartSelection);
}
else if (Clipboard.ContainsText(TextDataFormat.Text) ||
Clipboard.ContainsText(TextDataFormat.UnicodeText))
{
var text = Clipboard.GetText();
// https://trello.com/c/2T5k5ivG
return ConvertPlainTextToHtml(text);
}
else
{
return null;
}
}
public static string GetClipboardHtmlTextBody()
{
var htmlCode = GetClipboardHtmlText();
if (string.IsNullOrEmpty(htmlCode) || !htmlCode.ContainsNoCase(@"<body"))
{
return htmlCode;
}
else
{
// TODO (2014-12-18, Uwe Keim): Das hier war im Profiler BRECHEND langsam.
var regex = new Regex(
@".*?<body[^>]*>(.*?)</body>",
RegexOptions.Singleline |
RegexOptions.IgnoreCase);
var m = regex.Match(htmlCode);
if (m.Success)
{
var groups = GetEffectiveMatchGroups(m);
return groups[1].Value;
}
else
{
return htmlCode;
}
}
}
public static string GetClipboardHtmlTextComplete()
{
if (Clipboard.ContainsText(TextDataFormat.Html))
{
getHtmlFromClipboard(out var htmlCode, out _);
return htmlCode;
}
else
{
return null;
}
}
[UsedImplicitly]
public static string ConvertPlainTextToHtml(string text)
{
if (string.IsNullOrWhiteSpace(text)) return text?.Trim();
text = text.Trim();
// Blanks aus komplett leeren Zeilen weg.
text = Regex.Replace(text, @"^\s+$", string.Empty, RegexOptions.Multiline);
text = text.Replace("\r\n", "\r");
text = text.Replace("\n", "\r");
text = text.Replace("\r", "\r\n");
// Mehrfache Leerzeilen weg.
text = Regex.Replace(text, "\n{3,}", Environment.NewLine + Environment.NewLine);
text = PathHelper.HtmlEncode(text);
text = text.Replace("\r\n", "\r");
text = text.Replace("\n", "\r");
text = text.Replace("\r\r", @"</p><p>");
text = text.Replace("\r", @"<br>");
text = @"<p>" + text + @"</p>";
return text;
}
internal static List<Group> GetEffectiveMatchGroups(
Match match)
{
var result = new List<Group>();
if (match != null && match.Success)
{
result.AddRange(match.Groups.Cast<Group>().Where(group => group.Success));
}
return result;
}
private static void getHtmlFromClipboard(
out string htmlCode,
out byte[] originalBuffer)
{
htmlCode = HtmlClipboardHelperInternal.GetHTMLWin32Native(out originalBuffer);
}
}
}
namespace ZetaProducer.RuntimeGui.ExtendedWebBrowser
{
using System;
using System.Runtime.InteropServices;
using System.Text;
/// <summary>
/// Von https://metadataconsulting.blogspot.com/2019/06/How-to-get-HTML-from-the-Windows-system-clipboard-directly-using-PInvoke-Win32-Native-methods-avoiding-bad-funny-characters.html
/// </summary>
internal static class HtmlClipboardHelperInternal
{
#region Win32 Native PInvoke
[DllImport(@"User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsClipboardFormatAvailable(uint format);
[DllImport(@"User32.dll", SetLastError = true)]
private static extern IntPtr GetClipboardData(uint uFormat);
[DllImport(@"User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport(@"User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseClipboard();
[DllImport(@"Kernel32.dll", SetLastError = true)]
private static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport(@"Kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GlobalUnlock(IntPtr hMem);
[DllImport(@"Kernel32.dll", SetLastError = true)]
private static extern uint GlobalSize(IntPtr hMem);
[DllImport(@"user32.dll", SetLastError = true)]
private static extern uint RegisterClipboardFormatA(string lpszFormat);
#endregion
public static string GetHTMLWin32Native(out byte[] rawBytes)
{
rawBytes = null;
var CF_HTML = RegisterClipboardFormatA(@"HTML Format");
if (/*CF_HTML != null ||*/ CF_HTML == 0)
return null;
if (!IsClipboardFormatAvailable(CF_HTML))
return null;
try
{
if (!OpenClipboard(IntPtr.Zero)) return null;
var handle = GetClipboardData(CF_HTML);
if (handle == IntPtr.Zero) return null;
var pointer = IntPtr.Zero;
try
{
pointer = GlobalLock(handle);
if (pointer == IntPtr.Zero) return null;
var size = GlobalSize(handle);
var buff = new byte[size];
Marshal.Copy(pointer, buff, 0, (int)size);
rawBytes = buff;
return Encoding.UTF8.GetString(buff);
}
finally
{
if (pointer != IntPtr.Zero) GlobalUnlock(handle);
}
}
finally
{
CloseClipboard();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment