Immaginavo... perchè la stessa cosa l'ho fatta io.
Ti dò un aiuto.
Questa è la classe che gestisce la visualizzazione dei messaggi della chat che ho sviluppato io. Io uso un JTextPane e non un JEditorPane (è più semplice e non ho bisogno delle funzionalità del JEditorPane per quel che mi serve).
La parte che gestisce i messaggi da visualizzare è abbastanza commentata, ma in soldoni succede questo:

1) Arriva un Messaggio (puoi pensarlo come ad una stringa + altre informazioni di controllo)
2) Estraggo la stringa del messaggio
3) La spezzetto in token che mi dicono di cosa si tratta (testo o immagine)
4) Visualizzo tutti i token nell'ordine, secondo lo stile adatto.

Per semplicità ho fatto in modo che il token dell'immagine corrispondesse al nome del file GIF dell'immagine stessa. Ad esempio ': pizza :' --> pizza.gif [senza gli spazi]).
codice:
import java.io.*;
import java.awt.*;
import java.net.*;
import javax.swing.*;
import javax.swing.text.*;
import java.util.StringTokenizer;
import java.util.Vector;

public class ChatArea extends JPanel {

   private JScrollPane jsp;
   private JTextPane jtp;
   private ElencoMessaggi messaggi;
   private String [] immagini;

   private final int MAX_NUM_MESSAGGI = 300;   // Massimo numero messaggi visibili nell'area messaggi

   public ChatArea(String [] immagini) {
      setLayout(null);
      messaggi = new ElencoMessaggi();
      this.immagini = immagini;
      jtp = new JTextPane();
      jtp.setEditable(false);
      jsp = new JScrollPane(jtp);
      jsp.setBounds(0, 0, super.getWidth(), super.getHeight());
      messaggi.addObserver( new ChatAreaListener(jtp, MAX_NUM_MESSAGGI) );
      add(jsp);
      creaStili(jtp.getStyledDocument());
      creaEmotIcon(jtp.getStyledDocument());
   }

   public void paintComponent(Graphics g) {
      jsp.setSize(getWidth(), getHeight());
      jtp.setSize(getWidth(), getHeight());
   }

   public void aggiungiMessaggio(Messaggio msg) {
      int msgId = msg.getMsgId();
      String from = msg.getFrom() + ": ";
      String to = msg.getTo();
      String msgTesto = msg.getMsg();
      StyledDocument doc = jtp.getStyledDocument();
      int inizioMsg = doc.getLength();
      int lunghezzaMsg = 0;
      try {
         if (!from.equals(": ")) {
            doc.insertString(doc.getLength(), from, doc.getStyle("nome"));
         }
         if (msgId == Messaggio.PUBBLICO) {           // Messaggio pubblico
            if (from.equals(": ")) {   // Messaggio del server

               // Decodifico il messaggio secondo le regole del metodo 'decodifica'
               String [][] tmpMsg = decodifica(msgTesto, "server");

               // Scorro tutto l'array dei token del messaggio e li visualizzo secondo lo stile adottato
               for (int i=0; i<tmpMsg.length; i++)
                  doc.insertString(doc.getLength(), tmpMsg[i][1], doc.getStyle(tmpMsg[i][0]));
            } else {                 // Messaggio di un utente

               // Decodifico il messaggio secondo le regole del metodo 'decodifica'
               String [][] tmpMsg = decodifica(msgTesto, "normale");

               // Scorro tutto l'array dei token del messaggio e li visualizzo secondo lo stile adatto
               for (int i=0; i<tmpMsg.length; i++)
                  doc.insertString(doc.getLength(), tmpMsg[i][1], doc.getStyle(tmpMsg[i][0]));
            }
         } else {                    // Messaggio privato
            if (from.equals(": ")) {   // !!-- Messaggio privato del server --!!

               // Decodifico il messaggio secondo le regole del metodo 'decodifica'
               String [][] tmpMsg = decodifica(msgTesto, "pvt_server");

               // Scorro tutto l'array dei token del messaggio e li visualizzo secondo lo stile adatto
               for (int i=0; i<tmpMsg.length; i++)
                  doc.insertString(doc.getLength(), tmpMsg[i][1], doc.getStyle(tmpMsg[i][0]));
            } else {                 // Messaggio privato di un utente

               // Decodifico il messaggio secondo le regole del metodo 'decodifica'
               String [][] tmpMsg = decodifica(msgTesto, "pvt_utente");

               // Scorro tutto l'array dei token del messaggio e li visualizzo secondo lo stile adatto
               for (int i=0; i<tmpMsg.length; i++)
                  doc.insertString(doc.getLength(), tmpMsg[i][1], doc.getStyle(tmpMsg[i][0]));
            }
         }

         // Aggiungo 2 ritorni a capo per spaziare ciascun messaggio
         doc.insertString(doc.getLength(), "\n\n", doc.getStyle("normale"));
         jtp.setCaretPosition(doc.getLength());

         // Aggiorno l'elenco dei messaggi presenti nella ChatArea
         lunghezzaMsg = doc.getLength() - inizioMsg;
         messaggi.aggiungi(lunghezzaMsg);
      } catch (BadLocationException ble) { ble.printStackTrace(); }
   }

   /* Questo metodo decodifica un messaggio utente e lo spezzetta in un array di stringe:
      |------|-------| TIPO: indica il tipo di token e può essere il tipo passato come secondo parametro
      | TIPO | TOKEN |       oppure il tipo che identifica l'immagine rappresentata dal token
      |------|-------| TOKEN: è il contenuto del messaggio e può essere il testo da stampare oppure il nome
                              dell'immagine nel caso il token rappresenti un'immagine.

      Esempio: testo="Ciao! :smile: Come va oggi?" tipo="normale" --> decodifica di questo messaggio:
      |-----------|------------------|
      | "normale" | "Ciao! "         |
      |-----------|------------------|
      | "smile"   | "smile"          |
      |-----------|------------------|
      | "normale" | " Come va oggi?" |
      |-----------|------------------|
    */
   private String [][] decodifica(String testo, String tipo) {
      StringTokenizer st = new StringTokenizer(testo, ":", true);
      Vector v = new Vector();
      String [] elemento = new String[2];
      String tmpToken = "";
      String tokenString = "";
      String tmp_img = "";
      int countStd = 0;
      int countImg = 0;
      boolean cambio = false;
      boolean passo = false;

      // Tokenizzo il testo e lo salvo nel vector
      while (st.hasMoreTokens()) v.add(st.nextToken());

      // Creo l'array dei tokens
      String [] arrayToken = new String[v.size()];
      for (int i=0; i<arrayToken.length; i++) arrayToken[i] = (String) v.elementAt(i);
      v = new Vector();

      for (int i=0; i<arrayToken.length; i++) {
         tmpToken = arrayToken[i];
         if (tmpToken.equals(":")) {
            if (countImg == 2) {
               tmp_img = immagine(arrayToken[i-1]);
               if (tmp_img.equals("")) { // Non è un'immagine
                  countImg--;
                  countStd++;
                  tokenString += arrayToken[i-2] + arrayToken[i-1];
                  passo = true;
               } else { // E' un'immagine
                  cambio = true;
               }
            }
            if (countImg == 1 && !passo) { // Ho trovato "::"
               tokenString += ":";
            }
            if (countImg == 0) {
               countImg++;
            }
            passo = false;
         } else {
            if (countImg == 1) {
               countImg++;
            } else {
               tokenString += tmpToken;
               countStd++;
            }
         }
         if (cambio) {
            elemento[0] = tipo;
            elemento[1] = tokenString;
            v.add((String []) elemento.clone());
            elemento[0] = tmp_img;
            elemento[1] = arrayToken[i-1];
            v.add((String []) elemento.clone());
            countImg = countStd = 0;
            elemento = new String[2];
            tokenString = "";
            tmp_img = "";
            cambio = false;
         }
      }
      if (countImg > 0) {
         if (countImg > 1) {
            tokenString += arrayToken[arrayToken.length-2] + arrayToken[arrayToken.length-1];
            countImg = 0;
            countStd++;
         } else {
            tokenString += arrayToken[arrayToken.length-1];
            countImg = 0;
            countStd++;
         }
      }
      if (countStd > 0) {
         elemento[0] = tipo;
         elemento[1] = tokenString;
         v.add((String []) elemento.clone());
         countStd = 0;
      }
      // Recupero l'array di Stringhe nel Vector
      String [][] result = new String[v.size()][2];
      for (int i=0; i<v.size(); i++) {
         elemento = (String []) v.elementAt(i);
         result[i][0] = elemento[0];
         result[i][1] = elemento[1];
      }
      return result;
   }

   private String immagine(String token) {
      boolean trovato = false;
      int i = 0;
      String result = "";
      while ((i<immagini.length) && !trovato) {
         if (token.equals(immagini[i])) {
            trovato = true;
            result = immagini[i];
         }
         i++;
      }
      return result;
   }

   private void creaStili(StyledDocument doc) {
      Style deflt = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);

      // Stile normale
      Style normale = doc.addStyle("normale", deflt);
      StyleConstants.setFontFamily(deflt, "Arial");
      StyleConstants.setForeground(deflt, Color.black);
      StyleConstants.setFontSize(deflt, 12);

      // Stile server
      Style s = doc.addStyle("server", normale);
      StyleConstants.setItalic(s, true);
      StyleConstants.setUnderline(s, true);
      StyleConstants.setForeground(s, Color.gray);
      StyleConstants.setFontSize(s, 11);

      // Stile pvt_utente
      s = doc.addStyle("pvt_utente", normale);
      StyleConstants.setBold(s, true);
      StyleConstants.setForeground(s, Color.blue);
      StyleConstants.setFontSize(s, 14);

      // Stile pvt_server
      s = doc.addStyle("pvt_server", normale);
      StyleConstants.setBold(s, true);
      StyleConstants.setItalic(s, true);
      StyleConstants.setUnderline(s, true);
      StyleConstants.setForeground(s, Color.red);
      StyleConstants.setFontSize(s, 14);

      // Stile nome
      s = doc.addStyle("nome", normale);
      StyleConstants.setBold(s, true);
      StyleConstants.setForeground(s, Color.black);
   }

   private void creaEmotIcon(StyledDocument doc) {
      Style deflt = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
      Style s = null;
      ImageIcon img = null;
      // Creazione delle immagini
      for (int i=0; i<immagini.length; i++) {
         s = doc.addStyle(immagini[i], deflt);
         java.net.URL urlImg = ChatArea.class.getResource("immagini/" + immagini[i] + ".gif");
         if (urlImg != null) {
            img = new ImageIcon(urlImg, immagini[i]);
            StyleConstants.setIcon(s, img);
         } else {
            System.out.println("Impossibile trovare l'immagine" + immagini[i]);
            System.exit(0);
         }
      }
   }
}
Buon lavoro!


Ciao.