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
22// Latex is a type that implements the Renderer interface for LaTeX output.
23//
24// Do not create this directly, instead use the LatexRenderer function.
25type Latex struct {
26
27}
28
29// LatexRenderer creates and configures a Latex object, which
30// satisfies the Renderer interface.
31//
32// flags is a set of LATEX_* options ORed together (currently no such options
33// are defined).
34func LatexRenderer(flags int) Renderer {
35 return &Latex{}
36}
37
38// render code chunks using verbatim, or listings if we have a language
39func (options *Latex) BlockCode(out *bytes.Buffer, text []byte, lang string) {
40 if lang == "" {
41 out.WriteString("\n\\begin{verbatim}\n")
42 } else {
43 out.WriteString("\n\\begin{lstlisting}[language=")
44 out.WriteString(lang)
45 out.WriteString("]\n")
46 }
47 out.Write(text)
48 if lang == "" {
49 out.WriteString("\n\\end{verbatim}\n")
50 } else {
51 out.WriteString("\n\\end{lstlisting}\n")
52 }
53}
54
55func (options *Latex) BlockQuote(out *bytes.Buffer, text []byte) {
56 out.WriteString("\n\\begin{quotation}\n")
57 out.Write(text)
58 out.WriteString("\n\\end{quotation}\n")
59}
60
61func (options *Latex) BlockHtml(out *bytes.Buffer, text []byte) {
62 // a pretty lame thing to do...
63 out.WriteString("\n\\begin{verbatim}\n")
64 out.Write(text)
65 out.WriteString("\n\\end{verbatim}\n")
66}
67
68func (options *Latex) Header(out *bytes.Buffer, text func() bool, level int) {
69 marker := out.Len()
70
71 switch level {
72 case 1:
73 out.WriteString("\n\\section{")
74 case 2:
75 out.WriteString("\n\\subsection{")
76 case 3:
77 out.WriteString("\n\\subsubsection{")
78 case 4:
79 out.WriteString("\n\\paragraph{")
80 case 5:
81 out.WriteString("\n\\subparagraph{")
82 case 6:
83 out.WriteString("\n\\textbf{")
84 }
85 if !text() {
86 out.Truncate(marker)
87 return
88 }
89 out.WriteString("}\n")
90}
91
92func (options *Latex) HRule(out *bytes.Buffer) {
93 out.WriteString("\n\\HRule\n")
94}
95
96func (options *Latex) List(out *bytes.Buffer, text func() bool, flags int) {
97 marker := out.Len()
98 if flags&LIST_TYPE_ORDERED != 0 {
99 out.WriteString("\n\\begin{enumerate}\n")
100 } else {
101 out.WriteString("\n\\begin{itemize}\n")
102 }
103 if !text() {
104 out.Truncate(marker)
105 return
106 }
107 if flags&LIST_TYPE_ORDERED != 0 {
108 out.WriteString("\n\\end{enumerate}\n")
109 } else {
110 out.WriteString("\n\\end{itemize}\n")
111 }
112}
113
114func (options *Latex) ListItem(out *bytes.Buffer, text []byte, flags int) {
115 out.WriteString("\n\\item ")
116 out.Write(text)
117}
118
119func (options *Latex) Paragraph(out *bytes.Buffer, text func() bool) {
120 marker := out.Len()
121 out.WriteString("\n")
122 if !text() {
123 out.Truncate(marker)
124 return
125 }
126 out.WriteString("\n")
127}
128
129func (options *Latex) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {
130 out.WriteString("\n\\begin{tabular}{")
131 for _, elt := range columnData {
132 switch elt {
133 case TABLE_ALIGNMENT_LEFT:
134 out.WriteByte('l')
135 case TABLE_ALIGNMENT_RIGHT:
136 out.WriteByte('r')
137 default:
138 out.WriteByte('c')
139 }
140 }
141 out.WriteString("}\n")
142 out.Write(header)
143 out.WriteString(" \\\\\n\\hline\n")
144 out.Write(body)
145 out.WriteString("\n\\end{tabular}\n")
146}
147
148func (options *Latex) TableRow(out *bytes.Buffer, text []byte) {
149 if out.Len() > 0 {
150 out.WriteString(" \\\\\n")
151 }
152 out.Write(text)
153}
154
155func (options *Latex) TableCell(out *bytes.Buffer, text []byte, align int) {
156 if out.Len() > 0 {
157 out.WriteString(" & ")
158 }
159 out.Write(text)
160}
161
162func (options *Latex) AutoLink(out *bytes.Buffer, link []byte, kind int) {
163 out.WriteString("\\href{")
164 if kind == LINK_TYPE_EMAIL {
165 out.WriteString("mailto:")
166 }
167 out.Write(link)
168 out.WriteString("}{")
169 out.Write(link)
170 out.WriteString("}")
171}
172
173func (options *Latex) CodeSpan(out *bytes.Buffer, text []byte) {
174 out.WriteString("\\texttt{")
175 escapeSpecialChars(out, text)
176 out.WriteString("}")
177}
178
179func (options *Latex) DoubleEmphasis(out *bytes.Buffer, text []byte) {
180 out.WriteString("\\textbf{")
181 out.Write(text)
182 out.WriteString("}")
183}
184
185func (options *Latex) Emphasis(out *bytes.Buffer, text []byte) {
186 out.WriteString("\\textit{")
187 out.Write(text)
188 out.WriteString("}")
189}
190
191func (options *Latex) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
192 if bytes.HasPrefix(link, []byte("http://")) || bytes.HasPrefix(link, []byte("https://")) {
193 // treat it like a link
194 out.WriteString("\\href{")
195 out.Write(link)
196 out.WriteString("}{")
197 out.Write(alt)
198 out.WriteString("}")
199 } else {
200 out.WriteString("\\includegraphics{")
201 out.Write(link)
202 out.WriteString("}")
203 }
204}
205
206func (options *Latex) LineBreak(out *bytes.Buffer) {
207 out.WriteString(" \\\\\n")
208}
209
210func (options *Latex) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
211 out.WriteString("\\href{")
212 out.Write(link)
213 out.WriteString("}{")
214 out.Write(content)
215 out.WriteString("}")
216}
217
218func (options *Latex) RawHtmlTag(out *bytes.Buffer, tag []byte) {
219}
220
221func (options *Latex) TripleEmphasis(out *bytes.Buffer, text []byte) {
222 out.WriteString("\\textbf{\\textit{")
223 out.Write(text)
224 out.WriteString("}}")
225}
226
227func (options *Latex) StrikeThrough(out *bytes.Buffer, text []byte) {
228 out.WriteString("\\sout{")
229 out.Write(text)
230 out.WriteString("}")
231}
232
233func needsBackslash(c byte) bool {
234 for _, r := range []byte("_{}%$&\\~") {
235 if c == r {
236 return true
237 }
238 }
239 return false
240}
241
242func escapeSpecialChars(out *bytes.Buffer, text []byte) {
243 for i := 0; i < len(text); i++ {
244 // directly copy normal characters
245 org := i
246
247 for i < len(text) && !needsBackslash(text[i]) {
248 i++
249 }
250 if i > org {
251 out.Write(text[org:i])
252 }
253
254 // escape a character
255 if i >= len(text) {
256 break
257 }
258 out.WriteByte('\\')
259 out.WriteByte(text[i])
260 }
261}
262
263func (options *Latex) Entity(out *bytes.Buffer, entity []byte) {
264 // TODO: convert this into a unicode character or something
265 out.Write(entity)
266}
267
268func (options *Latex) NormalText(out *bytes.Buffer, text []byte) {
269 escapeSpecialChars(out, text)
270}
271
272// header and footer
273func (options *Latex) DocumentHeader(out *bytes.Buffer) {
274 out.WriteString("\\documentclass{article}\n")
275 out.WriteString("\n")
276 out.WriteString("\\usepackage{graphicx}\n")
277 out.WriteString("\\usepackage{listings}\n")
278 out.WriteString("\\usepackage[margin=1in]{geometry}\n")
279 out.WriteString("\\usepackage[utf8]{inputenc}\n")
280 out.WriteString("\\usepackage{verbatim}\n")
281 out.WriteString("\\usepackage[normalem]{ulem}\n")
282 out.WriteString("\\usepackage{hyperref}\n")
283 out.WriteString("\n")
284 out.WriteString("\\hypersetup{colorlinks,%\n")
285 out.WriteString(" citecolor=black,%\n")
286 out.WriteString(" filecolor=black,%\n")
287 out.WriteString(" linkcolor=black,%\n")
288 out.WriteString(" urlcolor=black,%\n")
289 out.WriteString(" pdfstartview=FitH,%\n")
290 out.WriteString(" breaklinks=true,%\n")
291 out.WriteString(" pdfauthor={Blackfriday Markdown Processor v")
292 out.WriteString(VERSION)
293 out.WriteString("}}\n")
294 out.WriteString("\n")
295 out.WriteString("\\newcommand{\\HRule}{\\rule{\\linewidth}{0.5mm}}\n")
296 out.WriteString("\\addtolength{\\parskip}{0.5\\baselineskip}\n")
297 out.WriteString("\\parindent=0pt\n")
298 out.WriteString("\n")
299 out.WriteString("\\begin{document}\n")
300}
301
302func (options *Latex) DocumentFooter(out *bytes.Buffer) {
303 out.WriteString("\n\\end{document}\n")
304}