git.iko.soy/typeable/pandoc
on master help sign in
Improved LaTeX tables.
b5411c0John MacFarlane
src/Text/Pandoc/Writers/LaTeX.hs+56−33
@@ -42,6 +42,9 @@ import System.FilePath (dropExtension)
42
42
43data WriterState =
43data WriterState =
44 WriterState { stInNote :: Bool -- @True@ if we're in a note
44 WriterState { stInNote :: Bool -- @True@ if we're in a note
45+ , stInTable :: Bool -- @True@ if we're in a table
46+ , stTableNotes :: [(Char, Doc)] -- List of markers, notes
47+ -- in current table
45 , stOLLevel :: Int -- level of ordered list nesting
48 , stOLLevel :: Int -- level of ordered list nesting
46 , stOptions :: WriterOptions -- writer options, so they don't have to be parameter
49 , stOptions :: WriterOptions -- writer options, so they don't have to be parameter
47 , stVerbInNote :: Bool -- true if document has verbatim text in note
50 , stVerbInNote :: Bool -- true if document has verbatim text in note
@@ -59,7 +62,8 @@ data WriterState =
59writeLaTeX :: WriterOptions -> Pandoc -> String
62writeLaTeX :: WriterOptions -> Pandoc -> String
60writeLaTeX options document =
63writeLaTeX options document =
61 evalState (pandocToLaTeX options document) $
64 evalState (pandocToLaTeX options document) $
65+ WriterState { stInNote = False, stInTable = False,
62− WriterState { stInNote = False, stOLLevel = 1, stOptions = options,
66+ stTableNotes = [], stOLLevel = 1, stOptions = options,
63 stVerbInNote = False, stEnumerate = False,
67 stVerbInNote = False, stEnumerate = False,
64 stTable = False, stStrikeout = False, stSubscript = False,
68 stTable = False, stStrikeout = False, stSubscript = False,
65 stUrl = False, stGraphics = False,
69 stUrl = False, stGraphics = False,
@@ -269,48 +273,61 @@ blockToLaTeX (Header level lst) = do
269 5 -> headerWith "\\subparagraph" stuffing
273 5 -> headerWith "\\subparagraph" stuffing
270 _ -> txt $$ blankline
274 _ -> txt $$ blankline
271blockToLaTeX (Table caption aligns widths heads rows) = do
275blockToLaTeX (Table caption aligns widths heads rows) = do
276+ modify $ \s -> s{ stInTable = True, stTableNotes = [] }
272 headers <- if all null heads
277 headers <- if all null heads
273 then return empty
278 then return empty
274− else liftM ($$ "\\hline\\noalign{\\smallskip}")
279+ else liftM ($$ "\\ML")
275− $ (tableRowToLaTeX widths) heads
280+ $ (tableRowToLaTeX True aligns widths) heads
276 captionText <- inlineListToLaTeX $ deVerb caption
281 captionText <- inlineListToLaTeX $ deVerb caption
282+ let capt = if isEmpty captionText
283+ then empty
284+ else text "caption = " <> captionText <> "," <> space
277− rows' <- mapM (tableRowToLaTeX widths) rows
285+ rows' <- mapM (tableRowToLaTeX False aligns widths) rows
286+ let rows'' = intersperse ("\\\\\\noalign{\\medskip}") rows'
287+ tableNotes <- liftM (reverse . stTableNotes) get
288+ let toNote (marker, x) = "\\tnote" <> brackets (char marker) <>
289+ braces (nest 2 x)
290+ let notes = vcat $ map toNote tableNotes
278− let colDescriptors = concat $ zipWith toColDescriptor widths aligns
291+ let colDescriptors = text $ concat $ map toColDescriptor aligns
292+ let tableBody =
293+ ("\\ctable" <> brackets (capt <> text "pos = H, center, botcap"))
279− let tableBody = text ("\\begin{tabular}{" ++ colDescriptors ++ "}") $$
294+ <> braces colDescriptors
295+ $$ braces ("% notes" <> cr <> notes <> cr)
296+ $$ braces (text "% rows" $$ "\\FL" $$
280− headers $$ vcat rows' $$ "\\end{tabular}"
297+ vcat (headers : rows'') $$ "\\LL" <> cr)
281− let centered txt = "\\begin{center}" $$ txt $$ "\\end{center}"
282− modify $ \s -> s{ stTable = True }
298+ modify $ \s -> s{ stTable = True, stInTable = False, stTableNotes = [] }
283− return $ if isEmpty captionText
299+ return $ tableBody $$ blankline
284− then centered tableBody $$ blankline
300+
285− else "\\begin{table}[h]" $$ centered tableBody $$
286− inCmd "caption" captionText $$ "\\end{table}" $$ blankline
287−
288−toColDescriptor :: Double -> Alignment -> String
301+toColDescriptor :: Alignment -> String
289−toColDescriptor 0 align =
302+toColDescriptor align =
290 case align of
303 case align of
291 AlignLeft -> "l"
304 AlignLeft -> "l"
292 AlignRight -> "r"
305 AlignRight -> "r"
293 AlignCenter -> "c"
306 AlignCenter -> "c"
294 AlignDefault -> "l"
307 AlignDefault -> "l"
295−toColDescriptor width align = ">{\\PBS" ++
296− (case align of
297− AlignLeft -> "\\raggedright"
298− AlignRight -> "\\raggedleft"
299− AlignCenter -> "\\centering"
300− AlignDefault -> "\\raggedright") ++
301− "\\hspace{0pt}}p{" ++ printf "%.2f" width ++ "\\columnwidth}"
302
308
303blockListToLaTeX :: [Block] -> State WriterState Doc
309blockListToLaTeX :: [Block] -> State WriterState Doc
304blockListToLaTeX lst = mapM blockToLaTeX lst >>= return . vcat
310blockListToLaTeX lst = mapM blockToLaTeX lst >>= return . vcat
305
311
312+tableRowToLaTeX :: Bool
313+ -> [Alignment]
314+ -> [Double]
315+ -> [[Block]]
306−tableRowToLaTeX :: [Double] -> [[Block]] -> State WriterState Doc
316+ -> State WriterState Doc
307−tableRowToLaTeX widths cols = do
317+tableRowToLaTeX header aligns widths cols = do
308 renderedCells <- mapM blockListToLaTeX cols
318 renderedCells <- mapM blockListToLaTeX cols
319+ let valign = text $ if header then "[b]" else "[t]"
320+ let halign x = case x of
321+ AlignLeft -> "\\raggedright"
322+ AlignRight -> "\\raggedleft"
323+ AlignCenter -> "\\centering"
324+ AlignDefault -> "\\raggedright"
309− let toCell 0 c = c
325+ let toCell 0 _ c = c
310− toCell w c = "\\parbox[t]{" <> text (printf "%.2f" w) <>
326+ toCell w a c = "\\parbox" <> valign <>
311− "\\columnwidth}{" <> c <> cr <> "}"
327+ braces (text (printf "%.2f\\columnwidth" w)) <>
328+ braces (halign a <> cr <> c <> cr)
312− let cells = zipWith toCell widths renderedCells
329+ let cells = zipWith3 toCell widths aligns renderedCells
313− return $ (hcat $ intersperse (" & ") cells) <> "\\\\\\noalign{\\medskip}"
330+ return $ hcat $ intersperse (" & ") cells
314
331
315listItemToLaTeX :: [Block] -> State WriterState Doc
332listItemToLaTeX :: [Block] -> State WriterState Doc
316listItemToLaTeX lst = blockListToLaTeX lst >>= return . (text "\\item" $$) .
333listItemToLaTeX lst = blockListToLaTeX lst >>= return . (text "\\item" $$) .
@@ -413,9 +430,15 @@ inlineToLaTeX (Note contents) = do
413 modify (\s -> s{stInNote = True})
430 modify (\s -> s{stInNote = True})
414 contents' <- blockListToLaTeX contents
431 contents' <- blockListToLaTeX contents
415 modify (\s -> s {stInNote = False})
432 modify (\s -> s {stInNote = False})
433+ inTable <- liftM stInTable get
434+ if inTable
435+ then do
436+ curnotes <- liftM stTableNotes get
437+ let marker = cycle ['a'..'z'] !! length curnotes
438+ modify $ \s -> s{ stTableNotes = (marker, contents') : curnotes }
439+ return $ "\\tmark" <> brackets (char marker) <> space
440+ else return $ "\\footnote" <> braces (nest 2 contents')
416− -- note: a \n before } is needed when note ends with a Verbatim environment
441+ -- note: a \n before } needed when note ends with a Verbatim environment
417− return $ "\\footnote" <> braces (nest 2 contents')
418−
419
442
420citationsToNatbib :: [Citation] -> State WriterState Doc
443citationsToNatbib :: [Citation] -> State WriterState Doc
421citationsToNatbib (one:[])
444citationsToNatbib (one:[])
templates/latex.template+2−4
@@ -40,10 +40,8 @@ $if(fancy-enums)$
40\usepackage{enumerate}
40\usepackage{enumerate}
41$endif$
41$endif$
42$if(tables)$
42$if(tables)$
43−\usepackage{array}
43+\usepackage{ctable}
44−% This is needed because raggedright in table elements redefines \\:
44+\usepackage{float} % provides the H option for float placement
45−\newcommand{\PreserveBackslash}[1]{\let\temp=\\#1\let\\=\temp}
46−\let\PBS=\PreserveBackslash
47$endif$
45$endif$
48$if(strikeout)$
46$if(strikeout)$
49\usepackage[normalem]{ulem}
47\usepackage[normalem]{ulem}
tests/tables.latex+155−100
@@ -1,121 +1,176 @@
1Simple table with caption:
1Simple table with caption:
2
2
3−\begin{table}[h]
4−\begin{center}
5−\begin{tabular}{rlcl}
6−Right & Left & Center & Default\\
7−\hline
8−12 & 12 & 12 & 12\\
9−123 & 123 & 123 & 123\\
10−1 & 1 & 1 & 1\\
11−\end{tabular}
12−\end{center}
13−\caption{Demonstration of simple table syntax.}
3+\ctable[caption = Demonstration of simple table syntax.,
14−\end{table}
4+pos = H, center, botcap]{rlcl}
5+{% notes
6+}
7+{% rows
8+\FL
9+Right & Left & Center & Default
10+\ML
11+12 & 12 & 12 & 12
12+\\\noalign{\medskip}
13+123 & 123 & 123 & 123
14+\\\noalign{\medskip}
15+1 & 1 & 1 & 1
16+\LL
17+}
15
18
16Simple table without caption:
19Simple table without caption:
17
20
18−\begin{center}
21+\ctable[pos = H, center, botcap]{rlcl}
19−\begin{tabular}{rlcl}
22+{% notes
23+}
24+{% rows
25+\FL
20−Right & Left & Center & Default\\
26+Right & Left & Center & Default
21−\hline
27+\ML
22−12 & 12 & 12 & 12\\
28+12 & 12 & 12 & 12
29+\\\noalign{\medskip}
23−123 & 123 & 123 & 123\\
30+123 & 123 & 123 & 123
31+\\\noalign{\medskip}
24−1 & 1 & 1 & 1\\
32+1 & 1 & 1 & 1
25−\end{tabular}
33+\LL
26−\end{center}
34+}
27
35
28Simple table indented two spaces:
36Simple table indented two spaces:
29
37
30−\begin{table}[h]
31−\begin{center}
32−\begin{tabular}{rlcl}
33−Right & Left & Center & Default\\
34−\hline
35−12 & 12 & 12 & 12\\
36−123 & 123 & 123 & 123\\
37−1 & 1 & 1 & 1\\
38−\end{tabular}
39−\end{center}
40−\caption{Demonstration of simple table syntax.}
38+\ctable[caption = Demonstration of simple table syntax.,
41−\end{table}
39+pos = H, center, botcap]{rlcl}
40+{% notes
41+}
42+{% rows
43+\FL
44+Right & Left & Center & Default
45+\ML
46+12 & 12 & 12 & 12
47+\\\noalign{\medskip}
48+123 & 123 & 123 & 123
49+\\\noalign{\medskip}
50+1 & 1 & 1 & 1
51+\LL
52+}
42
53
43Multiline table with caption:
54Multiline table with caption:
44
55
45−\begin{table}[h]
56+\ctable[caption = Here's the caption. It may span multiple lines.,
46−\begin{center}
57+pos = H, center, botcap]{clrl}
47−\begin{tabular}{>{\PBS\centering\hspace{0pt}}p{0.15\columnwidth}>{\PBS\raggedright\hspace{0pt}}p{0.14\columnwidth}>{\PBS\raggedleft\hspace{0pt}}p{0.16\columnwidth}>{\PBS\raggedright\hspace{0pt}}p{0.34\columnwidth}}
58+{% notes
59+}
60+{% rows
61+\FL
62+\parbox[b]{0.15\columnwidth}{\centering
48−\parbox{0.15\columnwidth}{Centered Header
63+Centered Header
49−} & \parbox{0.14\columnwidth}{Left Aligned
64+} & \parbox[b]{0.14\columnwidth}{\raggedright
65+Left Aligned
50−} & \parbox{0.16\columnwidth}{Right Aligned
66+} & \parbox[b]{0.16\columnwidth}{\raggedleft
67+Right Aligned
51−} & \parbox{0.34\columnwidth}{Default aligned
68+} & \parbox[b]{0.34\columnwidth}{\raggedright
69+Default aligned
52−}\\
70+}
53−\hline
71+\ML
54−\parbox{0.15\columnwidth}{First
72+\parbox[t]{0.15\columnwidth}{\centering
73+First
55−} & \parbox{0.14\columnwidth}{row
74+} & \parbox[t]{0.14\columnwidth}{\raggedright
75+row
56−} & \parbox{0.16\columnwidth}{12.0
76+} & \parbox[t]{0.16\columnwidth}{\raggedleft
77+12.0
78+} & \parbox[t]{0.34\columnwidth}{\raggedright
57−} & \parbox{0.34\columnwidth}{Example of a row that spans multiple lines.
79+Example of a row that spans multiple lines.
58−}\\
80+}
81+\\\noalign{\medskip}
59−\parbox{0.15\columnwidth}{Second
82+\parbox[t]{0.15\columnwidth}{\centering
83+Second
60−} & \parbox{0.14\columnwidth}{row
84+} & \parbox[t]{0.14\columnwidth}{\raggedright
85+row
61−} & \parbox{0.16\columnwidth}{5.0
86+} & \parbox[t]{0.16\columnwidth}{\raggedleft
87+5.0
88+} & \parbox[t]{0.34\columnwidth}{\raggedright
62−} & \parbox{0.34\columnwidth}{Here's another one. Note the blank line between
89+Here's another one. Note the blank line between rows.
63−rows.
64−}\\
90+}
65−\end{tabular}
91+\LL
66−\end{center}
92+}
67−\caption{Here's the caption. It may span multiple lines.}
68−\end{table}
69
93
70Multiline table without caption:
94Multiline table without caption:
71
95
72−\begin{center}
96+\ctable[pos = H, center, botcap]{clrl}
73−\begin{tabular}{>{\PBS\centering\hspace{0pt}}p{0.15\columnwidth}>{\PBS\raggedright\hspace{0pt}}p{0.14\columnwidth}>{\PBS\raggedleft\hspace{0pt}}p{0.16\columnwidth}>{\PBS\raggedright\hspace{0pt}}p{0.34\columnwidth}}
97+{% notes
98+}
99+{% rows
100+\FL
101+\parbox[b]{0.15\columnwidth}{\centering
74−\parbox{0.15\columnwidth}{Centered Header
102+Centered Header
75−} & \parbox{0.14\columnwidth}{Left Aligned
103+} & \parbox[b]{0.14\columnwidth}{\raggedright
104+Left Aligned
76−} & \parbox{0.16\columnwidth}{Right Aligned
105+} & \parbox[b]{0.16\columnwidth}{\raggedleft
106+Right Aligned
77−} & \parbox{0.34\columnwidth}{Default aligned
107+} & \parbox[b]{0.34\columnwidth}{\raggedright
108+Default aligned
78−}\\
109+}
79−\hline
110+\ML
80−\parbox{0.15\columnwidth}{First
111+\parbox[t]{0.15\columnwidth}{\centering
112+First
81−} & \parbox{0.14\columnwidth}{row
113+} & \parbox[t]{0.14\columnwidth}{\raggedright
114+row
82−} & \parbox{0.16\columnwidth}{12.0
115+} & \parbox[t]{0.16\columnwidth}{\raggedleft
116+12.0
117+} & \parbox[t]{0.34\columnwidth}{\raggedright
83−} & \parbox{0.34\columnwidth}{Example of a row that spans multiple lines.
118+Example of a row that spans multiple lines.
84−}\\
119+}
120+\\\noalign{\medskip}
85−\parbox{0.15\columnwidth}{Second
121+\parbox[t]{0.15\columnwidth}{\centering
122+Second
86−} & \parbox{0.14\columnwidth}{row
123+} & \parbox[t]{0.14\columnwidth}{\raggedright
124+row
87−} & \parbox{0.16\columnwidth}{5.0
125+} & \parbox[t]{0.16\columnwidth}{\raggedleft
126+5.0
127+} & \parbox[t]{0.34\columnwidth}{\raggedright
88−} & \parbox{0.34\columnwidth}{Here's another one. Note the blank line between
128+Here's another one. Note the blank line between rows.
89−rows.
90−}\\
129+}
91−\end{tabular}
130+\LL
92−\end{center}
131+}
93
132
94Table without column headers:
133Table without column headers:
95
134
96−\begin{center}
135+\ctable[pos = H, center, botcap]{rlcr}
97−\begin{tabular}{rlcr}
136+{% notes
137+}
138+{% rows
139+\FL
98−12 & 12 & 12 & 12\\
140+12 & 12 & 12 & 12
141+\\\noalign{\medskip}
99−123 & 123 & 123 & 123\\
142+123 & 123 & 123 & 123
143+\\\noalign{\medskip}
100−1 & 1 & 1 & 1\\
144+1 & 1 & 1 & 1
101−\end{tabular}
145+\LL
102−\end{center}
146+}
103
147
104Multiline table without column headers:
148Multiline table without column headers:
105
149
106−\begin{center}
150+\ctable[pos = H, center, botcap]{clrl}
107−\begin{tabular}{>{\PBS\centering\hspace{0pt}}p{0.15\columnwidth}>{\PBS\raggedright\hspace{0pt}}p{0.14\columnwidth}>{\PBS\raggedleft\hspace{0pt}}p{0.16\columnwidth}>{\PBS\raggedright\hspace{0pt}}p{0.34\columnwidth}}
151+{% notes
108−\parbox{0.15\columnwidth}{First
152+}
153+{% rows
154+\FL
155+\parbox[t]{0.15\columnwidth}{\centering
156+First
109−} & \parbox{0.14\columnwidth}{row
157+} & \parbox[t]{0.14\columnwidth}{\raggedright
158+row
110−} & \parbox{0.16\columnwidth}{12.0
159+} & \parbox[t]{0.16\columnwidth}{\raggedleft
160+12.0
161+} & \parbox[t]{0.34\columnwidth}{\raggedright
111−} & \parbox{0.34\columnwidth}{Example of a row that spans multiple lines.
162+Example of a row that spans multiple lines.
112−}\\
163+}
164+\\\noalign{\medskip}
113−\parbox{0.15\columnwidth}{Second
165+\parbox[t]{0.15\columnwidth}{\centering
166+Second
114−} & \parbox{0.14\columnwidth}{row
167+} & \parbox[t]{0.14\columnwidth}{\raggedright
168+row
115−} & \parbox{0.16\columnwidth}{5.0
169+} & \parbox[t]{0.16\columnwidth}{\raggedleft
170+5.0
171+} & \parbox[t]{0.34\columnwidth}{\raggedright
116−} & \parbox{0.34\columnwidth}{Here's another one. Note the blank line between
172+Here's another one. Note the blank line between rows.
117−rows.
118−}\\
173+}
119−\end{tabular}
174+\LL
120−\end{center}
175+}
121
176