blob: bb48e0df02820296b3a82212e536fe0a4d196460 [file] [log] [blame]
Hao Zhucdd7f922018-01-08 11:39:40 -05001#' Add footnote (new)
Hao Zhu8dd65a92018-01-05 20:40:27 -05002#'
Hao Zhue0782ab2018-01-09 13:24:13 -05003#' @description `footnote` provides a more flexible way to add footnote. You
4#' can add mutiple sets of footnote using differeny notation system. It is
5#' also possible to specify footnote section header one by one and print
6#' footnotes as a chunk of texts.
7#'
8#' @param kable_input HTML or LaTeX table generated by `knitr::kable`
9#' @param general Text for general footnote comments. Footnotes in this section
10#' won't be labeled with any notations
11#' @param number A vector of footnote texts. Footnotes here will be numbered.
12#' There is no upper cap for the number of footnotes here
13#' @param alphabet A vector of footnote texts, Footnotes here will be labeled
14#' with abc. The vector here should not have more than 26 elements.
15#' @param symbol A vector of footnote texts, Footnotes here will be labeled
16#' with special symbols. The vector here should not have more than 20 elements.
17#' @param footnote_order The order of how to arrange `general`, `number`,
18#' `alphabet` and `symbol`.
19#' @param footnote_as_chunk T/F value. Default is FALSE. It controls whether
20#' the footnotes should be printed in a chunk (without line break).
21#' @param escape T/F value. It controls whether the contents and titles should
22#' be escaped against HTML or LaTeX. Default is TRUE.
Hao Zhu17814c72018-01-10 11:32:14 -050023#' @param threeparttable T/F value for whether to use LaTeX package
24#' threeparttable. Threeparttable will force the width of caption and
25#' footnotes be the width of the original table. It's useful when you have
26#' long paragraph of footnotes.
Hao Zhue0782ab2018-01-09 13:24:13 -050027#' @param general_title Section header for general footnotes. Default is
28#' "Note: ".
29#' @param number_title Section header for number footnotes. Default is "".
30#' @param alphabet_title Section header for alphabet footnotes. Default is "".
31#' @param symbol_title Section header for symbol footnotes. Default is "".
32#'
Hao Zhub1e2e3d2018-01-09 13:31:42 -050033#' @examples dt <- mtcars[1:5, 1:5]
Hao Zhu593f57e2018-01-09 13:30:01 -050034#' footnote(knitr::kable(dt, "html"), alphabet = c("Note a", "Note b"))
35#'
Hao Zhu8dd65a92018-01-05 20:40:27 -050036#' @export
Hao Zhucdd7f922018-01-08 11:39:40 -050037footnote <- function(kable_input,
Hao Zhu1ac13ad2018-01-08 16:12:24 -050038 general = NULL,
39 number = NULL,
40 alphabet = NULL,
41 symbol = NULL,
42 footnote_order = c("general", "number",
43 "alphabet", "symbol"),
44 footnote_as_chunk = FALSE,
Hao Zhue0782ab2018-01-09 13:24:13 -050045 escape = TRUE,
Hao Zhu17814c72018-01-10 11:32:14 -050046 threeparttable = FALSE,
Hao Zhu1ac13ad2018-01-08 16:12:24 -050047 general_title = "Note: ",
48 number_title = "",
49 alphabet_title = "",
50 symbol_title = ""
Hao Zhu8dd65a92018-01-05 20:40:27 -050051) {
52 kable_format <- attr(kable_input, "format")
53 if (!kable_format %in% c("html", "latex")) {
54 message("Currently generic markdown table using pandoc is not supported.")
55 return(kable_input)
56 }
Hao Zhu8dd65a92018-01-05 20:40:27 -050057 if (length(alphabet) > 26) {
58 alphabet <- alphabet[1:26]
59 warning("Please don't use more than 26 footnotes in table_footnote ",
60 "alphabet. Use number instead.")
61 }
62 if (length(symbol) > 20) {
63 symbol <- symbol[1:20]
64 warning("Please don't use more than 20 footnotes in table_footnote ",
65 "symbol. Use number instead.")
66 }
Hao Zhue0782ab2018-01-09 13:24:13 -050067 footnote_titles <- list(
68 general = general_title, number = number_title,
69 alphabet = alphabet_title, symbol = symbol_title
70 )
71 footnote_contents <- list(
72 general = general, number = number, alphabet = alphabet, symbol = symbol
73 )
74 notnull <- names(footnote_contents)[!sapply(footnote_contents, is.null)]
75 if (length(notnull) == 0) {return(kable_input)}
Hao Zhu8dd65a92018-01-05 20:40:27 -050076 footnote_order <- footnote_order[footnote_order %in% notnull]
77 footnote_titles <- footnote_titles[footnote_order]
78 footnote_contents <- footnote_contents[footnote_order]
Hao Zhue0782ab2018-01-09 13:24:13 -050079 if (escape) {
80 if (kable_format == "html") {
81 footnote_contents <- lapply(footnote_contents, escape_html)
82 footnote_titles <- lapply(footnote_titles, escape_html)
83 } else {
84 footnote_contents <- lapply(footnote_contents, escape_latex)
85 footnote_titles <- lapply(footnote_titles, escape_latex)
86 }
87 }
Hao Zhu8dd65a92018-01-05 20:40:27 -050088 footnote_table <- footnote_table_maker(
89 kable_format, footnote_titles, footnote_contents
90 )
91 if (kable_format == "html") {
Hao Zhucdd7f922018-01-08 11:39:40 -050092 return(footnote_html(kable_input, footnote_table, footnote_as_chunk))
Hao Zhu8dd65a92018-01-05 20:40:27 -050093 }
Hao Zhu19c4fa52018-01-09 12:01:14 -050094 if (kable_format == "latex") {
Hao Zhu17814c72018-01-10 11:32:14 -050095 return(footnote_latex(kable_input, footnote_table, footnote_as_chunk,
96 threeparttable))
Hao Zhu19c4fa52018-01-09 12:01:14 -050097 }
Hao Zhu8dd65a92018-01-05 20:40:27 -050098}
99
100footnote_table_maker <- function(format, footnote_titles, footnote_contents) {
101 number_index <- read.csv(system.file("symbol_index.csv",
102 package = "kableExtra"))
103 if (format == "latex") {
104 symbol_index <- number_index$symbol.latex
105 } else {
106 symbol_index <- number_index$symbol.html
107 }
Hao Zhu8dd65a92018-01-05 20:40:27 -0500108
109 if (!is.null(footnote_contents$general)) {
110 footnote_contents$general <- data.frame(
111 index = "",
Hao Zhubab692d2018-01-09 17:49:55 -0500112 footnote = footnote_contents$general
Hao Zhu8dd65a92018-01-05 20:40:27 -0500113 )
114 }
115 if (!is.null(footnote_contents$number)) {
116 footnote_contents$number <- data.frame(
117 index = as.character(1:length(footnote_contents$number)),
118 footnote = footnote_contents$number
119 )
120 }
121 if (!is.null(footnote_contents$alphabet)) {
122 footnote_contents$alphabet <- data.frame(
123 index = letters[1:length(footnote_contents$alphabet)],
124 footnote = footnote_contents$alphabet
125 )
126 }
127 if (!is.null(footnote_contents$symbol)) {
128 footnote_contents$symbol <- data.frame(
129 index = symbol_index[1:length(footnote_contents$symbol)],
130 footnote = footnote_contents$symbol
131 )
132 }
133
134 out <- list()
135 out$contents <- footnote_contents
136 out$titles <- footnote_titles
137 return(out)
138}
139
140# HTML
Hao Zhu1ac13ad2018-01-08 16:12:24 -0500141footnote_html <- function(kable_input, footnote_table, footnote_as_chunk) {
Hao Zhu8dd65a92018-01-05 20:40:27 -0500142 kable_attrs <- attributes(kable_input)
143 kable_xml <- read_kable_as_xml(kable_input)
144
Hao Zhucdd7f922018-01-08 11:39:40 -0500145 new_html_footnote <- html_tfoot_maker(footnote_table, footnote_as_chunk)
Hao Zhu8dd65a92018-01-05 20:40:27 -0500146 xml_add_child(kable_xml, new_html_footnote)
147
148 out <- as_kable_xml(kable_xml)
149 attributes(out) <- kable_attrs
Hao Zhuf2100832018-01-11 16:20:29 -0500150 if (!"kableExtra" %in% class(out)) class(out) <- c("kableExtra", class(out))
Hao Zhu8dd65a92018-01-05 20:40:27 -0500151 return(out)
152}
153
Hao Zhucdd7f922018-01-08 11:39:40 -0500154html_tfoot_maker <- function(footnote_table, footnote_as_chunk) {
Hao Zhu8dd65a92018-01-05 20:40:27 -0500155 footnote_types <- names(footnote_table$contents)
156 footnote_text <- c()
157 for (i in footnote_types) {
Hao Zhucdd7f922018-01-08 11:39:40 -0500158 footnote_text <- c(footnote_text, html_tfoot_maker_(
Hao Zhu8dd65a92018-01-05 20:40:27 -0500159 footnote_table$contents[[i]], footnote_table$titles[[i]], i,
160 footnote_as_chunk))
161 }
162 footnote_text <- paste0(
163 "<tfoot>", paste0(footnote_text, collapse = ""), "</tfoot>"
164 )
165 footnote_node <- read_html(footnote_text, options = c("RECOVER", "NOERROR"))
166 return(xml_child(xml_child(footnote_node, 1), 1))
167}
168
Hao Zhucdd7f922018-01-08 11:39:40 -0500169html_tfoot_maker_ <- function(ft_contents, ft_title, ft_type, ft_chunk) {
Hao Zhu8dd65a92018-01-05 20:40:27 -0500170 footnote_text <- apply(ft_contents, 1, function(x) {
171 paste0('<sup>', x[1], '</sup> ', x[2])
172 })
173 if (ft_title != "") {
174 title_text <- paste0('<strong>', ft_title, '</strong>')
175 footnote_text <- c(title_text, footnote_text)
176 }
177 if (!ft_chunk) {
178 footnote_text <- paste0(
179 '<tr><td style="padding: 0; border: 0;" colspan="100%">',
180 footnote_text, '</td></tr>'
181 )
182 } else {
183 footnote_text <- paste0(
184 '<tr><td style="padding: 0; border: 0;" colspan="100%">',
Hao Zhucdd7f922018-01-08 11:39:40 -0500185 paste0(footnote_text, collapse = " "),
Hao Zhu8dd65a92018-01-05 20:40:27 -0500186 '</td></tr>'
187 )
188 }
Hao Zhu8dd65a92018-01-05 20:40:27 -0500189 return(footnote_text)
190}
Hao Zhucdd7f922018-01-08 11:39:40 -0500191
192# LaTeX
Hao Zhu17814c72018-01-10 11:32:14 -0500193footnote_latex <- function(kable_input, footnote_table, footnote_as_chunk,
194 threeparttable) {
Hao Zhu1ac13ad2018-01-08 16:12:24 -0500195 table_info <- magic_mirror(kable_input)
Hao Zhu19c4fa52018-01-09 12:01:14 -0500196 out <- enc2utf8(as.character(kable_input))
Hao Zhu17814c72018-01-10 11:32:14 -0500197
Hao Zhu25028072018-01-10 12:08:51 -0500198 if (table_info$tabular == "longtable" & threeparttable == TRUE) {
Hao Zhu17814c72018-01-10 11:32:14 -0500199 threeparttable <- FALSE
200 warning("threeparttable does not support longtable.")
201 }
Hao Zhu19c4fa52018-01-09 12:01:14 -0500202 footnote_text <- latex_tfoot_maker(footnote_table, footnote_as_chunk,
Hao Zhu17814c72018-01-10 11:32:14 -0500203 table_info$ncol, threeparttable)
204 if (threeparttable) {
205 if (grepl("\\\\caption\\{.*?\\}", out)) {
206 out <- sub("\\\\caption\\{", "\\\\begin{threeparttable}\n\\\\caption{",
207 out)
208 } else {
209 out <- sub(paste0("\\\\begin\\{", table_info$tabular, "\\}"),
210 paste0("\\\\begin{threeparttable}\n\\\\begin{",
211 table_info$tabular, "}"),
212 out)
213 }
214 out <- sub(table_info$end_tabular,
215 paste0("\\\\end{", table_info$tabular,
216 "}\n\\\\begin{tablenotes}",
217 ifelse(footnote_as_chunk, "[para]", ""),
218 "\n\\\\small\n", footnote_text,
219 "\n\\\\end{tablenotes}\n\\\\end{threeparttable}"),
220 out)
221 } else {
222 out <- sub(table_info$end_tabular,
223 paste0(footnote_text, "\n\\\\end{", table_info$tabular, "}"),
224 out)
225 }
226
Hao Zhu19c4fa52018-01-09 12:01:14 -0500227 out <- structure(out, format = "latex", class = "knitr_kable")
228 attr(out, "kable_meta") <- table_info
229 return(out)
Hao Zhu19c4fa52018-01-09 12:01:14 -0500230}
Hao Zhucdd7f922018-01-08 11:39:40 -0500231
Hao Zhu17814c72018-01-10 11:32:14 -0500232latex_tfoot_maker <- function(footnote_table, footnote_as_chunk, ncol,
233 threeparttable) {
Hao Zhu19c4fa52018-01-09 12:01:14 -0500234 footnote_types <- names(footnote_table$contents)
235 footnote_text <- c()
Hao Zhu17814c72018-01-10 11:32:14 -0500236 if (threeparttable) {
237 for (i in footnote_types) {
238 footnote_text <- c(footnote_text, latex_tfoot_maker_tpt_(
239 footnote_table$contents[[i]], footnote_table$titles[[i]],
240 footnote_as_chunk, ncol))
241 }
242 } else {
243 for (i in footnote_types) {
244 footnote_text <- c(footnote_text, latex_tfoot_maker_(
245 footnote_table$contents[[i]], footnote_table$titles[[i]],
246 footnote_as_chunk, ncol))
247 }
Hao Zhu19c4fa52018-01-09 12:01:14 -0500248 }
249 footnote_text <- paste0(footnote_text, collapse = "\n")
250 return(footnote_text)
251}
Hao Zhu9f917482018-01-08 18:09:33 -0500252
Hao Zhu17814c72018-01-10 11:32:14 -0500253latex_tfoot_maker_ <- function(ft_contents, ft_title, ft_chunk, ncol) {
Hao Zhu19c4fa52018-01-09 12:01:14 -0500254 footnote_text <- apply(ft_contents, 1, function(x) {
255 if (x[1] == "") {
256 x[2]
257 } else {
258 paste0('\\\\textsuperscript{', x[1], '} ', x[2])
259 }
260 })
261 if (ft_title != "") {
262 title_text <- paste0('\\\\textbf{', ft_title, '} ')
263 footnote_text <- c(title_text, footnote_text)
264 }
265 if (!ft_chunk) {
266 footnote_text <- paste0(
267 '\\\\multicolumn{', ncol, '}{l}{', footnote_text, '}\\\\\\\\'
268 )
269 } else {
270 footnote_text <- paste0(
271 '\\\\multicolumn{', ncol, '}{l}{',
272 paste0(footnote_text, collapse = " "),
273 '}\\\\\\\\'
274 )
275 }
276 return(footnote_text)
Hao Zhucdd7f922018-01-08 11:39:40 -0500277}
Hao Zhu17814c72018-01-10 11:32:14 -0500278
279latex_tfoot_maker_tpt_ <- function(ft_contents, ft_title, ft_chunk, ncol) {
280 footnote_text <- apply(ft_contents, 1, function(x) {
281 if (x[1] == "") {
282 paste0('\\\\item ', x[2])
283 } else {
284 paste0('\\\\item[', x[1], '] ', x[2])
285 }
286 })
287 if (ft_title != "") {
288 title_text <- paste0('\\\\item \\\\textbf{', ft_title, '} ')
289 footnote_text <- c(title_text, footnote_text)
290 }
291 footnote_text <- paste0(footnote_text, collapse = "\n")
292 # if (!ft_chunk) {
293 # footnote_text <- paste0(footnote_text, collapse = "\n")
294 # } else {
295 # footnote_text <- paste0(footnote_text, collapse = " ")
296 # }
297 return(footnote_text)
298}