Hey, implemented this. It still doesn't work well due to remaining bugs in AbstractWriter, StyledDocument, etc. But it works fine on the JDK.
2006-05-20 Sven de Marothy <[EMAIL PROTECTED]> * javax/swing/text/html/MinimalHTMLWriter.java: New file
/* MinimalHTMLWriter.java -- Copyright (C) 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. GNU Classpath is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Classpath is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Classpath; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ package javax.swing.text.html; import javax.swing.text.AttributeSet; import javax.swing.text.AbstractWriter; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Element; import javax.swing.text.ElementIterator; import javax.swing.text.StyleConstants; import javax.swing.text.Style; import javax.swing.text.StyledDocument; import java.io.Writer; import java.io.IOException; import java.util.Enumeration; import java.util.Stack; import java.awt.Color; /** * MinimalHTMLWriter, * A minimal AbstractWriter implementation for HTML. * * @author Sven de Marothy */ public class MinimalHTMLWriter extends AbstractWriter { private StyledDocument doc; private Stack tagStack; private boolean inFontTag = false; /** * Constructs a MinimalHTMLWriter. * @param w - a Writer, for output. * @param doc - the document */ public MinimalHTMLWriter(Writer w, StyledDocument doc) { super(w, doc); this.doc = doc; tagStack = new Stack(); } /** * Constructs a MinimalHTMLWriter. * @param w - a Writer, for output. * @param doc - the document * @param pos - start position * @param len - length */ public MinimalHTMLWriter(Writer w, StyledDocument doc, int pos, int len) { super(w, doc, pos, len); this.doc = doc; tagStack = new Stack(); } /** * Starts a span tag. */ protected void startFontTag(String style) throws IOException { if( inFontTag() ) endOpenTags(); writeStartTag("<span style=\""+style+"\">"); inFontTag = true; } /** * Returns whether the writer is within two span tags. */ protected boolean inFontTag() { return inFontTag; } /** * Ends a span tag. */ protected void endFontTag() throws IOException { writeEndTag("</span>"); inFontTag = false; } /** * Write the entire HTML document. */ public synchronized void write() throws IOException, BadLocationException { writeStartTag("<html>"); writeHeader(); writeBody(); writeEndTag("</html>"); } /** * Write a start tag and increment the indent. */ protected void writeStartTag(String tag) throws IOException { indent(); write(tag+NEWLINE); incrIndent(); } /** * Write an ending tag and decrement the indent. */ protected void writeEndTag(String endTag) throws IOException { decrIndent(); indent(); write(endTag+NEWLINE); } /** * Write the HTML header. */ protected void writeHeader() throws IOException { writeStartTag("<head>"); writeStartTag("<style>"); writeStartTag("<!--"); writeStyles(); writeEndTag("-->"); writeEndTag("</style>"); writeEndTag("</head>"); } /** * Write a paragraph start tag. */ protected void writeStartParagraph(Element elem) throws IOException { indent(); write("<p class=default>"+NEWLINE); // FIXME: Class value = ? incrIndent(); } /** * Write a paragraph end tag, closes any other open tags. */ protected void writeEndParagraph() throws IOException { endOpenTags(); writeEndTag("</p>"); } /** * Writes the body of the HTML document. */ protected void writeBody() throws IOException, BadLocationException { writeStartTag("<body>"); ElementIterator ei = getElementIterator(); Element e = ei.first(); boolean inParagraph = false; do { if( e.isLeaf() ) { boolean hasNL = (getText(e).indexOf(NEWLINE) != -1); if( !inParagraph && hasText( e ) ) { writeStartParagraph(e); inParagraph = true; } if( hasText( e ) ) writeContent(e, true); if( hasNL && inParagraph ) { writeEndParagraph(); inParagraph = false; } else endOpenTags(); } } while((e = ei.next()) != null); writeEndTag("</body>"); } protected void text(Element elem) throws IOException, BadLocationException { write( getText(elem).trim() ); } /** * Write bold, indent and underline tags. */ protected void writeHTMLTags(AttributeSet attr) throws IOException { if(attr.getAttribute(StyleConstants.Bold) != null) if(((Boolean)attr.getAttribute(StyleConstants.Bold)).booleanValue()) { write("<b>"); tagStack.push("</b>"); } if(attr.getAttribute(StyleConstants.Italic) != null) if(((Boolean)attr.getAttribute(StyleConstants.Italic)).booleanValue()) { write("<i>"); tagStack.push("</i>"); } if(attr.getAttribute(StyleConstants.Underline) != null) if(((Boolean)attr.getAttribute(StyleConstants.Underline)).booleanValue()) { write("<u>"); tagStack.push("</u>"); } } /** * Returns whether the element contains text or not. */ protected boolean isText(Element elem) { return (elem.getEndOffset() != elem.getStartOffset()); } /** * Writes the content of an element. */ protected void writeContent(Element elem, boolean needsIndenting) throws IOException, BadLocationException { writeNonHTMLAttributes(elem.getAttributes()); if(needsIndenting) indent(); writeHTMLTags(elem.getAttributes()); if( isText(elem) ) text(elem); else writeLeaf(elem); endOpenTags(); } /** * Writes a non-text leaf element. */ protected void writeLeaf(Element e) throws IOException { // NOTE: Haven't tested if this is correct. if(e.getName().equals(StyleConstants.IconElementName)) writeImage(e); else writeComponent(e); } /** * Write the HTML attributes which do not have tag equivalents, * e.g. attributes other than bold/italic/underlined. */ protected void writeNonHTMLAttributes(AttributeSet attr) throws IOException { String style = ""; // Alignment? Background? if( StyleConstants.getForeground(attr) != null ) style = style + "color: " + getColor(StyleConstants.getForeground(attr)) + "; "; style = style + "font-size: "+StyleConstants.getFontSize(attr)+"pt; "; style = style + "font-family: "+StyleConstants.getFontFamily(attr); startFontTag(style); } /** * Write the styles used. */ protected void writeStyles() throws IOException { if(doc instanceof DefaultStyledDocument) { Enumeration styles = ((DefaultStyledDocument)doc).getStyleNames(); while(styles.hasMoreElements()) writeStyle(doc.getStyle((String)styles.nextElement())); } else { // What else to do here? Style s = (Style)doc.getStyle("default"); if(s != null) writeStyle( s ); } } /** * Write a set of attributes. */ protected void writeAttributes(AttributeSet attr) throws IOException { Enumeration attribs = attr.getAttributeNames(); while(attribs.hasMoreElements()) { Object attribName = attribs.nextElement(); String name = attribName.toString(); String output = getAttribute(name, attr.getAttribute(attribName)); if( output != null ) { indent(); write( output + NEWLINE ); } } } /** * Deliberately unimplemented, handles component elements. */ protected void writeComponent(Element elem) { } /** * Deliberately unimplemented. * Writes StyleConstants.IconElementName elements. */ protected void writeImage(Element elem) { } // -------------------- Private methods. -------------------------------- /** * Write a single style attribute */ private String getAttribute(String name, Object a) throws IOException { if(name.equals("foreground")) return "foreground:"+getColor((Color)a)+";"; if(name.equals("background")) return "background:"+getColor((Color)a)+";"; if(name.equals("italic")) return "italic:"+(((Boolean)a).booleanValue() ? "italic;" : ";"); if(name.equals("bold")) return "bold:"+(((Boolean)a).booleanValue() ? "bold;" : "normal;"); if(name.equals("family")) return "family:" + a + ";"; if(name.equals("size")) { int size = ((Integer)a).intValue(); int htmlSize; if( size > 24 ) htmlSize = 7; else if( size > 18 ) htmlSize = 6; else if( size > 14 ) htmlSize = 5; else if( size > 12 ) htmlSize = 4; else if( size > 10 ) htmlSize = 3; else if( size > 8 ) htmlSize = 2; else htmlSize = 1; return "size:" + htmlSize + ";"; } return null; } /** * Stupid that Color doesn't have a method for this. */ private String getColor(Color c) { String r = "00" + Integer.toHexString(c.getRed()); r = r.substring(r.length() - 2); String g = "00" + Integer.toHexString(c.getGreen()); g = g.substring(g.length() - 2); String b = "00" + Integer.toHexString(c.getBlue()); b = b.substring(b.length() - 2); return "#" + r + g + b; } /** * Empty the stack of open tags */ private void endOpenTags() throws IOException { while(!tagStack.empty()) write((String)tagStack.pop()); if( inFontTag() ) { write(""+NEWLINE); endFontTag(); } } /** * Output a single style */ private void writeStyle(Style s) throws IOException { if( s == null ) return; writeStartTag("p."+s.getName()+" {"); writeAttributes(s); writeEndTag("}"); } private boolean hasText(Element e) throws BadLocationException { return (getText(e).trim().length() > 0); } }