all repos — grayfriday @ f5e3dc8073dcaef4a37da1bfee94d67f0866bd3c

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