Processing Ajax...

Title
Close Dialog

Message

Confirm
Close Dialog

Confirm
Close Dialog

Confirm
Close Dialog

Set Clipboard with HTML Anchor

Description
This macro will take a copied plaintext link and set the clipboard with an HTML formatted link.
Language
C#.net
Minimum Version
Created By
Thomas Malloch (BFS)
Contributors
-
Date Created
17d ago
Date Last Modified
17d ago

Macro Code

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Web;

// The 'text' parameter will contain the text from the:
//   - Current Clipboard when run by HotKey
//   - History Item when run from the History Menu
// The returned string will be:
//   - Placed directly on the Clipboard when run as a Macro
//   - Ignored by ClipboardFusion if it is 'null'
//   - Passed along to the next action in a Trigger (null changed to an empty string)
public static class ClipboardFusionHelper
{
	public static string ProcessText(string text)
	{
		// Check to see if the text is a valid url.
		// If it isn't, return null
		if(!Uri.TryCreate(text, UriKind.Absolute, out Uri uri))
			return null;
		
		// Get the title from the url
		string title = GetPageTitleFromUrl(uri).Result;
		
		// Create the link out of the text and the title. this needs to be valid html
		// or other programs to accept it, so it's also surrounded by body and html tags
		string link = $"<html><body><a href=\"{text}\">{title}</a></body></html>";
		
		// Create a data object for the clipboard
		DataObject clipboard = new DataObject();
		
		// Set the html format
		clipboard.SetData(DataFormats.Html, GenerateHtmlClipboardFormat(link));
		
		// Set the raw link as the text format
		clipboard.SetData(DataFormats.Text, text);
		
		// Set the clipboard with the data object and make the data available even when ClipboardFusion exits
		Clipboard.SetDataObject(clipboard, true);
		
		// Since we already set the clipboard ourselves tell CF that we handled it
		return null;
	}
	
	private static string GenerateHtmlClipboardFormat(string html) 
	{
		// https://learn.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format
		// Create the header for the html format
        string header = "Version:1.0\r\n" +
                        "StartHTML:-1\r\n" +
                        "EndHTML:-1\r\n" +
                        "StartFragment:0000000000\r\n" +
                        "EndFragment:0000000000\r\n";
		
		// Grab the offsets from the content		
		int startFragment = Encoding.UTF8.GetBytes(header).Length;
		int endFragment = startFragment + Encoding.UTF8.GetBytes(html).Length;
		
		// Finish building the header
		header = header
			.Replace("StartFragment:0000000000", $"StartFragment:{startFragment:D10}")
            .Replace("EndFragment:0000000000", $"EndFragment:{endFragment:D10}");
		
		// Return the html clipboard format
		return header + html;
	}
	
	private static async Task<string> GetPageTitleFromUrl(Uri uri)
	{		
		try
		{			
			using (HttpClient client = new HttpClient())
	        {
				// Get the html from the uri
				var response = await client.GetAsync(uri);
				
				// Get the web source
				string body = await response.Content.ReadAsStringAsync();
				
				// Grab the title and return it
				return Regex.Match(body, @"(?<=<title>).*?(?=</title>)", RegexOptions.Multiline).ToString();
			}
		}
		catch
		{
			// If we ran into an issue just return an empty string
			return uri.ToString();
		}
	}
}