[Rd] Unicode substitutions for Rd2txt \[d]eqn{}

Kurt Hornik Kurt@Horn|k @end|ng |rom wu@@c@@t
Wed May 13 15:32:26 CEST 2026


>>>>> Ivan Krylov via R-devel writes:

I like your patch :-)

Perhaps do this conditionally on Rd2txt_options()$unicode_symbols set to 
TRUE?

Best
-k

> Hello R-devel,
> The idea to use Unicode when rendering equations in plain text was
> mentioned a few times in the past, most recently by Sebastian Meyer in
> a Bugzilla comment. I would like to share the patch to use Unicode
> characters for approximately the same symbols that used to be
> substituted in the old HTML equation renderer.

> Why do it? Familiar Greek letters would be more consistent with HTML
> and PDF documentation.

> What are the downsides?

>  - Some symbols don't look as good on a terminal as they do in a
>    document when typeset with a mathematical font. Depending on the
>    font used in the terminal, \pi → π may look very similar to n. (For
>    example, the popular Terminus font uses exactly the same glyph for
>    'π', U+03C0 GREEK SMALL LETTER PI, and 'п', U+043F CYRILLIC SMALL
>    LETTER PE.) \infty → '∞' and \dots → '…' are too small and narrow
>    when rendered at the same width as normal monospace letters. '√',
>    'Σ'/'∑', and 'Π'/'∏' may look too unexpected compared to 'sqrt',
>    'sum', and 'prod'.

>  - There may be accessibility implications. Would a screen reader
>    struggle upon seeing a 'Π' instead of 'prod'? Hopefully the screen
>    reader users can enjoy the more accessible HTML documentation now,
>    and there is an option to disable the Unicode symbols.

>  - The patch is only mildly tested by reading ?stats::TDist and
>    ?pracma::expint and making sure that for (pkg in
>    rownames(installed.packages())) for (Rd in tools::Rd_db(pkg))
>    tools::Rd2txt(Rd) doesn't fail, with or without a UTF-8-capable
>    locale.

> Thanks to Sebastian Meyer for the initial comments!

> Index: src/library/tools/R/Rd2HTML.R
> ===================================================================
> --- src/library/tools/R/Rd2HTML.R	(revision 90047)
> +++ src/library/tools/R/Rd2HTML.R	(working copy)
> @@ -109,14 +109,13 @@
>      x <- fsub('"\\{"', '"{"', x)
>      ## http://htmlhelp.com/reference/html40/entities/symbols.html
>      if(inEqn) {
> -        x <- psub("\\\\(Alpha|Beta|Gamma|Delta|Epsilon|Zeta|Eta|Theta|Iota|Kappa|Lambda|Mu|Nu|Xi|Omicron|Pi|Rho|Sigma|Tau|Upsilon|Phi|Chi|Psi|Omega|alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|sum|prod)", "&\\1;", x)
> -        x <- psub("\\\\(dots|ldots)", "…", x)
> -        x <- psub("\\\\(left|right)", "", x)
> -        x <- psub("\\\\leq?", "≤", x)
> -        x <- psub("\\\\geq?", "≥", x)
> -        x <- psub("\\\\neq?", "≠", x)
> -        x <- fsub("\\infty", "∞", x)
> -        x <- fsub("\\sqrt", "√", x)
> +        rx <- paste0("\\\\(",
> +                     paste(math_replacements[,"name"], collapse = "|"),
> +                     ")(?![a-zA-Z])")
> +        m <- gregexec(rx, x, perl = TRUE)
> +        ii <- lapply(regmatches(x, m),
> +                     function(mm) if (length(mm)) match(mm[2,], math_replacements[,"name"]))
> +        regmatches(x, gregexpr(rx, x, perl = TRUE)) <- lapply(ii, function(i) math_replacements[i, "html"])
>      }
>      x
>  }
> Index: src/library/tools/R/Rd2txt.R
> ===================================================================
> --- src/library/tools/R/Rd2txt.R	(revision 90047)
> +++ src/library/tools/R/Rd2txt.R	(working copy)
> @@ -55,6 +55,71 @@
>      }
>  })
 
> +math_replacements <- matrix(c(
> +    "Alpha",   "\u391",  "Alpha",   "Α",
> +    "Beta",    "\u392",  "Beta",    "Β",
> +    "Gamma",   "\u393",  "Gamma",   "Γ",
> +    "Delta",   "\u394",  "Delta",   "Δ",
> +    "Epsilon", "\u395",  "Epsilon", "Ε",
> +    "Zeta",    "\u396",  "Zeta",    "Ζ",
> +    "Eta",     "\u397",  "Eta",     "Η",
> +    "Theta",   "\u398",  "Theta",   "Θ",
> +    "Iota",    "\u399",  "Iota",    "Ι",
> +    "Kappa",   "\u39a",  "Kappa",   "Κ",
> +    "Lambda",  "\u39b",  "Lambda",  "Λ",
> +    "Mu",      "\u39c",  "Mu",      "Μ",
> +    "Nu",      "\u39d",  "Nu",      "Ν",
> +    "Xi",      "\u39e",  "Xi",      "Ξ",
> +    "Omicron", "\u39f",  "Omicron", "Ο",
> +    "Pi",      "\u3a0",  "Pi",      "Π",
> +    "Rho",     "\u3a1",  "Rho",     "Ρ",
> +    "Sigma",   "\u3a3",  "Sigma",   "Σ",
> +    "Tau",     "\u3a4",  "Tau",     "Τ",
> +    "Upsilon", "\u3a5",  "Upsilon", "Υ",
> +    "Phi",     "\u3a6",  "Phi",     "Φ",
> +    "Chi",     "\u3a7",  "Chi",     "Χ",
> +    "Psi",     "\u3a8",  "Psi",     "Ψ",
> +    "Omega",   "\u3a9",  "Omega",   "Ω",
> +    "alpha",   "\u3b1",  "alpha",   "α",
> +    "beta",    "\u3b2",  "beta",    "β",
> +    "gamma",   "\u3b3",  "gamma",   "γ",
> +    "delta",   "\u3b4",  "delta",   "δ",
> +    "epsilon", "\u3b5",  "epsilon", "ε",
> +    "zeta",    "\u3b6",  "zeta",    "ζ",
> +    "eta",     "\u3b7",  "eta",     "η",
> +    "theta",   "\u3b8",  "theta",   "θ",
> +    "iota",    "\u3b9",  "iota",    "ι",
> +    "kappa",   "\u3ba",  "kappa",   "κ",
> +    "lambda",  "\u3bb",  "lambda",  "λ",
> +    "mu",      "\u3bc",  "mu",      "μ",
> +    "nu",      "\u3bd",  "nu",      "ν",
> +    "xi",      "\u3be",  "xi",      "ξ",
> +    "omicron", "\u3bf",  "omicron", "ο",
> +    "pi",      "\u3c0",  "pi",      "π",
> +    "rho",     "\u3c1",  "rho",     "ρ",
> +    "sigma",   "\u3c3",  "sigma",   "σ",
> +    "tau",     "\u3c4",  "tau",     "τ",
> +    "upsilon", "\u3c5",  "upsilon", "υ",
> +    "phi",     "\u3c6",  "phi",     "φ",
> +    "chi",     "\u3c7",  "chi",     "χ",
> +    "psi",     "\u3c8",  "psi",     "ψ",
> +    "omega",   "\u3c9",  "omega",   "ω",
> +    "sum",     "\u2211", "sum",     "∑",
> +    "prod",    "\u220f", "prod",    "∏",
> +    "sqrt",    "\u221a", "sqrt",    "√",
> +    "dots",    "\u2026", "...",     "…",
> +    "ldots",   "\u2026", "...",     "…",
> +    "le",      "\u2264", "<=",      "≤",
> +    "leq",     "\u2264", "<=",      "≤",
> +    "ge",      "\u2265", ">=",      "≥",
> +    "geq",     "\u2265", ">=",      "≥",
> +    "ne",      "\u2260", "!=",      "≠",
> +    "neq",     "\u2260", "!=",      "≠",
> +    "infty",   "\u221e", "Inf",     "∞",
> +    "left",    "",       "",        "",
> +    "right",   "",       "",        ""
> +), ncol = 4, byrow = TRUE, dimnames = list(NULL, c("name", "fancy", "ascii", "html")))
> +
>  transformMethod <- function(i, blocks, Rdfile) {
>      editblock <- function(block, newtext)
>      	list(tagged(newtext,
> @@ -529,13 +594,14 @@
>      }
 
>      txt_eqn <- function(x) {
> -        x <- psub("\\\\(Alpha|Beta|Gamma|Delta|Epsilon|Zeta|Eta|Theta|Iota|Kappa|Lambda|Mu|Nu|Xi|Omicron|Pi|Rho|Sigma|Tau|Upsilon|Phi|Chi|Psi|Omega|alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega|sum|prod|sqrt)", "\\1", x)
> -        x <- psub("\\\\(dots|ldots)", "...", x)
> -        x <- psub("\\\\(left|right)", "", x)
> -        x <- psub("\\\\leq?", "<=", x)
> -        x <- psub("\\\\geq?", ">=", x)
> -        x <- psub("\\\\neq?", "!=", x)
> -        x <- fsub("\\infty", "Inf", x)
> +        replacement <- if (unicode_symbols) "fancy" else "ascii"
> +        rx <- paste0("\\\\(",
> +                     paste(math_replacements[,"name"], collapse = "|"),
> +                     ")(?![a-zA-Z])")
> +        m <- gregexec(rx, x, perl = TRUE)
> +        ii <- lapply(regmatches(x, m),
> +                     function(mm) if (length(mm)) match(mm[2,], math_replacements[,"name"]))
> +        regmatches(x, gregexpr(rx, x, perl = TRUE)) <- lapply(ii, function(i) math_replacements[i, replacement])
>          ## FIXME: are these needed?
>          x <- psub("\\\\(bold|strong|emph|var)\\{([^}]*)\\}", "\\2", x)
>          x <- psub("\\\\(code|samp)\\{([^}]*)\\}", "'\\2'", x)

> -- 
> Best regards,
> Ivan

> ______________________________________________
> R-devel using r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel



More information about the R-devel mailing list