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 func() bool, level int, opaque interface{}) {
80 marker := out.Len()
81
82 switch level {
83 case 1:
84 out.WriteString("\n\\section{")
85 case 2:
86 out.WriteString("\n\\subsection{")
87 case 3:
88 out.WriteString("\n\\subsubsection{")
89 case 4:
90 out.WriteString("\n\\paragraph{")
91 case 5:
92 out.WriteString("\n\\subparagraph{")
93 case 6:
94 out.WriteString("\n\\textbf{")
95 }
96 if !text() {
97 out.Truncate(marker)
98 return
99 }
100 out.WriteString("}\n")
101}
102
103func latexHRule(out *bytes.Buffer, opaque interface{}) {
104 out.WriteString("\n\\HRule\n")
105}
106
107func latexList(out *bytes.Buffer, text func() bool, flags int, opaque interface{}) {
108 marker := out.Len()
109 if flags&LIST_TYPE_ORDERED != 0 {
110 out.WriteString("\n\\begin{enumerate}\n")
111 } else {
112 out.WriteString("\n\\begin{itemize}\n")
113 }
114 if !text() {
115 out.Truncate(marker)
116 return
117 }
118 if flags&LIST_TYPE_ORDERED != 0 {
119 out.WriteString("\n\\end{enumerate}\n")
120 } else {
121 out.WriteString("\n\\end{itemize}\n")
122 }
123}
124
125func latexListItem(out *bytes.Buffer, text []byte, flags int, opaque interface{}) {
126 out.WriteString("\n\\item ")
127 out.Write(text)
128}
129
130func latexParagraph(out *bytes.Buffer, text func() bool, opaque interface{}) {
131 marker := out.Len()
132 out.WriteString("\n")
133 if !text() {
134 out.Truncate(marker)
135 return
136 }
137 out.WriteString("\n")
138}
139
140func latexTable(out *bytes.Buffer, header []byte, body []byte, columnData []int, opaque interface{}) {
141 out.WriteString("\n\\begin{tabular}{")
142 for _, elt := range columnData {
143 switch elt {
144 case TABLE_ALIGNMENT_LEFT:
145 out.WriteByte('l')
146 case TABLE_ALIGNMENT_RIGHT:
147 out.WriteByte('r')
148 default:
149 out.WriteByte('c')
150 }
151 }
152 out.WriteString("}\n")
153 out.Write(header)
154 out.WriteString(" \\\\\n\\hline\n")
155 out.Write(body)
156 out.WriteString("\n\\end{tabular}\n")
157}
158
159func latexTableRow(out *bytes.Buffer, text []byte, opaque interface{}) {
160 if out.Len() > 0 {
161 out.WriteString(" \\\\\n")
162 }
163 out.Write(text)
164}
165
166func latexTableCell(out *bytes.Buffer, text []byte, align int, opaque interface{}) {
167 if out.Len() > 0 {
168 out.WriteString(" & ")
169 }
170 out.Write(text)
171}
172
173func latexAutoLink(out *bytes.Buffer, link []byte, kind int, opaque interface{}) int {
174 out.WriteString("\\href{")
175 if kind == LINK_TYPE_EMAIL {
176 out.WriteString("mailto:")
177 }
178 out.Write(link)
179 out.WriteString("}{")
180 out.Write(link)
181 out.WriteString("}")
182 return 1
183}
184
185func latexCodeSpan(out *bytes.Buffer, text []byte, opaque interface{}) int {
186 out.WriteString("\\texttt{")
187 escapeSpecialChars(out, text)
188 out.WriteString("}")
189 return 1
190}
191
192func latexDoubleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) int {
193 out.WriteString("\\textbf{")
194 out.Write(text)
195 out.WriteString("}")
196 return 1
197}
198
199func latexEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) int {
200 out.WriteString("\\textit{")
201 out.Write(text)
202 out.WriteString("}")
203 return 1
204}
205
206func latexImage(out *bytes.Buffer, link []byte, title []byte, alt []byte, opaque interface{}) int {
207 if bytes.HasPrefix(link, []byte("http://")) || bytes.HasPrefix(link, []byte("https://")) {
208 // treat it like a link
209 out.WriteString("\\href{")
210 out.Write(link)
211 out.WriteString("}{")
212 out.Write(alt)
213 out.WriteString("}")
214 } else {
215 out.WriteString("\\includegraphics{")
216 out.Write(link)
217 out.WriteString("}")
218 }
219 return 1
220}
221
222func latexLineBreak(out *bytes.Buffer, opaque interface{}) int {
223 out.WriteString(" \\\\\n")
224 return 1
225}
226
227func latexLink(out *bytes.Buffer, link []byte, title []byte, content []byte, opaque interface{}) int {
228 out.WriteString("\\href{")
229 out.Write(link)
230 out.WriteString("}{")
231 out.Write(content)
232 out.WriteString("}")
233 return 1
234}
235
236func latexRawHtmlTag(out *bytes.Buffer, tag []byte, opaque interface{}) int {
237 return 0
238}
239
240func latexTripleEmphasis(out *bytes.Buffer, text []byte, opaque interface{}) int {
241 out.WriteString("\\textbf{\\textit{")
242 out.Write(text)
243 out.WriteString("}}")
244 return 1
245}
246
247func latexStrikeThrough(out *bytes.Buffer, text []byte, opaque interface{}) int {
248 out.WriteString("\\sout{")
249 out.Write(text)
250 out.WriteString("}")
251 return 1
252}
253
254func needsBackslash(c byte) bool {
255 for _, r := range []byte("_{}%$&\\~") {
256 if c == r {
257 return true
258 }
259 }
260 return false
261}
262
263func escapeSpecialChars(out *bytes.Buffer, text []byte) {
264 for i := 0; i < len(text); i++ {
265 // directly copy normal characters
266 org := i
267
268 for i < len(text) && !needsBackslash(text[i]) {
269 i++
270 }
271 if i > org {
272 out.Write(text[org:i])
273 }
274
275 // escape a character
276 if i >= len(text) {
277 break
278 }
279 out.WriteByte('\\')
280 out.WriteByte(text[i])
281 }
282}
283
284func latexNormalText(out *bytes.Buffer, text []byte, opaque interface{}) {
285 escapeSpecialChars(out, text)
286}
287
288// header and footer
289func latexDocumentHeader(out *bytes.Buffer, opaque interface{}) {
290 out.WriteString("\\documentclass{article}\n")
291 out.WriteString("\n")
292 out.WriteString("\\usepackage{graphicx}\n")
293 out.WriteString("\\usepackage{listings}\n")
294 out.WriteString("\\usepackage[margin=1in]{geometry}\n")
295 out.WriteString("\\usepackage[utf8]{inputenc}\n")
296 out.WriteString("\\usepackage{verbatim}\n")
297 out.WriteString("\\usepackage[normalem]{ulem}\n")
298 out.WriteString("\\usepackage{hyperref}\n")
299 out.WriteString("\n")
300 out.WriteString("\\hypersetup{colorlinks,%\n")
301 out.WriteString(" citecolor=black,%\n")
302 out.WriteString(" filecolor=black,%\n")
303 out.WriteString(" linkcolor=black,%\n")
304 out.WriteString(" urlcolor=black,%\n")
305 out.WriteString(" pdfstartview=FitH,%\n")
306 out.WriteString(" breaklinks=true,%\n")
307 out.WriteString(" pdfauthor={Black Friday Markdown Processor}}\n")
308 out.WriteString("\n")
309 out.WriteString("\\newcommand{\\HRule}{\\rule{\\linewidth}{0.5mm}}\n")
310 out.WriteString("\\addtolength{\\parskip}{0.5\\baselineskip}\n")
311 out.WriteString("\\parindent=0pt\n")
312 out.WriteString("\n")
313 out.WriteString("\\begin{document}\n")
314}
315
316func latexDocumentFooter(out *bytes.Buffer, opaque interface{}) {
317 out.WriteString("\n\\end{document}\n")
318}