all repos — grayfriday @ 3af64a90ad74a0c4bd13fc63b41b298b8a09ac21

blackfriday fork with a few changes

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}