logo

class

fandoc::HtmlDocWriter

sys::Obj
  fandoc::HtmlDocWriter : fandoc::DocWriter
   1  //
   2  // Copyright (c) 2007, Brian Frank and Andy Frank
   3  // Licensed under the Academic Free License version 3.0
   4  //
   5  // History:
   6  //   17 Feb 07  Brian Frank  Creation
   7  //
   8  
   9  **
  10  ** HtmlDocWriter outputs a fandoc model to XHTML
  11  **
  12  class HtmlDocWriter : DocWriter
  13  {
  14  
  15  //////////////////////////////////////////////////////////////////////////
  16  // Constructor
  17  //////////////////////////////////////////////////////////////////////////
  18  
  19    new make(OutStream out := Sys.out)
  20    {
  21      this.out = out
  22    }
  23  
  24  //////////////////////////////////////////////////////////////////////////
  25  // DocWriter
  26  //////////////////////////////////////////////////////////////////////////
  27  
  28    override Void docStart(Doc doc)
  29    {
  30      out.print("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n")
  31      out.print(" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n")
  32      out.print("<html>\n")
  33      out.print("<head>\n")
  34      docHead(doc)
  35      out.print("</head>\n")
  36    }
  37  
  38    virtual Void docHead(Doc doc)
  39    {
  40      out.print("  <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>\n")
  41      doc.meta.each |Str val, Str key|
  42      {
  43        switch (key)
  44        {
  45          case "title":
  46            out.print("  <title>")
  47            safeText(val)
  48            out.print("</title>\n")
  49          default:
  50            out.print("  <meta")
  51            attr("name", key)
  52            attr("content", val)
  53            out.print("/>\n")
  54        }
  55      }
  56    }
  57  
  58    override Void docEnd(Doc doc)
  59    {
  60      out.print("</html>")
  61    }
  62  
  63    override Void elemStart(DocElem elem)
  64    {
  65      if (elem.isBlock) out.writeChar('\n')
  66  
  67      // if hyperlink to code, then wrap in code element
  68      if (elem.id == DocNodeId.link && ((Link)elem).isCode)
  69        out.print("<code>")
  70  
  71      out.writeChar('<').print(elem.htmlName)
  72      if (elem.anchorId != null) out.print(" id='$elem.anchorId'")
  73      switch (elem.id)
  74      {
  75        case DocNodeId.link:
  76          link := elem as Link
  77          out.print(" href='$link.uri'")
  78        case DocNodeId.image:
  79          img := elem as Image
  80          out.print(" src='$img.uri' alt='")
  81          safeAttr(img.alt)
  82          out.print("'/>")
  83          return
  84        case DocNodeId.para:
  85          para := elem as Para
  86          if (para.admonition != null)
  87          {
  88            out.print(" class='$para.admonition'")
  89            out.print(">").print(para.admonition).print(": ")
  90            return
  91          }
  92        case DocNodeId.orderedList:
  93          ol := elem as OrderedList
  94          out.print(" type='$ol.style.htmlType'")
  95      }
  96      out.writeChar('>')
  97    }
  98  
  99    override Void elemEnd(DocElem elem)
 100    {
 101      if (elem.id == DocNodeId.image) return
 102      out.writeChar('<').writeChar('/').print(elem.htmlName).writeChar('>')
 103  
 104      // if hyperlink to code, then wrap in code element
 105      if (elem.id == DocNodeId.link && ((Link)elem).isCode)
 106        out.print("</code>")
 107  
 108      if (elem.isBlock) out.writeChar('\n')
 109    }
 110  
 111    override Void text(DocText text)
 112    {
 113      safeText(text.str)
 114    }
 115  
 116  //////////////////////////////////////////////////////////////////////////
 117  // Escapes
 118  //////////////////////////////////////////////////////////////////////////
 119  
 120    private Void attr(Str name, Str val)
 121    {
 122      out.writeChar(' ').print(name).print("='")
 123      safeAttr(val)
 124      out.writeChar('\'')
 125    }
 126  
 127    private Void safeAttr(Str s)
 128    {
 129      s.each |Int ch|
 130      {
 131        if (ch == '<') out.print("&lt;")
 132        else if (ch == '&') out.print("&amp;")
 133        else if (ch == '\'') out.print("&#39;")
 134        else if (ch == '"') out.print("&#34;")
 135        else out.writeChar(ch)
 136      }
 137    }
 138  
 139    private Void safeText(Str s)
 140    {
 141      s.each |Int ch|
 142      {
 143        if (ch == '<') out.print("&lt;")
 144        else if (ch == '&') out.print("&amp;")
 145        else out.writeChar(ch)
 146      }
 147    }
 148  
 149  //////////////////////////////////////////////////////////////////////////
 150  // Fields
 151  //////////////////////////////////////////////////////////////////////////
 152  
 153    OutStream out
 154  
 155  }
 156  
 157