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