pdf - 在R中使用工具提示创建pdf

标签 pdf r tooltip

一个简单的问题:有没有一种方法可以从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标注放入绘图中。

涉及的步骤是:
  • 设置LaTeX宏以生成产生标注所需的PDF命令。
  • 设置一个R函数,该函数使用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可能会变得很奇怪-尤其是当您会因此而陷入困境):
  • 第1行:定义一个对象tooltiptarget,它是不可见的(幻像),是1mm x 1mm的矩形(规则)。这是屏幕区域,我们将使用它来检测鼠标悬停。
  • 第2行:创建一个新框,就像排版 Material 的“页面片段”一样。可以容纳几乎所有东西,包括其他盒子(类似于R列表)。称之为tempboxa
  • 第3行:分配tempboxa的内容以包含一个空框,该框使用水平布局排列其内容(不重要,可以使用vbox或其他框)。
  • 第4行:使用tempboxa的内容创建PDF表单XObject。 PDF文件可使用Form XObject来存储可能反复使用的图形(如徽标)。在这里,我们创建了一个“空白图标”,以后可以用来减少视觉困惑。 TeX会延迟输出操作(例如将对象写入PDF文件),直到满足某些条件为止,例如页面已满。立即确保不推迟此操作。
  • 第5行:该行捕获一个整数值,该值用作对我们刚刚创建的PDF XObject的引用,并为其指定名称emptyicon
  • 第6行:开始定义一个名为tooltip的新宏,该宏接受1个参数,该参数在宏的主体中称为#1。宏中的每一行都以注释字符%结尾,以防止TeX注意到为可读性而添加的换行符(换行符在宏内部可能产生奇怪的影响)。
  • 第7行:输出原始PDF命令(pdfstartlink)。这开始创建一个新的PDF注释对象(\Type \Annot),该对象大约有20种不同的子类型,其中包括超链接。其后的每一行都包含带有两个TeX宏的原始PDF标记。
  • 第8行:声明我们将要使用的注释子类型。在这里,我将使用纯文本注释,例如注释或便笺。
  • 第9行:声明注释的内容。这将是我们的工具提示的内容,并设置为#1(工具提示宏的参数)。
  • 第10至12行:通常,文本注释由图标(例如便笺)标记,以突出显示它们在文本中的位置。如果我们允许该行为在整个图形中散布一些小便条,则会导致视觉困惑。在这里,我们使用外观数组(\AP << >>)将“常规”注释图标(\N)设置为我们先前创建的空白图标。我们与emptyicon一起存储在0 R中的整数形成对我们在第4行使用空框形成的Form XObject的引用。
  • 第14行:如果我们要创建普通的超链接,则此处将文本/图像/所执行的操作作为链接正文。相反,我们插入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宏中的单发oji​​t_code命令生成工具提示。表格XObject由tooltipcallout生成并分配给tooltipcallout
  • tooltipicon也可以通过评估emptyicon内部的tooltipcallout来动态生成。这是必需的,因为默认图标的大小显然设置了可用于翻转图标的视口(viewport)。
  • 生成PDF命令时,会使用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以便工具提示具有空字符串将无济于事。这可能可以通过使用其他注释类型来解决,但是我现在不会在上面花费更多时间。

    注意事项
  • 很多TeX命令都包含反斜杠,在用R代码表达内容时,您需要将反斜杠加倍。
  • 某些字符是TeX的特殊字符,例如\Contents ()_^complete list here,您需要确保在使用tikzDevice时可以正确转义这些字符。
  • 尽管应该认为PDF优于HTML,因为它在各个平台上都具有一致的呈现效果,但是根据所使用的查看器的不同,您的工作量也会有很大的不同。屏幕截图是在OS X的Acrobat 8​​中拍摄的,预览版也做得不错,但没有呈现过渡标注。在Linux上,xpdf不会呈现任何内容,而okular会显示工具提示,但不会取消显示工具提示图标,而是显示一个股票图标,在图的中间看起来有点扎眼。

  • 替代实现

    cooltooltipsfancytooltips是LaTeX软件包,它们提供了可能在tikzDevice中使用的工具提示功能。以上示例中使用的一些想法来自cooltooltips。

    结论思想

    这值得吗?好吧,到今天结束时,我确实学到了两件事:
  • 我不是PDF专家,我不得不搜索几个邮件列表,并消化700多页规范的某些部分,甚至回答“这可能吗?”这个问题。更不用说实现了。
  • 我不是SVG专家,但是我知道SVG中的工具提示不是“这可能吗?”的问题。而是“how sexy do you want it to look?”。

  • 考虑到这一点,我认为现在是PDF踏入日落的时候了,它具有700页的规格。我们需要一种新的页面描述标记语言,我个人希望SVG Print足够完整以完成这项工作。如果规范足够容易接受,我什至会接受Adobe Mars。只要给我们提供一种基于XML的格式,该格式可以利用当今JavaScript库的全部功能,然后就可以开始一些真正的创新。一个LuaTeX后端将是一个不错的奖励。

    引用文献
  • tikzDevice:一种用于将R图形输出为LaTeX代码的设备。
  • comp.text.tex:深入了解深奥的TeX详细信息的来源。 Herbert VoßMycroft的帖子提供了实现思路。
  • The pdfTeX manual:有关可以生成原始PDF代码的TeX命令的信息源,例如%
  • TeX by Topic:有关低级TeX详细信息(例如\pdfstartlink实际工作方式)的手册。
  • The Adobe PDF Specification:有关PDF原语的详细信息。
  • The TikZ Manual:可能是有史以来最好的开源文档示例。
  • 关于pdf - 在R中使用工具提示创建pdf,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4691780/

    相关文章:

    java - 无法通过selenium单击工具提示中的链接

    java - 从其他带有页码的 PDF 生成 PDF

    java - 使用 Runtime.getRuntime() 在 JAVA 中打印 pdf 文件时出错

    javascript - 适用于 Windows Phone 的 Cordova Visual Studio 2012 Express : Open External PDF URL

    jquery - R Shiny - 通过列排序禁用数据表中的特定行

    用函数替换多个 `summarize` 语句

    r - 将 {.tabset} 与 bookdown::bs4_book() 一起使用

    javascript - 如何默认显示工具提示而无需在纯 css 中悬停

    ms-access - Crystal Reports 11 - 添加了无用的空白页,仅添加了包含数据的组标题

    c# - 如何让工具提示跟随鼠标?