Home Articles

将JTextArea复制为“text / html”DataFlavor

Asked
Viewed 1000 times
2

我有一个 JTextArea ,我正在使用 Highlighter 根据我的SSCCE在我的部分文本中应用一些语法高亮:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;

public class SSCCE extends JFrame {
  public SSCCE() {
    final JTextArea aMain = new JTextArea();
    aMain.setFont(new Font("Consolas", Font.PLAIN, 11));
    aMain.setMargin(new Insets(5, 5, 5, 5));
    aMain.setEditable(false);
    add(aMain);

    aMain.setText("The quick brown fox jumped over the lazy dog.");
    Highlighter h = aMain.getHighlighter();
    try {
      h.addHighlight(10, 15, new DefaultHighlighter.DefaultHighlightPainter(new Color(0xFFC800)));
    }
    catch (BadLocationException e) { 
      e.printStackTrace(); 
    }

    aMain.getActionMap().put("Copy", new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        aMain.copy();
      }
    });
    aMain.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "Copy");

    setTitle("SSCCE");
    setSize(350, 150);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setLocationRelativeTo(null);
    setVisible(true);
  }

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        new SSCCE();
      }
    });
  }
}

当用户选择一部分文本并按CTRL C然后我调用 JTextArea 类的 copy() 方法 . 这将文本作为纯文本复制到系统剪贴板上,我丢失了我应用于文本的任何突出显示 . 我正在寻找能够将包含高亮的样式信息复制为"text/html"或"text/rtf"的功能 . 我相信我需要使用 TransferableDataFlavor 类,但我正在努力将某些内容放在一起 - 我不知道如何以正确的格式从 JTextArea 获取数据以放置到剪贴板上 .

我基本上试图复制突出显示,然后将其粘贴到Microsoft Word或类似的应用程序中,突出显示完整 . 样式数据是否以正确的格式提供,还是我手动必须通过枚举所有高亮显示来构建HTML标记?

2 Answers

  • 5

    好吧,基本上,因为荧光笔在 JTextArea 上只是"painted"并且实际上并没有调整文本的样式,所以你需要自己动手

    基本上你需要:

    • 获取所有当前要点的列表

    • 从文档中提取文本

    • 包装html

    • 创建合适的基于HTML的可转让

    • 将html标记复制到剪贴板

    简单...

    import java.awt.Color;
    import java.awt.Font;
    import java.awt.Insets;
    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.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    import java.io.InputStream;
    import java.io.Reader;
    import java.io.StringBufferInputStream;
    import java.io.StringReader;
    import java.util.ArrayList;
    import java.util.List;
    import javax.swing.AbstractAction;
    import javax.swing.JFrame;
    import static javax.swing.JFrame.EXIT_ON_CLOSE;
    import javax.swing.JTextArea;
    import javax.swing.KeyStroke;
    import javax.swing.SwingUtilities;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.DefaultHighlighter;
    import javax.swing.text.Highlighter;
    
    public class SSCCE extends JFrame {
    
        public SSCCE() {
            final JTextArea aMain = new JTextArea();
            aMain.setFont(new Font("Consolas", Font.PLAIN, 11));
            aMain.setMargin(new Insets(5, 5, 5, 5));
            aMain.setEditable(false);
            add(aMain);
    
            aMain.setText("The quick brown fox jumped over the lazy dog.");
            Highlighter h = aMain.getHighlighter();
            try {
                h.addHighlight(10, 15, new DefaultHighlighter.DefaultHighlightPainter(new Color(0xFFC800)));
            } catch (BadLocationException e) {
                e.printStackTrace();
            }
    
            aMain.getActionMap().put("Copy", new AbstractAction() {
                public void actionPerformed(ActionEvent e) {
                    Highlighter h = aMain.getHighlighter();
                    Highlighter.Highlight[] highlights = h.getHighlights();
                    StringBuilder sb = new StringBuilder(64);
                    sb.append("<html><body>");
                    boolean markedUp = false;
                    for (Highlighter.Highlight highlight : highlights) {
                        int start = highlight.getStartOffset();
                        int end = highlight.getEndOffset();
    
                        try {
                            String text = aMain.getDocument().getText(start, end - start);
    
                            sb.append("<span style = 'background-color: #FFC800'>");
                            sb.append(text);
                            sb.append("</span>");
                            markedUp = true;
                        } catch (BadLocationException ex) {
                            ex.printStackTrace();
                        }
                    }
                    sb.append("</body></html>");
                    if (markedUp) {
                        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
                        clipboard.setContents(new HtmlSelection(sb.toString()), null);
                    }
                }
            });
            aMain.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "Copy");
    
            setTitle("SSCCE");
            setSize(350, 150);
            setDefaultCloseOperation(EXIT_ON_CLOSE);
            setLocationRelativeTo(null);
            setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    new SSCCE();
                }
            });
        }
    
        private static class HtmlSelection implements Transferable {
    
            private static List<DataFlavor> htmlFlavors = new ArrayList<>(3);
    
            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) {
                    ex.printStackTrace();
                }
    
            }
    
            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);
            }
        }
    }
    

    我已将其粘贴到文本编辑器中并在浏览器中打开并粘贴到Word中

    以上是基于Copy jTable row with its grid lines into excel/word documents

  • 0

    根据MadProgrammer的答案,从概念的角度来看,这是一个现实,并使我能够提出以下建议 . 两个答案之间的唯一区别是我对多个DataFlavor的支持,因此 text/plain 被复制以及 text/html 味道 . 我还提供了一个改进的HTML标记例程,其中包含一个突出显示但在选择范围内的文本 .

    UPDATE 1 :我的原始答案没有处理嵌套突出显示的情况 - 现在可以使用解决方案将 JTextArea 中任何突出显示的文本导出到支持 text/htmltext/plain 的任何应用程序 .

    UPDATE 2 :现在添加了对所有不同类DataFlavor _1850489的建议的支持 . 如果我们有重叠突出显示从同一位置开始,我也修复了一个问题 .

    import java.awt.*;
    import java.awt.datatransfer.*;
    import java.awt.event.*;
    import java.io.*;
    import java.nio.charset.StandardCharsets;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.text.*;
    
    @SuppressWarnings("serial")
    
    public class SSCCE extends JFrame {
      public SSCCE() {
        final JTextArea aMain = new JTextArea();
        aMain.setFont(new Font("Consolas", Font.PLAIN, 11));
        aMain.setMargin(new Insets(5, 5, 5, 5));
        aMain.setEditable(false);
        add(aMain);
    
        aMain.setText("The quick brown fox jumped over the lazy dog.");
        Highlighter h = aMain.getHighlighter();
        try {
          h.addHighlight(10, 15, new DefaultHighlighter.DefaultHighlightPainter(new Color(0xFFC800)));
        }
        catch (BadLocationException e) { 
          e.printStackTrace(); 
        }
    
        aMain.getActionMap().put("Copy", new AbstractAction() {
          public void actionPerformed(ActionEvent e) {
            Highlighter h = aMain.getHighlighter();
            Highlighter.Highlight[] hls = h.getHighlights();
            int start = aMain.getSelectionStart();
            int end = aMain.getSelectionEnd();
            Document d = aMain.getDocument();
    
            Arrays.sort(hls, new Comparator<Highlighter.Highlight>() {
              @Override
              public int compare(Highlighter.Highlight a, Highlighter.Highlight b) {
                int r = a.getStartOffset() - b.getStartOffset();
                if (r == 0) {
                  r = (b.getEndOffset() - b.getStartOffset()) - (a.getEndOffset() - a.getStartOffset());
                }
                return r;
              }
            });
    
            try {
              StringBuilder sb = new StringBuilder();
              sb.append("<html><body>");
              sb.append("<pre style='font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace; font-size: 10pt'>");
    
              String s = d.getText(start, end - start);
              String as[] = s.replaceAll("\r?\n", "\n").split("(?!^)");
              Color ac[] = new Color[as.length];
    
              for (Highlighter.Highlight hl : hls) { 
                int hs = hl.getStartOffset();
                int he = hl.getEndOffset();
    
                if ((he > start) && (hs < end)) {
                  Color c = ((DefaultHighlighter.DefaultHighlightPainter)hl.getPainter()).getColor();
                  if ((c != null) && (aMain.getSelectionColor() != c)) {
                    hs = (hs < start) ? start : hs;
                    he = (he > end) ? end : he;
    
                    for (int i = (hs - start); i < (he - start); i++) {
                      ac[i] = c;
                    }
                  }
                }
                else if (hs > end) {
                  break;
                }
              }
    
              Color pc = null;
              for (int i = 0; i < as.length; i++) {
                if (ac[i] != pc) {
                  if (pc != null) {
                    sb.append("</span>");
                  }
                  if (ac[i] != null) {
                    sb.append("<span style='background-color: " + String.format("#%02x%02x%02x", ac[i].getRed(), ac[i].getGreen(), ac[i].getBlue()) + "'>");
                  }
                  pc = ac[i];
                }
                sb.append(as[i].replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\n", "<br>"));
              }
              if (pc != null) {
                sb.append("</span>");
              }
    
              sb.append("</pre>");
              sb.append("</body></html>");
    
              Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
              c.setContents(new MyTransferable(s, sb.toString()), null);
            }
            catch (BadLocationException ex) {
              ex.printStackTrace();
              aMain.copy();
            }
          }
        });
        aMain.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), "Copy");
    
        setTitle("SSCCE");
        setSize(350, 150);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
      }
    
      private static class MyTransferable implements Transferable {
        private static ArrayList<DataFlavor> MyFlavors = new ArrayList<DataFlavor>();
        private String plain = null;
        private String html = null;
    
        static {
          try {
            for (String m : new String[]{"text/plain", "text/html"}) {
              MyFlavors.add(new DataFlavor(m + ";class=java.lang.String"));
              MyFlavors.add(new DataFlavor(m + ";class=java.io.Reader"));
              MyFlavors.add(new DataFlavor(m + ";class=java.io.InputStream;charset=utf-8"));
            }
          }
          catch (ClassNotFoundException e) {
            e.printStackTrace();
          }
        }
    
        public MyTransferable(String plain, String html) {
          this.plain = plain;
          this.html = html;
        }
    
        public DataFlavor[] getTransferDataFlavors() {
          return MyFlavors.toArray(new DataFlavor[MyFlavors.size()]);
        }
    
        public boolean isDataFlavorSupported(DataFlavor flavor) {
          return MyFlavors.contains(flavor);
        }
    
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
          String s = null;
          if (flavor.getMimeType().contains("text/plain")) {
            s = plain;
          }
          else if (flavor.getMimeType().contains("text/html")) {
            s = html;
          }
          if (s != null) {
            if (String.class.equals(flavor.getRepresentationClass())) {
              return s;
            }
            else if (Reader.class.equals(flavor.getRepresentationClass())) {
              return new StringReader(s);
            }
            else if (InputStream.class.equals(flavor.getRepresentationClass())) {
              return new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8));
            }
          }
          throw new UnsupportedFlavorException(flavor);
        }
      }
    
      public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            new SSCCE();
          }
        });
      }
    }
    

Related