1

テキストの一部に前景色と背景色を適用できる小さな HTML エディターを実装しようとしています。テキストを HTML 形式で保存し、カラー情報をファイルに保存したいと考えています。私の現在の実装は、さまざまなスタイルを使用してテキストを編集するときに適切に機能します (スタイルは前景色と背景色の情報を定義します)。しかし、編集したドキュメントをファイルに保存すると、不適切な構造化 HTML が表示され、特定のスタイルを使用して入力されたテキストの一部が失われます。これが私のコードです:

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);
        }
    }
}

以下は、スタイル付きテキストが入力されたアプリケーションのスクリーンショットです。 色付きのテキストを含むアプリケーション

ドキュメントを保存したときに作成される HTML ファイル ( textPane.write(writer);) の構造が正しくありません。パーツは次の<body> ... </body>ようになります。

<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>

生成された HTML コードが正しくないことは明らかです。要素内で、スタイルの前景色を定義<p>する新しい要素が作成されます。<font>次に<p>、背景色の定義を持つ別の要素が作成されます。しかし、テキストが欠落しています。</p>次に、終了タグの代わりに終了</font>タグが続き、その後に</p>外側の<p>要素を閉じるタグが続きます。私は何を間違っていますか?

4

1 に答える 1