计算特定字体和字体大小的字符串宽度(无需以批处理模式创建任何文件)

Calculate the width of a string in a specific font and font size (without creating any files in batch mode)

有一个类似的问题 here 要求在默认字体大小下字符串的宽度。但是,我想计算特定字体和字体大小的文本字符串的宽度。

Base R strwidth() 函数似乎忽略了对字体和系列参数的更改。观察:

  strwidth("This is cool.", units = "inches", font = 12, family = "sans")
  # [1] 0.9479167
  
  # font 10 gives same result as font 12 
  strwidth("This is cool.", units = "inches", font = 10, family = "sans")
  # [1] 0.9479167
  
  # monospace font gives same result as sans serif 
  strwidth("This is cool.", units = "inches", font = 10, family = "mono")
  # [1] 0.9479167

所以函数似乎没有提取字体和系列参数。

我发现用 par() 设置图形参数可以使 strwidth() 功能正常工作:

  par(ps = 12, family = "sans")
  strwidth("This is cool.", units = "inches")
  # [1] 0.8541667
  
  par(ps = 10, family = "sans")
  strwidth("This is cool.", units = "inches")
  # [1] 0.7291667
  
  par(ps = 10, family = "mono")
  strwidth("This is cool.", units = "inches")
  # [1] 1.083333

而且这些宽度似乎是不错的估计值。

现在的问题是 par() 正在我的项目中创建空的 PDF 文件,因为没有图形设备被传递给它。我的问题是,我没有创建任何图形。我只是想找到字符串的宽度。我不应该设置图形参数来执行此操作。

所以问题仍然存在:如何在 R 中找到特定字体和字体大小的字符串的宽度(不创建任何文件)?

经过几个小时的摸索,我发现了一种使用 R.devices 包的方法:

get_text_width <- function(txt, font, font_size = 10, units = "inches") {
  
  f <- "mono"
  if (tolower(font) == "arial")
    f <- "sans"
  else if (tolower(font) == "times")
    f <- "serif"
  
  R.devices::devEval("nulldev", {
    par(family = f, ps = font_size)
    ret <- strwidth(txt, units = units) 
  })
  
  return(ret)
}

下面是函数的作用:

  get_text_width("This is cool.", "Arial", 12)
  # [1] 0.8798333
  
  get_text_width("This is cool.", "Arial", 10)
  # [1] 0.7331944
  
  get_text_width("This is cool.", "Times", 10)
  # [1] 0.6829167

  get_text_width("This is cool.", "Courier", 10)
  # [1] 1.083333
  
  get_text_width(c("Hello", "Goodbye now!"), "Courier", 10)
  # [1] 0.4166667 1.0000000

我很想知道是否有其他方法可以做到这一点,因为这似乎是一项不必要的工作。我也很想知道这是否适用于 Windows.

以外的其他操作系统

对于批量使用,请考虑在 pdf 帮助页面中对 grDevices 中的文件参数进行的 material 测试-package 默认情况下从作为一部分安装的基础包加载核心套件:

file
a character string giving the file path. If it is of the form "|cmd", the output is piped to the command given by cmd. If it is NULL, then no external file is created (effectively, no drawing occurs), but the device may still be queried (e.g., for size of text).

运行 这个我命名为“parwidth.R”的文件:

pdf(NULL) # and this could be opened with additional parameters
par(ps = 12, family = "sans")
strwidth("This is cool.", units = "inches")
dev.off()

... 来自终端会话 Rscript parwidth.R ...

不生成 'Rplots.pdf' 文件并生成 return 与上面相同的值。我怀疑您最终可能需要查看 pdf 调用的第一个函数的代码,即 getAnywhere(initPSandPDFfonts)。我怀疑它会在 OS 到 OS 之间变化。