git.iko.soy/typeable/pandoc
on master help sign in
Improved LaTeX tables.

* Use ctable package, which allows footnotes and provides additional options. * Made cell alignments work in multiline tables. * Closes #272.

commitb5411c06aa5c4909cf10647e6ba0fe186cfa41f6 authorJohn MacFarlane <jgm@berkeley.edu> dateSun 10 Jul 2011, 09:13 (15 years ago) parent09479ba
3 files changed+213−137
src/Text/Pandoc/Writers/LaTeX.hs+56−33 templates/latex.template+2−4 tests/tables.latex+155−100
src/Text/Pandoc/Writers/LaTeX.hs+56−33
@@ -42,6 +42,9 @@ import System.FilePath (dropExtension)
4242
4343data WriterState =
4444 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
4548 , stOLLevel :: Int -- level of ordered list nesting
4649 , stOptions :: WriterOptions -- writer options, so they don't have to be parameter
4750 , stVerbInNote :: Bool -- true if document has verbatim text in note
@@ -59,7 +62,8 @@ data WriterState =
5962writeLaTeX :: WriterOptions -> Pandoc -> String
6063writeLaTeX options document =
6164 evalState (pandocToLaTeX options document) $
62− WriterState { stInNote = False, stOLLevel = 1, stOptions = options,
65+ WriterState { stInNote = False, stInTable = False,
66+ stTableNotes = [], stOLLevel = 1, stOptions = options,
6367 stVerbInNote = False, stEnumerate = False,
6468 stTable = False, stStrikeout = False, stSubscript = False,
6569 stUrl = False, stGraphics = False,
@@ -269,48 +273,61 @@ blockToLaTeX (Header level lst) = do
269273 5 -> headerWith "\\subparagraph" stuffing
270274 _ -> txt $$ blankline
271275blockToLaTeX (Table caption aligns widths heads rows) = do
276+ modify $ \s -> s{ stInTable = True, stTableNotes = [] }
272277 headers <- if all null heads
273278 then return empty
274− else liftM ($$ "\\hline\\noalign{\\smallskip}")
275− $ (tableRowToLaTeX widths) heads
279+ else liftM ($$ "\\ML")
280+ $ (tableRowToLaTeX True aligns widths) heads
276281 captionText <- inlineListToLaTeX $ deVerb caption
277− rows' <- mapM (tableRowToLaTeX widths) rows
278− let colDescriptors = concat $ zipWith toColDescriptor widths aligns
279− let tableBody = text ("\\begin{tabular}{" ++ colDescriptors ++ "}") $$
280− headers $$ vcat rows' $$ "\\end{tabular}"
281− let centered txt = "\\begin{center}" $$ txt $$ "\\end{center}"
282− modify $ \s -> s{ stTable = True }
283− return $ if isEmpty captionText
284− then centered tableBody $$ blankline
285− else "\\begin{table}[h]" $$ centered tableBody $$
286− inCmd "caption" captionText $$ "\\end{table}" $$ blankline
287−
288−toColDescriptor :: Double -> Alignment -> String
289−toColDescriptor 0 align =
282+ let capt = if isEmpty captionText
283+ then empty
284+ else text "caption = " <> captionText <> "," <> space
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
291+ let colDescriptors = text $ concat $ map toColDescriptor aligns
292+ let tableBody =
293+ ("\\ctable" <> brackets (capt <> text "pos = H, center, botcap"))
294+ <> braces colDescriptors
295+ $$ braces ("% notes" <> cr <> notes <> cr)
296+ $$ braces (text "% rows" $$ "\\FL" $$
297+ vcat (headers : rows'') $$ "\\LL" <> cr)
298+ modify $ \s -> s{ stTable = True, stInTable = False, stTableNotes = [] }
299+ return $ tableBody $$ blankline
300+
301+toColDescriptor :: Alignment -> String
302+toColDescriptor align =
290303 case align of
291304 AlignLeft -> "l"
292305 AlignRight -> "r"
293306 AlignCenter -> "c"
294307 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}"
302308
303309blockListToLaTeX :: [Block] -> State WriterState Doc
304310blockListToLaTeX lst = mapM blockToLaTeX lst >>= return . vcat
305311
306−tableRowToLaTeX :: [Double] -> [[Block]] -> State WriterState Doc
307−tableRowToLaTeX widths cols = do
312+tableRowToLaTeX :: Bool
313+ -> [Alignment]
314+ -> [Double]
315+ -> [[Block]]
316+ -> State WriterState Doc
317+tableRowToLaTeX header aligns widths cols = do
308318 renderedCells <- mapM blockListToLaTeX cols
309− let toCell 0 c = c
310− toCell w c = "\\parbox[t]{" <> text (printf "%.2f" w) <>
311− "\\columnwidth}{" <> c <> cr <> "}"
312− let cells = zipWith toCell widths renderedCells
313− return $ (hcat $ intersperse (" & ") cells) <> "\\\\\\noalign{\\medskip}"
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"
325+ let toCell 0 _ c = c
326+ toCell w a c = "\\parbox" <> valign <>
327+ braces (text (printf "%.2f\\columnwidth" w)) <>
328+ braces (halign a <> cr <> c <> cr)
329+ let cells = zipWith3 toCell widths aligns renderedCells
330+ return $ hcat $ intersperse (" & ") cells
314331
315332listItemToLaTeX :: [Block] -> State WriterState Doc
316333listItemToLaTeX lst = blockListToLaTeX lst >>= return . (text "\\item" $$) .
@@ -413,9 +430,15 @@ inlineToLaTeX (Note contents) = do
413430 modify (\s -> s{stInNote = True})
414431 contents' <- blockListToLaTeX contents
415432 modify (\s -> s {stInNote = False})
416− -- note: a \n before } is needed when note ends with a Verbatim environment
417− return $ "\\footnote" <> braces (nest 2 contents')
418−
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')
441+ -- note: a \n before } needed when note ends with a Verbatim environment
419442
420443citationsToNatbib :: [Citation] -> State WriterState Doc
421444citationsToNatbib (one:[])
templates/latex.template+2−4
@@ -40,10 +40,8 @@ $if(fancy-enums)$
4040\usepackage{enumerate}
4141$endif$
4242$if(tables)$
43−\usepackage{array}
44−% This is needed because raggedright in table elements redefines \\:
45−\newcommand{\PreserveBackslash}[1]{\let\temp=\\#1\let\\=\temp}
46−\let\PBS=\PreserveBackslash
43+\usepackage{ctable}
44+\usepackage{float} % provides the H option for float placement
4745$endif$
4846$if(strikeout)$
4947\usepackage[normalem]{ulem}
tests/tables.latex+155−100
@@ -1,121 +1,176 @@
11Simple table with caption:
22
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.}
14−\end{table}
3+\ctable[caption = Demonstration of simple table syntax.,
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+}
1518
1619Simple table without caption:
1720
18−\begin{center}
19−\begin{tabular}{rlcl}
20−Right & Left & Center & Default\\
21−\hline
22−12 & 12 & 12 & 12\\
23−123 & 123 & 123 & 123\\
24−1 & 1 & 1 & 1\\
25−\end{tabular}
26−\end{center}
21+\ctable[pos = H, center, botcap]{rlcl}
22+{% notes
23+}
24+{% rows
25+\FL
26+Right & Left & Center & Default
27+\ML
28+12 & 12 & 12 & 12
29+\\\noalign{\medskip}
30+123 & 123 & 123 & 123
31+\\\noalign{\medskip}
32+1 & 1 & 1 & 1
33+\LL
34+}
2735
2836Simple table indented two spaces:
2937
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.}
41−\end{table}
38+\ctable[caption = Demonstration of simple table syntax.,
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+}
4253
4354Multiline table with caption:
4455
45−\begin{table}[h]
46−\begin{center}
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}}
48−\parbox{0.15\columnwidth}{Centered Header
49−} & \parbox{0.14\columnwidth}{Left Aligned
50−} & \parbox{0.16\columnwidth}{Right Aligned
51−} & \parbox{0.34\columnwidth}{Default aligned
52−}\\
53−\hline
54−\parbox{0.15\columnwidth}{First
55−} & \parbox{0.14\columnwidth}{row
56−} & \parbox{0.16\columnwidth}{12.0
57−} & \parbox{0.34\columnwidth}{Example of a row that spans multiple lines.
58−}\\
59−\parbox{0.15\columnwidth}{Second
60−} & \parbox{0.14\columnwidth}{row
61−} & \parbox{0.16\columnwidth}{5.0
62−} & \parbox{0.34\columnwidth}{Here's another one. Note the blank line between
63−rows.
64−}\\
65−\end{tabular}
66−\end{center}
67−\caption{Here's the caption. It may span multiple lines.}
68−\end{table}
56+\ctable[caption = Here's the caption. It may span multiple lines.,
57+pos = H, center, botcap]{clrl}
58+{% notes
59+}
60+{% rows
61+\FL
62+\parbox[b]{0.15\columnwidth}{\centering
63+Centered Header
64+} & \parbox[b]{0.14\columnwidth}{\raggedright
65+Left Aligned
66+} & \parbox[b]{0.16\columnwidth}{\raggedleft
67+Right Aligned
68+} & \parbox[b]{0.34\columnwidth}{\raggedright
69+Default aligned
70+}
71+\ML
72+\parbox[t]{0.15\columnwidth}{\centering
73+First
74+} & \parbox[t]{0.14\columnwidth}{\raggedright
75+row
76+} & \parbox[t]{0.16\columnwidth}{\raggedleft
77+12.0
78+} & \parbox[t]{0.34\columnwidth}{\raggedright
79+Example of a row that spans multiple lines.
80+}
81+\\\noalign{\medskip}
82+\parbox[t]{0.15\columnwidth}{\centering
83+Second
84+} & \parbox[t]{0.14\columnwidth}{\raggedright
85+row
86+} & \parbox[t]{0.16\columnwidth}{\raggedleft
87+5.0
88+} & \parbox[t]{0.34\columnwidth}{\raggedright
89+Here's another one. Note the blank line between rows.
90+}
91+\LL
92+}
6993
7094Multiline table without caption:
7195
72−\begin{center}
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}}
74−\parbox{0.15\columnwidth}{Centered Header
75−} & \parbox{0.14\columnwidth}{Left Aligned
76−} & \parbox{0.16\columnwidth}{Right Aligned
77−} & \parbox{0.34\columnwidth}{Default aligned
78−}\\
79−\hline
80−\parbox{0.15\columnwidth}{First
81−} & \parbox{0.14\columnwidth}{row
82−} & \parbox{0.16\columnwidth}{12.0
83−} & \parbox{0.34\columnwidth}{Example of a row that spans multiple lines.
84−}\\
85−\parbox{0.15\columnwidth}{Second
86−} & \parbox{0.14\columnwidth}{row
87−} & \parbox{0.16\columnwidth}{5.0
88−} & \parbox{0.34\columnwidth}{Here's another one. Note the blank line between
89−rows.
90−}\\
91−\end{tabular}
92−\end{center}
96+\ctable[pos = H, center, botcap]{clrl}
97+{% notes
98+}
99+{% rows
100+\FL
101+\parbox[b]{0.15\columnwidth}{\centering
102+Centered Header
103+} & \parbox[b]{0.14\columnwidth}{\raggedright
104+Left Aligned
105+} & \parbox[b]{0.16\columnwidth}{\raggedleft
106+Right Aligned
107+} & \parbox[b]{0.34\columnwidth}{\raggedright
108+Default aligned
109+}
110+\ML
111+\parbox[t]{0.15\columnwidth}{\centering
112+First
113+} & \parbox[t]{0.14\columnwidth}{\raggedright
114+row
115+} & \parbox[t]{0.16\columnwidth}{\raggedleft
116+12.0
117+} & \parbox[t]{0.34\columnwidth}{\raggedright
118+Example of a row that spans multiple lines.
119+}
120+\\\noalign{\medskip}
121+\parbox[t]{0.15\columnwidth}{\centering
122+Second
123+} & \parbox[t]{0.14\columnwidth}{\raggedright
124+row
125+} & \parbox[t]{0.16\columnwidth}{\raggedleft
126+5.0
127+} & \parbox[t]{0.34\columnwidth}{\raggedright
128+Here's another one. Note the blank line between rows.
129+}
130+\LL
131+}
93132
94133Table without column headers:
95134
96−\begin{center}
97−\begin{tabular}{rlcr}
98−12 & 12 & 12 & 12\\
99−123 & 123 & 123 & 123\\
100−1 & 1 & 1 & 1\\
101−\end{tabular}
102−\end{center}
135+\ctable[pos = H, center, botcap]{rlcr}
136+{% notes
137+}
138+{% rows
139+\FL
140+12 & 12 & 12 & 12
141+\\\noalign{\medskip}
142+123 & 123 & 123 & 123
143+\\\noalign{\medskip}
144+1 & 1 & 1 & 1
145+\LL
146+}
103147
104148Multiline table without column headers:
105149
106−\begin{center}
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}}
108−\parbox{0.15\columnwidth}{First
109−} & \parbox{0.14\columnwidth}{row
110−} & \parbox{0.16\columnwidth}{12.0
111−} & \parbox{0.34\columnwidth}{Example of a row that spans multiple lines.
112−}\\
113−\parbox{0.15\columnwidth}{Second
114−} & \parbox{0.14\columnwidth}{row
115−} & \parbox{0.16\columnwidth}{5.0
116−} & \parbox{0.34\columnwidth}{Here's another one. Note the blank line between
117−rows.
118−}\\
119−\end{tabular}
120−\end{center}
150+\ctable[pos = H, center, botcap]{clrl}
151+{% notes
152+}
153+{% rows
154+\FL
155+\parbox[t]{0.15\columnwidth}{\centering
156+First
157+} & \parbox[t]{0.14\columnwidth}{\raggedright
158+row
159+} & \parbox[t]{0.16\columnwidth}{\raggedleft
160+12.0
161+} & \parbox[t]{0.34\columnwidth}{\raggedright
162+Example of a row that spans multiple lines.
163+}
164+\\\noalign{\medskip}
165+\parbox[t]{0.15\columnwidth}{\centering
166+Second
167+} & \parbox[t]{0.14\columnwidth}{\raggedright
168+row
169+} & \parbox[t]{0.16\columnwidth}{\raggedleft
170+5.0
171+} & \parbox[t]{0.34\columnwidth}{\raggedright
172+Here's another one. Note the blank line between rows.
173+}
174+\LL
175+}
121176