blob: a7e2aa25e57f7bcc917b65aa0de85c6cbb69e74a [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 Zhu465fc652018-05-20 20:02:36 -040033#'
Hao Zhub1e2e3d2018-01-09 13:31:42 -050034#' @examples dt <- mtcars[1:5, 1:5]
Hao Zhu593f57e2018-01-09 13:30:01 -050035#' footnote(knitr::kable(dt, "html"), alphabet = c("Note a", "Note b"))
36#'
Hao Zhu8dd65a92018-01-05 20:40:27 -050037#' @export
Hao Zhucdd7f922018-01-08 11:39:40 -050038footnote <- function(kable_input,
Hao Zhu1ac13ad2018-01-08 16:12:24 -050039 general = NULL,
40 number = NULL,
41 alphabet = NULL,
42 symbol = NULL,
43 footnote_order = c("general", "number",
44 "alphabet", "symbol"),
45 footnote_as_chunk = FALSE,
Hao Zhue0782ab2018-01-09 13:24:13 -050046 escape = TRUE,
Hao Zhu17814c72018-01-10 11:32:14 -050047 threeparttable = FALSE,
Hao Zhu1ac13ad2018-01-08 16:12:24 -050048 general_title = "Note: ",
49 number_title = "",
50 alphabet_title = "",
51 symbol_title = ""
Hao Zhu8dd65a92018-01-05 20:40:27 -050052) {
53 kable_format <- attr(kable_input, "format")
54 if (!kable_format %in% c("html", "latex")) {
Hao Zhu401ebd82018-01-14 17:10:20 -050055 warning("Please specify format in kable. kableExtra can customize either ",
56 "HTML or LaTeX outputs. See https://haozhu233.github.io/kableExtra/ ",
57 "for details.")
Hao Zhu8dd65a92018-01-05 20:40:27 -050058 return(kable_input)
59 }
Hao Zhu8dd65a92018-01-05 20:40:27 -050060 if (length(alphabet) > 26) {
61 alphabet <- alphabet[1:26]
62 warning("Please don't use more than 26 footnotes in table_footnote ",
63 "alphabet. Use number instead.")
64 }
65 if (length(symbol) > 20) {
66 symbol <- symbol[1:20]
67 warning("Please don't use more than 20 footnotes in table_footnote ",
68 "symbol. Use number instead.")
69 }
Hao Zhue0782ab2018-01-09 13:24:13 -050070 footnote_titles <- list(
71 general = general_title, number = number_title,
72 alphabet = alphabet_title, symbol = symbol_title
73 )
74 footnote_contents <- list(
75 general = general, number = number, alphabet = alphabet, symbol = symbol
76 )
77 notnull <- names(footnote_contents)[!sapply(footnote_contents, is.null)]
78 if (length(notnull) == 0) {return(kable_input)}
Hao Zhu8dd65a92018-01-05 20:40:27 -050079 footnote_order <- footnote_order[footnote_order %in% notnull]
80 footnote_titles <- footnote_titles[footnote_order]
81 footnote_contents <- footnote_contents[footnote_order]
Hao Zhue0782ab2018-01-09 13:24:13 -050082 if (escape) {
83 if (kable_format == "html") {
84 footnote_contents <- lapply(footnote_contents, escape_html)
85 footnote_titles <- lapply(footnote_titles, escape_html)
86 } else {
Hao Zhud4630872018-03-26 11:26:36 -040087 footnote_contents <- lapply(footnote_contents, escape_latex2)
Hao Zhu1aff7342018-04-02 18:33:15 -040088 footnote_contents <- lapply(footnote_contents, linebreak)
Hao Zhud4630872018-03-26 11:26:36 -040089 footnote_titles <- lapply(footnote_titles, escape_latex2)
Hao Zhu1aff7342018-04-02 18:33:15 -040090 footnote_titles <- lapply(footnote_titles, linebreak)
Hao Zhue0782ab2018-01-09 13:24:13 -050091 }
92 }
Hao Zhu8dd65a92018-01-05 20:40:27 -050093 footnote_table <- footnote_table_maker(
94 kable_format, footnote_titles, footnote_contents
95 )
96 if (kable_format == "html") {
Hao Zhucdd7f922018-01-08 11:39:40 -050097 return(footnote_html(kable_input, footnote_table, footnote_as_chunk))
Hao Zhu8dd65a92018-01-05 20:40:27 -050098 }
Hao Zhu19c4fa52018-01-09 12:01:14 -050099 if (kable_format == "latex") {
Hao Zhu17814c72018-01-10 11:32:14 -0500100 return(footnote_latex(kable_input, footnote_table, footnote_as_chunk,
101 threeparttable))
Hao Zhu19c4fa52018-01-09 12:01:14 -0500102 }
Hao Zhu8dd65a92018-01-05 20:40:27 -0500103}
104
105footnote_table_maker <- function(format, footnote_titles, footnote_contents) {
106 number_index <- read.csv(system.file("symbol_index.csv",
107 package = "kableExtra"))
108 if (format == "latex") {
109 symbol_index <- number_index$symbol.latex
110 } else {
111 symbol_index <- number_index$symbol.html
112 }
Hao Zhu8dd65a92018-01-05 20:40:27 -0500113
114 if (!is.null(footnote_contents$general)) {
115 footnote_contents$general <- data.frame(
116 index = "",
Hao Zhubab692d2018-01-09 17:49:55 -0500117 footnote = footnote_contents$general
Hao Zhu8dd65a92018-01-05 20:40:27 -0500118 )
119 }
120 if (!is.null(footnote_contents$number)) {
121 footnote_contents$number <- data.frame(
122 index = as.character(1:length(footnote_contents$number)),
123 footnote = footnote_contents$number
124 )
125 }
126 if (!is.null(footnote_contents$alphabet)) {
127 footnote_contents$alphabet <- data.frame(
128 index = letters[1:length(footnote_contents$alphabet)],
129 footnote = footnote_contents$alphabet
130 )
131 }
132 if (!is.null(footnote_contents$symbol)) {
133 footnote_contents$symbol <- data.frame(
134 index = symbol_index[1:length(footnote_contents$symbol)],
135 footnote = footnote_contents$symbol
136 )
137 }
138
139 out <- list()
140 out$contents <- footnote_contents
141 out$titles <- footnote_titles
142 return(out)
143}
144
145# HTML
Hao Zhu1ac13ad2018-01-08 16:12:24 -0500146footnote_html <- function(kable_input, footnote_table, footnote_as_chunk) {
Hao Zhu8dd65a92018-01-05 20:40:27 -0500147 kable_attrs <- attributes(kable_input)
148 kable_xml <- read_kable_as_xml(kable_input)
149
Hao Zhucdd7f922018-01-08 11:39:40 -0500150 new_html_footnote <- html_tfoot_maker(footnote_table, footnote_as_chunk)
Hao Zhu8dd65a92018-01-05 20:40:27 -0500151 xml_add_child(kable_xml, new_html_footnote)
152
153 out <- as_kable_xml(kable_xml)
154 attributes(out) <- kable_attrs
Hao Zhuf2100832018-01-11 16:20:29 -0500155 if (!"kableExtra" %in% class(out)) class(out) <- c("kableExtra", class(out))
Hao Zhu8dd65a92018-01-05 20:40:27 -0500156 return(out)
157}
158
Hao Zhucdd7f922018-01-08 11:39:40 -0500159html_tfoot_maker <- function(footnote_table, footnote_as_chunk) {
Hao Zhu8dd65a92018-01-05 20:40:27 -0500160 footnote_types <- names(footnote_table$contents)
161 footnote_text <- c()
162 for (i in footnote_types) {
Hao Zhucdd7f922018-01-08 11:39:40 -0500163 footnote_text <- c(footnote_text, html_tfoot_maker_(
Hao Zhu8dd65a92018-01-05 20:40:27 -0500164 footnote_table$contents[[i]], footnote_table$titles[[i]], i,
165 footnote_as_chunk))
166 }
167 footnote_text <- paste0(
168 "<tfoot>", paste0(footnote_text, collapse = ""), "</tfoot>"
169 )
170 footnote_node <- read_html(footnote_text, options = c("RECOVER", "NOERROR"))
171 return(xml_child(xml_child(footnote_node, 1), 1))
172}
173
Hao Zhucdd7f922018-01-08 11:39:40 -0500174html_tfoot_maker_ <- function(ft_contents, ft_title, ft_type, ft_chunk) {
Hao Zhu8dd65a92018-01-05 20:40:27 -0500175 footnote_text <- apply(ft_contents, 1, function(x) {
176 paste0('<sup>', x[1], '</sup> ', x[2])
177 })
178 if (ft_title != "") {
Hao Zhu465fc652018-05-20 20:02:36 -0400179 title_text <- paste0('<em>', ft_title, '</em>')
Hao Zhu8dd65a92018-01-05 20:40:27 -0500180 footnote_text <- c(title_text, footnote_text)
181 }
182 if (!ft_chunk) {
183 footnote_text <- paste0(
184 '<tr><td style="padding: 0; border: 0;" colspan="100%">',
185 footnote_text, '</td></tr>'
186 )
187 } else {
188 footnote_text <- paste0(
189 '<tr><td style="padding: 0; border: 0;" colspan="100%">',
Hao Zhucdd7f922018-01-08 11:39:40 -0500190 paste0(footnote_text, collapse = " "),
Hao Zhu8dd65a92018-01-05 20:40:27 -0500191 '</td></tr>'
192 )
193 }
Hao Zhu8dd65a92018-01-05 20:40:27 -0500194 return(footnote_text)
195}
Hao Zhucdd7f922018-01-08 11:39:40 -0500196
197# LaTeX
Hao Zhu17814c72018-01-10 11:32:14 -0500198footnote_latex <- function(kable_input, footnote_table, footnote_as_chunk,
199 threeparttable) {
Hao Zhu1ac13ad2018-01-08 16:12:24 -0500200 table_info <- magic_mirror(kable_input)
Hao Zhu3fc0e882018-04-03 16:06:41 -0400201 out <- solve_enc(kable_input)
Hao Zhu17814c72018-01-10 11:32:14 -0500202
Hao Zhu19c4fa52018-01-09 12:01:14 -0500203 footnote_text <- latex_tfoot_maker(footnote_table, footnote_as_chunk,
Hao Zhu17814c72018-01-10 11:32:14 -0500204 table_info$ncol, threeparttable)
205 if (threeparttable) {
Hao Zhu23bde3a2018-03-28 16:00:55 -0400206 if (table_info$tabular %in% c("longtable", "longtabu") ) {
Hao Zhu17814c72018-01-10 11:32:14 -0500207 out <- sub(paste0("\\\\begin\\{", table_info$tabular, "\\}"),
Hao Zhu27c7c852018-03-26 16:18:23 -0400208 paste0("\\\\begin{ThreePartTable}\n\\\\begin{TableNotes}",
209 ifelse(footnote_as_chunk, "[para]", ""),
Hao Zhu5b8eb2b2018-05-20 18:16:06 -0400210 "\n", footnote_text,
Hao Zhu27c7c852018-03-26 16:18:23 -0400211 "\n\\\\end{TableNotes}\n\\\\begin{",
Hao Zhu17814c72018-01-10 11:32:14 -0500212 table_info$tabular, "}"),
213 out)
Hao Zhu23bde3a2018-03-28 16:00:55 -0400214 out <- sub(paste0("\\\\end\\{",table_info$tabular, "\\}"),
215 paste0("\\\\end{", table_info$tabular,
216 "}\n\\\\end{ThreePartTable}"),
217 out)
218 if (table_info$booktabs) {
219 out <- sub("\\\\bottomrule", "\\\\bottomrule\n\\\\insertTableNotes", out)
220 } else {
221 out <- sub("\\\\hline\n\\\\end\\{longtable\\}",
222 "\\\\hline\n\\\\insertTableNotes\n\\\\end\\{longtable\\}",
223 out)
224 }
Hao Zhu27c7c852018-03-26 16:18:23 -0400225 } else {
Hao Zhu23bde3a2018-03-28 16:00:55 -0400226 if (table_info$tabular == "tabu") {
227 stop("Please use `longtable = T` in your kable function. ",
228 "Full width threeparttable only works with longtable.")
229 }
230 out <- sub(paste0("\\\\begin\\{", table_info$tabular, "\\}"),
231 paste0("\\\\begin{threeparttable}\n\\\\begin{",
232 table_info$tabular, "}"),
233 out)
234 out <- sub(table_info$end_tabular,
235 paste0("\\\\end{", table_info$tabular,
236 "}\n\\\\begin{tablenotes}",
237 ifelse(footnote_as_chunk, "[para]", ""),
Hao Zhu5b8eb2b2018-05-20 18:16:06 -0400238 "\n", footnote_text,
Hao Zhu23bde3a2018-03-28 16:00:55 -0400239 "\n\\\\end{tablenotes}\n\\\\end{threeparttable}"),
Hao Zhu27c7c852018-03-26 16:18:23 -0400240 out)
241 }
Hao Zhu17814c72018-01-10 11:32:14 -0500242 } else {
Hao Zhu23bde3a2018-03-28 16:00:55 -0400243 if (table_info$booktabs) {
244 out <- sub("\\\\bottomrule",
245 paste0("\\\\bottomrule\n", footnote_text), out)
246 } else {
247 out <- sub(table_info$end_tabular,
248 paste0(footnote_text, "\n\\\\end{", table_info$tabular, "}"),
249 out)
250 }
Hao Zhu17814c72018-01-10 11:32:14 -0500251 }
252
Hao Zhu19c4fa52018-01-09 12:01:14 -0500253 out <- structure(out, format = "latex", class = "knitr_kable")
254 attr(out, "kable_meta") <- table_info
255 return(out)
Hao Zhu19c4fa52018-01-09 12:01:14 -0500256}
Hao Zhucdd7f922018-01-08 11:39:40 -0500257
Hao Zhu17814c72018-01-10 11:32:14 -0500258latex_tfoot_maker <- function(footnote_table, footnote_as_chunk, ncol,
259 threeparttable) {
Hao Zhu19c4fa52018-01-09 12:01:14 -0500260 footnote_types <- names(footnote_table$contents)
261 footnote_text <- c()
Hao Zhu17814c72018-01-10 11:32:14 -0500262 if (threeparttable) {
263 for (i in footnote_types) {
264 footnote_text <- c(footnote_text, latex_tfoot_maker_tpt_(
265 footnote_table$contents[[i]], footnote_table$titles[[i]],
266 footnote_as_chunk, ncol))
267 }
268 } else {
269 for (i in footnote_types) {
270 footnote_text <- c(footnote_text, latex_tfoot_maker_(
271 footnote_table$contents[[i]], footnote_table$titles[[i]],
272 footnote_as_chunk, ncol))
273 }
Hao Zhu19c4fa52018-01-09 12:01:14 -0500274 }
275 footnote_text <- paste0(footnote_text, collapse = "\n")
276 return(footnote_text)
277}
Hao Zhu9f917482018-01-08 18:09:33 -0500278
Hao Zhu17814c72018-01-10 11:32:14 -0500279latex_tfoot_maker_ <- function(ft_contents, ft_title, ft_chunk, ncol) {
Hao Zhu19c4fa52018-01-09 12:01:14 -0500280 footnote_text <- apply(ft_contents, 1, function(x) {
281 if (x[1] == "") {
282 x[2]
283 } else {
284 paste0('\\\\textsuperscript{', x[1], '} ', x[2])
285 }
286 })
287 if (ft_title != "") {
Hao Zhu465fc652018-05-20 20:02:36 -0400288 title_text <- paste0('\\\\textit{', ft_title, '} ')
Hao Zhu19c4fa52018-01-09 12:01:14 -0500289 footnote_text <- c(title_text, footnote_text)
290 }
291 if (!ft_chunk) {
292 footnote_text <- paste0(
293 '\\\\multicolumn{', ncol, '}{l}{', footnote_text, '}\\\\\\\\'
294 )
295 } else {
296 footnote_text <- paste0(
297 '\\\\multicolumn{', ncol, '}{l}{',
298 paste0(footnote_text, collapse = " "),
299 '}\\\\\\\\'
300 )
301 }
302 return(footnote_text)
Hao Zhucdd7f922018-01-08 11:39:40 -0500303}
Hao Zhu17814c72018-01-10 11:32:14 -0500304
305latex_tfoot_maker_tpt_ <- function(ft_contents, ft_title, ft_chunk, ncol) {
306 footnote_text <- apply(ft_contents, 1, function(x) {
307 if (x[1] == "") {
308 paste0('\\\\item ', x[2])
309 } else {
310 paste0('\\\\item[', x[1], '] ', x[2])
311 }
312 })
313 if (ft_title != "") {
Hao Zhu465fc652018-05-20 20:02:36 -0400314 title_text <- paste0('\\\\item \\\\textit{', ft_title, '} ')
Hao Zhu17814c72018-01-10 11:32:14 -0500315 footnote_text <- c(title_text, footnote_text)
316 }
317 footnote_text <- paste0(footnote_text, collapse = "\n")
318 # if (!ft_chunk) {
319 # footnote_text <- paste0(footnote_text, collapse = "\n")
320 # } else {
321 # footnote_text <- paste0(footnote_text, collapse = " ")
322 # }
323 return(footnote_text)
324}