├── .Rbuildignore ├── .gitattributes ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── NAMESPACE ├── NEWS.md ├── R ├── cacheMetrics.R ├── deviceUtils.R ├── latexStrWidth.R ├── sanitizeTexString.R ├── tikz.R ├── tikzAnnotate.R ├── tikzDevice-package.R ├── tikzInternal.R └── zzz.R ├── README.md ├── TODO ├── _pkgdown.yml ├── appveyor.yml ├── codecov.yml ├── cran-comments.md ├── docs ├── articles │ └── tikzDevice.pdf ├── authors.html ├── docsearch.css ├── docsearch.js ├── index.html ├── link.svg ├── news │ └── index.html ├── pkgdown.css ├── pkgdown.js ├── pkgdown.yml └── reference │ ├── anyMultibyteUTF8Characters.html │ ├── getLatexStrWidth.html │ ├── gridToDevice.html │ ├── index.html │ ├── sanitizeTexString.html │ ├── setTikzDefaults.html │ ├── tikz.html │ ├── tikzAnnotate.html │ ├── tikzCompilerInfo.html │ ├── tikzDevice-package.html │ └── tikzTest.html ├── inst └── staticdocs │ ├── README.md │ └── index.R ├── man ├── anyMultibyteUTF8Characters.Rd ├── getLatexStrWidth.Rd ├── gridToDevice.Rd ├── sanitizeTexString.Rd ├── setTikzDefaults.Rd ├── tikz.Rd ├── tikzAnnotate.Rd ├── tikzCompilerInfo.Rd ├── tikzDevice-package.Rd └── tikzTest.Rd ├── revdep ├── .gitignore ├── README.md ├── failures.md └── problems.md ├── src ├── init.c ├── tikzDevice.c └── tikzDevice.h ├── tests ├── testthat.R └── testthat │ ├── .tikzMetrics │ └── .gitignore │ ├── helper_bootstrap.R │ ├── helper_graphics.R │ ├── standard_graphs │ ├── annotation_noflush.pdf │ ├── base_annotation.pdf │ ├── base_raster.pdf │ ├── base_raster_noresample.pdf │ ├── base_symbolic_simple.pdf │ ├── character_expansion.pdf │ ├── contour_lines.pdf │ ├── draw_circles.pdf │ ├── draw_filled_circles.pdf │ ├── filled_rectangle.pdf │ ├── ggplot2_superscripts.pdf │ ├── ggplot2_test.pdf │ ├── ggplot_old │ │ ├── ggplot2_superscripts.pdf │ │ └── ggplot2_test.pdf │ ├── graph_box.pdf │ ├── grid_annotation.pdf │ ├── grid_raster.pdf │ ├── hello_TeX.pdf │ ├── line_color.pdf │ ├── line_color_width.pdf │ ├── line_types.pdf │ ├── line_weights.pdf │ ├── lots_of_elements.pdf │ ├── luatex_utf8_characters.pdf │ ├── pch_caracters.pdf │ ├── persp_3D.pdf │ ├── plot_legend.pdf │ ├── polypath.pdf │ ├── raster_reflection.pdf │ ├── string_placement.pdf │ ├── text_alignment.pdf │ ├── text_color.pdf │ ├── transparency.pdf │ ├── utf8_characters.pdf │ └── xetex_variants.pdf │ ├── test_error_handling.R │ ├── test_graphics.R │ ├── test_metrics_dict.R │ ├── test_misc.R │ └── test_pointsize.R ├── tikzDevice.Rproj └── vignettes ├── .gitignore ├── agufull04.bst ├── consoleExample.tex ├── img ├── Rlogo.png ├── plotmathDefault.pdf ├── plotmathTikz.pdf └── xelatexEx.pdf ├── refs.bib ├── sweavetolst.sty ├── tikzCodeBlock.sty ├── tikzDevice.Rnw └── tikzDeviceVignette.sty /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^CRAN-RELEASE$ 2 | \.git.*$ 3 | NEWS.pdf 4 | README.md 5 | TODO 6 | updateVersion.sh 7 | md2news.hs 8 | Makefile 9 | tests/test_work 10 | tests/test_output 11 | tests/testthat/test_work 12 | tests/testthat/test_output 13 | ^.*\.Rproj$ 14 | ^\.Rproj\.user$ 15 | GNUmakefile 16 | ^\.travis\.yml$ 17 | ^cran-comments\.md$ 18 | ^appveyor\.yml$ 19 | ^revdep$ 20 | inst/staticdocs 21 | ^tests/testthat/\.tikzMetrics$ 22 | ^vignettes/\.tikzMetrics$ 23 | vignettes/.*\.synctex\.gz$ 24 | .idea$ 25 | CMakeLists\.txt$ 26 | clion-test\.R$ 27 | cmake-build-debug$ 28 | ^codecov\.yml$ 29 | ^_pkgdown\.yml$ 30 | ^docs$ 31 | ^debug$ 32 | ^\.github$ 33 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /NEWS.md merge=union 2 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | # 4 | # NOTE: This workflow is overkill for most R packages and 5 | # check-standard.yaml is likely a better choice. 6 | # usethis::use_github_action("check-standard") will install it. 7 | on: 8 | push: 9 | branches: [main, master] 10 | pull_request: 11 | branches: [main, master] 12 | workflow_dispatch: 13 | 14 | name: R-CMD-check 15 | 16 | jobs: 17 | R-CMD-check: 18 | runs-on: ${{ matrix.config.os }} 19 | 20 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | config: 26 | - {os: macos-latest, r: 'release'} 27 | 28 | - {os: windows-latest, r: 'release'} 29 | 30 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 31 | - {os: ubuntu-latest, r: 'release'} 32 | - {os: ubuntu-latest, r: 'oldrel'} 33 | 34 | env: 35 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 36 | R_KEEP_PKG_SOURCE: yes 37 | 38 | steps: 39 | - uses: actions/checkout@v3 40 | 41 | - uses: r-lib/actions/setup-pandoc@v2 42 | 43 | - uses: r-lib/actions/setup-tinytex@v2 44 | - run: tlmgr --version 45 | 46 | - name: Install additional LaTeX packages 47 | run: | 48 | tlmgr install pgf preview grfext memoir microtype pdflscape babel-english listings xstring upquote 49 | tlmgr list --only-installed 50 | 51 | - uses: r-lib/actions/setup-r@v2 52 | with: 53 | r-version: ${{ matrix.config.r }} 54 | http-user-agent: ${{ matrix.config.http-user-agent }} 55 | use-public-rspm: true 56 | extra-repositories: https://r-forge.r-universe.dev 57 | 58 | - uses: r-lib/actions/setup-r-dependencies@v2 59 | with: 60 | extra-packages: any::rcmdcheck 61 | needs: check 62 | 63 | - uses: r-lib/actions/check-r-package@v2 64 | with: 65 | args: 'c("--no-manual", "--as-cran")' 66 | build_args: 'c("--no-manual", "--no-build-vignettes")' 67 | error-on: '"error"' 68 | check-dir: '"check"' 69 | upload-snapshots: true 70 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | - master 6 | pull_request: 7 | branches: 8 | - main 9 | - master 10 | 11 | name: test-coverage 12 | 13 | jobs: 14 | test-coverage: 15 | runs-on: ubuntu-20.04 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | - uses: r-lib/actions/setup-r@v1 22 | 23 | - uses: r-lib/actions/setup-pandoc@v1 24 | 25 | - uses: r-lib/actions/setup-tinytex@v1 26 | - run: | 27 | tlmgr --version 28 | tlmgr install pgf preview grfext 29 | 30 | - name: Install ImageMagick 31 | if: runner.os == 'Linux' 32 | run: sudo apt-get install -y imagemagick 33 | 34 | - name: Query dependencies 35 | run: | 36 | install.packages('remotes') 37 | saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) 38 | writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") 39 | shell: Rscript {0} 40 | 41 | - name: Restore R package cache 42 | uses: actions/cache@v2 43 | with: 44 | path: ${{ env.R_LIBS_USER }} 45 | key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} 46 | restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- 47 | 48 | - name: Install system dependencies 49 | run: | 50 | while read -r cmd 51 | do 52 | eval sudo $cmd 53 | done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') 54 | 55 | - name: Install dependencies 56 | run: | 57 | install.packages(c("remotes")) 58 | remotes::install_deps(dependencies = TRUE) 59 | remotes::install_cran("covr") 60 | shell: Rscript {0} 61 | 62 | - name: Test coverage 63 | run: covr::codecov() 64 | shell: Rscript {0} 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.Rproj.user 2 | /.Rhistory 3 | /inst/doc/* 4 | /src/*.o 5 | /src/*.so 6 | /src/*.dll 7 | /tests/test_output 8 | /tests/test_work 9 | /tests/testthat/test_output 10 | /tests/testthat/test_work 11 | /tests/testthat/Rplots* 12 | /test_output 13 | /test_work 14 | .Rproj.user 15 | /inst/web 16 | /vignettes/*.synctex.gz 17 | /.idea/ 18 | /CMakeLists.txt 19 | /clion-test.R 20 | /cmake-build-debug/ 21 | debug 22 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Encoding: UTF-8 2 | Package: tikzDevice 3 | Type: Package 4 | Title: R Graphics Output in LaTeX Format 5 | Version: 0.12.6 6 | Authors@R: c( person("Charlie", "Sharpsteen", role = "aut"), 7 | person("Cameron", "Bracken", role = "aut"), person("Kirill", 8 | "Müller", role = c("ctb"), email = 9 | "krlmlr+r@mailbox.org"), person("Yihui", "Xie", role = "ctb"), 10 | person("Ralf", "Stubner", email = "ralf.stubner@gmail.com", 11 | role = "cre"), person("Nico", "Bellack", 12 | email = "nico.belack@daqana.com", role = "ctb") ) 13 | URL: https://daqana.github.io/tikzDevice/, https://github.com/daqana/tikzDevice 14 | BugReports: https://github.com/daqana/tikzDevice/issues 15 | Description: Provides a graphics output device for R that records plots 16 | in a LaTeX-friendly format. The device transforms plotting 17 | commands issued by R functions into LaTeX code blocks. When 18 | included in a LaTeX document, these blocks are interpreted with 19 | the help of 'TikZ'---a graphics package for TeX and friends 20 | written by Till Tantau. Using the 'tikzDevice', the text of R 21 | plots can contain LaTeX commands such as mathematical formula. 22 | The device also allows arbitrary LaTeX code to be inserted into 23 | the output stream. 24 | License: GPL (>= 2) 25 | Depends: R (>= 2.14.0) 26 | Imports: 27 | filehash (>= 2.3), 28 | png 29 | Suggests: 30 | evaluate, 31 | formatR, 32 | ggplot2, 33 | knitr, 34 | lattice, 35 | maps, 36 | scales, 37 | stringr, 38 | testthat (>= 0.8.1), 39 | withr, 40 | covr 41 | SystemRequirements: pgf (>= 2.00) 42 | LazyLoad: yes 43 | VignetteBuilder: knitr 44 | RoxygenNote: 7.2.3 45 | Roxygen: list(markdown = TRUE) 46 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(drawDetails,tikz_annotation) 4 | S3method(drawDetails,tikz_coord) 5 | S3method(drawDetails,tikz_node) 6 | S3method(format,ENV_VAR) 7 | S3method(format,OPTION) 8 | S3method(format,PATH) 9 | export(anyMultibyteUTF8Characters) 10 | export(getLatexCharMetrics) 11 | export(getLatexStrWidth) 12 | export(grid.tikzAnnotate) 13 | export(grid.tikzCoord) 14 | export(grid.tikzNode) 15 | export(gridToDevice) 16 | export(sanitizeTexString) 17 | export(setTikzDefaults) 18 | export(tikz) 19 | export(tikzAnnotate) 20 | export(tikzAnnotateGrob) 21 | export(tikzCompilerInfo) 22 | export(tikzCoord) 23 | export(tikzCoordGrob) 24 | export(tikzNode) 25 | export(tikzNodeGrob) 26 | export(tikzTest) 27 | importFrom(filehash,dbCreate) 28 | importFrom(filehash,dbExists) 29 | importFrom(filehash,dbFetch) 30 | importFrom(filehash,dbInit) 31 | importFrom(filehash,dbInsert) 32 | importFrom(grDevices,as.raster) 33 | importFrom(grDevices,dev.cur) 34 | importFrom(grDevices,dev.list) 35 | importFrom(grDevices,dev.off) 36 | importFrom(grDevices,png) 37 | importFrom(graphics,grconvertX) 38 | importFrom(graphics,grconvertY) 39 | importFrom(graphics,par) 40 | importFrom(graphics,plot.new) 41 | importFrom(graphics,rasterImage) 42 | importFrom(grid,convertX) 43 | importFrom(grid,convertY) 44 | importFrom(grid,current.transform) 45 | importFrom(grid,drawDetails) 46 | importFrom(grid,grid.draw) 47 | importFrom(grid,grob) 48 | importFrom(grid,unit) 49 | importFrom(stats,complete.cases) 50 | importFrom(utils,packageVersion) 51 | useDynLib(tikzDevice, .registration = TRUE) 52 | -------------------------------------------------------------------------------- /R/cacheMetrics.R: -------------------------------------------------------------------------------- 1 | # Filehash contains a function for generating a SHA1 hash from an R object, but 2 | # doesn't export it. The digest package also contains the exact same code made 3 | # publicly available but it seems redundant to add it to the dependency list. 4 | # This function simplifies access to filehash's unexported SHA1 function. 5 | sha1 <- function(object) { 6 | # The get() for every call has very low impact on performance, but simplifies 7 | # matters here 8 | internal_sha1 <- get("sha1", asNamespace("filehash")) 9 | internal_sha1(object) 10 | } 11 | 12 | # Since calling LaTeX to obtain string metrics is inefficient and expensive, 13 | # this function will search a string metrics dictionary to see if we have 14 | # already calculated metrics for this particular object. If so we return the 15 | # cached value. 16 | # 17 | #' @importFrom filehash dbExists dbFetch 18 | queryMetricsDictionary <- function(key, verbose) { 19 | # Ensure the dictionary is available. 20 | checkDictionaryStatus(verbose = verbose) 21 | 22 | # Check for the string. 23 | haveMetrics <- evalWithoutInterrupts(dbExists(.tikzInternal[["dictionary"]], sha1(key))) 24 | if (haveMetrics) { 25 | 26 | # Yay! The width exists! Recover and return it. 27 | metrics <- evalWithoutInterrupts(dbFetch(.tikzInternal[["dictionary"]], sha1(key))) 28 | } else { 29 | 30 | # No dice. Return -1 to indicate that metrics for this string 31 | # are not present in the dictionary. 32 | return(-1) 33 | } 34 | } 35 | 36 | 37 | # This function enters values into the metrics dictionary. The metrics are 38 | # stored under a key which is a SHA1 hash created from the object they are 39 | # associated with. 40 | # 41 | #' @importFrom filehash dbInsert 42 | storeMetricsInDictionary <- function(key, metrics) { 43 | evalWithoutInterrupts(dbInsert(.tikzInternal[["dictionary"]], sha1(key), metrics)) 44 | 45 | # Return nothing. 46 | invisible() 47 | } 48 | 49 | 50 | # This function checks to see if our dictionary has been created as a variable 51 | # in our private .tikzInternal environment. If not, it either opens a user 52 | # specified dictionary or creates a new one in tempdir(). 53 | # 54 | #' @importFrom filehash dbCreate dbInit 55 | checkDictionaryStatus <- function(verbose) { 56 | if (!verbose) { 57 | message <- function(...) invisible() 58 | } 59 | 60 | dict_path <- getOption("tikzMetricsDictionary") 61 | old_dict_path <- .tikzInternal[["dict_path"]] 62 | old_dictionary <- .tikzInternal[["dictionary"]] 63 | old_db_file <- .tikzInternal[["db_file"]] 64 | need_create <- FALSE 65 | 66 | # Check for a user specified dictionary. 67 | if (!is.null(dict_path)) { 68 | db_file <- path.expand(dict_path) 69 | 70 | # Create the database file if it does not exist. 71 | if (!file.exists(db_file)) { 72 | message("Creating new TikZ metrics dictionary at:\n\t", db_file) 73 | need_create <- TRUE 74 | } 75 | } else { 76 | # Create a temporary dictionary- it will disappear after 77 | # the R session finishes. 78 | db_file <- old_db_file 79 | if (is.null(db_file) || !file.exists(db_file) || !is.null(old_dict_path)) { 80 | db_file <- file.path(tempdir(), "tikzMetricsDictionary") 81 | message("Creating temporary TikZ metrics dictionary at:\n\t", db_file) 82 | need_create <- TRUE 83 | } 84 | } 85 | 86 | # Create the database file if it does not exist. 87 | if (need_create) { 88 | unlink(db_file, recursive = TRUE) 89 | dbCreate(db_file, type = "DB1") 90 | 91 | # Need to initialize new database 92 | old_dictionary <- NULL 93 | } 94 | 95 | if (!is.null(old_dictionary) && identical(dict_path, old_dict_path)) { 96 | return(invisible()) 97 | } 98 | 99 | # Add the dictionary as an object in the .tikzOptions 100 | # environment. 101 | message("Using TikZ metrics dictionary at:\n\t", db_file) 102 | .tikzInternal[["dictionary"]] <- dbInit(db_file) 103 | .tikzInternal[["dict_path"]] <- dict_path 104 | .tikzInternal[["db_file"]] <- db_file 105 | 106 | # Return nothing. 107 | invisible() 108 | } 109 | -------------------------------------------------------------------------------- /R/sanitizeTexString.R: -------------------------------------------------------------------------------- 1 | #' Replace LaTeX Special Characters in a String 2 | #' 3 | #' This function is used by tikzDevice when `sanitize = TRUE` to replace 4 | #' special LaTeX characters (such as the comment character %) in plotting text 5 | #' where the user does not have direct control over the generated text. 6 | #' 7 | #' `sanitizeTexString()` searches character by character through a string 8 | #' replacing each occurrence of a special character contained in 9 | #' `strip[i]` with the corresponding replacement value in 10 | #' `replacement[i]`. tikzDevice calls back this function for every piece 11 | #' of text when the sanitize option is TRUE. See [tikz()] for more 12 | #' information on the default special characters and replacement values. 13 | #' 14 | #' By default, `tikzSanitizeCharacters` replaces the following characters: 15 | #' 16 | #' \itemize{ 17 | #' \item \verb{\%} 18 | #' \item \verb{$} 19 | #' \item \verb{\}} 20 | #' \item \verb{\{} 21 | #' \item \verb{^} 22 | #' \item \verb{_} 23 | #' \item \verb{#} 24 | #' \item \verb{&} 25 | #' \item \verb{~} 26 | #' } 27 | #' 28 | #' With the contents of `tikzReplacementCharacters`: 29 | #' 30 | #' \itemize{ 31 | #' \item \verb{\\\%} 32 | #' \item \verb{\\$} 33 | #' \item \verb{\\\}} 34 | #' \item \verb{\\\{} 35 | #' \item \verb{\\^{}} 36 | #' \item \verb{\\_{}} 37 | #' \item \verb{\\#} 38 | #' \item \verb{\\&} 39 | #' \item \verb{\char`\~} 40 | #' } 41 | #' 42 | #' These defaults may be adjusted using the [options()] function. 43 | #' 44 | #' @param string A character vector of length 1 (a string). 45 | #' @param strip A character vector of single characters to search for. 46 | #' @param replacement A character vector of replacement values. 47 | #' 48 | #' @return \item{sanitizedString}{A character vector of length 1 with all 49 | #' special characters replaced.} 50 | #' 51 | #' @author Cameron Bracken \email{cameron.bracken@@gmail.com} 52 | #' 53 | #' @seealso [tikz()] 54 | #' @keywords character 55 | #' 56 | #' @examples 57 | #' 58 | #' # Be careful with sanitizing, it may lead to unexpected behavior. 59 | #' # For example, we may want -1 to be a superscript it gets 60 | #' # sanitized away with the other default special characters. 61 | #' # The string appears in LaTeX exactly as shown. 62 | #' \dontrun{ 63 | #' sanitizeTexString('10\% of 10$ is 10^\{-1\}$') 64 | #' } 65 | #' 66 | #' @export 67 | sanitizeTexString <- function(string, 68 | strip = getOption("tikzSanitizeCharacters"), 69 | replacement = getOption("tikzReplacementCharacters")) { 70 | 71 | # separate the string into a vector of charaters 72 | explode <- strsplit(string, "")[[1]] 73 | if (any(is.na(explode))) stop("Unable to sanitize string, you may be trying to pass in an unsupported symbol") 74 | 75 | # Replace each matching character with its replacement characters 76 | for (i in 1:length(explode)) { 77 | matches <- (explode[i] == strip) 78 | if (any(matches)) { 79 | explode[i] <- paste("{", replacement[which(matches)], "}", sep = "") 80 | } 81 | } 82 | # stick the string back together 83 | return(paste(explode, collapse = "")) 84 | } 85 | -------------------------------------------------------------------------------- /R/tikzDevice-package.R: -------------------------------------------------------------------------------- 1 | #' Support for native LaTeX output of R graphics 2 | #' 3 | #' The tikzDevice package implements the [tikz()] ouput device which 4 | #' generates R graphics in a LaTeX friendly format. LaTeX handles the 5 | #' typesetting of all text in graphics generated by `tikz`. This allows for 6 | #' seamless integration between these graphics and documents that are also being 7 | #' typeset by LaTeX. Using LaTeX to generate graph text also means that 8 | #' \strong{LaTeX mathematics can be typeset directly into labels and 9 | #' annotations}. 10 | #' 11 | #' @name tikzDevice-package 12 | #' @aliases tikzDevice-package tikzDevice 13 | #' @docType package 14 | #' @section Options That Affect Package Behavior: The \pkg{tikzDevice} package 15 | #' is currently influenced by a number of global options that may be set in 16 | #' scripts, from the console or in a `.Rprofile` file. All of the options 17 | #' can be set by using `options(