blob: a05360feea0d9c000ec1fd991edf320eba5c89e6 [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
150 return(out)
151}
152
Hao Zhucdd7f922018-01-08 11:39:40 -0500153html_tfoot_maker <- function(footnote_table, footnote_as_chunk) {
Hao Zhu8dd65a92018-01-05 20:40:27 -0500154 footnote_types <- names(footnote_table$contents)
155 footnote_text <- c()
156 for (i in footnote_types) {
Hao Zhucdd7f922018-01-08 11:39:40 -0500157 footnote_text <- c(footnote_text, html_tfoot_maker_(
Hao Zhu8dd65a92018-01-05 20:40:27 -0500158 footnote_table$contents[[i]], footnote_table$titles[[i]], i,
159 footnote_as_chunk))
160 }
161 footnote_text <- paste0(
162 "<tfoot>", paste0(footnote_text, collapse = ""), "</tfoot>"
163 )
164 footnote_node <- read_html(footnote_text, options = c("RECOVER", "NOERROR"))
165 return(xml_child(xml_child(footnote_node, 1), 1))
166}
167
Hao Zhucdd7f922018-01-08 11:39:40 -0500168html_tfoot_maker_ <- function(ft_contents, ft_title, ft_type, ft_chunk) {
Hao Zhu8dd65a92018-01-05 20:40:27 -0500169 footnote_text <- apply(ft_contents, 1, function(x) {
170 paste0('<sup>', x[1], '</sup> ', x[2])
171 })
172 if (ft_title != "") {
173 title_text <- paste0('<strong>', ft_title, '</strong>')
174 footnote_text <- c(title_text, footnote_text)
175 }
176 if (!ft_chunk) {
177 footnote_text <- paste0(
178 '<tr><td style="padding: 0; border: 0;" colspan="100%">',
179 footnote_text, '</td></tr>'
180 )
181 } else {
182 footnote_text <- paste0(
183 '<tr><td style="padding: 0; border: 0;" colspan="100%">',
Hao Zhucdd7f922018-01-08 11:39:40 -0500184 paste0(footnote_text, collapse = " "),
Hao Zhu8dd65a92018-01-05 20:40:27 -0500185 '</td></tr>'
186 )
187 }
Hao Zhu8dd65a92018-01-05 20:40:27 -0500188 return(footnote_text)
189}
Hao Zhucdd7f922018-01-08 11:39:40 -0500190
191# LaTeX
Hao Zhu17814c72018-01-10 11:32:14 -0500192footnote_latex <- function(kable_input, footnote_table, footnote_as_chunk,
193 threeparttable) {
Hao Zhu1ac13ad2018-01-08 16:12:24 -0500194 table_info <- magic_mirror(kable_input)
Hao Zhu19c4fa52018-01-09 12:01:14 -0500195 out <- enc2utf8(as.character(kable_input))
Hao Zhu17814c72018-01-10 11:32:14 -0500196
Hao Zhu25028072018-01-10 12:08:51 -0500197 if (table_info$tabular == "longtable" & threeparttable == TRUE) {
Hao Zhu17814c72018-01-10 11:32:14 -0500198 threeparttable <- FALSE
199 warning("threeparttable does not support longtable.")
200 }
Hao Zhu19c4fa52018-01-09 12:01:14 -0500201 footnote_text <- latex_tfoot_maker(footnote_table, footnote_as_chunk,
Hao Zhu17814c72018-01-10 11:32:14 -0500202 table_info$ncol, threeparttable)
203 if (threeparttable) {
204 if (grepl("\\\\caption\\{.*?\\}", out)) {
205 out <- sub("\\\\caption\\{", "\\\\begin{threeparttable}\n\\\\caption{",
206 out)
207 } else {
208 out <- sub(paste0("\\\\begin\\{", table_info$tabular, "\\}"),
209 paste0("\\\\begin{threeparttable}\n\\\\begin{",
210 table_info$tabular, "}"),
211 out)
212 }
213 out <- sub(table_info$end_tabular,
214 paste0("\\\\end{", table_info$tabular,
215 "}\n\\\\begin{tablenotes}",
216 ifelse(footnote_as_chunk, "[para]", ""),
217 "\n\\\\small\n", footnote_text,
218 "\n\\\\end{tablenotes}\n\\\\end{threeparttable}"),
219 out)
220 } else {
221 out <- sub(table_info$end_tabular,
222 paste0(footnote_text, "\n\\\\end{", table_info$tabular, "}"),
223 out)
224 }
225
Hao Zhu19c4fa52018-01-09 12:01:14 -0500226 out <- structure(out, format = "latex", class = "knitr_kable")
227 attr(out, "kable_meta") <- table_info
228 return(out)
Hao Zhu19c4fa52018-01-09 12:01:14 -0500229}
Hao Zhucdd7f922018-01-08 11:39:40 -0500230
Hao Zhu17814c72018-01-10 11:32:14 -0500231latex_tfoot_maker <- function(footnote_table, footnote_as_chunk, ncol,
232 threeparttable) {
Hao Zhu19c4fa52018-01-09 12:01:14 -0500233 footnote_types <- names(footnote_table$contents)
234 footnote_text <- c()
Hao Zhu17814c72018-01-10 11:32:14 -0500235 if (threeparttable) {
236 for (i in footnote_types) {
237 footnote_text <- c(footnote_text, latex_tfoot_maker_tpt_(
238 footnote_table$contents[[i]], footnote_table$titles[[i]],
239 footnote_as_chunk, ncol))
240 }
241 } else {
242 for (i in footnote_types) {
243 footnote_text <- c(footnote_text, latex_tfoot_maker_(
244 footnote_table$contents[[i]], footnote_table$titles[[i]],
245 footnote_as_chunk, ncol))
246 }
Hao Zhu19c4fa52018-01-09 12:01:14 -0500247 }
248 footnote_text <- paste0(footnote_text, collapse = "\n")
249 return(footnote_text)
250}
Hao Zhu9f917482018-01-08 18:09:33 -0500251
Hao Zhu17814c72018-01-10 11:32:14 -0500252latex_tfoot_maker_ <- function(ft_contents, ft_title, ft_chunk, ncol) {
Hao Zhu19c4fa52018-01-09 12:01:14 -0500253 footnote_text <- apply(ft_contents, 1, function(x) {
254 if (x[1] == "") {
255 x[2]
256 } else {
257 paste0('\\\\textsuperscript{', x[1], '} ', x[2])
258 }
259 })
260 if (ft_title != "") {
261 title_text <- paste0('\\\\textbf{', ft_title, '} ')
262 footnote_text <- c(title_text, footnote_text)
263 }
264 if (!ft_chunk) {
265 footnote_text <- paste0(
266 '\\\\multicolumn{', ncol, '}{l}{', footnote_text, '}\\\\\\\\'
267 )
268 } else {
269 footnote_text <- paste0(
270 '\\\\multicolumn{', ncol, '}{l}{',
271 paste0(footnote_text, collapse = " "),
272 '}\\\\\\\\'
273 )
274 }
275 return(footnote_text)
Hao Zhucdd7f922018-01-08 11:39:40 -0500276}
Hao Zhu17814c72018-01-10 11:32:14 -0500277
278latex_tfoot_maker_tpt_ <- function(ft_contents, ft_title, ft_chunk, ncol) {
279 footnote_text <- apply(ft_contents, 1, function(x) {
280 if (x[1] == "") {
281 paste0('\\\\item ', x[2])
282 } else {
283 paste0('\\\\item[', x[1], '] ', x[2])
284 }
285 })
286 if (ft_title != "") {
287 title_text <- paste0('\\\\item \\\\textbf{', ft_title, '} ')
288 footnote_text <- c(title_text, footnote_text)
289 }
290 footnote_text <- paste0(footnote_text, collapse = "\n")
291 # if (!ft_chunk) {
292 # footnote_text <- paste0(footnote_text, collapse = "\n")
293 # } else {
294 # footnote_text <- paste0(footnote_text, collapse = " ")
295 # }
296 return(footnote_text)
297}