git.iko.soy/typeable/pandoc
on master help sign in
LaTeX writer: extract table handling into separate module.
commitccd235e31ff00c4741ba52552ba58669f700bbdc authorAlbert Krewinkel <albert@zeitkraut.de> dateSat 12 Dec 2020, 16:45 (6 years ago) parentfcd0658
6 files changed+359−237
pandoc.cabal+4 src/Text/Pandoc/Writers/LaTeX.hs+12−237 src/Text/Pandoc/Writers/LaTeX/Caption.hs+48 src/Text/Pandoc/Writers/LaTeX/Notes.hs+34 src/Text/Pandoc/Writers/LaTeX/Table.hs+181 src/Text/Pandoc/Writers/LaTeX/Types.hs+80
pandoc.cabal+4
@@ -630,6 +630,10 @@ library
630630 Text.Pandoc.Writers.Docx.StyleMap,
631631 Text.Pandoc.Writers.JATS.Table,
632632 Text.Pandoc.Writers.JATS.Types,
633+ Text.Pandoc.Writers.LaTeX.Caption,
634+ Text.Pandoc.Writers.LaTeX.Notes,
635+ Text.Pandoc.Writers.LaTeX.Table,
636+ Text.Pandoc.Writers.LaTeX.Types,
633637 Text.Pandoc.Writers.Roff,
634638 Text.Pandoc.Writers.Powerpoint.Presentation,
635639 Text.Pandoc.Writers.Powerpoint.Output,
src/Text/Pandoc/Writers/LaTeX.hs+12−237
@@ -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
src/Text/Pandoc/Writers/LaTeX/Caption.hsnew file+48
@@ -0,0 +1,48 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{- |
3+ Module : Text.Pandoc.Writers.LaTeX.Caption
4+ Copyright : Copyright (C) 2006-2020 John MacFarlane
5+ License : GNU GPL, version 2 or above
6+
7+ Maintainer : John MacFarlane <jgm@berkeley.edu>
8+ Stability : alpha
9+ Portability : portable
10+
11+Write figure or table captions as LaTeX.
12+-}
13+module Text.Pandoc.Writers.LaTeX.Caption
14+ ( getCaption
15+ ) where
16+
17+import Control.Monad.State.Strict
18+import Data.Monoid (Any(..))
19+import Data.Text (Text)
20+import Text.Pandoc.Class.PandocMonad (PandocMonad)
21+import Text.Pandoc.Definition
22+import Text.DocLayout (Doc, brackets, empty)
23+import Text.Pandoc.Shared
24+import Text.Pandoc.Walk
25+import Text.Pandoc.Writers.LaTeX.Notes (notesToLaTeX)
26+import Text.Pandoc.Writers.LaTeX.Types
27+ ( LW, WriterState (stExternalNotes, stNotes) )
28+
29+getCaption :: PandocMonad m
30+ => ([Inline] -> LW m (Doc Text))
31+ -> Bool -> [Inline]
32+ -> LW m (Doc Text, Doc Text, Doc Text)
33+getCaption inlineListToLaTeX externalNotes txt = do
34+ oldExternalNotes <- gets stExternalNotes
35+ modify $ \st -> st{ stExternalNotes = externalNotes, stNotes = [] }
36+ capt <- inlineListToLaTeX txt
37+ footnotes <- if externalNotes
38+ then notesToLaTeX <$> gets stNotes
39+ else return empty
40+ modify $ \st -> st{ stExternalNotes = oldExternalNotes, stNotes = [] }
41+ -- We can't have footnotes in the list of figures/tables, so remove them:
42+ let getNote (Note _) = Any True
43+ getNote _ = Any False
44+ let hasNotes = getAny . query getNote
45+ captForLof <- if hasNotes txt
46+ then brackets <$> inlineListToLaTeX (walk deNote txt)
47+ else return empty
48+ return (capt, captForLof, footnotes)
src/Text/Pandoc/Writers/LaTeX/Notes.hsnew file+34
@@ -0,0 +1,34 @@
1+{-# LANGUAGE LambdaCase #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{- |
4+ Module : Text.Pandoc.Writers.LaTeX.Notes
5+ Copyright : Copyright (C) 2006-2020 John MacFarlane
6+ License : GNU GPL, version 2 or above
7+
8+ Maintainer : John MacFarlane <jgm@berkeley.edu>
9+ Stability : alpha
10+ Portability : portable
11+
12+Output tables as LaTeX.
13+-}
14+module Text.Pandoc.Writers.LaTeX.Notes
15+ ( notesToLaTeX
16+ ) where
17+
18+import Data.List (intersperse)
19+import Text.DocLayout ( Doc, braces, empty, text, vcat, ($$))
20+import Data.Text (Text)
21+
22+notesToLaTeX :: [Doc Text] -> Doc Text
23+notesToLaTeX = \case
24+ [] -> empty
25+ ns -> (case length ns of
26+ n | n > 1 -> "\\addtocounter" <>
27+ braces "footnote" <>
28+ braces (text $ show $ 1 - n)
29+ | otherwise -> empty)
30+ $$
31+ vcat (intersperse
32+ ("\\addtocounter" <> braces "footnote" <> braces "1")
33+ $ map (\x -> "\\footnotetext" <> braces x)
34+ $ reverse ns)
src/Text/Pandoc/Writers/LaTeX/Table.hsnew file+181
@@ -0,0 +1,181 @@
1+{-# LANGUAGE LambdaCase #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{- |
4+ Module : Text.Pandoc.Writers.LaTeX.Table
5+ Copyright : Copyright (C) 2006-2020 John MacFarlane
6+ License : GNU GPL, version 2 or above
7+
8+ Maintainer : John MacFarlane <jgm@berkeley.edu>
9+ Stability : alpha
10+ Portability : portable
11+
12+Output LaTeX formatted tables.
13+-}
14+module Text.Pandoc.Writers.LaTeX.Table
15+ ( tableToLaTeX
16+ ) where
17+import Control.Monad.State.Strict
18+import Data.List (intersperse)
19+import Data.Text (Text)
20+import qualified Data.Text as T
21+import Text.Pandoc.Class.PandocMonad (PandocMonad)
22+import Text.Pandoc.Definition
23+import Text.DocLayout
24+ ( Doc, braces, cr, empty, hcat, hsep, isEmpty, literal, nest
25+ , text, vcat, ($$) )
26+import Text.Pandoc.Shared (splitBy)
27+import Text.Pandoc.Walk (walk)
28+import Text.Pandoc.Writers.Shared (toLegacyTable)
29+import Text.Pandoc.Writers.LaTeX.Caption (getCaption)
30+import Text.Pandoc.Writers.LaTeX.Notes (notesToLaTeX)
31+import Text.Pandoc.Writers.LaTeX.Types
32+ ( LW, WriterState (stBeamer, stExternalNotes, stInMinipage, stNotes, stTable) )
33+import Text.Printf (printf)
34+
35+tableToLaTeX :: PandocMonad m
36+ => ([Inline] -> LW m (Doc Text))
37+ -> ([Block] -> LW m (Doc Text))
38+ -> Caption -> [ColSpec] -> TableHead -> [TableBody] -> TableFoot
39+ -> LW m (Doc Text)
40+tableToLaTeX inlnsToLaTeX blksToLaTeX blkCapt specs thead tbody tfoot = do
41+ let (caption, aligns, widths, heads, rows) =
42+ toLegacyTable blkCapt specs thead tbody tfoot
43+ -- simple tables have to have simple cells:
44+ let isSimple = \case
45+ [Plain _] -> True
46+ [Para _] -> True
47+ [] -> True
48+ _ -> False
49+ let widths' = if all (== 0) widths && not (all (all isSimple) rows)
50+ then replicate (length aligns)
51+ (1 / fromIntegral (length aligns))
52+ else widths
53+ (captionText, captForLof, captNotes) <- getCaption inlnsToLaTeX False caption
54+ let toHeaders hs = do contents <- tableRowToLaTeX blksToLaTeX True aligns hs
55+ return ("\\toprule" $$ contents $$ "\\midrule")
56+ let removeNote (Note _) = Span ("", [], []) []
57+ removeNote x = x
58+ firsthead <- if isEmpty captionText || all null heads
59+ then return empty
60+ else ($$ text "\\endfirsthead") <$> toHeaders heads
61+ head' <- if all null heads
62+ then return "\\toprule"
63+ -- avoid duplicate notes in head and firsthead:
64+ else toHeaders (if isEmpty firsthead
65+ then heads
66+ else walk removeNote heads)
67+ let capt = if isEmpty captionText
68+ then empty
69+ else "\\caption" <> captForLof <> braces captionText
70+ <> "\\tabularnewline"
71+ rows' <- mapM (tableRowToLaTeX blksToLaTeX False aligns) rows
72+ let colDescriptors =
73+ (if all (== 0) widths'
74+ then hcat . map literal
75+ else (\xs -> cr <> nest 2 (vcat $ map literal xs))) $
76+ zipWith (toColDescriptor (length widths')) aligns widths'
77+ modify $ \s -> s{ stTable = True }
78+ notes <- notesToLaTeX <$> gets stNotes
79+ return $ "\\begin{longtable}[]" <>
80+ braces ("@{}" <> colDescriptors <> "@{}")
81+ -- the @{} removes extra space at beginning and end
82+ $$ capt
83+ $$ firsthead
84+ $$ head'
85+ $$ "\\endhead"
86+ $$ vcat rows'
87+ $$ "\\bottomrule"
88+ $$ "\\end{longtable}"
89+ $$ captNotes
90+ $$ notes
91+
92+toColDescriptor :: Int -> Alignment -> Double -> Text
93+toColDescriptor _numcols align 0 =
94+ case align of
95+ AlignLeft -> "l"
96+ AlignRight -> "r"
97+ AlignCenter -> "c"
98+ AlignDefault -> "l"
99+toColDescriptor numcols align width =
100+ T.pack $ printf
101+ ">{%s\\arraybackslash}p{(\\columnwidth - %d\\tabcolsep) * \\real{%0.2f}}"
102+ align'
103+ ((numcols - 1) * 2)
104+ width
105+ where
106+ align' :: String
107+ align' = case align of
108+ AlignLeft -> "\\raggedright"
109+ AlignRight -> "\\raggedleft"
110+ AlignCenter -> "\\centering"
111+ AlignDefault -> "\\raggedright"
112+
113+tableRowToLaTeX :: PandocMonad m
114+ => ([Block] -> LW m (Doc Text))
115+ -> Bool
116+ -> [Alignment]
117+ -> [[Block]]
118+ -> LW m (Doc Text)
119+tableRowToLaTeX blockListToLaTeX header aligns cols = do
120+ cells <- mapM (tableCellToLaTeX blockListToLaTeX header) $ zip aligns cols
121+ return $ hsep (intersperse "&" cells) <> " \\\\ \\addlinespace"
122+
123+-- For simple latex tables (without minipages or parboxes),
124+-- we need to go to some lengths to get line breaks working:
125+-- as LineBreak bs = \vtop{\hbox{\strut as}\hbox{\strut bs}}.
126+fixLineBreaks :: Block -> Block
127+fixLineBreaks (Para ils) = Para $ fixLineBreaks' ils
128+fixLineBreaks (Plain ils) = Plain $ fixLineBreaks' ils
129+fixLineBreaks x = x
130+
131+fixLineBreaks' :: [Inline] -> [Inline]
132+fixLineBreaks' ils = case splitBy (== LineBreak) ils of
133+ [] -> []
134+ [xs] -> xs
135+ chunks -> RawInline "tex" "\\vtop{" :
136+ concatMap tohbox chunks <>
137+ [RawInline "tex" "}"]
138+ where tohbox ys = RawInline "tex" "\\hbox{\\strut " : ys <>
139+ [RawInline "tex" "}"]
140+
141+-- We also change display math to inline math, since display
142+-- math breaks in simple tables.
143+displayMathToInline :: Inline -> Inline
144+displayMathToInline (Math DisplayMath x) = Math InlineMath x
145+displayMathToInline x = x
146+
147+tableCellToLaTeX :: PandocMonad m
148+ => ([Block] -> LW m (Doc Text))
149+ -> Bool -> (Alignment, [Block])
150+ -> LW m (Doc Text)
151+tableCellToLaTeX blockListToLaTeX header (align, blocks) = do
152+ beamer <- gets stBeamer
153+ externalNotes <- gets stExternalNotes
154+ inMinipage <- gets stInMinipage
155+ -- See #5367 -- footnotehyper/footnote don't work in beamer,
156+ -- so we need to produce the notes outside the table...
157+ modify $ \st -> st{ stExternalNotes = beamer }
158+ let isPlainOrPara = \case
159+ Para{} -> True
160+ Plain{} -> True
161+ _ -> False
162+ result <-
163+ if all isPlainOrPara blocks
164+ then
165+ blockListToLaTeX $ walk fixLineBreaks $ walk displayMathToInline blocks
166+ else do
167+ modify $ \st -> st{ stInMinipage = True }
168+ cellContents <- blockListToLaTeX blocks
169+ modify $ \st -> st{ stInMinipage = inMinipage }
170+ let valign = text $ if header then "[b]" else "[t]"
171+ let halign = case align of
172+ AlignLeft -> "\\raggedright"
173+ AlignRight -> "\\raggedleft"
174+ AlignCenter -> "\\centering"
175+ AlignDefault -> "\\raggedright"
176+ return $ "\\begin{minipage}" <> valign <>
177+ braces "\\linewidth" <> halign <> cr <>
178+ cellContents <> cr <>
179+ "\\end{minipage}"
180+ modify $ \st -> st{ stExternalNotes = externalNotes }
181+ return result
src/Text/Pandoc/Writers/LaTeX/Types.hsnew file+80
@@ -0,0 +1,80 @@
1+module Text.Pandoc.Writers.LaTeX.Types
2+ ( LW
3+ , WriterState (..)
4+ , startingState
5+ ) where
6+
7+import Control.Monad.State.Strict (StateT)
8+import Data.Text (Text)
9+import Text.DocLayout (Doc)
10+import Text.Pandoc.Options
11+ ( WriterOptions (writerIncremental, writerTopLevelDivision)
12+ , TopLevelDivision (..)
13+ )
14+
15+-- | LaTeX writer type. The type constructor @m@ will typically be an
16+-- instance of PandocMonad.
17+type LW m = StateT WriterState m
18+
19+data WriterState =
20+ WriterState
21+ { stInNote :: Bool -- ^ true if we're in a note
22+ , stInQuote :: Bool -- ^ true if in a blockquote
23+ , stExternalNotes :: Bool -- ^ true if in context where
24+ -- we need to store footnotes
25+ , stInMinipage :: Bool -- ^ true if in minipage
26+ , stInHeading :: Bool -- ^ true if in a section heading
27+ , stInItem :: Bool -- ^ true if in \item[..]
28+ , stNotes :: [Doc Text] -- ^ notes in a minipage
29+ , stOLLevel :: Int -- ^ level of ordered list nesting
30+ , stOptions :: WriterOptions -- ^ writer options, so they don't have to
31+ -- be parameter
32+ , stVerbInNote :: Bool -- ^ true if document has verbatim text in note
33+ , stTable :: Bool -- ^ true if document has a table
34+ , stStrikeout :: Bool -- ^ true if document has strikeout
35+ , stUrl :: Bool -- ^ true if document has visible URL link
36+ , stGraphics :: Bool -- ^ true if document contains images
37+ , stLHS :: Bool -- ^ true if document has literate haskell code
38+ , stHasChapters :: Bool -- ^ true if document has chapters
39+ , stCsquotes :: Bool -- ^ true if document uses csquotes
40+ , stHighlighting :: Bool -- ^ true if document has highlighted code
41+ , stIncremental :: Bool -- ^ true if beamer lists should be
42+ -- displayed bit by bit
43+ , stInternalLinks :: [Text] -- ^ list of internal link targets
44+ , stBeamer :: Bool -- ^ produce beamer
45+ , stEmptyLine :: Bool -- ^ true if no content on line
46+ , stHasCslRefs :: Bool -- ^ has a Div with class refs
47+ , stIsFirstInDefinition :: Bool -- ^ first block in a defn list
48+ }
49+
50+startingState :: WriterOptions -> WriterState
51+startingState options =
52+ WriterState
53+ { stInNote = False
54+ , stInQuote = False
55+ , stExternalNotes = False
56+ , stInHeading = False
57+ , stInMinipage = False
58+ , stInItem = False
59+ , stNotes = []
60+ , stOLLevel = 1
61+ , stOptions = options
62+ , stVerbInNote = False
63+ , stTable = False
64+ , stStrikeout = False
65+ , stUrl = False
66+ , stGraphics = False
67+ , stLHS = False
68+ , stHasChapters = case writerTopLevelDivision options of
69+ TopLevelPart -> True
70+ TopLevelChapter -> True
71+ _ -> False
72+ , stCsquotes = False
73+ , stHighlighting = False
74+ , stIncremental = writerIncremental options
75+ , stInternalLinks = []
76+ , stBeamer = False
77+ , stEmptyLine = True
78+ , stHasCslRefs = False
79+ , stIsFirstInDefinition = False
80+ }