From tadakado at gmail.com  Sat Mar 29 19:08:37 2008
From: tadakado at gmail.com (Tadashi Kadowaki)
Date: Sun, 30 Mar 2008 03:08:37 +0900
Subject: [Rd] A patch for extending pdf device to embed popup text and web
	links
Message-ID: <dcb3073c0803291108m57c4ef6fh22dac712e6a60ddb@mail.gmail.com>

Dear all,

I am sending a patch for extending pdf device to embed popup text and web links.
I implemented this feature by using annotation object, a interactive
feature of pdf.
You can see a sample code for using this feature in the following, and
sample pdf is in
http://www.okada.jp.org/RWiki/?plugin=attach&pcmd=open&file=sample.pdf&refer=PDF%A4%CB%A5%E1%A5%E2%A4%E4URL%A4%CE%A5%EA%A5%F3%A5%AF%A4%F2%CB%E4%A4%E1%B9%FE%A4%E0%A5%D1%A5%C3%A5%C1
In the pdf, boxes in cyan are  web links, and each point has popup text.
If you know about pdf a lot, you can embed any annotation object by calling
PDFAnnotBox directly. I think this patch helps R generate more interactive pdf.

Regards,

Tadashi Kadowaki

--------------------------------------------------------------------------------
sample script
--------------------------------------------------------------------------------
pdf("sample.pdf")
par(oma=c(2,2,2,2))

plot(1:10)

pdf.link.on.box(1, 5, 3, 8, url="http://www.r-project.org")

for(i in 1:10)
    pdf.text.on.box(i-0.1, i-0.1, i+0.1, i+0.1, LETTERS[i], border=c(0,0,0))

text(5, 9, "mouse over points!", col=2)

text(5, 2, "text test")
pdf.link.on.text("http://www.google.com")

mtext("mtext test 1", cex=2)
pdf.link.on.text("http://www.apple.com")

mtext("mtext test 2", side = 2, line=2)
pdf.link.on.text("http://www.vmware.com")

mtext("mtext test 3", outer=T, side = 1)
pdf.link.on.text("http://www.playstation.com")

mtext("mtext test 4", outer=T, side = 4, cex=3)
pdf.link.on.text("http://www.bioconductor.org")

dev.off()
--------------------------------------------------------------------------------
patch to Revision: 44977 Last Changed Date: 2008-03-29
--------------------------------------------------------------------------------
diff -r -c R.org/src/library/grDevices/NAMESPACE
R/src/library/grDevices/NAMESPACE
*** R.org/src/library/grDevices/NAMESPACE	2008-03-29 23:36:12.000000000 +0900
--- R/src/library/grDevices/NAMESPACE	2008-03-30 01:40:49.000000000 +0900
***************
*** 4,10 ****

  if(tools:::.OStype() == "windows") {
      useDynLib(grDevices, R_chull,
!               PicTeX, PostScript, XFig, PDF,
                Cdevga=devga, CsavePlot=savePlot,
                Type1FontInUse, CIDFontInUse, R_GD_nullDevice)
  } else {
--- 4,10 ----

  if(tools:::.OStype() == "windows") {
      useDynLib(grDevices, R_chull,
!               PicTeX, PostScript, XFig, PDF, PDFAnnotBox, PDFTextBoxInfo,
                Cdevga=devga, CsavePlot=savePlot,
                Type1FontInUse, CIDFontInUse, R_GD_nullDevice)
  } else {
***************
*** 23,28 ****
--- 23,30 ----
         extendrange, getGraphicsEvent, graphics.off, gray, grey,
         gray.colors, grey.colors, heat.colors, hsv, hcl, make.rgb,
         n2mfrow, nclass.Sturges, nclass.FD, nclass.scott, palette, pdf,
+        pdf.annot.box, pdf.text.box.info, pdf.link.on.box, pdf.text.on.box,
+        pdf.link.on.text, pdf.text.on.text,
         pdf.options, pdfFonts, pictex, postscript, postscriptFont,
         postscriptFonts, ps.options, rainbow, recordGraphics,
         recordPlot, replayPlot, rgb, rgb2hsv, savePlot, setEPS, setPS,
diff -r -c R.org/src/library/grDevices/R/postscript.R
R/src/library/grDevices/R/postscript.R
*** R.org/src/library/grDevices/R/postscript.R	2008-03-29
23:36:12.000000000 +0900
--- R/src/library/grDevices/R/postscript.R	2008-03-30 01:41:13.000000000 +0900
***************
*** 344,349 ****
--- 344,420 ----
      invisible()
  }

+ pdf.annot.box <- function(x0, y0, x1, y1, annotation_text, coord="USER")
+ {
+     ## is the device pdf?
+     if (names(dev.cur())[1] == "pdf") {
+ 	.External("PDFAnnotBox", x0, y0, x1, y1,
+ 		  as.character(annotation_text[1]), as.character(coord[1]))
+     }
+ }
+
+ pdf.text.box.info <- function()
+ {
+     ## is the device pdf?
+     if (names(dev.cur())[1] == "pdf") {
+ 	.External("PDFTextBoxInfo")
+     }
+ }
+
+ pdf.link.on.box <- function(x0, y0, x1, y1, url, col=c(0,1,1),
+ 			    border=c(0,0,1), coord="USER")
+ {
+     col <- as.numeric(abs(col))[1:3]
+     if (max(col)>1) {
+ 	col <- col / max(col)
+     }
+     border <- as.integer(abs(border))[1:3]
+     annotation_text <-
+ 	paste(paste(c("/C [", col, "]"), collapse=" "),
+ 	      paste(c("/Border [", border, "]"), collapse=" "),
+ 	      paste("/Subtype /Link /A << /Type /Action /S /URI /URI(",
+ 		    as.character(url[1]), ")>>", sep=""), sep="\n")
+     pdf.annot.box(x0, y0, x1, y1, annotation_text, coord)
+ }
+
+ pdf.text.on.box <- function(x0, y0, x1, y1, text, col=NULL,
+ 			    border=c(0,0,0), coord="USER")
+ {
+     if (length(col)>=1 && max(col)>1) {
+ 	col <- col / max(col)
+     }
+     border <- as.integer(abs(border))[1:3]
+     annotation_text <-
+ 	paste(paste(c("/C [", col, "]"), collapse=" "),
+ 	      paste(c("/Border [", border, "]"), collapse=" "),
+ 	      "/Subtype /Square",
+ 	      paste("/Contents (", paste(text, collapse="\r"), ")", sep=""),
+ 	      "/BS << /Type /Border /W 0 >>", sep="\n")
+     pdf.annot.box(x0, y0, x1, y1, annotation_text, coord)
+ }
+
+ pdf.link.on.text <- function(url, col=c(0,1,1), border=c(0,0,1))
+ {
+     geo <- pdf.text.box.info()
+     m <- matrix(c(geo[2], geo[3], -geo[3], geo[2]), nrow=2)/geo[1]
+     x <- cbind(c(0, -geo[8]), c(0, geo[7]),
+                c(geo[6], -geo[8]), c(geo[6], geo[7]))
+     xx <- m %*% x + c(geo[4], geo[5])
+     pdf.link.on.box(min(xx[1,]), min(xx[2,]), max(xx[1,]), max(xx[2,]), url,
+ 		    col, border, "DEVICE")
+ }
+
+ pdf.text.on.text <- function(text, col=c(0,1,1), border=c(0,0,1))
+ {
+     geo <- pdf.text.box.info()
+     m <- matrix(c(geo[2], geo[3], -geo[3], geo[2]), nrow=2)/geo[1]
+     x <- cbind(c(0, -geo[8]), c(0, geo[7]),
+                c(geo[6], -geo[8]), c(geo[6], geo[7]))
+     xx <- m %*% x + c(geo[4], geo[5])
+     pdf.text.on.box(min(xx[1,]), min(xx[2,]), max(xx[1,]), max(xx[2,]), text,
+ 		    col, border, "DEVICE")
+ }
+
  .ps.prolog <- c(
  "/gs  { gsave } def",
  "/gr  { grestore } def",
diff -r -c R.org/src/library/grDevices/src/devPS.c
R/src/library/grDevices/src/devPS.c
*** R.org/src/library/grDevices/src/devPS.c	2008-03-29 23:36:12.000000000 +0900
--- R/src/library/grDevices/src/devPS.c	2008-03-30 01:44:20.000000000 +0900
***************
*** 49,54 ****
--- 49,55 ----
  #include <R_ext/Error.h>
  #include "Fileio.h"
  #include "grDevices.h"
+ #include <Rgraphics.h>          /* for GConvert and GUnit */

  #ifdef HAVE_ERRNO_H
  #include <errno.h>
***************
*** 5136,5141 ****
--- 5137,5146 ----
      Rboolean inText;
      char title[1024];

+     char **annots; /* annotations in a page */
+     int annotsmax; /* allocated size */
+     int annotspos; /* nubmer of annotations */
+
      /*
       * Fonts and encodings used on the device
       */
***************
*** 5149,5154 ****
--- 5154,5166 ----
      cidfontfamily   defaultCIDFont;
      /* Record if fonts are used */
      Rboolean fontUsed[100];
+
+     /*
+      * Current text geometry information (stored in PDF_Text)
+      */
+     int text_size;
+     double text_a, text_b, text_x, text_y;
+     double text_ascent, text_descent, text_width;
  }
  PDFDesc;

***************
*** 5188,5197 ****
--- 5200,5217 ----
  static double PDF_StrWidth(const char *str,
  			   const pGEcontext gc,
  			   pDevDesc dd);
+ static void PDF_StrSize(const char *str,
+ 			const pGEcontext gc,
+ 			pDevDesc dd,
+ 			double *ascent_max,
+ 			double *descent_max,
+ 			double *width_sum);
  static void PDF_Text(double x, double y, const char *str,
  		     double rot, double hadj,
  		     const pGEcontext gc,
  		     pDevDesc dd);
+ static void PDF_free_annots(PDFDesc *pd);
+ static void PDF_add_annot(PDFDesc *pd, char *str);
  #ifdef SUPPORT_MBCS
  static double PDF_StrWidthUTF8(const char *str,
  			       const pGEcontext gc,
***************
*** 5315,5320 ****
--- 5335,5348 ----
      }
      pd->pagemax = 100;

+     pd->annots = (char **) calloc(100, sizeof(char *));
+     if(!pd->annots) {
+ 	free(pd->pos); free(pd->pageobj); free(pd); free(dd);
+ 	error(_("cannot allocate pd->annots"));
+     }
+     pd->annotsmax = 100;
+     pd->annotspos = 0;
+

      /* initialize PDF device description */
      strcpy(pd->filename, file);
***************
*** 5328,5334 ****

      if(strlen(encoding) > PATH_MAX - 1) {
  	free(dd);
! 	free(pd->pos); free(pd->pageobj); free(pd);
  	error(_("encoding path is too long"));
      }
      /*
--- 5356,5362 ----

      if(strlen(encoding) > PATH_MAX - 1) {
  	free(dd);
! 	free(pd->pos); free(pd->pageobj); PDF_free_annots(pd); free(pd);
  	error(_("encoding path is too long"));
      }
      /*
***************
*** 5344,5349 ****
--- 5372,5378 ----
  	pd->encodings = enclist;
      } else {
  	free(dd);
+ 	PDF_free_annots(pd);
  	free(pd);
  	error(_("failed to load default encoding"));
      }
***************
*** 5410,5415 ****
--- 5439,5445 ----
      }
      if (!gotFont) {
  	free(dd);
+ 	PDF_free_annots(pd);
  	free(pd);
  	error(_("Failed to initialise default PostScript font"));
      }
***************
*** 5469,5474 ****
--- 5499,5505 ----
  	    pd->fonts = NULL;
  	    pd->encodings = NULL;
  	    free(dd);
+ 	    PDF_free_annots(pd);
  	    free(pd);
  	    error(_("Failed to initialise additional PostScript fonts"));
  	}
***************
*** 5540,5545 ****
--- 5571,5577 ----
  	pd->fonts = NULL;
  	pd->cidfonts = NULL;
  	free(dd);
+ 	PDF_free_annots(pd);
  	free(pd);
  	error(_("invalid paper type '%s' (pdf)"), pd->papername);
      }
***************
*** 5570,5576 ****
  	pd->cidfonts = NULL;
  	pd->encodings = NULL;
  	free(dd);
! 	free(pd->pos); free(pd->pageobj); free(pd);
  	error(_("invalid foreground/background color (pdf)"));
      }

--- 5602,5608 ----
  	pd->cidfonts = NULL;
  	pd->encodings = NULL;
  	free(dd);
! 	free(pd->pos); free(pd->pageobj); PDF_free_annots(pd); free(pd);
  	error(_("invalid foreground/background color (pdf)"));
      }

***************
*** 5631,5637 ****
  	pd->cidfonts = NULL;
  	pd->encodings = NULL;
  	free(dd);
! 	free(pd->pos); free(pd->pageobj); free(pd);
  	return 0;
      }

--- 5663,5669 ----
  	pd->cidfonts = NULL;
  	pd->encodings = NULL;
  	free(dd);
! 	free(pd->pos); free(pd->pageobj); PDF_free_annots(pd); free(pd);
  	return 0;
      }

***************
*** 6336,6341 ****
--- 6368,6374 ----
  static void PDF_endpage(PDFDesc *pd)
  {
      int here;
+     int i;
      if(pd->inText) textoff(pd);
      fprintf(pd->pdffp, "Q\n");
      here = (int) ftell(pd->pdffp);
***************
*** 6343,6348 ****
--- 6376,6397 ----
      pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
      fprintf(pd->pdffp, "%d 0 obj\n%d\nendobj\n", pd->nobjs,
  	    here - pd->startstream);
+     pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
+     fprintf(pd->pdffp, "%d 0 obj\n<<\n/Type /Page\n/Parent 3 0
R\n/Contents %d 0 R\n/Resources 4 0 R\n",
+ 	    pd->nobjs, pd->nobjs-2);
+     if(pd->annotspos) {
+ 	fprintf(pd->pdffp, "/Annots [");
+ 	for(i = 0 ; i < pd->annotspos ; i++)
+ 	  fprintf(pd->pdffp, " %d 0 R", pd->nobjs+1+i);
+ 	fprintf(pd->pdffp, " ]\n");
+     }
+     fprintf(pd->pdffp, ">>\nendobj\n");
+     for(i = 0 ; i < pd->annotspos ; i++) {
+ 	pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
+ 	fprintf(pd->pdffp, "%d %s", pd->nobjs, pd->annots[i]);
+ 	free(pd->annots[i]);
+     }
+     pd->annotspos = 0;
  }

  #define R_VIS(col) (R_ALPHA(col) > 0)
***************
*** 6352,6357 ****
--- 6401,6407 ----
  {
      PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
      char buf[512];
+     int i;

      if(pd->pageno >= pd->pagemax || pd->nobjs >= 3*pd->pagemax) {
  	pd->pageobj = (int *)
***************
*** 6377,6386 ****
  	}
      }

!     pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
!     pd->pageobj[pd->pageno++] = pd->nobjs;
!     fprintf(pd->pdffp, "%d 0 obj\n<<\n/Type /Page\n/Parent 3 0
R\n/Contents %d 0 R\n/Resources 4 0 R\n>>\nendobj\n",
! 	    pd->nobjs, pd->nobjs+1);
      pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
      fprintf(pd->pdffp, "%d 0 obj\n<<\n/Length %d 0 R\n>>\nstream\r\n",
  	    pd->nobjs, pd->nobjs + 1);
--- 6427,6433 ----
  	}
      }

!     pd->pageobj[pd->pageno++] = pd->nobjs+3;
      pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
      fprintf(pd->pdffp, "%d 0 obj\n<<\n/Length %d 0 R\n>>\nstream\r\n",
  	    pd->nobjs, pd->nobjs + 1);
***************
*** 6411,6417 ****
      freeDeviceEncList(pd->encodings);
      pd->fonts = NULL;
      pd->encodings = NULL;
!     free(pd->pos); free(pd->pageobj); free(pd);
  }

  static void PDF_Activate(pDevDesc dd) {}
--- 6458,6464 ----
      freeDeviceEncList(pd->encodings);
      pd->fonts = NULL;
      pd->encodings = NULL;
!     free(pd->pos); free(pd->pageobj); PDF_free_annots(pd); free(pd);
  }

  static void PDF_Activate(pDevDesc dd) {}
***************
*** 6663,6668 ****
--- 6710,6716 ----
      int face = gc->fontface;
      double a, b, rot1;
      const char *str1 = str;
+     double ascent, descent, width;

      if(!R_VIS(gc->col)) return;

***************
*** 6683,6688 ****
--- 6731,6745 ----
  	    a, b, -b, a, x, y);
      PostScriptWriteString(pd->pdffp, str1);
      fprintf(pd->pdffp, " Tj\n");
+     pd->text_size = size;
+     pd->text_a = a;
+     pd->text_b = b;
+     pd->text_x = x;
+     pd->text_y = y;
+     PDF_StrSize(str1, gc, dd, &ascent, &descent, &width);
+     pd->text_ascent = ascent;
+     pd->text_descent = descent;
+     pd->text_width = width;
  }

  #ifndef SUPPORT_MBCS
***************
*** 6711,6716 ****
--- 6768,6774 ----
      double a, b, rot1;
      const char *str1 = str;
      char *buff;
+     double ascent, descent, width;

      if(!R_VIS(gc->col)) return;

***************
*** 6732,6737 ****
--- 6790,6805 ----
      if(fabs(b) < 0.01) b = 0.0;
      if(!pd->inText) texton(pd);

+     pd->text_size = size;
+     pd->text_a = a;
+     pd->text_b = b;
+     pd->text_x = x;
+     pd->text_y = y;
+     PDF_StrSize(str1, gc, dd, &ascent, &descent, &width);
+     pd->text_ascent = ascent;
+     pd->text_descent = descent;
+     pd->text_width = width;
+
      if(isCIDFont(gc->fontfamily, PDFFonts, pd->defaultCIDFont) && face != 5) {
  	/* NB we could be in a SBCS here */
          unsigned char *buf = NULL /* -Wall */;
***************
*** 7048,7053 ****
--- 7116,7146 ----
  }
  #endif

+ static void PDF_StrSize(const char *str,
+ 			R_GE_gcontext *gc,
+ 			NewDevDesc *dd,
+ 			double* ascent_max,
+ 			double* descent_max,
+ 			double* width_sum)
+ {
+     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
+     int face = gc->fontface;
+     double ascent, descent, width;
+     int i, n;
+
+     *ascent_max = 0.0;
+     *descent_max = 0.0;
+     *width_sum = 0.0;
+
+     n = (int) strlen(str);
+     for(i=0; i<n; i++) {
+ 	PDF_MetricInfo((int) str[i], gc, &ascent, &descent, &width, dd);
+ 	if (*ascent_max < ascent) *ascent_max = ascent;
+ 	if (*descent_max < descent) *descent_max = descent;
+ 	*width_sum += width;
+     }
+ }
+
  static void PDF_MetricInfo(int c,
  			   const pGEcontext gc,
  			   double* ascent, double* descent,
***************
*** 7076,7081 ****
--- 7169,7205 ----
      *width = floor(gc->cex * gc->ps + 0.5) * *width;
  }

+ static void PDF_free_annots(PDFDesc *pd)
+ {
+     int i;
+     for (i = 0; i < pd->annotspos; i++) free(pd->annots[i]);
+     free(pd->annots);
+ }
+
+ static void PDF_add_annot(PDFDesc *pd, char *str)
+ {
+     char **annots;
+     char *annot_str;
+     int nchar;
+
+     if (pd->annotspos >= pd->annotsmax) {
+ 	annots = realloc(pd->annots, pd->annotsmax + 100);
+ 	if (annots) {
+ 	    pd->annots = annots;
+ 	    pd->annotsmax += 100;
+ 	} else {
+ 	    error(_("unable to increase annotation limit"));
+ 	}
+     }
+     nchar = strlen(str);
+     annot_str = (char *) calloc(nchar+1, sizeof(char));
+     if(!annot_str) {
+ 	error(_("cannot allocate pd->annots[i]"));
+     }
+     strncpy(annot_str, str, nchar+1);
+     pd->annots[pd->annotspos++] = annot_str;
+ }
+

  /*  PostScript Device Driver Parameters:
   *  ------------------------
***************
*** 7302,7304 ****
--- 7426,7496 ----
      vmaxset(vmax);
      return R_NilValue;
  }
+
+
+ /* PDFAnnotBox(x0, y0, x1, y1, text, coord) */
+
+ SEXP PDFAnnotBox(SEXP args)
+ {
+     pGEDevDesc gdd = GEcurrentDevice();
+     PDFDesc *pd = (PDFDesc *) gdd->dev->deviceSpecific;
+     double x0, y0, x1, y1;
+     char *text, *coord;
+     GUnit from, to;
+     char annot_text[1024] = "";
+
+     args = CDR(args);
+     x0 = asReal(CAR(args)); args = CDR(args);
+     y0 = asReal(CAR(args)); args = CDR(args);
+     x1 = asReal(CAR(args)); args = CDR(args);
+     y1 = asReal(CAR(args)); args = CDR(args);
+     text = (char *)CHAR(asChar(CAR(args))); args = CDR(args);
+     coord = (char *)CHAR(asChar(CAR(args)));
+
+     if (strcmp("DEVICE", coord) == 0) { from = DEVICE; }
+     if (strcmp("NDC",    coord) == 0) { from = NDC; }
+     if (strcmp("INCHES", coord) == 0) { from = INCHES; }
+     if (strcmp("OMA1",   coord) == 0) { from = OMA1; }
+     if (strcmp("OMA2",   coord) == 0) { from = OMA2; }
+     if (strcmp("OMA3",   coord) == 0) { from = OMA3; }
+     if (strcmp("OMA4",   coord) == 0) { from = OMA4; }
+     if (strcmp("NIC",    coord) == 0) { from = NIC; }
+     if (strcmp("NFC",    coord) == 0) { from = NFC; }
+     if (strcmp("MAR1",   coord) == 0) { from = MAR1; }
+     if (strcmp("MAR2",   coord) == 0) { from = MAR2; }
+     if (strcmp("MAR3",   coord) == 0) { from = MAR3; }
+     if (strcmp("MAR4",   coord) == 0) { from = MAR4; }
+     if (strcmp("NPC",    coord) == 0) { from = NPC; }
+     if (strcmp("USER",   coord) == 0) { from = USER; }
+     to = DEVICE;
+
+     GConvert(&x0, &y0, from, to, gdd);
+     GConvert(&x1, &y1, from, to, gdd);
+
+     snprintf(annot_text, 1024,
+            "0 obj <<\n/Type /Annot\n/Rect [ %.2f %.2f %.2f %.2f
]\n%s\n>> endobj\n",
+           x0, y0, x1, y1, text);
+     PDF_add_annot(pd, annot_text);
+     return R_NilValue;
+ }
+
+
+ /* PDFTextBoxInfo() */
+
+ SEXP PDFTextBoxInfo(SEXP args)
+ {
+     PDFDesc *pd = GEcurrentDevice()->dev->deviceSpecific;
+     SEXP out;
+
+     out = allocVector(REALSXP, 8);
+     REAL(out)[0] = pd->text_size;
+     REAL(out)[1] = pd->text_a;
+     REAL(out)[2] = pd->text_b;
+     REAL(out)[3] = pd->text_x;
+     REAL(out)[4] = pd->text_y;
+     REAL(out)[5] = pd->text_width;
+     REAL(out)[6] = pd->text_ascent;
+     REAL(out)[7] = pd->text_descent;
+
+     return out;
+ }
diff -r -c R.org/src/library/grDevices/src/grDevices.h
R/src/library/grDevices/src/grDevices.h
*** R.org/src/library/grDevices/src/grDevices.h	2008-03-29
23:36:12.000000000 +0900
--- R/src/library/grDevices/src/grDevices.h	2008-03-30 00:30:24.000000000 +0900
***************
*** 43,48 ****
--- 43,51 ----

  SEXP Quartz(SEXP);

+ SEXP PDFAnnotBox(SEXP);
+ SEXP PDFTextBoxInfo(SEXP);
+
  SEXP R_GD_nullDevice();

  Rboolean
diff -r -c R.org/src/library/grDevices/src/init.c
R/src/library/grDevices/src/init.c
*** R.org/src/library/grDevices/src/init.c	2008-03-29 23:36:12.000000000 +0900
--- R/src/library/grDevices/src/init.c	2008-03-30 00:31:03.000000000 +0900
***************
*** 62,67 ****
--- 62,69 ----
  #else
      EXTDEF(Quartz, 12),
  #endif
+     EXTDEF(PDFAnnotBox, 6),
+     EXTDEF(PDFTextBoxInfo, 0),
      {NULL, NULL, 0}
  };



[Rd] A patch for extending pdf device to embed popup text and web links
Tadashi Kadowaki