{- Copyright (C) 2006 John MacFarlane This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Writers.LaTeX Copyright : Copyright (C) 2006 John MacFarlane License : GNU GPL, version 2 or above Maintainer : John MacFarlane Stability : alpha Portability : portable Conversion of 'Pandoc' format into 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 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{}" -- | Escape string for LaTeX stringToLaTeX :: String -> String stringToLaTeX = 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 ++ "\n\\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 = 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 (Quoted SingleQuote lst) = "`" ++ inlineListToLaTeX notes lst ++ "'" inlineToLaTeX notes (Quoted DoubleQuote lst) = "``" ++ inlineListToLaTeX notes lst ++ "''" inlineToLaTeX notes Apostrophe = "'" inlineToLaTeX notes EmDash = "---" inlineToLaTeX notes EnDash = "--" inlineToLaTeX notes Ellipses = "\\ldots{}" 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 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 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)