Source for javax.swing.text.html.MinimalHTMLWriter

   1: /* MinimalHTMLWriter.java -- 
   2:    Copyright (C) 2006 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package javax.swing.text.html;
  39: 
  40: import javax.swing.text.AttributeSet;
  41: import javax.swing.text.AbstractWriter;
  42: import javax.swing.text.BadLocationException;
  43: import javax.swing.text.DefaultStyledDocument;
  44: import javax.swing.text.Element;
  45: import javax.swing.text.ElementIterator;
  46: import javax.swing.text.StyleConstants;
  47: import javax.swing.text.Style;
  48: import javax.swing.text.StyledDocument;
  49: import java.io.Writer;
  50: import java.io.IOException;
  51: import java.util.Enumeration;
  52: import java.util.Stack;
  53: import java.awt.Color;
  54: 
  55: /**
  56:  * MinimalHTMLWriter,
  57:  * A minimal AbstractWriter implementation for HTML.
  58:  *
  59:  * @author Sven de Marothy
  60:  */
  61: public class MinimalHTMLWriter extends AbstractWriter 
  62: {
  63:   private StyledDocument doc;
  64:   private Stack tagStack;
  65:   private boolean inFontTag = false;
  66: 
  67:   /**
  68:    * Constructs a MinimalHTMLWriter.
  69:    * @param w - a Writer, for output.
  70:    * @param doc - the document
  71:    */
  72:   public MinimalHTMLWriter(Writer w, StyledDocument doc)
  73:   {
  74:     super(w, doc);
  75:     this.doc = doc;
  76:     tagStack = new Stack();
  77:   }
  78: 
  79:   /**
  80:    * Constructs a MinimalHTMLWriter.
  81:    * @param w - a Writer, for output.
  82:    * @param doc - the document
  83:    * @param pos - start position
  84:    * @param len - length
  85:    */
  86:   public MinimalHTMLWriter(Writer w, StyledDocument doc, int pos, int len)
  87:   {
  88:     super(w, doc, pos, len);
  89:     this.doc = doc;
  90:     tagStack = new Stack();
  91:   }
  92: 
  93:   /**
  94:    * Starts a span tag.
  95:    */
  96:   protected void startFontTag(String style) throws IOException
  97:   {
  98:     if( inFontTag() )
  99:       endOpenTags();
 100:     writeStartTag("<span style=\""+style+"\">");
 101:     inFontTag = true;
 102:   }
 103: 
 104:   /**
 105:    * Returns whether the writer is within two span tags.
 106:    */
 107:   protected boolean inFontTag()
 108:   {
 109:     return inFontTag;
 110:   }
 111: 
 112:   /**
 113:    * Ends a span tag.
 114:    */
 115:   protected void endFontTag() throws IOException
 116:   {    
 117:     writeEndTag("</span>");
 118:     inFontTag = false;
 119:   }
 120: 
 121:   /**
 122:    * Write the entire HTML document.
 123:    */
 124:   public synchronized void write() throws IOException, BadLocationException
 125:   {
 126:     writeStartTag("<html>");
 127:     writeHeader();
 128:     writeBody();
 129:     writeEndTag("</html>");
 130:   }
 131: 
 132:   /**
 133:    * Write a start tag and increment the indent.
 134:    */
 135:   protected void writeStartTag(String tag) throws IOException
 136:   {
 137:     indent();
 138:     write(tag+NEWLINE);
 139:     incrIndent();
 140:   }
 141: 
 142:   /**
 143:    * Write an ending tag and decrement the indent.
 144:    */
 145:   protected void writeEndTag(String endTag) throws IOException
 146:   {
 147:     decrIndent();
 148:     indent();
 149:     write(endTag+NEWLINE);
 150:   }
 151: 
 152:   /**
 153:    * Write the HTML header.
 154:    */
 155:   protected void writeHeader() throws IOException 
 156:   {
 157:     writeStartTag("<head>");
 158:     writeStartTag("<style>");
 159:     writeStartTag("<!--");
 160:     writeStyles();
 161:     writeEndTag("-->");
 162:     writeEndTag("</style>");
 163:     writeEndTag("</head>");
 164:   }
 165: 
 166:   /**
 167:    * Write a paragraph start tag.
 168:    */
 169:   protected void writeStartParagraph(Element elem) throws IOException
 170:   {      
 171:     indent();
 172:     write("<p class=default>"+NEWLINE); // FIXME: Class value = ?
 173:     incrIndent();
 174:   }
 175: 
 176:   /**
 177:    * Write a paragraph end tag, closes any other open tags.
 178:    */
 179:   protected void writeEndParagraph() throws IOException
 180:   {
 181:     endOpenTags();
 182:     writeEndTag("</p>");
 183:   }
 184: 
 185:   /**
 186:    * Writes the body of the HTML document.
 187:    */ 
 188:   protected void writeBody() throws IOException, BadLocationException
 189:   {
 190:     writeStartTag("<body>");
 191: 
 192:     ElementIterator ei = getElementIterator();
 193:     Element e = ei.first();
 194:     boolean inParagraph = false;
 195:     do
 196:       {
 197:     if( e.isLeaf() )
 198:       {
 199:         boolean hasNL = (getText(e).indexOf(NEWLINE) != -1);
 200:         if( !inParagraph && hasText( e ) )
 201:           {
 202:         writeStartParagraph(e);
 203:         inParagraph = true;
 204:           }
 205: 
 206:         if( hasText( e ) )
 207:           writeContent(e, true);
 208: 
 209:         if( hasNL && inParagraph )
 210:           {
 211:         writeEndParagraph();
 212:         inParagraph = false;
 213:           }
 214:         else
 215:           endOpenTags();
 216:       }
 217:       } 
 218:     while((e = ei.next()) != null);
 219: 
 220:     writeEndTag("</body>");
 221:   }
 222: 
 223:   protected void text(Element elem) throws IOException, BadLocationException
 224:   {
 225:     write( getText(elem).trim() );
 226:   }
 227: 
 228:   /**
 229:    * Write bold, indent and underline tags.
 230:    */
 231:   protected void writeHTMLTags(AttributeSet attr) throws IOException
 232:   {
 233:     if(attr.getAttribute(StyleConstants.Bold) != null)
 234:       if(((Boolean)attr.getAttribute(StyleConstants.Bold)).booleanValue())
 235:     {
 236:       write("<b>");
 237:       tagStack.push("</b>");
 238:     }
 239:     if(attr.getAttribute(StyleConstants.Italic) != null)
 240:       if(((Boolean)attr.getAttribute(StyleConstants.Italic)).booleanValue())
 241:     {
 242:       write("<i>");
 243:       tagStack.push("</i>");
 244:     }
 245:     if(attr.getAttribute(StyleConstants.Underline) != null)
 246:       if(((Boolean)attr.getAttribute(StyleConstants.Underline)).booleanValue())
 247:     {
 248:       write("<u>");
 249:       tagStack.push("</u>");
 250:     }
 251:   }
 252: 
 253:   /**
 254:    * Returns whether the element contains text or not.
 255:    */
 256:   protected boolean isText(Element elem) 
 257:   {
 258:     return (elem.getEndOffset() != elem.getStartOffset());
 259:   }
 260: 
 261:   /**
 262:    * Writes the content of an element.
 263:    */
 264:   protected void writeContent(Element elem, boolean needsIndenting)
 265:     throws IOException, BadLocationException
 266:   {
 267:     writeNonHTMLAttributes(elem.getAttributes());
 268:     if(needsIndenting)
 269:       indent();
 270:     writeHTMLTags(elem.getAttributes());
 271:     if( isText(elem) )
 272:       text(elem);
 273:     else 
 274:       writeLeaf(elem);
 275: 
 276:     endOpenTags();
 277:   }
 278: 
 279:   /**
 280:    * Writes a non-text leaf element.
 281:    */
 282:   protected void writeLeaf(Element e) throws IOException
 283:   {
 284:     // NOTE: Haven't tested if this is correct.
 285:     if(e.getName().equals(StyleConstants.IconElementName))
 286:       writeImage(e);
 287:     else 
 288:       writeComponent(e);
 289:   }
 290: 
 291:   /**
 292:    * Write the HTML attributes which do not have tag equivalents, 
 293:    * e.g. attributes other than bold/italic/underlined.
 294:    */
 295:   protected void writeNonHTMLAttributes(AttributeSet attr) throws IOException
 296:   {
 297:     String style = "";
 298: 
 299:     // Alignment? Background?
 300: 
 301:     if( StyleConstants.getForeground(attr) != null )
 302:       style = style + "color: " + 
 303:     getColor(StyleConstants.getForeground(attr)) + "; ";
 304: 
 305:     style = style + "font-size: "+StyleConstants.getFontSize(attr)+"pt; ";
 306:     style = style + "font-family: "+StyleConstants.getFontFamily(attr);
 307: 
 308:     startFontTag(style);
 309:   }
 310: 
 311:   /**
 312:    * Write the styles used.
 313:    */
 314:   protected void writeStyles() throws IOException
 315:   {
 316:     if(doc instanceof DefaultStyledDocument)
 317:       {
 318:     Enumeration styles = ((DefaultStyledDocument)doc).getStyleNames();
 319:     while(styles.hasMoreElements())
 320:       writeStyle(doc.getStyle((String)styles.nextElement()));
 321:       }
 322:     else
 323:       { // What else to do here?
 324:     Style s = (Style)doc.getStyle("default");
 325:     if(s != null)
 326:       writeStyle( s );
 327:       }
 328:   }
 329: 
 330:   /**
 331:    * Write a set of attributes.
 332:    */
 333:   protected void writeAttributes(AttributeSet attr) throws IOException
 334:   {
 335:     Enumeration attribs = attr.getAttributeNames();
 336:     while(attribs.hasMoreElements())
 337:       {
 338:     Object attribName = attribs.nextElement();
 339:     String name = attribName.toString();
 340:     String output = getAttribute(name, attr.getAttribute(attribName));
 341:     if( output != null )
 342:       {
 343:         indent();
 344:         write( output + NEWLINE );
 345:       }
 346:       }
 347:   }
 348: 
 349:   /**
 350:    * Deliberately unimplemented, handles component elements.
 351:    */ 
 352:   protected void writeComponent(Element elem) throws IOException
 353:   {
 354:   }
 355: 
 356:   /**
 357:    * Deliberately unimplemented. 
 358:    * Writes StyleConstants.IconElementName elements.
 359:    */ 
 360:   protected void writeImage(Element elem) throws IOException
 361:   {
 362:   }
 363: 
 364:   // -------------------- Private methods. --------------------------------
 365: 
 366:   /**
 367:    * Write a single style attribute
 368:    */
 369:   private String getAttribute(String name, Object a) throws IOException
 370:   {
 371:     if(name.equals("foreground"))
 372:       return "foreground:"+getColor((Color)a)+";";
 373:     if(name.equals("background"))
 374:       return "background:"+getColor((Color)a)+";";
 375:     if(name.equals("italic"))
 376:       return "italic:"+(((Boolean)a).booleanValue() ? "italic;" : ";");
 377:     if(name.equals("bold"))
 378:       return "bold:"+(((Boolean)a).booleanValue() ? "bold;" : "normal;");
 379:     if(name.equals("family"))
 380:       return "family:" + a + ";";
 381:     if(name.equals("size"))
 382:       {
 383:     int size = ((Integer)a).intValue();
 384:     int htmlSize;
 385:     if( size > 24 )
 386:       htmlSize = 7;
 387:     else if( size > 18 )
 388:       htmlSize = 6;
 389:     else if( size > 14 )
 390:       htmlSize = 5;
 391:     else if( size > 12 )
 392:       htmlSize = 4;
 393:     else if( size > 10 )
 394:       htmlSize = 3;
 395:     else if( size > 8 )
 396:       htmlSize = 2;
 397:     else
 398:       htmlSize = 1;
 399: 
 400:     return "size:" + htmlSize + ";";
 401:       }
 402: 
 403:     return null;
 404:   }
 405: 
 406:   /**
 407:    * Stupid that Color doesn't have a method for this.
 408:    */
 409:   private String getColor(Color c)
 410:   {
 411:     String r = "00" + Integer.toHexString(c.getRed());
 412:     r = r.substring(r.length() - 2);
 413:     String g = "00" + Integer.toHexString(c.getGreen());
 414:     g = g.substring(g.length() - 2);
 415:     String b = "00" + Integer.toHexString(c.getBlue());
 416:     b = b.substring(b.length() - 2);
 417:     return "#" + r + g + b;
 418:   }
 419: 
 420:   /**
 421:    * Empty the stack of open tags
 422:    */
 423:   private void endOpenTags() throws IOException
 424:   {
 425:     while(!tagStack.empty())
 426:       write((String)tagStack.pop());
 427: 
 428:     if( inFontTag() )
 429:       {
 430:     write(""+NEWLINE);
 431:     endFontTag();
 432:       }
 433:   }
 434: 
 435:   /**
 436:    * Output a single style
 437:    */
 438:   private void writeStyle(Style s) throws IOException
 439:   {
 440:     if( s == null )
 441:       return;
 442: 
 443:     writeStartTag("p."+s.getName()+" {");
 444:     writeAttributes(s);
 445:     writeEndTag("}");
 446:   }
 447: 
 448:   private boolean hasText(Element e) throws BadLocationException
 449:   {
 450:     return (getText(e).trim().length() > 0);
 451:   }
 452: }