ggplot2 PDF输出中的Unicode字符

34

我该如何在使用ggplot2创建的PDF图中,使用Unicode字符作为标签、标题和类似的元素?

请考虑下面的示例:

library(ggplot2)
qplot(Sepal.Length, Petal.Length, data=iris, main="Aʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢ")
ggsave("t.pdf")

这个图的标题使用了Unicode字符(小型大写字母),在输出中会显示为...。问题只发生在PDF图中;如果我将最后一行替换为ggsave("t.png"),那么输出就如预期。

我做错了什么?我用的R脚本是UTF-8编码的。一些系统信息:

R version 2.14.1 (2011-12-22)
Platform: x86_64-pc-linux-gnu (64-bit)

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=C                 LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base
当我在寻找解决此问题的方法时,我发现一些证据表明R在像PDF或postscript输出这样的多字节编码中使用单字节编码(例如UTF-8)。我还发现了一些建议,例如能够让欧元符号正常工作,但没有通用的解决方案。

4
cairo_pdf("t.pdf"); ...; dev.off() 对我有效...请参考 https://dev59.com/OW025IYBdhLWcg3wpXod。 - Ben Bolker
3个回答

25

正如Ben所建议的那样,cairo_pdf() 是您的好帮手。此外,它还允许您通过 family 参数在PDF中嵌入非Postscript字体(即TTF/OTF),如果您没有包含所需字形的任何Postscript字体,则这一点至关重要。例如:

library(ggplot2)
cairo_pdf("example.pdf", family="DejaVu Sans")
qplot(Sepal.Length, Petal.Length, data=iris, main="Aʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢ")
dev.off()

提供一个类似于这样的PDF文件: ggplot2图表,标题使用自定义字体家族和非ASCII字符

另请参见此问题;尽管从标题上看似乎不直接相关,但其中有很多关于在R中获取所需字体的内容。

编辑 根据评论的要求,这是特定于Windows的代码:

library(ggplot2)
windowsFonts(myCustomWindowsFontName=windowsFont("DejaVu Sans"))
cairo_pdf("example.pdf", family="myCustomWindowsFontName")
qplot(Sepal.Length, Petal.Length, data=iris, main="Aʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢ")
dev.off()

想要使用基本图形命令cairo_pdf(),只需要首先使用windowsFonts()命令定义字体系列,如上所示即可。当然,请确保您使用的字体实际存在于您的系统中,并且确实具有您所需的所有字形。

下面评论中关于DLL文件的说明是我在Windows上让library(Cairo)中的Cairo()CairoPDF()命令正常工作所必须执行的操作。然后:

library(ggplot2)
library(Cairo)
windowsFonts(myCustomWindowsFontName=windowsFont("DejaVu Sans"))
CairoPDF("example.pdf")
par(family="myCustomWindowsFontName")
qplot(Sepal.Length, Petal.Length, data=iris, main="Aʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢ")
dev.off()

1
在Windows上让Cairo软件包正常运行有点棘手。我写了一份小教程,放在这里:https://raw.github.com/drammock/phonR/master/installingCairo.txt。希望可以有所帮助。 - drammock
1
要在Windows上使用Cairo库,请访问此页面,单击Windows(32/64位),并在“所需的第三方依赖项”下下载zlib、cairo、libpng、fontconfig、freetype和expat的运行时文件。解压缩并收集所有DLL文件,并将它们放入:C:\Program Files\R\R-2.14.0\bin\i386(或在64位系统上为C:\Program Files\R\R-2.14.0\bin\x64)。还需要将fontconfig zip文件中的fonts.conf文件移动到C:\Program Files\R\R-2.14.0\etc\i386\fonts\。在64位系统上,将路径名中的“i386”替换为“x64”;还需要将2.14.0替换为您的R版本号。 - drammock
对不起,我很少使用 Windows ,所以忘记了那个问题。我已经纠正了答案,但我现在没有其他想法(除了“坚持使用 Linux”)。 - drammock
不再是这样了,请参见我的更新 - DomQ
cairo也存在问题,因为它在使用小字间距时性能较差。https://stackoverflow.com/questions/65188058/font-spacing-whern-using-cairo-pdf-device - kennyB
显示剩余8条评论

9
截至2020年和R版本4.0.3,在Mac OS X上,至少在涉及西里尔字母时,cairo_pdf()不再是你的朋友,请参见下面的失败图库。如果必须使用西里尔字母,请返回到传统的png驱动程序。(并且放弃抗锯齿图表)。
R -e 'png(filename = "ftw.png"); library(ggplot2); qplot(Sepal.Length, Petal.Length, data=iris, main="Aʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢ"); dev.off()'
open ftw.png

What is old, is new again.

或者如果您使用knitr与Rmarkdown

R -e 'rmarkdown::render("foo.Rmd", "pdf_document", output_file="foo.pdf", runtime = "static", output_options = list(dev = "png"))'

失败的画廊

如下所示,使用Cairo的“现代”方法在v4.0.3中失败。请注意,这不是(或不仅是)字体嵌入或呈现问题,因为从生成的PDF中选择和粘贴文本也会产生乱码输出。

准备步骤:

  1. 安装最新的R(版本4.0.3或更高版本,并且所有capabilities()都显示TRUE
  2. R -e 'install.packages(c("Cairo", "ggplot2"), repos="https://cloud.r-project.org")'

香草配置

R -e 'library(ggplot2); qplot(Sepal.Length, Petal.Length, data=iris, main="Aʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢ"); ggsave("fail1.pdf")'
open fail1.pdf

Fail Gallery: vanilla config

仅使用cairo_pdf()

R -e 'cairo_pdf("fail2.pdf"); library(ggplot2); qplot(Sepal.Length, Petal.Length, data=iris, main="Aʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢ"); dev.off()'
open fail2.pdf

Fail Gallery: using cairo_pdf() alone

使用自定义(假定符合Unicode标准)字体的cairo_pdf()

R -e 'cairo_pdf("fail3.pdf", family = "Arial Unicode MS"); library(ggplot2); qplot(Sepal.Length, Petal.Length, data=iris, main="Aʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢ"); dev.off()'
open fail3.pdf

This is as close as it gets to working with “modern” approaches.

再试一次Comic Sans字体以确保效果:

R -e 'cairo_pdf("fail3bis.pdf", family = "Comic Sans MS"); library(ggplot2); qplot(Sepal.Length, Petal.Length, data=iris, main="Aʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢ"); dev.off()'
open fail3bis.pdf

Fail Gallery: using cairo_pdf() with family = "MS Comic Sans"

还有一些...

使用旧版的“Dark and Stormy Night”(3.6.2):

/Library/Frameworks/R.framework/Versions/3.6/Resources/bin/R -e 'cairo_pdf("fail4.pdf", family = "Arial Unicode MS"); library(ggplot2); qplot(Sepal.Length, Petal.Length, data=iris, main="Aʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢ"); dev.off()'
open fail4.pdf

enter image description here

使用@drammock建议的DejaVu Sans字体:

R -e 'cairo_pdf("fail5.pdf", family = "DejaVu Sans"); library(ggplot2); qplot(Sepal.Length, Petal.Length, data=iris, main="Aʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢ"); dev.off()'
open fail5.pdf

enter image description here

在旧版 R 上使用 DejaVu Sans:

/Library/Frameworks/R.framework/Versions/3.6/Resources/bin/R -e 'cairo_pdf("fail5bis.pdf", family = "DejaVu Sans"); library(ggplot2); qplot(Sepal.Length, Petal.Length, data=iris, main="Aʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴᴏᴘǫʀsᴛᴜᴠᴡxʏᴢ"); dev.off()'
open fail5bis.pdf

enter image description here


1
仅仅因为某个字体是“Unicode兼容”的并不意味着它在每个代码点上都包含字形。如果您使用我回答中展示的字体(DejaVu Sans),它是否会失败?我回答中的许多小型大写字母字形位于Unicode的“音标扩展”块中,这些字形很可能不存在于Comic Sans或Arial MS中。 - drammock
@drammock DejaVu Sans确实是最接近工作的字体(就成功渲染字形数量而言,与Arial Unicode MS并列)。更新了失败图库。 - DomQ
即使使用DejaVu Sans,它仍然无法正常工作,这很奇怪。 DejaVu Sans字形表的第22页(http://dejavu.sourceforge.net/samples/DejaVuSans.pdf)表明,它确实至少具有ᴄ,ᴅ,ᴇ的字形,但它们对您而言并未出现(我没有检查每个字形)。值得注意的是,所有失败的代码点都是“U+1xxx”,而所有成功的代码点都是“U+0xxx”,因此我怀疑这是编码问题而不是字体问题。 - drammock
我很高兴不是唯一一个遇到这个问题的人。这让我感觉不那么孤单了。 - tjebo

2

如果您正在使用ggsave(...),您可以调用ggsave(..., device=cairo_pdf)

您需要先安装并加载Cairo绑定。

install.packages("Cairo")
library(Cairo)

这里有一个完整的示例(不是我的作品)。


Cairo包与grDevices::cairo_pdf有什么关系? - bers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接