| /* 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) throws IOException |
| { |
| } |
| |
| /** |
| * Deliberately unimplemented. |
| * Writes StyleConstants.IconElementName elements. |
| */ |
| protected void writeImage(Element elem) throws IOException |
| { |
| } |
| |
| // -------------------- 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); |
| } |
| } |