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.hs106
1 files changed, 73 insertions, 33 deletions
diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs
index ae2f4e907..506edd182 100644
--- a/src/Text/Pandoc/Writers/LaTeX.hs
+++ b/src/Text/Pandoc/Writers/LaTeX.hs
@@ -1,7 +1,7 @@
{-# LANGUAGE OverloadedStrings, ScopedTypeVariables,
PatternGuards #-}
{-
-Copyright (C) 2006-2014 John MacFarlane <jgm@berkeley.edu>
+Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu>
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
@@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
{- |
Module : Text.Pandoc.Writers.LaTeX
- Copyright : Copyright (C) 2006-2014 John MacFarlane
+ Copyright : Copyright (C) 2006-2015 John MacFarlane
License : GNU GPL, version 2 or above
Maintainer : John MacFarlane <jgm@berkeley.edu>
@@ -38,12 +38,13 @@ import Text.Pandoc.Options
import Text.Pandoc.Templates
import Text.Printf ( printf )
import Network.URI ( isURI, unEscapeString )
-import Data.List ( (\\), isSuffixOf, isInfixOf, stripPrefix,
- isPrefixOf, intercalate, intersperse )
+import Data.List ( (\\), isInfixOf, stripPrefix, intercalate, intersperse )
import Data.Char ( toLower, isPunctuation, isAscii, isLetter, isDigit, ord )
import Data.Maybe ( fromMaybe )
+import Data.Aeson.Types ( (.:), parseMaybe, withObject )
import Control.Applicative ((<|>))
import Control.Monad.State
+import qualified Text.Parsec as P
import Text.Pandoc.Pretty
import Text.Pandoc.Slides
import Text.Pandoc.Highlighting (highlight, styleToLaTeX,
@@ -102,25 +103,33 @@ pandocToLaTeX options (Pandoc meta blocks) = do
modify $ \s -> s{ stInternalLinks = query isInternalLink blocks' }
let template = writerTemplate options
-- set stBook depending on documentclass
+ let colwidth = if writerWrapText options
+ then Just $ writerColumns options
+ else Nothing
+ metadata <- metaToJSON options
+ (fmap (render colwidth) . blockListToLaTeX)
+ (fmap (render colwidth) . inlineListToLaTeX)
+ meta
let bookClasses = ["memoir","book","report","scrreprt","scrbook"]
- case lookup "documentclass" (writerVariables options) of
+ let documentClass = case P.parse (do P.skipMany (P.satisfy (/='\\'))
+ P.string "\\documentclass"
+ P.skipMany (P.satisfy (/='{'))
+ P.char '{'
+ P.manyTill P.letter (P.char '}')) "template"
+ template of
+ Right r -> r
+ Left _ -> ""
+ case lookup "documentclass" (writerVariables options) `mplus`
+ parseMaybe (withObject "object" (.: "documentclass")) metadata of
Just x | x `elem` bookClasses -> modify $ \s -> s{stBook = True}
| otherwise -> return ()
- Nothing | any (\x -> "\\documentclass" `isPrefixOf` x &&
- (any (`isSuffixOf` x) bookClasses))
- (lines template) -> modify $ \s -> s{stBook = True}
+ Nothing | documentClass `elem` bookClasses
+ -> modify $ \s -> s{stBook = True}
| otherwise -> return ()
-- check for \usepackage...{csquotes}; if present, we'll use
-- \enquote{...} for smart quotes:
when ("{csquotes}" `isInfixOf` template) $
modify $ \s -> s{stCsquotes = True}
- let colwidth = if writerWrapText options
- then Just $ writerColumns options
- else Nothing
- metadata <- metaToJSON options
- (fmap (render colwidth) . blockListToLaTeX)
- (fmap (render colwidth) . inlineListToLaTeX)
- meta
let (blocks'', lastHeader) = if writerCiteMethod options == Citeproc then
(blocks', [])
else case last blocks' of
@@ -135,6 +144,11 @@ pandocToLaTeX options (Pandoc meta blocks) = do
st <- get
titleMeta <- stringToLaTeX TextString $ stringify $ docTitle meta
authorsMeta <- mapM (stringToLaTeX TextString . stringify) $ docAuthors meta
+ let (mainlang, otherlang) =
+ case (reverse . splitBy (==',') . filter (/=' ')) `fmap`
+ getField "lang" metadata of
+ Just (m:os) -> (m, reverse os)
+ _ -> ("", [])
let context = defField "toc" (writerTableOfContents options) $
defField "toc-depth" (show (writerTOCDepth options -
if stBook st
@@ -159,8 +173,8 @@ pandocToLaTeX options (Pandoc meta blocks) = do
defField "euro" (stUsesEuro st) $
defField "listings" (writerListings options || stLHS st) $
defField "beamer" (writerBeamer options) $
- defField "mainlang" (maybe "" (reverse . takeWhile (/=',') . reverse)
- (lookup "lang" $ writerVariables options)) $
+ defField "mainlang" mainlang $
+ defField "otherlang" otherlang $
(if stHighlighting st
then defField "highlighting-macros" (styleToLaTeX
$ writerHighlightStyle options )
@@ -206,7 +220,7 @@ stringToLaTeX ctx (x:xs) = do
'€' -> "\\euro{}" ++ rest
'{' -> "\\{" ++ rest
'}' -> "\\}" ++ rest
- '$' -> "\\$" ++ rest
+ '$' | not isUrl -> "\\$" ++ rest
'%' -> "\\%" ++ rest
'&' -> "\\&" ++ rest
'_' | not isUrl -> "\\_" ++ rest
@@ -240,7 +254,7 @@ toLabel z = go `fmap` stringToLaTeX URLString z
where go [] = ""
go (x:xs)
| (isLetter x || isDigit x) && isAscii x = x:go xs
- | elem x "-+=:;." = x:go xs
+ | elem x ("-+=:;." :: String) = x:go xs
| otherwise = "ux" ++ printf "%x" (ord x) ++ go xs
-- | Puts contents into LaTeX command.
@@ -272,10 +286,11 @@ elementToBeamer slideLevel (Sec lvl _num (ident,classes,kvs) tit elts)
let hasCode (Code _ _) = [True]
hasCode _ = []
opts <- gets stOptions
- let fragile = not $ null $ query hasCodeBlock elts ++
+ let fragile = "fragile" `elem` classes ||
+ not (null $ query hasCodeBlock elts ++
if writerListings opts
then query hasCode elts
- else []
+ else [])
let allowframebreaks = "allowframebreaks" `elem` classes
let optionslist = ["fragile" | fragile] ++
["allowframebreaks" | allowframebreaks]
@@ -311,7 +326,8 @@ blockToLaTeX (Div (identifier,classes,_) bs) = do
ref <- toLabel identifier
let linkAnchor = if null identifier
then empty
- else "\\hyperdef{}" <> braces (text ref) <> "{}"
+ else "\\hyperdef{}" <> braces (text ref) <>
+ braces ("\\label" <> braces (text ref))
contents <- blockListToLaTeX bs
if beamer && "notes" `elem` classes -- speaker notes
then return $ "\\note" <> braces contents
@@ -414,7 +430,7 @@ blockToLaTeX (BulletList lst) = do
let inc = if incremental then "[<+->]" else ""
items <- mapM listItemToLaTeX lst
let spacing = if isTightList lst
- then text "\\itemsep1pt\\parskip0pt\\parsep0pt"
+ then text "\\tightlist"
else empty
return $ text ("\\begin{itemize}" ++ inc) $$ spacing $$ vcat items $$
"\\end{itemize}"
@@ -449,7 +465,7 @@ blockToLaTeX (OrderedList (start, numstyle, numdelim) lst) = do
else "\\setcounter" <> braces enum <>
braces (text $ show $ start - 1)
let spacing = if isTightList lst
- then text "\\itemsep1pt\\parskip0pt\\parsep0pt"
+ then text "\\tightlist"
else empty
return $ text ("\\begin{enumerate}" ++ inc)
$$ stylecommand
@@ -463,7 +479,7 @@ blockToLaTeX (DefinitionList lst) = do
let inc = if incremental then "[<+->]" else ""
items <- mapM defListItemToLaTeX lst
let spacing = if all isTightList (map snd lst)
- then text "\\itemsep1pt\\parskip0pt\\parsep0pt"
+ then text "\\tightlist"
else empty
return $ text ("\\begin{description}" ++ inc) $$ spacing $$ vcat items $$
"\\end{description}"
@@ -545,10 +561,16 @@ fixLineBreaks' ils = case splitBy (== LineBreak) ils of
where tohbox ys = RawInline "tex" "\\hbox{\\strut " : ys ++
[RawInline "tex" "}"]
+-- We also change display math to inline math, since display
+-- math breaks in simple tables.
+displayMathToInline :: Inline -> Inline
+displayMathToInline (Math DisplayMath x) = Math InlineMath x
+displayMathToInline x = x
+
tableCellToLaTeX :: Bool -> (Double, Alignment, [Block])
-> State WriterState Doc
tableCellToLaTeX _ (0, _, blocks) =
- blockListToLaTeX $ walk fixLineBreaks blocks
+ blockListToLaTeX $ walk fixLineBreaks $ walk displayMathToInline blocks
tableCellToLaTeX header (width, align, blocks) = do
modify $ \st -> st{ stInMinipage = True, stNotes = [] }
cellContents <- blockListToLaTeX blocks
@@ -613,6 +635,7 @@ sectionHeader :: Bool -- True for unnumbered
sectionHeader unnumbered ref level lst = do
txt <- inlineListToLaTeX lst
lab <- text `fmap` toLabel ref
+ plain <- stringToLaTeX TextString $ foldl (++) "" $ map stringify lst
let noNote (Note _) = Str ""
noNote x = x
let lstNoNotes = walk noNote lst
@@ -625,7 +648,12 @@ sectionHeader unnumbered ref level lst = do
then return empty
else do
return $ brackets txtNoNotes
- let stuffing = star <> optional <> braces txt
+ let contents = if render Nothing txt == plain
+ then braces txt
+ else braces (text "\\texorpdfstring"
+ <> braces txt
+ <> braces (text plain))
+ let stuffing = star <> optional <> contents
book <- gets stBook
opts <- gets stOptions
let level' = if book || writerChapters opts then level - 1 else level
@@ -669,7 +697,7 @@ sectionHeader unnumbered ref level lst = do
inlineListToLaTeX :: [Inline] -- ^ Inlines to convert
-> State WriterState Doc
inlineListToLaTeX lst =
- mapM inlineToLaTeX (fixLineInitialSpaces lst)
+ mapM inlineToLaTeX (fixBreaks $ fixLineInitialSpaces lst)
>>= return . hcat
-- nonbreaking spaces (~) in LaTeX don't work after line breaks,
-- so we turn nbsps after hard breaks to \hspace commands.
@@ -681,6 +709,14 @@ inlineListToLaTeX lst =
fixNbsps s = let (ys,zs) = span (=='\160') s
in replicate (length ys) hspace ++ [Str zs]
hspace = RawInline "latex" "\\hspace*{0.333em}"
+ -- linebreaks after blank lines cause problems:
+ fixBreaks [] = []
+ fixBreaks ys@(LineBreak : LineBreak : _) =
+ case span (== LineBreak) ys of
+ (lbs, rest) -> RawInline "latex"
+ ("\\\\[" ++ show (length lbs) ++
+ "\\baselineskip]") : fixBreaks rest
+ fixBreaks (y:ys) = y : fixBreaks ys
isQuoted :: Inline -> Bool
isQuoted (Quoted _ _) = True
@@ -696,7 +732,8 @@ inlineToLaTeX (Span (id',classes,_) ils) = do
ref <- toLabel id'
let linkAnchor = if null id'
then empty
- else "\\hyperdef{}" <> braces (text ref) <> "{}"
+ else "\\hyperdef{}" <> braces (text ref) <>
+ braces ("\\label" <> braces (text ref))
fmap (linkAnchor <>)
((if noEmph then inCmd "textup" else id) .
(if noStrong then inCmd "textnormal" else id) .
@@ -730,10 +767,11 @@ inlineToLaTeX (Cite cits lst) = do
inlineToLaTeX (Code (_,classes,_) str) = do
opts <- gets stOptions
+ inHeading <- gets stInHeading
case () of
- _ | writerListings opts -> listingsCode
+ _ | writerListings opts && not inHeading -> listingsCode
| writerHighlight opts && not (null classes) -> highlightCode
- | otherwise -> rawCode
+ | otherwise -> rawCode
where listingsCode = do
inNote <- gets stInNote
when inNote $ modify $ \s -> s{ stVerbInNote = True }
@@ -746,8 +784,10 @@ inlineToLaTeX (Code (_,classes,_) str) = do
Nothing -> rawCode
Just h -> modify (\st -> st{ stHighlighting = True }) >>
return (text h)
- rawCode = liftM (text . (\s -> "\\texttt{" ++ s ++ "}"))
+ rawCode = liftM (text . (\s -> "\\texttt{" ++ escapeSpaces s ++ "}"))
$ stringToLaTeX CodeString str
+ where
+ escapeSpaces = concatMap (\c -> if c == ' ' then "\\ " else [c])
inlineToLaTeX (Quoted qt lst) = do
contents <- inlineListToLaTeX lst
csquotes <- liftM stCsquotes get
@@ -780,7 +820,7 @@ inlineToLaTeX (RawInline f str)
| f == Format "latex" || f == Format "tex"
= return $ text str
| otherwise = return empty
-inlineToLaTeX (LineBreak) = return "\\\\"
+inlineToLaTeX (LineBreak) = return $ "\\\\" <> cr
inlineToLaTeX Space = return space
inlineToLaTeX (Link txt ('#':ident, _)) = do
contents <- inlineListToLaTeX txt