blob: dafcc23bde979c071539069ba5e2fbe5e214fa67 [file] [log] [blame]
Hao Zhudb04e302015-11-15 16:57:38 -05001#' Magic mirror that returns kable's attributes
2#'
Hao Zhuf7994dd2017-02-27 16:58:42 -05003#' @description Mirror mirror tell me, how does this kable look like?
4#'
5#' @param kable_input The output of kable
Hao Zhu78e61222017-05-24 20:53:35 -04006#'
7#' @examples magic_mirror(knitr::kable(head(mtcars), "html"))
Hao Zhudb04e302015-11-15 16:57:38 -05008#' @export
9
Hao Zhuf7994dd2017-02-27 16:58:42 -050010magic_mirror <- function(kable_input){
Hao Zhuf7994dd2017-02-27 16:58:42 -050011 kable_format <- attr(kable_input, "format")
12 if (kable_format == "latex") {
Hao Zhu24bf30c2019-01-22 02:14:33 -050013 table_info <- magic_mirror_latex(kable_input)
Hao Zhudb04e302015-11-15 16:57:38 -050014 }
Hao Zhuf7994dd2017-02-27 16:58:42 -050015 if (kable_format == "html") {
Hao Zhu24bf30c2019-01-22 02:14:33 -050016 table_info <- magic_mirror_html(kable_input)
Hao Zhudb04e302015-11-15 16:57:38 -050017 }
Vincent Arel-Bundockef8ffce2021-01-19 08:55:16 -050018 if ("kable_meta" %in% names(attributes(kable_input))) {
19 out <- attr(kable_input, "kable_meta")
20 # if we return `kable_meta` immediately, `kable_styling` will use the
21 # original `table_env` value. So if we call `kable_styling` twice on the
22 # same object, it will nest a table within a table. Make sure this does not
23 # happen.
24 if (kable_format == "latex") {
25 table_info <- magic_mirror_latex(kable_input)
26 if (table_info$table_env && !out$table_env) {
27 out$table_env <- table_info$table_env
28 }
29 }
30 return(out)
31 }
Hao Zhu24bf30c2019-01-22 02:14:33 -050032 return(table_info)
Hao Zhudb04e302015-11-15 16:57:38 -050033}
34
Hao Zhu953f3bd2017-07-28 11:43:40 -040035# Magic mirror for latex tables --------------
Hao Zhuf7994dd2017-02-27 16:58:42 -050036magic_mirror_latex <- function(kable_input){
Hao Zhu24bf30c2019-01-22 02:14:33 -050037 table_info <- list(tabular = NULL, booktabs = FALSE, align = NULL,
Hao Zhuc05e1812017-02-25 01:45:35 -050038 valign = NULL, ncol = NULL, nrow = NULL, colnames = NULL,
Hao Zhud57c2d72017-08-16 22:51:17 -040039 rownames = NULL, caption = NULL, caption.short = NULL,
40 contents = NULL,
Hao Zhuc05e1812017-02-25 01:45:35 -050041 centering = FALSE, table_env = FALSE)
Hao Zhu4adea852015-11-16 16:38:34 -050042 # Tabular
Hao Zhu24bf30c2019-01-22 02:14:33 -050043 table_info$tabular <- ifelse(
Hao Zhuf7994dd2017-02-27 16:58:42 -050044 grepl("\\\\begin\\{tabular\\}", kable_input),
Hao Zhu4adea852015-11-16 16:38:34 -050045 "tabular", "longtable"
Hao Zhudb04e302015-11-15 16:57:38 -050046 )
Hao Zhu4adea852015-11-16 16:38:34 -050047 # Booktabs
Hao Zhu24bf30c2019-01-22 02:14:33 -050048 table_info$booktabs <- grepl("\\\\toprule", kable_input)
Hao Zhu4adea852015-11-16 16:38:34 -050049 # Align
Hao Zhu24bf30c2019-01-22 02:14:33 -050050 table_info$align <- gsub("\\|", "", str_match(
Hao Zhubff01912017-05-23 18:05:00 -040051 kable_input, paste0("\\\\begin\\{",
Hao Zhu24bf30c2019-01-22 02:14:33 -050052 table_info$tabular,"\\}.*\\{(.*?)\\}"))[2])
53 table_info$align_vector <- unlist(strsplit(table_info$align, ""))
54 table_info$align_vector_origin <- table_info$align_vector
Hao Zhuc05e1812017-02-25 01:45:35 -050055 # valign
Hao Zhu24bf30c2019-01-22 02:14:33 -050056 table_info$valign <- gsub("\\|", "", str_match(
57 kable_input, paste0("\\\\begin\\{", table_info$tabular,"\\}(.*)\\{.*?\\}"))[2])
58 table_info$valign2 <- sub("\\[", "\\\\[", table_info$valign)
59 table_info$valign2 <- sub("\\]", "\\\\]", table_info$valign2)
60 table_info$valign3 <- sub("\\[", "", table_info$valign)
61 table_info$valign3 <- sub("\\]", "", table_info$valign3)
62 table_info$begin_tabular <- paste0("\\\\begin\\{", table_info$tabular, "\\}",
63 table_info$valign2)
64 table_info$end_tabular <- paste0("\\\\end\\{", table_info$tabular, "\\}")
Hao Zhu4adea852015-11-16 16:38:34 -050065 # N of columns
Hao Zhu24bf30c2019-01-22 02:14:33 -050066 table_info$ncol <- nchar(table_info$align)
Hao Zhu4adea852015-11-16 16:38:34 -050067 # Caption
Hao Zhud57c2d72017-08-16 22:51:17 -040068 if (str_detect(kable_input, "caption\\[")) {
Hao Zhu70b89b12017-08-19 14:52:55 -040069 caption_line <- str_match(kable_input, "\\\\caption(.*)\\n")[2]
Hao Zhu24bf30c2019-01-22 02:14:33 -050070 table_info$caption.short <- str_match(caption_line, "\\[(.*?)\\]")[2]
71 table_info$caption <- substr(caption_line,
72 nchar(table_info$caption.short) + 4,
qifei8743c772017-08-30 21:20:34 +020073 nchar(caption_line))
Hao Zhud57c2d72017-08-16 22:51:17 -040074 } else {
Hao Zhu24bf30c2019-01-22 02:14:33 -050075 table_info$caption <- str_match(kable_input, "caption\\{(.*?)\\n")[2]
Hao Zhud57c2d72017-08-16 22:51:17 -040076 }
Hao Zhu24bf30c2019-01-22 02:14:33 -050077 if (table_info$tabular == "longtable") {
78 table_info$caption <- str_sub(table_info$caption, 1, -4)
Hao Zhud57c2d72017-08-16 22:51:17 -040079 } else {
Hao Zhu24bf30c2019-01-22 02:14:33 -050080 table_info$caption <- str_sub(table_info$caption, 1, -2)
Hao Zhud57c2d72017-08-16 22:51:17 -040081 }
Hao Zhu4adea852015-11-16 16:38:34 -050082 # Contents
Hao Zhu24bf30c2019-01-22 02:14:33 -050083 table_info$contents <- str_match_all(kable_input, "\n(.*)\\\\\\\\")[[1]][,2]
84 table_info$contents <- regex_escape(table_info$contents, T)
85 if (table_info$tabular == "longtable" & !is.na(table_info$caption) &
Hao Zhud384bc22018-05-12 22:24:30 -040086 !str_detect(kable_input, "\\\\begin\\{table\\}\\n\\n\\\\caption")) {
Hao Zhu24bf30c2019-01-22 02:14:33 -050087 table_info$contents <- table_info$contents[-1]
Hao Zhua3fc0c42017-02-27 12:04:59 -050088 }
Hao Zhud384bc22018-05-12 22:24:30 -040089 if (!is.null(attr(kable_input, "n_head"))) {
90 n_head <- attr(kable_input, "n_head")
Hao Zhu24bf30c2019-01-22 02:14:33 -050091 table_info$new_header_row <- table_info$contents[seq(n_head - 1, 1)]
92 table_info$contents <- table_info$contents[-seq(1, n_head - 1)]
93 table_info$header_df <- extra_header_to_header_df(table_info$new_header_row)
94 table_info$new_header_row <- paste0(table_info$new_header_row, "\\\\\\\\")
Hao Zhud384bc22018-05-12 22:24:30 -040095 }
Hao Zhu24bf30c2019-01-22 02:14:33 -050096 table_info$nrow <- length(table_info$contents)
97 table_info$duplicated_rows <- (sum(duplicated(table_info$contents)) != 0)
Hao Zhu4adea852015-11-16 16:38:34 -050098 # Column names
Hao Zhu24bf30c2019-01-22 02:14:33 -050099 if (table_info$booktabs & !grepl("\\\\midrule", kable_input)) {
100 table_info$colnames <- NULL
101 table_info$position_offset <- 0
Leo83f05132018-05-10 14:12:16 +0800102 } else {
Hao Zhu24bf30c2019-01-22 02:14:33 -0500103 table_info$colnames <- str_split(table_info$contents[1], " \\& ")[[1]]
104 table_info$position_offset <- 1
Leo83f05132018-05-10 14:12:16 +0800105 }
Hao Zhu4adea852015-11-16 16:38:34 -0500106 # Row names
Hao Zhu24bf30c2019-01-22 02:14:33 -0500107 table_info$rownames <- str_extract(table_info$contents, "^[^ &]*")
Hao Zhuc05e1812017-02-25 01:45:35 -0500108
Hao Zhu24bf30c2019-01-22 02:14:33 -0500109 table_info$centering <- grepl("\\\\centering", kable_input)
Hao Zhuc05e1812017-02-25 01:45:35 -0500110
Hao Zhu24bf30c2019-01-22 02:14:33 -0500111 table_info$table_env <- (!is.na(table_info$caption) &
Vincent Arel-Bundockef8ffce2021-01-19 08:55:16 -0500112 table_info$tabular != "longtable") ||
113 grepl("\\\\begin\\{table\\}", kable_input)
Hao Zhud384bc22018-05-12 22:24:30 -0400114
Hao Zhu24bf30c2019-01-22 02:14:33 -0500115 return(table_info)
Hao Zhudb04e302015-11-15 16:57:38 -0500116}
Hao Zhu8977a8a2015-11-19 16:52:21 -0500117
Hao Zhud384bc22018-05-12 22:24:30 -0400118extra_header_to_header_df <- function(extra_header_rows) {
119 lapply(str_split(extra_header_rows, " \\& "), function(x) {
120 as.data.frame(t(sapply(x, extra_header_to_header_df_)), row.names = NA)
121 })
122}
123
124extra_header_to_header_df_ <- function(x) {
125 if (trimws(x) == "") return(c(header = " ", colspan = "1"))
126 x <- trimws(x)
127 x_header <- str_match(x, "([^\\}\\{]*)\\\\\\}$")[2]
128 x_colspan <- str_match(x, "^\\\\\\\\multicolumn\\\\\\{([^\\\\\\}]*)")[2]
129 return(c(header = x_header, colspan = x_colspan))
130}
131
Hao Zhu953f3bd2017-07-28 11:43:40 -0400132# Magic Mirror for html table --------
Hao Zhuf7994dd2017-02-27 16:58:42 -0500133magic_mirror_html <- function(kable_input){
Hao Zhu24bf30c2019-01-22 02:14:33 -0500134 table_info <- list()
Hao Zhu558c72f2017-07-24 15:12:00 -0400135 kable_xml <- read_kable_as_xml(kable_input)
Hao Zhu8977a8a2015-11-19 16:52:21 -0500136 # Caption
Hao Zhu24bf30c2019-01-22 02:14:33 -0500137 table_info$caption <- xml_text(xml_child(kable_xml, "caption"))
Hao Zhu8977a8a2015-11-19 16:52:21 -0500138 # Contents
Hao Zhu24bf30c2019-01-22 02:14:33 -0500139 # table_info$contents <- html_table(read_html(as.character(kable_input)))[[1]]
Hao Zhu8977a8a2015-11-19 16:52:21 -0500140 # colnames
Hao Zhu24bf30c2019-01-22 02:14:33 -0500141 table_info$colnames <- lapply(xml_children(xml_child(kable_xml, "thead")),
Hao Zhuf7994dd2017-02-27 16:58:42 -0500142 xml_children)
Hao Zhu24bf30c2019-01-22 02:14:33 -0500143 table_info$colnames <- table_info$colnames[[length(table_info$colnames)]]
144 table_info$colnames <- trimws(xml_text(table_info$colnames))
145 table_info$ncol <- length(table_info$colnames)
146 table_info$nrow_header <- length(xml_children(xml_child(kable_xml, "thead")))
147 table_info$nrow_body <- nrow(table_info$contents)
148 table_info$table_class <- xml_attr(kable_xml, "class")
149 table_info$table_style <- xml_attr(kable_xml, "style")
150 return(table_info)
Hao Zhu8977a8a2015-11-19 16:52:21 -0500151}
152
Hao Zhu26234122017-02-22 15:34:33 -0500153