记录一个R图像存储的问题,在通过R-studio生成图像时,以pdf格式存储会出现如下错误提示:
Error in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, : invalid font type
同时的warning更加描述直接:
Warning messages:
1: In grid.Call(C_textBounds, as.graphicsAnnot(x$label), ... : font family 'Arial' not found in PostScript font database
2: In grid.Call(C_textBounds, as.graphicsAnnot(x$label), ... : font family 'Arial' not found in PostScript font database
...
无论是ggplot2还是原生graphics都遇到过类似的情况。
特征是直接在页面绘制,包括以png/tiff/jpg等格式存储都是没问题的,就是在pdf存储时包括。
最荒谬的是,有的时候没有问题,偶尔出现一次,非常恶心。
问题来源
核心就是warning里面提示的,(部分)特殊字体/字符不支持的问题,或者说不兼容(其实逻辑很奇怪,PDF/R/系统,分别各自都支持该字体,组合在一起,就会有bug,底层逻辑我还没太搞明白)。
但是其实本质上无论是R还是系统本身,肯定是支持相关字体的,不然是什么方式都可视化不出来,尤其是像我这里的arial,是广泛使用的无衬字体。
问题解决
任意选其中一个就可以解决,具体情况具体尝试
1. 修改字体
直接修改pdf基本都支持的字体就行了,推荐使用万金油字体Helvetica(也是sans),无衬字体,期刊都接受。
graphics系别的图像(包括常见的ComplexHeatmap, UpsetR等)通过par()来修改字体就行了
par(family = "Helvetica")
pdf();plot;dev.off()
par(family="") # 取消字体指定 恢复系统默认字体
ggplot2系列会更加简单,因为它有theme参数可以直接指定想要的字体
p + theme(text = element_text(family = "Helvetica")) # 单图即时生效
theme_bw(base_family = "Helvetica") # 全局临时生效
2. 修改输出模式
不采用pdf输出,从本质上解决问题,当然,许多时候我们就是必须要矢量图结构的图片,所以还是得pdf格式。
所有的chatbot都推荐通过cairo_pdf(),不过我个人测试是没有解决问题。包括ggsave()的方式也是一样的,不改变问题。
大家可以自己测试一下,能不能解决。
3. 注册并使用自定义字体
我个人经验就是上面那些方法有时候没有,尤其是par()修改字体,就是一个笑话,ggplot2::theme()倒是好用。
不兼容的核心就是R没有这些字体,那我加进去就行了。
首先查看系统可用字体,一般这里就可以发现,里面没有想要的字体。
# 基础方法
names(pdfFonts()) # PDF 设备可用字体
names(postscriptFonts()) # PS 设备可用字体
然后我们手动把系统库里面的字体,手动注册到R兼容的环境就行了,推荐用 extrafont(最常用)
install.packages("extrafont")
library(extrafont)
# 第一次使用需要导入系统字体(只需运行一次,较慢)
font_import()
# 加载字体
loadfonts()
# 验证 Arial 是否进来了
"Arial" %in% fonts()
# 现在就可以用了
names(pdfFonts()) # 应该能看到 Arial 了
现在PDF就可以成功保存了。
但是这个办法仍然不是最终解决这个问题,仍然有问题
产出的pdf图中的字体看起来像 Serif(衬线体),有点像 Times New Roman 或 Computer Modern,而 我RStudio 预览窗口默认用的是无衬线字体(类 Helvetica/Arial),后者才是我想要的。
Claude给的推测原因 -- PDF 设备和屏幕设备用的字体系统不同:
设备默认字体RStudio 预览(屏幕)Helvetica / Arial(无衬线)pdf() 设备Helvetica 理论上也是,但实际回退到 Type1 字体,看起来像衬线体cairo_pdf() 设备跟随系统字体
你图里那个字体很可能是 PDF 设备找不到 Helvetica 时自动回退到了 Times 或类似的 Type1 字体
不过问题不大了,反正我只需要图像是矢量的,字体格式我可以Adobe改。