tinytex/0000755000176200001440000000000014616211632011760 5ustar liggesuserstinytex/NAMESPACE0000644000176200001440000000155314366520052013204 0ustar liggesusers# Generated by roxygen2: do not edit by hand export(check_installed) export(copy_tinytex) export(install_tinytex) export(is_tinytex) export(latexmk) export(lualatex) export(parse_install) export(parse_packages) export(pdflatex) export(r_texmf) export(reinstall_tinytex) export(tinytex_root) export(tl_pkgs) export(tl_sizes) export(tlmgr) export(tlmgr_conf) export(tlmgr_install) export(tlmgr_path) export(tlmgr_remove) export(tlmgr_repo) export(tlmgr_search) export(tlmgr_update) export(tlmgr_version) export(uninstall_tinytex) export(use_tinytex) export(xelatex) import(stats) import(tools) import(utils) importFrom(xfun,dir_exists) importFrom(xfun,download_file) importFrom(xfun,grep_sub) importFrom(xfun,in_dir) importFrom(xfun,is_linux) importFrom(xfun,is_macos) importFrom(xfun,is_unix) importFrom(xfun,is_windows) importFrom(xfun,raw_string) importFrom(xfun,with_ext) tinytex/LICENSE0000644000176200001440000000010414575617663013002 0ustar liggesusersYEAR: 2017-2024 COPYRIGHT HOLDER: Yihui Xie and Posit Software, PBC tinytex/README.md0000644000176200001440000000434414240530506013241 0ustar liggesusers# TinyTeX [![R-CMD-check](https://github.com/rstudio/tinytex/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/rstudio/tinytex/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/rstudio/tinytex/branch/main/graph/badge.svg)](https://app.codecov.io/gh/rstudio/tinytex?branch=main) [![CRAN release](https://www.r-pkg.org/badges/version/tinytex)](https://cran.r-project.org/package=tinytex) tinytex logo The motivation behind TinyTeX was from two common problems in installing and maintaining LaTeX distributions: 1. You have to either install a basic version that is relatively small (several hundred MB) but basically doesn't work, because it is very likely that certain frequently used LaTeX packages are missing; or you install the full version that is several GB, but in your whole life, you probably will only use 1% of the packages. 2. The documentation for installation and maintenance is often too long for beginners. For example, [the `tlmgr` manual](https://www.tug.org/texlive/doc/tlmgr.html) is comprehensive and very useful, but it is often hard to figure out what to do when running into a LaTeX issue that says a certain `.sty` file is not found. I believe these problems can be solved by TinyTeX, a custom LaTeX distribution based on TeX Live that is small in size but still functions well in most cases. Even if you run into the problem of missing LaTeX packages, it should be super clear to you what you need to do. In fact, if you are an R Markdown user, there is nothing you need to do, because missing packages will just be installed automatically. You may not even know the existence of LaTeX at all since it should rarely bother you. This repo contains the installation scripts of TinyTeX (under the `tools` directory) and the R companion package **tinytex**. Please see the full documentation at . Obviously I hope it is not too long. The R package **tinytex** is licensed under MIT. The LaTeX distribution TinyTeX is [licensed under GPL-2](https://github.com/rstudio/tinytex-releases#license). tinytex/man/0000755000176200001440000000000014574207444012544 5ustar liggesuserstinytex/man/tl_sizes.Rd0000644000176200001440000000177513730722122014665 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tlmgr.R \name{tl_sizes} \alias{tl_sizes} \title{Sizes of LaTeX packages in TeX Live} \usage{ tl_sizes(show_total = TRUE, pkgs = NULL, only_installed = TRUE, field = "size") } \arguments{ \item{show_total}{Whether to show the total size.} \item{pkgs}{A character vector of package names (by default, all packages).} \item{only_installed}{Whether to list installed packages only.} \item{field}{A character vector of field names in the package information. See \url{https://www.tug.org/texlive/doc/tlmgr.html#info} for more info.} } \value{ By default, a data frame of three columns: \code{package} is the package names, \code{size} is the sizes in bytes, and \code{size_h} is the human-readable version of sizes. If different field names are provided in the \code{field} argument, the returned data frame will contain these columns. } \description{ Use the command \command{tlmgr info --list} to obtain the sizes of LaTeX packages. } tinytex/man/tlmgr.Rd0000644000176200001440000001226714420323066014155 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tlmgr.R \name{tlmgr} \alias{tlmgr} \alias{tlmgr_search} \alias{tlmgr_install} \alias{tlmgr_remove} \alias{tlmgr_version} \alias{tlmgr_update} \alias{tlmgr_path} \alias{tlmgr_conf} \alias{tlmgr_repo} \title{Run the TeX Live Manager} \usage{ tlmgr(args = character(), usermode = FALSE, ..., .quiet = FALSE) tlmgr_search(what, file = TRUE, all = FALSE, global = TRUE, word = FALSE, ...) tlmgr_install( pkgs = character(), usermode = FALSE, path = !usermode && os != "windows", ... ) tlmgr_remove(pkgs = character(), usermode = FALSE) tlmgr_version(format = c("raw", "string", "list")) tlmgr_update( all = TRUE, self = TRUE, more_args = character(), usermode = FALSE, run_fmtutil = TRUE, delete_tlpdb = getOption("tinytex.delete_tlpdb", FALSE), ... ) tlmgr_path(action = c("add", "remove")) tlmgr_conf(more_args = character(), ...) tlmgr_repo(url = NULL, ...) } \arguments{ \item{args}{A character vector of arguments to be passed to the command \command{tlmgr}.} \item{usermode}{(For expert users only) Whether to use TeX Live's \href{https://www.tug.org/texlive/doc/tlmgr.html#USER-MODE}{user mode}. If \code{TRUE}, you must have run \code{tlmgr('init-usertree')} once before. This option allows you to manage a user-level texmf tree, e.g., install a LaTeX package to your home directory instead of the system directory, to which you do not have write permission. This option should not be needed on personal computers, and has some limitations, so please read the \pkg{tlmgr} manual very carefully before using it.} \item{...}{For \code{tlmgr()}, additional arguments to be passed to \code{\link{system2}()} (e.g., \code{stdout = TRUE} to capture stdout). For other functions, arguments to be passed to \code{tlmgr()}.} \item{.quiet}{Whether to hide the actual command before executing it.} \item{what}{A search keyword as a (Perl) regular expression.} \item{file}{Whether to treat \code{what} as a filename (pattern).} \item{all}{For \code{tlmgr_search()}, whether to search in everything, including package names, descriptions, and filenames. For \code{tlmgr_update()}, whether to update all installed packages.} \item{global}{Whether to search the online TeX Live Database or locally.} \item{word}{Whether to restrict the search of package names and descriptions to match only full words.} \item{pkgs}{A character vector of LaTeX package names.} \item{path}{Whether to run \code{tlmgr_path('add')} after installing packages (\code{path = TRUE} is a conservative default: it is only necessary to do this after a binary package is installed, such as the \pkg{metafont} package, which contains the executable \command{mf}, but it does not hurt even if no binary packages were installed).} \item{format}{The data format to be returned: \code{raw} means the raw output of the command \command{tlmgr --version}, \code{string} means a character string of the format \samp{TeX Live YEAR (TinyTeX) with tlmgr DATE}, and \code{list} means a list of the form \code{list(texlive = YEAR, tlmgr = DATE, tinytex = TRUE/FALSE)}.} \item{self}{Whether to update the TeX Live Manager itself.} \item{more_args}{A character vector of more arguments to be passed to the command \command{tlmgr update} or \command{tlmgr conf}.} \item{run_fmtutil}{Whether to run \command{fmtutil-sys --all} to (re)create format and hyphenation files after updating \pkg{tlmgr}.} \item{delete_tlpdb}{Whether to delete the \file{texlive.tlpdb.HASH} files (where \verb{HASH} is an MD5 hash) under the \file{tlpkg} directory of the root directory of TeX Live after updating.} \item{action}{On Unix, add/remove symlinks of binaries to/from the system's \code{PATH}. On Windows, add/remove the path to the TeXLive binary directory to/from the system environment variable \code{PATH}.} \item{url}{The URL of the CTAN mirror. If \code{NULL}, show the current repository, otherwise set the repository. See the \code{repository} argument of \code{\link{install_tinytex}()} for examples.} } \description{ Execute the \command{tlmgr} command to search for LaTeX packages, install packages, update packages, and so on. } \details{ The \code{tlmgr()} function is a wrapper of \code{system2('tlmgr')}. All other \code{tlmgr_*()} functions are based on \code{tlmgr} for specific tasks. For example, \code{tlmgr_install()} runs the command \command{tlmgr install} to install LaTeX packages, and \code{tlmgr_update} runs the command \command{tlmgr update}, etc. Note that \code{tlmgr_repo} runs \command{tlmgr options repository} to query or set the CTAN repository. Please consult the \pkg{tlmgr} manual for full details. } \examples{ \dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} # search for a package that contains titling.sty tlmgr_search('titling.sty') # to match titling.sty exactly, add a slash before the keyword, e.g. tlmgr_search('/titling.sty') # use a regular expression if you want to be more precise, e.g. tlmgr_search('/titling\\\\.sty$') # list all installed LaTeX packages tlmgr(c('info', '--list', '--only-installed', '--data', 'name')) \dontshow{\}) # examplesIf} } \references{ The \pkg{tlmgr} manual: \url{https://www.tug.org/texlive/doc/tlmgr.html} } tinytex/man/copy_tinytex.Rd0000644000176200001440000000304514525563766015603 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/install.R \name{copy_tinytex} \alias{copy_tinytex} \alias{use_tinytex} \title{Copy TinyTeX to another location and use it in another system} \usage{ copy_tinytex( from = tinytex_root(), to = select_dir("Select Destination Directory"), move = FALSE ) use_tinytex(from = select_dir("Select TinyTeX Directory")) } \arguments{ \item{from}{The root directory of the TinyTeX installation. For \code{copy_tinytex()}, the default value \code{tinytex_root()} should be a reasonable guess if you installed TinyTeX via \code{install_tinytex()}. For \code{use_tinytex()}, if \code{from} is not provided, a dialog for choosing the directory interactively will pop up.} \item{to}{The destination directory where you want to make a copy of TinyTeX. Like \code{from} in \code{use_tinytex()}, a dialog will pop up if \code{to} is not provided in \code{copy_tinytex()}.} \item{move}{Whether to use the new copy and delete the original copy of TinyTeX after copying it.} } \description{ The function \code{copy_tinytex()} copies the existing TinyTeX installation to another directory (e.g., a portable device like a USB stick). The function \code{use_tinytex()} runs \command{tlmgr path add} to add the copy of TinyTeX in an existing folder to the \code{PATH} variable of the current system, so that you can use utilities such as \command{tlmgr} and \command{pdflatex}, etc. } \note{ You can only copy TinyTeX and use it in the same system, e.g., the Windows version of TinyTeX only works on Windows. } tinytex/man/r_texmf.Rd0000644000176200001440000000232413736752265014505 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tlmgr.R \name{r_texmf} \alias{r_texmf} \title{Add/remove R's texmf tree to/from TeX Live} \usage{ r_texmf(action = c("add", "remove"), ...) } \arguments{ \item{action}{Add/remove R's texmf tree to/from TeX Live.} \item{...}{Arguments passed to \code{\link{tlmgr}()}.} } \description{ R ships a custom texmf tree containing a few LaTeX style and class files, which are required when compiling R packages manuals (\file{Rd.sty}) or Sweave documents (\file{Sweave.sty}). This tree can be found under the directory \code{file.path(R.home('share'), 'texmf')}. This function can be used to add/remove R's texmf tree to/from TeX Live via \code{\link{tlmgr_conf}('auxtrees')}. } \examples{ # running the code below will modify your texmf tree; please do not run # unless you know what it means # r_texmf('remove') # r_texmf('add') # all files under R's texmf tree list.files(file.path(R.home('share'), 'texmf'), recursive = TRUE, full.names = TRUE) } \references{ See the \pkg{tlmgr} manual for detailed information about \command{tlmgr conf auxtrees}. Check out \url{https://tex.stackexchange.com/q/77720/9128} if you don't know what \code{texmf} means. } tinytex/man/parse_packages.Rd0000644000176200001440000000260414512002556015772 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/latex.R \name{parse_packages} \alias{parse_packages} \title{Find missing LaTeX packages from a LaTeX log file} \usage{ parse_packages( log, text = read_lines(log), files = detect_files(text), quiet = rep(FALSE, 3) ) } \arguments{ \item{log}{Path to the LaTeX log file (typically named \file{*.log}).} \item{text}{A character vector of the error log (read from the file provided by the \code{log} argument by default).} \item{files}{A character vector of names of the missing files (automatically detected from the \code{log} by default).} \item{quiet}{Whether to suppress messages when finding packages. It should be a logical vector of length 3: the first element indicates whether to suppress the message when no missing LaTeX packages could be detected from the log, the second element indicate whether to suppress the message when searching for packages via \code{tlmgr_search()}, and the third element indicates whether to warn if no packages could be found via \code{tlmgr_search()}.} } \value{ A character vector of LaTeX package names. } \description{ Analyze the error messages in a LaTeX log file to figure out the names of missing LaTeX packages that caused the errors. These packages can be installed via \code{\link{tlmgr_install}()}. Searching for missing packages is based on \code{\link{tlmgr_search}()}. } tinytex/man/install_tinytex.Rd0000644000176200001440000000725714574207444016300 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/install.R \name{install_tinytex} \alias{install_tinytex} \alias{uninstall_tinytex} \alias{reinstall_tinytex} \alias{tinytex_root} \title{Install/Uninstall TinyTeX} \usage{ install_tinytex( force = FALSE, dir = "auto", version = "daily", bundle = "TinyTeX-1", repository = "auto", extra_packages = if (is_tinytex()) tl_pkgs(), add_path = TRUE ) uninstall_tinytex(force = FALSE, dir = tinytex_root()) reinstall_tinytex(packages = TRUE, dir = tinytex_root(), ...) tinytex_root(error = TRUE) } \arguments{ \item{force}{Whether to force to install or uninstall TinyTeX. For \code{install_tinytex()}, \code{force = FALSE} will stop this function from installing TinyTeX if another LaTeX distribution is detected, or the directory specified via the \code{dir} argument exists.} \item{dir}{The directory to install (should not exist unless \code{force = TRUE}) or uninstall TinyTeX.} \item{version}{The version of TinyTeX, e.g., \code{"2020.09"} (see all available versions at \url{https://github.com/rstudio/tinytex-releases}, or via \code{xfun::github_releases('rstudio/tinytex-releases')}). By default, it installs the latest daily build of TinyTeX. If \code{version = 'latest'}, it installs the latest monthly Github release of TinyTeX.} \item{bundle}{The bundle name of TinyTeX (which determines the collection of LaTeX packages to install). See \url{https://github.com/rstudio/tinytex-releases#releases} for all possible bundles and their meanings.} \item{repository}{The CTAN repository to set. By default, it is the repository automatically chosen by \code{https://mirror.ctan.org} (which is usually the fastest one to your location). You can find available repositories at \code{https://ctan.org/mirrors}), e.g., \code{'http://mirrors.tuna.tsinghua.edu.cn/CTAN/'}, or \code{'https://mirror.las.iastate.edu/tex-archive/'}. In theory, this argument should end with the path \file{/systems/texlive/tlnet}, and if it does not, the path will be automatically appended. You can get a full list of CTAN mirrors via \code{tinytex:::ctan_mirrors()}.} \item{extra_packages}{A character vector of extra LaTeX packages to be installed. By default, a vector of all currently installed LaTeX packages if an existing installation of TinyTeX is found. If you want a fresh installation, you may use \code{extra_packages = NULL}.} \item{add_path}{Whether to run the command \command{tlmgr path add} to add the bin path of TeX Live to the system environment variable \var{PATH}.} \item{packages}{Whether to reinstall all currently installed packages.} \item{...}{Other arguments to be passed to \code{install_tinytex()} (note that the \code{extra_packages} argument will be set to \code{tl_pkgs()} if \code{packages = TRUE}).} \item{error}{Whether to signal an error if TinyTeX is not found.} } \description{ The function \code{install_tinytex()} downloads and installs TinyTeX, a custom LaTeX distribution based on TeX Live. The function \code{uninstall_tinytex()} removes TinyTeX; \code{reinstall_tinytex()} reinstalls TinyTeX as well as previously installed LaTeX packages by default; \code{tinytex_root()} returns the root directory of TinyTeX if found. } \note{ If you really want to disable the installation, you may set the environment variable \var{TINYTEX_PREVENT_INSTALL} to \code{true}. Then \code{install_tinytex()} will fail immediately. This can be useful to sysadmins who want to prevent the accidental installation of TinyTeX. Installing TinyTeX requires perl (on Linux, perl-base is insufficient). } \references{ See the TinyTeX documentation (\url{https://yihui.org/tinytex/}) for the default installation directories on different platforms. } tinytex/man/parse_install.Rd0000644000176200001440000000066113644503423015667 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/latex.R \name{parse_install} \alias{parse_install} \title{Parse the LaTeX log and install missing LaTeX packages if possible} \usage{ parse_install(...) } \arguments{ \item{...}{Arguments passed to \code{\link{parse_packages}()}.} } \description{ This is a helper function that combines \code{\link{parse_packages}()} and \code{\link{tlmgr_install}()}. } tinytex/man/tl_pkgs.Rd0000644000176200001440000000111113730721204014454 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tlmgr.R \name{tl_pkgs} \alias{tl_pkgs} \title{List the names of installed TeX Live packages} \usage{ tl_pkgs(only_installed = TRUE) } \arguments{ \item{only_installed}{Whether to list installed packages only.} } \value{ A character vector of package names. } \description{ Calls \command{tlmgr info --list --data name} to obtain the names of all (installed) TeX Live packages. Platform-specific strings in package names are removed, e.g., \code{"tex"} is returned for the package \pkg{tex.x86_64-darwin}. } tinytex/man/latexmk.Rd0000644000176200001440000001126414406404033014467 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/latex.R \name{latexmk} \alias{latexmk} \alias{pdflatex} \alias{xelatex} \alias{lualatex} \title{Compile a LaTeX document} \usage{ latexmk( file, engine = c("pdflatex", "xelatex", "lualatex", "latex", "tectonic"), bib_engine = c("bibtex", "biber"), engine_args = NULL, emulation = TRUE, min_times = 1, max_times = 10, install_packages = emulation && tlmgr_writable(), pdf_file = gsub("tex$", "pdf", file), clean = TRUE ) pdflatex(...) xelatex(...) lualatex(...) } \arguments{ \item{file}{A LaTeX file path.} \item{engine}{A LaTeX engine (can be set in the global option \code{tinytex.engine}, e.g., \code{options(tinytex.engine = 'xelatex')}).} \item{bib_engine}{A bibliography engine (can be set in the global option \code{tinytex.bib_engine}).} \item{engine_args}{Command-line arguments to be passed to \code{engine} (can be set in the global option \code{tinytex.engine_args}, e.g., \code{options(tinytex.engine_args = '-shell-escape'}).} \item{emulation}{Whether to emulate the executable \command{latexmk} using R. Note that this is unused when \code{engine == 'tectonic'}.} \item{min_times, max_times}{The minimum and maximum number of times to rerun the LaTeX engine when using emulation. You can set the global options \code{tinytex.compile.min_times} or \code{tinytex.compile.max_times}, e.g., \code{options(tinytex.compile.max_times = 3)}.} \item{install_packages}{Whether to automatically install missing LaTeX packages found by \code{\link{parse_packages}()} from the LaTeX log. This argument is only for the emulation mode and TeX Live. Its value can also be set via the global option \code{tinytex.install_packages}, e.g., \code{options(tinytex.install_packages = FALSE)}.} \item{pdf_file}{Path to the PDF output file. By default, it is under the same directory as the input \code{file} and also has the same base name. When \code{engine == 'latex'}, this will be a DVI file.} \item{clean}{Whether to clean up auxiliary files after compilation (can be set in the global option \code{tinytex.clean}, which defaults to \code{TRUE}).} \item{...}{Arguments to be passed to \code{latexmk()} (other than \code{engine} and \code{emulation}).} } \value{ A character string of the path of the output file (i.e., the value of the \code{pdf_file} argument). } \description{ The function \code{latexmk()} emulates the system command \command{latexmk} (\url{https://ctan.org/pkg/latexmk}) to compile a LaTeX document. The functions \code{pdflatex()}, \code{xelatex()}, and \code{lualatex()} are wrappers of \code{latexmk(engine =, emulation = TRUE)}. } \details{ The \command{latexmk} emulation works like this: run the LaTeX engine once (e.g., \command{pdflatex}), run \command{makeindex} to make the index if necessary (the \file{*.idx} file exists), run the bibliography engine \command{bibtex} or \command{biber} to make the bibliography if necessary (the \file{*.aux} or \file{*.bcf} file exists), and finally run the LaTeX engine a number of times (the maximum is 10 by default) to resolve all cross-references. By default, LaTeX warnings will be converted to R warnings. To suppress these warnings, set \code{options(tinytex.latexmk.warning = FALSE)}. If \code{emulation = FALSE}, you need to make sure the executable \command{latexmk} is available in your system, otherwise \code{latexmk()} will fall back to \code{emulation = TRUE}. You can set the global option \code{options(tinytex.latexmk.emulation = FALSE)} to always avoid emulation (i.e., always use the executable \command{latexmk}). The default command to generate the index (if necessary) is \command{makeindex}. To change it to a different command (e.g., \command{zhmakeindex}), you may set the global option \code{tinytex.makeindex}. To pass additional command-line arguments to the command, you may set the global option \code{tinytex.makeindex.args} (e.g., \code{options(tinytex.makeindex = 'zhmakeindex', tinytex.makeindex.args = c('-z', 'pinyin'))}). If you are using the LaTeX distribution TinyTeX, but its path is not in the \code{PATH} variable of your operating system, you may set the global option \code{tinytex.tlmgr.path} to the full path of the executable \command{tlmgr}, so that \code{latexmk()} knows where to find executables like \command{pdflatex}. For example, if you are using Windows and your TinyTeX is on an external drive \file{Z:/} under the folder \file{TinyTeX}, you may set \code{options(tinytex.tlmgr.path = "Z:/TinyTeX/bin/windows/tlmgr.bat")}. Usually you should not need to set this option because TinyTeX can add itself to the \code{PATH} variable during installation or via \code{\link{use_tinytex}()}. In case both methods fail, you can use this manual approach. } tinytex/man/check_installed.Rd0000644000176200001440000000147314346015623016145 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/tlmgr.R \name{check_installed} \alias{check_installed} \title{Check if certain LaTeX packages are installed} \usage{ check_installed(pkgs) } \arguments{ \item{pkgs}{A character vector of LaTeX package names.} } \value{ A logical vector indicating if packages specified in \code{pkgs} are installed. } \description{ If a package has been installed in TinyTeX or TeX Live, the command \command{tlmgr info PKG} should return \code{PKG} where \code{PKG} is the package name. } \note{ This function only works with LaTeX distributions based on TeX Live, such as TinyTeX. } \examples{ \dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} tinytex::check_installed('framed') \dontshow{\}) # examplesIf} } tinytex/man/tinytex-package.Rd0000644000176200001440000000242214406217342016120 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/package.R \docType{package} \name{tinytex-package} \alias{tinytex} \alias{tinytex-package} \title{tinytex: Helper Functions to Install and Maintain TeX Live, and Compile LaTeX Documents} \description{ Helper functions to install and maintain the 'LaTeX' distribution named 'TinyTeX' (\url{https://yihui.org/tinytex/}), a lightweight, cross-platform, portable, and easy-to-maintain version of 'TeX Live'. This package also contains helper functions to compile 'LaTeX' documents, and install missing 'LaTeX' packages automatically. } \seealso{ Useful links: \itemize{ \item \url{https://github.com/rstudio/tinytex} \item Report bugs at \url{https://github.com/rstudio/tinytex/issues} } } \author{ \strong{Maintainer}: Yihui Xie \email{xie@yihui.name} (\href{https://orcid.org/0000-0003-0645-5666}{ORCID}) [copyright holder] Other contributors: \itemize{ \item Posit Software, PBC [copyright holder, funder] \item Christophe Dervieux (\href{https://orcid.org/0000-0003-4474-2498}{ORCID}) [contributor] \item Devon Ryan \email{dpryan79@gmail.com} (\href{https://orcid.org/0000-0002-8549-0971}{ORCID}) [contributor] \item Ethan Heinzen [contributor] \item Fernando Cagua [contributor] } } \keyword{internal} tinytex/man/is_tinytex.Rd0000644000176200001440000000145714002315322015216 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/install.R \name{is_tinytex} \alias{is_tinytex} \title{Check if the LaTeX installation is TinyTeX} \usage{ is_tinytex() } \value{ A logical value indicating if the LaTeX installation is TinyTeX. } \description{ First find the root directory of the installation via \code{\link{tinytex_root}()}. Then check if the directory name is \code{"tinytex"} (case-insensitive). If not, further check if the first line of the file \file{texmf-dist/web2c/fmtutil.cnf} under the directory contains \code{"TinyTeX"} or \code{".TinyTeX"}. If the binary version of TinyTeX was installed, \file{fmtutil.cnf} should contain a line like \samp{Generated by */TinyTeX/bin/x86_64-darwin/tlmgr on Thu Sep 17 07:13:28 2020}. } \examples{ tinytex::is_tinytex() } tinytex/DESCRIPTION0000644000176200001440000000320014616211632013461 0ustar liggesusersPackage: tinytex Type: Package Title: Helper Functions to Install and Maintain TeX Live, and Compile LaTeX Documents Version: 0.51 Authors@R: c( person("Yihui", "Xie", role = c("aut", "cre", "cph"), email = "xie@yihui.name", comment = c(ORCID = "0000-0003-0645-5666")), person(given = "Posit Software, PBC", role = c("cph", "fnd")), person("Christophe", "Dervieux", role = "ctb", comment = c(ORCID = "0000-0003-4474-2498")), person("Devon", "Ryan", role = "ctb", email = "dpryan79@gmail.com", comment = c(ORCID = "0000-0002-8549-0971")), person("Ethan", "Heinzen", role = "ctb"), person("Fernando", "Cagua", role = "ctb"), person() ) Description: Helper functions to install and maintain the 'LaTeX' distribution named 'TinyTeX' (), a lightweight, cross-platform, portable, and easy-to-maintain version of 'TeX Live'. This package also contains helper functions to compile 'LaTeX' documents, and install missing 'LaTeX' packages automatically. Imports: xfun (>= 0.29) Suggests: testit, rstudioapi License: MIT + file LICENSE URL: https://github.com/rstudio/tinytex BugReports: https://github.com/rstudio/tinytex/issues Encoding: UTF-8 RoxygenNote: 7.3.1 NeedsCompilation: no Packaged: 2024-05-06 16:53:21 UTC; yihui Author: Yihui Xie [aut, cre, cph] (), Posit Software, PBC [cph, fnd], Christophe Dervieux [ctb] (), Devon Ryan [ctb] (), Ethan Heinzen [ctb], Fernando Cagua [ctb] Maintainer: Yihui Xie Repository: CRAN Date/Publication: 2024-05-06 17:30:02 UTC tinytex/tests/0000755000176200001440000000000014477353116013133 5ustar liggesuserstinytex/tests/test-travis.R0000644000176200001440000000024314017545604015535 0ustar liggesusers# run tests on Travis (these tests depend on TeX Live) if (!is.na(Sys.getenv('CI', NA)) && tinytex:::tlmgr_available()) testit::test_pkg('tinytex', 'test-travis') tinytex/tests/test-cran.R0000644000176200001440000000005113211546267015146 0ustar liggesuserstestit::test_pkg('tinytex', 'test-cran') tinytex/tests/test-travis/0000755000176200001440000000000014616211632015407 5ustar liggesuserstinytex/tests/test-travis/test-tlmgr.R0000644000176200001440000000225514124474270017643 0ustar liggesuserslibrary(testit) assert('tlmgr is available', { tlmgr_available() }) assert('tlmgr_search() searches the online TeX Live database', { res = tlmgr_search('/framed', stdout = TRUE) ('framed:' %in% res) (any(grepl('/framed[.]sty$', res))) }) assert('`tlmgr info` can list the installed packages', { res = tlmgr(c('info', '--list', '--only-installed', '--data', 'name'), stdout = TRUE) # only check a few basic packages (c('xetex', 'luatex', 'graphics') %in% res) }) assert('`tl_size()` can correctly list name and size', { res = tl_sizes(pkgs = "luatex")$package # only check a few basic packages ('luatex' %==% tl_sizes(pkgs = "luatex")$package) }) assert('fonts package are correctly identified', { p_q = function(...) parse_packages(..., quiet = c(TRUE, TRUE, TRUE)) (p_q(text = "! Font U/psy/m/n/10=psyr at 10.0pt not loadable: Metric (TFM) file not found") %==% 'symbol') (p_q(text = '! The font "FandolSong-Regular" cannot be found.') %==% 'fandol') (p_q(text = '!pdfTeX error: /usr/local/bin/pdflatex (file tcrm0700): Font tcrm0700 at 600 not found') %==% 'ec') (p_q(text = '! Package fontspec Error: The font "Caladea" cannot be found.') %==% 'caladea') }) tinytex/tests/test-travis/test-install.R0000644000176200001440000000057114153473103020157 0ustar liggesuserslibrary(testit) assert('normalize_repo() creates a valid url', { (normalize_repo('https://ctan.math.illinois.edu/') %==% 'https://ctan.math.illinois.edu/systems/texlive/tlnet') (normalize_repo('https://ftp.tu-chemnitz.de/pub/tug/historic/systems/texlive/2020/tlnet-final/') %==% 'https://ftp.tu-chemnitz.de/pub/tug/historic/systems/texlive/2020/tlnet-final') }) tinytex/tests/test-cran/0000755000176200001440000000000014616211632015022 5ustar liggesuserstinytex/tests/test-cran/test-latex.R0000644000176200001440000000337014207444233017243 0ustar liggesuserslibrary(testit) assert('detect_files() can detect filenames from LaTeX log', { # Fonts are tested in test-tlmgr.R also (detect_files("! Font U/psy/m/n/10=psyr at 10.0pt not loadable: Metric (TFM) file not found") %==% font_ext("psyr")) (detect_files('! The font "FandolSong-Regular" cannot be found.') %==% font_ext("FandolSong-Regular")) (detect_files('!pdfTeX error: /usr/local/bin/pdflatex (file tcrm0700): Font tcrm0700 at 600 not found') %==% font_ext('tcrm0700')) (length(detect_files("asdf qwer")) == 0) (detect_files("! LaTeX Error: File `framed.sty' not found.") %==% 'framed.sty') (detect_files("/usr/local/bin/mktexpk: line 123: mf: command not found") %==% 'mf') (detect_files("or the language definition file ngerman.ldf was not found") %==% 'ngerman.ldf') (detect_files("!pdfTeX error: pdflatex (file 8r.enc): cannot open encoding file for reading") %==% '8r.enc') (detect_files("! CTeX fontset `fandol' is unavailable in current mode") %==% 'fandol') (detect_files('Package widetext error: Install the flushend package which is a part of sttools') %==% 'flushend.sty') (detect_files('! Package isodate.sty Error: Package file substr.sty not found.') %==% 'substr.sty') (detect_files("! Package fontenc Error: Encoding file `t2aenc.def' not found.") %==% 't2aenc.def') (detect_files("! I can't find file `hyph-de-1901.ec.tex'.") %==% 'hyph-de-1901.ec.tex') (detect_files("luaotfload-features.lua:835: module 'lua-uni-normalize' not found:") %==% 'lua-uni-algos.lua') }) assert('rerun are correctly detected', { (needs_rerun(text = "Package biblatex Warning: Please rerun LaTeX.")) (needs_rerun(text = "Please (re)run the file")) (needs_rerun(text = "error: Rerun LaTeX.")) (needs_rerun(text = "Rerun to get the final file")) }) tinytex/R/0000755000176200001440000000000014574207444012172 5ustar liggesuserstinytex/R/utils.R0000644000176200001440000000211114520055437013442 0ustar liggesusers# for xfun::session_info('tinytex') xfun_session_info = function() { try_null = function(...) tryCatch(..., error = function(e) NULL) version_info = function(cmd) try_null(system2(cmd, '--version', stdout = TRUE)) tweak_path() pdftex_info = version_info('pdflatex')[1] info = if (is.null(pdftex_info)) { version_info('tectonic')[1] # try tectonic engine? } else if (grepl('TeX Live', pdftex_info, fixed = TRUE)) { # we get more information on tlmgr in that case try_null(tlmgr_version('string')) } else { # for other distributions, e.g., MiKTeX-pdfTeX 4.8 (MiKTeX 21.8) xfun::grep_sub('^.*\\((.*)\\)$', '\\1', pdftex_info) } if (!length(info)) return(invisible(NULL)) paste(c('LaTeX version used: ', paste0(' ', info)), collapse = '\n') } # read a file without warning, and discard lines with invalid characters to # avoid warnings in the grep() family (invalid lines in log files should be safe # to discard in this package, although it isn't so in general) read_lines = function(...) { x = readLines(..., warn = FALSE) x[!validEnc(x)] = '' x } tinytex/R/install.R0000644000176200001440000006057314574207444013776 0ustar liggesusers#' Install/Uninstall TinyTeX #' #' The function \code{install_tinytex()} downloads and installs TinyTeX, a #' custom LaTeX distribution based on TeX Live. The function #' \code{uninstall_tinytex()} removes TinyTeX; \code{reinstall_tinytex()} #' reinstalls TinyTeX as well as previously installed LaTeX packages by default; #' \code{tinytex_root()} returns the root directory of TinyTeX if found. #' @param force Whether to force to install or uninstall TinyTeX. For #' \code{install_tinytex()}, \code{force = FALSE} will stop this function from #' installing TinyTeX if another LaTeX distribution is detected, or the #' directory specified via the \code{dir} argument exists. #' @param dir The directory to install (should not exist unless \code{force = #' TRUE}) or uninstall TinyTeX. #' @param version The version of TinyTeX, e.g., \code{"2020.09"} (see all #' available versions at \url{https://github.com/rstudio/tinytex-releases}, or #' via \code{xfun::github_releases('rstudio/tinytex-releases')}). By default, #' it installs the latest daily build of TinyTeX. If \code{version = #' 'latest'}, it installs the latest monthly Github release of TinyTeX. #' @param bundle The bundle name of TinyTeX (which determines the collection of #' LaTeX packages to install). See #' \url{https://github.com/rstudio/tinytex-releases#releases} for all possible #' bundles and their meanings. #' @param repository The CTAN repository to set. By default, it is the #' repository automatically chosen by \code{https://mirror.ctan.org} (which is #' usually the fastest one to your location). You can find available #' repositories at \code{https://ctan.org/mirrors}), e.g., #' \code{'http://mirrors.tuna.tsinghua.edu.cn/CTAN/'}, or #' \code{'https://mirror.las.iastate.edu/tex-archive/'}. In theory, this #' argument should end with the path \file{/systems/texlive/tlnet}, and if it #' does not, the path will be automatically appended. You can get a full list #' of CTAN mirrors via \code{tinytex:::ctan_mirrors()}. #' @param extra_packages A character vector of extra LaTeX packages to be #' installed. By default, a vector of all currently installed LaTeX packages #' if an existing installation of TinyTeX is found. If you want a fresh #' installation, you may use \code{extra_packages = NULL}. #' @param add_path Whether to run the command \command{tlmgr path add} to add #' the bin path of TeX Live to the system environment variable \var{PATH}. #' @references See the TinyTeX documentation (\url{https://yihui.org/tinytex/}) #' for the default installation directories on different platforms. #' @note If you really want to disable the installation, you may set the #' environment variable \var{TINYTEX_PREVENT_INSTALL} to \code{true}. Then #' \code{install_tinytex()} will fail immediately. This can be useful to #' sysadmins who want to prevent the accidental installation of TinyTeX. #' #' Installing TinyTeX requires perl (on Linux, perl-base is insufficient). #' @export install_tinytex = function( force = FALSE, dir = 'auto', version = 'daily', bundle = 'TinyTeX-1', repository = 'auto', extra_packages = if (is_tinytex()) tl_pkgs(), add_path = TRUE ) { if (tolower(Sys.getenv('TINYTEX_PREVENT_INSTALL')) == 'true') stop( "The environment variable 'TINYTEX_PREVENT_INSTALL' was set to 'true', so ", "the installation is aborted." ) if (!is.logical(force)) stop('The argument "force" must take a logical value.') continue_inst = function() { tolower(substr(readline('Continue the installation anyway? (Y/N) '), 1, 1)) == 'y' } # if tlmgr is detected in the system, ask in interactive mode whether to # continue the installation, and stop in non-interactive() mode p = which_bin(c('tlmgr', 'pdftex', 'xetex', 'luatex')) p = p[p != ''] if (!force && length(p)) { message("Found '", p[1], "', which indicates a LaTeX distribution may have existed in the system.") if (interactive()) { if (!continue_inst()) return(invisible('')) } else stop( 'If you want to force installing TinyTeX anyway, use tinytex::install_tinytex(force = TRUE).' ) } force(extra_packages) # evaluate it before TinyTeX is removed or reinstalled next check_dir = function(dir) { if (dir_exists(dir) && !force) stop( 'The directory "', dir, '" exists. Please either delete it, ', 'or use tinytex::install_tinytex(force = TRUE).' ) } if (missing(dir)) dir = '' user_dir = '' if (dir != '') { dir = gsub('[/\\]+$', '', dir) # remove trailing slashes check_dir(dir) dir = xfun::normalize_path(dir) if (is_windows() && !valid_path(dir)) { warning( "The directory path '", dir, "' contains spaces or non-ASCII characters, ", "and TinyTeX may not work. Please use a path with pure ASCII characters and no spaces.", immediate. = TRUE ) if (!force && !(interactive() && continue_inst())) return(invisible(dir)) } unlink(dir, recursive = TRUE) user_dir = dir } repository = normalize_repo(repository) not_ctan = repository != 'ctan' https = grepl('^https://', repository) if (!grepl('TinyTeX', bundle)) message( "The bundle name '", bundle, "' has been automatically corrected to '", bundle <- gsub('tinytex', 'TinyTeX', bundle, ignore.case = TRUE), "'." ) owd = setwd(tempdir()); on.exit(setwd(owd), add = TRUE) if ((texinput <- Sys.getenv('TEXINPUT')) != '') message( 'Your environment variable TEXINPUT is "', texinput, '". Normally you should not set this variable, because it may lead to issues like ', 'https://github.com/rstudio/tinytex/issues/92.' ) switch( os, 'unix' = { check_local_bin() if (os_index != 3 && !any(dir_exists(c('~/bin', '~/.local/bin')))) on.exit(message( 'You may have to restart your system after installing TinyTeX to make sure ', '~/bin appears in your PATH variable (https://github.com/rstudio/tinytex/issues/16).' ), add = TRUE) }, 'windows' = {}, stop('Sorry, but tinytex::install_tinytex() does not support this platform: ', os) ) src_install = getOption('tinytex.source.install', need_source_install()) # if needs to install from source, set `extra_packages` according to `bundle` if (src_install && missing(extra_packages)) { extra_packages = switch( bundle, 'TinyTeX-2' = 'scheme-full', 'TinyTeX' = read_lines('https://yihui.org/gh/tinytex/tools/pkgs-custom.txt'), 'TinyTeX-0' = { warning("bundle = 'TinyTeX-0' is not supported for your system"); NULL } ) } install = function(...) { if (src_install) { install_tinytex_source(repository, ...) } else { install_prebuilt(bundle, ..., repo = repository) } } if (version == 'daily') { version = '' # test if https://yihui.org or github.com is accessible because the daily # version is downloaded from there determine_version = function() { if (xfun::url_accessible('https://yihui.org')) return('') if (xfun::url_accessible('https://github.com')) return('daily-github') warning( "The daily version of TinyTeX does not appear to be accessible. ", "Switching to version = 'latest' instead. If you are sure to install ", "the daily version, call tinytex::install_tinytex(version = 'daily') ", "(which may fail)." ) 'latest' } if (missing(version) && !src_install) version = determine_version() } user_dir = install(user_dir, version, add_path, extra_packages) opts = options(tinytex.tlmgr.path = find_tlmgr(user_dir)) on.exit(options(opts), add = TRUE) if (not_ctan) { # install tlgpg for Windows and macOS users if an HTTPS repo is preferred if (os_index %in% c(1, 3) && https) { tlmgr(c('--repository', 'http://www.preining.info/tlgpg/', 'install', 'tlgpg')) } tlmgr_repo(repository) if (tlmgr(c('update', '--list')) != 0) { warning('The repository ', repository, ' does not seem to be accessible. Reverting to the default CTAN mirror.') tlmgr(c('option', 'repository', 'ctan')) } } invisible(user_dir) } # TinyTeX has to be installed from source for OSes that are not Linux or # non-x86_64 Linux machines need_source_install = function() { os_index == 0 || (os_index == 2 && !identical(Sys.info()[['machine']], 'x86_64')) } # append /systems/texlive/tlnet to the repo url if necessary normalize_repo = function(url) { # don't normalize the url if users passes I(url) or 'ctan' or NULL if (is.null(url) || url == 'ctan' || inherits(url, 'AsIs')) return(url) if (url == 'auto') return(auto_repo()) if (url == 'illinois') return('https://ctan.math.illinois.edu/systems/texlive/tlnet') url = sub('/+$', '', url) if (!grepl('/tlnet$', url)) { url2 = paste0(url, '/systems/texlive/tlnet') # return the amended url if it works if (xfun::url_accessible(url2)) return(url2) } url } # get the automatic CTAN mirror returned from mirror.ctan.org auto_repo = function() { # curlGetHeaders() may time out, hence tryCatch() here x = tryCatch( curlGetHeaders('https://mirror.ctan.org/systems/texlive/tlnet'), error = function(e) character() ) x = xfun::grep_sub('^location: (https://[^[:space:]]+)\\s*$', '\\1', x, ignore.case = TRUE) x = tail(x, 1) if (length(x) == 1) x else 'ctan' } # retrieve all CTAN (https) mirrors ctan_mirrors = function() { x = readLines('https://ctan.org/mirrors/mirmon') u = xfun::grep_sub('.* 0) { dir.create(texmf_tmp <- tempfile(), recursive = TRUE) message( 'The directory ', texmf, ' is not empty. It will be backed up to ', texmf_tmp, ' and restored later.\n' ) file.copy(texmf, texmf_tmp, recursive = TRUE) on.exit( file.copy(file.path(texmf_tmp, basename(texmf)), dirname(texmf), recursive = TRUE), add = TRUE ) } uninstall_tinytex() install_tinytex(extra_packages = pkgs, dir = dir, ...) } #' @param error Whether to signal an error if TinyTeX is not found. #' @rdname install_tinytex #' @export tinytex_root = function(error = TRUE) { path = which_bin('tlmgr') if (path == '') return('') root_dir = function(path, ...) { dir = normalizePath(file.path(dirname(path), ...), mustWork = TRUE) if (!'bin' %in% list.files(dir)) if (error) stop( dir, ' does not seem to be the root directory of TeX Live (no "bin/" dir under it)' ) else return('') dir } if (os == 'windows') return(root_dir(path, '..', '..')) if (Sys.readlink(path) == '') if (error) stop( 'Cannot figure out the root directory of TeX Live from ', path, ' (not a symlink on ', os, ')' ) else return('') path = symlink_root(path) root_dir(normalizePath(path), '..', '..', '..') } # return paths to TinyTeX's executables even if TinyTeX was not added to PATH which_bin = function(exec) { tweak_path() Sys.which(exec) } # trace a symlink to its final destination symlink_root = function(path) { path = normalizePath(path, mustWork = TRUE) path2 = Sys.readlink(path) if (path2 == '') return(path) # no longer a symlink; must be resolved now # path2 may still be a _relative_ symlink in_dir(dirname(path), symlink_root(path2)) } # a helper function to open tlmgr.pl (on *nix) open_tlmgr = function() { file.edit(symlink_root(Sys.which('tlmgr'))) } #' Check if the LaTeX installation is TinyTeX #' #' First find the root directory of the installation via #' \code{\link{tinytex_root}()}. Then check if the directory name is #' \code{"tinytex"} (case-insensitive). If not, further check if the first line #' of the file \file{texmf-dist/web2c/fmtutil.cnf} under the directory contains #' \code{"TinyTeX"} or \code{".TinyTeX"}. If the binary version of TinyTeX was #' installed, \file{fmtutil.cnf} should contain a line like \samp{Generated by #' */TinyTeX/bin/x86_64-darwin/tlmgr on Thu Sep 17 07:13:28 2020}. #' @return A logical value indicating if the LaTeX installation is TinyTeX. #' @export #' @examples tinytex::is_tinytex() is_tinytex = function() tryCatch({ root = tinytex_root() root != '' && ( grepl('^[.]?tinytex$', tolower(basename(root))) || file.exists(file.path(root, '.tinytex')) ) }, error = function(e) FALSE) dir_rename = function(from, to) { # cannot rename '/foo' to '/bar' because of 'Invalid cross-device link' suppressWarnings(file.rename(from, to)) || dir_copy(from, to) } dir_copy = function(from, to) { dir.create(to, showWarnings = FALSE, recursive = TRUE) all(file.copy(list.files(from, full.names = TRUE), to, recursive = TRUE)) && unlink(from, recursive = TRUE) == 0 } # LaTeX packages that I use install_yihui_pkgs = function() { pkgs = read_lines('https://yihui.org/gh/tinytex/tools/pkgs-yihui.txt') tlmgr_install(pkgs) } # install a prebuilt version of TinyTeX install_prebuilt = function( pkg = '', dir = '', version = '', add_path = TRUE, extra_packages = NULL, repo = 'ctan', hash = FALSE, cache = NA ) { if (need_source_install()) stop( 'There is no prebuilt version of TinyTeX for this platform: ', paste(Sys.info()[c('sysname', 'machine')], collapse = ' '), '.' ) dir0 = default_inst(); b = basename(dir0) dir1 = xfun::normalize_path(dir) # expected installation dir if (dir1 == '') dir1 = dir0 # the archive is extracted to this target dir target = dirname(dir1) dir2 = file.path(target, b) # path to (.)TinyTeX/ after extraction if (xfun::file_ext(pkg) == '') { if (version == 'latest') { version = xfun::github_releases('rstudio/tinytex-releases', version) } else if (version == 'daily-github') { version = '' opts = options(tinytex.install.url = 'https://github.com/rstudio/tinytex-releases/releases/download/daily/') on.exit(options(opts), add = TRUE) } version = gsub('^v', '', version) installer = if (pkg == '') 'TinyTeX' else pkg # e.g., TinyTeX-0.zip, TinyTeX-1-v2020.10.tar.gz, ... pkg = paste0( installer, if (version != '') paste0('-v', version), '.', c('zip', 'tar.gz', 'tgz')[os_index] ) # Full scheme is bundled as a self extracting archive on Windows if (os_index == 1 && installer == 'TinyTeX-2') pkg = xfun::with_ext(pkg, "exe") if (file.exists(pkg) && is.na(cache)) { # invalidate cache (if unspecified) when the installer is more than one day old if (as.numeric(difftime(Sys.time(), file.mtime(pkg), units = 'days')) > 1) cache = FALSE } if (identical(cache, FALSE)) { file.remove(pkg); on.exit(file.remove(pkg), add = TRUE) } if (!file.exists(pkg)) download_installer(pkg, version) } pkg = path.expand(pkg) # installation dir shouldn't be a file but a directory file.remove(exist_files(c(dir1, dir2))) if (grepl('[.]exe$', pkg)) { system2(pkg, args = c('-y', paste0('-o', path.expand(target)))) } else { extract = if (grepl('[.]zip$', pkg)) unzip else untar extract(pkg, exdir = path.expand(target)) } # TinyTeX (or .TinyTeX) is extracted to the parent dir of `dir`; may need to rename if (dir != '') { if (basename(dir1) != b) file.rename(dir2, dir1) opts = options(tinytex.tlmgr.path = find_tlmgr(dir1)) on.exit(options(opts), add = TRUE) } post_install_config(add_path, extra_packages, repo, hash) invisible(dir1) } # post-install configurations post_install_config = function(add_path = TRUE, extra_packages = NULL, repo = 'ctan', hash = FALSE) { if (os_index == 2) { if (!dir_exists(bin_dir <- '~/.local/bin')) dir.create(bin_dir <- '~/bin', FALSE, TRUE) tlmgr(c('option', 'sys_bin', bin_dir)) } # fix fonts.conf: https://github.com/rstudio/tinytex/issues/313 tlmgr(c('postaction', 'install', 'script', 'xetex'), .quiet = TRUE) # do not wrap lines in latex log (#322) tlmgr_conf(c('texmf', 'max_print_line', '10000'), .quiet = TRUE, stdout = FALSE) if (add_path) tlmgr_path() r_texmf(.quiet = TRUE) # don't use the default random ctan mirror when installing on CI servers if (repo != 'ctan' || tolower(Sys.getenv('CI')) != 'true') tlmgr_repo(repo, stdout = FALSE, .quiet = TRUE) tlmgr_install(setdiff(extra_packages, tl_pkgs())) if (hash) { texhash(); fmtutil(stdout = FALSE); updmap(); fc_cache() } } download_installer = function(file, version) { url = if (version != '') sprintf( 'https://github.com/rstudio/tinytex-releases/releases/download/v%s/%s', version, file ) else paste0(getOption('tinytex.install.url', 'https://yihui.org/tinytex/'), file) download_file(url, file, mode = 'wb') } #' Copy TinyTeX to another location and use it in another system #' #' The function \code{copy_tinytex()} copies the existing TinyTeX installation #' to another directory (e.g., a portable device like a USB stick). The function #' \code{use_tinytex()} runs \command{tlmgr path add} to add the copy of TinyTeX #' in an existing folder to the \code{PATH} variable of the current system, so #' that you can use utilities such as \command{tlmgr} and \command{pdflatex}, #' etc. #' @param from The root directory of the TinyTeX installation. For #' \code{copy_tinytex()}, the default value \code{tinytex_root()} should be a #' reasonable guess if you installed TinyTeX via \code{install_tinytex()}. For #' \code{use_tinytex()}, if \code{from} is not provided, a dialog for choosing #' the directory interactively will pop up. #' @param to The destination directory where you want to make a copy of TinyTeX. #' Like \code{from} in \code{use_tinytex()}, a dialog will pop up if \code{to} #' is not provided in \code{copy_tinytex()}. #' @param move Whether to use the new copy and delete the original copy of #' TinyTeX after copying it. #' @note You can only copy TinyTeX and use it in the same system, e.g., the #' Windows version of TinyTeX only works on Windows. #' @export copy_tinytex = function( from = tinytex_root(), to = select_dir('Select Destination Directory'), move = FALSE ) { op = options(tinytex.warn.appdata = FALSE); on.exit(options(op), add = TRUE) if (!dir_exists(from)) stop('TinyTeX does not seem to be installed.') if (length(to) != 1 || !dir_exists(to)) stop("The destination directory '", to, "' does not exist.") target = file.path(to, basename(from)) if (!move || !{tlmgr_path('remove'); res <- file.rename(from, target)}) { res = file.copy(from, to, recursive = TRUE) if (res && move) { tlmgr_path('remove') unlink(from, recursive = TRUE) } } if (res && move) use_tinytex(target) res } #' @rdname copy_tinytex #' @export use_tinytex = function(from = select_dir('Select TinyTeX Directory')) { if (length(from) != 1) stop('Please provide a valid path to the TinyTeX directory.') d = list.files(file.path(from, 'bin'), full.names = TRUE) d = d[dir_exists(d)] if (length(d) != 1) stop("The directory '", from, "' does not contain TinyTeX.") p = file.path(d, 'tlmgr') if (os == 'windows') p = paste0(p, '.bat') if (system2(p, c('path', 'add')) != 0) stop( "Failed to add '", d, "' to your system's environment variable PATH. You may ", "consider the fallback approach, i.e., set options(tinytex.tlmgr.path = '", p, "')." ) op = options(tinytex.tlmgr.path = p); on.exit(options(op), add = TRUE) post_install_config(FALSE) message('Restart R and your editor and check if tinytex::tinytex_root() points to ', from) } select_dir = function(caption = 'Select Directory') { d = tryCatch(rstudioapi::selectDirectory(caption), error = function(e) { if (os == 'windows') utils::choose.dir(caption = caption) else { tcltk::tk_choose.dir(caption = caption) } }) if (!is.null(d) && !is.na(d)) d } tinytex/R/latex.R0000644000176200001440000006773314526723450013447 0ustar liggesusers#' Compile a LaTeX document #' #' The function \code{latexmk()} emulates the system command \command{latexmk} #' (\url{https://ctan.org/pkg/latexmk}) to compile a LaTeX document. The #' functions \code{pdflatex()}, \code{xelatex()}, and \code{lualatex()} are #' wrappers of \code{latexmk(engine =, emulation = TRUE)}. #' #' The \command{latexmk} emulation works like this: run the LaTeX engine once #' (e.g., \command{pdflatex}), run \command{makeindex} to make the index if #' necessary (the \file{*.idx} file exists), run the bibliography engine #' \command{bibtex} or \command{biber} to make the bibliography if necessary #' (the \file{*.aux} or \file{*.bcf} file exists), and finally run the LaTeX #' engine a number of times (the maximum is 10 by default) to resolve all #' cross-references. #' #' By default, LaTeX warnings will be converted to R warnings. To suppress these #' warnings, set \code{options(tinytex.latexmk.warning = FALSE)}. #' #' If \code{emulation = FALSE}, you need to make sure the executable #' \command{latexmk} is available in your system, otherwise \code{latexmk()} #' will fall back to \code{emulation = TRUE}. You can set the global option #' \code{options(tinytex.latexmk.emulation = FALSE)} to always avoid emulation #' (i.e., always use the executable \command{latexmk}). #' #' The default command to generate the index (if necessary) is #' \command{makeindex}. To change it to a different command (e.g., #' \command{zhmakeindex}), you may set the global option #' \code{tinytex.makeindex}. To pass additional command-line arguments to the #' command, you may set the global option \code{tinytex.makeindex.args} (e.g., #' \code{options(tinytex.makeindex = 'zhmakeindex', tinytex.makeindex.args = #' c('-z', 'pinyin'))}). #' #' If you are using the LaTeX distribution TinyTeX, but its path is not in the #' \code{PATH} variable of your operating system, you may set the global option #' \code{tinytex.tlmgr.path} to the full path of the executable \command{tlmgr}, #' so that \code{latexmk()} knows where to find executables like #' \command{pdflatex}. For example, if you are using Windows and your TinyTeX is #' on an external drive \file{Z:/} under the folder \file{TinyTeX}, you may set #' \code{options(tinytex.tlmgr.path = "Z:/TinyTeX/bin/windows/tlmgr.bat")}. #' Usually you should not need to set this option because TinyTeX can add itself #' to the \code{PATH} variable during installation or via #' \code{\link{use_tinytex}()}. In case both methods fail, you can use this #' manual approach. #' @param file A LaTeX file path. #' @param engine A LaTeX engine (can be set in the global option #' \code{tinytex.engine}, e.g., \code{options(tinytex.engine = 'xelatex')}). #' @param bib_engine A bibliography engine (can be set in the global option #' \code{tinytex.bib_engine}). #' @param engine_args Command-line arguments to be passed to \code{engine} (can #' be set in the global option \code{tinytex.engine_args}, e.g., #' \code{options(tinytex.engine_args = '-shell-escape'}). #' @param emulation Whether to emulate the executable \command{latexmk} using R. #' Note that this is unused when \code{engine == 'tectonic'}. #' @param min_times,max_times The minimum and maximum number of times to rerun #' the LaTeX engine when using emulation. You can set the global options #' \code{tinytex.compile.min_times} or \code{tinytex.compile.max_times}, e.g., #' \code{options(tinytex.compile.max_times = 3)}. #' @param install_packages Whether to automatically install missing LaTeX #' packages found by \code{\link{parse_packages}()} from the LaTeX log. This #' argument is only for the emulation mode and TeX Live. Its value can also be #' set via the global option \code{tinytex.install_packages}, e.g., #' \code{options(tinytex.install_packages = FALSE)}. #' @param pdf_file Path to the PDF output file. By default, it is under the same #' directory as the input \code{file} and also has the same base name. When #' \code{engine == 'latex'}, this will be a DVI file. #' @param clean Whether to clean up auxiliary files after compilation (can be #' set in the global option \code{tinytex.clean}, which defaults to #' \code{TRUE}). #' @export #' @return A character string of the path of the output file (i.e., the value of #' the \code{pdf_file} argument). latexmk = function( file, engine = c('pdflatex', 'xelatex', 'lualatex', 'latex', 'tectonic'), bib_engine = c('bibtex', 'biber'), engine_args = NULL, emulation = TRUE, min_times = 1, max_times = 10, install_packages = emulation && tlmgr_writable(), pdf_file = gsub('tex$', 'pdf', file), clean = TRUE ) { if (!grepl('[.]tex$', file)) stop("The input file '", file, "' does not have the .tex extension") file = path.expand(file) if (missing(engine)) engine = getOption('tinytex.engine', engine) engine = gsub('^(pdf|xe|lua)(tex)$', '\\1la\\2', engine) # normalize *tex to *latex engine = match.arg(engine) is_latex = engine == 'latex' tweak_path() if (missing(emulation)) emulation = getOption('tinytex.latexmk.emulation', emulation) if (!emulation) { if (Sys.which('latexmk') == '') { warning('The executable "latexmk" not found in your system') emulation = TRUE } else if (system2_quiet('latexmk', '-v') != 0) { warning('The executable "latexmk" was found but does not work') emulation = TRUE } } if (missing(min_times)) min_times = getOption('tinytex.compile.min_times', min_times) if (missing(max_times)) max_times = getOption('tinytex.compile.max_times', max_times) if (missing(install_packages)) install_packages = getOption('tinytex.install_packages', install_packages) if (missing(bib_engine)) bib_engine = getOption('tinytex.bib_engine', bib_engine) if (missing(engine_args)) engine_args = getOption('tinytex.engine_args', engine_args) if (missing(clean)) clean = getOption('tinytex.clean', TRUE) pdf = gsub('tex$', if (is_latex) 'dvi' else 'pdf', basename(file)) if (!is.null(output_dir <- getOption('tinytex.output_dir'))) { output_dir_arg = shQuote(paste0(if (emulation) '-', '-output-directory=', output_dir)) if (length(grep(output_dir_arg, engine_args, fixed = TRUE)) == 0) stop( "When you set the global option 'tinytex.output_dir', the argument 'engine_args' ", "must contain this value: ", capture.output(dput(output_dir_arg)) ) pdf = file.path(output_dir, pdf) if (missing(pdf_file)) pdf_file = file.path(output_dir, basename(pdf_file)) } if (is_latex) pdf_file = with_ext(pdf_file, 'dvi') check_pdf = function() { if (!file.exists(pdf)) show_latex_error(file, with_ext(pdf, 'log'), TRUE) file_rename(pdf, pdf_file) pdf_file } if (engine == 'tectonic') { system2_quiet('tectonic', c(engine_args, shQuote(file))) return(check_pdf()) } if (emulation) { latexmk_emu( file, engine, bib_engine, engine_args, min_times, max_times, install_packages, clean ) return(check_pdf()) } system2_quiet('latexmk', c( '-latexoption=-halt-on-error -interaction=batchmode', if (is_latex) '-latex=latex' else paste0('-pdf -pdflatex=', engine), engine_args, shQuote(file) ), error = { if (install_packages) warning( 'latexmk(install_packages = TRUE) does not work when emulation = FALSE' ) check_latexmk_version() }) if (clean) system2('latexmk', c('-c', engine_args), stdout = FALSE) check_pdf() } #' @param ... Arguments to be passed to \code{latexmk()} (other than #' \code{engine} and \code{emulation}). #' @rdname latexmk #' @export pdflatex = function(...) latexmk(engine = 'pdflatex', emulation = TRUE, ...) #' @rdname latexmk #' @export xelatex = function(...) latexmk(engine = 'xelatex', emulation = TRUE, ...) #' @rdname latexmk #' @export lualatex = function(...) latexmk(engine = 'lualatex', emulation = TRUE, ...) # a quick and dirty version of latexmk (should work reasonably well unless the # LaTeX document is extremely complicated) latexmk_emu = function( file, engine, bib_engine = c('bibtex', 'biber'), engine_args = NULL, min_times = 1, max_times = 10, install_packages = FALSE, clean ) { aux = c( 'log', 'idx', 'aux', 'bcf', 'blg', 'bbl', 'fls', 'out', 'lof', 'lot', 'toc', 'nav', 'snm', 'vrb', 'ilg', 'ind', 'xwm', 'brf', 'run.xml' ) base = gsub('[.]tex$', '', basename(file)) aux_files = paste(base, aux, sep = '.') aux_files = c(aux_files, 'preview.aux') # generated by the preview package if (!is.null(output_dir <- getOption('tinytex.output_dir'))) aux_files = file.path(output_dir, aux_files) names(aux_files)[seq_along(aux)] = aux logfile = aux_files['log']; unlink(logfile) # clean up the log before compilation # clean up aux files from LaTeX compilation files1 = exist_files(aux_files) keep_log = FALSE on.exit({ files2 = exist_files(aux_files) files3 = setdiff(files2, files1) if (keep_log || length(latex_warning(logfile))) files3 = setdiff(files3, logfile) if (clean) unlink(files3) .global$update_noted = NULL }, add = TRUE) pkgs_last = character() filep = sub('.log$', if (engine == 'latex') '.dvi' else '.pdf', logfile) verbose = getOption('tinytex.verbose', FALSE) # install commands like pdflatex, bibtex, biber, and makeindex if necessary install_cmd = function(cmd) { if (install_packages && Sys.which(cmd) == '') parse_install(file = cmd) } install_cmd(engine) run_engine = function() { on_error = function() { if (install_packages && file.exists(logfile)) { pkgs = parse_packages(logfile, quiet = !verbose) if (length(pkgs) && !identical(pkgs, pkgs_last)) { if (verbose) message('Trying to automatically install missing LaTeX packages...') if (tlmgr_install(pkgs, .quiet = !verbose) == 0) { pkgs_last <<- pkgs return(run_engine()) } } else if (tlmgr_writable()) { # chances are you are the sysadmin, and don't need ~/.TinyTeX if (delete_texmf_user()) return(run_engine()) } } keep_log <<- TRUE show_latex_error(file, logfile) } res = system2_quiet( engine, c('-halt-on-error', '-interaction=batchmode', engine_args, shQuote(file)), error = { if (install_packages) tlmgr_update(run_fmtutil = FALSE, .quiet = TRUE) on_error() }, logfile = logfile, fail_rerun = verbose ) # PNAS you are the worst! Why don't you signal an error in case of missing packages? if (res == 0 && !file.exists(filep)) on_error() invisible(res) } run_engine() # some problems only trigger warnings but not errors, e.g., # https://github.com/rstudio/tinytex/issues/311 fix them and re-run engine if (install_packages && check_extra(logfile)) run_engine() # generate index if (file.exists(idx <- aux_files['idx'])) { idx_engine = getOption('tinytex.makeindex', 'makeindex') install_cmd(idx_engine) run_engine() # run the engine one more time (cf rstudio/bookdown#1274) system2_quiet(idx_engine, c(getOption('tinytex.makeindex.args'), shQuote(idx)), error = { stop("Failed to build the index via ", idx_engine, call. = FALSE) }) } # generate bibliography bib_engine = match.arg(bib_engine) install_cmd(bib_engine) pkgs_last = character() aux = aux_files[if ((biber <- bib_engine == 'biber')) 'bcf' else 'aux'] if (file.exists(aux)) { if (biber || require_bibtex(aux)) { blg = aux_files['blg'] # bibliography log file build_bib = function() system2_quiet(bib_engine, shQuote(aux), error = { check_blg = function() { if (!file.exists(blg)) return(TRUE) x = read_lines(blg) if (length(grep('error message', x)) == 0) return(TRUE) warn = function() { warning( bib_engine, ' seems to have failed:\n\n', paste(x, collapse = '\n'), call. = FALSE ) TRUE } if (!tlmgr_available() || !install_packages) return(warn()) # install the possibly missing .bst package and rebuild bib pkgs = parse_packages(text = x, quiet = !verbose) if (length(pkgs) == 0 || identical(pkgs, pkgs_last)) return(warn()) pkgs_last <<- pkgs tlmgr_install(pkgs); build_bib() FALSE } if (check_blg()) stop("Failed to build the bibliography via ", bib_engine, call. = FALSE) }) build_bib() } } for (i in seq_len(max_times)) { if (i > min_times) { if (file.exists(logfile)) { if (!needs_rerun(logfile)) break } else warning('The LaTeX log file "', logfile, '" is not found') } run_engine() } } require_bibtex = function(aux) { x = read_lines(aux) r = length(grep('^\\\\citation\\{', x)) && length(grep('^\\\\bibdata\\{', x)) && length(grep('^\\\\bibstyle\\{', x)) if (r && !tlmgr_available() && os == 'windows') tweak_aux(aux, x) r } # remove the .bib extension in \bibdata{} in the .aux file, because bibtex on # Windows requires no .bib extension (sigh) tweak_aux = function(aux, x = read_lines(aux)) { r = '^\\\\bibdata\\{.+\\}\\s*$' if (length(i <- grep(r, x)) == 0) return() x[i] = gsub('[.]bib([,}])', '\\1', x[i]) writeLines(x, aux) } needs_rerun = function(log, text = read_lines(log)) { any(grepl( '(Rerun to get |Please \\(?re\\)?run | Rerun LaTeX\\.)', text, useBytes = TRUE )) } system2_quiet = function(..., error = NULL, logfile = NULL, fail_rerun = TRUE) { # system2(stdout = FALSE) fails on Windows with MiKTeX's pdflatex in the R # console in RStudio: https://github.com/rstudio/rstudio/issues/2446 so I have # to redirect stdout and stderr to files instead f1 = tempfile('stdout'); f2 = tempfile('stderr') on.exit(unlink(c(f1, f2)), add = TRUE) # run the command quietly if possible res = system2(..., stdout = if (use_file_stdout()) f1 else FALSE, stderr = f2) if (is.character(logfile) && file.exists(f2) && length(e <- read_lines(f2))) { i = grep('^\\s*$', e, invert = TRUE) e[i] = paste('!', e[i]) # prepend ! to non-empty error messages cat('', e, file = logfile, sep = '\n', append = TRUE) } # if failed, use the normal mode if (fail_rerun && res != 0) res = system2(...) # if still fails, run the error callback if (res != 0) error # lazy evaluation invisible(res) } use_file_stdout = function() { getOption('tinytex.stdout.file', { os == 'windows' && interactive() && !is.na(Sys.getenv('RSTUDIO', NA)) }) } # parse the LaTeX log and show error messages show_latex_error = function( file, logfile = gsub('[.]tex$', '.log', basename(file)), force = FALSE ) { e = c('LaTeX failed to compile ', file, '. See https://yihui.org/tinytex/r/#debugging for debugging tips.') if (!file.exists(logfile)) stop(e, call. = FALSE) x = read_lines(logfile) b = grep('^\\s*$', x) # blank lines b = c(b, which(x == "Here is how much of TeX's memory you used:")) m = NULL for (i in grep('^! ', x)) { # ignore the last error message about the fatal error if (grepl('==> Fatal error occurred', x[i], fixed = TRUE)) next n = b[b > i] n = if (length(n) == 0) i else min(n) - 1L m = c(m, x[i:n], '') } if (length(m)) { message(paste(m, collapse = '\n')) latex_hints(m, file) stop(e, ' See ', logfile, ' for more info.', call. = FALSE) } else if (force) stop(e, call. = FALSE) } latex_hints = function(x, f) { check_inline_math(x, f) check_unicode(x) } check_inline_math = function(x, f) { r = 'l[.][0-9]+\\s*|\\s*[0-9.]+\\\\times.*' if (!any('! Missing $ inserted.' == x) || !length(i <- grep(r, x))) return() m = gsub(r, '', x[i]); m = m[m != ''] s = with_ext(f, 'Rmd') if (file.exists(s)) message( if (length(m)) c('Try to find the following text in ', s, ':\n', paste(' ', m, '\n'), '\n'), 'You may need to add $ $ around a certain inline R expression `r ` in ', s, if (length(m)) ' (see the above hint)', '. See https://github.com/rstudio/rmarkdown/issues/385 for more info.' ) } check_unicode = function(x) { if (length(grep('! (Package inputenc|LaTeX) Error: Unicode character', x))) message( 'Try other LaTeX engines instead (e.g., xelatex) if you are using pdflatex.', if ('rmarkdown' %in% loadedNamespaces()) ' See https://bookdown.org/yihui/rmarkdown-cookbook/latex-unicode.html' ) } # whether a LaTeX log file contains LaTeX or package (e.g. babel) warnings latex_warning = function(file, show = getOption('tinytex.latexmk.warning', TRUE)) { if (!file.exists(file)) return() # if the option tinytex.latexmk.warning = FALSE, delete the log in latexmk_emu() if (!show && missing(show)) return() x = read_lines(file) if (length(i <- grep('^(LaTeX|Package [[:alnum:]]+) Warning:', x)) == 0) return() # these warnings may be okay (our Pandoc LaTeX template in rmarkdown may need an update) i = i[grep('^Package (fixltx2e|caption|hyperref) Warning:', x[i], invert = TRUE)] if (length(i) == 0) return() b = grep('^\\s*$', x) i = unlist(lapply(i, function(j) { n = b[b > j] n = if (length(n) == 0) i else min(n) - 1L j:n })) i = sort(unique(i)) if (show) for(msg in x[i]) warning(msg, call. = FALSE, immediate. = TRUE) x[i] } # check if any babel/glossaries/... packages are missing check_extra = function(file) { length(m <- latex_warning(file, FALSE)) > 0 && length(grep('^Package ([^ ]+) Warning:', m)) > 0 && any( check_babel(m), check_glossaries(m), check_datetime2(m), check_polyglossia(m) ) } check_babel = function(text) { r = "^(\\(babel\\).* |Package babel Warning: No hyphenation patterns were preloaded for the )language [`']([^']+)'.*$" if (length(m <- grep_sub(r, 'hyphen-\\2', text)) == 0) return(FALSE) # (babel) the language `German (new orthography)' into the format m = gsub('\\s.*', '', m) m[m == 'hyphen-pinyin'] = 'hyphen-chinese' tlmgr_install(tolower(m)) == 0 } # Package glossaries Warning: No language module detected for `english'. # (glossaries) Language modules need to be installed separately. # (glossaries) Please check on CTAN for a bundle called # (glossaries) `glossaries-english' or similar. check_glossaries = function(text) { r = "^\\(glossaries).* [`']([^']+)'.*$" if (length(m <- grep_sub(r, '\\1', text)) == 0) return(FALSE) tlmgr_install(m) == 0 } # Package polyglossia Warning: No hyphenation patterns were loaded for `hungarian' # Package polyglossia Warning: No hyphenation patterns were loaded for British English check_polyglossia = function(text) { r = "^Package polyglossia Warning: No hyphenation patterns were loaded for ([`'][^']+'|British English).*" if (length(m <- grep_sub(r, '\\1', text)) == 0) return(FALSE) m[m == 'British English'] = 'english' m = gsub("[`']", '', m) tlmgr_install(paste0('hyphen-', m)) == 0 } # Package datetime2 Warning: Date-Time Language Module `english' not installed on # input line xxx. check_datetime2 = function(text) { r = "^Package datetime2 Warning: Date-Time Language Module [`']([^']+)' not installed.*$" if (length(m <- grep_sub(r, '\\1', text)) == 0) return(FALSE) tlmgr_install(paste0('datetime2-', m)) == 0 } # check the version of latexmk check_latexmk_version = function() { out = system2('latexmk', '-v', stdout = TRUE) reg = '^.*Version (\\d+[.]\\d+).*$' out = grep_sub(reg, '\\1', out) if (length(out) == 0) return() ver = as.numeric_version(out[1]) if (ver >= '4.43') return() system2('latexmk', '-v') warning( 'Your latexmk version seems to be too low. ', 'You may need to update the latexmk package or your LaTeX distribution.', call. = FALSE ) } # return file paths that exist exist_files = function(files) { files[utils::file_test('-f', files)] } # use file.copy() if file.rename() fails file_rename = function(from, to) { if (from == to) return(TRUE) if (!suppressWarnings(file.rename(from, to))) { if (file.copy(from, to, overwrite = TRUE)) file.remove(from) } } #' Find missing LaTeX packages from a LaTeX log file #' #' Analyze the error messages in a LaTeX log file to figure out the names of #' missing LaTeX packages that caused the errors. These packages can be #' installed via \code{\link{tlmgr_install}()}. Searching for missing packages #' is based on \code{\link{tlmgr_search}()}. #' @param log Path to the LaTeX log file (typically named \file{*.log}). #' @param text A character vector of the error log (read from the file provided #' by the \code{log} argument by default). #' @param files A character vector of names of the missing files (automatically #' detected from the \code{log} by default). #' @param quiet Whether to suppress messages when finding packages. It should be #' a logical vector of length 3: the first element indicates whether to #' suppress the message when no missing LaTeX packages could be detected from #' the log, the second element indicate whether to suppress the message when #' searching for packages via \code{tlmgr_search()}, and the third element #' indicates whether to warn if no packages could be found via #' \code{tlmgr_search()}. #' @return A character vector of LaTeX package names. #' @export parse_packages = function( log, text = read_lines(log), files = detect_files(text), quiet = rep(FALSE, 3) ) { pkgs = character(); quiet = rep_len(quiet, length.out = 3) x = unique(c(files, miss_font())) if (length(x) == 0) { if (!quiet[1]) message( 'I was unable to find any missing LaTeX packages from the error log', if (missing(log)) '.' else c(' ', log, '.') ) return(invisible(pkgs)) } for (j in seq_along(x)) { l = tlmgr_search(paste0('/', x[j]), stdout = TRUE, .quiet = quiet[2]) if (length(l) == 0) next if (x[j] == 'fandol') return(x[j]) # a known package # why $? e.g. searching for mf returns a list like this # metafont.x86_64-darwin: # bin/x86_64-darwin/mf <- what we want # metapost.x86_64-darwin: # bin/x86_64-darwin/mfplain <- but this also matches /mf k = grep(paste0('/', x[j], '$'), l) # only match /mf exactly if (length(k) == 0) { if (!quiet[3]) warning('Failed to find a package that contains ', x[j]) next } k = k[k > 2] p = grep(':$', l) if (length(p) == 0) next for (i in k) { pkg = gsub(':$', '', l[max(p[p < i])]) # find the package name pkgs = c(pkgs, setNames(pkg, x[j])) } } pkgs = gsub('[.].*', '', pkgs) # e.g., 'metafont.x86_64-darwin' unique(pkgs) } regex_errors = function() { # possible errors are like: # ! LaTeX Error: File `framed.sty' not found. # /usr/local/bin/mktexpk: line 123: mf: command not found # ! Font U/psy/m/n/10=psyr at 10.0pt not loadable: Metric (TFM) file not found # !pdfTeX error: /usr/local/bin/pdflatex (file tcrm0700): Font tcrm0700 at 600 not found # xdvipdfmx:fatal: Unable to find TFM file "rsfs10" # ! The font "FandolSong-Regular" cannot be found. # ! Package babel Error: Unknown option `ngerman'. Either you misspelled it # (babel) or the language definition file ngerman.ldf was not found. # !pdfTeX error: pdflatex (file 8r.enc): cannot open encoding file for reading # ! CTeX fontset `fandol' is unavailable in current mode # Package widetext error: Install the flushend package which is a part of sttools # Package biblatex Info: ... file 'trad-abbrv.bbx' not found # ! Package pdftex.def Error: File `logo-mdpi-eps-converted-to.pdf' not found # ! xdvipdfmx:fatal: pdf_ref_obj(): passed invalid object. # ! Package tikz Error: I did not find the tikz library 'hobby'... named tikzlibraryhobby.code.tex # support file `supp-pdf.mkii' (supp-pdf.tex) is missing # ! I can't find file `hyph-de-1901.ec.tex'. # ! Package pdfx Error: No color profile sRGB_IEC61966-2-1_black_scaled.icc found # No file LGRcmr.fd. ! LaTeX Error: This NFSS system isn't set up properly. list( font = c( # error messages about missing fonts (don't move the first item below, as # it is special and emitted by widetext; the rest can be freely reordered) '.*Package widetext error: Install the ([^ ]+) package.*', ".*! Font [^=]+=([^ ]+).+ not loadable.*", '.*! .*The font "([^"]+)" cannot be found.*', '.*!.+ error:.+\\(file ([^)]+)\\): .*', '.*Unable to find TFM file "([^"]+)".*' ), fd = c( # font definition files ".*No file ([^`'. ]+[.]fd)[.].*" ), epstopdf = c( # possible errors when epstopdf is missing ".* File [`'](.+eps-converted-to.pdf)'.*", ".*xdvipdfmx:fatal: pdf_ref_obj.*" ), colorprofiles.sty = c( '.* Package pdfx Error: No color profile ([^ ]+).*' ), `lua-uni-algos.lua` = c( ".* module '(lua-uni-normalize)' not found:.*" ), tikz = c( # when a required tikz library is missing '.* (tikzlibrary[^ ]+?[.]code[.]tex).*' ), l3backend = c( # L3 programming layer mismatch (#424) '^File: ([^ ]+) \\d{4,}-\\d{2}-\\d{2} .*$' ), style = c( # missing .sty or commands ".* Loading '([^']+)' aborted!", ".*! LaTeX Error: File [`']([^']+)' not found.*", ".* [fF]ile ['`]?([^' ]+)'? not found.*", '.*the language definition file ([^ ]+) .*', '.* \\(file ([^)]+)\\): cannot open .*', '.* open style file ([^ ]+).*', ".*file [`']([^']+)' .*is missing.*", ".*! CTeX fontset [`']([^']+)' is unavailable.*", ".*: ([^:]+): command not found.*", ".*! I can't find file [`']([^']+)'.*" ) ) } # find filenames (could also be font names) from LaTeX error logs detect_files = function(text) { r = regex_errors() x = grep(paste(unlist(r), collapse = '|'), text, value = TRUE) if (length(x) > 0) unique(unlist(lapply(unlist(r), function(p) { v = grep_sub(p, '\\1', x) if (length(v) == 0) return(v) if (p == r$tikz && length(grep('! Package tikz Error:', text)) == 0) return() # if the problem is caused by the L3 programming layer mismatch, use the # last found file before the error line, which should be from l3backend if (p == r$l3backend) return( if (length(grep('^! Undefined control sequence', text)) > 0) tail(v, 1) ) # these are some known filenames for (i in c('epstopdf', grep('[.]', names(r), value = TRUE))) { if (p %in% r[[i]]) return(i) } if (p == r$fd) v = tolower(v) # LGRcmr.fd -> lgrcmr.fd if (!(p %in% r$font)) return(v) if (p == r$font[1]) paste0(v, '.sty') else font_ext(v) }))) } #' Parse the LaTeX log and install missing LaTeX packages if possible #' #' This is a helper function that combines \code{\link{parse_packages}()} and #' \code{\link{tlmgr_install}()}. #' @param ... Arguments passed to \code{\link{parse_packages}()}. #' @export parse_install = function(...) { tlmgr_install(parse_packages(...)) } # check missfont.log and detect the missing font packages; missfont.log # typically looks like this: # mktexpk --mfmode / --bdpi 600 --mag 1+0/600 --dpi 600 ecrm0900 miss_font = function() { if (!file.exists(f <- 'missfont.log')) return() on.exit(unlink(f), add = TRUE) x = gsub('\\s*$', '', read_lines(f)) x = grep('.+\\s+.+', x, value = TRUE) if (length(x) == 0) return() x1 = gsub('.+\\s+', '', x) # possibly missing fonts x2 = gsub('\\s+.+', '', x) # the command to make fonts unique(c(font_ext(x1), x2)) } font_ext = function(x) { i = !grepl('[.]', x) x[i] = paste0(x[i], '(-(Bold|Italic|Regular).*)?[.](tfm|afm|mf|otf|ttf)') x } # it should be rare that we need to manually run texhash texhash = function() { tweak_path() system2('texhash') } updmap = function(usermode = FALSE) { tweak_path() system2('updmap', if (usermode) '--user' else '--sys') } fmtutil = function(usermode = FALSE, ...) { tweak_path() system2('fmtutil', c(if (usermode) '--user' else '--sys', '--all'), ...) } fc_cache = function(args = c('-v', '-r')) { tweak_path() # run fc-cache on default dirs, then on the TinyTeX root dir for (i in unique(c('', tinytex_root(error = FALSE)))) system2('fc-cache', shQuote(c(args, if (i != '') i))) } # refresh/update/regenerate everything refresh_all = function(...) { fc_cache(); fmtutil(...); updmap(...); texhash() } # look up files in the Kpathsea library, e.g., kpsewhich('Sweave.sty') kpsewhich = function(filename, options = character(), ...) { tweak_path() system2('kpsewhich', c(options, shQuote(filename)), ...) } tinytex/R/package.R0000644000176200001440000000027314366520027013704 0ustar liggesusers#' @import stats utils tools #' @importFrom xfun grep_sub dir_exists download_file in_dir #' @keywords internal '_PACKAGE' os = .Platform$OS.type .global = new.env(parent = emptyenv()) tinytex/R/tlmgr.R0000644000176200001440000004152514616205351013441 0ustar liggesusers#' Run the TeX Live Manager #' #' Execute the \command{tlmgr} command to search for LaTeX packages, install #' packages, update packages, and so on. #' #' The \code{tlmgr()} function is a wrapper of \code{system2('tlmgr')}. All #' other \code{tlmgr_*()} functions are based on \code{tlmgr} for specific #' tasks. For example, \code{tlmgr_install()} runs the command \command{tlmgr #' install} to install LaTeX packages, and \code{tlmgr_update} runs the command #' \command{tlmgr update}, etc. Note that \code{tlmgr_repo} runs \command{tlmgr #' options repository} to query or set the CTAN repository. Please consult the #' \pkg{tlmgr} manual for full details. #' @param args A character vector of arguments to be passed to the command #' \command{tlmgr}. #' @param usermode (For expert users only) Whether to use TeX Live's #' \href{https://www.tug.org/texlive/doc/tlmgr.html#USER-MODE}{user mode}. If #' \code{TRUE}, you must have run \code{tlmgr('init-usertree')} once before. #' This option allows you to manage a user-level texmf tree, e.g., install a #' LaTeX package to your home directory instead of the system directory, to #' which you do not have write permission. This option should not be needed on #' personal computers, and has some limitations, so please read the #' \pkg{tlmgr} manual very carefully before using it. #' @param ... For \code{tlmgr()}, additional arguments to be passed to #' \code{\link{system2}()} (e.g., \code{stdout = TRUE} to capture stdout). For #' other functions, arguments to be passed to \code{tlmgr()}. #' @param .quiet Whether to hide the actual command before executing it. #' @references The \pkg{tlmgr} manual: #' \url{https://www.tug.org/texlive/doc/tlmgr.html} #' @export #' @examplesIf interactive() #' # search for a package that contains titling.sty #' tlmgr_search('titling.sty') #' #' # to match titling.sty exactly, add a slash before the keyword, e.g. #' tlmgr_search('/titling.sty') #' #' # use a regular expression if you want to be more precise, e.g. #' tlmgr_search('/titling\\.sty$') #' #' # list all installed LaTeX packages #' tlmgr(c('info', '--list', '--only-installed', '--data', 'name')) tlmgr = function(args = character(), usermode = FALSE, ..., .quiet = FALSE) { tweak_path() if (!.quiet && !tlmgr_available()) warning( '\nTeX Live does not seem to be installed. See https://yihui.org/tinytex/.\n' ) if (usermode) args = c('--usermode', args) if (!.quiet) message(paste(c('tlmgr', args), collapse = ' ')) # use TeX Live's own binaries (e.g., curl): https://github.com/rstudio/tinytex/issues/354 vars = xfun::set_envvar(c(TEXLIVE_PREFER_OWN = 1)) on.exit(xfun::set_envvar(vars), add = TRUE) system2('tlmgr', args, ...) } # add ~/bin to PATH if necessary on Linux, because sometimes PATH may not be # inherited (https://github.com/rstudio/rstudio/issues/1878), and TinyTeX is # installed to ~/bin by default; on Windows, prioritize win_app_dir('TinyTeX') # if it exists (so TinyTeX can be used even when MiKTeX is installed); on macOS, # check if it is necessary to add ~/Library/TinyTeX/bin/*/ to PATH #' @importFrom xfun is_linux is_unix is_macos is_windows with_ext tweak_path = function() { # check tlmgr exists under the default installation dir of TinyTeX, or the # global option tinytex.tlmgr.path f = getOption('tinytex.tlmgr.path', find_tlmgr()) if (length(f) == 0 || !file_test('-x', f)) return() bin = normalizePath(dirname(f)) # if the pdftex from TinyTeX is already on PATH, no need to adjust the PATH if ((p <- Sys.which('pdftex')) != '') { p2 = with_ext(file.path(bin, 'pdftex'), xfun::file_ext(p)) if (xfun::same_path(p, p2)) return() } old = Sys.getenv('PATH') one = unlist(strsplit(old, s <- .Platform$path.sep, fixed = TRUE)) Sys.setenv(PATH = paste(c(bin, setdiff(one, bin)), collapse = s)) do.call( on.exit, list(substitute(Sys.setenv(PATH = x), list(x = old)), add = TRUE), envir = parent.frame() ) } tlmgr_available = function() Sys.which('tlmgr') != '' #' @param what A search keyword as a (Perl) regular expression. #' @param file Whether to treat \code{what} as a filename (pattern). #' @param all For \code{tlmgr_search()}, whether to search in everything, #' including package names, descriptions, and filenames. For #' \code{tlmgr_update()}, whether to update all installed packages. #' @param global Whether to search the online TeX Live Database or locally. #' @param word Whether to restrict the search of package names and descriptions #' to match only full words. #' @rdname tlmgr #' @export tlmgr_search = function(what, file = TRUE, all = FALSE, global = TRUE, word = FALSE, ...) { tlmgr(c( 'search', if (file) '--file', if (all) '--all', if (global) '--global', if (word) '--word', shQuote(what) ), ...) } #' @param pkgs A character vector of LaTeX package names. #' @param path Whether to run \code{tlmgr_path('add')} after installing packages #' (\code{path = TRUE} is a conservative default: it is only necessary to do #' this after a binary package is installed, such as the \pkg{metafont} #' package, which contains the executable \command{mf}, but it does not hurt #' even if no binary packages were installed). #' @rdname tlmgr #' @export tlmgr_install = function(pkgs = character(), usermode = FALSE, path = !usermode && os != 'windows', ...) { if (length(pkgs) == 0) return(invisible(0L)) update_pkgs = function(...) tlmgr_update(..., usermode = usermode) # if any packages have been installed, update packages first if (getOption('tinytex.tlmgr_update', TRUE) && any(check_installed(pkgs))) update_pkgs() res = tlmgr(c('install', pkgs), usermode, ...) if (res != 0 || any(!check_installed(pkgs))) { update_pkgs(all = FALSE) # update tlmgr itself since it might be outdated res = tlmgr(c('install', pkgs), usermode, ...) } if ('epstopdf' %in% pkgs && is_unix() && Sys.which('gs') == '') { if (is_macos() && Sys.which('brew') != '') { message('Trying to install GhostScript via Homebrew for the epstopdf package.') system('brew install ghostscript') } if (Sys.which('gs') == '') warning('GhostScript is required for the epstopdf package.') } if (missing(path)) path = path && need_add_path() if (path) tlmgr_path('add') invisible(res) } # we should run `tlmgr path add` after `tlmgr install` only when the `tlmgr` # found from PATH is a symlink that links to another symlink (typically under # TinyTeX/bin/platform/tlmgr, which is typically a symlink to tlmgr.pl) need_add_path = function() { is_writable(p <- Sys.which('tlmgr')) && (p2 <- Sys.readlink(p)) != '' && basename(Sys.readlink(p2)) == 'tlmgr.pl' && basename(dirname(dirname(p2))) == 'bin' } is_writable = function(p) file.access(p, 2) == 0 tlmgr_writable = function() is_writable(Sys.which('tlmgr')) #' Check if certain LaTeX packages are installed #' #' If a package has been installed in TinyTeX or TeX Live, the command #' \command{tlmgr info PKG} should return \code{PKG} where \code{PKG} is the #' package name. #' @param pkgs A character vector of LaTeX package names. #' @return A logical vector indicating if packages specified in \code{pkgs} are #' installed. #' @note This function only works with LaTeX distributions based on TeX Live, #' such as TinyTeX. #' @export #' @examplesIf interactive() #' tinytex::check_installed('framed') check_installed = function(pkgs) { if (length(pkgs) == 0) return(TRUE) res = suppressWarnings(tryCatch( tl_list(pkgs, stdout = TRUE, stderr = FALSE, .quiet = TRUE), error = function(e) NULL )) pkgs %in% res } #' @rdname tlmgr #' @export tlmgr_remove = function(pkgs = character(), usermode = FALSE) { if (length(pkgs)) tlmgr(c('remove', pkgs), usermode) } #' @param format The data format to be returned: \code{raw} means the raw output #' of the command \command{tlmgr --version}, \code{string} means a character #' string of the format \samp{TeX Live YEAR (TinyTeX) with tlmgr DATE}, and #' \code{list} means a list of the form \code{list(texlive = YEAR, tlmgr = #' DATE, tinytex = TRUE/FALSE)}. #' @rdname tlmgr #' @importFrom xfun raw_string #' @export tlmgr_version = function(format = c('raw', 'string', 'list')) { vers = tlmgr('--version', stdout = TRUE, .quiet = TRUE) format = match.arg(format) if (format != 'raw') { year = xfun::grep_sub('^TeX Live.* version (\\d+).*$', '\\1', vers)[1] tinytex = is_tinytex() date = xfun::grep_sub('^tlmgr revision \\d+ \\(([0-9-]+) .*$', '\\1', vers)[1] vers = if (format == 'list') { list(texlive = as.integer(year), tlmgr = as.Date(date), tinytex = tinytex) } else { sprintf('TeX Live %s %swith tlmgr %s', year, if (tinytex) '(TinyTeX) ' else '', date) } } if (is.character(vers)) xfun::raw_string(vers) else vers } #' @param self Whether to update the TeX Live Manager itself. #' @param more_args A character vector of more arguments to be passed to the #' command \command{tlmgr update} or \command{tlmgr conf}. #' @param run_fmtutil Whether to run \command{fmtutil-sys --all} to (re)create #' format and hyphenation files after updating \pkg{tlmgr}. #' @param delete_tlpdb Whether to delete the \file{texlive.tlpdb.HASH} files #' (where \verb{HASH} is an MD5 hash) under the \file{tlpkg} directory of the #' root directory of TeX Live after updating. #' @rdname tlmgr #' @export tlmgr_update = function( all = TRUE, self = TRUE, more_args = character(), usermode = FALSE, run_fmtutil = TRUE, delete_tlpdb = getOption('tinytex.delete_tlpdb', FALSE), ... ) { # if unable to update due to a new release of TeX Live, skip the update if (isTRUE(.global$update_noted)) return(invisible(NULL)) res = suppressWarnings(tlmgr( c('update', if (all) '--all', if (self && !usermode) '--self', more_args), usermode, ..., stdout = TRUE, stderr = TRUE )) check_tl_version(res) if (run_fmtutil) fmtutil(usermode, stdout = FALSE) if (delete_tlpdb) delete_tlpdb_files() invisible() } # check if a new version of TeX Live has been released and give instructions on # how to upgrade check_tl_version = function(x) { i = grep('Local TeX Live \\([0-9]+) is older than remote repository \\([0-9]+)', x) if (length(i) > 0) auto_upgrade() .global$update_noted = TRUE } # provide a way options(tinytex.upgrade = TRUE) to automatically upgrade TinyTeX # (this is an ugly workaround for rstudio/revdepcheck-cloud#115) auto_upgrade = function() { up = is_tinytex() && getOption( 'tinytex.upgrade', all(Sys.getenv(c('DEV_PACKAGE_TARBALL', 'OUTPUT_S3_PATH')) != '') ) if (!up) return(message( 'A new version of TeX Live has been released. If you need to install or update ', 'any LaTeX packages, you have to upgrade ', if (!is_tinytex()) 'TeX Live.' else c( 'TinyTeX with tinytex::reinstall_tinytex(repository = "illinois").' ) )) root = tinytex_root() message('Trying to upgrade TinyTeX automatically now...') reinstall_tinytex(force = TRUE, dir = if (file.access(root, 2) == 0) root else default_inst()) } delete_tlpdb_files = function() { if ((root <- tinytex_root(FALSE)) != '') file.remove(list.files( file.path(root, 'tlpkg'), '^texlive[.]tlpdb.*[.][0-9a-f]{32}$', full.names = TRUE )) } #' @param action On Unix, add/remove symlinks of binaries to/from the system's #' \code{PATH}. On Windows, add/remove the path to the TeXLive binary #' directory to/from the system environment variable \code{PATH}. #' @rdname tlmgr #' @export tlmgr_path = function(action = c('add', 'remove')) tlmgr(c('path', match.arg(action)), .quiet = TRUE) #' @rdname tlmgr #' @export tlmgr_conf = function(more_args = character(), ...) { tlmgr(c('conf', more_args), ...) } #' @param url The URL of the CTAN mirror. If \code{NULL}, show the current #' repository, otherwise set the repository. See the \code{repository} #' argument of \code{\link{install_tinytex}()} for examples. #' @rdname tlmgr #' @export tlmgr_repo = function(url = NULL, ...) { tlmgr(c('option', 'repository', shQuote(normalize_repo(url))), ...) } #' Add/remove R's texmf tree to/from TeX Live #' #' R ships a custom texmf tree containing a few LaTeX style and class files, #' which are required when compiling R packages manuals (\file{Rd.sty}) or #' Sweave documents (\file{Sweave.sty}). This tree can be found under the #' directory \code{file.path(R.home('share'), 'texmf')}. This function can be #' used to add/remove R's texmf tree to/from TeX Live via #' \code{\link{tlmgr_conf}('auxtrees')}. #' @param action Add/remove R's texmf tree to/from TeX Live. #' @param ... Arguments passed to \code{\link{tlmgr}()}. #' @references See the \pkg{tlmgr} manual for detailed information about #' \command{tlmgr conf auxtrees}. Check out #' \url{https://tex.stackexchange.com/q/77720/9128} if you don't know what #' \code{texmf} means. #' @export #' @examples #' # running the code below will modify your texmf tree; please do not run #' # unless you know what it means #' #' # r_texmf('remove') #' # r_texmf('add') #' #' # all files under R's texmf tree #' list.files(file.path(R.home('share'), 'texmf'), recursive = TRUE, full.names = TRUE) r_texmf = function(action = c('add', 'remove'), ...) { tlmgr_conf(c('auxtrees', match.arg(action), shQuote(r_texmf_path())), ...) } r_texmf_path = function() { d = file.path(R.home('share'), 'texmf') if (dir_exists(d)) return(d) # retry another directory: https://github.com/rstudio/tinytex/issues/60 if ('Rd.sty' %in% basename(list.files(d2 <- '/usr/share/texmf', recursive = TRUE))) { return(d2) } warning("Cannot find R's texmf tree; returning '", d, "'") d } #' Sizes of LaTeX packages in TeX Live #' #' Use the command \command{tlmgr info --list} to obtain the sizes of LaTeX #' packages. #' @param show_total Whether to show the total size. #' @param pkgs A character vector of package names (by default, all packages). #' @param field A character vector of field names in the package information. #' See \url{https://www.tug.org/texlive/doc/tlmgr.html#info} for more info. #' @inheritParams tl_pkgs #' @export #' @return By default, a data frame of three columns: \code{package} is the #' package names, \code{size} is the sizes in bytes, and \code{size_h} is the #' human-readable version of sizes. If different field names are provided in #' the \code{field} argument, the returned data frame will contain these #' columns. tl_sizes = function(show_total = TRUE, pkgs = NULL, only_installed = TRUE, field = 'size') { info = tl_list(pkgs, paste(c('name', field), collapse = ','), only_installed, stdout = TRUE) info = read.table(sep = ',', text = info, stringsAsFactors = FALSE, col.names = c('package', field)) info = info[info$package %in% tl_names(info$package), , drop = FALSE] if ('size' %in% names(info)) { info = info[order(info[, 'size'], decreasing = TRUE), , drop = FALSE] info$size_h = xfun::format_bytes(info[, 'size']) if (show_total) message('The total size is ', xfun::format_bytes(sum(info$size))) } rownames(info) = NULL info } #' List the names of installed TeX Live packages #' #' Calls \command{tlmgr info --list --data name} to obtain the names of all #' (installed) TeX Live packages. Platform-specific strings in package names are #' removed, e.g., \code{"tex"} is returned for the package #' \pkg{tex.x86_64-darwin}. #' @param only_installed Whether to list installed packages only. #' @export #' @return A character vector of package names. tl_pkgs = function(only_installed = TRUE) { x = tl_list(NULL, 'name', only_installed, stdout = TRUE, .quiet = TRUE) tl_names(x, NULL) } tl_list = function(pkgs = NULL, field = 'name', only_installed = TRUE, ...) { tlmgr(c('info', '--list', if (only_installed) '--only-installed', '--data', shQuote(field), pkgs), ...) } tl_platform = function() tlmgr('print-platform', stdout = TRUE, .quiet = TRUE) # get all supported platforms (this needs Internet connection since the info is # fetched from CTAN) tl_platforms = function(print = FALSE) { x = tlmgr(c('platform', 'list'), stdout = TRUE, .quiet = TRUE) x = sub('^\\(i)', ' ', x) x = sort(trimws(grep('^ ', x, value = TRUE))) if (print) { cat(sprintf("'%s'", x), sep = ', ') invisible(x) } else x } # a copy of the returned result from tl_platform() is saved here because # tl_platform() is a little slow and requires Internet connection .tl_platforms = c( 'aarch64-linux', 'amd64-freebsd', 'amd64-netbsd', 'armhf-linux', 'i386-freebsd', 'i386-linux', 'i386-netbsd', 'i386-solaris', 'universal-darwin', 'windows', 'x86_64-cygwin', 'x86_64-darwinlegacy', 'x86_64-linux', 'x86_64-linuxmusl', 'x86_64-solaris' ) # remove the platform suffixes from texlive package names, and optionally keep # the suffixes for certain platforms tl_names = function(x, platform = tl_platform()) { unique(sub(paste0( '[.](', paste(setdiff(.tl_platforms, platform), collapse = '|'), ')$' ), '', x)) } # get the names of packages that are not relocatable tl_unrelocatable = function() { x = tl_list(NULL, 'name,relocatable', FALSE, stdout = TRUE, .quiet = TRUE) x = grep_sub(',0$', '', x) tl_names(x) } tinytex/MD50000644000176200001440000000262414616211632012274 0ustar liggesusersd1ea1300082ce1dd13988fdd04c02e00 *DESCRIPTION 214aad66205c39813fa32d36c37a64c9 *LICENSE 3fed972b9d8fbe8160ac430077bc4258 *NAMESPACE f17a892f25f12a8499002a83dc371d93 *R/install.R 0f7c616dfc3219b7b1130e41e15e1fde *R/latex.R 0a363e5d065005b52e4d93270ddaf416 *R/package.R d459ef7bbab626fd8d4ee0370e45c105 *R/tlmgr.R a94156ccfbc5d10fd9f90ea685954d1e *R/utils.R 6bb251e68e88b7b36342b62c0b67f030 *README.md 757e76a4de0b6b3cc0549f6c08e84fe8 *inst/CITATION 8319d1d7c068cb31fa38c74cdcd2b830 *inst/NEWS.Rd beb72b729727b5d7e94b9ba60ecaa66a *man/check_installed.Rd 51be9176879b070ef6d0e0a3e92d463d *man/copy_tinytex.Rd 9fa5ddd0d5a5cba87b9e979a8fa5d7ea *man/install_tinytex.Rd 2856e7abb8fa85e11e67b8ab98928604 *man/is_tinytex.Rd e8a825623e22c69e3291e1c48af34d49 *man/latexmk.Rd 6e322ddcc88698ded4976ae80cc3788d *man/parse_install.Rd dee1900604e4dac0d368df63650b11e9 *man/parse_packages.Rd 618fbe58b795fa525d7a59c0339f7b53 *man/r_texmf.Rd a5242c76dddeaeb81e6be6b82d0c3bf2 *man/tinytex-package.Rd 179f7f5074f6498051c9f9ff570b3e1c *man/tl_pkgs.Rd 58ee80fb118f9e8acb8050f8d3c1b499 *man/tl_sizes.Rd 11609ea6698f90a0f34232637e50d5ef *man/tlmgr.Rd adafda5ea0b62571a6766d4c95aaa065 *tests/test-cran.R 67e664e0b0f058f3d9288ffefeeb3017 *tests/test-cran/test-latex.R fe61c3b9a1a1e5a590c79e02644aa45e *tests/test-travis.R 3566092b9846c158fed92c0536cfa798 *tests/test-travis/test-install.R 598698a9ddda44f24fa635c94ff92c17 *tests/test-travis/test-tlmgr.R tinytex/inst/0000755000176200001440000000000014477353115012745 5ustar liggesuserstinytex/inst/CITATION0000644000176200001440000000124114366525535014104 0ustar liggesusersyear = sub('.*(2[[:digit:]]{3})-.*', '\\1', meta$Date, perl = TRUE) vers = paste('R package version', meta$Version) if (length(year) == 0) year = format(Sys.Date(), '%Y') bibentry( 'manual', title = paste('tinytex:', meta$Title), author = Filter(function(p) 'aut' %in% p$role, as.person(meta$Author)), year = year, note = vers, url = meta$URL ) bibentry( 'article', title = 'TinyTeX: A lightweight, cross-platform, and easy-to-maintain LaTeX distribution based on TeX Live', author = 'Yihui Xie', journal = 'TUGboat', year = '2019', volume = '40', number = '1', pages = '30--32', url = 'https://tug.org/TUGboat/Contents/contents40-1.html' ) tinytex/inst/NEWS.Rd0000644000176200001440000000044114240530641013774 0ustar liggesusers\name{NEWS} \title{News for Package 'tinytex'} \section{CHANGES IN tinytex VERSION 999.999}{ \itemize{ \item This NEWS file is only a placeholder. The version 999.999 does not really exist. Please read the NEWS on Github: \url{https://github.com/rstudio/tinytex/releases} } }