Copying HTML to the clipboard from Java

It's rare that I find myself faced with something that works perfectly with Apple's Java implementation and is broken on Linux and Windows. It's not often true the other way round, but Apple has been playing catch-up, and we still don't have 1.5.0 on Mac OS.

But I'm pleased to report that you can copy/paste HTML to/from the clipboard on Mac OS, even though it doesn't seem to work on Linux, and someone else says it doesn't work on Windows (Java bug #4765240).

Note that what I'm talking about is being able to paste styled text into a native application. So rather than "hello <b>world!</b>", you get "hello world!".

If you run the oddly-named "PasteHorkTest" from bug #4765240 on Linux and try to paste from Mozilla Firefox, you'll find that there seems to be a problem with the character encoding. It looks like the clipboard has 2-byte Unicode data (I can't remember which endian), because each recognizable character is separated from the next by the square that Sun's implementations use to render characters they have no glyph for. It may be that the HTML tags would be correctly interpreted if it weren't for this encoding problem, but whatever, it's broken.

If you run the same .class file on Mac OS, you can paste from Safari or Mail just fine.

I couldn't find anything useful on the web about copying HTML to the clipboard from Java, perhaps because the most common case – copying from a JEditorPane – is already dealt with by Swing. I knocked up some code based on javax.swing.plaf.basic.BasicTransferable but using the collections classes rather than primitive arrays, for clarity:

import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.io.*;
import java.util.*;
import javax.swing.*;

public class html {
public html() {
JButton b = new JButton("copy");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {

JFrame f = new JFrame();

public static void main(String[] args) {
new html();

private void copy() {
Transferable t = new HtmlSelection("<html><p>Hello <b>world!</b>");
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(t, null);

private static class HtmlSelection implements Transferable {
private static ArrayList htmlFlavors = new ArrayList();

static {
try {
htmlFlavors.add(new DataFlavor("text/html;class=java.lang.String"));
htmlFlavors.add(new DataFlavor("text/html;class=java.io.Reader"));
htmlFlavors.add(new DataFlavor("text/html;charset=unicode;class=java.io.InputStream"));
} catch (ClassNotFoundException ex) {

private String html;

public HtmlSelection(String html) {
this.html = html;

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 (String.class.equals(flavor.getRepresentationClass())) {
return html;
} else if (Reader.class.equals(flavor.getRepresentationClass())) {
return new StringReader(html);
} else if (InputStream.class.equals(flavor.getRepresentationClass())) {
return new StringBufferInputStream(html);
throw new UnsupportedFlavorException(flavor);

This doesn't work on Linux, if you want to paste into a native application. I assume that it doesn't work on Windows either (but I don't use Windows, so I don't know).

On Mac OS, it works like a charm. Click the button, and you can paste styled text into Mail. Which is exactly what I wanted to be able to do. Annoyingly, inline CSS (or any other kind of CSS, as far as I can tell) doesn't work. And I really need CSS for what I want to do. It's strange, because I can copy this from Safari to Mail just fine:

 White Text on a Blue Background 

Anyway, feel free to copy & paste the HtmlSelection class into your own software!

[If you don't want deprecated code, you can remove the support for InputStream and the copying still works fine on Mac OS, which as far as I know is the only platform where this works at all. Note also that I'm not really gloating here; I'd much rather this worked on the other platforms.]