2015-09-28 2 views
6
JTextPane text; 
text.setText("somewords <img src=\"file:///C:/filepath/fire.png\" text=\"[fire1]\" title=\"[fire2]\" alt=\"[fire3]\" style=\"width:11px;height:11px;\"> otherwords"); 

Дает мне это enter image description here, что как и ожидалось. Но когда я выделяю его и копирую его, я получаю «someords   другие слова». То же самое сделано в Firefox при копировании, чтобы вставить «someords [fire3] otherwords» (он заменяет текст alt для изображения). Есть ли способ воспроизвести это поведение, когда скопирован текст alt или какое-либо другое указание на копирование изображения? Я предполагаю, что это не встроенная функция, поэтому то, что мне, вероятно, нужно знать, - это то, что должно быть перегружено, чтобы имитировать это поведение.Копирование img из HTML в Java Swing

Его за выход/окно чата так важно, что, когда пользователи процитировать это включает в себя изображения (как бы) эмоций


Update: Успешно отменяют метод CopyAction ... что теперь?

// (should) allow copying of alt text in place of images 
class CustomEditorKit extends HTMLEditorKit { 
    Action[] modifiedactions; 
    CustomEditorKit() { 
     int whereat=-1; 
     modifiedactions=super.getActions(); 
     for(int k=0;k<super.getActions().length;k++) { 
      if(super.getActions()[k] instanceof CopyAction) //find where they keep the copyaction 
      { 
       whereat=k; 
       modifiedactions[whereat]=new CustomCopyAction(); //and replace it with a different one 
      } 
     } 
    } 
    @Override 
    public Action[] getActions() { 
     return modifiedactions; //returns the modified version instead of defaultActions 
    } 
    public static class CustomCopyAction extends TextAction { 
     public CustomCopyAction() { 
      super(copyAction); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { //need to change this to substitute images with text, preferably their alt text. 
      JTextComponent target = getTextComponent(e); 
      //target.getText() gives full body of html, unbounded by selection area 
      if (target != null) { 
       target.copy(); //a confusing and seemingly never ending labyrinth of classes and methods 
      } 
     } 
    } 
} 
+0

Примечание: 'import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; 'поддерживать это по умолчанию. – gunfulker

ответ

2

Единственный способ, которым я могу думать о выполнении этого, написав свой собственный TransferHandler, и опрокинув getSourceActions и методы exportToClipboard.

Вы можете конвертировать HTML в простой текст самостоятельно, вместо того, чтобы свинг использовать getSelectedText метод JTextPane, рекурсивно преобразования каждого Element из HTML Document, настройка преобразования в том случае, когда элемент имеет NameAttribute из IMG и также имеет атрибут ALT.

Вот что я придумал:

import java.io.InputStream; 
import java.io.ByteArrayInputStream; 
import java.io.Reader; 
import java.io.StringReader; 
import java.io.StringWriter; 
import java.io.IOException; 

import java.nio.ByteBuffer; 
import java.nio.CharBuffer; 
import java.nio.charset.Charset; 
import java.nio.charset.StandardCharsets; 

import java.util.Collection; 
import java.util.Collections; 
import java.util.LinkedHashSet; 

import java.awt.EventQueue; 
import java.awt.datatransfer.Clipboard; 
import java.awt.datatransfer.DataFlavor; 
import java.awt.datatransfer.Transferable; 
import java.awt.datatransfer.UnsupportedFlavorException; 

import javax.swing.JComponent; 
import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTextPane; 
import javax.swing.TransferHandler; 

import javax.swing.text.AttributeSet; 
import javax.swing.text.Document; 
import javax.swing.text.Element; 
import javax.swing.text.BadLocationException; 
import javax.swing.text.html.HTML; 

public class HTMLCopier 
extends TransferHandler { 
    private static final long serialVersionUID = 1; 

    private final Collection<DataFlavor> flavors; 

    HTMLCopier() { 
     Collection<DataFlavor> flavorList = new LinkedHashSet<>(); 
     Collections.addAll(flavorList, 
      new DataFlavor(String.class, null), 
      DataFlavor.stringFlavor); 

     String[] mimeTypes = { 
      "text/html", "text/plain" 
     }; 
     Class<?>[] textClasses = { 
      Reader.class, String.class, CharBuffer.class, char[].class 
     }; 
     Class<?>[] byteClasses = { 
      InputStream.class, ByteBuffer.class, byte[].class 
     }; 
     String[] charsets = { 
      Charset.defaultCharset().name(), 
      StandardCharsets.UTF_8.name(), 
      StandardCharsets.UTF_16.name(), 
      StandardCharsets.UTF_16BE.name(), 
      StandardCharsets.UTF_16LE.name(), 
      StandardCharsets.ISO_8859_1.name(), 
      "windows-1252", 
      StandardCharsets.US_ASCII.name(), 
     }; 

     try { 
      flavorList.add(new DataFlavor(
       DataFlavor.javaJVMLocalObjectMimeType + 
       "; class=" + String.class.getName())); 

      for (String mimeType : mimeTypes) { 
       for (Class<?> textClass : textClasses) { 
        flavorList.add(new DataFlavor(String.format(
         "%s; class=\"%s\"", 
         mimeType, textClass.getName()))); 
       } 
       for (String charset : charsets) { 
        for (Class<?> byteClass : byteClasses) { 
         flavorList.add(new DataFlavor(String.format(
          "%s; charset=%s; class=\"%s\"", 
          mimeType, charset, byteClass.getName()))); 
        } 
       } 
      } 

      for (String mimeType : mimeTypes) { 
       flavorList.add(new DataFlavor(String.format(
        "%s; charset=unicode; class=\"%s\"", 
        mimeType, InputStream.class.getName()))); 
      } 
     } catch (ClassNotFoundException e) { 
      throw new RuntimeException(e); 
     } 

     this.flavors = Collections.unmodifiableCollection(flavorList); 
    } 

    @Override 
    public int getSourceActions(JComponent component) { 
     return COPY_OR_MOVE; 
    } 

    @Override 
    public void exportToClipboard(JComponent component, 
            Clipboard clipboard, 
            int action) { 
     JTextPane pane = (JTextPane) component; 
     Document doc = pane.getDocument(); 

     int start = pane.getSelectionStart(); 
     int end = pane.getSelectionEnd(); 

     final String html; 
     final String plainText; 
     try { 
      StringWriter writer = new StringWriter(end - start); 
      pane.getEditorKit().write(writer, doc, start, end - start); 
      html = writer.toString(); 

      StringBuilder plainTextBuilder = new StringBuilder(); 
      appendTextContent(doc.getDefaultRootElement(), start, end, 
       plainTextBuilder); 
      plainText = plainTextBuilder.toString(); 
     } catch (BadLocationException | IOException e) { 
      throw new RuntimeException(e); 
     } 

     Transferable contents = new Transferable() { 
      @Override 
      public boolean isDataFlavorSupported(DataFlavor flavor) { 
       return flavors.contains(flavor); 
      } 

      @Override 
      public DataFlavor[] getTransferDataFlavors() { 
       return flavors.toArray(new DataFlavor[0]); 
      } 

      @Override 
      public Object getTransferData(DataFlavor flavor) 
      throws UnsupportedFlavorException, 
        IOException { 

       String data; 
       if (flavor.isMimeTypeEqual("text/html")) { 
        data = html; 
       } else { 
        data = plainText; 
       } 

       Class<?> dataClass = flavor.getRepresentationClass(); 
       if (dataClass.equals(char[].class)) { 
        return data.toCharArray(); 
       } 
       if (flavor.isRepresentationClassReader()) { 
        return new StringReader(data); 
       } 
       if (flavor.isRepresentationClassCharBuffer()) { 
        return CharBuffer.wrap(data); 
       } 
       if (flavor.isRepresentationClassByteBuffer()) { 
        String charset = flavor.getParameter("charset"); 
        return Charset.forName(charset).encode(data); 
       } 
       if (flavor.isRepresentationClassInputStream()) { 
        String charset = flavor.getParameter("charset"); 
        return new ByteArrayInputStream(
         data.getBytes(charset)); 
       } 
       if (dataClass.equals(byte[].class)) { 
        String charset = flavor.getParameter("charset"); 
        return data.getBytes(charset); 
       } 
       return data; 
      } 
     }; 

     clipboard.setContents(contents, null); 

     if (action == MOVE) { 
      pane.replaceSelection(""); 
     } 
    } 

    private void appendTextContent(Element element, 
            int textStart, 
            int textEnd, 
            StringBuilder content) 
    throws BadLocationException { 
     int start = element.getStartOffset(); 
     int end = element.getEndOffset(); 
     if (end < textStart || start >= textEnd) { 
      return; 
     } 

     start = Math.max(start, textStart); 
     end = Math.min(end, textEnd); 

     AttributeSet attr = element.getAttributes(); 
     Object tag = attr.getAttribute(AttributeSet.NameAttribute); 

     if (tag.equals(HTML.Tag.HEAD) || 
      tag.equals(HTML.Tag.TITLE) || 
      tag.equals(HTML.Tag.COMMENT) || 
      tag.equals(HTML.Tag.SCRIPT)) { 

      return; 
     } 

     if (tag.equals(HTML.Tag.INPUT) || 
      tag.equals(HTML.Tag.TEXTAREA) || 
      tag.equals(HTML.Tag.SELECT)) { 

      // Swing doesn't provide a way to read input values 
      // dynamically (as far as I know; I could be wrong). 
      return; 
     } 

     if (tag.equals(HTML.Tag.IMG)) { 
      Object altText = attr.getAttribute(HTML.Attribute.ALT); 
      if (altText != null) { 
       content.append(altText); 
      } 
      return; 
     } 

     if (tag.equals(HTML.Tag.CONTENT)) { 
      content.append(
       element.getDocument().getText(start, end - start)); 
      return; 
     } 

     int count = element.getElementCount(); 
     for (int i = 0; i < count; i++) { 
      appendTextContent(element.getElement(i), textStart, textEnd, 
       content); 
     } 
    } 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       JTextPane text = new JTextPane(); 
       text.setContentType("text/html"); 
       text.setEditable(false); 
       text.setText("somewords <img src=\"file:///C:/filepath/fire.png\" text=\"[fire1]\" title=\"[fire2]\" alt=\"[fire3]\" style=\"width:11px;height:11px;\"> otherwords"); 

       text.setTransferHandler(new HTMLCopier()); 

       JFrame window = new JFrame("HTML Copier"); 
       window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       window.getContentPane().add(new JScrollPane(text)); 
       window.pack(); 
       window.setLocationByPlatform(true); 
       window.setVisible(true); 

       text.selectAll(); 
       text.copy(); 
      } 
     }); 
    } 
} 

Edit: Обновленный код, чтобы правильно разместить только выделенный текст в буфер обмена.

+0

Работает отлично, спасибо за такой полный ответ. – gunfulker

+0

О, он копирует все это каждый раз, независимо от того, что подсвечено. Полагая, что сейчас ... – gunfulker

+0

@gunfulker К сожалению, вы правы. Исправлена. – VGR

2

JTextPane предоставляет метод setEditorKit(EditorKit). Я думаю, вы найдете свое решение, предоставив собственный редактор EditorKit.

Вы можете переопределить действия копирования и вырезания в DefaultEditorKit, а затем передать их в JTextPane.

http://docs.oracle.com/javase/7/docs/api/javax/swing/text/DefaultEditorKit.html#copyAction

Или Java 8 предоставляет HTMLEditorKit, что, если это совместимо с JTextPane, может обеспечить поведение, которое вы хотите.

https://docs.oracle.com/javase/8/docs/api/javax/swing/text/html/HTMLEditorKit.html

+0

Спасибо, я попытался это, но столкнулся с некоторыми осложнениями. Я пытаюсь сделать первый вариант, который вы выдвинули. Во-первых, это 'StyledEditorKit' (легко сделано). Во-вторых, нет 'CopyAction()', вместо этого его класс в 'DefaultEditorKit' назвал это, его добавили в список' Action [] defaultActions', из которого извлекаются действия. Поэтому я попробовал «Переопределить» этот подкласс. Не работает. Я также попробовал 'getActions() [8] = new CustomCopyAction();' Не работал. Ни один из них никогда не входил в метод actionPerformed() 'TextAction', который я написал. Все еще пытаетесь, какие-то предложения? – gunfulker

+0

Расширение 'HTMLEditorKit' вместо этого делает HTML-рендеринг корректным, так как он расширяет' StyledEditorKit'. 'createInputAttributes' выглядит так, будто может позаботиться о том, чего я хочу? (В третий раз это редактирование) – gunfulker

+0

Успех (при переопределении CustomCopyAction, все равно придется вставлять текст изображения как-то) – gunfulker

Смежные вопросы