blob: 665f9d98c403b95a895cf5218b4534de5cebc104 [file] [log] [blame]
Hao Zhu2a64dc62017-08-28 10:57:57 -04001#' Convert a LaTeX table to an image and place it in a rmarkdown document
2#'
3#' @description This is a LaTeX-only function. This function will render the
4#' raw LaTeX code (could be codes generated by other table packages like
5#' `xtable`) to generate a table, convert it to an image and put it back to a
6#' rmarkdown environment. It is a "better than nothing" solution to print high
7#' quality tables in rmarkdown Word document. By using this, you need to take
Hao Zhu245931c2017-09-01 22:43:56 -04008#' the responsibility of explaining to your collaborators why they can't make
Hao Zhua9c43dc2017-09-04 23:00:39 -04009#' edits to the tables in Word.
Hao Zhu2a64dc62017-08-28 10:57:57 -040010#'
11#' Also, if a filename is provided, user has the option to "save" the table to
12#' an image file like `ggplot2::ggsave()`.
13#'
Hao Zhu246b95c2017-09-14 14:29:48 -040014#' Note that, if you are using this function on a Windows computer, you need
15#' to install Ghostscript before you can use this feature. It is essential for
16#' magick to read PDFs on Windows. Website for Ghostscript: https://ghostscript.com/
17#'
Hao Zhu2a64dc62017-08-28 10:57:57 -040018#' The idea of this function was coming from [this StackOverflow question](https://stackoverflow.com/questions/44711313/save-rmarkdowns-report-tables-and-figures-to-file).
19#' The approach was learned and adopted from the [texpreview](https://github.com/metrumresearchgroup/texPreview)
20#' package, which allows you to preview the results of TeX code in the Viewer panel.
21#'
22#' @param kable_input Raw LaTeX code to generate a table. It doesn't have to
23#' came from `kable` or `kableExtra`.
24#' @param filename Character String. If specified, the image will be saved under
25#' the specified (path &) name. You don't need to put file format like ".png"
26#' here.
27#' @param file_format Character String to specify image format, such as `png`,
28#' `jpeg`, `gif`, `tiff`, etc. Default is `png`.
29#' @param latex_header_includes A character vector of extra LaTeX header stuff.
30#' Each element is a row. You can have things like
31#' `c("\\usepackage{threeparttable}", "\\usepackage{icons}")`
32#' @param keep_pdf A T/F option to control if the mid-way standalone pdf should
33#' be kept. Default is `FALSE`.
Hao Zhu4840bc92017-09-15 15:55:05 -040034#' @param density Resolution to read the PDF file. Default value is 300, which
35#' should be sufficient in most cases.
Salzer27eb90b2018-02-11 16:14:04 -050036#' @param keep_tex A T/F option to control if the latex file that is initially created
37#' should be kept. Default is `FALSE`.
Hao Zhu2a64dc62017-08-28 10:57:57 -040038#'
Hao Zhu2a64dc62017-08-28 10:57:57 -040039#' @export
40kable_as_image <- function(kable_input, filename = NULL,
41 file_format = "png",
42 latex_header_includes = NULL,
Hao Zhu4840bc92017-09-15 15:55:05 -040043 keep_pdf = FALSE,
Salzer27eb90b2018-02-11 16:14:04 -050044 density = 300,
45 keep_tex = FALSE) {
Hao Zhu4840bc92017-09-15 15:55:05 -040046 if (!requireNamespace("magick", quietly = TRUE)) {
47 stop('kable_as_image requires the magick package, which is not available ',
48 'on all platforms. Please get it installed ',
49 'via install.packages("magick"). If you are running on Windows, you ',
50 'also need to install Ghostscript. Please download it here:',
51 'https://ghostscript.com/')
Hao Zhu2a64dc62017-08-28 10:57:57 -040052 } else {
Hao Zhu4840bc92017-09-15 15:55:05 -040053 temp_tex <- c(
54 "\\documentclass[border=1mm, preview]{standalone}",
55 "\\usepackage[active,tightpage]{preview}",
56 "\\usepackage{varwidth}",
57 "\\usepackage{amssymb, amsmath}",
58 "\\usepackage{ifxetex,ifluatex}",
59 "\\usepackage{fixltx2e}",
60 "\\usepackage{polyglossia}",
61 "\\setmainlanguage{$mainlang$}",
62 latex_pkg_list(),
63 "\\usepackage{graphicx}",
64 "\\usepackage{mathspec}",
65 "\\usepackage{xltxtra,xunicode}",
66 latex_header_includes,
67 "\\begin{document}",
68 enc2utf8(as.character(kable_input)),
69 "\\end{document}"
70 )
71 temp_tex <- paste(temp_tex, collapse = "\n")
72 temp_file <- paste0("table_", format(Sys.time(), "%Y-%m-%d_%H%M%S"))
Hao Zhub35f1882017-10-10 16:34:16 -040073 writeLines(temp_tex, paste0(temp_file, ".tex"))
Hao Zhu4840bc92017-09-15 15:55:05 -040074 system(paste0("xelatex -interaction=batchmode ", temp_file, ".tex"))
75 temp_file_delete <- paste0(temp_file, c(".tex", ".aux", ".log"))
Salzer27eb90b2018-02-11 16:14:04 -050076 if(!keep_tex) {
77 unlink(temp_file_delete)
78 }
Hao Zhu2a64dc62017-08-28 10:57:57 -040079
Hao Zhu4840bc92017-09-15 15:55:05 -040080 table_img_pdf <- try(magick::image_read(paste0(temp_file, ".pdf"),
81 density = density),
82 silent = T)
83 if (class(table_img_pdf) == "try-error") {
84 stop("Ghostscript is required to read PDF on windows. ",
85 "Please download it here: https://ghostscript.com/")
86 }
87 if (!keep_pdf) {
88 unlink(paste0(temp_file, ".pdf"))
89 }
90 table_img <- magick::image_convert(table_img_pdf, file_format)
91 if (!is.null(filename)) {
92 temp_img <- paste0(filename, ".", file_format)
93 } else {
94 temp_img <- tempfile(fileext = paste0(".", file_format))
95 }
96 magick::image_write(table_img, temp_img)
97
98 include_graphics(temp_img)
99 }
Hao Zhu2a64dc62017-08-28 10:57:57 -0400100}