HenrikBengtsson / R.rsp
1
###########################################################################/**
2
# @RdocDefault compileLaTeX
3
#
4
# @title "Compiles a LaTeX file"
5
#
6
# \description{
7
#  @get "title" to either PDF or DVI.
8
# }
9
#
10
# @synopsis
11
#
12
# \arguments{
13
#   \item{filename, path}{The filename and (optional) path of the
14
#      LaTeX document to be compiled.
15
#      Only *.tex and *.ltx filename extensions are allowed.}
16
#   \item{format}{A @character string specifying the output format.}
17
#   \item{clean, quiet, texinputs}{Additional arguments passed to
18
#      @see "tools::texi2dvi".}
19
#   \item{...}{Not used.}
20
#   \item{outPath}{The output and working directory.}
21
#   \item{verbose}{See @see "R.utils::Verbose".}
22
# }
23
#
24
# \value{
25
#   Returns the pathname of the generated (PDF or DVI) document.
26
# }
27
#
28
# \section{Supported filename extensions}{
29
#   Internally @see "tools::texi2dvi" is used, which in turn uses
30
#   \code{Sys.which("texi2dvi")} if available.  Most known implementation
31
#   of the latter will only recognize LaTeX documents with filename
32
#   extensions *.tex and *.ltx (case sensitive).  (Any other filenames
33
#   will be compiled with 'texinfo', which is not a LaTeX compiler.)
34
# }
35
#
36
# @author
37
#
38
# \seealso{
39
#   Internally, @see "tools::texi2dvi" is used.
40
#   To compile Sweave LaTeX documents, @see "compileSweave".
41
# }
42
#
43
# @keyword file
44
# @keyword IO
45
# @keyword internal
46
#*/###########################################################################
47
setMethodS3("compileLaTeX", "default", function(filename, path=NULL, format=c("pdf", "dvi"), clean=FALSE, quiet=TRUE, texinputs=NULL, ..., outPath=".", verbose=FALSE) {
48
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
49
  # Validate arguments
50
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
51
  # Arguments 'filename' & 'path':
52 1
  pathname <- if (is.null(path)) filename else file.path(path, filename)
53 1
  if (!isUrl(pathname)) {
54 1
    pathname <- Arguments$getReadablePathname(pathname)
55
  }
56

57
  # Arguments 'outPath':
58 1
  outPath <- Arguments$getWritablePath(outPath)
59 1
  if (is.null(outPath)) outPath <- "."
60

61
  # Arguments 'texinputs':
62 1
  texinputs <- Arguments$getCharacters(texinputs)
63

64
  # Arguments 'format':
65 1
  format <- match.arg(format)
66

67
  # Argument 'verbose':
68 1
  verbose <- Arguments$getVerbose(verbose)
69 1
  if (verbose) {
70 1
    pushState(verbose)
71 1
    on.exit(popState(verbose))
72
  }
73

74

75 1
  verbose && enter(verbose, "Compiling LaTeX document")
76

77
  # Download URL?
78 1
  if (isUrl(pathname)) {
79 0
    verbose && enter(verbose, "Downloading URL")
80 0
    url <- pathname
81 0
    verbose && cat(verbose, "URL: ", url)
82 0
    pathname <- downloadFile(url, verbose=less(verbose,50))
83 0
    verbose && cat(verbose, "Local file: ", pathname)
84 0
    verbose && exit(verbose)
85
  }
86

87
  ## Assert supported filename extension
88 1
  ext <- file_ext(pathname)
89 1
  if (!ext %in% c("tex", "ltx")) {
90 0
    throw("Unknown LaTeX filename extension (should lower case *.tex or *.ltx): ", pathname)
91
  }
92

93
  # Shorten, e.g. ../foo/../foo/ to ../foo
94 1
  pathname <- normalizePath(pathname)
95 1
  pathname <- getAbsolutePath(pathname)
96 1
  verbose && cat(verbose, "LaTeX pathname (absolute): ", pathname)
97 1
  verbose && printf(verbose, "Input file size: %g bytes\n", file.info(pathname)$size)
98 1
  verbose && cat(verbose, "Output format: ", format)
99 1
  verbose && cat(verbose, "Output and working directory: ", getAbsolutePath(outPath))
100 1
  pattern <- "(.*)[.]([^.]+)$"
101 1
  replace <- sprintf("\\1.%s", format)
102 1
  filenameOut <- gsub(pattern, replace, basename(pathname))
103 1
  pathnameOut <- filePath(outPath, filenameOut)
104 1
  verbose && cat(verbose, "Output pathname (", toupper(format), "): ", getAbsolutePath(pathnameOut))
105

106 1
  opwd <- "."
107 1
  on.exit(setwd(opwd), add=TRUE)
108 1
  if (!is.null(outPath)) {
109 1
    opwd <- setwd(outPath)
110
  }
111

112 1
  verbose && enter(verbose, "Calling tools::texi2dvi()")
113 1
  pdf <- (format == "pdf")
114 1
  pathnameR <- getRelativePath(pathname)
115
  # Sanity check
116 1
  pathnameRx <- Arguments$getReadablePathname(pathname)
117

118
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
119
  # Append the directory of the TeX file to TEXINPUTS search path?
120
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
121 1
  pathR <- dirname(pathnameR)
122 1
  if (pathR != ".") {
123 1
    verbose && enter(verbose, "Appending directory of TeX file to 'texinputs'")
124 1
    if (!is.null(texinputs)) {
125 0
      texinputs <- unlist(strsplit(texinputs, split="[:;]", fixed=FALSE), use.names=FALSE)
126
    }
127 1
    verbose && cat(verbose, "'texinputs' before:")
128 1
    verbose && print(verbose, texinputs)
129
    # Shorten, e.g. ../foo/../foo/ to ../foo
130 1
    pathR <- normalizePath(pathR)
131

132
    # Append as relative or absolute path (using the shortest one)
133 1
    pathRR <- getRelativePath(pathR)
134 1
    pathRA <- getAbsolutePath(pathR)
135 1
    if (nchar(pathRA) < nchar(pathRR)) {
136 0
      texinputs <- c(pathRA, texinputs)
137
    } else {
138 1
      texinputs <- c(pathRR, texinputs)
139
    }
140
##      texinputs <- c(pathRR, texinputs)
141
##      texinputs <- c(pathRA, texinputs)
142

143
    # Append as temporary link, iff possible
144 1
    verbose && enter(verbose, "Appending temporary link, iff possible")
145 1
    link <- basename(tempdir())
146 1
    verbose && cat(verbose, "Link: ", link)
147 1
    if (!file.exists(link)) {
148 1
      verbose && cat(verbose, "Trying to create link to target: ", pathR)
149 1
      tryCatch({
150 1
        linkT <- createLink(target=pathR, link=link)
151 1
        verbose && cat(verbose, "Created link: ", linkT)
152 1
      }, error = function(ex) {
153 0
        verbose && print(verbose, ex)
154
      })
155 1
      if (file.exists(link)) {
156 1
        linkA <- getAbsolutePath(link)
157 1
        on.exit(removeDirectory(linkA, mustExist=FALSE), add=TRUE)
158 1
        verbose && cat(verbose, "Link created: ", link)
159 1
        texinputs <- c(link, texinputs)
160
      }
161
    }
162 1
    verbose && exit(verbose)
163

164
    # Keep unique
165 1
    texinputs <- unique(texinputs)
166

167 1
    verbose && exit(verbose)
168
  }
169

170 1
  verbose && cat(verbose, "texinputs:")
171 1
  verbose && print(verbose, texinputs)
172

173
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
174
  # Temporarily cleanup TEXINPUTS, BIBINPUTS, BSTINPUTS, TEXINDY
175
  # by removing empty, duplicated and non-existing paths.  This
176
  # lowers the risk for compilation failure due to too long paths.
177
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
178 1
  verbose && enter(verbose, "Cleaning up LaTeX environment variable")
179 1
  cleanupPath <- function(path, sep=.Platform$path.sep) {
180 1
    appendSep <- (regexpr(sprintf("%s$", sep), path) != -1L)
181

182 1
    path <- unlist(strsplit(path, split=sep, fixed=TRUE))
183

184
    # Drop duplicates
185 1
    path <- unique(path)
186
    # Drop empty paths
187 1
    path <- path[nchar(path) > 0L]
188
    # Drop non-existing paths (accounting for foo// specified paths)
189 1
    pathX <- gsub("[/\\]*$", "", path)
190 1
    isDir <- sapply(pathX, FUN=file.exists)
191 1
    path <- path[isDir]
192
    # Re-append separator to the end?
193 1
    if (appendSep) path <- c(path, "")
194

195 1
    paste(path, collapse=sep)
196 1
  } # cleanupPath()
197

198 1
  vars <- c("TEXINPUTS", "BIBINPUTS", "BSTINPUTS", "TEXINDY")
199 1
  verbose && cat(verbose, "Original:")
200 1
  verbose && printf(verbose, " %s: %s\n", vars, Sys.getenv(vars))
201 1
  envs <- Sys.getenv(vars, NA)
202 1
  envs <- envs[!is.na(envs)]
203

204
  # Cleanup paths
205 1
  if (length(envs) > 0L) {
206
    # Undo any changes to system environments
207 1
    on.exit(do.call(Sys.setenv, as.list(envs)), add=TRUE)
208 1
    envs2 <- sapply(envs, FUN=cleanupPath)
209 1
    if (length(envs2) > 0L) {
210 1
      do.call(Sys.setenv, as.list(envs2))
211 1
      vars <- names(envs2)
212 1
      verbose && cat(verbose, "Cleaned up:")
213 1
      verbose && printf(verbose, " %s: %s\n", vars, Sys.getenv(vars))
214
    }
215
  }
216

217 1
  verbose && exit(verbose)
218

219

220
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
221
  # Fake output PDF/DVI in case LaTeX is not available?
222
  # This makes it possible to test the compilation of vignettes
223
  # in 'R CMD check' up to the point of compiling a LaTeX file
224
  # into a PDF.  This is useful on Travis CI where we then can
225
  # avoid having to install a huge LaTeX system for each check.
226
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
227 1
  fallback <- Sys.getenv("R_RSP_COMPILELATEX_FALLBACK")
228 1
  if (nzchar(fallback)) {
229 0
    verbose && enter(verbose, "Fallback")
230
    # Disable fallback until done to avoid recursive calls when
231
    # calling isCapableOf(..., "latex").
232 0
    Sys.unsetenv("R_RSP_COMPILELATEX_FALLBACK")
233 0
    fallback0 <- fallback
234 0
    on.exit(Sys.setenv("R_RSP_COMPILELATEX_FALLBACK"=fallback0), add=TRUE)
235

236 0
    verbose && cat(verbose, "R_RSP_COMPILELATEX_FALLBACK=", fallback)
237 0
    forceFB <- (regexpr("-force", fallback) != -1L)
238 0
    if (forceFB) {
239 0
      fallback <- gsub("-force", "", fallback)
240 0
      verbose && cat(verbose, "Forced fallback")
241 0
      verbose && cat(verbose, "R_RSP_COMPILELATEX_FALLBACK=", fallback)
242
    }
243

244 0
    if (forceFB || !isCapableOf(R.rsp, "latex")) {
245 0
      if (fallback == "copy") {
246 0
        texi2dvi <- function(pathnameR, pdf=TRUE, ...) {
247 0
          verbose && enter(verbose, "Faking texi2dvi() by copying source file.")
248 0
          pathnameD <- file_path_sans_ext(basename(pathnameR))
249 0
          pathnameD <- paste(pathnameD, if (pdf) "pdf" else "dvi", sep=".")
250 0
          copyFile(pathnameR, pathnameD, overwrite=TRUE)
251 0
          verbose && printf(verbose, "Copied: %s -> %s\n", pathnameR, pathnameD)
252 0
          verbose && exit(verbose)
253 0
        } # texi2dvi()
254 0
        verbose && cat(verbose, "Faking texi2dvi() by copying source file.")
255
      } else {
256 0
        throw("Unknown value on _RSP_COMPILELATEX_FALLBACK_: ", fallback)
257
      }
258
    }
259 0
    verbose && exit(verbose)
260
  }
261

262

263
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
264
  # Compile
265
  # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
266 1
  texi2dvi(pathnameR, pdf=pdf, clean=clean, quiet=quiet, texinputs=texinputs)
267

268

269 1
  verbose && exit(verbose)
270

271 1
  setwd(opwd); opwd <- "."
272 1
  verbose && printf(verbose, "Output file size: %g bytes\n", file.info(pathnameOut)$size)
273

274 1
  verbose && exit(verbose)
275

276 1
  pathnameOut
277
}) # compileLaTeX()

Read our documentation on viewing source code .

Loading