diff --git a/DESCRIPTION b/DESCRIPTION index 97418f2..7b125ac 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: latex2exp Type: Package Title: Use LaTeX Expressions in Plots -Version: 0.9.7 -Date: 2022-12-28 +Version: 0.9.8 +Date: 2024-07-05 Authors@R: person("Stefano", "Meschiari", email="stefano.meschiari@gmail.com", role=c("aut", "cre")) Description: Parses and converts LaTeX math formulas to R's plotmath expressions, used to enter mathematical formulas and symbols to be rendered as @@ -10,11 +10,9 @@ Description: Parses and converts LaTeX math formulas to R's plotmath License: MIT + file LICENSE URL: https://www.stefanom.io/latex2exp/, https://github.com/stefano-meschiari/latex2exp BugReports: https://github.com/stefano-meschiari/latex2exp/issues -Imports: - stringr, - magrittr Encoding: UTF-8 Suggests: + tools, testthat, waldo, knitr, @@ -28,5 +26,5 @@ Suggests: rlang, dplyr VignetteBuilder: knitr -RoxygenNote: 7.2.2 +RoxygenNote: 7.2.3 Language: en-US diff --git a/NAMESPACE b/NAMESPACE index 33eb66e..7680047 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -12,17 +12,4 @@ importFrom(graphics,par) importFrom(graphics,plot.new) importFrom(graphics,plot.window) importFrom(graphics,text) -importFrom(magrittr,"%>%") -importFrom(stringr,fixed) -importFrom(stringr,str_c) -importFrom(stringr,str_detect) -importFrom(stringr,str_length) -importFrom(stringr,str_match) -importFrom(stringr,str_match_all) -importFrom(stringr,str_replace) -importFrom(stringr,str_replace_all) -importFrom(stringr,str_split) -importFrom(stringr,str_starts) -importFrom(stringr,str_sub) -importFrom(stringr,str_trim) importFrom(utils,vignette) diff --git a/NEWS.md b/NEWS.md index 44159d2..fcc2850 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# 0.9.8 (07/05/2024) + +* The code is reworked to eliminate dependencies to stringr and magrittr. + # 0.9.7 (12/28/2022) ## Bug fixes * In math mode, numbers that started with 0 would be displayed without it (e.g. diff --git a/R/demos.R b/R/demos.R index 98098c9..ff0bba2 100644 --- a/R/demos.R +++ b/R/demos.R @@ -5,7 +5,7 @@ #' #' @param cex Multiplier for font size #' @export -latex2exp_examples <- function(cex=1) { +latex2exp_examples <- function(cex = 1) { oldpar <- par(no.readonly = TRUE) on.exit(suppressWarnings(par(oldpar))) @@ -31,9 +31,8 @@ latex2exp_examples <- function(cex=1) { x <- 0 y <- seq(0.95, 0.05, length.out = length(examples)) - text( - 0.5, y, str_c("TeX(r\"(", examples, ")\")"), pos = 2, cex = 0.5 * cex, family = 'mono' - ) - text(0.5, y, TeX(examples), pos = 4, cex=cex) + text(0.5, y, paste0("TeX(r\"(", examples, ")\")"), pos = 2, cex = 0.5 * cex, + family = 'mono') + text(0.5, y, TeX(examples), pos = 4, cex = cex) return(invisible(TRUE)) -} \ No newline at end of file +} diff --git a/R/latex2exp.R b/R/latex2exp.R index 90f2615..1db0b49 100644 --- a/R/latex2exp.R +++ b/R/latex2exp.R @@ -1,31 +1,20 @@ -#' @importFrom magrittr %>% -#' @importFrom stringr str_c -#' @importFrom stringr str_detect -#' @importFrom stringr str_replace_all -#' @importFrom stringr str_replace -#' @importFrom stringr str_split -#' @importFrom stringr str_sub -#' @importFrom stringr str_match -#' @importFrom stringr str_trim -#' @importFrom stringr str_starts -#' @importFrom stringr str_match -#' @importFrom stringr str_match_all -#' @importFrom stringr fixed -#' @importFrom stringr str_length NULL #' Deprecated; use \code{\link{TeX}} instead. #' -#' @param string A character vector containing LaTeX expressions. Note that any backslashes must be escaped (e.g. "$\\alpha"). -#' @param output The returned object, one of "expression" (default, returns a plotmath expression ready for plotting), "character" (returns the expression as a string), and "ast" (returns the tree used to generate the expression). +#' @param string A character vector containing LaTeX expressions. Note that any +#' backslashes must be escaped (e.g. "$\\alpha"). +#' @param output The returned object, one of "expression" (default, returns a +#' plotmath expression ready for plotting), "character" (returns the +#' expression as a string), and "ast" (returns the tree used to generate the +#' expression). #' #' @return Returns an expression (see the \code{output} parameter). #' @export -latex2exp <- - function(string, output = c('expression', 'character', 'ast')) { - .Deprecated('TeX', 'latex2exp') - return(TeX(string, output=output)) - } +latex2exp <- function(string, output = c('expression', 'character', 'ast')) { + .Deprecated('TeX', 'latex2exp') + return(TeX(string, output = output)) +} #' Converts LaTeX to a \code{\link{plotmath}} expression. #' @@ -44,117 +33,124 @@ latex2exp <- #' "character" (returns the expression as a string), #' and "ast" (returns the tree used to generate the expression). #' -#' @return Returns a plotmath expression by default. The \code{output} parameter can -#' modify the type of the returned value. +#' @return Returns a plotmath expression by default. The \code{output} parameter +#' can modify the type of the returned value. #' -#' If more than one string is specified in the \code{input} parameter, returns a list -#' of expressions. +#' If more than one string is specified in the \code{input} parameter, returns a +#' list of expressions. #' #' @section Adding new commands: -#' New LaTeX commands can be defined by supplying the \code{user_defined} parameter. -#' The \code{user_defined} parameter is a list that contains LaTeX commands -#' as names, and template strings as values. A LaTeX command that matches -#' one of the names is translated into the corresponding string and included in -#' the final plotmath expression. The file \code{symbols.R} in the source code -#' of this package contains one such table that can be used as a reference. +#' New LaTeX commands can be defined by supplying the \code{user_defined} +#' parameter. The \code{user_defined} parameter is a list that contains LaTeX +#' commands as names, and template strings as values. A LaTeX command that +#' matches one of the names is translated into the corresponding string and +#' included in the final plotmath expression. The file \code{symbols.R} in the +#' source code of this package contains one such table that can be used as a +#' reference. #' #' The template string can include one of the following special template #' parameters: #' #' \itemize{ -#' \item \code{$arg1, $arg2, ...} represent the first, second, ... brace argument. -#' E.g. for \code{\\frac{x}{y}}, \code{$arg1} is \code{x} and \code{$arg2} is \code{y}. +#' \item \code{$arg1, $arg2, ...} represent the first, second, ... brace +#' argument. E.g. for \code{\\frac{x}{y}}, \code{$arg1} is \code{x} and +#' \code{$arg2} is \code{y}. #' \item \code{$opt} is an optional argument in square brackets. E.g. for #' \code{\\sqrt[2]{x}}, \code{$opt} is \code{2}. -#' \item \code{$sub} and \code{$sup} are arguments in the exponent (\code{^}) or subscript (\code{_}) -#' following the current expression. E.g. for \code{\\sum^{x}}, \code{$sup} is \code{x}. -#' \item \code{$LEFT} and \code{$RIGHT} are substituted the previous and following LaTeX expression -#' relative to the current token. +#' \item \code{$sub} and \code{$sup} are arguments in the exponent (\code{^}) or +#' subscript (\code{_}) following the current expression. E.g. for +#' \code{\\sum^{x}}, \code{$sup} is \code{x}. +#' \item \code{$LEFT} and \code{$RIGHT} are substituted the previous and +#' following LaTeX expression relative to the current token. #' } -#' See the Examples section for an example of using the \code{user_defined} option. +#' See the Examples section for an example of using the \code{user_defined} +#' option. #' #' @examples #' TeX("$\\alpha$") # plots the greek alpha character #' TeX("The ratio of 1 and 2 is $\\frac{1}{2}$") #' #' a <- 1:100 -#' plot(a, a^2, xlab=TeX("$\\alpha$"), ylab=TeX("$\\alpha^2$")) +#' plot(a, a^2, xlab = TeX("$\\alpha$"), ylab = TeX("$\\alpha^2$")) #' #' # create a \\variance command that takes a single argument -#' TeX("$\\variance{X} = 10$", user_defined=list("\\variance"="sigma[$arg1]^2")) +#' TeX("$\\variance{X} = 10$", +#' user_defined = list("\\variance" = "sigma[$arg1]^2")) #' @export -TeX <- - function(input, bold=FALSE, italic=FALSE, user_defined=list(), output = c('expression', 'character', 'ast')) { - if (length(input) > 1) { - return(sapply(input, TeX, bold=bold, italic=italic, user_defined=user_defined, output = output)) - } - stopifnot(is.character(input)) +TeX <- function(input, bold = FALSE, italic = FALSE, user_defined = list(), + output = c('expression', 'character', 'ast')) { + if (length(input) > 1) { + return(sapply(input, TeX, bold = bold, italic = italic, + user_defined = user_defined, output = output)) + } + stopifnot(is.character(input)) + + output <- match.arg(output) + parsed <- parse_latex(input) - output <- match.arg(output) - parsed <- parse_latex(input) + # Try all combinations of "hacks" in this grid, until one succeeds. + # As more hacks are introduced, the resulting expression will be less and + # less tidy, although it should still be visually equivalent to the + # desired output given the latex string. + grid <- expand.grid(hack_parentheses = c(FALSE, TRUE)) + successful <- FALSE + for (row in seq_len(nrow(grid))) { + # Make a deep clone of the LaTeX token tree + parsed_clone <- clone_token(parsed) + rendered <- render_latex(parsed_clone, user_defined, + hack_parentheses = grid$hack_parentheses[[row]]) - # Try all combinations of "hacks" in this grid, until one succeeds. - # As more hacks are introduced, the resulting expression will be less and - # less tidy, although it should still be visually equivalent to the - # desired output given the latex string. - grid <- expand.grid(hack_parentheses = c(FALSE, TRUE)) - successful <- FALSE - for (row in seq_len(nrow(grid))) { - # Make a deep clone of the LaTeX token tree - parsed_clone <- clone_token(parsed) - rendered <- render_latex(parsed_clone, user_defined, - hack_parentheses=grid$hack_parentheses[[row]]) - - if (bold && italic) { - rendered <- str_c("bolditalic(", rendered, ")") - } else if (bold) { - rendered <- str_c("bold(", rendered, ")") - } else if (italic) { - rendered <- str_c("italic(", rendered, ")") - } - - cat_trace("Rendered as ", rendered, " with parameters ", toString(grid[row,])) - - if (output == "ast") { - return(parsed) - } - - rendered_expression <- try({ - str2expression(rendered) - }, silent=TRUE) - - if (inherits(rendered_expression, "try-error")) { - error <- rendered_expression - cat_trace("Failed, trying next combination of hacks, error:", error, - " parsed as: ", rendered) - - if (row == 1) { - original_error <- error - } - } else { - successful <- TRUE - break - } + if (bold && italic) { + rendered <- paste0("bolditalic(", rendered, ")") + } else if (bold) { + rendered <- paste0("bold(", rendered, ")") + } else if (italic) { + rendered <- paste0("italic(", rendered, ")") } - if (!successful) { - stop("Error while converting LaTeX into valid plotmath.\n", - "Original string: ", input, "\n", - "Parsed expression: ", rendered, "\n", - original_error) - } - if (output == "character") { - return(rendered) - } + cat_trace("Rendered as ", rendered, " with parameters ", + toString(grid[row, ])) - # if the rendered expression is empty, return expression('') instead. - if (length(rendered_expression) == 0) { - rendered_expression <- expression('') + if (output == "ast") { + return(parsed) } - class(rendered_expression) <- c("latexexpression", "expression") - attr(rendered_expression, "latex") <- input - attr(rendered_expression, "plotmath") <- rendered + rendered_expression <- try({ + str2expression(rendered) + }, silent = TRUE) - rendered_expression + if (inherits(rendered_expression, "try-error")) { + error <- rendered_expression + cat_trace("Failed, trying next combination of hacks, error:", error, + " parsed as: ", rendered) + + if (row == 1) { + original_error <- error + } + } else { + successful <- TRUE + break + } + } + + if (!successful) { + stop("Error while converting LaTeX into valid plotmath.\n", + "Original string: ", input, "\n", + "Parsed expression: ", rendered, "\n", + original_error) + } + if (output == "character") { + return(rendered) + } + + # if the rendered expression is empty, return expression('') instead. + if (length(rendered_expression) == 0) { + rendered_expression <- expression('') } + + class(rendered_expression) <- c("latexexpression", "expression") + attr(rendered_expression, "latex") <- input + attr(rendered_expression, "plotmath") <- rendered + + rendered_expression +} diff --git a/R/parser.R b/R/parser.R index 5de3a63..8a6970a 100644 --- a/R/parser.R +++ b/R/parser.R @@ -6,7 +6,7 @@ tok$sub_arg <- list() tok$children <- list() tok$command <- command - tok$is_command <- str_starts(command, fixed("\\")) + tok$is_command <- startsWith(command, "\\") tok$text_mode <- text_mode tok$left_operator <- tok$right_operator <- FALSE class(tok) <- "latextoken2" @@ -29,19 +29,31 @@ clone_token <- function(tok) { } .find_substring <- function(string, boundary_characters) { - pattern <- str_c("^[^", - str_c("\\", boundary_characters, collapse=""), - "]+") - ret <- str_match(string, pattern)[1,1] - if ((is.na(ret) || nchar(ret) == 0) && nchar(string) > 0) { - str_sub(string, 1, 1) - } else { - ret - } + # This appears overly complex, based on the value returned by str_match() + #pattern <- paste0("^[^", + # paste0("\\", boundary_characters, collapse = ""), + # "]+") + #ret <- str_match(string, pattern)[1,1] + #if ((is.na(ret) || nchar(ret) == 0) && nchar(string) > 0) { + # substring(string, 1, 1) + #} else { + # ret + #} + # Empty string is returned as such + if (nchar(string) == 0) + return("") + # Boundary characters at the beginning of the string is returned + first_char <- substring(string, 1, 1) + if (first_char %in% boundary_characters) + return(first_char) + # Otherwise, anything, starting from a boundary character is eliminated + boundary_pattern <- paste0("\\", boundary_characters, collapse = "") + pattern <- paste0("[", boundary_pattern, "].*$") + sub(pattern, "", string, perl = TRUE) } .find_substring_matching <- function(string, opening, closing) { - chars <- str_split(string, "")[[1]] + chars <- strsplit(string, "", fixed = TRUE)[[1]] depth <- 0 start_expr <- -1 @@ -54,47 +66,53 @@ clone_token <- function(tok) { } else if (chars[i] == closing) { depth <- depth - 1 if (depth == 0) { - return(str_sub(string, start_expr+1, i-1)) + return(substring(string, start_expr + 1, i - 1)) } } } if (depth != 0) { - stop("Unmatched '", opening, "' (opened at position: ", start_expr, ") while parsing '", string, "'") + stop("Unmatched '", opening, "' (opened at position: ", start_expr, + ") while parsing '", string, "'") } else { return(string) } } -parse_latex <- function(latex_string, - text_mode=TRUE, - depth=0, - pos=0, - parent=NULL) { +parse_latex <- function(latex_string, text_mode = TRUE, depth = 0, pos = 0, + parent = NULL) { input <- latex_string if (depth == 0) { validate_input(latex_string) } if (depth == 0) { - latex_string <- latex_string %>% - str_replace_fixed('\\|', '\\@pipe ') %>% - - str_replace_all("\\\\['\\$\\{\\}\\[\\]\\!\\?\\_\\^]", function(char) { - str_c("\\ESCAPED@", - as.integer(charToRaw(str_replace_fixed(char, "\\", ""))), - "{}") - }) %>% + latex_string <- str_replace_fixed(latex_string, '\\|', '\\@pipe ') + # This one must be replaced by several calls to gsub() + #latex_string <- str_replace_all(latex_string, + # "\\\\['\\$\\{\\}\\[\\]\\!\\?\\_\\^]", function(char) { + # paste0("\\ESCAPED@", + # as.integer(charToRaw(str_replace_fixed(char, "\\", ""))), + # "{}") + # }) + latex_string <- gsub("\\\\'", "\\\\ESCAPED@39{}", latex_string) + latex_string <- gsub("\\\\\\$", "\\\\ESCAPED@36{}", latex_string) + latex_string <- gsub("\\\\\\{", "\\\\ESCAPED@123{}", latex_string) + latex_string <- gsub("\\\\\\}", "\\\\ESCAPED@125{}", latex_string) + latex_string <- gsub("\\\\\\[", "\\\\ESCAPED@91{}", latex_string) + latex_string <- gsub("\\\\\\]", "\\\\ESCAPED@93{}", latex_string) + latex_string <- gsub("\\\\\\!", "\\\\ESCAPED@33{}", latex_string) + latex_string <- gsub("\\\\\\?", "\\\\ESCAPED@63{}", latex_string) + latex_string <- gsub("\\\\\\_", "\\\\ESCAPED@95{}", latex_string) + latex_string <- gsub("\\\\\\^", "\\\\ESCAPED@94{}", latex_string) - str_replace_all("([^\\\\]?)\\\\,", "\\1\\\\@SPACE1{}") %>% - str_replace_all("([^\\\\]?)\\\\;", "\\1\\\\@SPACE2{}") %>% - str_replace_all("([^\\\\]?)\\\\\\s", "\\1\\\\@SPACE2{}") + latex_string <- gsub("([^\\\\]?)\\\\,", "\\1\\\\@SPACE1{}", latex_string) + latex_string <- gsub("([^\\\\]?)\\\\;", "\\1\\\\@SPACE2{}", latex_string) + latex_string <- gsub("([^\\\\]?)\\\\\\s", "\\1\\\\@SPACE2{}", latex_string) cat_trace("String with special tokens substituted: ", latex_string) } - - i <- 1 tokens <- list() @@ -103,12 +121,13 @@ parse_latex <- function(latex_string, withCallingHandlers({ while (i <= nchar(latex_string)) { # Look at current character, previous character, and next character - ch <- str_sub(latex_string, i, i) - prevch <- if (i == 1) "" else str_sub(latex_string, i-1, i-1) - nextch <- if (i == nchar(latex_string)) "" else str_sub(latex_string, i+1, i+1) + ch <- substring(latex_string, i, i) + prevch <- if (i == 1) "" else substring(latex_string, i - 1, i - 1) + nextch <- if (i == nchar(latex_string)) "" else + substring(latex_string, i + 1, i + 1) # LaTeX string left to be processed - current_fragment <- str_sub(latex_string, i) + current_fragment <- substring(latex_string, i) cat_trace("Position: ", i, " ch: ", ch, " next: ", nextch, " current fragment: ", current_fragment, @@ -126,10 +145,10 @@ parse_latex <- function(latex_string, # another backslash, or a separator, or a dollar if (ch == "\\" && nextch != "\\") { # Continue until we encounter a separator - current_fragment <- str_sub(current_fragment, 2) + current_fragment <- substring(current_fragment, 2) - command <- str_c("\\", - .find_substring(current_fragment, .math_separators)) + command <- paste0("\\", + .find_substring(current_fragment, .math_separators)) cat_trace("Found token ", command, " in text_mode: ", text_mode) token <- .token2(command, text_mode) tokens <- c(tokens, token) @@ -141,28 +160,24 @@ parse_latex <- function(latex_string, ch %in% c(".", "{", "}", "[", "]", "(", ")", "|")) { # a \\left or \\right command has started. eat up the next character # and append it to the command. - token$command <- str_c(token$command, ch) + token$command <- paste0(token$command, ch) i <- i + 1 } else if (ch == "{") { - argument <- .find_substring_matching(current_fragment, - "{", - "}") + argument <- .find_substring_matching(current_fragment, "{", "}") if (is.null(token)) { token <- .token2("", text_mode) tokens <- c(tokens, token) } - args <- parse_latex(argument, text_mode=text_mode, - depth=depth+1, parent=token, pos=i) + args <- parse_latex(argument, text_mode = text_mode, + depth = depth + 1, parent = token, pos = i) if (length(args) > 0) { token$args <- c(token$args, list(args)) } # advance by two units (the content of the braces + two braces) i <- i + nchar(argument) + 2 } else if (ch == "[") { - argument <- .find_substring_matching(current_fragment, - "[", - "]") + argument <- .find_substring_matching(current_fragment, "[", "]") if (is.null(token)) { token <- .token2("", text_mode) tokens <- c(tokens, token) @@ -170,8 +185,8 @@ parse_latex <- function(latex_string, token$optional_arg <- c( token$optional_arg, - parse_latex(argument, text_mode=text_mode, - depth=depth+1, parent=token, pos=i) + parse_latex(argument, text_mode = text_mode, + depth = depth + 1, parent = token, pos = i) ) # advance by two units (the content of the braces + two braces) @@ -189,31 +204,32 @@ parse_latex <- function(latex_string, # If there are spaces after the ^ or _ character, # consume them and advance past the spaces if (nextch == " ") { - n_spaces <- str_match(str_sub(current_fragment, 2), "\\s+")[1,1] + #n_spaces <- str_match(substring(current_fragment, 2), "\\s+")[1, 1] + n_spaces <- regmatches(current_fragment, + regexpr("\\s+", substring(current_fragment, 2))) advance <- advance + nchar(n_spaces) - nextch <- str_sub(current_fragment, advance + 1, advance+1) + nextch <- substring(current_fragment, advance + 1, advance + 1) } # Sub or sup arguments grouped with braces. This is easy! if (nextch == "{") { - argument <- .find_substring_matching(str_sub(current_fragment, advance+1), - "{", - "}") + argument <- .find_substring_matching(substring(current_fragment, + advance + 1), "{", "}") # advance by two units (the content of the braces + two braces) advance <- advance + nchar(argument) + 2 } else if (nextch == "\\") { # Advance until a separator is found - argument <- str_c("\\", - .find_substring(str_sub(current_fragment, advance+2), separators)) + argument <- paste0("\\", + .find_substring(substring(current_fragment, advance + 2), separators)) advance <- advance + nchar(argument) } else { - argument <- str_sub(current_fragment, advance+1, advance+1) + argument <- substring(current_fragment, advance + 1, advance + 1) advance <- advance + nchar(argument) } - token[[arg_type]] <- parse_latex(argument, text_mode=text_mode, - depth=depth+1, parent=token, pos=i) + token[[arg_type]] <- parse_latex(argument, text_mode = text_mode, + depth = depth + 1, parent = token, pos = i) i <- i + advance } else if (ch == "$") { @@ -230,7 +246,7 @@ parse_latex <- function(latex_string, token <- .token2(" ", text_mode) tokens <- c(tokens, token) } else { - token$command <- str_c(token$command, " ") + token$command <- paste0(token$command, " ") } } i <- i + 1 @@ -245,12 +261,12 @@ parse_latex <- function(latex_string, if (ch == "'") { ch <- "\\'" } - token$command <- str_c(token$command, ch) - i <- i+1 + token$command <- paste0(token$command, ch) + i <- i + 1 } else if (ch %in% c("?", "!", "@", ":", ";")) { # ...or escape them to avoid introducing illegal characters in the # plotmath expression... - token <- .token2(str_c("\\ESCAPED@", utf8ToInt(ch)), TRUE) + token <- .token2(paste0("\\ESCAPED@", utf8ToInt(ch)), TRUE) tokens <- c(tokens, token) i <- i + 1 } else if (ch == "'") { @@ -269,16 +285,14 @@ parse_latex <- function(latex_string, str <- .find_substring(current_fragment, separators) # If in math mode, ignore spaces - token <- .token2(str_replace_all(str, - "\\s+", ""), - text_mode) + token <- .token2(gsub("\\s+", "", str), text_mode) tokens <- c(tokens, token) i <- i + nchar(str) } } } - }, error=function(e) { + }, error = function(e) { token_command <- if (is.null(token)) { "" } else { @@ -290,7 +304,8 @@ parse_latex <- function(latex_string, message("Last token parsed:", token$command) } if (!is.null(parent)) { - message("The error happened within the arguments of :", parent$command, "\n") + message("The error happened within the arguments of :", + parent$command, "\n") } }) @@ -309,16 +324,18 @@ parse_latex <- function(latex_string, #' returned by \code{parse_latex}. #' #' @param tokens tree of tokens -#' @param user_defined any custom definitions of commands passed to \code{\link{TeX}} -#' @param hack_parentheses render parentheses using \code{group('(', phantom(), '.')} and -#' \code{group(')', phantom(), '.')}. This is useful to return -#' valid expressions when the LaTeX source contains mismatched -#' parentheses, but makes the returned expression much -#' less tidy. +#' @param user_defined any custom definitions of commands passed to +#' \code{\link{TeX}} +#' @param hack_parentheses render parentheses using +#' \code{group('(', phantom(), '.')} and \code{group(')', phantom(), '.')}. +#' This is useful to return valid expressions when the LaTeX source contains +#' mismatched parentheses, but makes the returned expression much less tidy. #' @return String that should be parseable as a valid plotmath expression -render_latex <- function(tokens, user_defined=list(), hack_parentheses=FALSE) { +render_latex <- function(tokens, user_defined = list(), + hack_parentheses = FALSE) { if (!is.null(tokens$children)) { - return(render_latex(tokens$children, user_defined, hack_parentheses=hack_parentheses)) + return(render_latex(tokens$children, user_defined, + hack_parentheses = hack_parentheses)) } translations <- c(user_defined, latex_supported_map) @@ -326,10 +343,12 @@ render_latex <- function(tokens, user_defined=list(), hack_parentheses=FALSE) { tok <- tokens[[tok_idx]] tok$skip <- FALSE - tok$rendered <- if (str_detect(tok$command, "^\\\\ESCAPED@")) { + tok$rendered <- if (grepl("^\\\\ESCAPED@", tok$command)) { # a character, like '!' or '?' was escaped as \\ESCAPED@ASCII_SYMBOL. # return it as a string. - arg <- str_match(tok$command, "@(\\d+)")[1,2] + #arg <- str_match(tok$command, "@(\\d+)")[1,2] + arg <- substring(regmatches(tok$command, + regexpr("@(\\d+)", tok$command)), 2) arg <- intToUtf8(arg) if (arg == "'") { @@ -341,11 +360,11 @@ render_latex <- function(tokens, user_defined=list(), hack_parentheses=FALSE) { tok$left_separator <- '' } - str_c("'", arg, "'") + paste0("'", arg, "'") #next } else if (!tok$text_mode || tok$is_command) { # translate using the translation table in symbols.R - translations[[str_trim(tok$command)]] %??% tok$command + translations[[trimws(tok$command)]] %??% tok$command } else { # leave as-is tok$command @@ -354,7 +373,8 @@ render_latex <- function(tokens, user_defined=list(), hack_parentheses=FALSE) { # empty command; if followed by arguments such as sup or sub, render as # an empty token, otherwise skip if (tok$rendered == "") { - if (length(tok$args) > 0 || length(tok$sup_arg) > 0 || length(tok$sub_arg) > 0) { + if (length(tok$args) > 0 || length(tok$sup_arg) > 0 || + length(tok$sub_arg) > 0) { tok$rendered <- "{}" } else { tok$skip <- TRUE @@ -362,28 +382,32 @@ render_latex <- function(tokens, user_defined=list(), hack_parentheses=FALSE) { } if (tok$text_mode && !tok$is_command) { - tok$rendered <- str_c("'", tok$rendered, "'") + tok$rendered <- paste0("'", tok$rendered, "'") } # If the token starts with a number, break the number from # the rest of the string. This is because a plotmath symbol # cannot start with a number. - if (str_detect(tok$rendered, "^[0-9]") && !tok$text_mode) { - split <- str_match(tok$rendered, "(^[0-9\\.]*)(.*)") + if (grepl("^[0-9]", tok$rendered) && !tok$text_mode) { + # This is ultra-complex for something simple using sub() + #split <- str_match(tok$rendered, "(^[0-9\\.]*)(.*)") + #if (split[1, 3] != "") { + # tok$rendered <- paste0(split[1, 2], "*", split[1, 3]) + #} else { + # tok$rendered <- split[1, 2] + #} + tok$rendered <- sub("^([0-9\\.]+)([^0-9\\.].*)", "\\1*\\2", tok$rendered) - if (split[1, 3] != "") { - tok$rendered <- str_c(split[1,2], "*", split[1,3]) - } else { - tok$rendered <- split[1,2] - } - if (str_starts(tok$rendered, "0") && str_length(tok$rendered) > 1) { - tok$rendered <- str_c("0*", str_sub(tok$rendered, 2)) + if (startsWith(tok$rendered, "0") && nchar(tok$rendered) > 1) { + tok$rendered <- paste0("0*", substring(tok$rendered, 2)) } + # I need this to avoid double zeros before the decimal point + tok$rendered <- gsub("0*.", "0.", tok$rendered, fixed = TRUE) } - tok$left_operator <- str_detect(tok$rendered, fixed("$LEFT")) - tok$right_operator <- str_detect(tok$rendered, fixed("$RIGHT")) + tok$left_operator <- grepl("$LEFT", tok$rendered, fixed = TRUE) + tok$right_operator <- grepl("$RIGHT", tok$rendered, fixed = TRUE) if (tok_idx == 1) { tok$left_separator <- "" @@ -392,85 +416,68 @@ render_latex <- function(tokens, user_defined=list(), hack_parentheses=FALSE) { if (tok$left_operator) { if (tok_idx == 1) { # Either this operator is the first token... - tok$rendered <- str_replace_all(tok$rendered, - fixed("$LEFT"), - "phantom()") - } else if (tokens[[tok_idx-1]]$right_operator) { + tok$rendered <- str_replace_fixed(tok$rendered, "$LEFT", "phantom()") + } else if (tokens[[tok_idx - 1]]$right_operator) { # or the previous token was also an operator or an open parentheses. # Bind the tokens using phantom() - tok$rendered <- str_replace_all(tok$rendered, - fixed("$LEFT"), - "phantom()") + tok$rendered <- str_replace_fixed(tok$rendered, "$LEFT", "phantom()") } else { - tok$rendered <- str_replace_all(tok$rendered, - fixed("$LEFT"), - "") + tok$rendered <- str_replace_fixed(tok$rendered, "$LEFT", "") tok$left_separator <- "" } } if (tok$right_operator) { if (tok_idx == length(tokens)) { - tok$rendered <- str_replace_all(tok$rendered, - fixed("$RIGHT"), - "phantom()") + tok$rendered <- str_replace_fixed(tok$rendered, "$RIGHT", "phantom()") } else { - tok$rendered <- str_replace_all(tok$rendered, - fixed("$RIGHT"), - "") - tokens[[tok_idx+1]]$left_separator <- "" + tok$rendered <- str_replace_fixed(tok$rendered, "$RIGHT", "") + tokens[[tok_idx + 1]]$left_separator <- "" } } if (length(tok$args) > 0) { for (argidx in seq_along(tok$args)) { - args <- render_latex(tok$args[[argidx]], user_defined, hack_parentheses=hack_parentheses) - argfmt <- str_c("$arg", argidx) - if (str_detect(tok$rendered, fixed(argfmt))) { - tok$rendered <- str_replace_all(tok$rendered, - fixed(argfmt), - args) + args <- render_latex(tok$args[[argidx]], user_defined, + hack_parentheses = hack_parentheses) + argfmt <- paste0("$arg", argidx) + if (grepl(argfmt, tok$rendered, fixed = TRUE)) { + tok$rendered <- str_replace_fixed(tok$rendered, argfmt, args) } else { if (tok$rendered != "{}") { - tok$rendered <- str_c(tok$rendered, " * {", - args, "}") + tok$rendered <- paste0(tok$rendered, " * {", args, "}") } else { - tok$rendered <- str_c("{", args, "}") + tok$rendered <- paste0("{", args, "}") } } } } if (length(tok$optional_arg) > 0) { - optarg <- render_latex(tok$optional_arg, user_defined, hack_parentheses=hack_parentheses) - if (str_detect(tok$rendered, fixed("$opt"))) { - tok$rendered <- str_replace_all(tok$rendered, - fixed("$opt"), - optarg) + optarg <- render_latex(tok$optional_arg, user_defined, + hack_parentheses = hack_parentheses) + if (grepl("$opt", tok$rendered, fixed = TRUE)) { + tok$rendered <- str_replace_fixed(tok$rendered, "$opt", optarg) } else { # the current token is not consuming an optional argument, so render # it as square brackets - tok$rendered <- str_c(tok$rendered, " * '[' *", - optarg, " * ']'") + tok$rendered <- paste0(tok$rendered, " * '[' *", optarg, " * ']'") } } for (type in c("sub", "sup")) { - arg <- tok[[str_c(type, "_arg")]] - argfmt <- str_c("$", type) + arg <- tok[[paste0(type, "_arg")]] + argfmt <- paste0("$", type) if (length(arg) > 0) { - rarg <- render_latex(arg, user_defined, hack_parentheses=hack_parentheses) + rarg <- render_latex(arg, user_defined, + hack_parentheses = hack_parentheses) - if (str_detect(tok$rendered, fixed(argfmt))) { - tok$rendered <- str_replace_all(tok$rendered, fixed(argfmt), rarg) + if (grepl(argfmt, tok$rendered, fixed = TRUE)) { + tok$rendered <- str_replace_fixed(tok$rendered, argfmt, rarg) } else { if (type == "sup") { - tok$rendered <- sprintf("%s^{%s}", - tok$rendered, - rarg) + tok$rendered <- sprintf("%s^{%s}", tok$rendered, rarg) } else { - tok$rendered <- sprintf("%s[%s]", - tok$rendered, - rarg) + tok$rendered <- sprintf("%s[%s]", tok$rendered, rarg) } } @@ -481,13 +488,14 @@ render_latex <- function(tokens, user_defined=list(), hack_parentheses=FALSE) { # any arguments that were not specified (e.g. if # there is no argument specified for the command, # substitute '' for '$arg1') - tok$rendered <- tok$rendered %>% - str_replace_fixed("$P", "phantom()") %>% - str_replace_fixed("$arg1", "") %>% - str_replace_fixed("$arg2", "") %>% - str_replace_fixed("$sup", "") %>% - str_replace_fixed("$sub", "") %>% - str_replace_fixed("$opt", "") + tkr <- tok$rendered + tkr <- str_replace_fixed(tkr, "$P", "phantom()") + tkr <- str_replace_fixed(tkr, "$arg1", "") + tkr <- str_replace_fixed(tkr, "$arg2", "") + tkr <- str_replace_fixed(tkr, "$sup", "") + tkr <- str_replace_fixed(tkr,"$sub", "") + tkr <- str_replace_fixed(tkr, "$opt", "") + tok$rendered <- tkr if (tok_idx != length(tokens) && tok$command == "\\frac") { tok$right_separator <- " * phantom(.)" @@ -498,10 +506,11 @@ render_latex <- function(tokens, user_defined=list(), hack_parentheses=FALSE) { tok$left_separator <- "" tok$right_separator <- "" } - if (tok_idx > 1 && tokens[[tok_idx-1]]$command == "(") { + if (tok_idx > 1 && tokens[[tok_idx - 1]]$command == "(") { tok$left_separator <- "" } - if (tok_idx > 1 && tokens[[tok_idx]]$command == "(" && length(tokens[[tok_idx-1]]$sup_arg) > 0) { + if (tok_idx > 1 && tokens[[tok_idx]]$command == + "(" && length(tokens[[tok_idx - 1]]$sup_arg) > 0) { tok$left_separator <- "*" } } else { @@ -517,7 +526,7 @@ render_latex <- function(tokens, user_defined=list(), hack_parentheses=FALSE) { # If the token still starts with a "\", substitute it # with the corresponding expression - tok$rendered <- str_replace(tok$rendered, "^\\\\", "") + tok$rendered <- sub("^\\\\", "", tok$rendered) if (tok$rendered == "{}") { tok$skip <- TRUE @@ -529,12 +538,12 @@ render_latex <- function(tokens, user_defined=list(), hack_parentheses=FALSE) { if (tok$skip) { "" } else { - str_c(tok$left_separator %??% "*", - tok$rendered, - tok$right_separator %??% "") + paste0(tok$left_separator %??% "*", + tok$rendered, + tok$right_separator %??% "") } }) - str_c(rendered_tokens, collapse="") + paste0(rendered_tokens, collapse = "") } # Validates the input LaTeX string @@ -548,29 +557,36 @@ render_latex <- function(tokens, user_defined=list(), hack_parentheses=FALSE) { # validate_input <- function(latex_string) { for (possible_slash_pattern in c("\a", "\b", "\f", "\v")) { - if (str_detect(latex_string, fixed(possible_slash_pattern))) { + if (grepl(possible_slash_pattern, latex_string, fixed = TRUE)) { repr <- deparse(possible_slash_pattern) message("latex2exp: Detected possible missing backslash: you entered ", repr, ", did you mean to type ", - str_replace(repr, fixed("\\"), "\\\\"), "?") + sub("\\\\?", "?", repr)) } } - if (str_detect(latex_string, fixed("\\\\"))) { - stop("The LaTeX string '", - latex_string, - "' includes a '\\\\' command. Line breaks are not currently supported.") + if (grepl("\\\\", latex_string, fixed = TRUE)) { + stop("The LaTeX string '", latex_string, + "' includes a '\\\\' command. Line breaks are not currently supported.") } - test_string <- latex_string %>% - str_replace_fixed("\\{", "") %>% - str_replace_fixed("\\}", "") + test_string <- str_replace_fixed(latex_string, "\\{", "") + test_string <- str_replace_fixed(test_string, "\\}", "") + n_match_all <- function(x, pattern) { + res <- gregexpr(pattern, x, perl = TRUE)[[1]] + if (length(res) == 1 && res == -1) 0 else length(res) + } + # check that opened and closed braces match in number - opened_braces <- nrow(str_match_all(test_string, "[^\\\\]*?(\\{)")[[1]]) - - nrow(str_match_all(test_string, "\\\\left\\{")[[1]]) - closed_braces <- nrow(str_match_all(test_string, "[^\\\\]*?(\\})")[[1]]) - - nrow(str_match_all(test_string, "\\\\right\\}")[[1]]) + #opened_braces <- nrow(str_match_all(test_string, "[^\\\\]*?(\\{)")[[1]]) - + # nrow(str_match_all(test_string, "\\\\left\\{")[[1]]) + opened_braces <- n_match_all(test_string, "[^\\\\]*?(\\{)") - + n_match_all(test_string, "\\\\left\\{") + #closed_braces <- nrow(str_match_all(test_string, "[^\\\\]*?(\\})")[[1]]) - + # nrow(str_match_all(test_string, "\\\\right\\}")[[1]]) + closed_braces <- n_match_all(test_string, "[^\\\\]*?(\\})") - + n_match_all(test_string, "\\\\right\\}") if (opened_braces != closed_braces) { stop("Mismatched number of braces in '", latex_string, "' (", @@ -579,11 +595,16 @@ validate_input <- function(latex_string) { } # check that the number of \left* and \right* commands match - lefts <- nrow(str_match_all(test_string, "[^\\\\]*\\\\left[\\(\\{\\|\\[\\.]")[[1]]) - rights <- nrow(str_match_all(test_string, "[^\\\\]*\\\\right[\\)\\}\\|\\]\\.]")[[1]]) + #lefts <- nrow(str_match_all(test_string, + # "[^\\\\]*\\\\left[\\(\\{\\|\\[\\.]")[[1]]) + lefts <- n_match_all(test_string, "[^\\\\]*\\\\left[\\(\\{\\|\\[\\.]") + #rights <- nrow(str_match_all(test_string, + # "[^\\\\]*\\\\right[\\)\\}\\|\\]\\.]")[[1]]) + rights <- n_match_all(test_string, "[^\\\\]*\\\\right[\\)\\}\\|\\]\\.]") if (lefts != rights) { - stop("Mismatched number of \\left and \\right commands in '", latex_string, "' (", + stop("Mismatched number of \\left and \\right commands in '", + latex_string, "' (", lefts, " left commands, ", rights, " right commands.") } diff --git a/R/plots.R b/R/plots.R index c550066..4b37ce1 100644 --- a/R/plots.R +++ b/R/plots.R @@ -14,7 +14,7 @@ NULL #' @export #' @examples #' plot(TeX("Example equation: $a \\geq b$")) -plot.expression <- function(x, ..., main=NULL) { +plot.expression <- function(x, ..., main = NULL) { oldpar <- par(no.readonly = TRUE) dots <- list(...) on.exit(suppressWarnings(par(oldpar))) @@ -37,11 +37,11 @@ plot.expression <- function(x, ..., main=NULL) { #' @param ... Other parameters (not used) #' @return A data frame containing a table of supported LaTeX commands. #' @export -latex2exp_supported <- function(show=FALSE, ...) { +latex2exp_supported <- function(show = FALSE, ...) { dots <- list(...) - # the previous version of latex2exp accepted the parameter `plot` with the same - # meaning as show=TRUE. + # the previous version of latex2exp accepted the parameter `plot` with the + # same meaning as show = TRUE. if (!is.null(dots$plot) && dots$plot) { .Deprecated("Use the parameter show=TRUE instead of plot.") show <- TRUE @@ -49,40 +49,42 @@ latex2exp_supported <- function(show=FALSE, ...) { if (!show) { supp <- lapply(latex_supported, function(it) { - # remove all commands that include the '@' character, which are used internally to - # escape certain commands. - names(it)[!str_detect(names(it), fixed("@"))] + # remove all commands that include the '@' character, which are used + # internally to escape certain commands. + names(it)[!grepl("@", names(it), fixed = TRUE)] }) supp <- mapply(function(category, commands) { # create an example suitable to demo each category of latex commands examples <- sapply(commands, function(commands) { - if (category %in% c("arithmetic operators", "binary operators", "arrows")) { - str_c("$\\alpha ", commands, " \\beta$") + if (category %in% + c("arithmetic operators", "binary operators", "arrows")) { + paste0("$\\alpha ", commands, " \\beta$") } else if (category == "set operators") { - str_c("$A ", commands, " B$") + paste0("$A ", commands, " B$") } else if (commands == "\\frac" || commands == "\\overset") { "$\\frac{x+y}{x-y}$" } else if (commands == "\\lim") { "$\\lim_{x \\to \\infty} \\frac{1}{x}$" } else if (commands %in% c("\\sum", "\\prod", "\\int")) { - str_c("$", commands, "_{i=0}^{\\infty}$") + paste0("$", commands, "_{i=0}^{\\infty}$") } else if (commands == "\\sqrt") { "$\\sqrt[z]{x+y}$" } else if (commands %in% c("\\max", "\\min")) { - str_c("$", commands, "_{x \\in X} x^2$") + paste0("$", commands, "_{x \\in X} x^2$") } else if (commands %in% c("\\bigcup", "\\bigcap")) { - str_c("$", commands, "_{i} A_i$") + paste0("$", commands, "_{i} A_i$") } else if (category == "text size" || category == "formatting") { - str_c(commands, "{example text}") + paste0(commands, "{example text}") } else if (category %in% c("\\ ", "\\;", "\\,")) { - str_c("$x ", commands, " y$") + paste0("$x ", commands, " y$") } else if (commands == "\\braket") { - str_c("$\\braket{\\Psi | \\Psi}$") + paste0("$\\braket{\\Psi | \\Psi}$") } else if (category == "decorations" || category == "vector") { - str_c("$", commands, "{\\Psi}$") + paste0("$", commands, "{\\Psi}$") } else if (category == "layout and spacing") { - str_c("$A ", commands, " B$") - } else if (category == "parentheses" || category == "parentheses (not scalable)") { + paste0("$A ", commands, " B$") + } else if (category == "parentheses" || + category == "parentheses (not scalable)") { op <- commands if (commands == "\\left(") { clo <- "\\right)" @@ -94,23 +96,23 @@ latex2exp_supported <- function(show=FALSE, ...) { clo <- "\\right|" } else if (commands == "\\left.") { clo <- "\\right." - } else if (str_detect(commands, "^\\\\l")) { - clo <- str_c("\\r", str_replace(commands, fixed("\\l"), "")) - } else if (str_detect(commands, "^\\\\r")) { + } else if (grepl("^\\\\l", commands)) { + clo <- paste0("\\r", sub("\\l", "", commands, fixed = TRUE)) + } else if (grepl("^\\\\r", commands)) { return(NA) } else { clo <- op } - str_c("$", op, " a+b ", clo, "$") + paste0("$", op, " a+b ", clo, "$") } else { - str_c("$", commands, "$") + paste0("$", commands, "$") } }) - data.frame(category, command=commands, example=examples) + data.frame(category, command = commands, example = examples) }, names(supp), supp, SIMPLIFY = FALSE) - do.call(function(...) rbind.data.frame(..., make.row.names=FALSE, stringsAsFactors = FALSE), - supp) + do.call(function(...) rbind.data.frame(..., make.row.names = FALSE, + stringsAsFactors = FALSE), supp) } else { vignette("supported-commands", package = "latex2exp") } diff --git a/R/utils.R b/R/utils.R index d748266..e0ae6f2 100644 --- a/R/utils.R +++ b/R/utils.R @@ -3,39 +3,43 @@ } str_replace_fixed <- function(string, pattern, replacement) { - str_replace_all(string, fixed(pattern), replacement) + #str_replace_all(string, fixed(pattern), replacement) + # This is a correct replacement of str_replace_all() only if one never + # uses a function for replacement... This is checked in v0.9.8 + gsub(pattern, replacement, string, fixed = TRUE) } -#' Prints out a parsed LaTeX object, as returned by TeX(..., output='ast'). +#' Prints out a parsed LaTeX object, as returned by TeX(..., output = 'ast'). #' This is primarily used for debugging. #' #' @param x The object #' @param depth Increases padding when recursing down the parsed structure #' @param ... (Ignored) #' @export -print.latextoken2 <- function(x, depth=0, ...) { +print.latextoken2 <- function(x, depth = 0, ...) { token <- x pad <- strrep(" ", depth) cat(pad, - if (depth > 0) str_c("| :", token$command, ":"), - if (!is.null(token$rendered)) str_c(" -> ", token$rendered), + if (depth > 0) paste0("| :", token$command, ":"), + if (!is.null(token$rendered)) paste0(" -> ", token$rendered), "\n", - sep="") + sep = "") - for (children_type in c("children", "args", "optional_arg", "sup_arg", "sub_arg")) { + for (children_type in + c("children", "args", "optional_arg", "sup_arg", "sub_arg")) { if (length(token[[children_type]]) > 0) { if (children_type != "children") { - cat(pad, "* <", children_type, ">", "\n", sep="") + cat(pad, "* <", children_type, ">", "\n", sep = "") } for (tok_idx in seq_along(token[[children_type]])) { c <- token[[children_type]][[tok_idx]] if (is.list(c)) { - cat(pad, " | [argument ", tok_idx, "]\n", sep="") + cat(pad, " | [argument ", tok_idx, "]\n", sep = "") for (cc in c) { - print(cc, depth+1) + print(cc, depth + 1) } } else { - print(c, depth+1) + print(c, depth + 1) } } } diff --git a/man/TeX.Rd b/man/TeX.Rd index 0b8c2e5..03946d8 100644 --- a/man/TeX.Rd +++ b/man/TeX.Rd @@ -28,11 +28,11 @@ a plotmath expression ready for plotting), and "ast" (returns the tree used to generate the expression).} } \value{ -Returns a plotmath expression by default. The \code{output} parameter can -modify the type of the returned value. +Returns a plotmath expression by default. The \code{output} parameter +can modify the type of the returned value. -If more than one string is specified in the \code{input} parameter, returns a list -of expressions. +If more than one string is specified in the \code{input} parameter, returns a +list of expressions. } \description{ \code{TeX} converts a string comprising LaTeX commands (such as @@ -42,27 +42,31 @@ formatted text and equations. } \section{Adding new commands}{ -New LaTeX commands can be defined by supplying the \code{user_defined} parameter. -The \code{user_defined} parameter is a list that contains LaTeX commands -as names, and template strings as values. A LaTeX command that matches -one of the names is translated into the corresponding string and included in -the final plotmath expression. The file \code{symbols.R} in the source code -of this package contains one such table that can be used as a reference. +New LaTeX commands can be defined by supplying the \code{user_defined} +parameter. The \code{user_defined} parameter is a list that contains LaTeX +commands as names, and template strings as values. A LaTeX command that +matches one of the names is translated into the corresponding string and +included in the final plotmath expression. The file \code{symbols.R} in the +source code of this package contains one such table that can be used as a +reference. The template string can include one of the following special template parameters: \itemize{ -\item \code{$arg1, $arg2, ...} represent the first, second, ... brace argument. - E.g. for \code{\\frac{x}{y}}, \code{$arg1} is \code{x} and \code{$arg2} is \code{y}. +\item \code{$arg1, $arg2, ...} represent the first, second, ... brace + argument. E.g. for \code{\\frac{x}{y}}, \code{$arg1} is \code{x} and + \code{$arg2} is \code{y}. \item \code{$opt} is an optional argument in square brackets. E.g. for \code{\\sqrt[2]{x}}, \code{$opt} is \code{2}. -\item \code{$sub} and \code{$sup} are arguments in the exponent (\code{^}) or subscript (\code{_}) - following the current expression. E.g. for \code{\\sum^{x}}, \code{$sup} is \code{x}. -\item \code{$LEFT} and \code{$RIGHT} are substituted the previous and following LaTeX expression - relative to the current token. +\item \code{$sub} and \code{$sup} are arguments in the exponent (\code{^}) or + subscript (\code{_}) following the current expression. E.g. for + \code{\\sum^{x}}, \code{$sup} is \code{x}. +\item \code{$LEFT} and \code{$RIGHT} are substituted the previous and + following LaTeX expression relative to the current token. } -See the Examples section for an example of using the \code{user_defined} option. +See the Examples section for an example of using the \code{user_defined} +option. } \examples{ @@ -70,8 +74,9 @@ TeX("$\\\\alpha$") # plots the greek alpha character TeX("The ratio of 1 and 2 is $\\\\frac{1}{2}$") a <- 1:100 -plot(a, a^2, xlab=TeX("$\\\\alpha$"), ylab=TeX("$\\\\alpha^2$")) +plot(a, a^2, xlab = TeX("$\\\\alpha$"), ylab = TeX("$\\\\alpha^2$")) # create a \\variance command that takes a single argument -TeX("$\\\\variance{X} = 10$", user_defined=list("\\\\variance"="sigma[$arg1]^2")) +TeX("$\\\\variance{X} = 10$", + user_defined = list("\\\\variance" = "sigma[$arg1]^2")) } diff --git a/man/latex2exp.Rd b/man/latex2exp.Rd index f999b72..a9d14b0 100644 --- a/man/latex2exp.Rd +++ b/man/latex2exp.Rd @@ -7,9 +7,13 @@ latex2exp(string, output = c("expression", "character", "ast")) } \arguments{ -\item{string}{A character vector containing LaTeX expressions. Note that any backslashes must be escaped (e.g. "$\\alpha").} +\item{string}{A character vector containing LaTeX expressions. Note that any +backslashes must be escaped (e.g. "$\\alpha").} -\item{output}{The returned object, one of "expression" (default, returns a plotmath expression ready for plotting), "character" (returns the expression as a string), and "ast" (returns the tree used to generate the expression).} +\item{output}{The returned object, one of "expression" (default, returns a +plotmath expression ready for plotting), "character" (returns the +expression as a string), and "ast" (returns the tree used to generate the +expression).} } \value{ Returns an expression (see the \code{output} parameter). diff --git a/man/print.latextoken2.Rd b/man/print.latextoken2.Rd index f83f38d..d548e1a 100644 --- a/man/print.latextoken2.Rd +++ b/man/print.latextoken2.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/utils.R \name{print.latextoken2} \alias{print.latextoken2} -\title{Prints out a parsed LaTeX object, as returned by TeX(..., output='ast'). +\title{Prints out a parsed LaTeX object, as returned by TeX(..., output = 'ast'). This is primarily used for debugging.} \usage{ \method{print}{latextoken2}(x, depth = 0, ...) @@ -15,6 +15,6 @@ This is primarily used for debugging.} \item{...}{(Ignored)} } \description{ -Prints out a parsed LaTeX object, as returned by TeX(..., output='ast'). +Prints out a parsed LaTeX object, as returned by TeX(..., output = 'ast'). This is primarily used for debugging. } diff --git a/man/render_latex.Rd b/man/render_latex.Rd index bd94e0b..27586a1 100644 --- a/man/render_latex.Rd +++ b/man/render_latex.Rd @@ -9,13 +9,13 @@ render_latex(tokens, user_defined = list(), hack_parentheses = FALSE) \arguments{ \item{tokens}{tree of tokens} -\item{user_defined}{any custom definitions of commands passed to \code{\link{TeX}}} +\item{user_defined}{any custom definitions of commands passed to +\code{\link{TeX}}} -\item{hack_parentheses}{render parentheses using \code{group('(', phantom(), '.')} and -\code{group(')', phantom(), '.')}. This is useful to return -valid expressions when the LaTeX source contains mismatched -parentheses, but makes the returned expression much -less tidy.} +\item{hack_parentheses}{render parentheses using +\code{group('(', phantom(), '.')} and \code{group(')', phantom(), '.')}. +This is useful to return valid expressions when the LaTeX source contains +mismatched parentheses, but makes the returned expression much less tidy.} } \value{ String that should be parseable as a valid plotmath expression diff --git a/vignettes/supported-commands.Rmd b/vignettes/supported-commands.Rmd index e55e007..b588c00 100644 --- a/vignettes/supported-commands.Rmd +++ b/vignettes/supported-commands.Rmd @@ -12,7 +12,6 @@ vignette: > ```{r, setup, message=FALSE, echo=FALSE} library(latex2exp) library(reactable) -library(stringr) library(purrr) library(dplyr) library(htmltools) @@ -21,43 +20,43 @@ library(htmltools) ```{r, echo=FALSE} # Saves the plot to PNG and embeds it in an tag using base64 data. # from https://stackoverflow.com/questions/50244709/how-to-store-r-ggplot-graph-as-html-code-snippet -encodeGraphic <- function(g, ..., style="") { - png(tf1 <- tempfile(fileext = ".png"), ...) +encodeGraphic <- function(g, ..., style = "") { + png(tf1 <- tempfile(fileext = ".png"), ...) force(g) dev.off() # Close the file. - txt <- RCurl::base64Encode(readBin(tf1, "raw", file.info(tf1)[1, "size"]), "txt") - myImage <- htmltools::img(src=sprintf("data:image/png;base64,%s", txt), style=style) + txt <- RCurl::base64Encode(readBin(tf1, "raw", file.info(tf1)[1, "size"]), "txt") + myImage <- htmltools::img(src = sprintf("data:image/png;base64,%s", txt), style = style) return(myImage) } supported <- latex2exp_supported() -supported$category <- str_to_title(supported$category) -supported <- supported %>% filter(!is.na(example)) +supported$category <- tools::toTitleCase(supported$category) +supported <- filter(supported, !is.na(example)) supported$command <- supported$example ``` ```{r, echo=FALSE} reactable(supported, - searchable=TRUE, - compact=TRUE, - pagination=FALSE, - style="background-color:inherit", + searchable = TRUE, + compact = TRUE, + pagination = FALSE, + style = "background-color:inherit", columns = list( category = colDef("Category"), - command = colDef("LaTeX command", cell=function(content) { + command = colDef("LaTeX command", cell = function(content) { pre(code("TeX(r\"(", strong(content), ")\")")) }, minWidth = 200), - example = colDef("Example", cell=function(content, ...) { + example = colDef("Example", cell = function(content, ...) { tryCatch({ div( div( - encodeGraphic(plot(TeX(content), cex=4.25), - width=600, height=150, - style="max-height:70px; max-width:100%") + encodeGraphic(plot(TeX(content), cex = 4.25), + width = 600, height = 150, + style = "max-height:70px; max-width:100%") ) ) - }, error=function(e) { + }, error = function(e) { warning("Couldn't render ", content, " because of error ", e) }) }) diff --git a/vignettes/using-latex2exp.Rmd b/vignettes/using-latex2exp.Rmd index 6010edd..15c3122 100644 --- a/vignettes/using-latex2exp.Rmd +++ b/vignettes/using-latex2exp.Rmd @@ -10,10 +10,9 @@ vignette: > \usepackage[utf8]{inputenc} --- ```{r, include=FALSE} -knitr::opts_chunk$set(fig.width=7, fig.height=5, fig.retina=2) +knitr::opts_chunk$set(fig.width = 7, fig.height = 5, fig.retina = 2) library(latex2exp) library(reactable) -library(stringr) library(purrr) library(dplyr) library(ggplot2) @@ -47,42 +46,43 @@ TeX("\\textbf{Euler's identity} is $e^{i\\pi} + 1 = 0$.") You can quickly preview what a translated LaTeX string would look like by using `plot`: ```{r plot-formula, fig.height=2, fig.width=5} -plot(TeX(r'(A $\LaTeX$ formula: $\frac{2hc^2}{\lambda^5}\frac{1}{e^{\frac{hc}{\lambda k_B T}} - 1}$)'), cex=2, main="") +plot(TeX(r'(A $\LaTeX$ formula: $\frac{2hc^2}{\lambda^5}\frac{1}{e^{\frac{hc}{\lambda k_B T}} - 1}$)'), cex = 2, main = "") ``` The following example shows plotting in base graphics: ```{r base-plot, warning=FALSE} -x <- seq(0, 4, length.out=100) +x <- seq(0, 4, length.out = 100) alpha <- 1:5 -plot(x, xlim=c(0, 4), ylim=c(0, 10), - xlab='x', ylab=TeX(r'($\alpha x^\alpha$, where $\alpha \in \{1 \ldots 5\}$)'), - type='n', main=TeX(r'(Using $\LaTeX$ for plotting in base graphics!)', bold=TRUE)) +plot(x, xlim = c(0, 4), ylim = c(0, 10), + xlab = 'x', ylab = TeX(r'($\alpha x^\alpha$, where $\alpha \in \{1 \ldots 5\}$)'), + type = 'n', main = TeX(r'(Using $\LaTeX$ for plotting in base graphics!)', bold = TRUE)) for (a in alpha) { - lines(x, a*x^a, col=a) + lines(x, a*x^a, col = a) } legend('topleft', - legend=TeX(sprintf(r'($\alpha = %d$)', alpha)), - lwd=1, - col=alpha) + legend = TeX(sprintf(r'($\alpha = %d$)', alpha)), + lwd = 1, + col = alpha) ``` This example shows plotting in [ggplot2](https://ggplot2.tidyverse.org): + ```{r ggplot, warning=FALSE} -x <- seq(0, 4, length.out=100) +x <- seq(0, 4, length.out = 100) alpha <- 1:5 -data <- map_df(alpha, ~ tibble(v=.*x^., x=x, alpha=.)) +data <- map_df(alpha, ~ tibble(v = .*x^., x = x, alpha = .)) -p <- ggplot(data, aes(x=x, y=v, color=as.factor(alpha))) + +p <- ggplot(data, aes(x = x, y = v, color = as.factor(alpha))) + geom_line() + ylab(TeX(r'($\alpha x^\alpha$, where $\alpha \in 1\ldots 5$)')) + ggtitle(TeX(r'(Using $\LaTeX$ for plotting in ggplot2. I $\heartsuit$ ggplot!)')) + - coord_cartesian(ylim=c(-1, 10)) + - guides(color=guide_legend(title=NULL)) + - scale_color_discrete(labels=lapply(sprintf(r'($\alpha = %d$)', alpha), TeX)) + coord_cartesian(ylim = c(-1, 10)) + + guides(color = guide_legend(title = NULL)) + + scale_color_discrete(labels = lapply(sprintf(r'($\alpha = %d$)', alpha), TeX)) # Note that ggplot2 legend labels must be lists of expressions, not vectors of expressions print(p) @@ -92,5 +92,5 @@ print(p) Here are a few examples of what you can do with **latex2exp**: ```{r examples, fig.width=7, fig.height=6} -latex2exp_examples(cex=0.9) +latex2exp_examples(cex = 0.9) ```