aboutsummaryrefslogtreecommitdiff
path: root/src/Text/Pandoc/Writers/LaTeX.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Text/Pandoc/Writers/LaTeX.hs')
-rw-r--r--src/Text/Pandoc/Writers/LaTeX.hs164
1 files changed, 164 insertions, 0 deletions
diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs
new file mode 100644
index 000000000..b77789e90
--- /dev/null
+++ b/src/Text/Pandoc/Writers/LaTeX.hs
@@ -0,0 +1,164 @@
+-- | Convert Pandoc to LaTeX.
+module Text.Pandoc.Writers.LaTeX (
+ writeLaTeX
+ ) where
+import Text.Pandoc.Definition
+import Text.Pandoc.Shared
+import List ( (\\) )
+
+-- | Convert Pandoc to LaTeX.
+writeLaTeX :: WriterOptions -> Pandoc -> String
+writeLaTeX options (Pandoc meta blocks) =
+ let notes = filter isNoteBlock blocks in -- assumes all notes are at outer level
+ let body = (writerIncludeBefore options) ++
+ (concatMap (blockToLaTeX notes) (replaceReferenceLinks blocks)) ++
+ (writerIncludeAfter options) in
+ let head = if writerStandalone options then
+ latexHeader notes options meta
+ else
+ "" in
+ let foot = if writerStandalone options then "\n\\end{document}\n" else "" in
+ head ++ body ++ foot
+
+-- | Insert bibliographic information into LaTeX header.
+latexHeader :: [Block] -- ^ List of note blocks to use in resolving note refs
+ -> WriterOptions -- ^ Options, including LaTeX header
+ -> Meta -- ^ Meta with bibliographic information
+ -> String
+latexHeader notes options (Meta title authors date) =
+ let titletext = if null title then
+ ""
+ else
+ "\\title{" ++ inlineListToLaTeX notes title ++ "}\n"
+ authorstext = if null authors then
+ ""
+ else
+ "\\author{" ++ (joinWithSep "\\\\" (map stringToLaTeX authors)) ++ "}\n"
+ datetext = if date == "" then
+ ""
+ else
+ "\\date{" ++ stringToLaTeX date ++ "}\n"
+ maketitle = if null title then
+ ""
+ else
+ "\\maketitle\n"
+ secnumline = if (writerNumberSections options) then
+ ""
+ else
+ "\\setcounter{secnumdepth}{0}\n"
+ header = writerHeader options in
+ header ++ secnumline ++ titletext ++ authorstext ++ datetext ++ "\\begin{document}\n" ++ maketitle
+
+
+-- escape things as needed for LaTeX (also ldots, dashes, quotes, etc.)
+
+escapeBrackets = backslashEscape "{}"
+escapeSpecial = backslashEscape "$%&~_#"
+
+escapeBackslash = gsub "\\\\" "\\\\textbackslash{}"
+fixBackslash = gsub "\\\\textbackslash\\\\\\{\\\\\\}" "\\\\textbackslash{}"
+escapeHat = gsub "\\^" "\\\\^{}"
+escapeBar = gsub "\\|" "\\\\textbar{}"
+escapeLt = gsub "<" "\\\\textless{}"
+escapeGt = gsub ">" "\\\\textgreater{}"
+
+escapeDoubleQuotes =
+ gsub "\"" "''" . -- rest are right quotes
+ gsub "([[:space:]])\"" "\\1``" . -- never right quote after space
+ gsub "\"('|`)([^[:punct:][:space:]])" "``{}`\\2" . -- "'word left
+ gsub "\"([^[:punct:][:space:]])" "``\\1" -- "word left
+
+escapeSingleQuotes =
+ gsub "('|`)(\"|``)" "`{}``" . -- '"word left
+ gsub "([^[:punct:][:space:]])`(s|S)" "\\1'\\2" . -- catch possessives
+ gsub "^'([^[:punct:][:space:]])" "`\\1" . -- 'word left
+ gsub "([[:space:]])'" "\\1`" . -- never right quote after space
+ gsub "([[:space:]])'([^[:punct:][:space:]])" "\\1`\\2" -- 'word left (leave possessives)
+
+escapeEllipses = gsub "\\.\\.\\.|\\. \\. \\." "\\ldots{}"
+
+escapeDashes = gsub "([0-9])-([0-9])" "\\1--\\2" .
+ gsub " -- " "---" .
+ gsub "([^[:punct:][:space:]])--([^[:punct:][:space:]])" "\\1---\\2"
+
+escapeSmart = escapeSingleQuotes . escapeDoubleQuotes . escapeDashes . escapeEllipses
+
+-- | Escape string for LaTeX (including smart quotes, dashes, ellipses)
+stringToLaTeX :: String -> String
+stringToLaTeX = escapeSmart . escapeGt . escapeLt . escapeBar . escapeHat .
+ escapeSpecial . fixBackslash . escapeBrackets . escapeBackslash
+
+-- | Remove all code elements from list of inline elements
+-- (because it's illegal to have a \\verb inside a command argument)
+deVerb :: [Inline] -> [Inline]
+deVerb [] = []
+deVerb ((Code str):rest) = (Str str):(deVerb rest)
+deVerb (other:rest) = other:(deVerb rest)
+
+-- | Convert Pandoc block element to LaTeX.
+blockToLaTeX :: [Block] -- ^ List of note blocks to use in resolving note refs
+ -> Block -- ^ Block to convert
+ -> String
+blockToLaTeX notes Blank = "\n"
+blockToLaTeX notes Null = ""
+blockToLaTeX notes (Plain lst) = inlineListToLaTeX notes lst ++ "\n"
+blockToLaTeX notes (Para lst) = (inlineListToLaTeX notes lst) ++ "\n\n"
+blockToLaTeX notes (BlockQuote lst) =
+ "\\begin{quote}\n" ++ (concatMap (blockToLaTeX notes) lst) ++ "\\end{quote}\n"
+blockToLaTeX notes (Note ref lst) = ""
+blockToLaTeX notes (Key _ _) = ""
+blockToLaTeX notes (CodeBlock str) = "\\begin{verbatim}\n" ++ str ++ "\\end{verbatim}\n"
+blockToLaTeX notes (RawHtml str) = ""
+blockToLaTeX notes (BulletList lst) =
+ "\\begin{itemize}\n" ++ (concatMap (listItemToLaTeX notes) lst) ++ "\\end{itemize}\n"
+blockToLaTeX notes (OrderedList lst) =
+ "\\begin{enumerate}\n" ++ (concatMap (listItemToLaTeX notes) lst) ++ "\\end{enumerate}\n"
+blockToLaTeX notes HorizontalRule = "\\begin{center}\\rule{3in}{0.4pt}\\end{center}\n\n"
+blockToLaTeX notes (Header level lst) =
+ if (level > 0) && (level <= 3) then
+ "\\" ++ (concat (replicate (level - 1) "sub")) ++ "section{" ++
+ (inlineListToLaTeX notes (deVerb lst)) ++ "}\n\n"
+ else
+ (inlineListToLaTeX notes lst) ++ "\n\n"
+listItemToLaTeX notes list = "\\item " ++ (concatMap (blockToLaTeX notes) list)
+
+-- | Convert list of inline elements to LaTeX.
+inlineListToLaTeX :: [Block] -- ^ List of note blocks to use in resolving note refs
+ -> [Inline] -- ^ Inlines to convert
+ -> String
+inlineListToLaTeX notes lst =
+ -- first, consolidate Str and Space for more effective smartquotes:
+ let lst' = consolidateList lst in
+ concatMap (inlineToLaTeX notes) lst'
+
+-- | Convert inline element to LaTeX
+inlineToLaTeX :: [Block] -- ^ List of note blocks to use in resolving note refs
+ -> Inline -- ^ Inline to convert
+ -> String
+inlineToLaTeX notes (Emph lst) = "\\emph{" ++ (inlineListToLaTeX notes (deVerb lst)) ++ "}"
+inlineToLaTeX notes (Strong lst) = "\\textbf{" ++ (inlineListToLaTeX notes (deVerb lst)) ++ "}"
+inlineToLaTeX notes (Code str) = "\\verb" ++ [chr] ++ stuffing ++ [chr]
+ where stuffing = str
+ chr = ((enumFromTo '!' '~') \\ stuffing) !! 0
+inlineToLaTeX notes (Str str) = stringToLaTeX str
+inlineToLaTeX notes (TeX str) = str
+inlineToLaTeX notes (HtmlInline str) = ""
+inlineToLaTeX notes (LineBreak) = "\\\\\n"
+inlineToLaTeX notes Space = " "
+inlineToLaTeX notes (Link text (Src src tit)) =
+ "\\href{" ++ src ++ "}{" ++ (inlineListToLaTeX notes (deVerb text)) ++ "}"
+inlineToLaTeX notes (Link text (Ref [])) = "[" ++ (inlineListToLaTeX notes text) ++ "]"
+inlineToLaTeX notes (Link text (Ref ref)) = "[" ++ (inlineListToLaTeX notes text) ++ "][" ++
+ (inlineListToLaTeX notes ref) ++ "]" -- this is what markdown does, for better or worse
+inlineToLaTeX notes (Image alternate (Src source tit)) = "\\includegraphics{" ++ source ++ "}"
+inlineToLaTeX notes (Image alternate (Ref [])) =
+ "![" ++ (inlineListToLaTeX notes alternate) ++ "]"
+inlineToLaTeX notes (Image alternate (Ref ref)) =
+ "![" ++ (inlineListToLaTeX notes alternate) ++ "][" ++ (inlineListToLaTeX notes ref) ++ "]"
+inlineToLaTeX [] (NoteRef ref) = ""
+inlineToLaTeX ((Note firstref firstblocks):rest) (NoteRef ref) =
+ if (firstref == ref) then
+ "\\footnote{" ++ (stripTrailingNewlines (concatMap (blockToLaTeX rest) firstblocks)) ++ "}"
+ else
+ inlineToLaTeX rest (NoteRef ref)
+