一个简单的问题:有没有一种方法可以从R在pdf文件中绘制图形并包含工具提示?
最佳答案
Simple question: Is there a way to plot a graph from R in a pdf file and include tooltips?
总有办法。但是细节决定成败,所以真正的问题是:您是否愿意弄脏您的手?
PDF确实支持某些对象的工具提示,例如超链接。因此,如果有一种方法可以插入原始的PDF语句,表明在绘图中的某个位置应该有一个类似于超链接的对象,那么有一种方法可以弹出工具提示。
现在,我知道生成和编译原始PDF语句的唯一方法是使用TeX创建文档。肯定还有其他方法可以做到,但这是我熟悉的方法。以下示例使用了我和Cameron Bracken编写的图形设备tikzDevice,将R图形渲染为LaTeX代码。 tikzDevice初步支持通过
tikzAnnotate
函数将任意LaTeX代码注入(inject)图形输出流中-我们将使用它来将PDF标注放入绘图中。涉及的步骤是:
tikzAnnotate
在图中的特定点调用LaTeX宏。 在下面的示例中,第2步附加了一个主要警告。使用的坐标计算仅适用于基本图形,不适用于诸如ggplot2 之类的网格图形。
简单的工具提示
第1步
tikzDevice允许您创建R图形,其中包括执行任意LaTeX命令。通常,这样做是为了将诸如
$\alpha$
之类的内容插入情节标题中,以生成希腊字母,α或复杂方程式。在这里,我们将使用此功能来调用一些原始的PDF伏都教。您需要通过设置
tikzLatexPackages
选项预先定义tikzDevice图形生成期间希望使用的所有LaTeX宏。在这里,我们将向该声明添加一些内容:require(tikzDevice) # So that default options are set
options(tikzLatexPackages = c(
getOption('tikzLatexPackages'), # The original contents: required stuff
# Avert your eyes for a sec, all will be explained below
"\\def\\tooltiptarget{\\phantom{\\rule{1mm}{1mm}}}",
"\\newbox\\tempboxa\\setbox\\tempboxa=\\hbox{}\\immediate\\pdfxform\\tempboxa \\edef\\emptyicon{\\the\\pdflastxform}",
"\\newcommand\\tooltip[1]{\\pdfstartlink user{/Subtype /Text/Contents (#1)/AP <</N \\emptyicon\\space 0 R >>}\\tooltiptarget\\pdfendlink}"
))
如果所有引用废话的人都将由关心可读性的人写为LaTeX代码,它将看起来像这样:
\def\tooltiptarget{\phantom{\rule{1mm}{1mm}}}
\newbox\tempboxa
\setbox\tempboxa=\hbox{}
\immediate\pdfxform\tempboxa
\edef\emptyicon{\the\pdflastxform}
\newcommand\tooltip[1]{%
\pdfstartlink user{%
/Subtype /Text
/Contents (#1)
/AP <<
/N \emptyicon\space 0 R
>>
}%
\tooltiptarget%
\pdfendlink%
}
对于那些从未到过狂放而在TeX中完成过一些“编程”工作的程序员,以下是上述代码的一击(据我所知,TeX可能会变得很奇怪-尤其是当您会因此而陷入困境):
tooltiptarget
,它是不可见的(幻像),是1mm x 1mm的矩形(规则)。这是屏幕区域,我们将使用它来检测鼠标悬停。 tempboxa
。 tempboxa
的内容以包含一个空框,该框使用水平布局排列其内容(不重要,可以使用vbox或其他框)。 tempboxa
的内容创建PDF表单XObject。 PDF文件可使用Form XObject来存储可能反复使用的图形(如徽标)。在这里,我们创建了一个“空白图标”,以后可以用来减少视觉困惑。 TeX会延迟输出操作(例如将对象写入PDF文件),直到满足某些条件为止,例如页面已满。立即确保不推迟此操作。 emptyicon
。 #1
。宏中的每一行都以注释字符%
结尾,以防止TeX注意到为可读性而添加的换行符(换行符在宏内部可能产生奇怪的影响)。 \Type \Annot
),该对象大约有20种不同的子类型,其中包括超链接。其后的每一行都包含带有两个TeX宏的原始PDF标记。 #1
(工具提示宏的参数)。 \AP << >>
)将“常规”注释图标(\N
)设置为我们先前创建的空白图标。我们与emptyicon
一起存储在0 R
中的整数形成对我们在第4行使用空框形成的Form XObject的引用。 tooltiptarget
(我们的不可见幻象对象),该对象不会显示在屏幕上,但会对鼠标悬停使用react。 第2步
好了,现在我们已经告诉LaTeX如何创建工具提示,现在该使它们在R中可用了。这涉及编写一个函数,该函数将在我们的图形上获取坐标,例如(1,1),并将其转换为 Canvas 或“设备”坐标。在tikzDevice的情况下,所需的测量值为距绘图区域绝对左下角的“TeX点”(1 / 72.27英寸)。幸运的是,对于基本图形,有一些方便的函数可以为我们计算出来。网格图形的工作方式不同,因此此处示例中采用的方法不适用于它们。
R函数的最后一个任务是调用
tikzAnnotate
,将TikZ“节点”插入到位于我们计算出的坐标处的输出流中。节点可以包含任意TeX命令,在这种情况下,我们将调用tooltip
。这是包含上述功能的R函数:
place_PDF_tooltip <- function(x, y, text){
# Calculate coordinates
tikzX <- round(grconvertX(x, to = "device"), 2)
tikzY <- round(grconvertY(y, to = "device"), 2)
# Insert node
tikzAnnotate(paste(
"\\node at (", tikzX, ",", tikzY, ") ",
"{\\tooltip{", text, "}};",
sep = ''
))
invisible()
}
第三步
在剧情上尝试一下:
# standAlone creates a complete LaTeX document. Default output makes
# stripped-down graphs ment for inclusion in other LaTeX documents
tikz('tooltips_ahoy.tex', standAlone = TRUE)
plot(1,1)
place_PDF_tooltip(1,1, 'Hello, World!')
dev.off()
require(tools)
texi2dvi('tooltips_ahoy.tex', pdf = TRUE)
步骤4
看结果(download a pdf):
进阶工具提示
第1步
那么,既然我们已经有了一些简单的工具提示,为什么不将其提高到11?在前面的示例中,我们使用一个空的
hbox
摆脱了工具提示图标。但是,如果我们在该框中放了一些东西,例如文本或图形,该怎么办?如果有一种方法可以使图标仅在鼠标悬停事件期间出现,该怎么办?下面的TeX宏的边缘有点粗糙,但这表明这是可能的:
\usetikzlibrary{shapes.callouts}
\tikzset{tooltip/.style = {
rectangle callout,
draw,
callout absolute pointer = {(-2em, 1em)}
}}
\def\tooltiptarget{\phantom{\rule{1mm}{1mm}}}
\newbox\tempboxa
\newcommand\tooltip[1]{%
\def\tooltipcallout{\tikz{\node[tooltip]{#1};}}%
\setbox\tempboxa=\hbox{\phantom{\tooltipcallout}}%
\immediate\pdfxform\tempboxa%
\edef\emptyicon{\the\pdflastxform}%
\setbox\tempboxa=\hbox{\tooltipcallout}%
\immediate\pdfxform\tempboxa%
\edef\tooltipicon{\the\pdflastxform}%
\pdfstartlink user{%
/Subtype /Text
/Contents (#1)
/AP <<
/N \emptyicon\space 0 R
/R \tooltipicon\space 0 R
>>
}%
\tooltiptarget%
\pdfendlink%
}
与简单标注相比,进行了以下修改。
shapes.callouts
库,其中包含供TikZ在绘制标注框时使用的模板。 tooltip
样式,其中包含一些TikZ图形样板。它指定一个可见的矩形标注框(draw
)。 callout absolute pointer
业务是一个黑客,因为到此为止我已经喝了太多啤酒,无法弄清楚如何使用动态生成的PDF原语放置注释图标。这依赖于图标左上角的默认 anchor 定,因此将标注框的指针拉向该位置。结果是这些框将始终显示在指针的右下角,并且如果标注文本足够长,它们将看起来不正确。 tikz
宏中的单发ojit_code命令生成工具提示。表格XObject由tooltipcallout
生成并分配给tooltipcallout
。 tooltipicon
也可以通过评估emptyicon
内部的tooltipcallout
来动态生成。这是必需的,因为默认图标的大小显然设置了可用于翻转图标的视口(viewport)。 phantom
引用的XObject将新行添加到/AP
数组/R
进行翻转。 现成的R版本是:
require(tikzDevice)
options(tikzLatexPackages = c(
getOption('tikzLatexPackages'),
"\\usetikzlibrary{shapes.callouts}",
"\\tikzset{tooltip/.style = {rectangle callout,draw,callout absolute pointer = {(-2em, 1em)}}}",
"\\def\\tooltiptarget{\\phantom{\\rule{1mm}{1mm}}}",
"\\newbox\\tempboxa",
"\\newcommand\\tooltip[1]{\\def\\tooltipcallout{\\tikz{\\node[tooltip]{#1};}}\\setbox\\tempboxa=\\hbox{\\phantom{\\tooltipcallout}}\\immediate\\pdfxform\\tempboxa\\edef\\emptyicon{\\the\\pdflastxform}\\setbox\\tempboxa=\\hbox{\\tooltipcallout}\\immediate\\pdfxform\\tempboxa\\edef\\tooltipicon{\\the\\pdflastxform}\\pdfstartlink user{/Subtype /Text/Contents (#1)/AP <</N \\emptyicon\\space 0 R/R \\tooltipicon\\space 0 R>>}\\tooltiptarget\\pdfendlink}"
))
第2步
R级代码不变。
第三步
让我们尝试一个稍微复杂的图形:
tikz('tooltips_with_callouts.tex', standAlone = TRUE)
x <- 1:10
y <- runif(10, 0, 10)
plot(x,y)
place_PDF_tooltip(x,y,x)
dev.off()
require(tools)
texi2dvi('tooltips_with_callouts.tex', pdf = TRUE)
步骤4
结果(download a PDF):
如您所见,显示工具提示和标注都存在问题。设置
tooltipicon
以便工具提示具有空字符串将无济于事。这可能可以通过使用其他注释类型来解决,但是我现在不会在上面花费更多时间。注意事项
\Contents ()
,_
,^
,complete list here,您需要确保在使用tikzDevice时可以正确转义这些字符。 替代实现
cooltooltips和fancytooltips是LaTeX软件包,它们提供了可能在tikzDevice中使用的工具提示功能。以上示例中使用的一些想法来自cooltooltips。
结论思想
这值得吗?好吧,到今天结束时,我确实学到了两件事:
考虑到这一点,我认为现在是PDF踏入日落的时候了,它具有700页的规格。我们需要一种新的页面描述标记语言,我个人希望SVG Print足够完整以完成这项工作。如果规范足够容易接受,我什至会接受Adobe Mars。只要给我们提供一种基于XML的格式,该格式可以利用当今JavaScript库的全部功能,然后就可以开始一些真正的创新。一个LuaTeX后端将是一个不错的奖励。
引用文献
%
\pdfstartlink
实际工作方式)的手册。 关于pdf - 在R中使用工具提示创建pdf,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4691780/