git.iko.soy/typeable/pandoc
on master help sign in
LaTeX writer: extract table handling into separate module.
ccd235eAlbert Krewinkel
pandoc.cabal+4
@@ -630,6 +630,10 @@ library
630 Text.Pandoc.Writers.Docx.StyleMap,
630 Text.Pandoc.Writers.Docx.StyleMap,
631 Text.Pandoc.Writers.JATS.Table,
631 Text.Pandoc.Writers.JATS.Table,
632 Text.Pandoc.Writers.JATS.Types,
632 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,
633 Text.Pandoc.Writers.Roff,
637 Text.Pandoc.Writers.Roff,
634 Text.Pandoc.Writers.Powerpoint.Presentation,
638 Text.Pandoc.Writers.Powerpoint.Presentation,
635 Text.Pandoc.Writers.Powerpoint.Output,
639 Text.Pandoc.Writers.Powerpoint.Output,
src/Text/Pandoc/Writers/LaTeX.hs+12−237
@@ -20,7 +20,6 @@ module Text.Pandoc.Writers.LaTeX (
20 ) where
20 ) where
21import Control.Applicative ((<|>))
21import Control.Applicative ((<|>))
22import Control.Monad.State.Strict
22import Control.Monad.State.Strict
23−import Data.Monoid (Any(..))
24import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isSpace,
23import Data.Char (isAlphaNum, isAscii, isDigit, isLetter, isSpace,
25 isPunctuation, ord)
24 isPunctuation, ord)
26import Data.List (foldl', intersperse, nubBy, (\\), uncons)
25import Data.List (foldl', intersperse, nubBy, (\\), uncons)
@@ -42,69 +41,14 @@ import Text.Pandoc.Options
42import Text.DocLayout
41import Text.DocLayout
43import Text.Pandoc.Shared
42import Text.Pandoc.Shared
44import Text.Pandoc.Slides
43import 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)
46import Text.Pandoc.Writers.Shared
48import Text.Pandoc.Writers.Shared
47import Text.Printf (printf)
49import Text.Printf (printf)
48import qualified Data.Text.Normalize as Normalize
50import qualified Data.Text.Normalize as Normalize
49
51
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−
108-- | Convert Pandoc to LaTeX.
52-- | Convert Pandoc to LaTeX.
109writeLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m Text
53writeLaTeX :: PandocMonad m => WriterOptions -> Pandoc -> m Text
110writeLaTeX options document =
54writeLaTeX options document =
@@ -117,8 +61,6 @@ writeBeamer options document =
117 evalStateT (pandocToLaTeX options document) $
61 evalStateT (pandocToLaTeX options document) $
118 (startingState options){ stBeamer = True }
62 (startingState options){ stBeamer = True }
119
63
120−type LW m = StateT WriterState m
121−
122pandocToLaTeX :: PandocMonad m
64pandocToLaTeX :: PandocMonad m
123 => WriterOptions -> Pandoc -> LW m Text
65 => WriterOptions -> Pandoc -> LW m Text
124pandocToLaTeX options (Pandoc meta blocks) = do
66pandocToLaTeX options (Pandoc meta blocks) = do
@@ -573,7 +515,7 @@ blockToLaTeX (Plain lst) =
573blockToLaTeX (Para [Image attr@(ident, _, _) txt (src,tgt)])
515blockToLaTeX (Para [Image attr@(ident, _, _) txt (src,tgt)])
574 | Just tit <- T.stripPrefix "fig:" tgt
516 | Just tit <- T.stripPrefix "fig:" tgt
575 = do
517 = do
576− (capt, captForLof, footnotes) <- getCaption True txt
518+ (capt, captForLof, footnotes) <- getCaption inlineListToLaTeX True txt
577 lab <- labelFor ident
519 lab <- labelFor ident
578 let caption = "\\caption" <> captForLof <> braces capt <> lab
520 let caption = "\\caption" <> captForLof <> braces capt <> lab
579 img <- inlineToLaTeX (Image attr txt (src,tit))
521 img <- inlineToLaTeX (Image attr txt (src,tit))
@@ -774,181 +716,14 @@ blockToLaTeX (Header level (id',classes,_) lst) = do
774 hdr <- sectionHeader classes id' level lst
716 hdr <- sectionHeader classes id' level lst
775 modify $ \s -> s{stInHeading = False}
717 modify $ \s -> s{stInHeading = False}
776 return hdr
718 return hdr
777−blockToLaTeX (Table _ blkCapt specs thead tbody tfoot) = do
719+blockToLaTeX (Table _ blkCapt specs thead tbodies tfoot) =
720+ tableToLaTeX inlineListToLaTeX blockListToLaTeX
778− let (caption, aligns, widths, heads, rows) = toLegacyTable blkCapt specs thead tbody tfoot
721+ blkCapt specs thead tbodies 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"
866
722
867blockListToLaTeX :: PandocMonad m => [Block] -> LW m (Doc Text)
723blockListToLaTeX :: PandocMonad m => [Block] -> LW m (Doc Text)
868blockListToLaTeX lst =
724blockListToLaTeX lst =
869 vsep `fmap` mapM (\b -> setEmptyLine True >> blockToLaTeX b) lst
725 vsep `fmap` mapM (\b -> setEmptyLine True >> blockToLaTeX b) lst
870
726
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−
952listItemToLaTeX :: PandocMonad m => [Block] -> LW m (Doc Text)
727listItemToLaTeX :: PandocMonad m => [Block] -> LW m (Doc Text)
953listItemToLaTeX lst
728listItemToLaTeX lst
954 -- we need to put some text before a header if it's the first
729 -- we need to put some text before a header if it's the first
@@ -1081,7 +856,7 @@ mapAlignment a = case a of
1081 "top-baseline" -> "t"
856 "top-baseline" -> "t"
1082 "bottom" -> "b"
857 "bottom" -> "b"
1083 "center" -> "c"
858 "center" -> "c"
1084− _ -> a
859+ _ -> a
1085
860
1086wrapDiv :: PandocMonad m => Attr -> Doc Text -> LW m (Doc Text)
861wrapDiv :: PandocMonad m => Attr -> Doc Text -> LW m (Doc Text)
1087wrapDiv (_,classes,kvs) t = do
862wrapDiv (_,classes,kvs) t = do
@@ -1095,7 +870,7 @@ wrapDiv (_,classes,kvs) t = do
1095 (lookup "totalwidth" kvs)
870 (lookup "totalwidth" kvs)
1096 onlytextwidth = filter ("onlytextwidth" ==) classes
871 onlytextwidth = filter ("onlytextwidth" ==) classes
1097 options = text $ T.unpack $ T.intercalate "," $
872 options = text $ T.unpack $ T.intercalate "," $
1098− valign : totalwidth ++ onlytextwidth
873+ valign : totalwidth ++ onlytextwidth
1099 in inCmd "begin" "columns" <> brackets options
874 in inCmd "begin" "columns" <> brackets options
1100 $$ contents
875 $$ contents
1101 $$ inCmd "end" "columns"
876 $$ inCmd "end" "columns"
@@ -1106,8 +881,8 @@ wrapDiv (_,classes,kvs) t = do
1106 maybe ""
881 maybe ""
1107 (brackets . text . T.unpack . mapAlignment)
882 (brackets . text . T.unpack . mapAlignment)
1108 (lookup "align" kvs)
883 (lookup "align" kvs)
1109− w = maybe "0.48" fromPct (lookup "width" kvs)
884+ w = maybe "0.48" fromPct (lookup "width" kvs)
1110− in inCmd "begin" "column" <>
885+ in inCmd "begin" "column" <>
1111 valign <>
886 valign <>
1112 braces (literal w <> "\\textwidth")
887 braces (literal w <> "\\textwidth")
1113 $$ contents
888 $$ 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+ }