diff --git a/DESCRIPTION b/DESCRIPTION index cddade8..d17e341 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: tables Title: Formula-Driven Table Generation -Version: 0.9.32 +Version: 0.9.33 Authors@R: person(given = "Duncan", family = "Murdoch", role = c("aut", "cre"), diff --git a/NEWS.md b/NEWS.md index 117dfc5..875fd7c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,12 @@ -# tables 0.9.32 + +# tables 0.9.33 - The fix for issue #30 was incomplete for some reason. +- `table_options()` now returns the value of options if called +with a character argument. +- An option `escape` has been added to `table_options()`. If `TRUE`, +any special characters in HTML or LaTeX output are escaped so they +appear as-is. # tables 0.9.31 diff --git a/R/All.R b/R/All.R index 3483858..e6475bd 100644 --- a/R/All.R +++ b/R/All.R @@ -9,7 +9,7 @@ All <- function(df, texify = getOption("tables.texify", FALSE)) { names <- colnames(df) if (texify) - names <- Hmisc::latexTranslate(names) + names <- texify(names) f <- NULL for (i in seq_along(names)) { diff --git a/R/escapes.R b/R/escapes.R new file mode 100644 index 0000000..9eb259e --- /dev/null +++ b/R/escapes.R @@ -0,0 +1,37 @@ +texify <- function(x) { + # Based on knitr function + x <- gsub("\\\\", "\\\\textbackslash", x) + x <- gsub("([#$%&_{}])", "\\\\\\1", x) + x <- gsub("\\\\textbackslash", "\\\\textbackslash{}", x) + x <- gsub("~", "\\\\textasciitilde{}", x) + x <- gsub("\\^", "\\\\textasciicircum{}", x) + x +} + +htmlify <- function (x) + # Taken from the tools package +{ + fsub <- function(pattern, replacement, x) + gsub(pattern, + replacement, + x, + fixed = TRUE, + useBytes = TRUE) + + x <- fsub("&", "&", x) + x <- fsub("---", "—", x) + x <- fsub("--", "–", x) + x <- fsub("``", "“", x) + x <- fsub("''", "”", x) + x <- gsub("`([^']+)'", + "‘\\1’", + x, + perl = TRUE, + useBytes = TRUE) + x <- fsub("`", "'", x) + x <- fsub("<", "<", x) + x <- fsub(">", ">", x) + x <- fsub("\"\\{\"", "\"{\"", x) + x <- fsub("\"", """, x) + x +} diff --git a/R/html.tabular.R b/R/html.tabular.R index f9cf233..072e617 100644 --- a/R/html.tabular.R +++ b/R/html.tabular.R @@ -1,75 +1,74 @@ -htmlify <- function (x) # Taken from the tools package -{ - fsub <- function(pattern, replacement, x) - gsub(pattern, replacement, x, fixed=TRUE, useBytes=TRUE) - - x <- fsub("&", "&", x) - x <- fsub("---", "—", x) - x <- fsub("--", "–", x) - x <- fsub("``", "“", x) - x <- fsub("''", "”", x) - x <- gsub("`([^']+)'", "‘\\1’", x, perl=TRUE, useBytes=TRUE) - x <- fsub("`", "'", x) - x <- fsub("<", "<", x) - x <- fsub(">", ">", x) - x <- fsub("\"\\{\"", "\"{\"", x) - x <- fsub("\"", """, x) - x -} - -htmlNumeric <- function(chars, minus=TRUE, leftpad=TRUE, rightpad=TRUE) { - regexp <- "^( *)([-]?)([^ -][^ ]*)( *)$" - leadin <- sub(regexp, "\\1", chars) - sign <- sub(regexp, "\\2", chars) - rest <- sub(regexp, "\\3", chars) - tail <- sub(regexp, "\\4", chars) - - figurespace <- " " - minussign <- "−" - - if (minus && any(neg <- sign == "-")) { - if (any(leadin[!neg] == "")) - leadin <- sub("^", " ", leadin) - leadin[!neg] <- sub(" ", "", leadin[!neg]) - sign[!neg] <- figurespace - sign[neg] <- minussign - } - if (leftpad && any(ind <- leadin != "")) - leadin[ind] <- gsub(" ", figurespace, leadin[ind]) - - if (rightpad && any(ind <- tail != "")) - tail[ind] <- gsub(" ", figurespace, tail[ind]) - - paste(leadin, sign, rest, tail, sep="") +htmlNumeric <- function(chars, + minus = TRUE, + leftpad = TRUE, + rightpad = TRUE) { + regexp <- "^( *)([-]?)([^ -][^ ]*)( *)$" + leadin <- sub(regexp, "\\1", chars) + sign <- sub(regexp, "\\2", chars) + rest <- sub(regexp, "\\3", chars) + tail <- sub(regexp, "\\4", chars) + + figurespace <- " " + minussign <- "−" + + if (minus && any(neg <- sign == "-")) { + if (any(leadin[!neg] == "")) + leadin <- sub("^", " ", leadin) + leadin[!neg] <- sub(" ", "", leadin[!neg]) + sign[!neg] <- figurespace + sign[neg] <- minussign + } + if (leftpad && any(ind <- leadin != "")) + leadin[ind] <- gsub(" ", figurespace, leadin[ind]) + + if (rightpad && any(ind <- tail != "")) + tail[ind] <- gsub(" ", figurespace, tail[ind]) + + paste(leadin, sign, rest, tail, sep = "") } -CSSclassname <- function(just) - ifelse(just == "l", "left", - ifelse(just == "c", "center", - ifelse(just == "r", "right", just))) - -toHTML <- function(object, file = "", - options = NULL, id = NULL, - append = FALSE, - browsable = TRUE, ...) { +CSSclassname <- function(just) + ifelse(just == "l", "left", ifelse(just == "c", "center", ifelse(just == "r", "right", just))) +toHTML <- function(object, + file = "", + options = NULL, + id = NULL, + append = FALSE, + browsable = TRUE, + ...) { if (!is.null(options)) { saveopts <- do.call(table_options, options) - on.exit(table_options(saveopts), add=TRUE) + on.exit(table_options(saveopts), add = TRUE) } opts <- table_options() output <- character() - mycat <- function(...) output <<- c(output, unlist(list(...))) + mycat <- function(...) + output <<- c(output, unlist(list(...))) + + escape <- opts$escape + + do_escape <- function(x) { + if (escape) + x <- htmlify(x) + x + } defjust <- opts$justification blankhead <- "  \n" - classes <- chars <- format(object, html = TRUE, minus = opts$HTMLminus, - leftpad = opts$HTMLleftpad, - rightpad = opts$HTMLrightpad, ...) # format without justification + classes <- chars <- format( + object, + html = TRUE, + minus = opts$HTMLminus, + leftpad = opts$HTMLleftpad, + rightpad = opts$HTMLrightpad, + escape = escape, + ... + ) # format without justification classes[] <- "" vjust <- attr(object, "justification") @@ -79,7 +78,7 @@ toHTML <- function(object, file = "", chars[chars == ""] <- " " chars[] <- sprintf(" %s\n", classes, chars) - rowClasses <- rowLabels <- attr(object, "rowLabels") + rowClasses <- rowLabels <- do_escape(attr(object, "rowLabels")) rowClasses[] <- "" nleading <- ncol(rowLabels) rowLabels[is.na(rowLabels)] <- " " @@ -87,19 +86,19 @@ toHTML <- function(object, file = "", rjust[is.na(rjust)] <- opts$rowlabeljustification ind <- rjust != defjust rowClasses[ind] <- sprintf(' class="%s"', CSSclassname(rjust[ind])) - rowLabels[] <- sprintf( " %s\n", rowClasses, rowLabels) + rowLabels[] <- sprintf(" %s\n", rowClasses, rowLabels) colnamejust <- attr(rowLabels, "colnamejust") - colnamejust <- rep(colnamejust, length.out=nleading) + colnamejust <- rep(colnamejust, length.out = nleading) colnameClasses <- colnames(rowLabels) colnameClasses[] <- "" ind <- is.na(colnamejust) colnamejust[ind] <- defjust ind <- colnamejust != defjust colnameClasses[ind] <- sprintf(' class="%s"', CSSclassname(colnamejust[ind])) - colnames(rowLabels) <- sprintf(" %s\n", colnameClasses, colnames(rowLabels)) + colnames(rowLabels) <- sprintf(" %s\n", colnameClasses, do_escape(colnames(rowLabels))) - clabels <- attr(object, "colLabels") + clabels <- do_escape(attr(object, "colLabels")) cjust <- attr(clabels, "justification") ind <- is.na(cjust) cjust[ind] <- defjust @@ -107,29 +106,34 @@ toHTML <- function(object, file = "", multi <- matrix(0, nrow(clabels), ncol(clabels)) prevmulti <- rep(0, nrow(multi)) for (i in rev(seq_len(ncol(multi)))) { - ind <- is.na(clabels[,i]) + ind <- is.na(clabels[, i]) multi[!ind, i] <- 1 + prevmulti[!ind] prevmulti[ind] <- 1 + prevmulti[ind] prevmulti[!ind] <- 0 } colspan <- ifelse(multi < 2, "", sprintf(' colspan="%d"', multi)) - class <- ifelse(cjust == defjust | multi == 0, "", sprintf(' class="%s"', CSSclassname(cjust))) + class <- ifelse(cjust == defjust | + multi == 0, + "", + sprintf(' class="%s"', CSSclassname(cjust))) clabels[clabels == ""] <- " " - clabels <- ifelse(multi == 0, "", sprintf(' %s\n', colspan, class, clabels)) + clabels <- ifelse(multi == 0, + "", + sprintf(' %s\n', colspan, class, clabels)) rowLabelHeadings <- matrix(blankhead, nrow(clabels), ncol(rowLabels)) - rowLabelHeadings[nrow(clabels),] <- colnames(rowLabels) + rowLabelHeadings[nrow(clabels), ] <- colnames(rowLabels) if (opts$doHTMLheader) { - head <- sub("CHARSET", localeToCharset(), opts$HTMLhead, fixed=TRUE) + head <- sub("CHARSET", localeToCharset(), opts$HTMLhead, fixed = TRUE) mycat(head) } if (opts$doCSS) { - if (is.null(id)) - css <- gsub("#ID ", "", opts$CSS, fixed=TRUE) + if (is.null(id)) + css <- gsub("#ID ", "", opts$CSS, fixed = TRUE) else - css <- gsub("#ID", paste0("#", id), opts$CSS, fixed=TRUE) + css <- gsub("#ID", paste0("#", id), opts$CSS, fixed = TRUE) mycat(css) } @@ -137,7 +141,7 @@ toHTML <- function(object, file = "", mycat(opts$HTMLbody) if (opts$doBegin) { - if (is.null(id)) + if (is.null(id)) id <- "" else id <- sprintf(' id="%s"', id) @@ -147,7 +151,7 @@ toHTML <- function(object, file = "", mycat(sprintf('%s\n', opts$HTMLcaption)) if (opts$doHeader) { - rows <- apply(cbind(rowLabelHeadings, clabels), 1, paste0, collapse="") + rows <- apply(cbind(rowLabelHeadings, clabels), 1, paste0, collapse = "") mycat('\n') mycat(sprintf('\n%s\n', CSSclassname(defjust), rows)) mycat('\n') @@ -158,7 +162,7 @@ toHTML <- function(object, file = "", mycat('\n') } if (opts$doBody) { - rows <- apply(cbind(rowLabels, chars), 1, paste0, collapse="") + rows <- apply(cbind(rowLabels, chars), 1, paste0, collapse = "") mycat('\n') mycat(sprintf('\n%s\n', CSSclassname(defjust), rows)) mycat('\n') @@ -169,7 +173,10 @@ toHTML <- function(object, file = "", result <- browsable(HTML(output), value = browsable) if (!identical(file, "")) { if (is.character(file)) { - file <- file(file, open = if (append) "at" else "wt") + file <- file(file, open = if (append) + "at" + else + "wt") on.exit(close(file)) } writeLines(output, file) @@ -179,14 +186,13 @@ toHTML <- function(object, file = "", } html.tabular <- function(object, ...) { - toHTML(object, ...) + toHTML(object, ...) } writeCSS <- function(CSS = htmloptions()$CSS, id = NULL) { - if (is.null(id)) - css <- gsub("#ID ", "", CSS, fixed=TRUE) + if (is.null(id)) + css <- gsub("#ID ", "", CSS, fixed = TRUE) else - css <- gsub("#ID", paste0("#", id), CSS, fixed=TRUE) + css <- gsub("#ID", paste0("#", id), CSS, fixed = TRUE) cat(css) } - diff --git a/R/latex.tabular.R b/R/latex.tabular.R index f9ed28b..202afec 100644 --- a/R/latex.tabular.R +++ b/R/latex.tabular.R @@ -1,12 +1,7 @@ -texify <- function(x) { - x <- gsub("\\", "\\textbackslash{}", x, fixed = TRUE) - if (requireNamespace("Hmisc")) - x <- Hmisc::latexTranslate(x) - x -} toLatex.tabular <- function(object, file = "", options = NULL, - append = FALSE, ...) { + append = FALSE, + ...) { if (!is.null(options)) { saveopts <- do.call(table_options, options) @@ -24,9 +19,18 @@ toLatex.tabular <- function(object, file = "", options = NULL, output <<- paste0(output, paste(args, collapse = sep)) } + escape <- opts$escape + + do_escape <- function(x) { + if (escape) + x <- texify(x) + x + } + chars <- format(object, latex = TRUE, minus = opts$latexminus, leftpad = opts$latexleftpad, - rightpad = opts$latexrightpad,...) # format without justification + rightpad = opts$latexrightpad, + ...) # format without justification vjust <- attr(object, "justification") vjustdefs <- rep(opts$justification, length.out=ncol(object)) @@ -34,7 +38,7 @@ toLatex.tabular <- function(object, file = "", options = NULL, chars[ind] <- sprintf("\\multicolumn{1}{%s}{%s}", vjust[ind], chars[ind]) - rowLabels <- attr(object, "rowLabels") + rowLabels <- do_escape(attr(object, "rowLabels")) nleading <- ncol(rowLabels) rowLabels[is.na(rowLabels)] <- "" rjust <- attr(rowLabels, "justification") @@ -49,8 +53,8 @@ toLatex.tabular <- function(object, file = "", options = NULL, colnamejust[ind] <- rjustdefs[ind] ind <- colnamejust != rjustdefs colnames(rowLabels)[ind] <- sprintf("\\multicolumn{1}{%s}{%s}", - colnamejust[ind], colnames(rowLabels)[ind]) - clabels <- attr(object, "colLabels") + colnamejust[ind], do_escape(colnames(rowLabels)[ind])) + clabels <- do_escape(attr(object, "colLabels")) leadin <- paste(rep("&", max(nleading - 1, 0)), collapse=" ") cjust <- attr(clabels, "justification") ind <- is.na(cjust) diff --git a/R/options.R b/R/options.R index 6ae7f59..871e570 100644 --- a/R/options.R +++ b/R/options.R @@ -11,7 +11,7 @@ CSSdefault <- '\n" +[1] "\n" $HTMLhead [1] "\n\n\n\n" @@ -164,6 +164,9 @@ $HTMLrightpad $HTMLminus [1] FALSE +$escape +[1] FALSE + > table_options()[c("toprule", "midrule", "bottomrule", + "titlerule")] @@ -780,7 +783,6 @@ $i$ & Sepal.Length & Sepal.Width & Petal.Length & Petal.Width & \multicolumn{1} > options(tables.texify = TRUE) > toLatex(tabular(mean ~ Factor(A) * All(df), data = df)) -Loading required namespace: Hmisc \begin{tabular}{lcc} \toprule & \multicolumn{2}{c}{A} \\ \cmidrule(lr){2-3} @@ -983,4 +985,4 @@ All & $150$ & $5.84$ & $0.83$ & $3.06$ & $0.44$ \\ *** Run successfully completed *** > proc.time() user system elapsed - 0.688 0.030 0.718 + 0.561 0.024 0.584