
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("<") 132 else if (ch == '&') out.print("&") 133 else if (ch == '\'') out.print("'") 134 else if (ch == '"') out.print(""") 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("<") 144 else if (ch == '&') out.print("&") 145 else out.writeChar(ch) 146 } 147 } 148 149 ////////////////////////////////////////////////////////////////////////// 150 // Fields 151 ////////////////////////////////////////////////////////////////////////// 152 153 OutStream out 154 155 } 156 157