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