git.iko.soy/typeable/pandoc
on master help sign in
Added support to write natbib or biblatex citations in latex output.
commit48600fd5473d1a3c596c6ac8c29f1d7b17f1dc92 authorNathan Gass <gass@search.ch> dateMon 13 Dec 2010, 21:18 (16 years ago) parent1a4a0d0
5 files changed+166−18
src/Text/Pandoc.hs+1 src/Text/Pandoc/Shared.hs+10 src/Text/Pandoc/Writers/LaTeX.hs+113−5 src/pandoc.hs+24−13 templates/latex.template+18
src/Text/Pandoc.hs+1
@@ -97,6 +97,7 @@ module Text.Pandoc
9797 , WriterOptions (..)
9898 , HTMLSlideVariant (..)
9999 , HTMLMathMethod (..)
100+ , CiteMethod (..)
100101 , defaultWriterOptions
101102 -- * Rendering templates and default templates
102103 , module Text.Pandoc.Templates
src/Text/Pandoc/Shared.hs+10
@@ -66,6 +66,7 @@ module Text.Pandoc.Shared (
6666 headerShift,
6767 -- * Writer options
6868 HTMLMathMethod (..),
69+ CiteMethod (..),
6970 ObfuscationMethod (..),
7071 HTMLSlideVariant (..),
7172 WriterOptions (..),
@@ -472,6 +473,11 @@ data HTMLMathMethod = PlainMath
472473 | MathJax String -- url of MathJax.js
473474 deriving (Show, Read, Eq)
474475
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+
475481-- | Methods for obfuscating email addresses in HTML.
476482data ObfuscationMethod = NoObfuscation
477483 | ReferenceObfuscation
@@ -507,6 +513,8 @@ data WriterOptions = WriterOptions
507513 , writerIdentifierPrefix :: String -- ^ Prefix for section & note ids in HTML
508514 , writerSourceDirectory :: FilePath -- ^ Directory path of 1st source file
509515 , writerUserDataDir :: Maybe FilePath -- ^ Path of user data directory
516+ , writerCiteMethod :: CiteMethod -- ^ How to print cites
517+ , writerBiblioFile :: String -- ^ Biblio file to use for citations
510518 } deriving Show
511519
512520-- | Default writer options.
@@ -533,6 +541,8 @@ defaultWriterOptions =
533541 , writerIdentifierPrefix = ""
534542 , writerSourceDirectory = "."
535543 , writerUserDataDir = Nothing
544+ , writerCiteMethod = Citeproc
545+ , writerBiblioFile = ""
536546 }
537547
538548--
src/Text/Pandoc/Writers/LaTeX.hs+113−5
@@ -32,7 +32,7 @@ import Text.Pandoc.Definition
3232import Text.Pandoc.Shared
3333import Text.Pandoc.Templates
3434import Text.Printf ( printf )
35−import Data.List ( (\\), isSuffixOf, isPrefixOf, intersperse )
35+import Data.List ( (\\), isSuffixOf, isPrefixOf, intersperse, intercalate )
3636import Data.Char ( toLower )
3737import Control.Monad.State
3838import Text.PrettyPrint.HughesPJ hiding ( Str )
@@ -76,7 +76,16 @@ pandocToLaTeX options (Pandoc (Meta title authors date) blocks) = do
7676 body <- blockListToLaTeX blocks
7777 let main = render body
7878 st <- get
79− let context = writerVariables options ++
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+ _ -> []
88+ context = writerVariables options ++
8089 [ ("toc", if writerTableOfContents options then "yes" else "")
8190 , ("body", main)
8291 , ("title", titletext)
@@ -91,7 +100,8 @@ pandocToLaTeX options (Pandoc (Meta title authors date) blocks) = do
91100 [ ("url", "yes") | stUrl st ] ++
92101 [ ("numbersections", "yes") | writerNumberSections options ] ++
93102 [ ("lhs", "yes") | stLHS st ] ++
94− [ ("graphics", "yes") | stGraphics st ]
103+ [ ("graphics", "yes") | stGraphics st ] ++
104+ citecontext
95105 return $ if writerStandalone options
96106 then renderTemplate context template
97107 else main
@@ -298,8 +308,14 @@ inlineToLaTeX (Subscript lst) = do
298308 return $ inCmd "textsubscr" contents
299309inlineToLaTeX (SmallCaps lst) =
300310 inlineListToLaTeX (deVerb lst) >>= return . inCmd "textsc"
301−inlineToLaTeX (Cite _ lst) =
302− inlineListToLaTeX 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
317+ _ -> inlineListToLaTeX lst
318+
303319inlineToLaTeX (Code str) = do
304320 st <- get
305321 when (stInNote st) $ modify $ \s -> s{ stVerbInNote = True }
@@ -355,3 +371,95 @@ inlineToLaTeX (Note contents) = do
355371 let optNewline = "\\end{Verbatim}" `isSuffixOf` rawnote
356372 return $ text "\\footnote{" <>
357373 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
116116 , optIdentifierPrefix :: String
117117 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
118118 , optDataDir :: Maybe FilePath
119− , optBibliography :: [Reference]
119+ , optCiteMethod :: CiteMethod -- ^ Method to output cites
120+ , optBibliography :: [String]
120121 , optCslFile :: FilePath
121122 }
122123
@@ -154,6 +155,7 @@ defaultOpts = Opt
154155 , optIdentifierPrefix = ""
155156 , optIndentedCodeClasses = []
156157 , optDataDir = Nothing
158+ , optCiteMethod = Citeproc
157159 , optBibliography = []
158160 , optCslFile = ""
159161 }
@@ -453,14 +455,7 @@ options =
453455 "" -- "Print default template for FORMAT"
454456 , Option "" ["bibliography"]
455457 (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 =
463− optBibliography opt ++ refs } )
458+ (\arg opt -> return opt { optBibliography = (optBibliography opt) ++ [arg] })
464459 "FILENAME")
465460 ""
466461 , Option "" ["csl"]
@@ -468,6 +463,14 @@ options =
468463 (\arg opt -> return opt { optCslFile = arg })
469464 "FILENAME")
470465 ""
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"
471474 , Option "" ["data-dir"]
472475 (ReqArg
473476 (\arg opt -> return opt { optDataDir = Just arg })
@@ -618,8 +621,9 @@ main = do
618621 , optIdentifierPrefix = idPrefix
619622 , optIndentedCodeClasses = codeBlockClasses
620623 , optDataDir = mbDataDir
621− , optBibliography = refs
624+ , optBibliography = reffiles
622625 , optCslFile = cslfile
626+ , optCiteMethod = citeMethod
623627 } = opts
624628
625629 when dumpArgs $
@@ -693,6 +697,11 @@ main = do
693697 return $ ("mathml-script", s) : variables'
694698 _ -> return variables'
695699
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+
696705 let sourceDir = if null sources
697706 then "."
698707 else takeDirectory (head sources)
@@ -729,6 +738,8 @@ main = do
729738 writerSlideVariant = slideVariant,
730739 writerIncremental = incremental,
731740 writerXeTeX = xetex,
741+ writerCiteMethod = citeMethod,
742+ writerBiblioFile = head reffiles,
732743 writerIgnoreNotes = False,
733744 writerNumberSections = numberSections,
734745 writerSectionDivs = sectionDivs,
@@ -766,9 +777,8 @@ main = do
766777 let doc' = foldr ($) doc transforms
767778
768779 doc'' <- do
769− if null refs
770− then return doc'
771− else do
780+ if citeMethod == Citeproc && not (null refs)
781+ then do
772782 csldir <- getAppUserDataDirectory "csl"
773783 cslfile' <- if null cslfile
774784 then findDataFile datadir "default.csl"
@@ -781,6 +791,7 @@ main = do
781791 (replaceExtension cslfile "csl")
782792 csldir
783793 processBiblio cslfile' refs doc'
794+ else return doc'
784795
785796 writerOutput <- writer writerOptions doc''
786797
templates/latex.template+18
@@ -13,6 +13,16 @@ $else$
1313\usepackage[mathletters]{ucs}
1414\usepackage[utf8x]{inputenc}
1515$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$
1626$if(lhs)$
1727\usepackage{listings}
1828\lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{}
@@ -94,6 +104,14 @@ $if(toc)$
94104
95105$endif$
96106$body$
107+$if(biblio)$
108+$if(natbib)$
109+\bibliography{$biblio$}
110+$endif$
111+$endif$
112+$if(biblatex)$
113+\printbibliography
114+$endif$
97115$for(include-after)$
98116
99117$include-after$