package org import ( "fmt" "html" "path" "strings" ) type HTMLWriter struct { stringBuilder HighlightCodeBlock func(source, lang string) string } var emphasisTags = map[string][]string{ "/": []string{"", ""}, "*": []string{"", ""}, "+": []string{"", ""}, "~": []string{"", ""}, "=": []string{``, ""}, "_": []string{``, ""}, "_{}": []string{"", ""}, "^{}": []string{"", ""}, } var listTags = map[string][]string{ "+": []string{""}, "-": []string{""}, "*": []string{""}, "number": []string{"
    ", "
"}, "letter": []string{"
    ", "
"}, } func NewHTMLWriter() *HTMLWriter { return &HTMLWriter{ HighlightCodeBlock: func(source, lang string) string { return fmt.Sprintf(`
%s
`, html.EscapeString(source)) }, } } func (w *HTMLWriter) emptyClone() *HTMLWriter { wcopy := *w wcopy.stringBuilder = stringBuilder{} return &wcopy } func (w *HTMLWriter) before(d *Document) {} func (w *HTMLWriter) after(d *Document) { w.writeFootnotes(d) } func (w *HTMLWriter) writeNodes(ns ...Node) { for _, n := range ns { switch n := n.(type) { case Keyword, Comment: continue case Headline: w.writeHeadline(n) case Block: w.writeBlock(n) case FootnoteDefinition: w.writeFootnoteDefinition(n) case List: w.writeList(n) case ListItem: w.writeListItem(n) case Table: w.writeTable(n) case TableHeader: w.writeTableHeader(n) case TableRow: w.writeTableRow(n) case TableSeparator: w.writeTableSeparator(n) case Paragraph: w.writeParagraph(n) case HorizontalRule: w.writeHorizontalRule(n) case Line: w.writeLine(n) case Text: w.writeText(n) case Emphasis: w.writeEmphasis(n) case Linebreak: w.writeLinebreak(n) case RegularLink: w.writeRegularLink(n) case FootnoteLink: w.writeFootnoteLink(n) default: if n != nil { panic(fmt.Sprintf("bad node %#v", n)) } } } } func (w *HTMLWriter) writeBlock(b Block) { switch b.Name { case "SRC": lines, lang := []string{}, "text" if len(b.Parameters) >= 1 { lang = b.Parameters[0] } for _, n := range b.Children { lines = append(lines, n.(Line).Children[0].(Text).Content) } w.WriteString(w.HighlightCodeBlock(strings.Join(lines, "\n"), lang) + "\n") case "EXAMPLE": w.WriteString(`
\n`)
		w.writeNodes(b.Children...)
		w.WriteString("
\n") case "QUOTE": w.WriteString("
\n") w.writeNodes(b.Children...) w.WriteString("
\n") case "CENTER": w.WriteString(`

` + "\n") w.writeNodes(b.Children...) w.WriteString("

\n") default: w.WriteString(fmt.Sprintf(`

`, strings.ToLower(b.Name)) + "\n") w.writeNodes(b.Children...) w.WriteString("

\n") } } func (w *HTMLWriter) writeFootnoteDefinition(f FootnoteDefinition) { w.WriteString(`
` + "\n") w.WriteString(fmt.Sprintf(`%s`, f.Name, f.Name) + "\n") w.writeNodes(f.Children...) w.WriteString("
\n") } func (w *HTMLWriter) writeFootnotes(d *Document) { fs := d.Footnotes if len(fs.Definitions) == 0 { return } w.WriteString(`
` + "\n") w.WriteString(`

` + fs.Title + `

` + "\n") w.WriteString(`
` + "\n") for _, definition := range d.Footnotes.Ordered() { w.writeNodes(definition) } w.WriteString("
\n
\n") } func (w *HTMLWriter) writeHeadline(h Headline) { w.WriteString(fmt.Sprintf("", h.Lvl)) w.writeNodes(h.Title...) w.WriteString(fmt.Sprintf("\n", h.Lvl)) w.writeNodes(h.Children...) } func (w *HTMLWriter) writeText(t Text) { w.WriteString(html.EscapeString(t.Content)) } func (w *HTMLWriter) writeEmphasis(e Emphasis) { tags, ok := emphasisTags[e.Kind] if !ok { panic(fmt.Sprintf("bad emphasis %#v", e)) } w.WriteString(tags[0]) w.writeNodes(e.Content...) w.WriteString(tags[1]) } func (w *HTMLWriter) writeLinebreak(l Linebreak) { w.WriteString("
\n") } func (w *HTMLWriter) writeFootnoteLink(l FootnoteLink) { name := html.EscapeString(l.Name) w.WriteString(fmt.Sprintf(`%s`, name, name)) } func (w *HTMLWriter) writeRegularLink(l RegularLink) { url := html.EscapeString(l.URL) descriptionWriter := w.emptyClone() descriptionWriter.writeNodes(l.Description...) description := descriptionWriter.String() switch l.Protocol { case "file": // TODO url = url[len("file:"):] if strings.Contains(".png.jpg.jpeg.gif", path.Ext(l.URL)) { w.WriteString(fmt.Sprintf(`%s`, url, description, description)) } else { w.WriteString(fmt.Sprintf(`%s`, url, description)) } default: w.WriteString(fmt.Sprintf(`%s`, url, description)) } } func (w *HTMLWriter) writeList(l List) { tags, ok := listTags[l.Kind] if !ok { panic(fmt.Sprintf("bad list kind %#v", l)) } w.WriteString(tags[0] + "\n") w.writeNodes(l.Items...) w.WriteString(tags[1] + "\n") } func (w *HTMLWriter) writeListItem(li ListItem) { w.WriteString("
  • \n") w.writeNodes(li.Children...) w.WriteString("
  • \n") } func (w *HTMLWriter) writeLine(l Line) { if len(l.Children) != 0 { w.writeNodes(l.Children...) w.WriteString("\n") } } func (w *HTMLWriter) writeParagraph(p Paragraph) { if len(p.Children) == 1 && p.Children[0].(Line).Children == nil { return } w.WriteString("

    \n") w.writeNodes(p.Children...) w.WriteString("

    \n") } func (w *HTMLWriter) writeHorizontalRule(h HorizontalRule) { w.WriteString("
    \n") } func (w *HTMLWriter) writeTable(t Table) { w.WriteString("\n") w.writeNodes(t.Header) w.WriteString("\n") w.writeNodes(t.Rows...) w.WriteString("\n
    \n") } func (w *HTMLWriter) writeTableRow(t TableRow) { w.WriteString("\n") for _, column := range t.Columns { w.WriteString("") w.writeNodes(column...) w.WriteString("") } w.WriteString("\n\n") } func (w *HTMLWriter) writeTableHeader(t TableHeader) { w.WriteString("\n") for _, column := range t.Columns { w.WriteString("") w.writeNodes(column...) w.WriteString("") } w.WriteString("\n\n") } func (w *HTMLWriter) writeTableSeparator(t TableSeparator) { w.WriteString("\n") }