@@ -20,7 +20,6 @@ module Text.Pandoc.Writers.LaTeX (
2020 ) where
2121import Control.Applicative ((<|>))
2222import Control.Monad.State.Strict
23−import Data.Monoid (Any(..))
2423import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isSpace,
2524 isPunctuation, ord)
2625import Data.List (foldl', intersperse, nubBy, (\\), uncons)
@@ -42,69 +41,14 @@ import Text.Pandoc.Options
4241import Text.DocLayout
4342import Text.Pandoc.Shared
4443import Text.Pandoc.Slides
45−import Text.Pandoc.Walk
44+import Text.Pandoc.Walk (query, walk, walkM)
45+import Text.Pandoc.Writers.LaTeX.Caption (getCaption)
46+import Text.Pandoc.Writers.LaTeX.Table (tableToLaTeX)
47+import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState (..), startingState)
4648import Text.Pandoc.Writers.Shared
4749import Text.Printf (printf)
4850import qualified Data.Text.Normalize as Normalize
4951
50−data WriterState =
51− WriterState { stInNote :: Bool -- true if we're in a note
52− , stInQuote :: Bool -- true if in a blockquote
53− , stExternalNotes :: Bool -- true if in context where
54− -- we need to store footnotes
55− , stInMinipage :: Bool -- true if in minipage
56− , stInHeading :: Bool -- true if in a section heading
57− , stInItem :: Bool -- true if in \item[..]
58− , stNotes :: [Doc Text] -- notes in a minipage
59− , stOLLevel :: Int -- level of ordered list nesting
60− , stOptions :: WriterOptions -- writer options, so they don't have to be parameter
61− , stVerbInNote :: Bool -- true if document has verbatim text in note
62− , stTable :: Bool -- true if document has a table
63− , stStrikeout :: Bool -- true if document has strikeout
64− , stUrl :: Bool -- true if document has visible URL link
65− , stGraphics :: Bool -- true if document contains images
66− , stLHS :: Bool -- true if document has literate haskell code
67− , stHasChapters :: Bool -- true if document has chapters
68− , stCsquotes :: Bool -- true if document uses csquotes
69− , stHighlighting :: Bool -- true if document has highlighted code
70− , stIncremental :: Bool -- true if beamer lists should be displayed bit by bit
71− , stInternalLinks :: [Text] -- list of internal link targets
72− , stBeamer :: Bool -- produce beamer
73− , stEmptyLine :: Bool -- true if no content on line
74− , stHasCslRefs :: Bool -- has a Div with class refs
75− , stIsFirstInDefinition :: Bool -- first block in a defn list
76− }
77−
78−startingState :: WriterOptions -> WriterState
79−startingState options = WriterState {
80− stInNote = False
81− , stInQuote = False
82− , stExternalNotes = False
83− , stInHeading = False
84− , stInMinipage = False
85− , stInItem = False
86− , stNotes = []
87− , stOLLevel = 1
88− , stOptions = options
89− , stVerbInNote = False
90− , stTable = False
91− , stStrikeout = False
92− , stUrl = False
93− , stGraphics = False
94− , stLHS = False
95− , stHasChapters = case writerTopLevelDivision options of
96− TopLevelPart -> True
97− TopLevelChapter -> True
98− _ -> False
99− , stCsquotes = False
100− , stHighlighting = False
101− , stIncremental = writerIncremental options
102− , stInternalLinks = []
103− , stBeamer = False
104− , stEmptyLine = True
105− , stHasCslRefs = False
106− , stIsFirstInDefinition = False }
107−
10852-- | Convert Pandoc to LaTeX.
10953writeLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m Text
11054writeLaTeX options document =
@@ -117,8 +61,6 @@ writeBeamer options document =
11761 evalStateT (pandocToLaTeX options document) $
11862 (startingState options){ stBeamer = True }
11963
120−type LW m = StateT WriterState m
121−
12264pandocToLaTeX :: PandocMonad m
12365 => WriterOptions -> Pandoc -> LW m Text
12466pandocToLaTeX options (Pandoc meta blocks) = do
@@ -573,7 +515,7 @@ blockToLaTeX (Plain lst) =
573515blockToLaTeX (Para [Image attr@(ident, _, _) txt (src,tgt)])
574516 | Just tit <- T.stripPrefix "fig:" tgt
575517 = do
576− (capt, captForLof, footnotes) <- getCaption True txt
518+ (capt, captForLof, footnotes) <- getCaption inlineListToLaTeX True txt
577519 lab <- labelFor ident
578520 let caption = "\\caption" <> captForLof <> braces capt <> lab
579521 img <- inlineToLaTeX (Image attr txt (src,tit))
@@ -774,181 +716,14 @@ blockToLaTeX (Header level (id',classes,_) lst) = do
774716 hdr <- sectionHeader classes id' level lst
775717 modify $ \s -> s{stInHeading = False}
776718 return hdr
777−blockToLaTeX (Table _ blkCapt specs thead tbody tfoot) = do
778− let (caption, aligns, widths, heads, rows) = toLegacyTable blkCapt specs thead tbody tfoot
779− -- simple tables have to have simple cells:
780− let isSimple [Plain _] = True
781− isSimple [Para _] = True
782− isSimple [] = True
783− isSimple _ = False
784− let widths' = if all (== 0) widths && not (all (all isSimple) rows)
785− then replicate (length aligns)
786− (1 / fromIntegral (length aligns))
787− else widths
788− (captionText, captForLof, captNotes) <- getCaption False caption
789− let toHeaders hs = do contents <- tableRowToLaTeX True aligns hs
790− return ("\\toprule" $$ contents $$ "\\midrule")
791− let removeNote (Note _) = Span ("", [], []) []
792− removeNote x = x
793− firsthead <- if isEmpty captionText || all null heads
794− then return empty
795− else ($$ text "\\endfirsthead") <$> toHeaders heads
796− head' <- if all null heads
797− then return "\\toprule"
798− -- avoid duplicate notes in head and firsthead:
799− else toHeaders (if isEmpty firsthead
800− then heads
801− else walk removeNote heads)
802− let capt = if isEmpty captionText
803− then empty
804− else "\\caption" <> captForLof <> braces captionText
805− <> "\\tabularnewline"
806− rows' <- mapM (tableRowToLaTeX False aligns) rows
807− let colDescriptors =
808− (if all (== 0) widths'
809− then hcat . map literal
810− else (\xs -> cr <> nest 2 (vcat $ map literal xs))) $
811− zipWith (toColDescriptor (length widths')) aligns widths'
812− modify $ \s -> s{ stTable = True }
813− notes <- notesToLaTeX <$> gets stNotes
814− return $ "\\begin{longtable}[]" <>
815− braces ("@{}" <> colDescriptors <> "@{}")
816− -- the @{} removes extra space at beginning and end
817− $$ capt
818− $$ firsthead
819− $$ head'
820− $$ "\\endhead"
821− $$ vcat rows'
822− $$ "\\bottomrule"
823− $$ "\\end{longtable}"
824− $$ captNotes
825− $$ notes
826−
827−getCaption :: PandocMonad m
828− => Bool -> [Inline] -> LW m (Doc Text, Doc Text, Doc Text)
829−getCaption externalNotes txt = do
830− oldExternalNotes <- gets stExternalNotes
831− modify $ \st -> st{ stExternalNotes = externalNotes, stNotes = [] }
832− capt <- inlineListToLaTeX txt
833− footnotes <- if externalNotes
834− then notesToLaTeX <$> gets stNotes
835− else return empty
836− modify $ \st -> st{ stExternalNotes = oldExternalNotes, stNotes = [] }
837− -- We can't have footnotes in the list of figures/tables, so remove them:
838− let getNote (Note _) = Any True
839− getNote _ = Any False
840− let hasNotes = getAny . query getNote
841− captForLof <- if hasNotes txt
842− then brackets <$> inlineListToLaTeX (walk deNote txt)
843− else return empty
844− return (capt, captForLof, footnotes)
845−
846−toColDescriptor :: Int -> Alignment -> Double -> Text
847−toColDescriptor _numcols align 0 =
848− case align of
849− AlignLeft -> "l"
850− AlignRight -> "r"
851− AlignCenter -> "c"
852− AlignDefault -> "l"
853−toColDescriptor numcols align width =
854− T.pack $ printf
855− ">{%s\\arraybackslash}p{(\\columnwidth - %d\\tabcolsep) * \\real{%0.2f}}"
856− align'
857− ((numcols - 1) * 2)
858− width
859− where
860− align' :: String
861− align' = case align of
862− AlignLeft -> "\\raggedright"
863− AlignRight -> "\\raggedleft"
864− AlignCenter -> "\\centering"
865− AlignDefault -> "\\raggedright"
719+blockToLaTeX (Table _ blkCapt specs thead tbodies tfoot) =
720+ tableToLaTeX inlineListToLaTeX blockListToLaTeX
721+ blkCapt specs thead tbodies tfoot
866722
867723blockListToLaTeX :: PandocMonad m => [Block] -> LW m (Doc Text)
868724blockListToLaTeX lst =
869725 vsep `fmap` mapM (\b -> setEmptyLine True >> blockToLaTeX b) lst
870726
871−tableRowToLaTeX :: PandocMonad m
872− => Bool
873− -> [Alignment]
874− -> [[Block]]
875− -> LW m (Doc Text)
876−tableRowToLaTeX header aligns cols = do
877− cells <- mapM (tableCellToLaTeX header) $ zip aligns cols
878− return $ hsep (intersperse "&" cells) <> " \\\\ \\addlinespace"
879−
880−-- For simple latex tables (without minipages or parboxes),
881−-- we need to go to some lengths to get line breaks working:
882−-- as LineBreak bs = \vtop{\hbox{\strut as}\hbox{\strut bs}}.
883−fixLineBreaks :: Block -> Block
884−fixLineBreaks (Para ils) = Para $ fixLineBreaks' ils
885−fixLineBreaks (Plain ils) = Plain $ fixLineBreaks' ils
886−fixLineBreaks x = x
887−
888−fixLineBreaks' :: [Inline] -> [Inline]
889−fixLineBreaks' ils = case splitBy (== LineBreak) ils of
890− [] -> []
891− [xs] -> xs
892− chunks -> RawInline "tex" "\\vtop{" :
893− concatMap tohbox chunks <>
894− [RawInline "tex" "}"]
895− where tohbox ys = RawInline "tex" "\\hbox{\\strut " : ys <>
896− [RawInline "tex" "}"]
897−
898−-- We also change display math to inline math, since display
899−-- math breaks in simple tables.
900−displayMathToInline :: Inline -> Inline
901−displayMathToInline (Math DisplayMath x) = Math InlineMath x
902−displayMathToInline x = x
903−
904−tableCellToLaTeX :: PandocMonad m
905− => Bool -> (Alignment, [Block])
906− -> LW m (Doc Text)
907−tableCellToLaTeX header (align, blocks) = do
908− beamer <- gets stBeamer
909− externalNotes <- gets stExternalNotes
910− inMinipage <- gets stInMinipage
911− -- See #5367 -- footnotehyper/footnote don't work in beamer,
912− -- so we need to produce the notes outside the table...
913− modify $ \st -> st{ stExternalNotes = beamer }
914− let isPlainOrPara Para{} = True
915− isPlainOrPara Plain{} = True
916− isPlainOrPara _ = False
917− result <-
918− if all isPlainOrPara blocks
919− then
920− blockListToLaTeX $ walk fixLineBreaks $ walk displayMathToInline blocks
921− else do
922− modify $ \st -> st{ stInMinipage = True }
923− cellContents <- blockListToLaTeX blocks
924− modify $ \st -> st{ stInMinipage = inMinipage }
925− let valign = text $ if header then "[b]" else "[t]"
926− let halign = case align of
927− AlignLeft -> "\\raggedright"
928− AlignRight -> "\\raggedleft"
929− AlignCenter -> "\\centering"
930− AlignDefault -> "\\raggedright"
931− return $ "\\begin{minipage}" <> valign <>
932− braces "\\linewidth" <> halign <> cr <>
933− cellContents <> cr <>
934− "\\end{minipage}"
935− modify $ \st -> st{ stExternalNotes = externalNotes }
936− return result
937−
938−
939−notesToLaTeX :: [Doc Text] -> Doc Text
940−notesToLaTeX [] = empty
941−notesToLaTeX ns = (case length ns of
942− n | n > 1 -> "\\addtocounter" <>
943− braces "footnote" <>
944− braces (text $ show $ 1 - n)
945− | otherwise -> empty)
946− $$
947− vcat (intersperse
948− ("\\addtocounter" <> braces "footnote" <> braces "1")
949− $ map (\x -> "\\footnotetext" <> braces x)
950− $ reverse ns)
951−
952727listItemToLaTeX :: PandocMonad m => [Block] -> LW m (Doc Text)
953728listItemToLaTeX lst
954729 -- we need to put some text before a header if it's the first
@@ -1081,7 +856,7 @@ mapAlignment a = case a of
1081856 "top-baseline" -> "t"
1082857 "bottom" -> "b"
1083858 "center" -> "c"
1084− _ -> a
859+ _ -> a
1085860
1086861wrapDiv :: PandocMonad m => Attr -> Doc Text -> LW m (Doc Text)
1087862wrapDiv (_,classes,kvs) t = do
@@ -1095,7 +870,7 @@ wrapDiv (_,classes,kvs) t = do
1095870 (lookup "totalwidth" kvs)
1096871 onlytextwidth = filter ("onlytextwidth" ==) classes
1097872 options = text $ T.unpack $ T.intercalate "," $
1098− valign : totalwidth ++ onlytextwidth
873+ valign : totalwidth ++ onlytextwidth
1099874 in inCmd "begin" "columns" <> brackets options
1100875 $$ contents
1101876 $$ inCmd "end" "columns"
@@ -1106,8 +881,8 @@ wrapDiv (_,classes,kvs) t = do
1106881 maybe ""
1107882 (brackets . text . T.unpack . mapAlignment)
1108883 (lookup "align" kvs)
1109− w = maybe "0.48" fromPct (lookup "width" kvs)
1110− in inCmd "begin" "column" <>
884+ w = maybe "0.48" fromPct (lookup "width" kvs)
885+ in inCmd "begin" "column" <>
1111886 valign <>
1112887 braces (literal w <> "\\textwidth")
1113888 $$ contents