all repos — grayfriday @ ee3fe992034b5e391daf418765e97b37fe2b06be

blackfriday fork with a few changes

rudimentary latex backend, additional cleanup
Russ Ross russ@russross.com
Mon, 30 May 2011 11:06:20 -0600
commit

ee3fe992034b5e391daf418765e97b37fe2b06be

parent

81cefb5e7cdbc5ad5676c979ae33797a4c191583

8 files changed, 493 insertions(+), 166 deletions(-)

jump to
M MakefileMakefile

@@ -2,7 +2,9 @@ include $(GOROOT)/src/Make.inc

TARG=github.com/russross/blackfriday -GOFILES=markdown.go block.go inline.go html.go smartypants.go +GOFILES=markdown.go block.go inline.go html.go smartypants.go latex.go + +all: package markdown include $(GOROOT)/src/Make.pkg
M README.mdREADME.md

@@ -79,12 +79,26 @@ is translated into suitable HTML (instead of just a few special

cases). For example, `4/5` becomes `<sup>4</sup>&frasl;<sub>5</sub>` +LaTeX Output +------------ + +A rudimentary LaTeX rendering backend is also included. To see an +example of its usage, comment out this link in `main.go`: + + renderer := blackfriday.HtmlRenderer(html_flags) + +and uncomment this line: + + renderer := blackfriday.LatexRenderer(0) + +It renders some basic documents, but is only experimental at this point. + + Todo ---- * Code cleanup * Better code documentation -* Implement a LaTeX backend [1]: http://daringfireball.net/projects/markdown/ "Markdown"
M block.goblock.go

@@ -514,7 +514,7 @@ i++

} if rndr.mk.table != nil { - rndr.mk.table(out, header_work.Bytes(), body_work.Bytes(), rndr.mk.opaque) + rndr.mk.table(out, header_work.Bytes(), body_work.Bytes(), col_data, rndr.mk.opaque) } }
M example/Makefileexample/Makefile

@@ -4,8 +4,9 @@ TARG=markdown

GOFILES=main.go -LIBBF=github.com/russross/blackfriday +markdown: ../_obj/github.com/russross/blackfriday.a -PREREQ += ../_obj/$(LIBBF).a +GC += -I../_obj/ +LD += -L../_obj/ include $(GOROOT)/src/Make.cmd
M example/main.goexample/main.go

@@ -57,8 +57,13 @@ //html_flags |= blackfriday.HTML_USE_SMARTYPANTS

html_flags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS html_flags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES - // render the data - output := blackfriday.Markdown(input, blackfriday.HtmlRenderer(html_flags), extensions) + // render the data into HTML (comment this out to deselect HTML) + renderer := blackfriday.HtmlRenderer(html_flags) + + // render the data into LaTeX (uncomment to select LaTeX) + //renderer := blackfriday.LatexRenderer(0) + + output := blackfriday.Markdown(input, renderer, extensions) // output the result if len(os.Args) == 3 {
M html.gohtml.go

@@ -65,8 +65,8 @@ r.list = htmlList

r.listitem = htmlListitem r.paragraph = htmlParagraph r.table = htmlTable - r.tableRow = htmlTablerow - r.tableCell = htmlTablecell + r.tableRow = htmlTableRow + r.tableCell = htmlTableCell r.autolink = htmlAutolink r.codespan = htmlCodespan

@@ -120,7 +120,7 @@ r.opaque = &htmlOptions{flags: flags | HTML_TOC, close_tag: close_tag}

return r } -func attrEscape(ob *bytes.Buffer, src []byte) { +func attrEscape(out *bytes.Buffer, src []byte) { for i := 0; i < len(src); i++ { // directly copy normal characters org := i

@@ -128,7 +128,7 @@ for i < len(src) && src[i] != '<' && src[i] != '>' && src[i] != '&' && src[i] != '"' {

i++ } if i > org { - ob.Write(src[org:i]) + out.Write(src[org:i]) } // escape a character

@@ -137,36 +137,36 @@ break

} switch src[i] { case '<': - ob.WriteString("&lt;") + out.WriteString("&lt;") case '>': - ob.WriteString("&gt;") + out.WriteString("&gt;") case '&': - ob.WriteString("&amp;") + out.WriteString("&amp;") case '"': - ob.WriteString("&quot;") + out.WriteString("&quot;") } } } -func htmlHeader(ob *bytes.Buffer, text []byte, level int, opaque interface{}) { +func htmlHeader(out *bytes.Buffer, text []byte, level int, opaque interface{}) { options := opaque.(*htmlOptions) - if ob.Len() > 0 { - ob.WriteByte('\n') + if out.Len() > 0 { + out.WriteByte('\n') } if options.flags&HTML_TOC != 0 { - ob.WriteString(fmt.Sprintf("<h%d id=\"toc_%d\">", level, options.toc_data.header_count)) + out.WriteString(fmt.Sprintf("<h%d id=\"toc_%d\">", level, options.toc_data.header_count)) options.toc_data.header_count++ } else { - ob.WriteString(fmt.Sprintf("<h%d>", level)) + out.WriteString(fmt.Sprintf("<h%d>", level)) } - ob.Write(text) - ob.WriteString(fmt.Sprintf("</h%d>\n", level)) + out.Write(text) + out.WriteString(fmt.Sprintf("</h%d>\n", level)) } -func htmlRawBlock(ob *bytes.Buffer, text []byte, opaque interface{}) { +func htmlRawBlock(out *bytes.Buffer, text []byte, opaque interface{}) { sz := len(text) for sz > 0 && text[sz-1] == '\n' { sz--

@@ -178,30 +178,30 @@ }

if org >= sz { return } - if ob.Len() > 0 { - ob.WriteByte('\n') + if out.Len() > 0 { + out.WriteByte('\n') } - ob.Write(text[org:sz]) - ob.WriteByte('\n') + out.Write(text[org:sz]) + out.WriteByte('\n') } -func htmlHrule(ob *bytes.Buffer, opaque interface{}) { +func htmlHrule(out *bytes.Buffer, opaque interface{}) { options := opaque.(*htmlOptions) - if ob.Len() > 0 { - ob.WriteByte('\n') + if out.Len() > 0 { + out.WriteByte('\n') } - ob.WriteString("<hr") - ob.WriteString(options.close_tag) + out.WriteString("<hr") + out.WriteString(options.close_tag) } -func htmlBlockcode(ob *bytes.Buffer, text []byte, lang string, opaque interface{}) { - if ob.Len() > 0 { - ob.WriteByte('\n') +func htmlBlockcode(out *bytes.Buffer, text []byte, lang string, opaque interface{}) { + if out.Len() > 0 { + out.WriteByte('\n') } if lang != "" { - ob.WriteString("<pre><code class=\"") + out.WriteString("<pre><code class=\"") for i, cls := 0, 0; i < len(lang); i, cls = i+1, cls+1 { for i < len(lang) && isspace(lang[i]) {

@@ -219,22 +219,22 @@ org++

} if cls > 0 { - ob.WriteByte(' ') + out.WriteByte(' ') } - attrEscape(ob, []byte(lang[org:])) + attrEscape(out, []byte(lang[org:])) } } - ob.WriteString("\">") + out.WriteString("\">") } else { - ob.WriteString("<pre><code>") + out.WriteString("<pre><code>") } if len(text) > 0 { - attrEscape(ob, text) + attrEscape(out, text) } - ob.WriteString("</code></pre>\n") + out.WriteString("</code></pre>\n") } /*

@@ -255,13 +255,13 @@ * Note that we only generate HTML for the first specifier.

* E.g. * ~~~~ {.python .numbered} => <pre lang="python"><code> */ -func htmlBlockcodeGithub(ob *bytes.Buffer, text []byte, lang string, opaque interface{}) { - if ob.Len() > 0 { - ob.WriteByte('\n') +func htmlBlockcodeGithub(out *bytes.Buffer, text []byte, lang string, opaque interface{}) { + if out.Len() > 0 { + out.WriteByte('\n') } if len(lang) > 0 { - ob.WriteString("<pre lang=\"") + out.WriteString("<pre lang=\"") i := 0 for i < len(lang) && !isspace(lang[i]) {

@@ -269,102 +269,102 @@ i++

} if lang[0] == '.' { - attrEscape(ob, []byte(lang[1:i])) + attrEscape(out, []byte(lang[1:i])) } else { - attrEscape(ob, []byte(lang[:i])) + attrEscape(out, []byte(lang[:i])) } - ob.WriteString("\"><code>") + out.WriteString("\"><code>") } else { - ob.WriteString("<pre><code>") + out.WriteString("<pre><code>") } if len(text) > 0 { - attrEscape(ob, text) + attrEscape(out, text) } - ob.WriteString("</code></pre>\n") + out.WriteString("</code></pre>\n") } -func htmlBlockquote(ob *bytes.Buffer, text []byte, opaque interface{}) { - ob.WriteString("<blockquote>\n") - ob.Write(text) - ob.WriteString("</blockquote>") +func htmlBlockquote(out *bytes.Buffer, text []byte, opaque interface{}) { + out.WriteString("<blockquote>\n") + out.Write(text) + out.WriteString("</blockquote>") } -func htmlTable(ob *bytes.Buffer, header []byte, body []byte, opaque interface{}) { - if ob.Len() > 0 { - ob.WriteByte('\n') +func htmlTable(out *bytes.Buffer, header []byte, body []byte, columnData []int, opaque interface{}) { + if out.Len() > 0 { + out.WriteByte('\n') } - ob.WriteString("<table><thead>\n") - ob.Write(header) - ob.WriteString("\n</thead><tbody>\n") - ob.Write(body) - ob.WriteString("\n</tbody></table>") + out.WriteString("<table><thead>\n") + out.Write(header) + out.WriteString("\n</thead><tbody>\n") + out.Write(body) + out.WriteString("\n</tbody></table>") } -func htmlTablerow(ob *bytes.Buffer, text []byte, opaque interface{}) { - if ob.Len() > 0 { - ob.WriteByte('\n') +func htmlTableRow(out *bytes.Buffer, text []byte, opaque interface{}) { + if out.Len() > 0 { + out.WriteByte('\n') } - ob.WriteString("<tr>\n") - ob.Write(text) - ob.WriteString("\n</tr>") + out.WriteString("<tr>\n") + out.Write(text) + out.WriteString("\n</tr>") } -func htmlTablecell(ob *bytes.Buffer, text []byte, align int, opaque interface{}) { - if ob.Len() > 0 { - ob.WriteByte('\n') +func htmlTableCell(out *bytes.Buffer, text []byte, align int, opaque interface{}) { + if out.Len() > 0 { + out.WriteByte('\n') } switch align { case TABLE_ALIGNMENT_LEFT: - ob.WriteString("<td align=\"left\">") + out.WriteString("<td align=\"left\">") case TABLE_ALIGNMENT_RIGHT: - ob.WriteString("<td align=\"right\">") + out.WriteString("<td align=\"right\">") case TABLE_ALIGNMENT_CENTER: - ob.WriteString("<td align=\"center\">") + out.WriteString("<td align=\"center\">") default: - ob.WriteString("<td>") + out.WriteString("<td>") } - ob.Write(text) - ob.WriteString("</td>") + out.Write(text) + out.WriteString("</td>") } -func htmlList(ob *bytes.Buffer, text []byte, flags int, opaque interface{}) { - if ob.Len() > 0 { - ob.WriteByte('\n') +func htmlList(out *bytes.Buffer, text []byte, flags int, opaque interface{}) { + if out.Len() > 0 { + out.WriteByte('\n') } if flags&LIST_TYPE_ORDERED != 0 { - ob.WriteString("<ol>\n") + out.WriteString("<ol>\n") } else { - ob.WriteString("<ul>\n") + out.WriteString("<ul>\n") } - ob.Write(text) + out.Write(text) if flags&LIST_TYPE_ORDERED != 0 { - ob.WriteString("</ol>\n") + out.WriteString("</ol>\n") } else { - ob.WriteString("</ul>\n") + out.WriteString("</ul>\n") } } -func htmlListitem(ob *bytes.Buffer, text []byte, flags int, opaque interface{}) { - ob.WriteString("<li>") +func htmlListitem(out *bytes.Buffer, text []byte, flags int, opaque interface{}) { + out.WriteString("<li>") size := len(text) for size > 0 && text[size-1] == '\n' { size-- } - ob.Write(text[:size]) - ob.WriteString("</li>\n") + out.Write(text[:size]) + out.WriteString("</li>\n") } -func htmlParagraph(ob *bytes.Buffer, text []byte, opaque interface{}) { +func htmlParagraph(out *bytes.Buffer, text []byte, opaque interface{}) { options := opaque.(*htmlOptions) i := 0 - if ob.Len() > 0 { - ob.WriteByte('\n') + if out.Len() > 0 { + out.WriteByte('\n') } if len(text) == 0 {

@@ -379,7 +379,7 @@ if i == len(text) {

return } - ob.WriteString("<p>") + out.WriteString("<p>") if options.flags&HTML_HARD_WRAP != 0 { for i < len(text) { org := i

@@ -388,24 +388,24 @@ i++

} if i > org { - ob.Write(text[org:i]) + out.Write(text[org:i]) } if i >= len(text) { break } - ob.WriteString("<br>") - ob.WriteString(options.close_tag) + out.WriteString("<br>") + out.WriteString(options.close_tag) i++ } } else { - ob.Write(text[i:]) + out.Write(text[i:]) } - ob.WriteString("</p>\n") + out.WriteString("</p>\n") } -func htmlAutolink(ob *bytes.Buffer, link []byte, kind int, opaque interface{}) int { +func htmlAutolink(out *bytes.Buffer, link []byte, kind int, opaque interface{}) int { options := opaque.(*htmlOptions) if len(link) == 0 {

@@ -415,12 +415,12 @@ if options.flags&HTML_SAFELINK != 0 && !isSafeLink(link) && kind != LINK_TYPE_EMAIL {

return 0 } - ob.WriteString("<a href=\"") + out.WriteString("<a href=\"") if kind == LINK_TYPE_EMAIL { - ob.WriteString("mailto:") + out.WriteString("mailto:") } - ob.Write(link) - ob.WriteString("\">") + out.Write(link) + out.WriteString("\">") /* * Pretty print: if we get an email address as

@@ -428,95 +428,95 @@ * an actual URI, e.g. `mailto:foo@bar.com`, we don't

* want to print the `mailto:` prefix */ if bytes.HasPrefix(link, []byte("mailto:")) { - attrEscape(ob, link[7:]) + attrEscape(out, link[7:]) } else { - attrEscape(ob, link) + attrEscape(out, link) } - ob.WriteString("</a>") + out.WriteString("</a>") return 1 } -func htmlCodespan(ob *bytes.Buffer, text []byte, opaque interface{}) int { - ob.WriteString("<code>") - attrEscape(ob, text) - ob.WriteString("</code>") +func htmlCodespan(out *bytes.Buffer, text []byte, opaque interface{}) int { + out.WriteString("<code>") + attrEscape(out, text) + out.WriteString("</code>") return 1 } -func htmlDoubleEmphasis(ob *bytes.Buffer, text []byte, opaque interface{}) int { +func htmlDoubleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) int { if len(text) == 0 { return 0 } - ob.WriteString("<strong>") - ob.Write(text) - ob.WriteString("</strong>") + out.WriteString("<strong>") + out.Write(text) + out.WriteString("</strong>") return 1 } -func htmlEmphasis(ob *bytes.Buffer, text []byte, opaque interface{}) int { +func htmlEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) int { if len(text) == 0 { return 0 } - ob.WriteString("<em>") - ob.Write(text) - ob.WriteString("</em>") + out.WriteString("<em>") + out.Write(text) + out.WriteString("</em>") return 1 } -func htmlImage(ob *bytes.Buffer, link []byte, title []byte, alt []byte, opaque interface{}) int { +func htmlImage(out *bytes.Buffer, link []byte, title []byte, alt []byte, opaque interface{}) int { options := opaque.(*htmlOptions) if len(link) == 0 { return 0 } - ob.WriteString("<img src=\"") - attrEscape(ob, link) - ob.WriteString("\" alt=\"") + out.WriteString("<img src=\"") + attrEscape(out, link) + out.WriteString("\" alt=\"") if len(alt) > 0 { - attrEscape(ob, alt) + attrEscape(out, alt) } if len(title) > 0 { - ob.WriteString("\" title=\"") - attrEscape(ob, title) + out.WriteString("\" title=\"") + attrEscape(out, title) } - ob.WriteByte('"') - ob.WriteString(options.close_tag) + out.WriteByte('"') + out.WriteString(options.close_tag) return 1 } -func htmlLinebreak(ob *bytes.Buffer, opaque interface{}) int { +func htmlLinebreak(out *bytes.Buffer, opaque interface{}) int { options := opaque.(*htmlOptions) - ob.WriteString("<br") - ob.WriteString(options.close_tag) + out.WriteString("<br") + out.WriteString(options.close_tag) return 1 } -func htmlLink(ob *bytes.Buffer, link []byte, title []byte, content []byte, opaque interface{}) int { +func htmlLink(out *bytes.Buffer, link []byte, title []byte, content []byte, opaque interface{}) int { options := opaque.(*htmlOptions) if options.flags&HTML_SAFELINK != 0 && !isSafeLink(link) { return 0 } - ob.WriteString("<a href=\"") + out.WriteString("<a href=\"") if len(link) > 0 { - ob.Write(link) + out.Write(link) } if len(title) > 0 { - ob.WriteString("\" title=\"") - attrEscape(ob, title) + out.WriteString("\" title=\"") + attrEscape(out, title) } - ob.WriteString("\">") + out.WriteString("\">") if len(content) > 0 { - ob.Write(content) + out.Write(content) } - ob.WriteString("</a>") + out.WriteString("</a>") return 1 } -func htmlRawTag(ob *bytes.Buffer, text []byte, opaque interface{}) int { +func htmlRawTag(out *bytes.Buffer, text []byte, opaque interface{}) int { options := opaque.(*htmlOptions) if options.flags&HTML_SKIP_HTML != 0 { return 1

@@ -530,72 +530,72 @@ }

if options.flags&HTML_SKIP_IMAGES != 0 && isHtmlTag(text, "img") { return 1 } - ob.Write(text) + out.Write(text) return 1 } -func htmlTripleEmphasis(ob *bytes.Buffer, text []byte, opaque interface{}) int { +func htmlTripleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) int { if len(text) == 0 { return 0 } - ob.WriteString("<strong><em>") - ob.Write(text) - ob.WriteString("</em></strong>") + out.WriteString("<strong><em>") + out.Write(text) + out.WriteString("</em></strong>") return 1 } -func htmlStrikethrough(ob *bytes.Buffer, text []byte, opaque interface{}) int { +func htmlStrikethrough(out *bytes.Buffer, text []byte, opaque interface{}) int { if len(text) == 0 { return 0 } - ob.WriteString("<del>") - ob.Write(text) - ob.WriteString("</del>") + out.WriteString("<del>") + out.Write(text) + out.WriteString("</del>") return 1 } -func htmlNormalText(ob *bytes.Buffer, text []byte, opaque interface{}) { - attrEscape(ob, text) +func htmlNormalText(out *bytes.Buffer, text []byte, opaque interface{}) { + attrEscape(out, text) } -func htmlTocHeader(ob *bytes.Buffer, text []byte, level int, opaque interface{}) { +func htmlTocHeader(out *bytes.Buffer, text []byte, level int, opaque interface{}) { options := opaque.(*htmlOptions) for level > options.toc_data.current_level { if options.toc_data.current_level > 0 { - ob.WriteString("<li>") + out.WriteString("<li>") } - ob.WriteString("<ul>\n") + out.WriteString("<ul>\n") options.toc_data.current_level++ } for level < options.toc_data.current_level { - ob.WriteString("</ul>") + out.WriteString("</ul>") if options.toc_data.current_level > 1 { - ob.WriteString("</li>\n") + out.WriteString("</li>\n") } options.toc_data.current_level-- } - ob.WriteString("<li><a href=\"#toc_") - ob.WriteString(strconv.Itoa(options.toc_data.header_count)) - ob.WriteString("\">") + out.WriteString("<li><a href=\"#toc_") + out.WriteString(strconv.Itoa(options.toc_data.header_count)) + out.WriteString("\">") options.toc_data.header_count++ if len(text) > 0 { - ob.Write(text) + out.Write(text) } - ob.WriteString("</a></li>\n") + out.WriteString("</a></li>\n") } -func htmlTocFinalize(ob *bytes.Buffer, opaque interface{}) { +func htmlTocFinalize(out *bytes.Buffer, opaque interface{}) { options := opaque.(*htmlOptions) for options.toc_data.current_level > 1 { - ob.WriteString("</ul></li>\n") + out.WriteString("</ul></li>\n") options.toc_data.current_level-- } if options.toc_data.current_level > 0 { - ob.WriteString("</ul>\n") + out.WriteString("</ul>\n") } }
A latex.go

@@ -0,0 +1,305 @@

+// +// Black Friday Markdown Processor +// Originally based on http://github.com/tanoku/upskirt +// by Russ Ross <russ@russross.com> +// + +// +// +// LaTeX rendering backend +// +// + +package blackfriday + +import ( + "bytes" +) + +func LatexRenderer(flags int) *Renderer { + // block-level rendering + r := new(Renderer) + r.blockcode = latexBlockcode + r.blockquote = latexBlockquote + //r.blockhtml = ? + r.header = latexHeader + r.hrule = latexHrule + r.list = latexList + r.listitem = latexListitem + r.paragraph = latexParagraph + r.table = latexTable + r.tableRow = latexTableRow + r.tableCell = latexTableCell + + // inline rendering + r.autolink = latexAutolink + r.codespan = latexCodespan + r.doubleEmphasis = latexDoubleEmphasis + r.emphasis = latexEmphasis + r.image = latexImage + r.linebreak = latexLinebreak + r.link = latexLink + //r.rawHtmlTag = ? + r.strikethrough = latexStrikethrough + + r.normalText = latexNormalText + + r.documentHeader = latexDocumentHeader + r.documentFooter = latexDocumentFooter + + r.opaque = nil + return r +} + +// render code chunks using verbatim, or listings if we have a language +func latexBlockcode(out *bytes.Buffer, text []byte, lang string, opaque interface{}) { + if lang == "" { + out.WriteString("\n\\begin{verbatim}\n") + } else { + out.WriteString("\n\\begin{lstlisting}[language=") + out.WriteString(lang) + out.WriteString("]\n") + } + out.Write(text) + if lang == "" { + out.WriteString("\n\\end{verbatim}\n") + } else { + out.WriteString("\n\\end{lstlisting}\n") + } +} + +func latexBlockquote(out *bytes.Buffer, text []byte, opaque interface{}) { + out.WriteString("\n\\begin{quotation}\n") + out.Write(text) + out.WriteString("\n\\end{quotation}\n") +} + +//blockhtml func(out *bytes.Buffer, text []byte, opaque interface{}) + +func latexHeader(out *bytes.Buffer, text []byte, level int, opaque interface{}) { + switch level { + case 1: + out.WriteString("\n\\section{") + case 2: + out.WriteString("\n\\subsection{") + case 3: + out.WriteString("\n\\subsubsection{") + case 4: + out.WriteString("\n\\paragraph{") + case 5: + out.WriteString("\n\\subparagraph{") + case 6: + out.WriteString("\n\\textbf{") + } + out.Write(text) + out.WriteString("}\n") +} + +func latexHrule(out *bytes.Buffer, opaque interface{}) { + out.WriteString("\n\\HRule\n") +} + +func latexList(out *bytes.Buffer, text []byte, flags int, opaque interface{}) { + if flags&LIST_TYPE_ORDERED != 0 { + out.WriteString("\n\\begin{enumerate}\n") + } else { + out.WriteString("\n\\begin{itemize}\n") + } + out.Write(text) + if flags&LIST_TYPE_ORDERED != 0 { + out.WriteString("\n\\end{enumerate}\n") + } else { + out.WriteString("\n\\end{itemize}\n") + } +} + +func latexListitem(out *bytes.Buffer, text []byte, flags int, opaque interface{}) { + out.WriteString("\n\\item ") + out.Write(text) +} + +func latexParagraph(out *bytes.Buffer, text []byte, opaque interface{}) { + out.WriteString("\n") + out.Write(text) + out.WriteString("\n") +} + +func latexTable(out *bytes.Buffer, header []byte, body []byte, columnData []int, opaque interface{}) { + out.WriteString("\n\\begin{tabular}{") + for _, elt := range columnData { + switch elt { + case TABLE_ALIGNMENT_LEFT: + out.WriteByte('l') + case TABLE_ALIGNMENT_RIGHT: + out.WriteByte('r') + default: + out.WriteByte('c') + } + } + out.WriteString("}\n") + out.Write(header) + out.WriteString(" \\\\\n\\hline\n") + out.Write(body) + out.WriteString("\n\\end{tabular}\n") +} + +func latexTableRow(out *bytes.Buffer, text []byte, opaque interface{}) { + if out.Len() > 0 { + out.WriteString(" \\\\\n") + } + out.Write(text) +} + +func latexTableCell(out *bytes.Buffer, text []byte, align int, opaque interface{}) { + if out.Len() > 0 { + out.WriteString(" & ") + } + out.Write(text) +} + +func latexAutolink(out *bytes.Buffer, link []byte, kind int, opaque interface{}) int { + out.WriteString("\\href{") + if kind == LINK_TYPE_EMAIL { + out.WriteString("mailto:") + } + out.Write(link) + out.WriteString("}{") + out.Write(link) + out.WriteString("}") + return 1 +} + +func latexCodespan(out *bytes.Buffer, text []byte, opaque interface{}) int { + out.WriteString("\\texttt{") + escapeSpecialChars(out, text) + out.WriteString("}") + return 1 +} + +func latexDoubleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) int { + out.WriteString("\\textbf{") + out.Write(text) + out.WriteString("}") + return 1 +} + +func latexEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) int { + out.WriteString("\\textit{") + out.Write(text) + out.WriteString("}") + return 1 +} + +func latexImage(out *bytes.Buffer, link []byte, title []byte, alt []byte, opaque interface{}) int { + if bytes.HasPrefix(link, []byte("http://")) || bytes.HasPrefix(link, []byte("https://")) { + // treat it like a link + out.WriteString("\\href{") + out.Write(link) + out.WriteString("}{") + out.Write(alt) + out.WriteString("}") + } else { + out.WriteString("\\includegraphics{") + out.Write(link) + out.WriteString("}") + } + return 1 +} + +func latexLinebreak(out *bytes.Buffer, opaque interface{}) int { + out.WriteString(" \\\\\n") + return 1 +} + +func latexLink(out *bytes.Buffer, link []byte, title []byte, content []byte, opaque interface{}) int { + out.WriteString("\\href{") + out.Write(link) + out.WriteString("}{") + out.Write(content) + out.WriteString("}") + return 1 +} + +func latexRawHtmlTag(out *bytes.Buffer, tag []byte, opaque interface{}) int { + return 0 +} + +func latexTripleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) int { + out.WriteString("\\textbf{\\textit{") + out.Write(text) + out.WriteString("}}") + return 1 +} + +func latexStrikethrough(out *bytes.Buffer, text []byte, opaque interface{}) int { + out.WriteString("\\sout{") + out.Write(text) + out.WriteString("}") + return 1 +} + +func needsBackslash(c byte) bool { + for _, r := range []byte("_{}%$&\\~") { + if c == r { + return true + } + } + return false +} + +func escapeSpecialChars(out *bytes.Buffer, text []byte) { + for i := 0; i < len(text); i++ { + // directly copy normal characters + org := i + + for i < len(text) && !needsBackslash(text[i]) { + i++ + } + if i > org { + out.Write(text[org:i]) + } + + // escape a character + if i >= len(text) { + break + } + out.WriteByte('\\') + out.WriteByte(text[i]) + } +} + +func latexNormalText(out *bytes.Buffer, text []byte, opaque interface{}) { + escapeSpecialChars(out, text) +} + +// header and footer +func latexDocumentHeader(out *bytes.Buffer, opaque interface{}) { + out.WriteString("\\documentclass{article}\n") + out.WriteString("\n") + out.WriteString("\\usepackage{graphicx}\n") + out.WriteString("\\usepackage{listings}\n") + out.WriteString("\\usepackage[margin=1in]{geometry}\n") + out.WriteString("\\usepackage[utf8]{inputenc}\n") + out.WriteString("\\usepackage{verbatim}\n") + out.WriteString("\\usepackage[normalem]{ulem}\n") + out.WriteString("\\usepackage{hyperref}\n") + out.WriteString("\n") + out.WriteString("\\hypersetup{colorlinks,%\n") + out.WriteString(" citecolor=black,%\n") + out.WriteString(" filecolor=black,%\n") + out.WriteString(" linkcolor=black,%\n") + out.WriteString(" urlcolor=black,%\n") + out.WriteString(" pdfstartview=FitH,%\n") + out.WriteString(" breaklinks=true,%\n") + out.WriteString(" pdfauthor={Black Friday Markdown Processor}}\n") + out.WriteString("\n") + out.WriteString("\\newcommand{\\HRule}{\\rule{\\linewidth}{0.5mm}}\n") + out.WriteString("\\addtolength{\\parskip}{0.5\\baselineskip}\n") + out.WriteString("\\parindent=0pt\n") + out.WriteString("\n") + out.WriteString("\\begin{document}\n") +} + +func latexDocumentFooter(out *bytes.Buffer, opaque interface{}) { + out.WriteString("\n\\end{document}\n") +}
M markdown.gomarkdown.go

@@ -104,7 +104,7 @@ hrule func(out *bytes.Buffer, opaque interface{})

list func(out *bytes.Buffer, text []byte, flags int, opaque interface{}) listitem func(out *bytes.Buffer, text []byte, flags int, opaque interface{}) paragraph func(out *bytes.Buffer, text []byte, opaque interface{}) - table func(out *bytes.Buffer, header []byte, body []byte, opaque interface{}) + table func(out *bytes.Buffer, header []byte, body []byte, columnData []int, opaque interface{}) tableRow func(out *bytes.Buffer, text []byte, opaque interface{}) tableCell func(out *bytes.Buffer, text []byte, flags int, opaque interface{})