git.iko.soy/typeable/pandoc
on master help sign in
Added support to write natbib or biblatex citations in latex output.
48600fdNathan Gass
src/Text/Pandoc.hs+1
@@ -97,6 +97,7 @@ module Text.Pandoc
97 , WriterOptions (..)
97 , WriterOptions (..)
98 , HTMLSlideVariant (..)
98 , HTMLSlideVariant (..)
99 , HTMLMathMethod (..)
99 , HTMLMathMethod (..)
100+ , CiteMethod (..)
100 , defaultWriterOptions
101 , defaultWriterOptions
101 -- * Rendering templates and default templates
102 -- * Rendering templates and default templates
102 , module Text.Pandoc.Templates
103 , module Text.Pandoc.Templates
src/Text/Pandoc/Shared.hs+10
@@ -66,6 +66,7 @@ module Text.Pandoc.Shared (
66 headerShift,
66 headerShift,
67 -- * Writer options
67 -- * Writer options
68 HTMLMathMethod (..),
68 HTMLMathMethod (..),
69+ CiteMethod (..),
69 ObfuscationMethod (..),
70 ObfuscationMethod (..),
70 HTMLSlideVariant (..),
71 HTMLSlideVariant (..),
71 WriterOptions (..),
72 WriterOptions (..),
@@ -472,6 +473,11 @@ data HTMLMathMethod = PlainMath
472 | MathJax String -- url of MathJax.js
473 | MathJax String -- url of MathJax.js
473 deriving (Show, Read, Eq)
474 deriving (Show, Read, Eq)
474
475
476+data CiteMethod = Citeproc -- use citeproc to render them
477+ | Natbib -- output natbib cite commands
478+ | Biblatex -- output biblatex cite commands
479+ deriving (Show, Read, Eq)
480+
475-- | Methods for obfuscating email addresses in HTML.
481-- | Methods for obfuscating email addresses in HTML.
476data ObfuscationMethod = NoObfuscation
482data ObfuscationMethod = NoObfuscation
477 | ReferenceObfuscation
483 | ReferenceObfuscation
@@ -507,6 +513,8 @@ data WriterOptions = WriterOptions
507 , writerIdentifierPrefix :: String -- ^ Prefix for section & note ids in HTML
513 , writerIdentifierPrefix :: String -- ^ Prefix for section & note ids in HTML
508 , writerSourceDirectory :: FilePath -- ^ Directory path of 1st source file
514 , writerSourceDirectory :: FilePath -- ^ Directory path of 1st source file
509 , writerUserDataDir :: Maybe FilePath -- ^ Path of user data directory
515 , writerUserDataDir :: Maybe FilePath -- ^ Path of user data directory
516+ , writerCiteMethod :: CiteMethod -- ^ How to print cites
517+ , writerBiblioFile :: String -- ^ Biblio file to use for citations
510 } deriving Show
518 } deriving Show
511
519
512-- | Default writer options.
520-- | Default writer options.
@@ -533,6 +541,8 @@ defaultWriterOptions =
533 , writerIdentifierPrefix = ""
541 , writerIdentifierPrefix = ""
534 , writerSourceDirectory = "."
542 , writerSourceDirectory = "."
535 , writerUserDataDir = Nothing
543 , writerUserDataDir = Nothing
544+ , writerCiteMethod = Citeproc
545+ , writerBiblioFile = ""
536 }
546 }
537
547
538--
548--
src/Text/Pandoc/Writers/LaTeX.hs+113−5
@@ -32,7 +32,7 @@ import Text.Pandoc.Definition
32import Text.Pandoc.Shared
32import Text.Pandoc.Shared
33import Text.Pandoc.Templates
33import Text.Pandoc.Templates
34import Text.Printf ( printf )
34import Text.Printf ( printf )
35−import Data.List ( (\\), isSuffixOf, isPrefixOf, intersperse )
35+import Data.List ( (\\), isSuffixOf, isPrefixOf, intersperse, intercalate )
36import Data.Char ( toLower )
36import Data.Char ( toLower )
37import Control.Monad.State
37import Control.Monad.State
38import Text.PrettyPrint.HughesPJ hiding ( Str )
38import Text.PrettyPrint.HughesPJ hiding ( Str )
@@ -76,7 +76,16 @@ pandocToLaTeX options (Pandoc (Meta title authors date) blocks) = do
76 body <- blockListToLaTeX blocks
76 body <- blockListToLaTeX blocks
77 let main = render body
77 let main = render body
78 st <- get
78 st <- get
79+ let biblio = takeWhile ((/=) '.') $ writerBiblioFile options
80+ citecontext = case writerCiteMethod options of
81+ Natbib -> [ ("biblio", biblio)
82+ , ("natbib", "yes")
83+ ]
84+ Biblatex -> [ ("biblio", biblio)
85+ , ("biblatex", "yes")
86+ ]
87+ _ -> []
79− let context = writerVariables options ++
88+ context = writerVariables options ++
80 [ ("toc", if writerTableOfContents options then "yes" else "")
89 [ ("toc", if writerTableOfContents options then "yes" else "")
81 , ("body", main)
90 , ("body", main)
82 , ("title", titletext)
91 , ("title", titletext)
@@ -91,7 +100,8 @@ pandocToLaTeX options (Pandoc (Meta title authors date) blocks) = do
91 [ ("url", "yes") | stUrl st ] ++
100 [ ("url", "yes") | stUrl st ] ++
92 [ ("numbersections", "yes") | writerNumberSections options ] ++
101 [ ("numbersections", "yes") | writerNumberSections options ] ++
93 [ ("lhs", "yes") | stLHS st ] ++
102 [ ("lhs", "yes") | stLHS st ] ++
94− [ ("graphics", "yes") | stGraphics st ]
103+ [ ("graphics", "yes") | stGraphics st ] ++
104+ citecontext
95 return $ if writerStandalone options
105 return $ if writerStandalone options
96 then renderTemplate context template
106 then renderTemplate context template
97 else main
107 else main
@@ -298,8 +308,14 @@ inlineToLaTeX (Subscript lst) = do
298 return $ inCmd "textsubscr" contents
308 return $ inCmd "textsubscr" contents
299inlineToLaTeX (SmallCaps lst) =
309inlineToLaTeX (SmallCaps lst) =
300 inlineListToLaTeX (deVerb lst) >>= return . inCmd "textsc"
310 inlineListToLaTeX (deVerb lst) >>= return . inCmd "textsc"
301−inlineToLaTeX (Cite _ lst) =
311+inlineToLaTeX (Cite cits lst) = do
312+ st <- get
313+ let opts = stOptions st
314+ case writerCiteMethod opts of
315+ Natbib -> citationsToNatbib cits
316+ Biblatex -> citationsToBiblatex cits
302− inlineListToLaTeX lst
317+ _ -> inlineListToLaTeX lst
318+
303inlineToLaTeX (Code str) = do
319inlineToLaTeX (Code str) = do
304 st <- get
320 st <- get
305 when (stInNote st) $ modify $ \s -> s{ stVerbInNote = True }
321 when (stInNote st) $ modify $ \s -> s{ stVerbInNote = True }
@@ -355,3 +371,95 @@ inlineToLaTeX (Note contents) = do
355 let optNewline = "\\end{Verbatim}" `isSuffixOf` rawnote
371 let optNewline = "\\end{Verbatim}" `isSuffixOf` rawnote
356 return $ text "\\footnote{" <>
372 return $ text "\\footnote{" <>
357 text rawnote <> (if optNewline then char '\n' else empty) <> char '}'
373 text rawnote <> (if optNewline then char '\n' else empty) <> char '}'
374+
375+
376+citationsToNatbib :: [Citation] -> State WriterState Doc
377+citationsToNatbib (one:[])
378+ = citeCommand c p s k
379+ where
380+ Citation { citationId = k
381+ , citationPrefix = p
382+ , citationSuffix = s
383+ , citationMode = m
384+ }
385+ = one
386+ c = case m of
387+ AuthorInText -> "citet"
388+ SuppressAuthor -> "citeyearpar"
389+ NormalCitation -> "citep"
390+
391+citationsToNatbib cits
392+ | noPrefix (tail cits) && noSuffix (init cits) && ismode NormalCitation cits
393+ = citeCommand "citep" p s ks
394+ where
395+ noPrefix = and . map (null . citationPrefix)
396+ noSuffix = and . map (null . citationSuffix)
397+ ismode m = and . map (((==) m) . citationMode)
398+ p = citationPrefix $ head $ cits
399+ s = citationSuffix $ last $ cits
400+ ks = intercalate ", " $ map citationId cits
401+
402+citationsToNatbib (c:cs) | citationMode c == AuthorInText = do
403+ author <- citeCommand "citeauthor" [] [] (citationId c)
404+ cits <- citationsToNatbib (c { citationMode = SuppressAuthor } : cs)
405+ return $ author <+> cits
406+
407+citationsToNatbib cits = do
408+ cits' <- mapM convertOne cits
409+ return $ text "\\citetext{" <> foldl combineTwo empty cits' <> text "}"
410+ where
411+ combineTwo a b | isEmpty a = b
412+ | otherwise = a <> text "; " <> b
413+ convertOne Citation { citationId = k
414+ , citationPrefix = p
415+ , citationSuffix = s
416+ , citationMode = m
417+ }
418+ = case m of
419+ AuthorInText -> citeCommand "citealt" p s k
420+ SuppressAuthor -> citeCommand "citeyear" p s k
421+ NormalCitation -> citeCommand "citealp" p s k
422+
423+citeCommand :: String -> [Inline] -> [Inline] -> String -> State WriterState Doc
424+citeCommand c p s k = do
425+ args <- citeArguments p s k
426+ return $ text ("\\" ++ c) <> args
427+
428+citeArguments :: [Inline] -> [Inline] -> String -> State WriterState Doc
429+citeArguments p s k = do
430+ pdoc <- inlineListToLaTeX p
431+ sdoc <- inlineListToLaTeX s
432+ let optargs = case (isEmpty pdoc, isEmpty sdoc) of
433+ (True, True ) -> empty
434+ (True, False) -> brackets sdoc
435+ (_ , _ ) -> brackets pdoc <> brackets sdoc
436+ return $ optargs <> braces (text k)
437+
438+citationsToBiblatex :: [Citation] -> State WriterState Doc
439+citationsToBiblatex (one:[])
440+ = citeCommand cmd p s k
441+ where
442+ Citation { citationId = k
443+ , citationPrefix = p
444+ , citationSuffix = s
445+ , citationMode = m
446+ } = one
447+ cmd = case m of
448+ SuppressAuthor -> "autocite*"
449+ AuthorInText -> "textcite"
450+ NormalCitation -> "autocite"
451+
452+citationsToBiblatex (c:cs) = do
453+ args <- mapM convertOne (c:cs)
454+ return $ text cmd <> foldl (<>) empty args
455+ where
456+ cmd = case citationMode c of
457+ AuthorInText -> "\\textcites"
458+ _ -> "\\autocites"
459+ convertOne Citation { citationId = k
460+ , citationPrefix = p
461+ , citationSuffix = s
462+ }
463+ = citeArguments p s k
464+
465+citationsToBiblatex _ = return empty
src/pandoc.hs+24−13
@@ -116,7 +116,8 @@ data Opt = Opt
116 , optIdentifierPrefix :: String
116 , optIdentifierPrefix :: String
117 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
117 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
118 , optDataDir :: Maybe FilePath
118 , optDataDir :: Maybe FilePath
119+ , optCiteMethod :: CiteMethod -- ^ Method to output cites
119− , optBibliography :: [Reference]
120+ , optBibliography :: [String]
120 , optCslFile :: FilePath
121 , optCslFile :: FilePath
121 }
122 }
122
123
@@ -154,6 +155,7 @@ defaultOpts = Opt
154 , optIdentifierPrefix = ""
155 , optIdentifierPrefix = ""
155 , optIndentedCodeClasses = []
156 , optIndentedCodeClasses = []
156 , optDataDir = Nothing
157 , optDataDir = Nothing
158+ , optCiteMethod = Citeproc
157 , optBibliography = []
159 , optBibliography = []
158 , optCslFile = ""
160 , optCslFile = ""
159 }
161 }
@@ -453,14 +455,7 @@ options =
453 "" -- "Print default template for FORMAT"
455 "" -- "Print default template for FORMAT"
454 , Option "" ["bibliography"]
456 , Option "" ["bibliography"]
455 (ReqArg
457 (ReqArg
456− (\arg opt -> do
457− refs <- catch (readBiblioFile arg) $ \e -> do
458− UTF8.hPutStrLn stderr $
459− "Error reading bibliography `" ++ arg ++ "'"
460− UTF8.hPutStrLn stderr $ show e
461− exitWith (ExitFailure 23)
462− return opt { optBibliography =
458+ (\arg opt -> return opt { optBibliography = (optBibliography opt) ++ [arg] })
463− optBibliography opt ++ refs } )
464 "FILENAME")
459 "FILENAME")
465 ""
460 ""
466 , Option "" ["csl"]
461 , Option "" ["csl"]
@@ -468,6 +463,14 @@ options =
468 (\arg opt -> return opt { optCslFile = arg })
463 (\arg opt -> return opt { optCslFile = arg })
469 "FILENAME")
464 "FILENAME")
470 ""
465 ""
466+ , Option "" ["natbib"]
467+ (NoArg
468+ (\opt -> return opt { optCiteMethod = Natbib }))
469+ "" -- "Use natbib cite commands in LaTeX output"
470+ , Option "" ["biblatex"]
471+ (NoArg
472+ (\opt -> return opt { optCiteMethod = Biblatex }))
473+ "" -- "Use biblatex cite commands in LaTeX output"
471 , Option "" ["data-dir"]
474 , Option "" ["data-dir"]
472 (ReqArg
475 (ReqArg
473 (\arg opt -> return opt { optDataDir = Just arg })
476 (\arg opt -> return opt { optDataDir = Just arg })
@@ -618,8 +621,9 @@ main = do
618 , optIdentifierPrefix = idPrefix
621 , optIdentifierPrefix = idPrefix
619 , optIndentedCodeClasses = codeBlockClasses
622 , optIndentedCodeClasses = codeBlockClasses
620 , optDataDir = mbDataDir
623 , optDataDir = mbDataDir
621− , optBibliography = refs
624+ , optBibliography = reffiles
622 , optCslFile = cslfile
625 , optCslFile = cslfile
626+ , optCiteMethod = citeMethod
623 } = opts
627 } = opts
624
628
625 when dumpArgs $
629 when dumpArgs $
@@ -693,6 +697,11 @@ main = do
693 return $ ("mathml-script", s) : variables'
697 return $ ("mathml-script", s) : variables'
694 _ -> return variables'
698 _ -> return variables'
695
699
700+ refs <- mapM (\f -> catch (readBiblioFile f) $ \e -> do
701+ UTF8.hPutStrLn stderr $ "Error reading bibliography `" ++ f ++ "'"
702+ UTF8.hPutStrLn stderr $ show e
703+ exitWith (ExitFailure 23)) reffiles >>= \rs -> return $ concat rs
704+
696 let sourceDir = if null sources
705 let sourceDir = if null sources
697 then "."
706 then "."
698 else takeDirectory (head sources)
707 else takeDirectory (head sources)
@@ -729,6 +738,8 @@ main = do
729 writerSlideVariant = slideVariant,
738 writerSlideVariant = slideVariant,
730 writerIncremental = incremental,
739 writerIncremental = incremental,
731 writerXeTeX = xetex,
740 writerXeTeX = xetex,
741+ writerCiteMethod = citeMethod,
742+ writerBiblioFile = head reffiles,
732 writerIgnoreNotes = False,
743 writerIgnoreNotes = False,
733 writerNumberSections = numberSections,
744 writerNumberSections = numberSections,
734 writerSectionDivs = sectionDivs,
745 writerSectionDivs = sectionDivs,
@@ -766,9 +777,8 @@ main = do
766 let doc' = foldr ($) doc transforms
777 let doc' = foldr ($) doc transforms
767
778
768 doc'' <- do
779 doc'' <- do
769− if null refs
780+ if citeMethod == Citeproc && not (null refs)
770− then return doc'
781+ then do
771− else do
772 csldir <- getAppUserDataDirectory "csl"
782 csldir <- getAppUserDataDirectory "csl"
773 cslfile' <- if null cslfile
783 cslfile' <- if null cslfile
774 then findDataFile datadir "default.csl"
784 then findDataFile datadir "default.csl"
@@ -781,6 +791,7 @@ main = do
781 (replaceExtension cslfile "csl")
791 (replaceExtension cslfile "csl")
782 csldir
792 csldir
783 processBiblio cslfile' refs doc'
793 processBiblio cslfile' refs doc'
794+ else return doc'
784
795
785 writerOutput <- writer writerOptions doc''
796 writerOutput <- writer writerOptions doc''
786
797
templates/latex.template+18
@@ -13,6 +13,16 @@ $else$
13\usepackage[mathletters]{ucs}
13\usepackage[mathletters]{ucs}
14\usepackage[utf8x]{inputenc}
14\usepackage[utf8x]{inputenc}
15$endif$
15$endif$
16+$if(natbib)$
17+\usepackage{natbib}
18+\bibliographystyle{plainnat}
19+$endif$
20+$if(biblatex)$
21+\usepackage{biblatex}
22+$if(biblio)$
23+\bibliography{$biblio$}
24+$endif$
25+$endif$
16$if(lhs)$
26$if(lhs)$
17\usepackage{listings}
27\usepackage{listings}
18\lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{}
28\lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{}
@@ -94,6 +104,14 @@ $if(toc)$
94
104
95$endif$
105$endif$
96$body$
106$body$
107+$if(biblio)$
108+$if(natbib)$
109+\bibliography{$biblio$}
110+$endif$
111+$endif$
112+$if(biblatex)$
113+\printbibliography
114+$endif$
97$for(include-after)$
115$for(include-after)$
98
116
99$include-after$
117$include-after$