Text copied from JTextArea have broken encoding af

2019-07-21 12:13发布

I have encountered one very strange problem with Java clipboard. I have JTextArea with text containing accents (e.g. "Žluťoučký kůň"). When I select the text and press CTRL+C and paste it into Notepad or Microsoft Word everything is OK.

But when I paste it into some third party application made in VisualFoxPro (I know it is ancient, but our user needs this application) all accents letters are broken due to encoding problem. Java app uses UTF-8 and FoxPro application uses Windows-1250.

When I use Clipboard viewer (https://code.google.com/p/clipboardviewer/) to view all DataFlavors created in clipboard after pressing CTRL+C in JTextArea I see following:

ClipboardViewer http://data.itpro.cz/clipboard.png

System.String and UnicodeText are displayed correctly, but Text is broken. I assume that FoxPro application is using this DataFlavor.

ClipboardViewer http://data.itpro.cz/clipboard2.png

When I paste text into Notepad, press CTRL+A and CTRL+C, contents of clipboard changes as you can see on next image.

ClipboardViewer http://data.itpro.cz/clipboard3.png

My question(s): Is it bug in Java? Is it possible to control this behaviour globally (with some command line -D switch or other approach) or it is required to catch CTRL+C keystroke in every JTextArea and JTextField and create DataTransfer with custom DataFlavor manually? (no need to suggest code for this, I am able to do that, but it would be painful to register it for each JTextComponent in application)

1条回答
Juvenile、少年°
2楼-- · 2019-07-21 12:45

I have found 3 possible solutions:

1) Modify jre\lib\flavormap.properties file. Line starting with TEXT can be modified to:

TEXT=text/plain;charset=cp1250;eoln="\r\n";terminators=1

Where cp1250 is requested destination encoding. This works perfectly, but I believe that this file is overwrited with each Java update, so this is not usable for production use.

2) Start JVM with -Dfile.encoding=cp1250, this option also modifies clipboard functionality, but it also might change some behaviour of your app if you are relying on constructors like new InputStreamReader without specifing Charset.

3) Is to catch CTRL+C on each JTextComponent with this:

textArea.addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_C && (e.getModifiers() & KeyEvent.CTRL_MASK) != 0) {

            Toolkit toolkit = Toolkit.getDefaultToolkit();
            Clipboard clipboard = toolkit.getSystemClipboard();
            CliboardString text = new CliboardString(textArea.getSelectedText(),"cp1250","Windows-1250");
            clipboard.setContents(text, null);
            e.consume();
        }
    }
});

Implementation of ClipboardString looks like this:

public class CliboardString implements Transferable, ClipboardOwner {

private final DataFlavor flavor;

private final String data;
private final String javaEncoding;

public CliboardString(String data,String flavorEncoding,String javaEncoding) {
    this.data = data;
    this.javaEncoding = javaEncoding;
    flavor =  new DataFlavor("text/plain;charset="+flavorEncoding, "TEXT");
}

@Override
public DataFlavor[] getTransferDataFlavors() {
    return new DataFlavor[] {flavor};
}

@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
    return flavor!=null && flavor.equals(this.flavor);
}

@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
    if (isDataFlavorSupported(flavor)) {
        return new ByteArrayInputStream(data.getBytes(javaEncoding));
    }
    throw new UnsupportedFlavorException(flavor);
}

@Override
public void lostOwnership(Clipboard clipboard, Transferable contents) {
}

}
查看更多
登录 后发表回答