Search code examples
javahtmlstringswingjtextpane

How to convert the position in JTextPane with HTMLDocument to position in JTextPane string text


I have an application in java swing containing JTextPane with HTMLDocument. Let's say I set text of the pane to:

<html>
   <head>

   </head>
   <body>
       <p style="margin-top: 0">
            I have a problem now.
       </p>
   </body>
</html>

So I see the text "I have a problem now." on the pane.

Let's assume that I clicked on a pane and the caret was set between 'p' and 'r' in "problem" word. In this situaction if I call getCaretPosition on my JTextPane it will return 10 (if I count well :) ).

Now knowing this position I would like to convert this position to the position in html string writen above (which is 94 again if I count well :) ) How to do that?


Solution

  • First of all, you have to understand that in html you can't keep the logic of "caret position". As StanislavL told you, it would make no sense as Hello can be translated as well by <html><body>Hello</body></html> than by <html> <body>Hello</body> </html>. How would you know in this case which position corresponds to what ?

    The mistake is to try to compare the JTextPane text content with the HTML conversion of it. Instead, you should compare the HTMLDocument with the DOM. So, first thing, you need an html parser like JSoup.

    Once you have added JSoup to your project, you can very easily make the parallel between your html and your JTextPane content.

    You can get the html with this method :

    public static String getHTMLContent(HTMLDocument htmlDoc, int startOffset, int length) {
        StringWriter writer = new StringWriter();
        try {
            new HTMLEditorKit().write(writer, htmlDoc, startOffset, length);
        } catch (IOException | BadLocationException ex) {
            Logger.getLogger(Editeur.class.getName()).log(Level.SEVERE, null, ex);
        }
        String html = writer.toString();
        return html;
    }
    

    You can then parse it with Jsoup :

    Document doc = Jsoup.parse(html);
    doc.getElementById("myId");//get the element by its ID
    

    So, now, if you want to locate a specific element from the HTMLDocument in the resulting html, what you need to do is to surround it with a <span> that you will give an ID to, and then get it with getElementById. To do so, you can use HTMLEditorKit.insertHTML :

    (new HTMLEditorKit()).insertHTML(htmlDoc, pos, "<span id='myId'>element of interest</span>", 0, 0, Tag.SPAN);
    

    For instance, to get the location of the selected text, you can do :

        if (getSelectedText() != null && getSelectedText().length()>0) {
            try {
                String selectedText = getSelectedText()
                htmlDoc.remove(getSelectionStart(), this.getSelectedText().length());
                (new HTMLEditorKit()).insertHTML(htmlDoc, pos, "<span id='myId'>"+selectedText+"</span>", 0, 0, Tag.SPAN);
            } catch (BadLocationException ex) {
                Logger.getLogger(Editeur.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    

    Now you can easily get the part you're interested or with getElementById from Jsoup, or by HTMLDocument.getElement(id) from Java.

    I can give more details on specific points if needed.