JEditorPane textausrichtung nach settext funktioniert nicht mehr

washpuda123

Mitglied
Hi, ich habe ein kleines Problem und hoffe jemand kann mir helfen. Ich habe ein JEditorPane vom Typ "text/html" zur Texteingabe. Über Buttons möchte ich die horizontale Textausrichtung für den selektierten Textabschnitt anpassen. Dies funktioniert einwandfrei, solange kein text mit setText(txt) gesetzt wurde (z.B. gespeicherten Text weiterbearbeiten möchte). Optisch funktioniert dies zwar weiterhin, jedoch gibt mir Methode getText() nur die Paragraphen mit den alten Ausrichtungen wieder (die Ausrichtung im p-Tag wird dann einfach nicht mehr übernommen, andere Attribute z.B. Textfarbe sind kein Problem). Mit der Schaltfläche "print to console" kann ich mir den HTML-Text auf der Console anzeigen lassen.

1671983732015.png
Ausgabe als HTML Text der Methode getText vom JEditorPane:
<html>
<head>

</head>
<body>
<p align="left" style="margin-top: 0">
A
</p>
<p align="center" style="margin-top: 0">
B
</p>
<p align="right" style="margin-top: 0">
C
</p>
</body>
</html>

Mit der Schaltfläche "setText" wird ePane.setText(ePane.getText) aufgerufen, danach wird die Ausrichtung im HTML-Text nicht mehr korrekt gesetzt.

Beispiel Quellcode.

Java:
import java.awt.BorderLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Element;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledEditorKit;

public class EditorPaneAlignTest implements CaretListener {

    JToggleButton tBtnAlignLeft = null, tBtnAlignCenter = null, tBtnAlignRight = null, tBtnAlignBlock = null;
    JEditorPane ePane;

    public static void main(String[] args) {
        new EditorPaneAlignTest();
    }

    public EditorPaneAlignTest() {

        JPanel pane = new JPanel(new BorderLayout(5, 5));
        ePane = new JEditorPane("text/html", null);
        ePane.addCaretListener(this);

        JScrollPane sPane = new JScrollPane(ePane);
        pane.add(sPane, BorderLayout.CENTER);
        JToolBar toolBar = new JToolBar();
        pane.add(toolBar, BorderLayout.NORTH);

        ButtonGroup btnGroupAlign = new ButtonGroup();

        tBtnAlignLeft = new JToggleButton();
        tBtnAlignLeft.setToolTipText("Text linksbündig ausrichten");
        tBtnAlignLeft.setAction(new StyledEditorKit.AlignmentAction("left", StyleConstants.ALIGN_LEFT));
        tBtnAlignLeft.setFocusable(false);
        toolBar.add(tBtnAlignLeft);
        btnGroupAlign.add(tBtnAlignLeft);

        tBtnAlignCenter = new JToggleButton();
        tBtnAlignCenter.setToolTipText("Text linksbündig ausrichten");
        tBtnAlignCenter.setAction(new StyledEditorKit.AlignmentAction("center", StyleConstants.ALIGN_CENTER));
        tBtnAlignCenter.setFocusable(false);
        toolBar.add(tBtnAlignCenter);
        btnGroupAlign.add(tBtnAlignCenter);

        tBtnAlignRight = new JToggleButton();
        tBtnAlignRight.setToolTipText("Text linksbündig ausrichten");
        tBtnAlignRight.setAction(new StyledEditorKit.AlignmentAction("right", StyleConstants.ALIGN_RIGHT));
        tBtnAlignRight.setFocusable(false);
        toolBar.add(tBtnAlignRight);
        btnGroupAlign.add(tBtnAlignRight);

        tBtnAlignBlock = new JToggleButton();
        tBtnAlignBlock.setToolTipText("Textblock");
        tBtnAlignBlock.setAction(new StyledEditorKit.AlignmentAction("block", StyleConstants.ALIGN_JUSTIFIED));
        tBtnAlignBlock.setFocusable(false);
        toolBar.add(tBtnAlignBlock);
        btnGroupAlign.add(tBtnAlignBlock);

        JButton btnPrintText = new JButton("print text to console");
        btnPrintText.addActionListener(a -> System.out.println(ePane.getText()));
        toolBar.add(btnPrintText);

        JButton btnSetText = new JButton("setText");
        btnSetText.addActionListener(a -> ePane.setText(ePane.getText()));
        toolBar.add(btnSetText);

        JFrame frame = new JFrame("Align-Test");
        frame.setBounds(100, 100, 600, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(pane);
        frame.setVisible(true);
    }

    @Override
    public void caretUpdate(CaretEvent e) {

        DefaultStyledDocument styleDoc = (DefaultStyledDocument) ePane.getDocument();
        Element element = styleDoc.getCharacterElement(ePane.getCaretPosition() - 1);
        AttributeSet attribSet = element.getAttributes();

        switch (StyleConstants.getAlignment(attribSet)) {
        case StyleConstants.ALIGN_LEFT:
            tBtnAlignLeft.setSelected(true);
            break;
        case StyleConstants.ALIGN_CENTER:
            tBtnAlignCenter.setSelected(true);
            break;
        case StyleConstants.ALIGN_RIGHT:
            tBtnAlignRight.setSelected(true);
            break;
        case StyleConstants.ALIGN_JUSTIFIED:
            tBtnAlignBlock.setSelected(true);
            break;
        }
    }
}
 

Anhänge

  • 1671983723560.png
    1671983723560.png
    11 KB · Aufrufe: 0
Mit der Schaltfläche "setText" wird ePane.setText(ePane.getText) aufgerufen, danach wird die Ausrichtung im HTML-Text nicht mehr korrekt gesetzt.
Bei mir funktioniert es. Ich kann auf meinem System den Knopf drücken und der Text behält die Ausrichtung bei.

Ich nutze einen M1 Mac und habe es sowohl mit GralVM 17 als auch Temurin 17 getestet.
 
Hi,
dies ist auch richtig, aber wenn du danach zum Beispiel das B selektierst und rechtsbündig ausrichtest, dann erfolgt dies nur optisch. Im HTML-Text über getText() bleibt die Ausrichtung für B weiterhin auf Center. Dies ist ein Problem beim Speichern und späterem Wiederherstellung des Textes.
 
Ahh ja, das hatte ich dann nicht richtig nachvollzogen. Das Verhalten kann ich nun bestätigen.

So auf Anhieb habe ich da aber auch keine Erklärung geschweige denn eine Lösung. Ich muss es mir später noch einmal mehr im Detail ansehen.
 
Habe damit zugegebenermaßen nicht viel Erfahrung, aber ich denke denke, das kommt daher, dass der Editor nicht html als Grundlage verwendet, bzw. nicht den Html-Text, den du übergeben hast. Den rendert er zwar, packt außen rum aber nochmal eine eigene Formatierung, Stichwort OuterHtml.
Weis einfach mal A,B,C zu ohne Html. Auch die kannst du nach Belieben rumschieben und mit der genannten Methode wirst du kein html rauskriegen.

Ich denke, du musst dir das HtmlDocument des Editors holen und dort die einzelnen Elemente untersuchen.
 
Also die Methoden getText / setText geben html bzw. nehmen html. Dabei ist es egal, ob man da <html> und <body> mitgibt oder nicht.

Es ist also möglich, den Code zur Erzeugung des JEditorPane z.B. so zu verändern:
Java:
        ePane = new JEditorPane("text/html",
                "    <p style=\"margin-top: 0\">\n" +
                "      aaa\n" +
                "    </p>\n" +
                "    <p align=\"center\" style=\"margin-top: 0\">\n" +
                "      bbb\n" +
                "    </p>\n" +
                "    <p align=\"right\" style=\"margin-top: 0\">\n" +
                "      ccc\n" +
                "    </p>\n");

Das führt dann intern bei getText() zu der Ausgabe:
HTML:
<html>
  <head>
    
  </head>
  <body>
    <p style="margin-top: 0">
      aaa
    </p>
    <p align="center" style="margin-top: 0">
      bbb
    </p>
    <p align="right" style="margin-top: 0">
      ccc
    </p>
  </body>
</html>

Das nimmt das Control also. Aber wenn man dann bei der Zeile bbb das Allignment ändert, dann bleibt es bei dem align="center" bei dem getText Aufruf.

Da bin ich auch gerade etwas ratlos. Denn die Anzeige passt er ja an. Nur eben die Grundlage der Daten wird irgendwie nicht angepasst.

Wenn man sich also das zugrunde liegende Dokument ausgeben lässt (was aber getText() ja auch nur macht), dann sieht man, dass das Dokument ebenso falsch ist:
Java:
            HTMLDocument doc = (HTMLDocument) ePane.getDocument();
            EditorKit kit = ePane.getEditorKit();
            StringWriter writer = new StringWriter();
            try {
                kit.write(writer, doc, 0, doc.getLength());
                System.out.println(writer.toString());
            } catch (Exception ex) {
                ex.printStackTrace();
            }

Ich bin also etwas ratlos, was das Problem hier angeht.
 
Wenn ich es richtig sehe (das ist eher eine Vermutung), besteht das Problem, weil die Stile "asymmetrisch" behandelt werden. Bei setText() wird die Methode StyleSheet#addAttributes aufgerufen:
Code:
     * Adds a set of attributes to the element.  If any of these attributes
     * are StyleConstants attributes, they will be converted to CSS prior
     * to forwarding to the superclass behavior.
D. h. das HTML-Attribut "align" wird geparst, und kommt dann zusätzlich als CSS-Stil zu den vorhandenen Attributen, zu denen auch das HTML-Attribut "align" zählt. Geändert wird dann das CSS-Attribut und bei der Ausgabe scheint wohl das align-Attribut verwendet zu werden, sofern vorhanden.

Das könnte man ggf. mal testen, indem man alle HTML-Attribute "align" nach setText() entfernt.
 
Hallo Danke erstmal,

wie kann ich den dass Align nach dem setzen entfernen? Vor dem Setzen ist es einfach, da ich einfach die Methode setText() vom Editor überschreiben kann und die Ausrichtung dort rauslöschen kann. Dann wird alles jedoch erwartungsgemäß linksbündig angezeigt. Nachträglich müsste ich ja direkt das HTML bearbeiten.

Java:
    @Override
            public void setText(String t) {
                if (t != null) {
                    String txtWithoutAlign = t.replace("align=\"center\"", "").replace("align=\"justify\"", "")
                            .replace("align=\"right\"", "").replace("align=\"left\"", "");
                    Utils.print(txtWithoutAlign);
                    super.setText(txtWithoutAlign);
                } else {
                    super.setText(null);
                }
            }
 
Nicht schön aber selten:

Java:
        btnSetText.addActionListener(a -> {
            ePane.setText(ePane.getText());
            Document doc = ePane.getDocument();
            replaceAlignByTextAlign(doc.getRootElements());
        });

Mit
Java:
    private void replaceAlignByTextAlign(Element[] siblings) {
        for (Element parent : siblings) {
            AttributeSet attributeSet = parent.getAttributes();
            if (attributeSet.isDefined(HTML.Attribute.ALIGN)) {
                setAttributes(parent, toTextAlign(parent.getAttributes()));
            }
            
            Element[] children = new Element[parent.getElementCount()];
            for (int i = 0; i < children.length; i++) {
                children[i] = parent.getElement(i);
            }
            
            replaceAlignByTextAlign(children);
        }
    }
    
    private void setAttributes(Element elem, AttributeSet newAttributes) {
        int start = elem.getStartOffset();
        HTMLDocument html = (HTMLDocument) elem.getDocument();
        html.setParagraphAttributes(start, 0, newAttributes, true);
    }
    
    private AttributeSet toTextAlign(AttributeSet attributes) {
        MutableAttributeSet attrs = new SimpleAttributeSet(attributes);
        String value = attrs.getAttribute(HTML.Attribute.ALIGN).toString();
        attrs.removeAttribute(HTML.Attribute.ALIGN);
        attrs.addAttribute(CSS.Attribute.TEXT_ALIGN, value);
        return attrs;
    }
 
Vielen Dank euch beiden. Die Lösung von Mihe7 funktioniert (zumindest ab da, wo ich drauf gekommen bin, das Löschen der Ausrichtung in der Methode setText() wieder zu entfernen). Ich musste jetzt nur noch die Methode caretUpdate() anpassen, damit auch die Schaltflächen korrekt gesetzt werden.

Java:
@Override
    public void caretUpdate(CaretEvent e) {
        DefaultStyledDocument styleDoc = (DefaultStyledDocument) ePane.getDocument();
        Element element = styleDoc.getCharacterElement(ePane.getCaretPosition());
        if (element.getParentElement().getName().equalsIgnoreCase("p")) {
            AttributeSet attribSetPara = element.getParentElement().getAttributes();
            Object obj = attribSetPara.getAttribute(CSS.Attribute.TEXT_ALIGN);
            if (obj == null) {
                tBtnAlignLeft.setSelected(true);
            } else {
                switch (obj.toString()) {
                case "left":
                    tBtnAlignLeft.setSelected(true);
                    break;
                case "center":
                    tBtnAlignCenter.setSelected(true);
                    break;
                case "right":
                    tBtnAlignRight.setSelected(true);
                    break;
                case "justify":
                    tBtnAlignBlock.setSelected(true);
                    break;
                }
            }
        }
    }
 

Zurück
Oben