Processing Ajax...

Title

Message

Confirm

Confirm

Confirm

Confirm

Are you sure you want to delete this item?

Confirm

Are you sure you want to delete this item?

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
Apr 18, 2024
Date Last Modified
Apr 18, 2024

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();
		}
	}
}