1

I'm trying to implement a little HTML editor that allows for application of foreground and background colors to parts of the text. I want to store the text in HTML format with the coloring information kept in the file. My current implementation functions properly when editing text in it using different styles (the styles define foreground and background color information). But when storing the edited document in a file I get improper structured HTML and the parts of the text that were entered using a specific style are missing. Here is my code:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JTextPane;
import javax.swing.JToolBar;
import javax.swing.text.AttributeSet;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.Style;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;

public class SimpleEditor extends JFrame {

    private static final long   serialVersionUID = 1L;
    private final JTextPane   textPane;
    private final HTMLEditorKit edtKit;
    private final HTMLDocument  doc;
    private final StyleSheet predefStyles;

    public static void main(String[] args) {
        final SimpleEditor editor = new SimpleEditor();
        editor.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        editor.setVisible(true);
    }

    public SimpleEditor() {
        super("Very Simple HTML Editor");
        textPane = new JTextPane();
        edtKit = new HTMLEditorKit();
        textPane.setEditorKit(edtKit);
        predefStyles = new StyleSheet();
        predefStyles.addRule("MyStyle1 { color:#cc0000; background-color:silver }\n" +
                             "MyStyle2 { color:#0000cc; background-color:aqua }");
        doc = new HTMLDocument(predefStyles);
        textPane.setDocument(doc);

        final Container content = getContentPane();
        content.add(textPane, BorderLayout.CENTER);
        content.add(createToolBar(), BorderLayout.NORTH);
        setJMenuBar(createMenuBar());
        setSize(500, 240);
    }

    private JToolBar createToolBar() {
        final Vector<String> styleNames = new Vector<String>();
        final Enumeration<?> names = predefStyles.getStyleNames();
        while (names.hasMoreElements()) {
            styleNames.add((String) names.nextElement());
        }
        final DefaultComboBoxModel<String> stylesModel =
                new DefaultComboBoxModel<String>(styleNames);
        final JComboBox<String> cbStyleSel = new JComboBox<String>(stylesModel);
        final JToolBar bar = new JToolBar();
        cbStyleSel.setEditable(false);
        cbStyleSel.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                e.getSource();
                @SuppressWarnings("unchecked")
                final JComboBox<CondStyle> cboStyleSel =
                          (JComboBox<CondStyle>) e.getSource();
                final String selItem = (String) cboStyleSel.getSelectedItem();
                final Style style = textPane.getStyle(selItem);
                textPane.setCharacterAttributes(extractStyleAttribs(style), true);
                SimpleEditor.this.requestFocus();
            }
        });
        bar.add(cbStyleSel);
        return bar;
    }

    /**
     * Extracts the style attributes except the style's name
     * @param aStyle The style to be processed
     * @return The visual attributes extracted from the style
     */
    AttributeSet extractStyleAttribs(Style aStyle) {
        final MutableAttributeSet attribs = new SimpleAttributeSet();
        final Enumeration<?> attribNames = aStyle.getAttributeNames();
        while (attribNames.hasMoreElements()) {
            final Object attribName = attribNames.nextElement();
            if (attribName == Style.NameAttribute) {
                continue;
            }
            attribs.addAttribute(attribName, aStyle.getAttribute(attribName));
        }
        return attribs;
    }

    private JMenuBar createMenuBar() {
        final JMenuBar menubar = new JMenuBar();
        final JMenu mnuFile = new JMenu("File");
        menubar.add(mnuFile);
        final ExitAction actSave = new ExitAction();
        mnuFile.add(actSave);
        return menubar;
    }

    class ExitAction extends AbstractAction {
        private static final long serialVersionUID = 1L;
        public ExitAction() {
            super("Save & Exit");
        }
        @Override
        public void actionPerformed(ActionEvent ev) {
            final JFileChooser chooser = new JFileChooser();
            if (chooser.showSaveDialog(SimpleEditor.this) != 
                JFileChooser.APPROVE_OPTION)
                return;
            final File file = chooser.getSelectedFile();
            if (file == null)
                return;
            FileWriter writer = null;
            try {
                writer = new FileWriter(file);
                textPane.write(writer);
            } catch (final IOException ex) {
                JOptionPane.showMessageDialog(SimpleEditor.this,
                                              "File Not Saved", "ERROR",
                                              JOptionPane.ERROR_MESSAGE);
            } finally {
                if (writer != null) {
                    try {
                        writer.close();
                    } catch (final IOException x) {
                    }
                }
            }
            System.exit(0);
        }
    }
}

Here a screenshot of the application with some styled text entered: The application with some colored text

The HTML file which is created when I save the document (textPane.write(writer);) is not properly structured. The <body> ... </body> part looks as follows:

<body>
    <p style="margin-top: 0">
      default text
    </p>
    <p style="margin-top: 0">
      <font color="#cc0000"><p style="background-color: silver">
</font>    </p>
    <p style="margin-top: 0">
      <font color="#0000cc"><p style="background-color: aqua">
</font>    </p>
    <p style="margin-top: 0">
      <font color="#333333" face="Dialog" size="3"><p FONT_ATTRIBUTE_KEY="javax.swing.plaf.FontUIResource[family=Dialog,name=Dialog,style=plain,size=12]">
</font>    </p>
</body>

It is quite obvious that the generated HTML code is not correct. Inside a <p> element a new <font> element is created which defines the style's foreground color. Then another <p> element with the definition of the background color is created. But the text is missing. Then instead of the closing </p> tag the closing </font> tag follows and afterwards the </p> tag closing the outer <p> element. What am I doing wrong?

apatwork
  • 71
  • 1
  • 8

1 Answers1

0

After lots of reseach I found a simple solution for my problem. See here.

Community
  • 1
  • 1
apatwork
  • 71
  • 1
  • 8