Improved LaTeX tables.

* Use ctable package, which allows footnotes and
  provides additional options.
* Made cell alignments work in multiline tables.
* Closes #272.
This commit is contained in:
John MacFarlane
2011-07-10 12:33:45 -07:00
parent 09479ba7b2
commit b5411c06aa
3 changed files with 212 additions and 136 deletions
+55 -32
View File
@@ -42,6 +42,9 @@ import System.FilePath (dropExtension)
data WriterState =
WriterState { stInNote :: Bool -- @True@ if we're in a note
, stInTable :: Bool -- @True@ if we're in a table
, stTableNotes :: [(Char, Doc)] -- List of markers, notes
-- in current table
, stOLLevel :: Int -- level of ordered list nesting
, stOptions :: WriterOptions -- writer options, so they don't have to be parameter
, stVerbInNote :: Bool -- true if document has verbatim text in note
@@ -59,7 +62,8 @@ data WriterState =
writeLaTeX :: WriterOptions -> Pandoc -> String
writeLaTeX options document =
evalState (pandocToLaTeX options document) $
WriterState { stInNote = False, stOLLevel = 1, stOptions = options,
WriterState { stInNote = False, stInTable = False,
stTableNotes = [], stOLLevel = 1, stOptions = options,
stVerbInNote = False, stEnumerate = False,
stTable = False, stStrikeout = False, stSubscript = False,
stUrl = False, stGraphics = False,
@@ -269,48 +273,61 @@ blockToLaTeX (Header level lst) = do
5 -> headerWith "\\subparagraph" stuffing
_ -> txt $$ blankline
blockToLaTeX (Table caption aligns widths heads rows) = do
modify $ \s -> s{ stInTable = True, stTableNotes = [] }
headers <- if all null heads
then return empty
else liftM ($$ "\\hline\\noalign{\\smallskip}")
$ (tableRowToLaTeX widths) heads
else liftM ($$ "\\ML")
$ (tableRowToLaTeX True aligns widths) heads
captionText <- inlineListToLaTeX $ deVerb caption
rows' <- mapM (tableRowToLaTeX widths) rows
let colDescriptors = concat $ zipWith toColDescriptor widths aligns
let tableBody = text ("\\begin{tabular}{" ++ colDescriptors ++ "}") $$
headers $$ vcat rows' $$ "\\end{tabular}"
let centered txt = "\\begin{center}" $$ txt $$ "\\end{center}"
modify $ \s -> s{ stTable = True }
return $ if isEmpty captionText
then centered tableBody $$ blankline
else "\\begin{table}[h]" $$ centered tableBody $$
inCmd "caption" captionText $$ "\\end{table}" $$ blankline
let capt = if isEmpty captionText
then empty
else text "caption = " <> captionText <> "," <> space
rows' <- mapM (tableRowToLaTeX False aligns widths) rows
let rows'' = intersperse ("\\\\\\noalign{\\medskip}") rows'
tableNotes <- liftM (reverse . stTableNotes) get
let toNote (marker, x) = "\\tnote" <> brackets (char marker) <>
braces (nest 2 x)
let notes = vcat $ map toNote tableNotes
let colDescriptors = text $ concat $ map toColDescriptor aligns
let tableBody =
("\\ctable" <> brackets (capt <> text "pos = H, center, botcap"))
<> braces colDescriptors
$$ braces ("% notes" <> cr <> notes <> cr)
$$ braces (text "% rows" $$ "\\FL" $$
vcat (headers : rows'') $$ "\\LL" <> cr)
modify $ \s -> s{ stTable = True, stInTable = False, stTableNotes = [] }
return $ tableBody $$ blankline
toColDescriptor :: Double -> Alignment -> String
toColDescriptor 0 align =
toColDescriptor :: Alignment -> String
toColDescriptor align =
case align of
AlignLeft -> "l"
AlignRight -> "r"
AlignCenter -> "c"
AlignDefault -> "l"
toColDescriptor width align = ">{\\PBS" ++
(case align of
AlignLeft -> "\\raggedright"
AlignRight -> "\\raggedleft"
AlignCenter -> "\\centering"
AlignDefault -> "\\raggedright") ++
"\\hspace{0pt}}p{" ++ printf "%.2f" width ++ "\\columnwidth}"
blockListToLaTeX :: [Block] -> State WriterState Doc
blockListToLaTeX lst = mapM blockToLaTeX lst >>= return . vcat
tableRowToLaTeX :: [Double] -> [[Block]] -> State WriterState Doc
tableRowToLaTeX widths cols = do
tableRowToLaTeX :: Bool
-> [Alignment]
-> [Double]
-> [[Block]]
-> State WriterState Doc
tableRowToLaTeX header aligns widths cols = do
renderedCells <- mapM blockListToLaTeX cols
let toCell 0 c = c
toCell w c = "\\parbox[t]{" <> text (printf "%.2f" w) <>
"\\columnwidth}{" <> c <> cr <> "}"
let cells = zipWith toCell widths renderedCells
return $ (hcat $ intersperse (" & ") cells) <> "\\\\\\noalign{\\medskip}"
let valign = text $ if header then "[b]" else "[t]"
let halign x = case x of
AlignLeft -> "\\raggedright"
AlignRight -> "\\raggedleft"
AlignCenter -> "\\centering"
AlignDefault -> "\\raggedright"
let toCell 0 _ c = c
toCell w a c = "\\parbox" <> valign <>
braces (text (printf "%.2f\\columnwidth" w)) <>
braces (halign a <> cr <> c <> cr)
let cells = zipWith3 toCell widths aligns renderedCells
return $ hcat $ intersperse (" & ") cells
listItemToLaTeX :: [Block] -> State WriterState Doc
listItemToLaTeX lst = blockListToLaTeX lst >>= return . (text "\\item" $$) .
@@ -413,9 +430,15 @@ inlineToLaTeX (Note contents) = do
modify (\s -> s{stInNote = True})
contents' <- blockListToLaTeX contents
modify (\s -> s {stInNote = False})
-- note: a \n before } is needed when note ends with a Verbatim environment
return $ "\\footnote" <> braces (nest 2 contents')
inTable <- liftM stInTable get
if inTable
then do
curnotes <- liftM stTableNotes get
let marker = cycle ['a'..'z'] !! length curnotes
modify $ \s -> s{ stTableNotes = (marker, contents') : curnotes }
return $ "\\tmark" <> brackets (char marker) <> space
else return $ "\\footnote" <> braces (nest 2 contents')
-- note: a \n before } needed when note ends with a Verbatim environment
citationsToNatbib :: [Citation] -> State WriterState Doc
citationsToNatbib (one:[])
+2 -4
View File
@@ -40,10 +40,8 @@ $if(fancy-enums)$
\usepackage{enumerate}
$endif$
$if(tables)$
\usepackage{array}
% This is needed because raggedright in table elements redefines \\:
\newcommand{\PreserveBackslash}[1]{\let\temp=\\#1\let\\=\temp}
\let\PBS=\PreserveBackslash
\usepackage{ctable}
\usepackage{float} % provides the H option for float placement
$endif$
$if(strikeout)$
\usepackage[normalem]{ulem}
+155 -100
View File
@@ -1,121 +1,176 @@
Simple table with caption:
\begin{table}[h]
\begin{center}
\begin{tabular}{rlcl}
Right & Left & Center & Default\\
\hline
12 & 12 & 12 & 12\\
123 & 123 & 123 & 123\\
1 & 1 & 1 & 1\\
\end{tabular}
\end{center}
\caption{Demonstration of simple table syntax.}
\end{table}
\ctable[caption = Demonstration of simple table syntax.,
pos = H, center, botcap]{rlcl}
{% notes
}
{% rows
\FL
Right & Left & Center & Default
\ML
12 & 12 & 12 & 12
\\\noalign{\medskip}
123 & 123 & 123 & 123
\\\noalign{\medskip}
1 & 1 & 1 & 1
\LL
}
Simple table without caption:
\begin{center}
\begin{tabular}{rlcl}
Right & Left & Center & Default\\
\hline
12 & 12 & 12 & 12\\
123 & 123 & 123 & 123\\
1 & 1 & 1 & 1\\
\end{tabular}
\end{center}
\ctable[pos = H, center, botcap]{rlcl}
{% notes
}
{% rows
\FL
Right & Left & Center & Default
\ML
12 & 12 & 12 & 12
\\\noalign{\medskip}
123 & 123 & 123 & 123
\\\noalign{\medskip}
1 & 1 & 1 & 1
\LL
}
Simple table indented two spaces:
\begin{table}[h]
\begin{center}
\begin{tabular}{rlcl}
Right & Left & Center & Default\\
\hline
12 & 12 & 12 & 12\\
123 & 123 & 123 & 123\\
1 & 1 & 1 & 1\\
\end{tabular}
\end{center}
\caption{Demonstration of simple table syntax.}
\end{table}
\ctable[caption = Demonstration of simple table syntax.,
pos = H, center, botcap]{rlcl}
{% notes
}
{% rows
\FL
Right & Left & Center & Default
\ML
12 & 12 & 12 & 12
\\\noalign{\medskip}
123 & 123 & 123 & 123
\\\noalign{\medskip}
1 & 1 & 1 & 1
\LL
}
Multiline table with caption:
\begin{table}[h]
\begin{center}
\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}}
\parbox{0.15\columnwidth}{Centered Header
} & \parbox{0.14\columnwidth}{Left Aligned
} & \parbox{0.16\columnwidth}{Right Aligned
} & \parbox{0.34\columnwidth}{Default aligned
}\\
\hline
\parbox{0.15\columnwidth}{First
} & \parbox{0.14\columnwidth}{row
} & \parbox{0.16\columnwidth}{12.0
} & \parbox{0.34\columnwidth}{Example of a row that spans multiple lines.
}\\
\parbox{0.15\columnwidth}{Second
} & \parbox{0.14\columnwidth}{row
} & \parbox{0.16\columnwidth}{5.0
} & \parbox{0.34\columnwidth}{Here's another one. Note the blank line between
rows.
}\\
\end{tabular}
\end{center}
\caption{Here's the caption. It may span multiple lines.}
\end{table}
\ctable[caption = Here's the caption. It may span multiple lines.,
pos = H, center, botcap]{clrl}
{% notes
}
{% rows
\FL
\parbox[b]{0.15\columnwidth}{\centering
Centered Header
} & \parbox[b]{0.14\columnwidth}{\raggedright
Left Aligned
} & \parbox[b]{0.16\columnwidth}{\raggedleft
Right Aligned
} & \parbox[b]{0.34\columnwidth}{\raggedright
Default aligned
}
\ML
\parbox[t]{0.15\columnwidth}{\centering
First
} & \parbox[t]{0.14\columnwidth}{\raggedright
row
} & \parbox[t]{0.16\columnwidth}{\raggedleft
12.0
} & \parbox[t]{0.34\columnwidth}{\raggedright
Example of a row that spans multiple lines.
}
\\\noalign{\medskip}
\parbox[t]{0.15\columnwidth}{\centering
Second
} & \parbox[t]{0.14\columnwidth}{\raggedright
row
} & \parbox[t]{0.16\columnwidth}{\raggedleft
5.0
} & \parbox[t]{0.34\columnwidth}{\raggedright
Here's another one. Note the blank line between rows.
}
\LL
}
Multiline table without caption:
\begin{center}
\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}}
\parbox{0.15\columnwidth}{Centered Header
} & \parbox{0.14\columnwidth}{Left Aligned
} & \parbox{0.16\columnwidth}{Right Aligned
} & \parbox{0.34\columnwidth}{Default aligned
}\\
\hline
\parbox{0.15\columnwidth}{First
} & \parbox{0.14\columnwidth}{row
} & \parbox{0.16\columnwidth}{12.0
} & \parbox{0.34\columnwidth}{Example of a row that spans multiple lines.
}\\
\parbox{0.15\columnwidth}{Second
} & \parbox{0.14\columnwidth}{row
} & \parbox{0.16\columnwidth}{5.0
} & \parbox{0.34\columnwidth}{Here's another one. Note the blank line between
rows.
}\\
\end{tabular}
\end{center}
\ctable[pos = H, center, botcap]{clrl}
{% notes
}
{% rows
\FL
\parbox[b]{0.15\columnwidth}{\centering
Centered Header
} & \parbox[b]{0.14\columnwidth}{\raggedright
Left Aligned
} & \parbox[b]{0.16\columnwidth}{\raggedleft
Right Aligned
} & \parbox[b]{0.34\columnwidth}{\raggedright
Default aligned
}
\ML
\parbox[t]{0.15\columnwidth}{\centering
First
} & \parbox[t]{0.14\columnwidth}{\raggedright
row
} & \parbox[t]{0.16\columnwidth}{\raggedleft
12.0
} & \parbox[t]{0.34\columnwidth}{\raggedright
Example of a row that spans multiple lines.
}
\\\noalign{\medskip}
\parbox[t]{0.15\columnwidth}{\centering
Second
} & \parbox[t]{0.14\columnwidth}{\raggedright
row
} & \parbox[t]{0.16\columnwidth}{\raggedleft
5.0
} & \parbox[t]{0.34\columnwidth}{\raggedright
Here's another one. Note the blank line between rows.
}
\LL
}
Table without column headers:
\begin{center}
\begin{tabular}{rlcr}
12 & 12 & 12 & 12\\
123 & 123 & 123 & 123\\
1 & 1 & 1 & 1\\
\end{tabular}
\end{center}
\ctable[pos = H, center, botcap]{rlcr}
{% notes
}
{% rows
\FL
12 & 12 & 12 & 12
\\\noalign{\medskip}
123 & 123 & 123 & 123
\\\noalign{\medskip}
1 & 1 & 1 & 1
\LL
}
Multiline table without column headers:
\begin{center}
\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}}
\parbox{0.15\columnwidth}{First
} & \parbox{0.14\columnwidth}{row
} & \parbox{0.16\columnwidth}{12.0
} & \parbox{0.34\columnwidth}{Example of a row that spans multiple lines.
}\\
\parbox{0.15\columnwidth}{Second
} & \parbox{0.14\columnwidth}{row
} & \parbox{0.16\columnwidth}{5.0
} & \parbox{0.34\columnwidth}{Here's another one. Note the blank line between
rows.
}\\
\end{tabular}
\end{center}
\ctable[pos = H, center, botcap]{clrl}
{% notes
}
{% rows
\FL
\parbox[t]{0.15\columnwidth}{\centering
First
} & \parbox[t]{0.14\columnwidth}{\raggedright
row
} & \parbox[t]{0.16\columnwidth}{\raggedleft
12.0
} & \parbox[t]{0.34\columnwidth}{\raggedright
Example of a row that spans multiple lines.
}
\\\noalign{\medskip}
\parbox[t]{0.15\columnwidth}{\centering
Second
} & \parbox[t]{0.14\columnwidth}{\raggedright
row
} & \parbox[t]{0.16\columnwidth}{\raggedleft
5.0
} & \parbox[t]{0.34\columnwidth}{\raggedright
Here's another one. Note the blank line between rows.
}
\LL
}