latex.go (view raw)
1//
2// Black Friday Markdown Processor
3// Originally based on http://github.com/tanoku/upskirt
4// by Russ Ross <russ@russross.com>
5//
6
7//
8//
9// LaTeX rendering backend
10//
11//
12
13package blackfriday
14
15import (
16 "bytes"
17)
18
19func LatexRenderer(flags int) *Renderer {
20 // block-level rendering
21 r := new(Renderer)
22 r.blockcode = latexBlockcode
23 r.blockquote = latexBlockquote
24 //r.blockhtml = ?
25 r.header = latexHeader
26 r.hrule = latexHrule
27 r.list = latexList
28 r.listitem = latexListitem
29 r.paragraph = latexParagraph
30 r.table = latexTable
31 r.tableRow = latexTableRow
32 r.tableCell = latexTableCell
33
34 // inline rendering
35 r.autolink = latexAutolink
36 r.codespan = latexCodespan
37 r.doubleEmphasis = latexDoubleEmphasis
38 r.emphasis = latexEmphasis
39 r.image = latexImage
40 r.linebreak = latexLinebreak
41 r.link = latexLink
42 //r.rawHtmlTag = ?
43 r.strikethrough = latexStrikethrough
44
45 r.normalText = latexNormalText
46
47 r.documentHeader = latexDocumentHeader
48 r.documentFooter = latexDocumentFooter
49
50 r.opaque = nil
51 return r
52}
53
54// render code chunks using verbatim, or listings if we have a language
55func latexBlockcode(out *bytes.Buffer, text []byte, lang string, opaque interface{}) {
56 if lang == "" {
57 out.WriteString("\n\\begin{verbatim}\n")
58 } else {
59 out.WriteString("\n\\begin{lstlisting}[language=")
60 out.WriteString(lang)
61 out.WriteString("]\n")
62 }
63 out.Write(text)
64 if lang == "" {
65 out.WriteString("\n\\end{verbatim}\n")
66 } else {
67 out.WriteString("\n\\end{lstlisting}\n")
68 }
69}
70
71func latexBlockquote(out *bytes.Buffer, text []byte, opaque interface{}) {
72 out.WriteString("\n\\begin{quotation}\n")
73 out.Write(text)
74 out.WriteString("\n\\end{quotation}\n")
75}
76
77//blockhtml func(out *bytes.Buffer, text []byte, opaque interface{})
78
79func latexHeader(out *bytes.Buffer, text []byte, level int, opaque interface{}) {
80 switch level {
81 case 1:
82 out.WriteString("\n\\section{")
83 case 2:
84 out.WriteString("\n\\subsection{")
85 case 3:
86 out.WriteString("\n\\subsubsection{")
87 case 4:
88 out.WriteString("\n\\paragraph{")
89 case 5:
90 out.WriteString("\n\\subparagraph{")
91 case 6:
92 out.WriteString("\n\\textbf{")
93 }
94 out.Write(text)
95 out.WriteString("}\n")
96}
97
98func latexHrule(out *bytes.Buffer, opaque interface{}) {
99 out.WriteString("\n\\HRule\n")
100}
101
102func latexList(out *bytes.Buffer, text []byte, flags int, opaque interface{}) {
103 if flags&LIST_TYPE_ORDERED != 0 {
104 out.WriteString("\n\\begin{enumerate}\n")
105 } else {
106 out.WriteString("\n\\begin{itemize}\n")
107 }
108 out.Write(text)
109 if flags&LIST_TYPE_ORDERED != 0 {
110 out.WriteString("\n\\end{enumerate}\n")
111 } else {
112 out.WriteString("\n\\end{itemize}\n")
113 }
114}
115
116func latexListitem(out *bytes.Buffer, text []byte, flags int, opaque interface{}) {
117 out.WriteString("\n\\item ")
118 out.Write(text)
119}
120
121func latexParagraph(out *bytes.Buffer, text []byte, opaque interface{}) {
122 out.WriteString("\n")
123 out.Write(text)
124 out.WriteString("\n")
125}
126
127func latexTable(out *bytes.Buffer, header []byte, body []byte, columnData []int, opaque interface{}) {
128 out.WriteString("\n\\begin{tabular}{")
129 for _, elt := range columnData {
130 switch elt {
131 case TABLE_ALIGNMENT_LEFT:
132 out.WriteByte('l')
133 case TABLE_ALIGNMENT_RIGHT:
134 out.WriteByte('r')
135 default:
136 out.WriteByte('c')
137 }
138 }
139 out.WriteString("}\n")
140 out.Write(header)
141 out.WriteString(" \\\\\n\\hline\n")
142 out.Write(body)
143 out.WriteString("\n\\end{tabular}\n")
144}
145
146func latexTableRow(out *bytes.Buffer, text []byte, opaque interface{}) {
147 if out.Len() > 0 {
148 out.WriteString(" \\\\\n")
149 }
150 out.Write(text)
151}
152
153func latexTableCell(out *bytes.Buffer, text []byte, align int, opaque interface{}) {
154 if out.Len() > 0 {
155 out.WriteString(" & ")
156 }
157 out.Write(text)
158}
159
160func latexAutolink(out *bytes.Buffer, link []byte, kind int, opaque interface{}) int {
161 out.WriteString("\\href{")
162 if kind == LINK_TYPE_EMAIL {
163 out.WriteString("mailto:")
164 }
165 out.Write(link)
166 out.WriteString("}{")
167 out.Write(link)
168 out.WriteString("}")
169 return 1
170}
171
172func latexCodespan(out *bytes.Buffer, text []byte, opaque interface{}) int {
173 out.WriteString("\\texttt{")
174 escapeSpecialChars(out, text)
175 out.WriteString("}")
176 return 1
177}
178
179func latexDoubleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) int {
180 out.WriteString("\\textbf{")
181 out.Write(text)
182 out.WriteString("}")
183 return 1
184}
185
186func latexEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) int {
187 out.WriteString("\\textit{")
188 out.Write(text)
189 out.WriteString("}")
190 return 1
191}
192
193func latexImage(out *bytes.Buffer, link []byte, title []byte, alt []byte, opaque interface{}) int {
194 if bytes.HasPrefix(link, []byte("http://")) || bytes.HasPrefix(link, []byte("https://")) {
195 // treat it like a link
196 out.WriteString("\\href{")
197 out.Write(link)
198 out.WriteString("}{")
199 out.Write(alt)
200 out.WriteString("}")
201 } else {
202 out.WriteString("\\includegraphics{")
203 out.Write(link)
204 out.WriteString("}")
205 }
206 return 1
207}
208
209func latexLinebreak(out *bytes.Buffer, opaque interface{}) int {
210 out.WriteString(" \\\\\n")
211 return 1
212}
213
214func latexLink(out *bytes.Buffer, link []byte, title []byte, content []byte, opaque interface{}) int {
215 out.WriteString("\\href{")
216 out.Write(link)
217 out.WriteString("}{")
218 out.Write(content)
219 out.WriteString("}")
220 return 1
221}
222
223func latexRawHtmlTag(out *bytes.Buffer, tag []byte, opaque interface{}) int {
224 return 0
225}
226
227func latexTripleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) int {
228 out.WriteString("\\textbf{\\textit{")
229 out.Write(text)
230 out.WriteString("}}")
231 return 1
232}
233
234func latexStrikethrough(out *bytes.Buffer, text []byte, opaque interface{}) int {
235 out.WriteString("\\sout{")
236 out.Write(text)
237 out.WriteString("}")
238 return 1
239}
240
241func needsBackslash(c byte) bool {
242 for _, r := range []byte("_{}%$&\\~") {
243 if c == r {
244 return true
245 }
246 }
247 return false
248}
249
250func escapeSpecialChars(out *bytes.Buffer, text []byte) {
251 for i := 0; i < len(text); i++ {
252 // directly copy normal characters
253 org := i
254
255 for i < len(text) && !needsBackslash(text[i]) {
256 i++
257 }
258 if i > org {
259 out.Write(text[org:i])
260 }
261
262 // escape a character
263 if i >= len(text) {
264 break
265 }
266 out.WriteByte('\\')
267 out.WriteByte(text[i])
268 }
269}
270
271func latexNormalText(out *bytes.Buffer, text []byte, opaque interface{}) {
272 escapeSpecialChars(out, text)
273}
274
275// header and footer
276func latexDocumentHeader(out *bytes.Buffer, opaque interface{}) {
277 out.WriteString("\\documentclass{article}\n")
278 out.WriteString("\n")
279 out.WriteString("\\usepackage{graphicx}\n")
280 out.WriteString("\\usepackage{listings}\n")
281 out.WriteString("\\usepackage[margin=1in]{geometry}\n")
282 out.WriteString("\\usepackage[utf8]{inputenc}\n")
283 out.WriteString("\\usepackage{verbatim}\n")
284 out.WriteString("\\usepackage[normalem]{ulem}\n")
285 out.WriteString("\\usepackage{hyperref}\n")
286 out.WriteString("\n")
287 out.WriteString("\\hypersetup{colorlinks,%\n")
288 out.WriteString(" citecolor=black,%\n")
289 out.WriteString(" filecolor=black,%\n")
290 out.WriteString(" linkcolor=black,%\n")
291 out.WriteString(" urlcolor=black,%\n")
292 out.WriteString(" pdfstartview=FitH,%\n")
293 out.WriteString(" breaklinks=true,%\n")
294 out.WriteString(" pdfauthor={Black Friday Markdown Processor}}\n")
295 out.WriteString("\n")
296 out.WriteString("\\newcommand{\\HRule}{\\rule{\\linewidth}{0.5mm}}\n")
297 out.WriteString("\\addtolength{\\parskip}{0.5\\baselineskip}\n")
298 out.WriteString("\\parindent=0pt\n")
299 out.WriteString("\n")
300 out.WriteString("\\begin{document}\n")
301}
302
303func latexDocumentFooter(out *bytes.Buffer, opaque interface{}) {
304 out.WriteString("\n\\end{document}\n")
305}