1
$\begingroup$

Question

The system clipboard can contain image data and text data (and a plethora of other types). Mathematica copies different expressions as different types of data; copy RandomImage[] and a string to see.

How can I copy, for instance, a Hyperlink so that it is pasted as a link in browser applications?


Subtleties

I'm not actually sure if the clipboard has metadata, or if programs try to parse the data in every possible way (I suspect the former). Regardless, we can query the clipboard datatypes with JLink (java):

Needs@"JLink`"
ReinstallJava[]
LoadJavaClass@"java.awt.Toolkit"
#@toString[]&/@ 
Toolkit`getDefaultToolkit[]@getSystemClipboard[]@getAvailableDataFlavors[]

A hyperlink copied from the web has many text/html mimetypes. Can a string copied from Mathematica be available with this mimetype? Naively copying a Hyperlink results in plaintext (or, strangely, java serialized object).

$\endgroup$

1 Answer 1

2
$\begingroup$

First I found this great post by @MadProgrammer which seems to work on MacOS but not on my setup (Windows 11). The following solution is based on reverse engineering the clipboard metadata.

You don't need to import JLink, just run the following code (assuming you have version 12.3 or higher):

javaCopyLink=ExternalFunction["Java","
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

 public static void CopyLink(String htmlText,String plainText){
        HtmlSelection htmlSelection = new HtmlSelection(htmlText, plainText);
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        clipboard.setContents(htmlSelection, null);
    }

public class HtmlSelection implements Transferable {

        private static List<DataFlavor> htmlFlavors = new ArrayList<>(3);

        static {
            try {
                htmlFlavors.add(DataFlavor.stringFlavor);
                htmlFlavors.add(DataFlavor.allHtmlFlavor);
                htmlFlavors.add(DataFlavor.fragmentHtmlFlavor);
            } catch (Exception ex) {
                
            }  
        }

        private String html;
        private String plainText;
        private float version= 0.9f;
        private String sourceURL = \"\";
        private int startHTML = 109 + sourceURL.length();
        private int endHTML;
        private int startFragment = 123  + sourceURL.length();
        private int endFragment;
        
        public HtmlSelection(String html, String plainText) {
            this.html = html;
            this.plainText = plainText;
            this.endHTML = this.startHTML + 28 + html.length();
            this.endFragment = this.startFragment +  html.length() + 18;
        }

        public DataFlavor[] getTransferDataFlavors() {
            return (DataFlavor[]) htmlFlavors.toArray(new DataFlavor[htmlFlavors.size()]);
        }

        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return htmlFlavors.contains(flavor);
        }

        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {

            if (flavor == DataFlavor.stringFlavor) {
                return plainText;
            } else if (flavor == DataFlavor.allHtmlFlavor || flavor == DataFlavor.fragmentHtmlFlavor) {
                
                return String.format(\"Version:%1.1f\\r\\nStartHTML:%08d\\r\\nEndHTML:%08d\\r\\nStartFragment:%08d\\r\\nEndFragment:%08d\\r\\nSourceURL:%s\\r\\n<html><body>\\r\\n<!--StartFragment-->%s<!--EndFragment-->\\r\\n</body></html>\", this.version,this.startHTML,this.endHTML,this.startFragment,this.endFragment,this.sourceURL,this.html);
            }
            throw new UnsupportedFlavorException(flavor);
        }
    }
"]

Which would give you an external function that accepts two inputs:

  1. HTML text which contains a link
  2. Plain text for places where pasting link is not possible (like a simple text editor).

Result

javaCopyLink["<a href=\"http://example.com\">link</a>", "plain text"]

enter image description here

$\endgroup$

Not the answer you're looking for? Browse other questions tagged or ask your own question.