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 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) writeLines(lines []Node) { for i, line := range lines { w.writeNodes(line) if i != len(lines)-1 && line.(Line).Children != nil { w.WriteString(" ") } } } func (w *HTMLWriter) writeBlock(b Block) { w.WriteString("") lang := "" if len(b.Parameters) >= 1 { lang = b.Parameters[0] } lines := []string{} for _, n := range b.Children { lines = append(lines, n.(Line).Children[0].(Text).Content) } w.WriteString(w.HighlightCodeBlock(strings.Join(lines, "\n"), lang)) 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 _, name := range fs.Order { w.writeNodes(fs.Definitions[name]) } 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("
  • ") if len(li.Children) == 1 { if p, ok := li.Children[0].(Paragraph); ok { w.writeLines(p.Children) } } else { w.writeNodes(li.Children...) } w.WriteString("
  • \n") } func (w *HTMLWriter) writeLine(l Line) { w.writeNodes(l.Children...) } func (w *HTMLWriter) writeParagraph(p Paragraph) { if len(p.Children) == 1 && p.Children[0].(Line).Children == nil { return } w.WriteString("

    ") w.writeLines(p.Children) w.WriteString("

    \n") } func (w *HTMLWriter) writeHorizontalRule(h HorizontalRule) { w.WriteString("
    \n") } func (w *HTMLWriter) writeTable(t Table) { w.WriteString("") w.writeNodes(t.Header) w.WriteString("") w.writeNodes(t.Rows...) w.WriteString("\n
    \n") } func (w *HTMLWriter) writeTableRow(t TableRow) { w.WriteString("\n\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\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\n") }