1 /*
2  *  R : A Computer Language for Statistical Data Analysis
3  *  Copyright (C) 1998--2020  The R Core Team
4  *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, a copy is available at
18  *  https://www.R-project.org/Licenses/
19  */
20 
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <Defn.h>
27 
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <limits.h> /* required for MB_LEN_MAX */
31 
32 #include <wchar.h>
33 #include <wctype.h>
34 static void
35 mbcsToSbcs(const char *in, char *out, const char *encoding, int enc);
36 
37 
38 #include <R_ext/Riconv.h>
39 
40 #include <Rmath.h>		/* for fround */
41 #define R_USE_PROTOTYPES 1
42 #include <R_ext/GraphicsEngine.h>
43 #include <R_ext/Error.h>
44 #include <R_ext/RS.h>
45 #include "Fileio.h"
46 #include "grDevices.h"
47 
48 #ifdef HAVE_ERRNO_H
49 #include <errno.h>
50 #else
51 extern int errno;
52 #endif
53 
54 #include "zlib.h"
55 
56 #ifndef max
57 #define max(a,b) ((a > b) ? a : b)
58 #endif
59 
60 /* from connections.o */
61 extern gzFile R_gzopen (const char *path, const char *mode);
62 extern char *R_gzgets(gzFile file, char *buf, int len);
63 extern int R_gzclose (gzFile file);
64 
65 #define INVALID_COL 0xff0a0b0c
66 
67 /* Define this to use hyphen except in -[0-9] */
68 #undef USE_HYPHEN
69 /* In ISOLatin1, minus is 45 and hyphen is 173 */
70 #ifdef USE_HYPHEN
71 static char PS_hyphen = 173;
72 #endif
73 
74 #define USERAFM 999
75 
76 /* Part 0.  AFM File Names */
77 
78 static const char *CIDBoldFontStr1 =
79 "16 dict begin\n"
80 "  /basecidfont exch def\n"
81 "  /basefont-H /.basefont-H /Identity-H [ basecidfont ] composefont def\n"
82 "  /basefont-V /.basefont-V /Identity-V [ basecidfont ] composefont def\n"
83 "  /CIDFontName dup basecidfont exch get def\n"
84 "  /CIDFontType 1 def\n"
85 "  /CIDSystemInfo dup basecidfont exch get def\n"
86 "  /FontInfo dup basecidfont exch get def\n"
87 "  /FontMatrix [ 1 0 0 1 0 0 ] def\n"
88 "  /FontBBox [\n"
89 "    basecidfont /FontBBox get cvx exec\n"
90 "    4 2 roll basecidfont /FontMatrix get transform\n"
91 "    4 2 roll basecidfont /FontMatrix get transform\n"
92 "  ] def\n"
93 "  /cid 2 string def\n";
94 static const char *CIDBoldFontStr2 =
95 "  /BuildGlyph {\n"
96 "    gsave\n"
97 "    exch begin\n"
98 "      dup 256 idiv cid exch 0 exch put\n"
99 "      256 mod cid exch 1 exch put\n"
100 "      rootfont\n"
101 "        /WMode known { rootfont /WMode get 1 eq } { false } ifelse\n"
102 "      { basefont-V } { basefont-H } ifelse setfont\n"
103 "      .03 setlinewidth 1 setlinejoin\n"
104 "      newpath\n"
105 "      0 0 moveto cid false charpath stroke\n"
106 "      0 0 moveto cid show\n"
107 "      currentpoint setcharwidth\n"
108 "    end\n"
109 "    grestore\n"
110 "  } bind def\n"
111 "  currentdict\n"
112 "end\n"
113 "/CIDFont defineresource pop\n";
114 
115 
116 /* Part 1.  AFM File Parsing.  */
117 
118 /* These are the basic entities in the AFM file */
119 
120 #define BUFSIZE 512
121 #define NA_SHORT -30000
122 
123 typedef struct {
124     unsigned char c1;
125     unsigned char c2;
126     short kern;
127 } KP;
128 
129 typedef struct {
130     short FontBBox[4];
131     short CapHeight;
132     short XHeight;
133     short Descender;
134     short Ascender;
135     short StemH;
136     short StemV;
137     short ItalicAngle;
138     struct {
139 	short WX;
140 	short BBox[4];
141     } CharInfo[256];
142     KP *KernPairs;
143     short KPstart[256];
144     short KPend[256];
145     short nKP;
146     short IsFixedPitch;
147 } FontMetricInfo;
148 
149 enum {
150     Empty,
151     StartFontMetrics,
152     Comment,
153     FontName,
154     EncodingScheme,
155     FullName,
156     FamilyName,
157     Weight,
158     ItalicAngle,
159     IsFixedPitch,
160     UnderlinePosition,
161     UnderlineThickness,
162     Version,
163     Notice,
164     FontBBox,
165     CapHeight,
166     XHeight,
167     Descender,
168     Ascender,
169     StartCharMetrics,
170     C,
171     CH,
172     EndCharMetrics,
173     StartKernData,
174     StartKernPairs,
175     KPX,
176     EndKernPairs,
177     EndKernData,
178     StartComposites,
179     CC,
180     EndComposites,
181     EndFontMetrics,
182     StdHW,
183     StdVW,
184     CharacterSet,
185     Unknown
186 };
187 
188 static const struct {
189     const char *keyword;
190     const int code;
191 }
192 KeyWordDictionary[] = {
193     { "StartFontMetrics",    StartFontMetrics },
194     { "Comment",	     Comment },
195     { "FontName",	     FontName },
196     { "EncodingScheme",	     EncodingScheme },
197     { "FullName",	     FullName },
198     { "FamilyName",	     FamilyName },
199     { "Weight",		     Weight },
200     { "ItalicAngle",	     ItalicAngle },
201     { "IsFixedPitch",	     IsFixedPitch },
202     { "UnderlinePosition",   UnderlinePosition },
203     { "UnderlineThickness",  UnderlineThickness },
204     { "Version",	     Version },
205     { "Notice",		     Notice },
206     { "FontBBox",	     FontBBox },
207     { "CapHeight",	     CapHeight },
208     { "XHeight",	     XHeight },
209     { "Descender",	     Descender },
210     { "Ascender",	     Ascender },
211     { "StartCharMetrics",    StartCharMetrics },
212     { "C ",		     C },
213     { "CH ",		     CH },
214     { "EndCharMetrics",	     EndCharMetrics },
215     { "StartKernData",	     StartKernData },
216     { "StartKernPairs",	     StartKernPairs },
217     { "KPX ",		     KPX },
218     { "EndKernPairs",	     EndKernPairs },
219     { "EndKernData",	     EndKernData },
220     { "StartComposites",     StartComposites },
221     { "CC ",		     CC },
222     { "EndComposites",	     EndComposites },
223     { "EndFontMetrics",	     EndFontMetrics },
224     { "StdHW",		     StdHW },
225     { "StdVW",		     StdVW },
226     { "CharacterSet",	     CharacterSet},
227     { NULL,		     Unknown },
228 };
229 
MatchKey(char const * l,char const * k)230 static int MatchKey(char const * l, char const * k)
231 {
232     while (*k)
233 	if (*k++ != *l++) return 0;
234     return 1;
235 }
236 
KeyType(const char * const s)237 static int KeyType(const char * const s)
238 {
239     int i;
240     if (*s == '\n')
241 	return Empty;
242     for (i = 0; KeyWordDictionary[i].keyword; i++)
243 	if (MatchKey(s, KeyWordDictionary[i].keyword))
244 	    return KeyWordDictionary[i].code;
245 //    printf("Unknown %s\n", s); // not needed, PR#15057 found it annoying
246     return Unknown;
247 }
248 
SkipToNextItem(char * p)249 static char *SkipToNextItem(char *p)
250 {
251     while (!isspace((int)*p)) p++;
252     while (isspace((int)*p)) p++;
253     return p;
254 }
255 
SkipToNextKey(char * p)256 static char *SkipToNextKey(char *p)
257 {
258     while (*p != ';') p++;
259     p++;
260     while (isspace((int)*p)) p++;
261     return p;
262 }
263 
GetFontBBox(const char * buf,FontMetricInfo * metrics)264 static int GetFontBBox(const char *buf, FontMetricInfo *metrics)
265 {
266     if (sscanf(buf, "FontBBox %hd %hd %hd %hd",
267 	      &(metrics->FontBBox[0]),
268 	      &(metrics->FontBBox[1]),
269 	      &(metrics->FontBBox[2]),
270 	      &(metrics->FontBBox[3])) != 4) return 0;
271 #ifdef DEBUG_PS2
272     Rprintf("FontBBox %d %d %d %d\n",
273 	    (metrics->FontBBox[0]),
274 	    (metrics->FontBBox[1]),
275 	    (metrics->FontBBox[2]),
276 	    (metrics->FontBBox[3]));
277 #endif
278     return 1;
279 }
280 
281 /* The longest named Adobe glyph is 39 chars:
282    whitediamondcontainingblacksmalldiamond
283  */
284 typedef struct {
285     char cname[40];
286 } CNAME;
287 
288 
289 /* If reencode > 0, remap to new encoding */
GetCharInfo(char * buf,FontMetricInfo * metrics,CNAME * charnames,CNAME * encnames,int reencode)290 static int GetCharInfo(char *buf, FontMetricInfo *metrics,
291 		       CNAME *charnames, CNAME *encnames,
292 		       int reencode)
293 {
294     char *p = buf, charname[40];
295     int nchar, nchar2 = -1, i;
296     short WX;
297 
298     if (!MatchKey(buf, "C ")) return 0;
299     p = SkipToNextItem(p);
300     sscanf(p, "%d", &nchar);
301     if ((nchar < 0 || nchar > 255) && !reencode) return 1;
302     p = SkipToNextKey(p);
303 
304     if (!MatchKey(p, "WX")) return 0;
305     p = SkipToNextItem(p);
306     sscanf(p, "%hd", &WX);
307     p = SkipToNextKey(p);
308 
309     if (!MatchKey(p, "N ")) return 0;
310     p = SkipToNextItem(p);
311     if(reencode) {
312 	sscanf(p, "%39s", charname);
313 #ifdef DEBUG_PS2
314 	Rprintf("char name %s\n", charname);
315 #endif
316 	/* a few chars appear twice in ISOLatin1 */
317 	nchar = nchar2 = -1;
318 	for (i = 0; i < 256; i++)
319 	    if(!strcmp(charname, encnames[i].cname)) {
320 		strcpy(charnames[i].cname, charname);
321 		if(nchar == -1) nchar = i; else nchar2 = i;
322 	    }
323 	if (nchar == -1) return 1;
324     } else {
325 	sscanf(p, "%39s", charnames[nchar].cname);
326     }
327     metrics->CharInfo[nchar].WX = WX;
328     p = SkipToNextKey(p);
329 
330     if (!MatchKey(p, "B ")) return 0;
331     p = SkipToNextItem(p);
332     sscanf(p, "%hd %hd %hd %hd",
333 	   &(metrics->CharInfo[nchar].BBox[0]),
334 	   &(metrics->CharInfo[nchar].BBox[1]),
335 	   &(metrics->CharInfo[nchar].BBox[2]),
336 	   &(metrics->CharInfo[nchar].BBox[3]));
337 
338 #ifdef DEBUG_PS2
339     Rprintf("nchar = %d %d %d %d %d %d\n", nchar,
340 	    metrics->CharInfo[nchar].WX,
341 	    metrics->CharInfo[nchar].BBox[0],
342 	    metrics->CharInfo[nchar].BBox[1],
343 	    metrics->CharInfo[nchar].BBox[2],
344 	    metrics->CharInfo[nchar].BBox[3]);
345 #endif
346     if (nchar2 > 0) {
347 	metrics->CharInfo[nchar2].WX = WX;
348 	sscanf(p, "%hd %hd %hd %hd",
349 	       &(metrics->CharInfo[nchar2].BBox[0]),
350 	       &(metrics->CharInfo[nchar2].BBox[1]),
351 	       &(metrics->CharInfo[nchar2].BBox[2]),
352 	       &(metrics->CharInfo[nchar2].BBox[3]));
353 
354 #ifdef DEBUG_PS2
355 	Rprintf("nchar = %d %d %d %d %d %d\n", nchar2,
356 		metrics->CharInfo[nchar2].WX,
357 		metrics->CharInfo[nchar2].BBox[0],
358 		metrics->CharInfo[nchar2].BBox[1],
359 		metrics->CharInfo[nchar2].BBox[2],
360 		metrics->CharInfo[nchar2].BBox[3]);
361 #endif
362     }
363     return 1;
364 }
365 
GetKPX(char * buf,int nkp,FontMetricInfo * metrics,CNAME * charnames)366 static int GetKPX(char *buf, int nkp, FontMetricInfo *metrics,
367 		  CNAME *charnames)
368 {
369     char *p = buf, c1[50], c2[50];
370     int i, done = 0;
371 
372     p = SkipToNextItem(p);
373     sscanf(p, "%49s %49s %hd", c1, c2, &(metrics->KernPairs[nkp].kern));
374     if (streql(c1, "space") || streql(c2, "space")) return 0;
375     for(i = 0; i < 256; i++) {
376 	if (!strcmp(c1, charnames[i].cname)) {
377 	    metrics->KernPairs[nkp].c1 = (unsigned char) i;
378 	    done++;
379 	    break;
380 	}
381     }
382     for(i = 0; i < 256; i++)
383 	if (!strcmp(c2, charnames[i].cname)) {
384 	    metrics->KernPairs[nkp].c2 = (unsigned char) i;
385 	    done++;
386 	    break;
387 	}
388     return (done==2);
389 }
390 
391 /* Encode File Parsing.  */
392 /* Statics here are OK, as all the calls are in one initialization
393    so no concurrency (until threads?) */
394 
395 typedef struct {
396   /* Probably can make buf and p0 local variables. Only p needs to be
397      stored across calls. Need to investigate this more closely. */
398   char buf[1000];
399   char *p;
400   char *p0;
401 } EncodingInputState;
402 
403 /* read in the next encoding item, separated by white space. */
GetNextItem(FILE * fp,char * dest,int c,EncodingInputState * state)404 static int GetNextItem(FILE *fp, char *dest, int c, EncodingInputState *state)
405 {
406     if (c < 0) state->p = NULL;
407     while (1) {
408 	if (feof(fp)) { state->p = NULL; return 1; }
409 	if (!state->p || *state->p == '\n' || *state->p == '\0') {
410 	    state->p = fgets(state->buf, 1000, fp);
411 	}
412 	/* check for incomplete encoding file */
413 	if(!state->p) return 1;
414 	while (isspace((int)* state->p)) state->p++;
415 	if (*state->p == '\0' || *state->p == '%'|| *state->p == '\n') { state->p = NULL; continue; }
416 	state->p0 = state->p;
417 	while (!isspace((int)*state->p)) state->p++;
418 	if (*state->p != '\0') *state->p++ = '\0';
419 	if(c == 45) strcpy(dest, "/minus"); else strcpy(dest, state->p0);
420 	break;
421     }
422     return 0;
423 }
424 
425 /*
426  * Convert the encoding file name into a name to be used with iconv()
427  * in mbcsToSbcs()
428  *
429  * FIXME:  Doesn't trim path/to/encfile (i.e., doesn't handle
430  *         custom encoding file selected by user).
431  *         Also assumes that encpath has ".enc" suffix supplied
432  *         (not required by R interface)
433  */
434 
pathcmp(const char * encpath,const char * comparison)435 static int pathcmp(const char *encpath, const char *comparison) {
436     char pathcopy[PATH_MAX];
437     char *p1, *p2;
438     strcpy(pathcopy, encpath);
439     /*
440      * Strip path/to/encfile/
441      */
442     p1 = &(pathcopy[0]);
443     while ((p2 = strchr(p1, FILESEP[0]))) {
444 	p1 = p2 + sizeof(char);
445     }
446     /*
447      * Strip suffix
448      */
449     p2 = (strchr(p1, '.'));
450     if (p2)
451 	*p2 = '\0';
452     return strcmp(p1, comparison);
453 }
454 
seticonvName(const char * encpath,char * convname)455 static void seticonvName(const char *encpath, char *convname)
456 {
457     /*
458      * Default to "latin1"
459      */
460     char *p;
461     strcpy(convname, "latin1");
462     if(pathcmp(encpath, "ISOLatin1")==0)
463 	strcpy(convname, "latin1");
464     else if(pathcmp(encpath, "ISOLatin2")==0)
465 	strcpy(convname, "latin2");
466     else if(pathcmp(encpath, "ISOLatin7")==0)
467 	strcpy(convname, "latin7");
468     else if(pathcmp(encpath, "ISOLatin9")==0)
469 	strcpy(convname, "latin-9");
470     else if (pathcmp(encpath, "WinAnsi")==0)
471 	strcpy(convname, "CP1252");
472     else {
473 	/*
474 	 * Last resort = trim .enc off encpath to produce convname
475 	 */
476 	strcpy(convname, encpath);
477 	p = strrchr(convname, '.');
478 	if(p) *p = '\0';
479     }
480 }
481 
482 /* Load encoding array from a file: defaults to the R_HOME/library/grDevices/afm directory */
483 
484 /*
485  * encpath gives the file to read from
486  * encname is filled with the encoding name from the file
487  * encconvname is filled with a "translation" of the encoding name into
488  *             one that can be used with iconv()
489  * encnames is filled with the character names from the file
490  * enccode is filled with the raw source of the file
491  */
492 static int
LoadEncoding(const char * encpath,char * encname,char * encconvname,CNAME * encnames,char * enccode,Rboolean isPDF)493 LoadEncoding(const char *encpath, char *encname,
494 	     char *encconvname, CNAME *encnames,
495 	     char *enccode, Rboolean isPDF)
496 {
497     char buf[BUFSIZE]; // BUFSIZE is 512
498     int i;
499     FILE *fp;
500     EncodingInputState state;
501     state.p = state.p0 = NULL;
502 
503     seticonvName(encpath, encconvname);
504 
505     if(strchr(encpath, FILESEP[0])) strcpy(buf, encpath);
506     else snprintf(buf, BUFSIZE,"%s%slibrary%sgrDevices%senc%s%s",
507 		  R_Home, FILESEP, FILESEP, FILESEP, FILESEP, encpath);
508 #ifdef DEBUG_PS
509     Rprintf("encoding path is %s\n", buf);
510 #endif
511     if (!(fp = R_fopen(R_ExpandFileName(buf), "r"))) {
512 	strcat(buf, ".enc");
513 	if (!(fp = R_fopen(R_ExpandFileName(buf), "r"))) return 0;
514     }
515     if (GetNextItem(fp, buf, -1, &state)) { fclose(fp); return 0;} /* encoding name */
516     memcpy(encname, buf+1, 99); // was strncpy, deliberate truncation
517     encname[99] = '\0';
518     if (!isPDF) snprintf(enccode, 5000, "/%s [\n", encname);
519     else enccode[0] = '\0';
520     if (GetNextItem(fp, buf, 0, &state)) { fclose(fp); return 0;} /* [ */
521     for(i = 0; i < 256; i++) {
522 	if (GetNextItem(fp, buf, i, &state)) { fclose(fp); return 0; }
523 	memcpy(encnames[i].cname, buf+1, 39); // was strncpy, gcc10 warned
524 	encnames[i].cname[39] = '\0';
525 	strcat(enccode, " /"); strcat(enccode, encnames[i].cname);
526 	if(i%8 == 7) strcat(enccode, "\n");
527     }
528     if (GetNextItem(fp, buf, 0, &state)) { fclose(fp); return 0;} /* ] */
529     fclose(fp);
530     if (!isPDF) strcat(enccode,"]\n");
531     return 1;
532 }
533 
534 /* Load font metrics from a file: defaults to the
535    R_HOME/library/grDevices/afm directory */
536 static int
PostScriptLoadFontMetrics(const char * const fontpath,FontMetricInfo * metrics,char * fontname,CNAME * charnames,CNAME * encnames,int reencode)537 PostScriptLoadFontMetrics(const char * const fontpath,
538 			  FontMetricInfo *metrics,
539 			  char *fontname,
540 			  CNAME *charnames,
541 			  CNAME *encnames,
542 			  int reencode)
543 {
544     char buf[BUFSIZE], *p, truth[10];
545     int mode, i = 0, j, ii, nKPX=0;
546     gzFile fp;
547 
548     if(strchr(fontpath, FILESEP[0])) strcpy(buf, fontpath);
549     else
550 	snprintf(buf, BUFSIZE,"%s%slibrary%sgrDevices%safm%s%s.gz",
551 		 R_Home, FILESEP, FILESEP, FILESEP, FILESEP, fontpath);
552 #ifdef DEBUG_PS
553     Rprintf("afmpath is %s\n", buf);
554     Rprintf("reencode is %d\n", reencode);
555 #endif
556 
557     if (!(fp = R_gzopen(R_ExpandFileName(buf), "rb"))) {
558 	/* try uncompressed version */
559 	snprintf(buf, BUFSIZE,"%s%slibrary%sgrDevices%safm%s%s",
560 		 R_Home, FILESEP, FILESEP, FILESEP, FILESEP, fontpath);
561 	if (!(fp = R_gzopen(R_ExpandFileName(buf), "rb"))) {
562 	    warning(_("afm file '%s' could not be opened"),
563 		    R_ExpandFileName(buf));
564 	    return 0;
565 	}
566     }
567 
568     metrics->KernPairs = NULL;
569     metrics->CapHeight = metrics->XHeight = metrics->Descender =
570 	metrics->Ascender = metrics->StemH = metrics->StemV = NA_SHORT;
571     metrics->IsFixedPitch = -1;
572     metrics->ItalicAngle = 0;
573     mode = 0;
574     for (ii = 0; ii < 256; ii++) {
575 	charnames[ii].cname[0] = '\0';
576 	metrics->CharInfo[ii].WX = NA_SHORT;
577 	for(j = 0; j < 4; j++) metrics->CharInfo[ii].BBox[j] = 0;
578     }
579     while (R_gzgets(fp, buf, BUFSIZE)) {
580 	switch(KeyType(buf)) {
581 
582 	case StartFontMetrics:
583 	    mode = StartFontMetrics;
584 	    break;
585 
586 	case EndFontMetrics:
587 	    mode = 0;
588 	    break;
589 
590 	case FontBBox:
591 	    if (!GetFontBBox(buf, metrics)) {
592 		warning("'FontBBox' could not be parsed");
593 		goto pserror;
594 	    }
595 	    break;
596 
597 	case C:
598 	    if (mode != StartFontMetrics) goto pserror;
599 	    if (!GetCharInfo(buf, metrics, charnames, encnames, reencode)) {
600 		warning("'CharInfo' could not be parsed");
601 		goto pserror;
602 	    }
603 	    break;
604 
605 	case StartKernData:
606 	    mode = StartKernData;
607 	    break;
608 
609 	case StartKernPairs:
610 	    if(mode != StartKernData) goto pserror;
611 	    p = SkipToNextItem(buf);
612 	    sscanf(p, "%d", &nKPX);
613 	    if(nKPX > 0) {
614 		/* nPKX == 0 should not happen, but has */
615 		metrics->KernPairs = (KP *) malloc(nKPX * sizeof(KP));
616 		if (!metrics->KernPairs) goto pserror;
617 	    }
618 	    break;
619 
620 	case KPX:
621 	    if(mode != StartKernData || i >= nKPX) goto pserror;
622 	    if (GetKPX(buf, i, metrics, charnames)) i++;
623 	    break;
624 
625 	case EndKernData:
626 	    mode = 0;
627 	    break;
628 
629 	case Unknown:
630 	    warning(_("unknown AFM entity encountered"));
631 	    break;
632 
633 	case FontName:
634 	    p = SkipToNextItem(buf);
635 	    sscanf(p, "%[^\n\f\r]", fontname);
636 	    break;
637 
638 	case CapHeight:
639 	    p = SkipToNextItem(buf);
640 	    sscanf(p, "%hd", &metrics->CapHeight);
641 	    break;
642 
643 	case XHeight:
644 	    p = SkipToNextItem(buf);
645 	    sscanf(p, "%hd", &metrics->XHeight);
646 	    break;
647 
648 	case Ascender:
649 	    p = SkipToNextItem(buf);
650 	    sscanf(p, "%hd", &metrics->Ascender);
651 	    break;
652 
653 	case Descender:
654 	    p = SkipToNextItem(buf);
655 	    sscanf(p, "%hd", &metrics->Descender);
656 	    break;
657 
658 	case StdHW:
659 	    p = SkipToNextItem(buf);
660 	    sscanf(p, "%hd", &metrics->StemH);
661 	    break;
662 
663 	case StdVW:
664 	    p = SkipToNextItem(buf);
665 	    sscanf(p, "%hd", &metrics->StemV);
666 	    break;
667 
668 	case ItalicAngle:
669 	    p = SkipToNextItem(buf);
670 	    sscanf(p, "%hd", &metrics->ItalicAngle);
671 	    break;
672 
673 	case IsFixedPitch:
674 	    p = SkipToNextItem(buf);
675 	    sscanf(p, "%[^\n\f\r]", truth);
676 	    metrics->IsFixedPitch = strcmp(truth, "true") == 0;
677 	    break;
678 
679 	case Empty:
680 	default:
681 	    break;
682 	}
683     }
684     metrics->nKP = (short) i;
685     R_gzclose(fp);
686     /* Make an index for kern-pair searches: relies on having contiguous
687        blocks by first char for efficiency, but works in all cases. */
688     {
689 	short ind, tmp;
690 	for (j = 0; j < 256; j++) {
691 	    metrics->KPstart[j] = (short) i;
692 	    metrics->KPend[j] = 0;
693 	}
694 	for (j = 0; j < i; j++) {
695 	    ind = metrics->KernPairs[j].c1;
696 	    tmp = metrics->KPstart[ind];
697 	    if(j < tmp) metrics->KPstart[ind] = (short) j;
698 	    tmp = metrics->KPend[ind];
699 	    if(j > tmp) metrics->KPend[ind] = (short) j;
700 	}
701     }
702     return 1;
703 pserror:
704     R_gzclose(fp);
705     return 0;
706 }
707 
708 
709 #include <rlocale.h> /* for Ri18n_wcwidth */
710 
711 
712 static double
PostScriptStringWidth(const unsigned char * str,int enc,FontMetricInfo * metrics,Rboolean useKerning,int face,const char * encoding)713     PostScriptStringWidth(const unsigned char *str, int enc,
714 			  FontMetricInfo *metrics,
715 			  Rboolean useKerning,
716 			  int face, const char *encoding)
717 {
718     int sum = 0, i;
719     short wx;
720     const unsigned char *p = NULL, *str1 = str;
721     unsigned char p1, p2;
722 
723     int status;
724     if(!metrics && (face % 5) != 0) {
725 	/* This is the CID font case, and should only happen for
726 	   non-symbol fonts.  So we assume monospaced with multipliers.
727 	   We need to remap even if we are in a SBCS, should we get to here */
728 	size_t ucslen;
729 	ucslen = mbcsToUcs2((char *)str, NULL, 0, enc);
730 	if (ucslen != (size_t)-1) {
731 	    /* We convert the characters but not the terminator here */
732 	    R_CheckStack2(ucslen * sizeof(R_ucs2_t));
733 	    R_ucs2_t ucs2s[ucslen];
734 	    status = (int) mbcsToUcs2((char *)str, ucs2s, (int) ucslen, enc);
735 	    if (status >= 0)
736 		for(i = 0 ; i < ucslen ; i++) {
737 #ifdef USE_RI18N_WIDTH
738 		    wx = (short)(500 * Ri18n_wcwidth(ucs2s[i]));
739 #else
740 		    wx = (short)(500 * wcwidth(ucs2s[i]));
741 #endif
742 		    /* printf("width for U+%04x is %d\n", ucs2s[i], wx); */
743 		    sum += wx;
744 		}
745 	    else
746 		warning(_("invalid string in '%s'"), "PostScriptStringWidth");
747 	    return 0.001 * sum;
748 	} else {
749 	    warning(_("invalid string in '%s'"), "PostScriptStringWidth");
750 	    return 0.0;
751 	}
752     } else
753 	if(!strIsASCII((char *) str) &&
754 	   /*
755 	    * Every fifth font is a symbol font:
756 	    * see postscriptFonts()
757 	    */
758 	   (face % 5) != 0) {
759 	    R_CheckStack2(strlen((char *)str)+1);
760 	    char buff[strlen((char *)str)+1];
761 	    /* Output string cannot be longer */
762 	    mbcsToSbcs((char *)str, buff, encoding, enc);
763 	    str1 = (unsigned char *)buff;
764 	}
765 
766     /* safety */
767     if(!metrics) return 0.0;
768 
769 
770     /* Now we know we have an 8-bit encoded string in the encoding to
771        be used for output. */
772     for (p = str1; *p; p++) {
773 #ifdef USE_HYPHEN
774 	if (*p == '-' && !isdigit(p[1]))
775 	    wx = metrics->CharInfo[(int)PS_hyphen].WX;
776 	else
777 #endif
778 	    wx = metrics->CharInfo[*p].WX;
779 	if(wx == NA_SHORT)
780 	    warning(_("font width unknown for character 0x%x"), *p);
781 	else sum += wx;
782 
783 	if(useKerning) {
784 	    /* check for kerning adjustment */
785 	    p1 = p[0]; p2 = p[1];
786 	    for (i =  metrics->KPstart[p1]; i < metrics->KPend[p1]; i++)
787 		/* second test is a safety check: should all start with p1 */
788 		if(metrics->KernPairs[i].c2 == p2 &&
789 		   metrics->KernPairs[i].c1 == p1) {
790 		    sum += metrics->KernPairs[i].kern;
791 		    break;
792 		}
793 	}
794     }
795     return 0.001 * sum;
796 }
797 
798 
799 /* Be careful about the assumptions here.  In an 8-bit locale 0 <= c < 256
800    and it is in the encoding in use.  As it is not going to be
801    re-encoded when text is output, it is correct not to re-encode here.
802 
803    When called in an MBCS locale and font != 5, chars < 128 are sent
804    as is (we assume that is ASCII) and others are re-encoded to
805    Unicode in GEText (and interpreted as Unicode in GESymbol).
806 */
807 # ifdef WORDS_BIGENDIAN
808 static const char UCS2ENC[] = "UCS-2BE";
809 # else
810 static const char UCS2ENC[] = "UCS-2LE";
811 # endif
812 
813 static void
PostScriptMetricInfo(int c,double * ascent,double * descent,double * width,FontMetricInfo * metrics,Rboolean isSymbol,const char * encoding)814 PostScriptMetricInfo(int c, double *ascent, double *descent, double *width,
815 		     FontMetricInfo *metrics,
816 		     Rboolean isSymbol,
817 		     const char *encoding)
818 {
819     Rboolean Unicode = mbcslocale;
820 
821     if (c == 0) {
822 	*ascent = 0.001 * metrics->FontBBox[3];
823 	*descent = -0.001 * metrics->FontBBox[1];
824 	*width = 0.001 * (metrics->FontBBox[2] - metrics->FontBBox[0]);
825 	return;
826     }
827 
828     if (c < 0) { Unicode = TRUE; c = -c; }
829     /* We don't need the restriction to 65536 here any more as we could
830        convert from  UCS4ENC, but there are few language chars above 65536. */
831     if(Unicode && !isSymbol && c >= 128 && c < 65536) { /* Unicode */
832 	void *cd = NULL;
833 	const char *i_buf; char *o_buf, out[2];
834 	size_t i_len, o_len, status;
835 	unsigned short w[2];
836 
837 	if ((void*)-1 == (cd = Riconv_open(encoding, UCS2ENC)))
838 	    error(_("unknown encoding '%s' in 'PostScriptMetricInfo'"),
839 		  encoding);
840 
841 	/* Here we use terminated strings, but could use one char */
842 	w[0] = (unsigned short) c; w[1] = 0;
843 	i_buf = (char *)w;
844 	i_len = 4;
845 	o_buf = out;
846 	o_len = 2;
847 	status = Riconv(cd, &i_buf, (size_t *)&i_len,
848 			(char **)&o_buf, (size_t *)&o_len);
849 	Riconv_close(cd);
850 	if (status == (size_t)-1) {
851 	    *ascent = 0;
852 	    *descent = 0;
853 	    *width = 0;
854 	    warning(_("font metrics unknown for Unicode character U+%04x"), c);
855 	    return;
856 	} else {
857 	    c = out[0] & 0xff;
858 	}
859     }
860 
861     if (c > 255) { /* Unicode */
862 	*ascent = 0;
863 	*descent = 0;
864 	*width = 0;
865 	warning(_("font metrics unknown for Unicode character U+%04x"), c);
866     } else {
867 	short wx;
868 
869 	*ascent = 0.001 * metrics->CharInfo[c].BBox[3];
870 	*descent = -0.001 * metrics->CharInfo[c].BBox[1];
871 	wx = metrics->CharInfo[c].WX;
872 	if(wx == NA_SHORT) {
873 	    warning(_("font metrics unknown for character 0x%x"), c);
874 	    wx = 0;
875 	}
876 	*width = 0.001 * wx;
877     }
878 }
879 
880 static void
PostScriptCIDMetricInfo(int c,double * ascent,double * descent,double * width)881 PostScriptCIDMetricInfo(int c, double *ascent, double *descent, double *width)
882 {
883     /* calling in a SBCS is probably not intentional, but we should try to
884        cope sensibly. */
885     if(!mbcslocale && c > 0) {
886 	if (c > 255)
887 	    error(_("invalid character (%04x) sent to 'PostScriptCIDMetricInfo' in a single-byte locale"),
888 		  c);
889 	else {
890 	    /* convert to UCS-2 to use wcwidth. */
891 	    char str[2]={0,0};
892 	    R_ucs2_t out;
893 	    str[0] = (char) c;
894 	    if(mbcsToUcs2(str, &out, 1, CE_NATIVE) == (size_t)-1)
895 		error(_("invalid character sent to 'PostScriptCIDMetricInfo' in a single-byte locale"));
896 	    c = out;
897 	}
898     }
899 
900     /* Design values for all CJK fonts */
901     *ascent = 0.880;
902     *descent = -0.120;
903     if (c == 0 || c > 65535) *width = 1.;
904     else {
905 #ifdef USE_RI18N_WIDTH
906 	*width = 0.5*Ri18n_wcwidth(c);
907 #else
908 	*width = 0.5*wcwidth(c);
909 #endif
910     }
911 }
912 
913 
914 /*******************************************************
915  * Data structures and functions for loading Type 1 fonts into an R session.
916  *
917  * Used by PostScript, XFig and PDF drivers.
918  *
919  * The idea is that font information is only loaded once for each font
920  * within an R session.  Also, each encoding is only loaded once per
921  * session.  A global list of loaded fonts and a global list of
922  * loaded encodings are maintained.  Devices maintain their own list
923  * of fonts and encodings used on the device;  the elements of these
924  * lists are just pointers to the elements of the global lists.
925  *
926  * Cleaning up device lists just involves free'ing the lists themselves.
927  * When the R session closes, the actual font and encoding information
928  * is unloaded using the global lists.
929  */
930 
931 /*
932  * Information about one Type 1 font
933  */
934 typedef struct CIDFontInfo {
935     char name[50];
936 } CIDFontInfo, *cidfontinfo;
937 
938 typedef struct T1FontInfo {
939     char name[50];
940     FontMetricInfo metrics;
941     CNAME charnames[256];
942 } Type1FontInfo, *type1fontinfo;
943 
944 /*
945  * Information about a font encoding
946  */
947 typedef struct EncInfo {
948     char encpath[PATH_MAX];
949     char name[100]; /* Name written to PostScript/PDF file */
950     char convname[50]; /* Name used in mbcsToSbcs() with iconv() */
951     CNAME encnames[256];
952     char enccode[5000];
953 } EncodingInfo, *encodinginfo;
954 
955 /*
956  * Information about a font family
957  * (5 fonts representing plain, bold, italic, bolditalic, and symbol)
958  *
959  * The name is a graphics engine font family name
960  * (distinct from the Type 1 font name)
961  */
962 typedef struct CIDFontFamily {
963     char fxname[50];
964     cidfontinfo cidfonts[4];
965     type1fontinfo symfont;
966     char cmap[50];
967     char encoding[50];
968 } CIDFontFamily, *cidfontfamily;
969 
970 typedef struct T1FontFamily {
971     char fxname[50];
972     type1fontinfo fonts[5];
973     encodinginfo encoding;
974 } Type1FontFamily, *type1fontfamily;
975 
976 /*
977  * A list of Type 1 font families
978  *
979  * Used to keep track of fonts currently loaded in the session
980  * AND by each device to keep track of fonts currently used on the device.
981  */
982 typedef struct CIDFontList {
983     cidfontfamily cidfamily;
984     struct CIDFontList *next;
985 } CIDFontList, *cidfontlist;
986 
987 typedef struct T1FontList {
988     type1fontfamily family;
989     struct T1FontList *next;
990 } Type1FontList, *type1fontlist;
991 
992 /*
993  * Same as type 1 font list, but for encodings.
994  */
995 typedef struct EncList {
996     encodinginfo encoding;
997     struct EncList *next;
998 } EncodingList, *encodinglist;
999 
1000 /*
1001  * Various constructors and destructors
1002  */
makeCIDFont()1003 static cidfontinfo makeCIDFont()
1004 {
1005     cidfontinfo font = (CIDFontInfo *) malloc(sizeof(CIDFontInfo));
1006     if (!font)
1007 	warning(_("failed to allocate CID font info"));
1008     return font;
1009 }
1010 
makeType1Font()1011 static type1fontinfo makeType1Font()
1012 {
1013     type1fontinfo font = (Type1FontInfo *) malloc(sizeof(Type1FontInfo));
1014     if (font) {
1015 	/*
1016 	 * Initialise font->metrics.KernPairs to NULL
1017 	 * so that we know NOT to free it if we fail to
1018 	 * load this font and have to
1019 	 * bail out and free this type1fontinfo
1020 	 */
1021 	font->metrics.KernPairs = NULL;
1022     } else
1023 	warning(_("failed to allocate Type 1 font info"));
1024     return font;
1025 }
1026 
freeCIDFont(cidfontinfo font)1027 static void freeCIDFont(cidfontinfo font)
1028 {
1029     free(font);
1030 }
1031 
freeType1Font(type1fontinfo font)1032 static void freeType1Font(type1fontinfo font)
1033 {
1034     if (font->metrics.KernPairs)
1035 	free(font->metrics.KernPairs);
1036     free(font);
1037 }
1038 
makeEncoding()1039 static encodinginfo makeEncoding()
1040 {
1041     encodinginfo encoding = (EncodingInfo *) malloc(sizeof(EncodingInfo));
1042     if (!encoding)
1043 	warning(_("failed to allocate encoding info"));
1044     return encoding;
1045 }
1046 
freeEncoding(encodinginfo encoding)1047 static void freeEncoding(encodinginfo encoding)
1048 {
1049     free(encoding);
1050 }
1051 
makeCIDFontFamily()1052 static cidfontfamily makeCIDFontFamily()
1053 {
1054     cidfontfamily family = (CIDFontFamily *) malloc(sizeof(CIDFontFamily));
1055     if (family) {
1056 	int i;
1057 	for (i = 0; i < 4; i++)
1058 	    family->cidfonts[i] = NULL;
1059 	family->symfont = NULL;
1060     } else
1061 	warning(_("failed to allocate CID font family"));
1062     return family;
1063 }
1064 
makeFontFamily()1065 static type1fontfamily makeFontFamily()
1066 {
1067     type1fontfamily family = (Type1FontFamily *) malloc(sizeof(Type1FontFamily));
1068     if (family) {
1069 	int i;
1070 	for (i = 0; i < 5; i++)
1071 	    family->fonts[i] = NULL;
1072 	family->encoding = NULL;
1073     } else
1074 	warning(_("failed to allocate Type 1 font family"));
1075     return family;
1076 }
1077 /*
1078  * Frees a font family, including fonts, but NOT encoding
1079  *
1080  * Used by global font list to free all fonts loaded in session
1081  * (should not be used by devices; else may free fonts more than once)
1082  *
1083  * Encodings are freed using the global encoding list
1084  * (to ensure that each encoding is only freed once)
1085  */
freeCIDFontFamily(cidfontfamily family)1086 static void freeCIDFontFamily(cidfontfamily family)
1087 {
1088     int i;
1089     for (i = 0; i < 4; i++)
1090 	if (family->cidfonts[i])
1091 	    freeCIDFont(family->cidfonts[i]);
1092     if (family->symfont)
1093 	freeType1Font(family->symfont);
1094     free(family);
1095 }
1096 
freeFontFamily(type1fontfamily family)1097 static void freeFontFamily(type1fontfamily family)
1098 {
1099     int i;
1100     for (i=0; i<5; i++)
1101 	if (family->fonts[i])
1102 	    freeType1Font(family->fonts[i]);
1103     free(family);
1104 }
1105 
makeCIDFontList()1106 static cidfontlist makeCIDFontList()
1107 {
1108     cidfontlist fontlist = (CIDFontList *) malloc(sizeof(CIDFontList));
1109     if (fontlist) {
1110 	fontlist->cidfamily = NULL;
1111 	fontlist->next = NULL;
1112     } else
1113 	warning(_("failed to allocate font list"));
1114     return fontlist;
1115 }
1116 
makeFontList()1117 static type1fontlist makeFontList()
1118 {
1119     type1fontlist fontlist = (Type1FontList *) malloc(sizeof(Type1FontList));
1120     if (fontlist) {
1121 	fontlist->family = NULL;
1122 	fontlist->next = NULL;
1123     } else
1124 	warning(_("failed to allocate font list"));
1125     return fontlist;
1126 }
1127 
1128 /*
1129  * Just free the Type1FontList structure, do NOT free elements it points to
1130  *
1131  * Used by both global font list and devices to free the font lists
1132  * (global font list separately takes care of the fonts pointed to)
1133  */
freeCIDFontList(cidfontlist fontlist)1134 static void freeCIDFontList(cidfontlist fontlist) {
1135     /*
1136      * These will help to find any errors if attempt to
1137      * use freed font list.
1138      */
1139     fontlist->cidfamily = NULL;
1140     fontlist->next = NULL;
1141     free(fontlist);
1142 }
freeFontList(type1fontlist fontlist)1143 static void freeFontList(type1fontlist fontlist) {
1144     /*
1145      * These will help to find any errors if attempt to
1146      * use freed font list.
1147      */
1148     fontlist->family = NULL;
1149     fontlist->next = NULL;
1150     free(fontlist);
1151 }
1152 
freeDeviceCIDFontList(cidfontlist fontlist)1153 static void freeDeviceCIDFontList(cidfontlist fontlist) {
1154     if (fontlist) {
1155 	if (fontlist->next)
1156 	    freeDeviceCIDFontList(fontlist->next);
1157 	freeCIDFontList(fontlist);
1158     }
1159 }
freeDeviceFontList(type1fontlist fontlist)1160 static void freeDeviceFontList(type1fontlist fontlist) {
1161     if (fontlist) {
1162 	if (fontlist->next)
1163 	    freeDeviceFontList(fontlist->next);
1164 	freeFontList(fontlist);
1165     }
1166 }
1167 
makeEncList()1168 static encodinglist makeEncList()
1169 {
1170     encodinglist enclist = (EncodingList *) malloc(sizeof(EncodingList));
1171     if (enclist) {
1172 	enclist->encoding = NULL;
1173 	enclist->next = NULL;
1174     } else
1175 	warning(_("failed to allocated encoding list"));
1176     return enclist;
1177 }
1178 
freeEncList(encodinglist enclist)1179 static void freeEncList(encodinglist enclist)
1180 {
1181     enclist->encoding = NULL;
1182     enclist->next = NULL;
1183     free(enclist);
1184 }
1185 
freeDeviceEncList(encodinglist enclist)1186 static void freeDeviceEncList(encodinglist enclist) {
1187     if (enclist) {
1188 	if (enclist->next)
1189 	    freeDeviceEncList(enclist->next);
1190 	freeEncList(enclist);
1191     }
1192 }
1193 
1194 /*
1195  * Global list of fonts and encodings that have been loaded this session
1196  */
1197 static cidfontlist loadedCIDFonts = NULL;
1198 static type1fontlist loadedFonts = NULL;
1199 static encodinglist loadedEncodings = NULL;
1200 /*
1201  * There are separate PostScript and PDF font databases at R level
1202  * so MUST have separate C level records too
1203  * (because SAME device-independent font family name could map
1204  *  to DIFFERENT font for PostScript and PDF)
1205  */
1206 static cidfontlist PDFloadedCIDFonts = NULL;
1207 static type1fontlist PDFloadedFonts = NULL;
1208 static encodinglist PDFloadedEncodings = NULL;
1209 
1210 /*
1211  * Names of R level font databases
1212  */
1213 static char PostScriptFonts[] = ".PostScript.Fonts";
1214 static char PDFFonts[] = ".PDF.Fonts";
1215 
1216 /*
1217  * Free the above globals
1218  *
1219  * NOTE that freeing the font families does NOT free the encodings
1220  * Hence we free all encodings first.
1221  */
1222 
1223 /* NB this is exported, and was at some point used by KillAllDevices
1224    in src/main/graphics.c.  That would be a problem now it is in a
1225    separate DLL.
1226 */
1227 #if 0
1228 void freeType1Fonts()
1229 {
1230     encodinglist enclist = loadedEncodings;
1231     type1fontlist fl = loadedFonts;
1232     cidfontlist   cidfl = loadedCIDFonts;
1233     type1fontlist pdffl = PDFloadedFonts;
1234     cidfontlist   pdfcidfl = PDFloadedCIDFonts;
1235     while (enclist) {
1236 	enclist = enclist->next;
1237 	freeEncoding(loadedEncodings->encoding);
1238 	freeEncList(loadedEncodings);
1239 	loadedEncodings = enclist;
1240     }
1241     while (fl) {
1242 	fl = fl->next;
1243 	freeFontFamily(loadedFonts->family);
1244 	freeFontList(loadedFonts);
1245 	loadedFonts = fl;
1246     }
1247     while (cidfl) {
1248 	cidfl = cidfl->next;
1249 	freeCIDFontFamily(loadedCIDFonts->cidfamily);
1250 	freeCIDFontList(loadedCIDFonts);
1251 	loadedCIDFonts = cidfl;
1252     }
1253     while (pdffl) {
1254 	pdffl = pdffl->next;
1255 	freeFontFamily(PDFloadedFonts->family);
1256 	freeFontList(PDFloadedFonts);
1257 	PDFloadedFonts = pdffl;
1258     }
1259     while (pdfcidfl) {
1260 	pdfcidfl = pdfcidfl->next;
1261 	freeCIDFontFamily(PDFloadedCIDFonts->cidfamily);
1262 	freeCIDFontList(PDFloadedCIDFonts);
1263 	PDFloadedCIDFonts = pdfcidfl;
1264     }
1265 }
1266 #endif
1267 
1268 /*
1269  * Given a path to an encoding file,
1270  * find an EncodingInfo that corresponds
1271  */
1272 static encodinginfo
findEncoding(const char * encpath,encodinglist deviceEncodings,Rboolean isPDF)1273 findEncoding(const char *encpath, encodinglist deviceEncodings, Rboolean isPDF)
1274 {
1275     encodinglist enclist = isPDF ? PDFloadedEncodings : loadedEncodings;
1276     encodinginfo encoding = NULL;
1277     int found = 0;
1278     /*
1279      * "default" is a special encoding which means use the
1280      * default (FIRST) encoding set up ON THIS DEVICE.
1281      */
1282     if (!strcmp(encpath, "default")) {
1283 	found = 1;
1284 	// called from PDFDeviceDriver with null deviceEncodings as last resort
1285 	if (deviceEncodings) encoding = deviceEncodings->encoding;
1286     } else {
1287 	while (enclist && !found) {
1288 	    found = !strcmp(encpath, enclist->encoding->encpath);
1289 	    if (found)
1290 		encoding = enclist->encoding;
1291 	    enclist = enclist->next;
1292 	}
1293     }
1294     return encoding;
1295 }
1296 
1297 /*
1298  * Find an encoding in device encoding list
1299  */
1300 static encodinginfo
findDeviceEncoding(const char * encpath,encodinglist enclist,int * index)1301 findDeviceEncoding(const char *encpath, encodinglist enclist, int *index)
1302 {
1303     encodinginfo encoding = NULL;
1304     int found = 0;
1305     *index = 0;
1306     while (enclist && !found) {
1307 	found = !strcmp(encpath, enclist->encoding->encpath);
1308 	if (found)
1309 	    encoding = enclist->encoding;
1310 	enclist = enclist->next;
1311 	*index = *index + 1;
1312     }
1313     return encoding;
1314 }
1315 
1316 /*
1317  * Utility to avoid string overrun
1318  */
safestrcpy(char * dest,const char * src,int maxlen)1319 static void safestrcpy(char *dest, const char *src, int maxlen)
1320 {
1321     if (strlen(src) < maxlen)
1322 	strcpy(dest, src);
1323     else {
1324 	warning(_("truncated string which was too long for copy"));
1325 	strncpy(dest, src, maxlen-1);
1326 	dest[maxlen-1] = '\0';
1327     }
1328 }
1329 
1330 /*
1331  * Add an encoding to the list of loaded encodings ...
1332  *
1333  * ... and return the new encoding
1334  */
addEncoding(const char * encpath,Rboolean isPDF)1335 static encodinginfo addEncoding(const char *encpath, Rboolean isPDF)
1336 {
1337     encodinginfo encoding = makeEncoding();
1338     if (encoding) {
1339 	if (LoadEncoding(encpath,
1340 			 encoding->name,
1341 			 encoding->convname,
1342 			 encoding->encnames,
1343 			 encoding->enccode,
1344 			 isPDF)) {
1345 	    encodinglist newenc = makeEncList();
1346 	    if (!newenc) {
1347 		freeEncoding(encoding);
1348 		encoding = NULL;
1349 	    } else {
1350 		encodinglist enclist =
1351 		    isPDF ? PDFloadedEncodings : loadedEncodings;
1352 		safestrcpy(encoding->encpath, encpath, PATH_MAX);
1353 		newenc->encoding = encoding;
1354 		if (!enclist) {
1355 		    if(isPDF) PDFloadedEncodings = newenc;
1356 		    else loadedEncodings = newenc;
1357 		} else {
1358 		    while (enclist->next)
1359 			enclist = enclist->next;
1360 		    enclist->next = newenc;
1361 		}
1362 	    }
1363 	} else {
1364 	    warning(_("failed to load encoding file '%s'"), encpath);
1365 	    freeEncoding(encoding);
1366 	    encoding = NULL;
1367 	}
1368     } else
1369 	encoding = NULL;
1370     return encoding;
1371 }
1372 
1373 /*
1374  * Add an encoding to a list of device encodings ...
1375  *
1376  * ... and return the new list
1377  */
addDeviceEncoding(encodinginfo encoding,encodinglist devEncs)1378 static encodinglist addDeviceEncoding(encodinginfo encoding,
1379 				      encodinglist devEncs)
1380 {
1381     encodinglist newenc = makeEncList();
1382     if (!newenc) {
1383 	devEncs = NULL;
1384     } else {
1385 	encodinglist enclist = devEncs;
1386 	newenc->encoding = encoding;
1387 	if (!devEncs)
1388 	    devEncs = newenc;
1389 	else {
1390 	    while (enclist->next)
1391 		enclist = enclist->next;
1392 	    enclist->next = newenc;
1393 	}
1394     }
1395     return devEncs;
1396 }
1397 
1398 /*
1399  * Given a graphics engine font family name,
1400  * find a Type1FontFamily that corresponds
1401  *
1402  * If get fxname match, check whether the encoding in the
1403  * R database is "default"
1404  * (i.e., the graphics engine font family encoding is unspecified)
1405  * If it is "default" then check that the loaded encoding is the
1406  * same as the encoding we want.  A matching encoding is defined
1407  * as one which leads to the same iconvname (see seticonvName()).
1408  * This could perhaps be made more rigorous by actually looking inside
1409  * the relevant encoding file for the encoding name.
1410  *
1411  * If the encoding we want is NULL, then we just don't care.
1412  *
1413  * Returns NULL if can't find font in loadedFonts
1414  */
1415 
1416 static const char *getFontEncoding(const char *family, const char *fontdbname);
1417 
1418 static type1fontfamily
findLoadedFont(const char * name,const char * encoding,Rboolean isPDF)1419 findLoadedFont(const char *name, const char *encoding, Rboolean isPDF)
1420 {
1421     type1fontlist fontlist;
1422     type1fontfamily font = NULL;
1423     char *fontdbname;
1424     int found = 0;
1425 
1426     if (isPDF) {
1427 	fontlist = PDFloadedFonts;
1428 	fontdbname = PDFFonts;
1429     } else {
1430 	fontlist = loadedFonts;
1431 	fontdbname = PostScriptFonts;
1432     }
1433     while (fontlist && !found) {
1434 	found = !strcmp(name, fontlist->family->fxname);
1435 	if (found) {
1436 	    font = fontlist->family;
1437 	    if (encoding) {
1438 		char encconvname[50];
1439 		const char *encname = getFontEncoding(name, fontdbname);
1440 		// encname could be NULL
1441 		if(encname) {
1442 		    seticonvName(encoding, encconvname);
1443 		    if (!strcmp(encname, "default") &&
1444 			strcmp(fontlist->family->encoding->convname,
1445 			       encconvname)) {
1446 			font = NULL;
1447 			found = 0;
1448 		    }
1449 		} else {
1450 		    font = NULL;
1451 		    found = 0;
1452 		}
1453 	    }
1454 	}
1455 	fontlist = fontlist->next;
1456     }
1457     return font;
1458 }
1459 
Type1FontInUse(SEXP name,SEXP isPDF)1460 SEXP Type1FontInUse(SEXP name, SEXP isPDF)
1461 {
1462     if (!isString(name) || LENGTH(name) > 1)
1463 	error(_("invalid font name or more than one font name"));
1464     return ScalarLogical(
1465 	findLoadedFont(CHAR(STRING_ELT(name, 0)), NULL, asLogical(isPDF))
1466 	!= NULL);
1467 }
1468 
findLoadedCIDFont(const char * family,Rboolean isPDF)1469 static cidfontfamily findLoadedCIDFont(const char *family, Rboolean isPDF)
1470 {
1471     cidfontlist fontlist;
1472     cidfontfamily font = NULL;
1473     int found = 0;
1474 
1475     if (isPDF) {
1476 	fontlist = PDFloadedCIDFonts;
1477     } else {
1478 	fontlist = loadedCIDFonts;
1479     }
1480     while (fontlist && !found) {
1481 	found = !strcmp(family, fontlist->cidfamily->cidfonts[0]->name);
1482 	if (found)
1483 	    font = fontlist->cidfamily;
1484 	fontlist = fontlist->next;
1485     }
1486 #ifdef PS_DEBUG
1487     if(found)
1488 	Rprintf("findLoadedCIDFont found = %s\n",family);
1489 #endif
1490     return font;
1491 }
1492 
CIDFontInUse(SEXP name,SEXP isPDF)1493 SEXP CIDFontInUse(SEXP name, SEXP isPDF)
1494 {
1495     if (!isString(name) || LENGTH(name) > 1)
1496 	error(_("invalid font name or more than one font name"));
1497     return ScalarLogical(
1498 	findLoadedCIDFont(CHAR(STRING_ELT(name, 0)), asLogical(isPDF))
1499 	!= NULL);
1500 }
1501 
1502 /*
1503  * Find a font in device font list
1504  */
1505 static cidfontfamily
findDeviceCIDFont(const char * name,cidfontlist fontlist,int * index)1506 findDeviceCIDFont(const char *name, cidfontlist fontlist, int *index)
1507 {
1508     cidfontfamily font = NULL;
1509     int found = 0;
1510     *index = 0;
1511     /*
1512      * If the graphics engine font family is ""
1513      * just use the default font that was loaded when the device
1514      * was created.
1515      * This will (MUST) be the first font in the device
1516      */
1517 #ifdef DEBUG_PS
1518     Rprintf("findDeviceCIDFont=%s\n", name);
1519     Rprintf("? cidfontlist %s\n", (fontlist) ? "found" : "not found");
1520 #endif
1521 
1522     if (strlen(name) > 0) {
1523 	while (fontlist && !found) {
1524 #ifdef DEBUG_PS
1525 	    Rprintf("findDeviceCIDFont=%s\n", name);
1526 	    Rprintf("findDeviceCIDFont fontlist->cidfamily->name=%s\n",
1527 		    fontlist->cidfamily->fxname);
1528 #endif
1529 
1530 	    found = !strcmp(name, fontlist->cidfamily->fxname);
1531 	    if (found)
1532 		font = fontlist->cidfamily;
1533 	    fontlist = fontlist->next;
1534 	    *index = *index + 1;
1535 	}
1536     } else {
1537 	font = fontlist->cidfamily;
1538 	*index = 1;
1539     }
1540 #ifdef DEBUG_PS
1541     Rprintf("findDeviceCIDFont find index=%d\n", *index);
1542     Rprintf("findDeviceCIDFont find font=%s\n", (font) ? "Found" : "NULL");
1543 #endif
1544     return font;
1545 }
1546 
1547 /*
1548  * Must only be called once a device has at least one font added
1549  * (i.e., after the default font has been added)
1550  */
1551 static type1fontfamily
findDeviceFont(const char * name,type1fontlist fontlist,int * index)1552 findDeviceFont(const char *name, type1fontlist fontlist, int *index)
1553 {
1554     type1fontfamily font = NULL;
1555     int found = 0;
1556     *index = 0;
1557     /*
1558      * If the graphics engine font family is ""
1559      * just use the default font that was loaded when the device
1560      * was created.
1561      * This will (MUST) be the first font in the device
1562      */
1563     if (strlen(name) > 0) {
1564 	while (fontlist && !found) {
1565 	    found = !strcmp(name, fontlist->family->fxname);
1566 	    if (found)
1567 		font = fontlist->family;
1568 	    fontlist = fontlist->next;
1569 	    *index = *index + 1;
1570 	}
1571     } else {
1572 	font = fontlist->family;
1573 	*index = 1;
1574     }
1575     return font;
1576 }
1577 
1578 /*
1579  * Get an R-level font database
1580  */
getFontDB(const char * fontdbname)1581 static SEXP getFontDB(const char *fontdbname) {
1582     SEXP graphicsNS, PSenv;
1583     SEXP fontdb;
1584     PROTECT(graphicsNS = R_FindNamespace(ScalarString(mkChar("grDevices"))));
1585     PROTECT(PSenv = findVar(install(".PSenv"), graphicsNS));
1586     /* under lazy loading this will be a promise on first use */
1587     if(TYPEOF(PSenv) == PROMSXP) {
1588 	PROTECT(PSenv);
1589 	PSenv = eval(PSenv, graphicsNS);
1590 	UNPROTECT(2);
1591 	PROTECT(PSenv);
1592     }
1593     PROTECT(fontdb = findVar(install(fontdbname), PSenv));
1594     UNPROTECT(3);
1595     return fontdb;
1596 }
1597 
1598 /*
1599  * Get an R-level font object
1600  */
getFont(const char * family,const char * fontdbname)1601 static SEXP getFont(const char *family, const char *fontdbname) {
1602     int i, nfonts;
1603     SEXP result = R_NilValue;
1604     int found = 0;
1605     SEXP fontdb = PROTECT(getFontDB(fontdbname));
1606     SEXP fontnames;
1607     PROTECT(fontnames = getAttrib(fontdb, R_NamesSymbol));
1608     nfonts = LENGTH(fontdb);
1609     for (i=0; i<nfonts && !found; i++) {
1610 	const char *fontFamily = CHAR(STRING_ELT(fontnames, i));
1611 	if (strcmp(family, fontFamily) == 0) {
1612 	    found = 1;
1613 	    result = VECTOR_ELT(fontdb, i);
1614 	}
1615     }
1616     if (!found)
1617 	warning(_("font family '%s' not found in PostScript font database"),
1618 		family);
1619     UNPROTECT(2);
1620     return result;
1621 }
1622 
1623 /*
1624  * Get the path to the afm file for a user-specifed font
1625  * given a graphics engine font family and the face
1626  * index (0..4)
1627  *
1628  * Do this by looking up the font name in the PostScript
1629  * font database
1630  */
1631 static const char*
fontMetricsFileName(const char * family,int faceIndex,const char * fontdbname)1632 fontMetricsFileName(const char *family, int faceIndex,
1633 		    const char *fontdbname)
1634 {
1635     int i, nfonts;
1636     const char *result = NULL;
1637     int found = 0;
1638     SEXP fontdb = PROTECT(getFontDB(fontdbname));
1639     SEXP fontnames;
1640     PROTECT(fontnames = getAttrib(fontdb, R_NamesSymbol));
1641     nfonts = LENGTH(fontdb);
1642     for (i = 0; i < nfonts && !found; i++) {
1643 	const char *fontFamily = CHAR(STRING_ELT(fontnames, i));
1644 	if (strcmp(family, fontFamily) == 0) {
1645 	    found = 1;
1646 	    /* 1 means vector of font afm file paths */
1647 	    result = CHAR(STRING_ELT(VECTOR_ELT(VECTOR_ELT(fontdb, i), 1),
1648 				     faceIndex));
1649 	}
1650     }
1651     if (!found)
1652 	warning(_("font family '%s' not found in PostScript font database"),
1653 		family);
1654     UNPROTECT(2);
1655     return result;
1656 }
1657 
getFontType(const char * family,const char * fontdbname)1658 static const char *getFontType(const char *family, const char *fontdbname)
1659 {
1660     const char *result = NULL;
1661     SEXP font = getFont(family, fontdbname);
1662     if (!isNull(font)) {
1663         result = CHAR(STRING_ELT(getAttrib(font, R_ClassSymbol), 0));
1664     }
1665     return result;
1666 }
1667 
isType1Font(const char * family,const char * fontdbname,type1fontfamily defaultFont)1668 static Rboolean isType1Font(const char *family, const char *fontdbname,
1669 			    type1fontfamily defaultFont)
1670 {
1671     /*
1672      * If family is "" then we're referring to the default device
1673      * font, so the test is just whether the default font is
1674      * type1
1675      *
1676      * If loading font, send NULL for defaultFont
1677      */
1678     if (strlen(family) == 0) {
1679 	if (defaultFont)
1680 	    return TRUE;
1681 	else
1682 	    return FALSE;
1683     } else {
1684         const char *fontType = getFontType(family, fontdbname);
1685         if (fontType)
1686             return !strcmp(fontType, "Type1Font");
1687         else
1688             return FALSE;
1689     }
1690 }
1691 
isCIDFont(const char * family,const char * fontdbname,cidfontfamily defaultCIDFont)1692 static Rboolean isCIDFont(const char *family, const char *fontdbname,
1693 			  cidfontfamily defaultCIDFont) {
1694     /*
1695      * If family is "" then we're referring to the default device
1696      * font, so the test is just whether the default font is
1697      * type1
1698      *
1699      * If loading font, send NULL for defaultCIDFont
1700      */
1701     if (strlen(family) == 0) {
1702 	if (defaultCIDFont)
1703 	    return TRUE;
1704 	else
1705 	    return FALSE;
1706     } else {
1707         const char *fontType = getFontType(family, fontdbname);
1708         if (fontType)
1709             return !strcmp(fontType, "CIDFont");
1710         else
1711             return FALSE;
1712     }
1713 }
1714 
1715 /*
1716  * Get encoding name from font database
1717  */
getFontEncoding(const char * family,const char * fontdbname)1718 static const char *getFontEncoding(const char *family, const char *fontdbname)
1719 {
1720     SEXP fontnames;
1721     int i, nfonts;
1722     const char *result = NULL;
1723     int found = 0;
1724     SEXP fontdb = PROTECT(getFontDB(fontdbname));
1725     PROTECT(fontnames = getAttrib(fontdb, R_NamesSymbol));
1726     nfonts = LENGTH(fontdb);
1727     for (i=0; i<nfonts && !found; i++) {
1728 	const char *fontFamily = CHAR(STRING_ELT(fontnames, i));
1729 	if (strcmp(family, fontFamily) == 0) {
1730 	    found = 1;
1731 	    /* 2 means 'encoding' element */
1732 	    result = CHAR(STRING_ELT(VECTOR_ELT(VECTOR_ELT(fontdb, i), 2), 0));
1733 	}
1734     }
1735     if (!found)
1736 	warning(_("font encoding for family '%s' not found in font database"),
1737 		family);
1738     UNPROTECT(2);
1739     return result;
1740 }
1741 
1742 /*
1743  * Get Font name from font database
1744  */
getFontName(const char * family,const char * fontdbname)1745 static const char *getFontName(const char *family, const char *fontdbname)
1746 {
1747     SEXP fontnames;
1748     int i, nfonts;
1749     const char *result = NULL;
1750     int found = 0;
1751     SEXP fontdb = PROTECT(getFontDB(fontdbname));
1752     PROTECT(fontnames = getAttrib(fontdb, R_NamesSymbol));
1753     nfonts = LENGTH(fontdb);
1754     for (i=0; i<nfonts && !found; i++) {
1755 	const char *fontFamily = CHAR(STRING_ELT(fontnames, i));
1756 	if (strcmp(family, fontFamily) == 0) {
1757 	    found = 1;
1758 	    /* 0 means 'family' element */
1759 	    result = CHAR(STRING_ELT(VECTOR_ELT(VECTOR_ELT(fontdb, i), 0), 0));
1760 	}
1761     }
1762     if (!found)
1763 	warning(_("font CMap for family '%s' not found in font database"),
1764 		family);
1765     UNPROTECT(2);
1766     return result;
1767 }
1768 
1769 /*
1770  * Get CMap name from font database
1771  */
getFontCMap(const char * family,const char * fontdbname)1772 static const char *getFontCMap(const char *family, const char *fontdbname)
1773 {
1774     SEXP fontnames;
1775     int i, nfonts;
1776     const char *result = NULL;
1777     int found = 0;
1778     SEXP fontdb = PROTECT(getFontDB(fontdbname));
1779     PROTECT(fontnames = getAttrib(fontdb, R_NamesSymbol));
1780     nfonts = LENGTH(fontdb);
1781     for (i=0; i<nfonts && !found; i++) {
1782 	const char *fontFamily = CHAR(STRING_ELT(fontnames, i));
1783 	if (strcmp(family, fontFamily) == 0) {
1784 	    found = 1;
1785 	    /* 2 means 'cmap' element */
1786 	    result = CHAR(STRING_ELT(VECTOR_ELT(VECTOR_ELT(fontdb, i), 2), 0));
1787 	}
1788     }
1789     if (!found)
1790 	warning(_("font CMap for family '%s' not found in font database"),
1791 		family);
1792     UNPROTECT(2);
1793     return result;
1794 }
1795 
1796 /*
1797  * Get Encoding name from CID font in font database
1798  */
1799 static const char *
getCIDFontEncoding(const char * family,const char * fontdbname)1800 getCIDFontEncoding(const char *family, const char *fontdbname)
1801 {
1802     SEXP fontnames;
1803     int i, nfonts;
1804     const char *result = NULL;
1805     int found = 0;
1806     SEXP fontdb = PROTECT(getFontDB(fontdbname));
1807     PROTECT(fontnames = getAttrib(fontdb, R_NamesSymbol));
1808     nfonts = LENGTH(fontdb);
1809     for (i=0; i<nfonts && !found; i++) {
1810 	const char *fontFamily = CHAR(STRING_ELT(fontnames, i));
1811 	if (strcmp(family, fontFamily) == 0) {
1812 	    found = 1;
1813 	    /* 3 means 'encoding' element */
1814 	    result = CHAR(STRING_ELT(VECTOR_ELT(VECTOR_ELT(fontdb, i), 3), 0));
1815 	}
1816     }
1817     if (!found)
1818 	warning(_("font encoding for family '%s' not found in font database"),
1819 		family);
1820     UNPROTECT(2);
1821     return result;
1822 }
1823 
1824 /*
1825  * Get Encoding name from CID font in font database
1826  */
getCIDFontPDFResource(const char * family)1827 static const char *getCIDFontPDFResource(const char *family)
1828 {
1829     SEXP fontnames;
1830     int i, nfonts;
1831     const char *result = NULL;
1832     int found = 0;
1833     SEXP fontdb = PROTECT(getFontDB(PDFFonts));
1834     PROTECT(fontnames = getAttrib(fontdb, R_NamesSymbol));
1835     nfonts = LENGTH(fontdb);
1836     for (i=0; i<nfonts && !found; i++) {
1837 	const char *fontFamily = CHAR(STRING_ELT(fontnames, i));
1838 	if (strcmp(family, fontFamily) == 0) {
1839 	    found = 1;
1840 	    /* 4 means 'pdfresource' element */
1841 	    result = CHAR(STRING_ELT(VECTOR_ELT(VECTOR_ELT(fontdb, i), 4), 0));
1842 	}
1843     }
1844     if (!found)
1845 	warning(_("font encoding for family '%s' not found in font database"),
1846 		family);
1847     UNPROTECT(2);
1848     return result;
1849 }
1850 
1851 /*
1852  * Add a graphics engine font family/encoding to the list of loaded fonts ...
1853  *
1854  * ... and return the new font
1855  */
addLoadedCIDFont(cidfontfamily font,Rboolean isPDF)1856 static cidfontfamily addLoadedCIDFont(cidfontfamily font, Rboolean isPDF)
1857 {
1858     cidfontlist newfont = makeCIDFontList();
1859     if (!newfont) {
1860 	freeCIDFontFamily(font);
1861 	font = NULL;
1862     } else {
1863 	cidfontlist fontlist;
1864 	if (isPDF)
1865 	    fontlist = PDFloadedCIDFonts;
1866 	else
1867 	    fontlist = loadedCIDFonts;
1868 	newfont->cidfamily = font;
1869 	if (!fontlist) {
1870 	    if (isPDF)
1871 		PDFloadedCIDFonts = newfont;
1872 	    else
1873 		loadedCIDFonts = newfont;
1874 	} else {
1875 	    while (fontlist->next)
1876 		fontlist = fontlist->next;
1877 	    fontlist->next = newfont;
1878 	}
1879     }
1880     return font;
1881 }
addLoadedFont(type1fontfamily font,Rboolean isPDF)1882 static type1fontfamily addLoadedFont(type1fontfamily font,
1883 				     Rboolean isPDF)
1884 {
1885     type1fontlist newfont = makeFontList();
1886     if (!newfont) {
1887 	freeFontFamily(font);
1888 	font = NULL;
1889     } else {
1890 	type1fontlist fontlist;
1891 	if (isPDF)
1892 	    fontlist = PDFloadedFonts;
1893 	else
1894 	    fontlist = loadedFonts;
1895 	newfont->family = font;
1896 	if (!fontlist) {
1897 	    if (isPDF)
1898 		PDFloadedFonts = newfont;
1899 	    else
1900 		loadedFonts = newfont;
1901 	} else {
1902 	    while (fontlist->next)
1903 		fontlist = fontlist->next;
1904 	    fontlist->next = newfont;
1905 	}
1906     }
1907     return font;
1908 }
1909 
1910 /*
1911  * Add a font from a graphics engine font family name
1912  */
addCIDFont(const char * name,Rboolean isPDF)1913 static cidfontfamily addCIDFont(const char *name, Rboolean isPDF)
1914 {
1915     cidfontfamily fontfamily = makeCIDFontFamily();
1916     char *fontdbname;
1917     if (isPDF)
1918 	fontdbname = PDFFonts;
1919     else
1920 	fontdbname = PostScriptFonts;
1921     if (fontfamily) {
1922 	int i;
1923 	const char *cmap = getFontCMap(name, fontdbname);
1924 	if (!cmap) {
1925 	    freeCIDFontFamily(fontfamily);
1926 	    fontfamily = NULL;
1927 	} else {
1928 	    /*
1929 	     * Set the name of the font
1930 	     */
1931 	    safestrcpy(fontfamily->fxname, name, 50);
1932 	    /*
1933 	     * Get the font CMap
1934 	     */
1935 	    safestrcpy(fontfamily->cmap, cmap, 50);
1936 	    /*
1937 	     * Get the font Encoding (name)
1938 	     *
1939 	     * If we have got here then we know there is a
1940 	     * match in the font database because we already
1941 	     * have the CMap => don't need to check for failure
1942 	     */
1943 	    safestrcpy(fontfamily->encoding,
1944 		       getCIDFontEncoding(name, fontdbname), 50);
1945 	    /*
1946 	     * Load font info
1947 	     */
1948 	    for(i = 0; i < 4; i++) {
1949 		fontfamily->cidfonts[i] = makeCIDFont();
1950 		/*
1951 		 * Use name from R object font database.
1952 		 */
1953 		safestrcpy(fontfamily->cidfonts[i]->name,
1954 			   getFontName(name, fontdbname), 50);
1955 	    }
1956 	    /*
1957 	     * Load the (Type 1!) symbol font
1958 	     *
1959 	     * Gratuitous loop of length 1 so "break" jumps to end of loop
1960 	     */
1961 	    for (i = 0; i < 1; i++) {
1962 		type1fontinfo font = makeType1Font();
1963 		const char *afmpath = fontMetricsFileName(name, 4, fontdbname);
1964 		if (!font) {
1965 		    freeCIDFontFamily(fontfamily);
1966 		    fontfamily = NULL;
1967 		    break;
1968 		}
1969 		if (!afmpath) {
1970 		    freeCIDFontFamily(fontfamily);
1971 		    fontfamily = NULL;
1972 		    freeType1Font(font);
1973 		    break;
1974 		}
1975 		fontfamily->symfont = font;
1976 		if (!PostScriptLoadFontMetrics(afmpath,
1977 					       &(fontfamily->symfont->metrics),
1978 					       fontfamily->symfont->name,
1979 					       fontfamily->symfont->charnames,
1980 					       /*
1981 						* Reencode all but
1982 						* symbol face
1983 						*/
1984 					       NULL, 0)) {
1985 		    warning(_("cannot load afm file '%s'"), afmpath);
1986 		    freeCIDFontFamily(fontfamily);
1987 		    fontfamily = NULL;
1988 		    break;
1989 		}
1990 	    }
1991 	    /*
1992 	     * Add font
1993 	     */
1994 	    if (fontfamily)
1995 		fontfamily = addLoadedCIDFont(fontfamily, isPDF);
1996 	}
1997     } else
1998 	fontfamily = NULL;
1999 #ifdef DEBUG_PS
2000     Rprintf("%d fontfamily =  %s\n", __LINE__, (fontfamily) ? "set" : "null");
2001     Rprintf("%d addCIDFont = %s\n", __LINE__, fontfamily->fxname);
2002 #endif
2003     return fontfamily;
2004 }
2005 
addFont(const char * name,Rboolean isPDF,encodinglist deviceEncodings)2006 static type1fontfamily addFont(const char *name, Rboolean isPDF,
2007 			       encodinglist deviceEncodings)
2008 {
2009     type1fontfamily fontfamily = makeFontFamily();
2010     char *fontdbname;
2011     if (isPDF)
2012 	fontdbname = PDFFonts;
2013     else
2014 	fontdbname = PostScriptFonts;
2015     if (fontfamily) {
2016 	int i;
2017 	encodinginfo encoding;
2018 	const char *encpath = getFontEncoding(name, fontdbname);
2019 	if (!encpath) {
2020 	    freeFontFamily(fontfamily);
2021 	    fontfamily = NULL;
2022 	} else {
2023 	    /*
2024 	     * Set the name of the font
2025 	     */
2026 	    safestrcpy(fontfamily->fxname, name, 50);
2027 	    /*
2028 	     * Find or add encoding
2029 	     */
2030 	    if (!(encoding = findEncoding(encpath, deviceEncodings, isPDF)))
2031 		encoding = addEncoding(encpath, isPDF);
2032 	    if (!encoding) {
2033 		freeFontFamily(fontfamily);
2034 		fontfamily = NULL;
2035 	    } else {
2036 		/*
2037 		 * Load font info
2038 		 */
2039 		fontfamily->encoding = encoding;
2040 		for(i = 0; i < 5 ; i++) {
2041 		    type1fontinfo font = makeType1Font();
2042 		    const char *afmpath = fontMetricsFileName(name, i, fontdbname);
2043 		    if (!font) {
2044 			freeFontFamily(fontfamily);
2045 			fontfamily = NULL;
2046 			break;
2047 		    }
2048 		    if (!afmpath) {
2049 			freeFontFamily(fontfamily);
2050 			fontfamily = NULL;
2051 			freeType1Font(font);
2052 			break;
2053 		    }
2054 		    fontfamily->fonts[i] = font;
2055 		    if (!PostScriptLoadFontMetrics(afmpath,
2056 						   &(fontfamily->fonts[i]->metrics),
2057 						   fontfamily->fonts[i]->name,
2058 						   fontfamily->fonts[i]->charnames,
2059 						   /*
2060 						    * Reencode all but
2061 						    * symbol face
2062 						    */
2063 						   encoding->encnames,
2064 						   (i < 4)?1:0)) {
2065 			warning(_("cannot load afm file '%s'"), afmpath);
2066 			freeFontFamily(fontfamily);
2067 			fontfamily = NULL;
2068 			break;
2069 		    }
2070 		}
2071 		/*
2072 		 * Add font
2073 		 */
2074 		if (fontfamily)
2075 		    fontfamily = addLoadedFont(fontfamily, isPDF);
2076 	    }
2077 	}
2078     } else
2079 	fontfamily = NULL;
2080     return fontfamily;
2081 }
2082 
2083 /*
2084  * Add a default font family/encoding to the list of loaded fonts ...
2085  *
2086  * ... using a set of AFM paths ...
2087  *
2088  * ... and return the new font
2089  */
2090 
2091 static type1fontfamily
addDefaultFontFromAFMs(const char * encpath,const char ** afmpaths,Rboolean isPDF,encodinglist deviceEncodings)2092 addDefaultFontFromAFMs(const char *encpath, const char **afmpaths,
2093 		       Rboolean isPDF,
2094 		       encodinglist deviceEncodings)
2095 {
2096     encodinginfo encoding;
2097     type1fontfamily fontfamily = makeFontFamily();
2098     if (fontfamily) {
2099 	int i;
2100 	if (!(encoding = findEncoding(encpath, deviceEncodings, isPDF)))
2101 	    encoding = addEncoding(encpath, isPDF);
2102 	if (!encoding) {
2103 	    freeFontFamily(fontfamily);
2104 	    fontfamily = NULL;
2105 	} else {
2106 	    /*
2107 	     * This is the device default font, so set the
2108 	     * graphics engine font family name to ""
2109 	     */
2110 	    fontfamily->fxname[0] ='\0';
2111 	    /*
2112 	     * Load font info
2113 	     */
2114 	    fontfamily->encoding = encoding;
2115 	    for(i = 0; i < 5 ; i++) {
2116 		type1fontinfo font = makeType1Font();
2117 		if (!font) {
2118 		    freeFontFamily(fontfamily);
2119 		    fontfamily = NULL;
2120 		    break;
2121 		}
2122 		fontfamily->fonts[i] = font;
2123 		if (!PostScriptLoadFontMetrics(afmpaths[i],
2124 					       &(fontfamily->fonts[i]->metrics),
2125 					       fontfamily->fonts[i]->name,
2126 					       fontfamily->fonts[i]->charnames,
2127 					       /*
2128 						* Reencode all but
2129 						* symbol face
2130 						*/
2131 					       encoding->encnames,
2132 					       (i < 4)?1:0)) {
2133 		    warning(_("cannot load afm file '%s'"), afmpaths[i]);
2134 		    freeFontFamily(fontfamily);
2135 		    fontfamily = NULL;
2136 		    break;
2137 		}
2138 	    }
2139 	    /*
2140 	     * Add font
2141 	     */
2142 	    if (fontfamily)
2143 		fontfamily = addLoadedFont(fontfamily, isPDF);
2144 	}
2145     } else
2146 	fontfamily = NULL;
2147     return fontfamily;
2148 }
2149 
2150 /*
2151  * Add a graphics engine font family/encoding to a list of device fonts ...
2152  *
2153  * ... and return the new font list
2154  */
addDeviceCIDFont(cidfontfamily font,cidfontlist devFonts,int * index)2155 static cidfontlist addDeviceCIDFont(cidfontfamily font,
2156 				    cidfontlist devFonts,
2157 				    int *index)
2158 {
2159     cidfontlist newfont = makeCIDFontList();
2160     *index = 0;
2161     if (!newfont) {
2162 	devFonts = NULL;
2163     } else {
2164 	cidfontlist fontlist = devFonts;
2165 	newfont->cidfamily = font;
2166 	*index = 1;
2167 	if (!devFonts) {
2168 	    devFonts = newfont;
2169 	} else {
2170 	    while (fontlist->next) {
2171 		fontlist = fontlist->next;
2172 		*index = *index + 1;
2173 	    }
2174 	    fontlist->next = newfont;
2175 	}
2176     }
2177     return devFonts;
2178 }
addDeviceFont(type1fontfamily font,type1fontlist devFonts,int * index)2179 static type1fontlist addDeviceFont(type1fontfamily font,
2180 				   type1fontlist devFonts,
2181 				   int *index)
2182 {
2183     type1fontlist newfont = makeFontList();
2184     *index = 0;
2185     if (!newfont) {
2186 	devFonts = NULL;
2187     } else {
2188 	type1fontlist fontlist = devFonts;
2189 	newfont->family = font;
2190 	*index = 1;
2191 	if (!devFonts) {
2192 	    devFonts = newfont;
2193 	} else {
2194 	    while (fontlist->next) {
2195 		fontlist = fontlist->next;
2196 		*index = *index + 1;
2197 	    }
2198 	    fontlist->next = newfont;
2199 	}
2200     }
2201     return devFonts;
2202 }
2203 
2204 /*
2205 ***********************************************************
2206 */
2207 
2208 /* Part 2.  Device Driver State. */
2209 
2210 typedef struct {
2211     char filename[PATH_MAX];
2212     int open_type;
2213 
2214     char papername[64];	/* paper name */
2215     int paperwidth;	/* paper width in big points (1/72 in) */
2216     int paperheight;	/* paper height in big points */
2217     Rboolean landscape;	/* landscape mode */
2218     int pageno;		/* page number */
2219     int fileno;		/* file number */
2220 
2221     int maxpointsize;
2222 
2223     double width;	/* plot width in inches */
2224     double height;	/* plot height in inches */
2225     double pagewidth;	/* page width in inches */
2226     double pageheight;	/* page height in inches */
2227     Rboolean pagecentre;/* centre image on page? */
2228     Rboolean printit;	/* print page at close? */
2229     char command[2*PATH_MAX];
2230     char title[1024];
2231     char colormodel[30];
2232 
2233     FILE *psfp;		/* output file */
2234 
2235     Rboolean onefile;	/* EPSF header etc*/
2236     Rboolean paperspecial;	/* suppress %%Orientation */
2237     Rboolean warn_trans; /* have we warned about translucent cols? */
2238     Rboolean useKern;
2239     Rboolean fillOddEven; /* polygon fill mode */
2240 
2241     /* This group of variables track the current device status.
2242      * They should only be set by routines that emit PostScript code. */
2243     struct {
2244 	double lwd;		 /* line width */
2245 	int lty;		 /* line type */
2246 	R_GE_lineend lend;
2247 	R_GE_linejoin ljoin;
2248 	double lmitre;
2249 	int font;
2250 	int cidfont;
2251 	int fontsize;	         /* font size in points */
2252 	rcolor col;		 /* color */
2253 	rcolor fill;	         /* fill color */
2254     } current;
2255 
2256     /*
2257      * Fonts and encodings used on the device
2258      */
2259     type1fontlist fonts;
2260     cidfontlist   cidfonts;
2261     encodinglist  encodings;
2262     /*
2263      * These next two just record the default device font
2264      */
2265     type1fontfamily defaultFont;
2266     cidfontfamily   defaultCIDFont;
2267 }
2268 PostScriptDesc;
2269 
2270 /*  Part 3.  Graphics Support Code.  */
2271 
specialCaseCM(FILE * fp,type1fontfamily family,int familynum)2272 static void specialCaseCM(FILE *fp, type1fontfamily family, int familynum)
2273 {
2274 	fprintf(fp, "%% begin encoding\n");
2275 	fprintf(fp, "/SymbolEncoding [\n");
2276 	fprintf(fp, " /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n");
2277 	fprintf(fp, " /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n");
2278 	fprintf(fp, " /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n");
2279 	fprintf(fp, " /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n");
2280 	fprintf(fp, " /space /exclam /universal /numbersign /existential /percent /ampersand /suchthat\n");
2281 	fprintf(fp, " /parenleft /parenright /asteriskmath /plus /comma /minus /period /slash\n");
2282 	fprintf(fp, " /zero /one /two /three /four /five /six /seven\n");
2283 	fprintf(fp, " /eight /nine /colon /semicolon /less /equal /greater /question\n");
2284 	fprintf(fp, " /congruent /Alpha /Beta /Chi /Delta /Epsilon /Phi /Gamma\n");
2285 	fprintf(fp, " /Eta /Iota /theta1 /Kappa /Lambda /Mu /Nu /Omicron\n");
2286 	fprintf(fp, " /Pi /Theta /Rho /Sigma /Tau /Upsilon /sigma1 /Omega\n");
2287 	fprintf(fp, " /Xi /Psi /Zeta /bracketleft /therefore /bracketright /perpendicular /underscore\n");
2288 	fprintf(fp, " /radicalex /alpha /beta /chi /delta /epsilon /phi /gamma\n");
2289 	fprintf(fp, " /eta /iota /phi1 /kappa /lambda /mu /nu /omicron\n");
2290 	fprintf(fp, " /pi /theta /rho /sigma /tau /upsilon /omega1 /omega\n");
2291 	fprintf(fp, " /xi /psi /zeta /braceleft /bar /braceright /similar /.notdef\n");
2292 	fprintf(fp, " /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n");
2293 	fprintf(fp, " /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n");
2294 	fprintf(fp, " /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n");
2295 	fprintf(fp, " /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n");
2296 	fprintf(fp, " /Euro /Upsilon1 /minute /lessequal /fraction /infinity /florin /club\n");
2297 	fprintf(fp, " /diamond /heart /spade /arrowboth /arrowleft /arrowup /arrowright /arrowdown\n");
2298 	fprintf(fp, " /degree /plusminus /second /greaterequal /multiply /proportional /partialdiff /bullet\n");
2299 	fprintf(fp, " /divide /notequal /equivalence /approxequal /ellipsis /arrowvertex /arrowhorizex /carriagereturn\n");
2300 	fprintf(fp, " /aleph /Ifraktur /Rfraktur /weierstrass /circlemultiply /circleplus /emptyset /intersection\n");
2301 	fprintf(fp, " /union /propersuperset /reflexsuperset /notsubset /propersubset /reflexsubset /element /notelement\n");
2302 	fprintf(fp, " /angle /gradient /registerserif /copyrightserif /trademarkserif /product /radical /dotmath\n");
2303 	fprintf(fp, " /logicalnot /logicaland /logicalor /arrowdblboth /arrowdblleft /arrowdblup /arrowdblright /arrowdbldown\n");
2304 	fprintf(fp, " /lozenge /angleleft /registersans /copyrightsans /trademarksans /summation /parenlefttp /parenleftex\n");
2305 	fprintf(fp, " /parenleftbt /bracketlefttp /bracketleftex /bracketleftbt /bracelefttp /braceleftmid /braceleftbt /braceex\n");
2306 	fprintf(fp, " /.notdef /angleright /integral /integraltp /integralex /integralbt /parenrighttp /parenrightex\n");
2307 	fprintf(fp, " /parenrightbt /bracketrighttp /bracketrightex /bracketrightbt /bracerighttp /bracerightmid /bracerightbt /.notdef\n");
2308 	fprintf(fp, "] def\n");
2309 	fprintf(fp, "%% end encoding\n");
2310 	fprintf(fp, "/mergefonts\n");
2311 	fprintf(fp, "{ /targetencoding exch def\n");
2312 	fprintf(fp, "  /fontarray exch def\n");
2313 	fprintf(fp, "  fontarray 0 get dup maxlength dict begin\n");
2314 	fprintf(fp, "  { 1 index /FID ne { def } { pop pop } ifelse } forall\n");
2315 	fprintf(fp, "  %% Create a new dictionary\n");
2316 	fprintf(fp, "  /CharStrings 256 dict def\n");
2317 	fprintf(fp, "  %% Add a definition of .notdef\n");
2318 	fprintf(fp, "  fontarray\n");
2319 	fprintf(fp, "  { /CharStrings get dup /.notdef known\n");
2320 	fprintf(fp, "    { /.notdef get /result exch def exit }\n");
2321 	fprintf(fp, "    { pop } ifelse\n");
2322 	fprintf(fp, "  } forall\n");
2323 	fprintf(fp, "  CharStrings /.notdef result put\n");
2324 	fprintf(fp, "  %% Add in the other definitions\n");
2325 	fprintf(fp, "  targetencoding\n");
2326 	fprintf(fp, "  { /code exch def\n");
2327 	fprintf(fp, "    %% Check that it is not a .notdef\n");
2328 	fprintf(fp, "    code /.notdef eq\n");
2329 	fprintf(fp, "    { /.notdef }\n");
2330 	fprintf(fp, "    { fontarray\n");
2331 	fprintf(fp, "      { /CharStrings get dup code known\n");
2332 	fprintf(fp, "        { code get /result exch def /found true def exit }\n");
2333 	fprintf(fp, "        { pop /found false def } ifelse\n");
2334 	fprintf(fp, "      } forall\n");
2335 	fprintf(fp, "      %% define character if it was found and accumulate encoding\n");
2336 	fprintf(fp, "      found { CharStrings code result put code } { /.notdef } ifelse\n");
2337 	fprintf(fp, "    } ifelse\n");
2338 	fprintf(fp, "  } forall\n");
2339 	fprintf(fp, "  %% grab new encoding off of stack\n");
2340 	fprintf(fp, "  256 array astore /Encoding exch def\n");
2341 	fprintf(fp, "  %% Undefine some local variables\n");
2342 	fprintf(fp, "  currentdict /fontarray undef\n");
2343 	fprintf(fp, "  currentdict /targetencoding undef\n");
2344 	fprintf(fp, "  currentdict /code undef\n");
2345 	fprintf(fp, "  currentdict /result undef\n");
2346 	fprintf(fp, "  currentdict /found undef\n");
2347 	fprintf(fp, "  %% Leave new font on the stack\n");
2348 	fprintf(fp, "  currentdict\n");
2349 	fprintf(fp, "  end\n");
2350 	fprintf(fp, "} def\n");
2351 	fprintf(fp, "%%%%IncludeResource: font %s\n",
2352 		family->fonts[0]->name);
2353 	fprintf(fp, "%%%%IncludeResource: font CMSY10\n");
2354 	fprintf(fp, "[ /%s findfont /CMSY10 findfont ] %s mergefonts\n",
2355 		family->fonts[0]->name, family->encoding->name);
2356 	fprintf(fp, "/Font%d exch definefont pop\n",
2357 		(familynum - 1)*5 + 1);
2358 	fprintf(fp, "%%%%IncludeResource: font %s\n",
2359 		family->fonts[1]->name);
2360 	fprintf(fp, "%%%%IncludeResource: font CMBSY10\n");
2361 	fprintf(fp, "[ /%s findfont /CMBSY10 findfont ] %s mergefonts\n",
2362 		family->fonts[1]->name, family->encoding->name);
2363 	fprintf(fp, "/Font%d exch definefont pop\n",
2364 		(familynum - 1)*5 + 2);
2365 	fprintf(fp, "%%%%IncludeResource: font %s\n",
2366 		family->fonts[2]->name);
2367 	fprintf(fp, "[ /%s findfont /CMSY10 findfont ] %s mergefonts\n",
2368 		family->fonts[2]->name, family->encoding->name);
2369 	fprintf(fp, "/Font%d exch definefont pop\n",
2370 		(familynum - 1)*5 + 3);
2371 	fprintf(fp, "%%%%IncludeResource: font %s\n",
2372 		family->fonts[3]->name);
2373 	fprintf(fp, "[ /%s findfont /CMBSY10 findfont ] %s mergefonts\n",
2374 		family->fonts[3]->name, family->encoding->name);
2375 	fprintf(fp, "/Font%d exch definefont pop\n",
2376 		(familynum - 1)*5 + 4);
2377 	fprintf(fp, "%%%%IncludeResource: font CMMI10\n");
2378 	fprintf(fp, "[ /CMR10 findfont /CMSY10 findfont /CMMI10 findfont ] SymbolEncoding mergefonts\n");
2379 	fprintf(fp, "/Font%d exch definefont pop\n",
2380 		(familynum - 1)*5 + 5);
2381 }
2382 
PSEncodeFonts(FILE * fp,PostScriptDesc * pd)2383 static void PSEncodeFonts(FILE *fp, PostScriptDesc *pd)
2384 {
2385     type1fontlist fonts = pd->fonts;
2386     int familynum = 1;
2387     int haveWrittenDefaultEnc = 0;
2388     cidfontlist cidfonts = pd->cidfonts;
2389     int cidfamilynum = 1;
2390 
2391     while (fonts) {
2392 	int dontcare;
2393 	/*
2394 	 * Has the encoding already been used on the device?
2395 	 */
2396 	encodinginfo encoding =
2397 	    findDeviceEncoding(fonts->family->encoding->encpath,
2398 			       pd->encodings, &dontcare);
2399 	/*
2400 	 * If we've added the encoding to the device then it has been
2401 	 * written to file ...
2402 	 *
2403 	 * ... UNLESS this is the default encoding for the device, in
2404 	 * which case it has been added, but not written to file.
2405 	 *
2406 	 * Use haveWrittenDefaultEnc to make sure we only do it once.
2407 	 */
2408 	if (!encoding ||
2409 	    (encoding == pd->encodings->encoding && !haveWrittenDefaultEnc)) {
2410 	    /*
2411 	     * Don't need to add default encoding again.
2412 	     */
2413 	    if (encoding != pd->encodings->encoding) {
2414 		/*
2415 		 * The encoding should have been loaded when the
2416 		 * font was loaded
2417 		 */
2418 		encoding = findEncoding(fonts->family->encoding->encpath,
2419 					pd->encodings, FALSE);
2420 		if (!encoding)
2421 		    warning(_("corrupt loaded encodings;  encoding not recorded"));
2422 		else {
2423 		    /*
2424 		     * Record encoding on device's list of encodings so
2425 		     * don't write same encoding more than once
2426 		     */
2427 		    encodinglist enclist = addDeviceEncoding(encoding,
2428 							     pd->encodings);
2429 		    if (enclist)
2430 			pd->encodings = enclist;
2431 		    else
2432 			warning(_("failed to record device encoding"));
2433 		}
2434 	    } else {
2435 		/*
2436 		 * Make sure we only write default encoding once.
2437 		 */
2438 		haveWrittenDefaultEnc = 1;
2439 	    }
2440 	    /*
2441 	     * Include encoding unless it is ISOLatin1Encoding,
2442 	     * which is predefined
2443 	     */
2444 	    if (strcmp(fonts->family->encoding->name, "ISOLatin1Encoding"))
2445 		fprintf(fp, "%% begin encoding\n%s def\n%% end encoding\n",
2446 			fonts->family->encoding->enccode);
2447 	}
2448 	if(strcmp(fonts->family->fonts[4]->name,
2449 		  "CMSY10 CMBSY10 CMMI10") == 0) {
2450 	    /* use different ps fragment for CM fonts */
2451 	    specialCaseCM(fp, fonts->family, familynum);
2452 	} else {
2453 	    int i;
2454 	    for (i = 0; i < 4 ; i++) {
2455 		fprintf(fp, "%%%%IncludeResource: font %s\n",
2456 			fonts->family->fonts[i]->name);
2457 		fprintf(fp, "/%s findfont\n",
2458 			fonts->family->fonts[i]->name);
2459 		fprintf(fp, "dup length dict begin\n");
2460 		fprintf(fp, "  {1 index /FID ne {def} {pop pop} ifelse} forall\n");
2461 		fprintf(fp, "  /Encoding %s def\n",
2462 			fonts->family->encoding->name);
2463 		fprintf(fp, "  currentdict\n");
2464 		fprintf(fp, "  end\n");
2465 		fprintf(fp, "/Font%d exch definefont pop\n",
2466 			(familynum - 1)*5 + i + 1);
2467 	    }
2468 	    fprintf(fp, "%%%%IncludeResource: font %s\n",
2469 		    fonts->family->fonts[4]->name);
2470 	    fprintf(fp, "/%s findfont\n",
2471 		    fonts->family->fonts[4]->name);
2472 	    fprintf(fp, "dup length dict begin\n");
2473 	    fprintf(fp, "  {1 index /FID ne {def} {pop pop} ifelse} forall\n");
2474 	    fprintf(fp, "  currentdict\n");
2475 	    fprintf(fp, "  end\n");
2476 	    fprintf(fp, "/Font%d exch definefont pop\n",
2477 		    (familynum - 1)*5 + 5);
2478 	}
2479 
2480 	familynum++;
2481 	fonts = fonts->next;
2482     }
2483     while(cidfonts) {
2484 	int i;
2485 	char *name = cidfonts->cidfamily->cidfonts[0]->name;
2486 	fprintf(fp, "%%%%IncludeResource: CID fake Bold font %s\n", name);
2487 	fprintf(fp, "/%s-Bold\n/%s /CIDFont findresource\n", name, name);
2488 	fprintf(fp, "%s", CIDBoldFontStr1);
2489 	fprintf(fp, "%s", CIDBoldFontStr2);
2490 	for (i = 0; i < 4 ; i++) {
2491 	    char *fmt = NULL /* -Wall */;
2492 	    fprintf(fp, "%%%%IncludeResource: CID font %s-%s\n", name,
2493 		    cidfonts->cidfamily->cmap);
2494 	    switch(i) {
2495 	    case 0: fmt = "/%s-%s findfont\n";
2496 		break;
2497 	    case 1: fmt = "/%s-Bold-%s findfont\n";
2498 		break;
2499 	    case 2: fmt = "/%s-%s findfont [1 0 .3 1 0 0] makefont\n";
2500 		break;
2501 	    case 3: fmt = "/%s-Bold-%s findfont [1 0 .3 1 0 0] makefont\n";
2502 		break;
2503 	    default:
2504 		break;
2505 	    }
2506 	    fprintf(fp, fmt, name, cidfonts->cidfamily->cmap);
2507 	    fprintf(fp, "dup length dict begin\n");
2508 	    fprintf(fp, "  {1 index /FID ne {def} {pop pop} ifelse} forall\n");
2509 	    fprintf(fp, "  currentdict\n");
2510 	    fprintf(fp, "  end\n");
2511 	    fprintf(fp, "/Font%d exch definefont pop\n",
2512 		    (familynum - 1)*5 + (cidfamilynum - 1)*5 + i + 1);
2513 	}
2514 	/*
2515 	 * Symbol font
2516 	 */
2517 	fprintf(fp, "%%%%IncludeResource: font %s\n",
2518 		cidfonts->cidfamily->symfont->name);
2519 	fprintf(fp, "/%s findfont\n",
2520 		cidfonts->cidfamily->symfont->name);
2521 	fprintf(fp, "dup length dict begin\n");
2522 	fprintf(fp, "  {1 index /FID ne {def} {pop pop} ifelse} forall\n");
2523 	fprintf(fp, "  currentdict\n");
2524 	fprintf(fp, "  end\n");
2525 	fprintf(fp, "/Font%d exch definefont pop\n",
2526 		(familynum - 1)*5 + (cidfamilynum - 1)*5 + 5);
2527 	cidfamilynum++;
2528 	cidfonts = cidfonts->next;
2529     }
2530 }
2531 
2532 /* The variables "paperwidth" and "paperheight" give the dimensions */
2533 /* of the (unrotated) printer page in points whereas the graphics */
2534 /* region box is for the rotated page. */
2535 
PSFileHeader(FILE * fp,const char * papername,double paperwidth,double paperheight,Rboolean landscape,int EPSFheader,Rboolean paperspecial,double left,double bottom,double right,double top,const char * title,PostScriptDesc * pd)2536 static void PSFileHeader(FILE *fp,
2537 			 const char *papername, double paperwidth,
2538 			 double paperheight, Rboolean landscape,
2539 			 int EPSFheader, Rboolean paperspecial,
2540 			 double left, double bottom, double right, double top,
2541 			 const char *title,
2542 			 PostScriptDesc *pd)
2543 {
2544     int i;
2545     SEXP prolog;
2546     type1fontlist fonts = pd->fonts;
2547     int firstfont = 1;
2548 
2549     if(EPSFheader)
2550 	fprintf(fp, "%%!PS-Adobe-3.0 EPSF-3.0\n");
2551     else
2552 	fprintf(fp, "%%!PS-Adobe-3.0\n");
2553     /*
2554      * DocumentNeededResources names all fonts
2555      */
2556     while (fonts) {
2557 	for (i=0; i<5; i++)
2558 	    if (firstfont) {
2559 		fprintf(fp, "%%%%DocumentNeededResources: font %s\n",
2560 			fonts->family->fonts[0]->name);
2561 		firstfont = 0;
2562 	    } else
2563 	fprintf(fp, "%%%%+ font %s\n", fonts->family->fonts[i]->name);
2564 	fonts = fonts->next;
2565     }
2566 
2567     if(!EPSFheader)
2568 	fprintf(fp, "%%%%DocumentMedia: %s %.0f %.0f 0 () ()\n",
2569 		papername, paperwidth, paperheight);
2570     fprintf(fp, "%%%%Title: %s\n", title);
2571     fprintf(fp, "%%%%Creator: R Software\n");
2572     fprintf(fp, "%%%%Pages: (atend)\n");
2573     if (!EPSFheader && !paperspecial) { /* gs gets confused by this */
2574 	if (landscape)
2575 	    fprintf(fp, "%%%%Orientation: Landscape\n");
2576 	else
2577 	    fprintf(fp, "%%%%Orientation: Portrait\n");
2578     }
2579     fprintf(fp, "%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
2580 	    left, bottom, right, top);
2581     fprintf(fp, "%%%%EndComments\n");
2582     fprintf(fp, "%%%%BeginProlog\n");
2583     fprintf(fp,  "/bp  { gs");
2584     if (streql(pd->colormodel, "srgb")) fprintf(fp,  " sRGB");
2585     if (landscape)
2586 	fprintf(fp, " %.2f 0 translate 90 rotate", paperwidth);
2587     fprintf(fp, " gs } def\n");
2588     prolog = findVar(install(".ps.prolog"), R_GlobalEnv);
2589     if(prolog == R_UnboundValue) {
2590 	/* if no object is visible, look in the graphics namespace */
2591 	SEXP graphicsNS = R_FindNamespace(ScalarString(mkChar("grDevices")));
2592 	PROTECT(graphicsNS);
2593 	prolog = findVar(install(".ps.prolog"), graphicsNS);
2594 	/* under lazy loading this will be a promise on first use */
2595 	if(TYPEOF(prolog) == PROMSXP) {
2596 	    PROTECT(prolog);
2597 	    prolog = eval(prolog, graphicsNS);
2598 	    UNPROTECT(1);
2599 	}
2600 	UNPROTECT(1);
2601     }
2602     if(!isString(prolog))
2603 	error(_("object '.ps.prolog' is not a character vector"));
2604     fprintf(fp, "%% begin .ps.prolog\n");
2605     for (i = 0; i < length(prolog); i++)
2606 	fprintf(fp, "%s\n", CHAR(STRING_ELT(prolog, i)));
2607     fprintf(fp, "%% end   .ps.prolog\n");
2608     if (streql(pd->colormodel, "srgb+gray") || streql(pd->colormodel, "srgb")) {
2609 	SEXP graphicsNS = R_FindNamespace(ScalarString(mkChar("grDevices")));
2610 	PROTECT(graphicsNS);
2611 	prolog = findVar(install(".ps.prolog.srgb"), graphicsNS);
2612 	/* under lazy loading this will be a promise on first use */
2613 	if(TYPEOF(prolog) == PROMSXP) {
2614 	    PROTECT(prolog);
2615 	    prolog = eval(prolog, graphicsNS);
2616 	    UNPROTECT(1);
2617 	}
2618 	UNPROTECT(1);
2619 	for (i = 0; i < length(prolog); i++)
2620 	    fprintf(fp, "%s\n", CHAR(STRING_ELT(prolog, i)));
2621     }
2622     if (streql(pd->colormodel, "srgb+gray"))
2623 	fprintf(fp, "/srgb { sRGB setcolor } bind def\n");
2624     else if (streql(pd->colormodel, "srgb"))
2625 	fprintf(fp, "/srgb { setcolor } bind def\n");
2626     PSEncodeFonts(fp, pd);
2627 
2628     fprintf(fp, "%%%%EndProlog\n");
2629 }
2630 
PostScriptFileTrailer(FILE * fp,int pageno)2631 static void PostScriptFileTrailer(FILE *fp, int pageno)
2632 {
2633     fprintf(fp, "ep\n");
2634     fprintf(fp, "%%%%Trailer\n");
2635     fprintf(fp, "%%%%Pages: %d\n", pageno);
2636     fprintf(fp, "%%%%EOF\n");
2637 }
2638 
PostScriptStartPage(FILE * fp,int pageno)2639 static void PostScriptStartPage(FILE *fp, int pageno)
2640 {
2641     fprintf(fp, "%%%%Page: %d %d\n", pageno, pageno);
2642     fprintf(fp, "bp\n");
2643 }
2644 
PostScriptEndPage(FILE * fp)2645 static void PostScriptEndPage(FILE *fp)
2646 {
2647     fprintf(fp, "ep\n");
2648 }
2649 
PostScriptSetClipRect(FILE * fp,double x0,double x1,double y0,double y1)2650 static void PostScriptSetClipRect(FILE *fp, double x0, double x1,
2651 				  double y0, double y1)
2652 {
2653     fprintf(fp, "%.2f %.2f %.2f %.2f cl\n", x0, y0, x1, y1);
2654 }
2655 
PostScriptSetLineWidth(FILE * fp,double linewidth)2656 static void PostScriptSetLineWidth(FILE *fp, double linewidth)
2657 {
2658     /* Must not allow line width to be zero */
2659     if (linewidth < .01)
2660         linewidth = .01;
2661     fprintf(fp, "%.2f setlinewidth\n", linewidth);
2662 }
2663 
PostScriptSetLineEnd(FILE * fp,R_GE_lineend lend)2664 static void PostScriptSetLineEnd(FILE *fp, R_GE_lineend lend)
2665 {
2666     int lineend = 1; /* -Wall */
2667     switch (lend) {
2668     case GE_ROUND_CAP:
2669 	lineend = 1;
2670 	break;
2671     case GE_BUTT_CAP:
2672 	lineend = 0;
2673 	break;
2674     case GE_SQUARE_CAP:
2675 	lineend = 2;
2676 	break;
2677     default:
2678 	error(_("invalid line end"));
2679     }
2680     fprintf(fp, "%1d setlinecap\n", lineend);
2681 }
2682 
PostScriptSetLineJoin(FILE * fp,R_GE_linejoin ljoin)2683 static void PostScriptSetLineJoin(FILE *fp, R_GE_linejoin ljoin)
2684 {
2685     int linejoin = 1; /* -Wall */
2686     switch (ljoin) {
2687     case GE_ROUND_JOIN:
2688 	linejoin = 1;
2689 	break;
2690     case GE_MITRE_JOIN:
2691 	linejoin = 0;
2692 	break;
2693     case GE_BEVEL_JOIN:
2694 	linejoin = 2;
2695 	break;
2696     default:
2697 	error(_("invalid line join"));
2698     }
2699     fprintf(fp, "%1d setlinejoin\n", linejoin);
2700 }
2701 
PostScriptSetLineMitre(FILE * fp,double linemitre)2702 static void PostScriptSetLineMitre(FILE *fp, double linemitre)
2703 {
2704     if (linemitre < 1)
2705 	error(_("invalid line mitre"));
2706     fprintf(fp, "%.2f setmiterlimit\n", linemitre);
2707 }
2708 
PostScriptSetFont(FILE * fp,int fontnum,double size)2709 static void PostScriptSetFont(FILE *fp, int fontnum, double size)
2710 {
2711     fprintf(fp, "/Font%d findfont %.0f s\n", fontnum, size);
2712 }
2713 
2714 static void
PostScriptSetLineTexture(FILE * fp,const char * dashlist,int nlty,double lwd,int lend)2715 PostScriptSetLineTexture(FILE *fp, const char *dashlist, int nlty,
2716 			 double lwd, int lend)
2717 {
2718 /* Historically the adjustment was 1 to allow for round end caps.
2719    As from 2.11.0, no adjustment is done for butt endcaps.
2720    The + 1 adjustment on the 'off' segments seems wrong, but it
2721    has been left in for back-compatibility
2722 */
2723     double dash[8], a = (lend == GE_BUTT_CAP) ? 0. : 1.;
2724     int i;
2725     Rboolean allzero = TRUE;
2726     for (i = 0; i < nlty; i++) {
2727 	dash[i] = lwd *
2728 	    ((i % 2) ? (dashlist[i] + a)
2729 	     : ((nlty == 1 && dashlist[i] == 1.) ? 1. : dashlist[i] - a) );
2730 	if (dash[i] < 0) dash[i] = 0;
2731         if (dash[i] > .01) allzero = FALSE;
2732     }
2733     fprintf(fp,"[");
2734     if (!allzero) {
2735         for (i = 0; i < nlty; i++) {
2736             fprintf(fp," %.2f", dash[i]);
2737         }
2738     }
2739     fprintf(fp,"] 0 setdash\n");
2740 }
2741 
2742 
PostScriptMoveTo(FILE * fp,double x,double y)2743 static void PostScriptMoveTo(FILE *fp, double x, double y)
2744 {
2745     fprintf(fp, "%.2f %.2f m\n", x, y);
2746 }
2747 
PostScriptRLineTo(FILE * fp,double x0,double y0,double x1,double y1)2748 static void PostScriptRLineTo(FILE *fp, double x0, double y0,
2749 			      double x1, double y1)
2750 {
2751     double x = fround(x1, 2) - fround(x0, 2),
2752 	y = fround(y1, 2) - fround(y0, 2);
2753     /* Warning: some machines seem to compute these differently from
2754        others, and we do want to diff the output.  x and y should be
2755        above around 0.01 or negligible (1e-14), and it is the latter case
2756        we are watching out for here.
2757     */
2758 
2759     if(fabs(x) < 0.005) fprintf(fp, "0"); else fprintf(fp, "%.2f", x);
2760     if(fabs(y) < 0.005) fprintf(fp, " 0"); else fprintf(fp, " %.2f", y);
2761     fprintf(fp, " l\n");
2762 }
2763 
PostScriptStartPath(FILE * fp)2764 static void PostScriptStartPath(FILE *fp)
2765 {
2766     fprintf(fp, "np\n");
2767 }
2768 
PostScriptEndPath(FILE * fp)2769 static void PostScriptEndPath(FILE *fp)
2770 {
2771     fprintf(fp, "o\n");
2772 }
2773 
PostScriptRectangle(FILE * fp,double x0,double y0,double x1,double y1)2774 static void PostScriptRectangle(FILE *fp, double x0, double y0,
2775 				double x1, double y1)
2776 {
2777     fprintf(fp, "%.2f %.2f %.2f %.2f r ", x0, y0, x1-x0, y1-y0);
2778 }
2779 
PostScriptCircle(FILE * fp,double x,double y,double r)2780 static void PostScriptCircle(FILE *fp, double x, double y, double r)
2781 {
2782     fprintf(fp, "%.2f %.2f %.2f c ", x, y, r);
2783 }
2784 
PostScriptWriteString(FILE * fp,const char * str,size_t nb)2785 static void PostScriptWriteString(FILE *fp, const char *str, size_t nb)
2786 {
2787     size_t i;
2788 
2789     fputc('(', fp);
2790     for (i = 0 ; i < nb && *str; i++, str++)
2791 	switch(*str) {
2792 	case '\n':
2793 	    fprintf(fp, "\\n");
2794 	    break;
2795 	case '\\':
2796 	    fprintf(fp, "\\\\");
2797 	    break;
2798 	case '-':
2799 #ifdef USE_HYPHEN
2800 	    if (!isdigit((int)str[1]))
2801 		fputc(PS_hyphen, fp);
2802 	    else
2803 #endif
2804 		fputc(*str, fp);
2805 	    break;
2806 	case '(':
2807 	case ')':
2808 	    fprintf(fp, "\\%c", *str);
2809 	    break;
2810 	default:
2811 	    fputc(*str, fp);
2812 	    break;
2813 	}
2814     fputc(')', fp);
2815 }
2816 
2817 
2818 static FontMetricInfo *metricInfo(const char *, int, PostScriptDesc *);
2819 
PostScriptText(FILE * fp,double x,double y,const char * str,size_t nb,double xc,double rot,const pGEcontext gc,pDevDesc dd)2820 static void PostScriptText(FILE *fp, double x, double y,
2821 			   const char *str, size_t nb, double xc, double rot,
2822 			   const pGEcontext gc,
2823 			   pDevDesc dd)
2824 {
2825     int face = gc->fontface;
2826 
2827     if(face < 1 || face > 5) face = 1;
2828 
2829     fprintf(fp, "%.2f %.2f ", x, y);
2830 
2831     PostScriptWriteString(fp, str, nb);
2832 
2833     if(xc == 0) fprintf(fp, " 0");
2834     else if(xc == 0.5) fprintf(fp, " .5");
2835     else if(xc == 1) fprintf(fp, " 1");
2836     else fprintf(fp, " %.2f", xc);
2837 
2838     if(rot == 0) fprintf(fp, " 0");
2839     else if(rot == 90) fprintf(fp, " 90");
2840     else fprintf(fp, " %.2f", rot);
2841 
2842     fprintf(fp, " t\n");
2843 }
2844 
PostScriptText2(FILE * fp,double x,double y,const char * str,size_t nb,Rboolean relative,double rot,const pGEcontext gc,pDevDesc dd)2845 static void PostScriptText2(FILE *fp, double x, double y,
2846 			    const char *str, size_t nb,
2847 			    Rboolean relative, double rot,
2848 			    const pGEcontext gc,
2849 			    pDevDesc dd)
2850 {
2851     int face = gc->fontface;
2852 
2853     if(face < 1 || face > 5) face = 1;
2854 
2855     if(relative) {
2856 	fprintf(fp, "\n%.3f ", x);
2857 	PostScriptWriteString(fp, str, nb);
2858 	fprintf(fp, " tb");
2859     } else {
2860 	fprintf(fp, "%.2f %.2f ", x, y);
2861 	PostScriptWriteString(fp, str, nb);
2862 	if(rot == 0) fprintf(fp, " 0");
2863 	else if(rot == 90) fprintf(fp, " 90");
2864 	else fprintf(fp, " %.2f", rot);
2865 	fprintf(fp, " ta");
2866     }
2867 }
2868 
PostScriptHexText(FILE * fp,double x,double y,const char * str,size_t strlen,double xc,double rot)2869 static void PostScriptHexText(FILE *fp, double x, double y,
2870 			      const char *str, size_t strlen,
2871 			      double xc, double rot)
2872 {
2873     unsigned char *p = (unsigned char *)str;
2874     size_t i;
2875 
2876     fprintf(fp, "%.2f %.2f ", x, y);
2877     fprintf(fp, "<");
2878     for(i = 0; i < strlen; i++) fprintf(fp, "%02x", *p++);
2879     fprintf(fp, ">");
2880 
2881     if(xc == 0) fprintf(fp, " 0");
2882     else if(xc == 0.5) fprintf(fp, " .5");
2883     else if(xc == 1) fprintf(fp, " 1");
2884     else fprintf(fp, " %.2f", xc);
2885 
2886     if(rot == 0) fprintf(fp, " 0");
2887     else if(rot == 90) fprintf(fp, " 90");
2888     else fprintf(fp, " %.2f", rot);
2889 
2890     fprintf(fp, " t\n");
2891 }
2892 
2893 static void
PostScriptTextKern(FILE * fp,double x,double y,const char * str,double xc,double rot,const pGEcontext gc,pDevDesc dd)2894 PostScriptTextKern(FILE *fp, double x, double y,
2895 		   const char *str, double xc, double rot,
2896 		   const pGEcontext gc,
2897 		   pDevDesc dd)
2898 {
2899     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
2900     int face = gc->fontface;
2901     FontMetricInfo *metrics;
2902     size_t i, n, nout = 0;
2903     int j, w;
2904     unsigned char p1, p2;
2905     double fac = 0.001 * floor(gc->cex * gc->ps + 0.5);
2906     Rboolean relative = FALSE;
2907     Rboolean haveKerning = FALSE;
2908 
2909     if(face < 1 || face > 5) {
2910 	warning(_("attempt to use invalid font %d replaced by font 1"), face);
2911 	face = 1;
2912     }
2913     /* check if this is T1 -- should be, but be safe*/
2914     if(!isType1Font(gc->fontfamily, PostScriptFonts, pd->defaultFont)) {
2915 	PostScriptText(fp, x, y, str, strlen(str), xc, rot, gc, dd);
2916 	return;
2917     }
2918     metrics = metricInfo(gc->fontfamily, face, pd);
2919 
2920     n = strlen(str);
2921     if (n < 1) return;
2922     /* First check for any kerning */
2923     for(i = 0; i < n-1; i++) {
2924 	p1 = str[i];
2925 	p2 = str[i+1];
2926 #ifdef USE_HYPHEN
2927 	if (p1 == '-' && !isdigit((int)p2))
2928 	    p1 = (unsigned char)PS_hyphen;
2929 #endif
2930 	for (j = metrics->KPstart[p1]; j < metrics->KPend[p1]; j++)
2931 	    if(metrics->KernPairs[j].c2 == p2 &&
2932 	       metrics->KernPairs[j].c1 == p1) {
2933 		haveKerning = TRUE;
2934 		break;
2935 	    }
2936     }
2937 
2938     if(haveKerning) {
2939 	/* We have to start at the left edge, as we are going
2940 	   to do this in pieces */
2941 	if (xc != 0) {
2942 	    double rot1 = rot * M_PI/180.;
2943 	    int w = 0; short wx;
2944 	    for(i = 0; i < n; i++) {
2945 		unsigned char p1 = str[i];
2946 		wx = metrics->CharInfo[(int)p1].WX;
2947 		w += (wx == NA_SHORT) ? 0 : wx;
2948 	    }
2949 	    x -= xc*fac*cos(rot1)*w;
2950 	    y -= xc*fac*sin(rot1)*w;
2951 	}
2952 	for(i = 0; i < n-1; i++) {
2953 	    p1 = str[i];
2954 	    p2 = str[i+1];
2955 #ifdef USE_HYPHEN
2956 	    if (p1 == '-' && !isdigit((int)p2))
2957 		p1 = (unsigned char)PS_hyphen;
2958 #endif
2959 	    for (j = metrics->KPstart[p1]; j < metrics->KPend[p1]; j++)
2960 		if(metrics->KernPairs[j].c2 == p2 &&
2961 		   metrics->KernPairs[j].c1 == p1) {
2962 		    PostScriptText2(fp, x, y, str+nout, i+1-nout,
2963 				    relative, rot, gc, dd);
2964 		    nout = i+1;
2965 		    w = metrics->KernPairs[j].kern;
2966 		    x = fac*w; y = 0;
2967 		    relative = TRUE;
2968 		    break;
2969 		}
2970 	}
2971 	PostScriptText2(fp, x, y, str+nout, n-nout, relative, rot, gc, dd);
2972 	fprintf(fp, " gr\n");
2973     } else
2974 	PostScriptText(fp, x, y, str, strlen(str), xc, rot, gc, dd);
2975 }
2976 
2977 /* Device Driver Actions */
2978 
2979 static void PS_Circle(double x, double y, double r,
2980 		      const pGEcontext gc,
2981 		      pDevDesc dd);
2982 static void PS_Clip(double x0, double x1, double y0, double y1,
2983 		     pDevDesc dd);
2984 static void PS_Close(pDevDesc dd);
2985 static void PS_Line(double x1, double y1, double x2, double y2,
2986 		    const pGEcontext gc,
2987 		    pDevDesc dd);
2988 static void PS_MetricInfo(int c,
2989 			  const pGEcontext gc,
2990 			  double* ascent, double* descent,
2991 			  double* width, pDevDesc dd);
2992 static void PS_NewPage(const pGEcontext gc,
2993 		       pDevDesc dd);
2994 static Rboolean PS_Open(pDevDesc, PostScriptDesc*);
2995 static void PS_Polygon(int n, double *x, double *y,
2996 		       const pGEcontext gc,
2997 		       pDevDesc dd);
2998 static void PS_Polyline(int n, double *x, double *y,
2999 			const pGEcontext gc,
3000 			pDevDesc dd);
3001 static void PS_Rect(double x0, double y0, double x1, double y1,
3002 		    const pGEcontext gc,
3003 		    pDevDesc dd);
3004 static void PS_Path(double *x, double *y,
3005                     int npoly, int *nper,
3006                     Rboolean winding,
3007                     const pGEcontext gc,
3008                     pDevDesc dd);
3009 static void PS_Raster(unsigned int *raster, int w, int h,
3010 		       double x, double y, double width, double height,
3011 		       double rot, Rboolean interpolate,
3012 		       const pGEcontext gc, pDevDesc dd);
3013 static void PS_Size(double *left, double *right,
3014 		     double *bottom, double *top,
3015 		     pDevDesc dd);
3016 static double PS_StrWidth(const char *str,
3017 			  const pGEcontext gc,
3018 			  pDevDesc dd);
3019 static void PS_Text(double x, double y, const char *str,
3020 		    double rot, double hadj,
3021 		    const pGEcontext gc,
3022 		    pDevDesc dd);
3023 static double PS_StrWidthUTF8(const char *str,
3024 			      const pGEcontext gc,
3025 			      pDevDesc dd);
3026 static void PS_TextUTF8(double x, double y, const char *str,
3027 			double rot, double hadj,
3028 			const pGEcontext gc,
3029 			pDevDesc dd);
3030 static SEXP     PS_setPattern(SEXP pattern, pDevDesc dd);
3031 static void     PS_releasePattern(SEXP ref, pDevDesc dd);
3032 static SEXP     PS_setClipPath(SEXP path, SEXP ref, pDevDesc dd);
3033 static void     PS_releaseClipPath(SEXP ref, pDevDesc dd);
3034 static SEXP     PS_setMask(SEXP path, SEXP ref, pDevDesc dd);
3035 static void     PS_releaseMask(SEXP ref, pDevDesc dd);
3036 
3037 /* PostScript Support (formerly in PostScript.c) */
3038 
PostScriptSetCol(FILE * fp,double r,double g,double b,PostScriptDesc * pd)3039 static void PostScriptSetCol(FILE *fp, double r, double g, double b,
3040 			     PostScriptDesc *pd)
3041 {
3042     const char *mm = pd->colormodel;
3043     if(r == g && g == b &&
3044        !(streql(mm, "cmyk") || streql(mm, "srgb")
3045 	 || streql(mm, "rgb-nogray")) ) { /* grey */
3046 	if(r == 0) fprintf(fp, "0");
3047 	else if (r == 1) fprintf(fp, "1");
3048 	else fprintf(fp, "%.4f", r);
3049 	fprintf(fp," setgray");
3050     } else {
3051 	if(strcmp(mm, "gray") == 0) {
3052 	    fprintf(fp, "%.4f setgray", 0.213*r + 0.715*g + 0.072*b);
3053 	    // error(_("only gray colors are allowed in this color model"));
3054 	} else if(strcmp(mm, "cmyk") == 0) {
3055 	    double c = 1.0-r, m=1.0-g, y=1.0-b, k=c;
3056 	    k = fmin2(k, m);
3057 	    k = fmin2(k, y);
3058 	    if(k == 1.0) c = m = y = 0.0;
3059 	    else { c = (c-k)/(1-k); m = (m-k)/(1-k); y = (y-k)/(1-k); }
3060 	    /* else {c /= (1.-k); m /= (1.-k); y /= (1.-k);} */
3061 	    if(c == 0) fprintf(fp, "0");
3062 	    else if (c == 1) fprintf(fp, "1");
3063 	    else fprintf(fp, "%.4f", c);
3064 	    if(m == 0) fprintf(fp, " 0");
3065 	    else if (m == 1) fprintf(fp, " 1");
3066 	    else fprintf(fp, " %.4f", m);
3067 	    if(y == 0) fprintf(fp, " 0");
3068 	    else if (y == 1) fprintf(fp, " 1");
3069 	    else fprintf(fp, " %.4f", y);
3070 	    if(k == 0) fprintf(fp, " 0");
3071 	    else if (k == 1) fprintf(fp, " 1");
3072 	    else fprintf(fp, " %.4f", k);
3073 	    fprintf(fp," setcmykcolor\n");
3074 	} else {
3075 	    if(r == 0) fprintf(fp, "0");
3076 	    else if (r == 1) fprintf(fp, "1");
3077 	    else fprintf(fp, "%.4f", r);
3078 	    if(g == 0) fprintf(fp, " 0");
3079 	    else if (g == 1) fprintf(fp, " 1");
3080 	    else fprintf(fp, " %.4f", g);
3081 	    if(b == 0) fprintf(fp, " 0");
3082 	    else if (b == 1) fprintf(fp, " 1");
3083 	    else fprintf(fp, " %.4f", b);
3084 	    if (streql(mm, "srgb+gray") || streql(mm, "srgb"))
3085 		fprintf(fp," srgb");
3086 	    else fprintf(fp," rgb");
3087 	}
3088     }
3089 }
3090 
PostScriptSetFill(FILE * fp,double r,double g,double b,PostScriptDesc * pd)3091 static void PostScriptSetFill(FILE *fp, double r, double g, double b,
3092 			      PostScriptDesc *pd)
3093 {
3094     fprintf(fp,"/bg { ");
3095     PostScriptSetCol(fp, r, g, b, pd);
3096     fprintf(fp, " } def\n");
3097 }
3098 
3099 
3100 
3101 /* Driver Support Routines */
3102 
3103 static void SetColor(int, pDevDesc);
3104 static void SetFill(int, pDevDesc);
3105 static void SetFont(int, int, pDevDesc);
3106 static void SetLineStyle(const pGEcontext, pDevDesc dd);
3107 static void Invalidate(pDevDesc);
3108 
3109 static void PS_cleanup(int stage, pDevDesc dd, PostScriptDesc *pd);
3110 
3111 
3112 Rboolean
PSDeviceDriver(pDevDesc dd,const char * file,const char * paper,const char * family,const char ** afmpaths,const char * encoding,const char * bg,const char * fg,double width,double height,Rboolean horizontal,double ps,Rboolean onefile,Rboolean pagecentre,Rboolean printit,const char * cmd,const char * title,SEXP fonts,const char * colormodel,int useKern,Rboolean fillOddEven)3113 PSDeviceDriver(pDevDesc dd, const char *file, const char *paper,
3114 	       const char *family, const char **afmpaths, const char *encoding,
3115 	       const char *bg, const char *fg, double width, double height,
3116 	       Rboolean horizontal, double ps,
3117 	       Rboolean onefile, Rboolean pagecentre, Rboolean printit,
3118 	       const char *cmd, const char *title, SEXP fonts,
3119 	       const char *colormodel, int useKern, Rboolean fillOddEven)
3120 {
3121     /* If we need to bail out with some sort of "error"
3122        then we must free(dd) */
3123 
3124     double xoff, yoff, pointsize;
3125     rcolor setbg, setfg;
3126     encodinginfo enc;
3127     encodinglist enclist;
3128     type1fontfamily font;
3129     cidfontfamily cidfont = NULL;
3130     int gotFont;
3131 
3132     PostScriptDesc *pd;
3133 
3134     /* Check and extract the device parameters */
3135 
3136     if(strlen(file) > PATH_MAX - 1) {
3137 	free(dd);
3138 	error(_("filename too long in %s()"), "postscript");
3139     }
3140 
3141     /* allocate new postscript device description */
3142     if (!(pd = (PostScriptDesc *) malloc(sizeof(PostScriptDesc)))) {
3143 	free(dd);
3144 	error(_("memory allocation problem in %s()"), "postscript");
3145     }
3146 
3147     /* from here on, if need to bail out with "error", must also */
3148     /* free(pd) */
3149 
3150     /* initialise postscript device description */
3151     strcpy(pd->filename, file);
3152     strcpy(pd->papername, paper);
3153     strncpy(pd->title, title, 1023);
3154     pd->title[1023] = '\0';
3155     if (streql(colormodel, "grey")) strcpy(pd->colormodel, "grey");
3156     else { strncpy(pd->colormodel, colormodel, 29); pd->colormodel[29] = '\0';}
3157     pd->useKern = (useKern != 0);
3158     pd->fillOddEven = fillOddEven;
3159 
3160     if(strlen(encoding) > PATH_MAX - 1) {
3161 	PS_cleanup(1, dd, pd);
3162 	error(_("encoding path is too long in %s()"), "postscript");
3163     }
3164     /*
3165      * Load the default encoding AS THE FIRST ENCODING FOR THIS DEVICE.
3166      *
3167      * encpath MUST NOT BE "default"
3168      */
3169     pd->encodings = NULL;
3170     if (!(enc = findEncoding(encoding, pd->encodings, FALSE)))
3171 	enc = addEncoding(encoding, 0);
3172     if (enc && (enclist = addDeviceEncoding(enc, pd->encodings))) {
3173 	pd->encodings = enclist;
3174     } else {
3175 	PS_cleanup(1, dd, pd);
3176 	error(_("failed to load encoding file in %s()"), "postscript");
3177     }
3178 
3179     /*****************************
3180      * Load fonts
3181      *****************************/
3182     pd->fonts = NULL;
3183     pd->cidfonts = NULL;
3184 
3185     gotFont = 0;
3186     /*
3187      * If user specified afms then assume the font hasn't been loaded
3188      * Could lead to redundant extra loading of a font, but not often(?)
3189      */
3190     if (!strcmp(family, "User")) {
3191 	font = addDefaultFontFromAFMs(encoding, afmpaths, 0, pd->encodings);
3192     } else {
3193 	/*
3194 	 * Otherwise, family is a device-independent font family.
3195 	 * One of the elements of postscriptFonts().
3196 	 * NOTE this is the first font loaded on this device!
3197 	 */
3198 	/*
3199 	 * Check first whether this font has been loaded
3200 	 * in this R session
3201 	 */
3202 	font = findLoadedFont(family, encoding, FALSE);
3203 	cidfont = findLoadedCIDFont(family, FALSE);
3204 	if (!(font || cidfont)) {
3205 	    /*
3206 	     * If the font has not been loaded yet, load it.
3207 	     *
3208 	     * The family SHOULD be in the font database to get this far.
3209 	     * (checked at R level in postscript() in postscript.R)
3210 	     */
3211 	    if (isType1Font(family, PostScriptFonts, NULL)) {
3212 		font = addFont(family, FALSE, pd->encodings);
3213 	    } else if (isCIDFont(family, PostScriptFonts, NULL)) {
3214 		cidfont = addCIDFont(family, FALSE);
3215 	    } else {
3216 		/*
3217 		 * Should NOT get here.
3218 		 * AND if we do, we should free
3219 		 */
3220 		PS_cleanup(3, dd, pd);
3221 		error(_("invalid font type"));
3222 	    }
3223 	}
3224     }
3225     if (font || cidfont) {
3226 	/*
3227 	 * At this point the font is loaded, so add it to the
3228 	 * device's list of fonts.
3229 	 *
3230 	 * If the user specified a vector of AFMs, it is a Type 1 font
3231 	 */
3232 	if (!strcmp(family, "User") ||
3233 	    isType1Font(family, PostScriptFonts, NULL)) {
3234 	    pd->fonts = addDeviceFont(font, pd->fonts, &gotFont);
3235 	    pd->defaultFont = pd->fonts->family;
3236 	    pd->defaultCIDFont = NULL;
3237 	} else /* (isCIDFont(family, PostScriptFonts)) */ {
3238 	    pd->cidfonts = addDeviceCIDFont(cidfont, pd->cidfonts, &gotFont);
3239 	    pd->defaultFont = NULL;
3240 	    pd->defaultCIDFont = pd->cidfonts->cidfamily;
3241 	}
3242     }
3243     if (!gotFont) {
3244 	PS_cleanup(3, dd, pd);
3245 	error(_("failed to initialise default PostScript font"));
3246     }
3247 
3248     /*
3249      * Load the font names sent in via the fonts arg
3250      * NOTE that these are the font names specified at the
3251      * R-level, NOT the translated font names.
3252      */
3253     if (!isNull(fonts)) {
3254 	int i, dontcare, gotFonts = 0, nfonts = LENGTH(fonts);
3255 	type1fontlist fontlist;
3256 	cidfontlist cidfontlist;
3257 	for (i = 0; i < nfonts; i++) {
3258 	    int index, cidindex;
3259 	    const char *name = CHAR(STRING_ELT(fonts, i));
3260 	    /*
3261 	     * Check first whether this device is already
3262 	     * using this font.
3263 	     */
3264 	    if (findDeviceFont(name, pd->fonts, &index) ||
3265 		findDeviceCIDFont(name, pd->cidfonts, &cidindex))
3266 		gotFonts++;
3267 	    else {
3268 		/*
3269 		 * Check whether the font is loaded and, if not,
3270 		 * load it.
3271 		 */
3272 		font = findLoadedFont(name, encoding, FALSE);
3273 		cidfont = findLoadedCIDFont(name, FALSE);
3274 		if (!(font || cidfont)) {
3275 		    if (isType1Font(name, PostScriptFonts, NULL)) {
3276 			font = addFont(name, FALSE, pd->encodings);
3277 		    } else if (isCIDFont(name, PostScriptFonts, NULL)) {
3278 			cidfont = addCIDFont(name, FALSE);
3279 		    } else {
3280 			/*
3281 			 * Should NOT get here.
3282 			 */
3283 			PS_cleanup(4, dd, pd);
3284 			error(_("invalid font type"));
3285 		    }
3286 		}
3287 		/*
3288 		 * Once the font is loaded, add it to the device's
3289 		 * list of fonts.
3290 		 */
3291 		if (font || cidfont) {
3292 		    if (isType1Font(name, PostScriptFonts, NULL)) {
3293 			if ((fontlist = addDeviceFont(font, pd->fonts,
3294 						      &dontcare))) {
3295 			    pd->fonts = fontlist;
3296 			    gotFonts++;
3297 			}
3298 		    } else /* (isCIDFont(family, PostScriptFonts)) */ {
3299 			if ((cidfontlist = addDeviceCIDFont(cidfont,
3300 							    pd->cidfonts,
3301 							    &dontcare))) {
3302 			    pd->cidfonts = cidfontlist;
3303 			    gotFonts++;
3304 			}
3305 		    }
3306 		}
3307 	    }
3308 	}
3309 	if (gotFonts < nfonts) {
3310 	    PS_cleanup(4, dd, pd);
3311 	    error(_("failed to initialise additional PostScript fonts"));
3312 	}
3313     }
3314     /*****************************
3315      * END Load fonts
3316      *****************************/
3317 
3318     setbg = R_GE_str2col(bg);
3319     setfg = R_GE_str2col(fg);
3320 
3321     pd->width = width;
3322     pd->height = height;
3323     pd->landscape = horizontal;
3324     pointsize = floor(ps);
3325     if(R_TRANSPARENT(setbg) && R_TRANSPARENT(setfg)) {
3326 	PS_cleanup(4, dd, pd);
3327 	error(_("invalid foreground/background color (postscript)"));
3328     }
3329     pd->printit = printit;
3330     if(strlen(cmd) > 2*PATH_MAX - 1) {
3331 	PS_cleanup(4, dd, pd);
3332 	error(_("'command' is too long"));
3333     }
3334     strcpy(pd->command, cmd);
3335     if (printit && strlen(cmd) == 0) {
3336 	PS_cleanup(4, dd, pd);
3337 	error(_("'postscript(print.it=TRUE)' used with an empty 'print' command"));
3338     }
3339     strcpy(pd->command, cmd);
3340 
3341 
3342     /* Deal with paper and plot size and orientation */
3343 
3344     pd->paperspecial = FALSE;
3345     if(!strcmp(pd->papername, "Default") ||
3346        !strcmp(pd->papername, "default")) {
3347 	SEXP s = STRING_ELT(GetOption1(install("papersize")), 0);
3348 	if(s != NA_STRING && strlen(CHAR(s)) > 0)
3349 	    strcpy(pd->papername, CHAR(s));
3350 	else strcpy(pd->papername, "a4");
3351     }
3352     if(!strcmp(pd->papername, "A4") ||
3353        !strcmp(pd->papername, "a4")) {
3354 	pd->pagewidth  = 21.0 / 2.54;
3355 	pd->pageheight = 29.7  /2.54;
3356     }
3357     else if(!strcmp(pd->papername, "Letter") ||
3358 	    !strcmp(pd->papername, "letter") ||
3359 	    !strcmp(pd->papername, "US") ||
3360 	    !strcmp(pd->papername, "us")) {
3361 	pd->pagewidth  =  8.5;
3362 	pd->pageheight = 11.0;
3363     }
3364     else if(!strcmp(pd->papername, "Legal") ||
3365 	    !strcmp(pd->papername, "legal")) {
3366 	pd->pagewidth  =  8.5;
3367 	pd->pageheight = 14.0;
3368     }
3369     else if(!strcmp(pd->papername, "Executive") ||
3370 	    !strcmp(pd->papername, "executive")) {
3371 	pd->pagewidth  =  7.25;
3372 	pd->pageheight = 10.5;
3373     }
3374     else if(!strcmp(pd->papername, "special")) {
3375 	if(pd->landscape) {
3376 	    pd->pagewidth  = height;
3377 	    pd->pageheight =  width;
3378 	} else {
3379 	    pd->pagewidth  =  width;
3380 	    pd->pageheight = height;
3381 	}
3382 	pd->paperspecial = TRUE;
3383     }
3384     else {
3385 	char errbuf[strlen(pd->papername) + 1];
3386 	strcpy(errbuf, pd->papername);
3387 	PS_cleanup(4, dd, pd);
3388 	error(_("invalid page type '%s' (postscript)"), errbuf);
3389     }
3390     pd->pagecentre = pagecentre;
3391     pd->paperwidth = (int)(72 * pd->pagewidth);
3392     pd->paperheight = (int)(72 * pd->pageheight);
3393     pd->onefile = onefile;
3394     if(pd->landscape) {
3395 	double tmp;
3396 	tmp = pd->pagewidth;
3397 	pd->pagewidth = pd->pageheight;
3398 	pd->pageheight = tmp;
3399     }
3400     if(strcmp(pd->papername, "special"))
3401     {
3402 	if(pd->width < 0.1 || pd->width > pd->pagewidth-0.5)
3403 	    pd->width = pd->pagewidth-0.5;
3404 	if(pd->height < 0.1 || pd->height > pd->pageheight-0.5)
3405 	    pd->height = pd->pageheight-0.5;
3406     }
3407     if(pagecentre)
3408     {
3409 	xoff = (pd->pagewidth - pd->width)/2.0;
3410 	yoff = (pd->pageheight - pd->height)/2.0;
3411     } else {
3412 	xoff = yoff = 0.0;
3413     }
3414     pd->maxpointsize = (int)(72.0 * ((pd->pageheight > pd->pagewidth) ?
3415 				     pd->pageheight : pd->pagewidth));
3416     pd->pageno = pd->fileno = 0;
3417     pd->warn_trans = FALSE;
3418 
3419     /* Base Pointsize */
3420     /* Nominal Character Sizes in Pixels */
3421     /* Only right for 12 point font. */
3422     /* Max pointsize suggested by Peter Dalgaard */
3423 
3424     if(pointsize < 6.0) pointsize = 6.0;
3425     if(pointsize > pd->maxpointsize) pointsize = pd->maxpointsize;
3426     dd->startps = pointsize;
3427     dd->startfont = 1;
3428     dd->startlty = 0;
3429     dd->startfill = setbg;
3430     dd->startcol = setfg;
3431     dd->startgamma = 1;
3432 
3433     /* Set graphics parameters that must be set by device driver. */
3434     /* Page dimensions in points. */
3435 
3436     dd->left = 72 * xoff;			/* left */
3437     dd->right = 72 * (xoff + pd->width);	/* right */
3438     dd->bottom = 72 * yoff;			/* bottom */
3439     dd->top = 72 * (yoff + pd->height);	        /* top */
3440     dd->clipLeft = dd->left; dd->clipRight = dd->right;
3441     dd->clipBottom = dd->bottom; dd->clipTop = dd->top;
3442 
3443     dd->cra[0] = 0.9 * pointsize;
3444     dd->cra[1] = 1.2 * pointsize;
3445 
3446     /* Character Addressing Offsets */
3447     /* These offsets should center a single */
3448     /* plotting character over the plotting point. */
3449     /* Pure guesswork and eyeballing ... */
3450 
3451     dd->xCharOffset =  0.4900;
3452     dd->yCharOffset =  0.3333;
3453     dd->yLineBias = 0.2;
3454 
3455     /* Inches per Raster Unit */
3456     /* We use points (72 dots per inch) */
3457 
3458     dd->ipr[0] = 1.0/72.0;
3459     dd->ipr[1] = 1.0/72.0;
3460     /* GREset(.)  dd->gp.mkh = dd->gp.cra[0] * dd->gp.ipr[0]; */
3461 
3462     dd->canClip = TRUE;
3463     dd->canHAdj = 2;
3464     dd->canChangeGamma = FALSE;
3465 
3466     /*	Start the driver */
3467     PS_Open(dd, pd);
3468 
3469     dd->close      = PS_Close;
3470     dd->size     = PS_Size;
3471     dd->newPage    = PS_NewPage;
3472     dd->clip	      = PS_Clip;
3473     dd->text	      = PS_Text;
3474     dd->strWidth   = PS_StrWidth;
3475     dd->metricInfo = PS_MetricInfo;
3476     dd->rect	      = PS_Rect;
3477     dd->path     = PS_Path;
3478     dd->raster     = PS_Raster;
3479     dd->circle     = PS_Circle;
3480     dd->line	      = PS_Line;
3481     dd->polygon    = PS_Polygon;
3482     dd->polyline   = PS_Polyline;
3483     /* dd->locator    = PS_Locator;
3484        dd->mode	      = PS_Mode; */
3485     dd->hasTextUTF8   = TRUE;
3486     dd->textUTF8      = PS_TextUTF8;
3487     dd->strWidthUTF8  = PS_StrWidthUTF8;
3488     dd->useRotatedTextInContour = TRUE;
3489     dd->haveTransparency = 1;
3490     dd->haveTransparentBg = 2;
3491     dd->haveRaster = 3; /* non-missing colours */
3492     dd->setPattern      = PS_setPattern;
3493     dd->releasePattern  = PS_releasePattern;
3494     dd->setClipPath     = PS_setClipPath;
3495     dd->releaseClipPath = PS_releaseClipPath;
3496     dd->setMask         = PS_setMask;
3497     dd->releaseMask     = PS_releaseMask;
3498 
3499     dd->deviceSpecific = (void *) pd;
3500     dd->displayListOn = FALSE;
3501 
3502     dd->deviceVersion = R_GE_definitions;
3503     return TRUE;
3504 }
3505 
CheckAlpha(int color,PostScriptDesc * pd)3506 static void CheckAlpha(int color, PostScriptDesc *pd)
3507 {
3508     unsigned int alpha = R_ALPHA(color);
3509     if (alpha > 0 && alpha < 255 && !pd->warn_trans) {
3510 	warning(_("semi-transparency is not supported on this device: reported only once per page"));
3511 	pd->warn_trans = TRUE;
3512     }
3513 }
3514 
SetColor(int color,pDevDesc dd)3515 static void SetColor(int color, pDevDesc dd)
3516 {
3517     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3518     if(color != pd->current.col) {
3519 	PostScriptSetCol(pd->psfp,
3520 			 R_RED(color)/255.0,
3521 			 R_GREEN(color)/255.0,
3522 			 R_BLUE(color)/255.0, pd);
3523 	fprintf(pd->psfp, "\n");
3524 	pd->current.col = color;
3525     }
3526 }
3527 
SetFill(int color,pDevDesc dd)3528 static void SetFill(int color, pDevDesc dd)
3529 {
3530     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3531     if(color != pd->current.fill) {
3532 	PostScriptSetFill(pd->psfp,
3533 			  R_RED(color)/255.0,
3534 			  R_GREEN(color)/255.0,
3535 			  R_BLUE(color)/255.0, pd);
3536 	pd->current.fill = color;
3537     }
3538 }
3539 
3540 /* Note that the line texture is scaled by the line width. */
3541 
SetLineStyle(const pGEcontext gc,pDevDesc dd)3542 static void SetLineStyle(const pGEcontext gc, pDevDesc dd)
3543 {
3544     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3545     char dashlist[8];
3546     int i;
3547     int newlty = gc->lty;
3548     double newlwd = gc->lwd;
3549     R_GE_lineend newlend = gc->lend;
3550     R_GE_linejoin newljoin = gc->ljoin;
3551     double newlmitre = gc->lmitre;
3552 
3553     if (pd->current.lty != newlty || pd->current.lwd != newlwd) {
3554 	pd->current.lwd = newlwd;
3555 	pd->current.lty = newlty;
3556 	PostScriptSetLineWidth(pd->psfp, newlwd * 0.75);
3557 	/* process lty : */
3558 	for(i = 0; i < 8 && newlty & 15 ; i++) {
3559 	    dashlist[i] = newlty & 15;
3560 	    newlty = newlty >> 4;
3561 	}
3562 	PostScriptSetLineTexture(pd->psfp, dashlist, i, newlwd * 0.75, newlend);
3563     }
3564     if (pd->current.lend != newlend) {
3565 	pd->current.lend = newlend;
3566 	PostScriptSetLineEnd(pd->psfp, newlend);
3567     }
3568     if (pd->current.ljoin != newljoin) {
3569 	pd->current.ljoin = newljoin;
3570 	PostScriptSetLineJoin(pd->psfp, newljoin);
3571     }
3572     if (pd->current.lmitre != newlmitre) {
3573 	pd->current.lmitre = newlmitre;
3574 	PostScriptSetLineMitre(pd->psfp, newlmitre);
3575     }
3576 }
3577 
SetFont(int font,int size,pDevDesc dd)3578 static void SetFont(int font, int size, pDevDesc dd)
3579 {
3580     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3581     if(size < 1 || size > pd->maxpointsize)
3582 	size = 10;
3583     if (size != pd->current.fontsize || font != pd->current.font) {
3584 	PostScriptSetFont(pd->psfp, font, size);
3585 	pd->current.fontsize = size;
3586 	pd->current.font = font;
3587     }
3588 }
3589 
PS_cleanup(int stage,pDevDesc dd,PostScriptDesc * pd)3590 static void PS_cleanup(int stage, pDevDesc dd, PostScriptDesc *pd)
3591 {
3592     switch (stage) {
3593     case 4: /* Allocated fonts */
3594     freeDeviceFontList(pd->fonts);
3595     freeDeviceCIDFontList(pd->cidfonts);
3596     case 3: /* Allocated encodings */
3597     freeDeviceEncList(pd->encodings);
3598     case 1: /* Allocated PDFDesc */
3599     free(pd);
3600     free(dd);
3601     }
3602 }
3603 
3604 
PS_Open(pDevDesc dd,PostScriptDesc * pd)3605 static Rboolean PS_Open(pDevDesc dd, PostScriptDesc *pd)
3606 {
3607     char buf[512];
3608 
3609     if (strlen(pd->filename) == 0) {
3610 	if(strlen(pd->command) == 0)
3611 	    pd->psfp = NULL;
3612 	else {
3613 	    errno = 0;
3614 	    pd->psfp = R_popen(pd->command, "w");
3615 	    pd->open_type = 1;
3616 	}
3617 	if (!pd->psfp || errno != 0) {
3618 	    char errbuf[strlen(pd->command) + 1];
3619 	    strcpy(errbuf, pd->command);
3620 	    PS_cleanup(4, dd, pd);
3621 	    error(_("cannot open 'postscript' pipe to '%s'"), errbuf);
3622 	    return FALSE;
3623 	}
3624     } else if (pd->filename[0] == '|') {
3625 	errno = 0;
3626 	pd->psfp = R_popen(pd->filename + 1, "w");
3627 	pd->open_type = 1;
3628 	if (!pd->psfp || errno != 0) {
3629 	    char errbuf[strlen(pd->filename + 1) + 1];
3630 	    strcpy(errbuf, pd->filename + 1);
3631 	    PS_cleanup(4, dd, pd);
3632 	    error(_("cannot open 'postscript' pipe to '%s'"),
3633 		     errbuf);
3634 	    return FALSE;
3635 	}
3636     } else {
3637 	snprintf(buf, 512, pd->filename, pd->fileno + 1); /* file 1 to start */
3638 	pd->psfp = R_fopen(R_ExpandFileName(buf), "w");
3639 	pd->open_type = 0;
3640     }
3641     if (!pd->psfp) {
3642 	PS_cleanup(4, dd, pd);
3643 	error(_("cannot open file '%s'"), buf);
3644 	return FALSE;
3645     }
3646 
3647     if(pd->landscape)
3648 	PSFileHeader(pd->psfp,
3649 		     pd->papername,
3650 		     pd->paperwidth,
3651 		     pd->paperheight,
3652 		     pd->landscape,
3653 		     !(pd->onefile),
3654 		     pd->paperspecial,
3655 		     dd->bottom,
3656 		     dd->left,
3657 		     dd->top,
3658 		     dd->right,
3659 		     pd->title,
3660 		     pd);
3661     else
3662 	PSFileHeader(pd->psfp,
3663 		     pd->papername,
3664 		     pd->paperwidth,
3665 		     pd->paperheight,
3666 		     pd->landscape,
3667 		     !(pd->onefile),
3668 		     pd->paperspecial,
3669 		     dd->left,
3670 		     dd->bottom,
3671 		     dd->right,
3672 		     dd->top,
3673 		     pd->title,
3674 		     pd);
3675 
3676     return TRUE;
3677 }
3678 
3679 /* The driver keeps track of the current values of colors, fonts and
3680    line parameters, to save emitting some PostScript. In some cases,
3681    the state becomes unknown, notably after changing the clipping and
3682    at the start of a new page, so we have the following routine to
3683    invalidate the saved values, which in turn causes the parameters to
3684    be set before usage.
3685 
3686    Called at the start of each page and by PS_Clip (since that
3687    does a grestore).
3688 */
3689 
Invalidate(pDevDesc dd)3690 static void Invalidate(pDevDesc dd)
3691 {
3692     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3693 
3694     pd->current.font = -1;
3695     pd->current.fontsize = -1;
3696     pd->current.lwd = -1;
3697     pd->current.lty = -1;
3698     pd->current.lend = 0;
3699     pd->current.ljoin = 0;
3700     pd->current.lmitre = 0;
3701     pd->current.col = INVALID_COL;
3702     pd->current.fill = INVALID_COL;
3703 }
3704 
PS_Clip(double x0,double x1,double y0,double y1,pDevDesc dd)3705 static void PS_Clip(double x0, double x1, double y0, double y1, pDevDesc dd)
3706 {
3707     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3708 
3709     PostScriptSetClipRect(pd->psfp, x0, x1, y0, y1);
3710     /* clipping does grestore so invalidate monitor variables */
3711     Invalidate(dd);
3712 }
3713 
PS_Size(double * left,double * right,double * bottom,double * top,pDevDesc dd)3714 static void PS_Size(double *left, double *right,
3715 		    double *bottom, double *top,
3716 		    pDevDesc dd)
3717 {
3718     *left = dd->left;
3719     *right = dd->right;
3720     *bottom = dd->bottom;
3721     *top = dd->top;
3722 }
3723 
3724 static void PostScriptClose(pDevDesc dd);
3725 
PS_NewPage(const pGEcontext gc,pDevDesc dd)3726 static void PS_NewPage(const pGEcontext gc,
3727 		       pDevDesc dd)
3728 {
3729     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3730 
3731 
3732     if(pd->onefile) {
3733 	if(++pd->pageno > 1) PostScriptEndPage(pd->psfp);
3734     } else if(pd->pageno > 0) {
3735 	PostScriptClose(dd);
3736 	pd->fileno++;
3737 	PS_Open(dd, pd);
3738 	pd->pageno = 1;
3739     } else pd->pageno++;
3740     PostScriptStartPage(pd->psfp, pd->pageno);
3741     Invalidate(dd);
3742     CheckAlpha(gc->fill, pd);
3743     if(R_OPAQUE(gc->fill)) {
3744 	/*
3745 	 * Override some gc settings
3746 	 */
3747 	gc->col = R_TRANWHITE;
3748 	PS_Rect(0, 0, 72.0 * pd->pagewidth, 72.0 * pd->pageheight, gc, dd);
3749     }
3750     pd->warn_trans = FALSE;
3751 }
3752 
3753 #ifdef Win32
3754 #include "run.h" /* for runcmd */
3755 #endif
PostScriptClose(pDevDesc dd)3756 static void PostScriptClose(pDevDesc dd)
3757 {
3758     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3759 
3760     PostScriptFileTrailer(pd->psfp, pd->pageno);
3761     if(pd->open_type == 1)
3762 	pclose(pd->psfp);
3763     else {
3764 	fclose(pd->psfp);
3765 	if (pd->printit) {
3766 	    char buff[3*PATH_MAX+ 10];
3767 	    int err = 0;
3768 	    /* This should not be possible: the command is limited
3769 	       to 2*PATH_MAX */
3770 	    if(strlen(pd->command) + strlen(pd->filename) > 3*PATH_MAX) {
3771 		warning(_("error from postscript() in running:\n    %s"),
3772 			pd->command);
3773 		return;
3774 	    }
3775 	    strcpy(buff, pd->command);
3776 	    strcat(buff, " ");
3777 	    strcat(buff, pd->filename);
3778 /*	    Rprintf("buff is %s\n", buff); */
3779 #ifdef Unix
3780 	    err = R_system(buff);
3781 #endif
3782 #ifdef Win32
3783 	    err = Rf_runcmd(buff, CE_NATIVE, 0, 0, NULL, NULL, NULL);
3784 #endif
3785 	    if (err)
3786 		warning(_("error from postscript() in running:\n    %s"),
3787 			buff);
3788 	}
3789     }
3790 }
3791 
PS_Close(pDevDesc dd)3792 static void PS_Close(pDevDesc dd)
3793 {
3794     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3795 
3796     PostScriptClose(dd);
3797     freeDeviceCIDFontList(pd->cidfonts);
3798     freeDeviceFontList(pd->fonts);
3799     freeDeviceEncList(pd->encodings);
3800     pd->cidfonts = NULL;
3801     pd->fonts = NULL;
3802     pd->encodings = NULL;
3803     free(pd);
3804 }
3805 
3806 static FontMetricInfo
CIDsymbolmetricInfo(const char * family,PostScriptDesc * pd)3807 *CIDsymbolmetricInfo(const char *family, PostScriptDesc *pd)
3808 {
3809     FontMetricInfo *result = NULL;
3810     int fontIndex;
3811     cidfontfamily fontfamily;
3812 
3813     fontfamily = findDeviceCIDFont(family, pd->cidfonts, &fontIndex);
3814     if (fontfamily) {
3815 	/* (Type 1!) symbol font */
3816 	result = &(fontfamily->symfont->metrics);
3817     } else
3818 	error(_("CID family '%s' not included in postscript() device"),
3819 	      family);
3820     return result;
3821 }
3822 
metricInfo(const char * family,int face,PostScriptDesc * pd)3823 static FontMetricInfo *metricInfo(const char *family, int face,
3824 				  PostScriptDesc *pd) {
3825     FontMetricInfo *result = NULL;
3826     int fontIndex;
3827     type1fontfamily fontfamily = findDeviceFont(family, pd->fonts, &fontIndex);
3828     if (fontfamily) {
3829 	if(face < 1 || face > 5) {
3830 	    warning(_("attempt to use invalid font %d replaced by font 1"),
3831 		    face);
3832 	    face = 1;
3833 	}
3834 	result = &(fontfamily->fonts[face-1]->metrics);
3835     } else
3836 	error(_("family '%s' not included in postscript() device"), family);
3837     return result;
3838 }
3839 
convname(const char * family,PostScriptDesc * pd)3840 static char *convname(const char *family, PostScriptDesc *pd) {
3841     char *result = NULL;
3842     int fontIndex;
3843     type1fontfamily fontfamily = findDeviceFont(family, pd->fonts, &fontIndex);
3844     if (fontfamily)
3845 	result = fontfamily->encoding->convname;
3846     else
3847 	error(_("family '%s' not included in postscript() device"), family);
3848     return result;
3849 }
3850 
PS_StrWidth(const char * str,const pGEcontext gc,pDevDesc dd)3851 static double PS_StrWidth(const char *str,
3852 			  const pGEcontext gc,
3853 			  pDevDesc dd)
3854 {
3855     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3856     int face = gc->fontface;
3857 
3858     if(face < 1 || face > 5) face = 1;
3859     if (isType1Font(gc->fontfamily, PostScriptFonts, pd->defaultFont)) {
3860 	return floor(gc->cex * gc->ps + 0.5) *
3861 	    PostScriptStringWidth((const unsigned char *)str, CE_NATIVE,
3862 				  metricInfo(gc->fontfamily, face, pd),
3863 				  pd->useKern, face,
3864 				  convname(gc->fontfamily, pd));
3865     } else { /* cidfont(gc->fontfamily, PostScriptFonts) */
3866 	if (face < 5) {
3867 	    return floor(gc->cex * gc->ps + 0.5) *
3868 		PostScriptStringWidth((const unsigned char *)str, CE_NATIVE,
3869 				      NULL, FALSE, face, NULL);
3870 	} else {
3871 	    return floor(gc->cex * gc->ps + 0.5) *
3872 		PostScriptStringWidth((const unsigned char *)str, CE_NATIVE,
3873 				      /* Send symbol face metric info */
3874 				      CIDsymbolmetricInfo(gc->fontfamily, pd),
3875 				      FALSE, face, NULL);
3876 	}
3877     }
3878 }
3879 
PS_StrWidthUTF8(const char * str,const pGEcontext gc,pDevDesc dd)3880 static double PS_StrWidthUTF8(const char *str,
3881 			      const pGEcontext gc,
3882 			      pDevDesc dd)
3883 {
3884     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3885     int face = gc->fontface;
3886 
3887     if(face < 1 || face > 5) face = 1;
3888     if (isType1Font(gc->fontfamily, PostScriptFonts, pd->defaultFont)) {
3889 	return floor(gc->cex * gc->ps + 0.5) *
3890 	    PostScriptStringWidth((const unsigned char *)str, CE_UTF8,
3891 				  metricInfo(gc->fontfamily, face, pd),
3892 				  pd->useKern, face,
3893 				  convname(gc->fontfamily, pd));
3894     } else { /* cidfont(gc->fontfamily, PostScriptFonts) */
3895 	if (face < 5) {
3896 	    return floor(gc->cex * gc->ps + 0.5) *
3897 		PostScriptStringWidth((const unsigned char *)str, CE_UTF8,
3898 				      NULL, FALSE, face, NULL);
3899 	} else {
3900 	    return floor(gc->cex * gc->ps + 0.5) *
3901 		PostScriptStringWidth((const unsigned char *)str, CE_UTF8,
3902 				      /* Send symbol face metric info */
3903 				      CIDsymbolmetricInfo(gc->fontfamily, pd),
3904 				      FALSE, face, NULL);
3905 	}
3906     }
3907 }
3908 
PS_MetricInfo(int c,const pGEcontext gc,double * ascent,double * descent,double * width,pDevDesc dd)3909 static void PS_MetricInfo(int c,
3910 			  const pGEcontext gc,
3911 			  double* ascent, double* descent,
3912 			  double* width, pDevDesc dd)
3913 {
3914     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3915     int face = gc->fontface;
3916 
3917     if(face < 1 || face > 5) face = 1;
3918 
3919     if (isType1Font(gc->fontfamily, PostScriptFonts, pd->defaultFont)) {
3920 	PostScriptMetricInfo(c, ascent, descent, width,
3921 			     metricInfo(gc->fontfamily, face, pd),
3922 			     face == 5, convname(gc->fontfamily, pd));
3923     } else { /* cidfont(gc->fontfamily, PostScriptFonts) */
3924 	if (face < 5) {
3925 	    PostScriptCIDMetricInfo(c, ascent, descent, width);
3926 	} else {
3927 	    PostScriptMetricInfo(c, ascent, descent, width,
3928 				 CIDsymbolmetricInfo(gc->fontfamily, pd),
3929 				 TRUE, "");
3930 	}
3931     }
3932     *ascent = floor(gc->cex * gc->ps + 0.5) * *ascent;
3933     *descent = floor(gc->cex * gc->ps + 0.5) * *descent;
3934     *width = floor(gc->cex * gc->ps + 0.5) * *width;
3935 }
3936 
PS_Rect(double x0,double y0,double x1,double y1,const pGEcontext gc,pDevDesc dd)3937 static void PS_Rect(double x0, double y0, double x1, double y1,
3938 		    const pGEcontext gc,
3939 		    pDevDesc dd)
3940 {
3941     int code;
3942     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
3943 
3944     /* code is set as follows */
3945     /* code == 0, nothing to draw */
3946     /* code == 1, outline only */
3947     /* code == 2, fill only */
3948     /* code == 3, outline and fill */
3949 
3950     CheckAlpha(gc->fill, pd);
3951     CheckAlpha(gc->col, pd);
3952     code = 2 * (R_OPAQUE(gc->fill)) + (R_OPAQUE(gc->col));
3953 
3954     if (code) {
3955 	if(code & 2)
3956 	    SetFill(gc->fill, dd);
3957 	if(code & 1) {
3958 	    SetColor(gc->col, dd);
3959 	    SetLineStyle(gc, dd);
3960 	}
3961 	PostScriptRectangle(pd->psfp, x0, y0, x1, y1);
3962 	fprintf(pd->psfp, "p%d\n", code);
3963     }
3964 }
3965 
3966 typedef rcolor * rcolorPtr;
3967 
PS_imagedata(rcolorPtr raster,int w,int h,PostScriptDesc * pd)3968 static void PS_imagedata(rcolorPtr raster,
3969 			 int w, int h,
3970 			 PostScriptDesc *pd)
3971 {
3972     /* Each original byte is translated to two hex digits
3973        (representing a number between 0 and 255) */
3974     for (int i = 0; i < w*h; i++)
3975 	fprintf(pd->psfp, "%02x%02x%02x",
3976 		R_RED(raster[i]), R_GREEN(raster[i]), R_BLUE(raster[i]));
3977 }
3978 
PS_grayimagedata(rcolorPtr raster,int w,int h,PostScriptDesc * pd)3979 static void PS_grayimagedata(rcolorPtr raster,
3980 			     int w, int h,
3981 			     PostScriptDesc *pd)
3982 {
3983     /* Weights as in PDF gray conversion */
3984     for (int i = 0; i < w*h; i++) {
3985 	double r = 0.213 * R_RED(raster[i]) + 0.715 * R_GREEN(raster[i])
3986 	    + 0.072 * R_BLUE(raster[i]);
3987 	fprintf(pd->psfp, "%02x", (int)(r+0.49));
3988     }
3989 }
3990 
3991 /* Could support 'colormodel = "cmyk"' */
PS_writeRaster(unsigned int * raster,int w,int h,double x,double y,double width,double height,double rot,Rboolean interpolate,pDevDesc dd)3992 static void PS_writeRaster(unsigned int *raster, int w, int h,
3993 			   double x, double y,
3994 			   double width, double height,
3995 			   double rot,
3996 			   Rboolean interpolate,
3997 			   pDevDesc dd)
3998 {
3999     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
4000 
4001     /* This takes the simple approach of creating an inline
4002      * image.
4003      * There is no support for semitransparent images, not even
4004      * for transparent pixels (missing values in image(useRaster = TRUE) ).
4005      *
4006      * The version in R < 2.13.2 used colorimage, hence the DeviceRGB
4007      * colour space.
4008      */
4009 
4010     /* Now we are using level-2 features, there are other things we could do
4011        (a) encode the data more compactly, e.g. using
4012        /DataSource currentfile /ASCII85Decode filter /FlateDecode filter def
4013 
4014        (b) add a mask with ImageType 3: see PLRM 3rd ed section 4.10.6.
4015 
4016        (c) interpolation (done but disabled, as at least ghostscript
4017        seems to ignore the request, and Mac preview always
4018        interpolates.)
4019 
4020        (d) sRGB colorspace (done)
4021     */
4022 
4023     /* Save graphics state */
4024     fprintf(pd->psfp, "gsave\n");
4025     /* set the colour space: this form of the image operator uses the
4026        current colour space. */
4027     if (streql(pd->colormodel, "srgb+gray"))
4028 	fprintf(pd->psfp, "sRGB\n");
4029     else if (streql(pd->colormodel, "srgb")) /* set for page */ ;
4030     else if (streql(pd->colormodel, "gray"))
4031 	fprintf(pd->psfp, "/DeviceGray setcolorspace\n");
4032     else
4033 	fprintf(pd->psfp, "/DeviceRGB setcolorspace\n");
4034     /* translate */
4035     fprintf(pd->psfp, "%.2f %.2f translate\n", x, y);
4036     /* rotate */
4037     if (rot != 0.0) fprintf(pd->psfp, "%.2f rotate\n", rot);
4038     /* scale */
4039     fprintf(pd->psfp, "%.2f %.2f scale\n", width, height);
4040     /* write dictionary */
4041     fprintf(pd->psfp, "8 dict dup begin\n");
4042     fprintf(pd->psfp, "  /ImageType 1 def\n");
4043     fprintf(pd->psfp, "  /Width %d def\n", w);
4044     fprintf(pd->psfp, "  /Height %d def\n", h);
4045     fprintf(pd->psfp, "  /BitsPerComponent 8 def\n");
4046     if (interpolate)
4047 	fprintf(pd->psfp, "  /Interpolate true def\n");
4048     if (streql(pd->colormodel, "gray"))
4049 	fprintf(pd->psfp, "  /Decode [0 1] def\n");
4050     else
4051 	fprintf(pd->psfp, "  /Decode [0 1 0 1 0 1] def\n");
4052     fprintf(pd->psfp, "  /DataSource currentfile /ASCIIHexDecode filter def\n");
4053     fprintf(pd->psfp, "  /ImageMatrix [%d 0 0 %d 0 %d] def\n", w, -h, h);
4054     fprintf(pd->psfp, "end\n");
4055     fprintf(pd->psfp, "image\n");
4056     /* now the data */
4057     if (streql(pd->colormodel, "gray"))
4058 	PS_grayimagedata(raster, w, h, pd);
4059     else
4060 	PS_imagedata(raster, w, h, pd);
4061     fprintf(pd->psfp, ">\n");
4062     /* Restore graphics state */
4063     fprintf(pd->psfp, "grestore\n");
4064 }
4065 
4066 /* see comments above */
4067 #define OLD 1
PS_Raster(unsigned int * raster,int w,int h,double x,double y,double width,double height,double rot,Rboolean interpolate,const pGEcontext gc,pDevDesc dd)4068 static void PS_Raster(unsigned int *raster, int w, int h,
4069 		      double x, double y,
4070 		      double width, double height,
4071 		      double rot,
4072 		      Rboolean interpolate,
4073 		      const pGEcontext gc, pDevDesc dd)
4074 {
4075 #ifdef OLD
4076     if (interpolate) {
4077 	/* Generate a new raster
4078 	 * which is interpolated from the original
4079 	 * Assume a resolution for the new raster of 72 dpi
4080 	 * Ideally would allow user to set this.
4081 	 */
4082 	const void *vmax;
4083 	vmax = vmaxget();
4084 	int newW = (int) width;
4085 	int newH = (int) height;
4086 	unsigned int *newRaster =
4087 	    (unsigned int *) R_alloc(newW * newH, sizeof(unsigned int));
4088 
4089 	R_GE_rasterInterpolate(raster, w, h,
4090 			       newRaster, newW, newH);
4091 	PS_writeRaster(newRaster, newW, newH,
4092 		       x, y, width, height, rot, FALSE, dd);
4093 	vmaxset(vmax);
4094     } else {
4095 	PS_writeRaster(raster, w, h,
4096 		       x, y, width, height, rot, FALSE, dd);
4097     }
4098 #else
4099 	PS_writeRaster(raster, w, h,
4100 		       x, y, width, height, rot, interpolate, dd);
4101 #endif
4102 }
4103 
PS_Circle(double x,double y,double r,const pGEcontext gc,pDevDesc dd)4104 static void PS_Circle(double x, double y, double r,
4105 		      const pGEcontext gc,
4106 		      pDevDesc dd)
4107 {
4108     int code;
4109     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
4110 
4111     /* code is set as follows */
4112     /* code == 0, nothing to draw */
4113     /* code == 1, outline only */
4114     /* code == 2, fill only */
4115     /* code == 3, outline and fill */
4116 
4117     CheckAlpha(gc->fill, pd);
4118     CheckAlpha(gc->col, pd);
4119     code = 2 * (R_OPAQUE(gc->fill)) + (R_OPAQUE(gc->col));
4120 
4121     if (code) {
4122 	if(code & 2)
4123 	    SetFill(gc->fill, dd);
4124 	if(code & 1) {
4125 	    SetColor(gc->col, dd);
4126 	    SetLineStyle(gc, dd);
4127 	}
4128 	PostScriptCircle(pd->psfp, x, y, r);
4129 	fprintf(pd->psfp, "p%d\n", code);
4130     }
4131 }
4132 
PS_Line(double x1,double y1,double x2,double y2,const pGEcontext gc,pDevDesc dd)4133 static void PS_Line(double x1, double y1, double x2, double y2,
4134 		    const pGEcontext gc,
4135 		    pDevDesc dd)
4136 {
4137     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
4138 
4139     CheckAlpha(gc->col, pd);
4140     /* FIXME : clip to the device extents here */
4141     if(R_OPAQUE(gc->col)) {
4142 	SetColor(gc->col, dd);
4143 	SetLineStyle(gc, dd);
4144 	PostScriptStartPath(pd->psfp);
4145 	PostScriptMoveTo(pd->psfp, x1, y1);
4146 	PostScriptRLineTo(pd->psfp, x1, y1, x2, y2);
4147 	/* fprintf(pd->psfp, "%.2f %.2f rl\n", x2 - x1, y2 - y1);*/
4148 	PostScriptEndPath(pd->psfp);
4149     }
4150 }
4151 
PS_Polygon(int n,double * x,double * y,const pGEcontext gc,pDevDesc dd)4152 static void PS_Polygon(int n, double *x, double *y,
4153 		       const pGEcontext gc,
4154 		       pDevDesc dd)
4155 {
4156     PostScriptDesc *pd;
4157     int i, code;
4158 
4159     pd = (PostScriptDesc *) dd->deviceSpecific;
4160 
4161     /* code is set as follows */
4162     /* code == 0, nothing to draw */
4163     /* code == 1, outline only */
4164     /* code == 2, fill only */
4165     /* code == 3, outline and fill */
4166     /* code == 6, eofill only */
4167     /* code == 7, outline and eofill */
4168 
4169     CheckAlpha(gc->fill, pd);
4170     CheckAlpha(gc->col, pd);
4171     code = 2 * (R_OPAQUE(gc->fill)) + (R_OPAQUE(gc->col));
4172 
4173     if (code) {
4174 	if(code & 2) {
4175 	    SetFill(gc->fill, dd);
4176 	    if (pd->fillOddEven) code |= 4;
4177 	}
4178 	if(code & 1) {
4179 	    SetColor(gc->col, dd);
4180 	    SetLineStyle(gc, dd);
4181 	}
4182 	fprintf(pd->psfp, "np\n");
4183 	fprintf(pd->psfp, " %.2f %.2f m\n", x[0], y[0]);
4184 	for(i = 1 ; i < n ; i++)
4185 	    if (i % 100 == 0)
4186 		fprintf(pd->psfp, "%.2f %.2f lineto\n", x[i], y[i]);
4187 	    else
4188 		PostScriptRLineTo(pd->psfp, x[i-1], y[i-1], x[i], y[i]);
4189 	fprintf(pd->psfp, "cp p%d\n", code);
4190     }
4191 }
4192 
PS_Path(double * x,double * y,int npoly,int * nper,Rboolean winding,const pGEcontext gc,pDevDesc dd)4193 static void PS_Path(double *x, double *y,
4194                     int npoly, int *nper,
4195                     Rboolean winding,
4196                     const pGEcontext gc,
4197                     pDevDesc dd)
4198 {
4199     PostScriptDesc *pd;
4200     int i, j, index, code;
4201 
4202     pd = (PostScriptDesc *) dd->deviceSpecific;
4203 
4204     /* code is set as follows */
4205     /* code == 0, nothing to draw */
4206     /* code == 1, outline only */
4207     /* code == 2, fill only */
4208     /* code == 3, outline and fill */
4209     /* code == 6, eofill only */
4210     /* code == 7, outline and eofill */
4211 
4212     CheckAlpha(gc->fill, pd);
4213     CheckAlpha(gc->col, pd);
4214     code = 2 * (R_OPAQUE(gc->fill)) + (R_OPAQUE(gc->col));
4215 
4216     if (code) {
4217 	if(code & 2) {
4218 	    SetFill(gc->fill, dd);
4219 	    if (!winding) code |= 4;
4220 	}
4221 	if(code & 1) {
4222 	    SetColor(gc->col, dd);
4223 	    SetLineStyle(gc, dd);
4224 	}
4225 	fprintf(pd->psfp, "np\n");
4226         index = 0;
4227         for (i = 0; i < npoly; i++) {
4228             fprintf(pd->psfp, " %.2f %.2f m\n", x[index], y[index]);
4229             index++;
4230             for(j = 1; j < nper[i]; j++) {
4231                 if (j % 100 == 0)
4232                     fprintf(pd->psfp, "%.2f %.2f lineto\n",
4233                             x[index], y[index]);
4234                 else
4235                     PostScriptRLineTo(pd->psfp, x[index-1], y[index-1],
4236                                       x[index], y[index]);
4237                 index++;
4238             }
4239             fprintf(pd->psfp, "cp\n");
4240         }
4241 	fprintf(pd->psfp, "p%d\n", code);
4242     }
4243 }
4244 
PS_Polyline(int n,double * x,double * y,const pGEcontext gc,pDevDesc dd)4245 static void PS_Polyline(int n, double *x, double *y,
4246 			const pGEcontext gc,
4247 			pDevDesc dd)
4248 {
4249     PostScriptDesc *pd;
4250     int i;
4251 
4252     pd = (PostScriptDesc*) dd->deviceSpecific;
4253     CheckAlpha(gc->col, pd);
4254     if(R_OPAQUE(gc->col)) {
4255 	SetColor(gc->col, dd);
4256 	SetLineStyle(gc, dd);
4257 	fprintf(pd->psfp, "np\n");
4258 	fprintf(pd->psfp, "%.2f %.2f m\n", x[0], y[0]);
4259 	for(i = 1 ; i < n ; i++) {
4260 	    /* split up solid lines (only) into chunks of size 1000 */
4261 	    if(gc->lty == 0 && i%1000 == 0)
4262 		fprintf(pd->psfp, "currentpoint o m\n");
4263 	    if (i % 100 == 0)
4264 		fprintf(pd->psfp, "%.2f %.2f lineto\n", x[i], y[i]);
4265 	    else
4266 		PostScriptRLineTo(pd->psfp, x[i-1], y[i-1], x[i], y[i]);
4267 	}
4268 	fprintf(pd->psfp, "o\n");
4269     }
4270 }
4271 
translateFont(char * family,int style,PostScriptDesc * pd)4272 static int translateFont(char *family, int style, PostScriptDesc *pd)
4273 {
4274     int result = style;
4275     type1fontfamily fontfamily;
4276     int fontIndex;
4277     if(style < 1 || style > 5) {
4278 	warning(_("attempt to use invalid font %d replaced by font 1"), style);
4279 	style = 1;
4280     }
4281     fontfamily = findDeviceFont(family, pd->fonts, &fontIndex);
4282     if (fontfamily) {
4283 	result = (fontIndex - 1)*5 + style;
4284     } else {
4285 	warning(_("family '%s' not included in postscript() device"), family);
4286     }
4287     return result;
4288 }
4289 
numFonts(type1fontlist fonts)4290 static int numFonts(type1fontlist fonts) {
4291     int i = 0;
4292     while (fonts) {
4293 	i++;
4294 	fonts = fonts->next;
4295     }
4296     return i;
4297 }
4298 
translateCIDFont(char * family,int style,PostScriptDesc * pd)4299 static int translateCIDFont(char *family, int style, PostScriptDesc *pd)
4300 {
4301     int result = style;
4302     cidfontfamily fontfamily;
4303     int fontIndex;
4304     if(style < 1 || style > 5) {
4305 	warning(_("attempt to use invalid font %d replaced by font 1"), style);
4306 	style = 1;
4307     }
4308     fontfamily = findDeviceCIDFont(family, pd->cidfonts, &fontIndex);
4309     if (fontfamily) {
4310 	/*
4311 	 * CID fonts all listed after all Type 1 fonts.
4312 	 */
4313 	result = (numFonts(pd->fonts)*5) + (fontIndex - 1)*5 + style;
4314     } else {
4315 	warning(_("family '%s' not included in postscript() device"), family);
4316     }
4317     return result;
4318 }
4319 
drawSimpleText(double x,double y,const char * str,double rot,double hadj,int font,const pGEcontext gc,pDevDesc dd)4320 static void drawSimpleText(double x, double y, const char *str,
4321 			   double rot, double hadj,
4322 			   int font,
4323 			   const pGEcontext gc,
4324 			   pDevDesc dd) {
4325     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
4326 
4327     SetFont(font,
4328 	    (int)floor(gc->cex * gc->ps + 0.5),dd);
4329     CheckAlpha(gc->col, pd);
4330     if(R_OPAQUE(gc->col)) {
4331 	SetColor(gc->col, dd);
4332 	if(pd->useKern)
4333 	    PostScriptTextKern(pd->psfp, x, y, str, hadj, rot, gc, dd);
4334 	else
4335 	    PostScriptText(pd->psfp, x, y, str, strlen(str), hadj, rot, gc, dd);
4336     }
4337 }
4338 
4339 /* <FIXME> it would make sense to cache 'cd' here, but we would also
4340    need to know if the current locale's charset changes.  However,
4341    currently this is only called in a UTF-8 locale.
4342  */
mbcsToSbcs(const char * in,char * out,const char * encoding,int enc)4343 static void mbcsToSbcs(const char *in, char *out, const char *encoding,
4344 		       int enc)
4345 {
4346     void *cd = NULL;
4347     const char *i_buf; char *o_buf;
4348     size_t i_len, o_len, status;
4349 
4350 #if 0
4351     if(enc != CE_UTF8 &&
4352        ( !strcmp(encoding, "latin1") || !strcmp(encoding, "ISOLatin1")) ) {
4353 	mbcsToLatin1(in, out); /* more tolerant */
4354 	return;
4355     }
4356 #endif
4357 
4358     if ((void*)-1 ==
4359 	(cd = Riconv_open(encoding, (enc == CE_UTF8) ? "UTF-8" : "")))
4360 	error(_("unknown encoding '%s' in 'mbcsToSbcs'"), encoding);
4361 
4362     i_buf = (char *) in;
4363     i_len = strlen(in)+1; /* include terminator */
4364     o_buf = (char *) out;
4365     o_len = i_len; /* must be the same or fewer chars */
4366 next_char:
4367     status = Riconv(cd, &i_buf, &i_len, &o_buf, &o_len);
4368     /* libiconv 1.13 gives EINVAL on \xe0 in UTF-8 (as used in fBasics) */
4369     if(status == (size_t) -1 && (errno == EILSEQ || errno == EINVAL)) {
4370 	warning(_("conversion failure on '%s' in 'mbcsToSbcs': dot substituted for <%02x>"),
4371 		in, (unsigned char) *i_buf),
4372 	*o_buf++ = '.'; i_buf++; o_len--; i_len--;
4373 	if(i_len > 0) goto next_char;
4374     }
4375 
4376     Riconv_close(cd);
4377     if (status == (size_t)-1)  /* internal error? */
4378 	error("conversion failure from %s to %s on '%s' in 'mbcsToSbcs'",
4379 	      (enc == CE_UTF8) ? "UTF-8" : "native", encoding, in);
4380 }
4381 
PS_Text0(double x,double y,const char * str,int enc,double rot,double hadj,const pGEcontext gc,pDevDesc dd)4382 static void PS_Text0(double x, double y, const char *str, int enc,
4383 		     double rot, double hadj,
4384 		     const pGEcontext gc,
4385 		     pDevDesc dd)
4386 {
4387     const char *str1 = str;
4388     char *buff;
4389 
4390     PostScriptDesc *pd = (PostScriptDesc *) dd->deviceSpecific;
4391 
4392     if (gc->fontface == 5) {
4393 	if (isCIDFont(gc->fontfamily, PostScriptFonts, pd->defaultCIDFont)) {
4394 	    drawSimpleText(x, y, str, rot, hadj,
4395 			   translateCIDFont(gc->fontfamily, gc->fontface, pd),
4396 			   gc, dd);
4397 	    return;
4398 	} else {
4399 	    drawSimpleText(x, y, str, rot, hadj,
4400 			   translateFont(gc->fontfamily, gc->fontface, pd),
4401 			   gc, dd);
4402 	    return;
4403 	}
4404     }
4405 
4406     /* No symbol fonts from now on */
4407 
4408     if (isCIDFont(gc->fontfamily, PostScriptFonts, pd->defaultCIDFont)) {
4409 	/* NB, we could be in a SBCS here */
4410 	size_t ucslen;
4411 	int fontIndex;
4412 
4413 	/*
4414 	 * CID convert optimize PS encoding == locale encode case
4415 	 */
4416 	cidfontfamily cidfont = findDeviceCIDFont(gc->fontfamily,
4417 						  pd->cidfonts,
4418 						  &fontIndex);
4419 	if(!cidfont)
4420 	    error(_("family '%s' not included in postscript() device"),
4421 		  gc->fontfamily);
4422 
4423 	if (!dd->hasTextUTF8 &&
4424 	    !strcmp(locale2charset(NULL), cidfont->encoding)) {
4425 	    SetFont(translateCIDFont(gc->fontfamily, gc->fontface, pd),
4426 		    (int)floor(gc->cex * gc->ps + 0.5),dd);
4427 	    CheckAlpha(gc->col, pd);
4428 	    if(R_OPAQUE(gc->col)) {
4429 		SetColor(gc->col, dd);
4430 		PostScriptHexText(pd->psfp, x, y, str, strlen(str), hadj, rot);
4431 	    }
4432 	    return;
4433 	}
4434 
4435 	/*
4436 	 * CID convert PS encoding != locale encode case
4437 	 */
4438 	ucslen = (dd->hasTextUTF8) ? Rf_utf8towcs(NULL, str, 0) : mbstowcs(NULL, str, 0);
4439 	if (ucslen != (size_t)-1) {
4440 	    void *cd;
4441 	    const char  *i_buf; char *o_buf;
4442 	    size_t nb, i_len,  o_len, buflen = ucslen * sizeof(R_ucs2_t);
4443 	    size_t status;
4444 
4445 	    cd = (void*) Riconv_open(cidfont->encoding,
4446 				     (enc == CE_UTF8) ? "UTF-8" : "");
4447 	    if(cd == (void*)-1) {
4448 		warning(_("failed open converter to encoding '%s'"),
4449 			cidfont->encoding);
4450 		return;
4451 	    }
4452 
4453 	    R_CheckStack2(buflen);
4454 	    unsigned char buf[buflen];
4455 
4456 	    i_buf = (char *)str;
4457 	    o_buf = (char *)buf;
4458 	    i_len = strlen(str); /* do not include terminator */
4459 	    nb = o_len = buflen;
4460 
4461 	    status = Riconv(cd, &i_buf, (size_t *)&i_len,
4462 			    (char **)&o_buf, (size_t *)&o_len);
4463 
4464 	    Riconv_close(cd);
4465 	    if(status == (size_t)-1)
4466 		warning(_("failed in text conversion to encoding '%s'"),
4467 			cidfont->encoding);
4468 	    else {
4469 		SetFont(translateCIDFont(gc->fontfamily, gc->fontface, pd),
4470 			(int)floor(gc->cex * gc->ps + 0.5), dd);
4471 		CheckAlpha(gc->col, pd);
4472 		if(R_OPAQUE(gc->col)) {
4473 		    SetColor(gc->col, dd);
4474 		    PostScriptHexText(pd->psfp, x, y, (char *)buf,
4475 				      nb - o_len, hadj, rot);
4476 		}
4477 	    }
4478 	    return;
4479 	} else {
4480 	    warning(_("invalid string in '%s'"), "PS_Text");
4481 	    return;
4482 	}
4483     }
4484 
4485     /* Now using single-byte non-symbol font.
4486 
4487        Was utf8locale, but it is not entirely obvious that only UTF-8
4488        needs re-encoding, although we don't have any other MBCSs that
4489        can sensibly be mapped to a SBCS.
4490        It would be perverse (but possible) to write English in a
4491        CJK MBCS.
4492     */
4493     if((enc == CE_UTF8 || mbcslocale) && !strIsASCII(str)) {
4494 	R_CheckStack2(strlen(str)+1);
4495 	buff = alloca(strlen(str)+1); /* Output string cannot be longer */
4496 	mbcsToSbcs(str, buff, convname(gc->fontfamily, pd), enc);
4497 	str1 = buff;
4498     }
4499     drawSimpleText(x, y, str1, rot, hadj,
4500 		   translateFont(gc->fontfamily, gc->fontface, pd),
4501 		   gc, dd);
4502 }
4503 
PS_Text(double x,double y,const char * str,double rot,double hadj,const pGEcontext gc,pDevDesc dd)4504 static void PS_Text(double x, double y, const char *str,
4505 		    double rot, double hadj,
4506 		    const pGEcontext gc,
4507 		    pDevDesc dd)
4508 {
4509     PS_Text0(x, y, str, CE_NATIVE, rot, hadj, gc, dd);
4510 }
4511 
PS_TextUTF8(double x,double y,const char * str,double rot,double hadj,const pGEcontext gc,pDevDesc dd)4512 static void PS_TextUTF8(double x, double y, const char *str,
4513 			double rot, double hadj,
4514 			const pGEcontext gc,
4515 			pDevDesc dd)
4516 {
4517     PS_Text0(x, y, str, CE_UTF8, rot, hadj, gc, dd);
4518 }
4519 
PS_setPattern(SEXP pattern,pDevDesc dd)4520 static SEXP PS_setPattern(SEXP pattern, pDevDesc dd) {
4521     return R_NilValue;
4522 }
4523 
PS_releasePattern(SEXP ref,pDevDesc dd)4524 static void PS_releasePattern(SEXP ref, pDevDesc dd) {}
4525 
PS_setClipPath(SEXP path,SEXP ref,pDevDesc dd)4526 static SEXP PS_setClipPath(SEXP path, SEXP ref, pDevDesc dd) {
4527     return R_NilValue;
4528 }
4529 
PS_releaseClipPath(SEXP ref,pDevDesc dd)4530 static void PS_releaseClipPath(SEXP ref, pDevDesc dd) {}
4531 
PS_setMask(SEXP path,SEXP ref,pDevDesc dd)4532 static SEXP PS_setMask(SEXP path, SEXP ref, pDevDesc dd) {
4533     return R_NilValue;
4534 }
4535 
PS_releaseMask(SEXP ref,pDevDesc dd)4536 static void PS_releaseMask(SEXP ref, pDevDesc dd) {}
4537 
4538 
4539 
4540 /***********************************************************************
4541 
4542 		 XFig driver shares font handling
4543 
4544 ************************************************************************/
4545 
4546 
4547 
4548 typedef struct {
4549     char filename[PATH_MAX];
4550 
4551     char papername[64];	 /* paper name */
4552     int paperwidth;	 /* paper width in big points (1/72 in) */
4553     int paperheight;	 /* paper height in big points */
4554     Rboolean landscape;	 /* landscape mode */
4555     int pageno;		 /* page number */
4556 
4557     int fontnum;	 /* font number in XFig */
4558     int maxpointsize;
4559 
4560     double width;	 /* plot width in inches */
4561     double height;	 /* plot height in inches */
4562     double pagewidth;	 /* page width in inches */
4563     double pageheight;	 /* page height in inches */
4564     Rboolean pagecentre;      /* centre image on page? */
4565 
4566     double lwd;		 /* current line width */
4567     int lty;		 /* current line type */
4568     rcolor col;		 /* current color */
4569     rcolor fill;	 /* current fill color */
4570     rcolor bg;		 /* background color */
4571     int XFigColors[534];
4572     int nXFigColors;
4573 
4574     FILE *psfp;		 /* output file */
4575     FILE *tmpfp;         /* temp file */
4576     char tmpname[PATH_MAX];
4577 
4578     Rboolean onefile;
4579     Rboolean warn_trans; /* have we warned about translucent cols? */
4580     int ymax;            /* used to invert coord system */
4581     char encoding[50];   /* for writing text */
4582 
4583     Rboolean textspecial; /* use textspecial flag in xfig for latex integration */
4584     Rboolean defaultfont; /* use the default font in xfig */
4585 
4586     /*
4587      * Fonts and encodings used on the device
4588      *
4589      * ASSUME ONLY ONE (DEFAULT) FOR NOW
4590      */
4591     type1fontlist fonts;
4592     encodinglist encodings;
4593 } XFigDesc;
4594 
4595 static void
XF_FileHeader(FILE * fp,const char * papername,Rboolean landscape,Rboolean onefile)4596 XF_FileHeader(FILE *fp, const char *papername, Rboolean landscape,
4597 	      Rboolean onefile)
4598 {
4599     fprintf(fp, "#FIG 3.2\n");
4600     fprintf(fp, landscape ? "Landscape\n" : "Portrait\n");
4601     fprintf(fp, "Flush Left\nInches\n");
4602     /* Fix */fprintf(fp, "%s\n", papername);
4603     fprintf(fp, "100.0\n");
4604     fprintf(fp, onefile ? "Multiple\n" : "Single\n");
4605     fprintf(fp, "-2\n"); /* no background */
4606     fprintf(fp, "1200 2\n"); /* coordinate system */
4607     fprintf(fp, "# End of XFig header\n");
4608 }
4609 
XF_FileTrailer(FILE * fp)4610 static void XF_FileTrailer(FILE *fp)
4611 {
4612     fprintf(fp, "# end of XFig file\n");
4613 }
4614 
4615 
XF_EndPage(FILE * fp)4616 static void XF_EndPage(FILE *fp)
4617 {
4618     fprintf(fp, "# end of XFig page\n");
4619 }
4620 
XF_WriteString(FILE * fp,const char * str)4621 static void XF_WriteString(FILE *fp, const char *str)
4622 {
4623     unsigned int c;
4624     for ( ; *str; str++) {
4625 	c = (unsigned char)*str;
4626 	if (c > 127) {
4627 	    fprintf(fp, "\\%o", c);
4628 	} else {
4629 	    switch(*str) {
4630 	    case '\n':
4631 		fprintf(fp, "\\n");
4632 		break;
4633 	    case '\\':
4634 		fprintf(fp, "\\\\");
4635 		break;
4636 	    default:
4637 		fputc(*str, fp);
4638 		break;
4639 	    }
4640 	}
4641     }
4642 }
4643 
XF_CheckAlpha(int color,XFigDesc * pd)4644 static void XF_CheckAlpha(int color, XFigDesc *pd)
4645 {
4646     unsigned int alpha = R_ALPHA(color);
4647     if (alpha > 0 && alpha < 255 && !pd->warn_trans) {
4648 	warning(_("semi-transparency is not supported on this device: reported only once per page"));
4649 	pd->warn_trans = TRUE;
4650     }
4651 }
4652 
4653 
XF_SetColor(int color,XFigDesc * pd)4654 static int XF_SetColor(int color, XFigDesc *pd)
4655 {
4656     int i;
4657     if(!R_OPAQUE(color))  return -1;
4658     color = color & 0xffffff;
4659     for (i = 0; i < pd->nXFigColors; i++)
4660 	if(color == pd->XFigColors[i]) return i;
4661     if(pd->nXFigColors == 534)
4662 	error(_("ran out of colors in xfig()"));
4663     /* new colour */
4664     fprintf(pd->psfp, "0 %d #%02x%02x%02x\n", pd->nXFigColors,
4665 	    R_RED(color), R_GREEN(color), R_BLUE(color));
4666     pd->XFigColors[pd->nXFigColors] = color;
4667     return pd->nXFigColors++;
4668 }
4669 
XFconvert(double * x,double * y,XFigDesc * pd)4670 static void XFconvert(double *x, double *y, XFigDesc *pd)
4671 {
4672     (*x) *= 16.667;
4673     (*y) = pd->ymax - 16.667*(*y);
4674 }
4675 
4676 
XF_SetLty(int lty)4677 static int XF_SetLty(int lty)
4678 {
4679     switch(lty) {
4680     case LTY_BLANK:
4681 	return -1;
4682     case LTY_SOLID:
4683 	return 0;
4684     case LTY_DASHED:
4685 	return 1;
4686     case LTY_DOTTED:
4687 	return 2;
4688     case LTY_DOTDASH:
4689 	return 3;
4690     default:
4691 	warning(_("unimplemented line texture %08x: using Dash-double-dotted"),
4692 		lty);
4693 	return 4;
4694     }
4695 }
4696 
4697 /* Device Driver Actions */
4698 
4699 static void XFig_Circle(double x, double y, double r,
4700 			const pGEcontext gc,
4701 			pDevDesc dd);
4702 static void XFig_Clip(double x0, double x1, double y0, double y1,
4703 		     pDevDesc dd);
4704 static void XFig_Close(pDevDesc dd);
4705 static void XFig_Line(double x1, double y1, double x2, double y2,
4706 		      const pGEcontext gc,
4707 		      pDevDesc dd);
4708 static void XFig_MetricInfo(int c,
4709 			    const pGEcontext gc,
4710 			    double* ascent, double* descent,
4711 			    double* width, pDevDesc dd);
4712 static void XFig_NewPage(const pGEcontext gc, pDevDesc dd);
4713 static void XFig_Polygon(int n, double *x, double *y,
4714 			 const pGEcontext gc,
4715 			 pDevDesc dd);
4716 static void XFig_Polyline(int n, double *x, double *y,
4717 			  const pGEcontext gc,
4718 			  pDevDesc dd);
4719 static void XFig_Rect(double x0, double y0, double x1, double y1,
4720 		      const pGEcontext gc,
4721 		      pDevDesc dd);
4722 static void XFig_Size(double *left, double *right,
4723 		     double *bottom, double *top,
4724 		     pDevDesc dd);
4725 static double XFig_StrWidth(const char *str,
4726 			    const pGEcontext gc,
4727 			    pDevDesc dd);
4728 static void XFig_Text(double x, double y, const char *str,
4729 		      double rot, double hadj,
4730 		      const pGEcontext gc,
4731 		      pDevDesc dd);
4732 static SEXP     XFig_setPattern(SEXP pattern, pDevDesc dd);
4733 static void     XFig_releasePattern(SEXP ref, pDevDesc dd);
4734 static SEXP     XFig_setClipPath(SEXP path, SEXP ref, pDevDesc dd);
4735 static void     XFig_releaseClipPath(SEXP ref, pDevDesc dd);
4736 static SEXP     XFig_setMask(SEXP path, SEXP ref, pDevDesc dd);
4737 static void     XFig_releaseMask(SEXP ref, pDevDesc dd);
4738 static Rboolean XFig_Open(pDevDesc, XFigDesc*);
4739 
4740 /*
4741  * Values taken from FIG format definition
4742  */
XFigBaseNum(const char * name)4743 static int XFigBaseNum(const char *name)
4744 {
4745     int i;
4746     if (!strcmp(name, "Times"))
4747 	i = 0;
4748     else if (!strcmp(name, "AvantGarde"))
4749 	i = 4;
4750     else if (!strcmp(name, "Bookman"))
4751 	i = 8;
4752     else if (!strcmp(name, "Courier"))
4753 	i = 12;
4754     else if (!strcmp(name, "Helvetica"))
4755 	i = 16;
4756     else if (!strcmp(name, "Helvetica-Narrow"))
4757 	i = 20;
4758     else if (!strcmp(name, "NewCenturySchoolbook"))
4759 	i = 24;
4760     else if (!strcmp(name, "Palatino"))
4761 	i = 28;
4762     else {
4763 	warning(_("unknown postscript font family '%s', using Helvetica"),
4764 		name);
4765 	i = 16;
4766     }
4767     return i;
4768 }
4769 
XF_resetColors(XFigDesc * pd)4770 static void XF_resetColors(XFigDesc *pd)
4771 {
4772     int i;
4773     for(i = 0; i < 32; i++) pd->XFigColors[i] = 0;
4774     pd->XFigColors[7] = 0xffffff; /* white */
4775     pd->nXFigColors = 32;
4776 }
4777 
4778 /* Driver Support Routines */
4779 
4780 static Rboolean
XFigDeviceDriver(pDevDesc dd,const char * file,const char * paper,const char * family,const char * bg,const char * fg,double width,double height,Rboolean horizontal,double ps,Rboolean onefile,Rboolean pagecentre,Rboolean defaultfont,Rboolean textspecial,const char * encoding)4781 XFigDeviceDriver(pDevDesc dd, const char *file, const char *paper,
4782 		 const char *family,
4783 		 const char *bg, const char *fg,
4784 		 double width, double height,
4785 		 Rboolean horizontal, double ps,
4786 		 Rboolean onefile, Rboolean pagecentre,
4787 		 Rboolean defaultfont, Rboolean textspecial,
4788 		 const char *encoding)
4789 {
4790     /* If we need to bail out with some sort of "error" */
4791     /* then we must free(dd) */
4792 
4793     int gotFont;
4794     double xoff, yoff, pointsize;
4795     XFigDesc *pd;
4796     type1fontfamily font;
4797     encodinginfo enc;
4798     encodinglist enclist;
4799 
4800     /* Check and extract the device parameters */
4801 
4802     if(strlen(file) > PATH_MAX - 1) {
4803 	free(dd);
4804 	error(_("filename too long in %s()"), "xfig");
4805     }
4806 
4807     /* allocate new xfig device description */
4808     if (!(pd = (XFigDesc *) malloc(sizeof(XFigDesc)))) {
4809 	free(dd);
4810 	error(_("memory allocation problem in %s()"), "xfig");
4811 	return FALSE;
4812     }
4813 
4814     /* from here on, if need to bail out with "error", must also */
4815     /* free(pd) */
4816 
4817     /* initialize xfig device description */
4818     strcpy(pd->filename, file);
4819     strcpy(pd->papername, paper);
4820     pd->fontnum = XFigBaseNum(family);
4821     /* this might have changed the family, so update */
4822     if(pd->fontnum == 16) family = "Helvetica";
4823     pd->bg = R_GE_str2col(bg);
4824     pd->col = R_GE_str2col(fg);
4825     pd->fill = R_TRANWHITE;
4826     pd->width = width;
4827     pd->height = height;
4828     pd->landscape = horizontal;
4829     pd->textspecial = textspecial;
4830     pd->defaultfont = defaultfont;
4831     pointsize = floor(ps);
4832     if(R_TRANSPARENT(pd->bg) && R_TRANSPARENT(pd->col)) {
4833 	free(dd);
4834 	free(pd);
4835 	error(_("invalid foreground/background color (xfig)"));
4836     }
4837     pd->warn_trans = FALSE;
4838 
4839     /*
4840      * Load the default encoding AS THE FIRST ENCODING FOR THIS DEVICE.
4841      */
4842     pd->encodings = NULL;
4843     if (!(enc = findEncoding("ISOLatin1.enc", pd->encodings, FALSE)))
4844 	enc = addEncoding("ISOLatin1.enc", 0);
4845     if (enc && (enclist = addDeviceEncoding(enc, pd->encodings))) {
4846 	pd->encodings = enclist;
4847     } else {
4848 	free(dd);
4849 	free(pd);
4850 	error(_("failed to load encoding file in %s()"), "xfig");
4851     }
4852 
4853     /* Load default font */
4854     pd->fonts = NULL;
4855 
4856     gotFont = 0;
4857     font = findLoadedFont(family, "ISOLatin1.enc", FALSE);
4858     if (!font) {
4859 	/*
4860 	 * If the font has not been loaded yet, load it.
4861 	 *
4862 	 * The family SHOULD be in the font database to get this far.
4863 	 * (checked at R level in postscript() in postscript.R)
4864 	 */
4865 	if (isType1Font(family, PostScriptFonts, NULL)) {
4866 	    font = addFont(family, FALSE, pd->encodings);
4867 	} else {
4868 	    error(_("only Type 1 fonts supported for XFig"));
4869 	}
4870     }
4871     if (font) {
4872 	/*
4873 	 * At this point the font is loaded, so add it to the
4874 	 * device's list of fonts.
4875 	 */
4876 	pd->fonts = addDeviceFont(font, pd->fonts, &gotFont);
4877     }
4878     if (!gotFont) {
4879 	free(dd);
4880 	free(pd);
4881 	error(_("failed to initialise default XFig font"));
4882     }
4883 
4884     /* Deal with paper and plot size and orientation */
4885 
4886     if(!strcmp(pd->papername, "Default") ||
4887        !strcmp(pd->papername, "default")) {
4888 	SEXP s = STRING_ELT(GetOption1(install("papersize")), 0);
4889 	if(s != NA_STRING && strlen(CHAR(s)) > 0)
4890 	    strcpy(pd->papername, CHAR(s));
4891 	else strcpy(pd->papername, "A4");
4892     }
4893     if(!strcmp(pd->papername, "A4") ||
4894        !strcmp(pd->papername, "a4")) {
4895 	strcpy(pd->papername, "A4");
4896 	pd->pagewidth  = 21.0 / 2.54;
4897 	pd->pageheight = 29.7 / 2.54;
4898     }
4899     else if(!strcmp(pd->papername, "Letter") ||
4900 	    !strcmp(pd->papername, "letter")) {
4901 	strcpy(pd->papername, "Letter");
4902 	pd->pagewidth  =  8.5;
4903 	pd->pageheight = 11.0;
4904     }
4905     else if(!strcmp(pd->papername, "Legal") ||
4906 	    !strcmp(pd->papername, "legal")) {
4907 	strcpy(pd->papername, "Legal");
4908 	pd->pagewidth  =  8.5;
4909 	pd->pageheight = 14.0;
4910     }
4911     else {
4912 	freeDeviceFontList(pd->fonts);
4913 	freeDeviceEncList(pd->encodings);
4914 	pd->fonts = NULL;
4915 	pd->encodings = NULL;
4916 	free(dd);
4917 	free(pd);
4918 	error(_("invalid page type '%s' (xfig)"), pd->papername);
4919     }
4920     pd->pagecentre = pagecentre;
4921     pd->paperwidth = (int)(72 * pd->pagewidth);
4922     pd->paperheight = (int)(72 * pd->pageheight);
4923     if(!onefile) {
4924 	char *p = strrchr(pd->filename, '%');
4925 	if(!p)
4926 	    warning(_("xfig(%s, onefile=FALSE) will only return the last plot"), pd->filename);
4927     }
4928     if(pd->landscape) {
4929 	double tmp;
4930 	tmp = pd->pagewidth;
4931 	pd->pagewidth = pd->pageheight;
4932 	pd->pageheight = tmp;
4933     }
4934     if(pd->width < 0.1 || pd->width > pd->pagewidth-0.5)
4935 	pd->width = pd->pagewidth-0.5;
4936     if(pd->height < 0.1 || pd->height > pd->pageheight-0.5)
4937 	pd->height = pd->pageheight-0.5;
4938     if(pagecentre) {
4939 	xoff = (pd->pagewidth - pd->width)/2.0;
4940 	yoff = (pd->pageheight - pd->height)/2.0;
4941     } else {
4942 	xoff = yoff = 0.0;
4943     }
4944     if(pagecentre)
4945 	pd->ymax = (int)(1200.0 * pd->pageheight);
4946     else
4947 	pd->ymax = (int)(1200.0 * pd->height);
4948     pd->onefile = onefile;
4949     pd->maxpointsize = (int)(72.0 * ((pd->pageheight > pd->pagewidth) ?
4950 				     pd->pageheight : pd->pagewidth));
4951     pd->pageno = 0;
4952     /* Base Pointsize */
4953     /* Nominal Character Sizes in Pixels */
4954     /* Only right for 12 point font. */
4955     /* Max pointsize suggested by Peter Dalgaard */
4956 
4957     if(pointsize < 6.0) pointsize = 6.0;
4958     if(pointsize > pd->maxpointsize) pointsize = pd->maxpointsize;
4959     dd->startps = pointsize;
4960     dd->startlty = LTY_SOLID;
4961     dd->startfont = 1;
4962     dd->startfill = pd->bg;
4963     dd->startcol = pd->col;
4964     dd->startgamma = 1;
4965 
4966     /* Set graphics parameters that must be set by device driver. */
4967     /* Page dimensions in points. */
4968 
4969     dd->left = 72 * xoff;			/* left */
4970     dd->right = 72 * (xoff + pd->width);	/* right */
4971     dd->bottom = 72 * yoff;		/* bottom */
4972     dd->top = 72 * (yoff + pd->height);	/* top */
4973     dd->clipLeft = dd->left; dd->clipRight = dd->right;
4974     dd->clipBottom = dd->bottom; dd->clipTop = dd->top;
4975 
4976     dd->cra[0] = 0.9 * pointsize;
4977     dd->cra[1] = 1.2 * pointsize;
4978 
4979     /* Character Addressing Offsets */
4980     /* These offsets should center a single */
4981     /* plotting character over the plotting point. */
4982     /* Pure guesswork and eyeballing ... */
4983 
4984     dd->xCharOffset =  0.4900;
4985     dd->yCharOffset =  0.3333;
4986     dd->yLineBias = 0.2;
4987 
4988     /* Inches per Raster Unit */
4989     /* 1200 dpi */
4990     dd->ipr[0] = 1.0/72.0;
4991     dd->ipr[1] = 1.0/72.0;
4992 
4993     dd->canClip = FALSE;
4994     dd->canHAdj = 1; /* 0, 0.5, 1 */
4995     dd->canChangeGamma = FALSE;
4996     strncpy(pd->encoding, encoding, 49);
4997     pd->encoding[49] = '\0';
4998 
4999     XF_resetColors(pd);
5000 
5001     /*	Start the driver */
5002 
5003     XFig_Open(dd, pd);
5004 
5005     dd->close      = XFig_Close;
5006     dd->size       = XFig_Size;
5007     dd->newPage    = XFig_NewPage;
5008     dd->clip	   = XFig_Clip;
5009     dd->text	   = XFig_Text;
5010     dd->strWidth   = XFig_StrWidth;
5011     dd->metricInfo = XFig_MetricInfo;
5012     dd->rect	   = XFig_Rect;
5013     /* dd->path       = XFig_Path;
5014        dd->raster     = XFig_Raster;
5015        dd->cap        = XFig_Cap; */
5016     dd->circle     = XFig_Circle;
5017     dd->line	   = XFig_Line;
5018     dd->polygon    = XFig_Polygon;
5019     dd->polyline   = XFig_Polyline;
5020     /* dd->locator    = XFig_Locator;
5021        dd->mode	   = XFig_Mode; */
5022     dd->hasTextUTF8 = FALSE;
5023     dd->useRotatedTextInContour = FALSE; /* maybe */
5024     dd->haveTransparency = 1;
5025     dd->haveTransparentBg = 1;
5026     dd->haveRaster = 1;
5027     dd->haveCapture = 1;
5028     dd->haveLocator = 1;
5029     dd->setPattern      = XFig_setPattern;
5030     dd->releasePattern  = XFig_releasePattern;
5031     dd->setClipPath     = XFig_setClipPath;
5032     dd->releaseClipPath = XFig_releaseClipPath;
5033     dd->setMask         = XFig_setMask;
5034     dd->releaseMask     = XFig_releaseMask;
5035 
5036     dd->deviceSpecific = (void *) pd;
5037     dd->displayListOn = FALSE;
5038     dd->deviceVersion = R_GE_definitions;
5039     return 1;
5040 }
5041 
XFig_cleanup(pDevDesc dd,XFigDesc * pd)5042 static void XFig_cleanup(pDevDesc dd, XFigDesc *pd)
5043 {
5044     freeDeviceFontList(pd->fonts);
5045     freeDeviceEncList(pd->encodings);
5046     pd->fonts = NULL;
5047     pd->encodings = NULL;
5048     free(dd);
5049     free(pd);
5050 }
5051 
5052 
XFig_Open(pDevDesc dd,XFigDesc * pd)5053 static Rboolean XFig_Open(pDevDesc dd, XFigDesc *pd)
5054 {
5055     char buf[512], *tmp;
5056 
5057     if (strlen(pd->filename) == 0) {
5058 	XFig_cleanup(dd, pd);
5059 	error(_("empty file name"));
5060 	return FALSE;
5061     } else {
5062 	snprintf(buf, 512, pd->filename, pd->pageno + 1); /* page 1 to start */
5063 	pd->psfp = R_fopen(R_ExpandFileName(buf), "w");
5064     }
5065     if (!pd->psfp) {
5066 	XFig_cleanup(dd, pd);
5067 	error(_("cannot open file '%s'"), buf);
5068 	return FALSE;
5069     }
5070     /* assume tmpname is less than PATH_MAX */
5071     tmp = R_tmpnam("Rxfig", R_TempDir);
5072     strcpy(pd->tmpname, tmp);
5073     free(tmp);
5074     pd->tmpfp = R_fopen(pd->tmpname, "w");
5075     if (!pd->tmpfp) {
5076 	fclose(pd->psfp);
5077 	char errbuf[strlen(pd->tmpname) + 1];
5078 	strcpy(errbuf, pd->tmpname);
5079 	XFig_cleanup(dd, pd);
5080 	error(_("cannot open file '%s'"), errbuf);
5081 	return FALSE;
5082     }
5083     XF_FileHeader(pd->psfp, pd->papername, pd->landscape, pd->onefile);
5084     pd->pageno = 0;
5085     return TRUE;
5086 }
5087 
5088 
XFig_Clip(double x0,double x1,double y0,double y1,pDevDesc dd)5089 static void XFig_Clip(double x0, double x1, double y0, double y1,
5090 		      pDevDesc dd)
5091 {
5092 }
5093 
XFig_Size(double * left,double * right,double * bottom,double * top,pDevDesc dd)5094 static void XFig_Size(double *left, double *right,
5095 		      double *bottom, double *top,
5096 		      pDevDesc dd)
5097 {
5098     *left = dd->left;
5099     *right = dd->right;
5100     *bottom = dd->bottom;
5101     *top = dd->top;
5102 }
5103 
5104 #define CHUNK 10000
XFig_NewPage(const pGEcontext gc,pDevDesc dd)5105 static void XFig_NewPage(const pGEcontext gc,
5106 			 pDevDesc dd)
5107 {
5108     char buf[PATH_MAX];
5109     XFigDesc *pd = (XFigDesc *) dd->deviceSpecific;
5110 
5111     pd->pageno++;
5112     if(pd->onefile) {
5113 	fprintf(pd->tmpfp, "#Start of page %d\n", pd->pageno);
5114 	if(pd->pageno > 1) XF_EndPage(pd->tmpfp);
5115     } else {
5116 	char buffer[CHUNK];
5117 	size_t nread, res;
5118 	if(pd->pageno == 1) return;
5119 	XF_FileTrailer(pd->tmpfp);
5120 	fclose(pd->tmpfp);
5121 	pd->tmpfp = R_fopen(pd->tmpname, "r");
5122 	while(1) {
5123 	    nread = fread(buffer, 1, CHUNK, pd->tmpfp);
5124 	    if(nread > 0) {
5125 		res = fwrite(buffer, 1, nread, pd->psfp);
5126 		if(res != nread) error(_("write failed"));
5127 	    }
5128 	    if(nread < CHUNK) break;
5129 	}
5130 	fclose(pd->tmpfp);
5131 	fclose(pd->psfp);
5132 	snprintf(buf, PATH_MAX, pd->filename, pd->pageno);
5133 	pd->psfp = R_fopen(R_ExpandFileName(buf), "w");
5134 	pd->tmpfp = R_fopen(pd->tmpname, "w");
5135 	XF_FileHeader(pd->psfp, pd->papername, pd->landscape, pd->onefile);
5136 	XF_resetColors(pd);
5137     }
5138     XF_CheckAlpha(gc->fill, pd);
5139     if(R_OPAQUE(gc->fill)) {
5140 	FILE *fp = pd->tmpfp;
5141 	int cbg = XF_SetColor(gc->fill, pd);
5142 	int ix0, iy0, ix1, iy1;
5143 	double x0 = 0.0, y0 = 0.0, x1 = 72.0 * pd->pagewidth,
5144 	    y1 = 72.0 * pd->pageheight;
5145 	XFconvert(&x0, &y0, pd); XFconvert(&x1, &y1, pd);
5146 	ix0 = (int)x0; iy0 = (int)y0; ix1 = (int)x1; iy1 = (int)y1;
5147 	fprintf(fp, "2 2 "); /* Polyline */
5148 	fprintf(fp, "%d %d ", 0, 0); /* style, thickness */
5149 	fprintf(fp, "%d %d ", cbg, cbg); /* pen colour fill colour */
5150 	fprintf(fp, "200 0 20 4.0 0 0 -1 0 0 ");
5151 	fprintf(fp, "%d\n", 5); /* number of points */
5152 	fprintf(fp, "%d %d ", ix0, iy0);
5153 	fprintf(fp, "%d %d ", ix0, iy1);
5154 	fprintf(fp, "%d %d ", ix1, iy1);
5155 	fprintf(fp, "%d %d ", ix1, iy0);
5156 	fprintf(fp, "%d %d\n", ix0, iy0);
5157     }
5158     pd->warn_trans = FALSE;
5159 }
5160 
5161 #ifdef HAVE_UNISTD_H
5162 #include <unistd.h>
5163 #endif
5164 
XFig_Close(pDevDesc dd)5165 static void XFig_Close(pDevDesc dd)
5166 {
5167     char buf[CHUNK];
5168     size_t nread, res;
5169     XFigDesc *pd = (XFigDesc *) dd->deviceSpecific;
5170 
5171     XF_FileTrailer(pd->tmpfp);
5172     fclose(pd->tmpfp);
5173     pd->tmpfp = R_fopen(pd->tmpname, "r");
5174     while(1) {
5175 	nread = fread(buf, 1, CHUNK, pd->tmpfp);
5176 	if(nread > 0) {
5177 	    res = fwrite(buf, 1, nread, pd->psfp);
5178 	    if(res != nread) error(_("write failed"));
5179 	}
5180 	if(nread < CHUNK) break;
5181     }
5182     fclose(pd->tmpfp);
5183     unlink(pd->tmpname);
5184     fclose(pd->psfp);
5185     free(pd);
5186 }
5187 
XFig_Rect(double x0,double y0,double x1,double y1,const pGEcontext gc,pDevDesc dd)5188 static void XFig_Rect(double x0, double y0, double x1, double y1,
5189 		      const pGEcontext gc,
5190 		      pDevDesc dd)
5191 {
5192     XFigDesc *pd = (XFigDesc *) dd->deviceSpecific;
5193     FILE *fp = pd->tmpfp;
5194     int ix0, iy0, ix1, iy1;
5195     int cbg = XF_SetColor(gc->fill, pd), cfg = XF_SetColor(gc->col, pd), cpen,
5196 	dofill, lty = XF_SetLty(gc->lty), lwd = (int)(gc->lwd*0.833 + 0.5);
5197 
5198     if(lty < 0) return;
5199 
5200     XF_CheckAlpha(gc->col, pd);
5201     XF_CheckAlpha(gc->fill, pd);
5202     cpen = (R_OPAQUE(gc->col))? cfg: -1;
5203     dofill = (R_OPAQUE(gc->fill))? 20: -1;
5204 
5205     XFconvert(&x0, &y0, pd);
5206     XFconvert(&x1, &y1, pd);
5207     ix0 = (int)x0; iy0 = (int)y0; ix1 = (int)x1; iy1 = (int)y1;
5208     fprintf(fp, "2 2 "); /* Polyline */
5209     fprintf(fp, "%d %d ", lty, lwd>0?lwd:1); /* style, thickness */
5210     fprintf(fp, "%d %d ", cpen, cbg); /* pen colour fill colour */
5211     fprintf(fp, "100 0 %d ", dofill); /* depth, pen style, area fill */
5212     fprintf(fp, "%.2f 0 0 -1 0 0 ", 4.0*lwd); /* style value, join .... */
5213     fprintf(fp, "%d\n", 5); /* number of points */
5214     fprintf(fp, "  %d %d ", ix0, iy0);
5215     fprintf(fp, "  %d %d ", ix0, iy1);
5216     fprintf(fp, "  %d %d ", ix1, iy1);
5217     fprintf(fp, "  %d %d ", ix1, iy0);
5218     fprintf(fp, "  %d %d\n", ix0, iy0);
5219 }
5220 
XFig_Circle(double x,double y,double r,const pGEcontext gc,pDevDesc dd)5221 static void XFig_Circle(double x, double y, double r,
5222 			const pGEcontext gc,
5223 			pDevDesc dd)
5224 {
5225     XFigDesc *pd = (XFigDesc *) dd->deviceSpecific;
5226     FILE *fp = pd->tmpfp;
5227     int ix, iy, ir;
5228     int cbg = XF_SetColor(gc->fill, pd), cfg = XF_SetColor(gc->col, pd), cpen,
5229 	dofill, lty = XF_SetLty(gc->lty), lwd = (int)(gc->lwd*0.833 + 0.5);
5230 
5231     if(lty < 0) return;
5232 
5233     XF_CheckAlpha(gc->col, pd);
5234     XF_CheckAlpha(gc->fill, pd);
5235     cpen = (R_OPAQUE(gc->col))? cfg: -1;
5236     dofill = (R_OPAQUE(gc->fill))? 20: -1;
5237 
5238     XFconvert(&x, &y, pd);
5239     ix = (int)x; iy = (int)y; ir = (int)(16.667*r);
5240 
5241     fprintf(fp, "1 3 "); /* Circle + radius */
5242     fprintf(fp, "%d %d ", lty, lwd>0?lwd:1); /* style, thickness */
5243     fprintf(fp, "%d %d ", cpen, cbg); /* pen colour fill colour */
5244     fprintf(fp, "100 0 %d ", dofill); /* depth, pen style, area fill */
5245     fprintf(fp, "%.2f 1 0 ", 4.0*lwd); /* style value, direction, x, angle */
5246     fprintf(fp, "  %d %d %d %d %d %d %d %d \n",
5247 	    ix, iy, ir, ir, ix, iy, ix+ir, iy);
5248 }
5249 
XFig_Line(double x1,double y1,double x2,double y2,const pGEcontext gc,pDevDesc dd)5250 static void XFig_Line(double x1, double y1, double x2, double y2,
5251 		      const pGEcontext gc,
5252 		      pDevDesc dd)
5253 {
5254     XFigDesc *pd = (XFigDesc *) dd->deviceSpecific;
5255     FILE *fp = pd->tmpfp;
5256     int lty = XF_SetLty(gc->lty), lwd = (int)(gc->lwd*0.833 + 0.5);
5257 
5258     if(lty < 0) return;
5259 
5260     XFconvert(&x1, &y1, pd);
5261     XFconvert(&x2, &y2, pd);
5262     XF_CheckAlpha(gc->col, pd);
5263     if(R_OPAQUE(gc->col)) {
5264 	fprintf(fp, "2 1 "); /* Polyline */
5265 	fprintf(fp, "%d %d ", lty, lwd>0?lwd:1); /* style, thickness */
5266 	fprintf(fp, "%d %d ", XF_SetColor(gc->col, pd), 7);
5267 	/* pen colour fill colour */
5268 	fprintf(fp, "100 0 -1 "); /* depth, pen style, area fill */
5269 	fprintf(fp, "%.2f 0 0 -1 0 0 ", 4.0*lwd); /* style value, join .... */
5270 	fprintf(fp, "%d\n", 2); /* number of points */
5271 	fprintf(fp, "%d %d %d %d\n", (int)x1, (int)y1, (int)x2, (int)y2);
5272     }
5273 }
5274 
XFig_Polygon(int n,double * x,double * y,const pGEcontext gc,pDevDesc dd)5275 static void XFig_Polygon(int n, double *x, double *y,
5276 			 const pGEcontext gc,
5277 			 pDevDesc dd)
5278 {
5279     XFigDesc *pd = (XFigDesc *) dd->deviceSpecific;
5280     FILE *fp = pd->tmpfp;
5281     double xx, yy;
5282     int i;
5283     int cbg = XF_SetColor(gc->fill, pd), cfg = XF_SetColor(gc->col, pd), cpen,
5284 	dofill, lty = XF_SetLty(gc->lty), lwd = (int)(gc->lwd*0.833 + 0.5);
5285 
5286     if(lty < 0) return;
5287 
5288     XF_CheckAlpha(gc->col, pd);
5289     XF_CheckAlpha(gc->fill, pd);
5290     cpen = (R_OPAQUE(gc->col))? cfg: -1;
5291     dofill = (R_OPAQUE(gc->fill))? 20: -1;
5292 
5293     fprintf(fp, "2 3 "); /* Polyline */
5294     fprintf(fp, "%d %d ", lty, cfg<0?0:(lwd>0?lwd:1)); /* style, thickness */
5295     fprintf(fp, "%d %d ", cpen, cbg); /* pen colour fill colour */
5296     fprintf(fp, "100 0 %d ", dofill); /* depth, pen style, area fill */
5297     fprintf(fp, "%.2f 0 0 -1 0 0 ", 4.0*lwd); /* style value, join .... */
5298     fprintf(fp, "%d\n", n+1); /* number of points */
5299     /* close the path */
5300     for(i = 0 ; i <= n ; i++) {
5301 	xx = x[i%n];
5302 	yy = y[i%n];
5303 	XFconvert(&xx, &yy, pd);
5304 	fprintf(fp, "  %d %d\n", (int)xx, (int)yy);
5305     }
5306 }
5307 
XFig_Polyline(int n,double * x,double * y,const pGEcontext gc,pDevDesc dd)5308 static void XFig_Polyline(int n, double *x, double *y,
5309 			  const pGEcontext gc,
5310 			  pDevDesc dd)
5311 {
5312     XFigDesc *pd = (XFigDesc*) dd->deviceSpecific;
5313     FILE *fp = pd->tmpfp;
5314     double xx, yy;
5315     int i, lty = XF_SetLty(gc->lty), lwd = (int)(gc->lwd*0.833 + 0.5);
5316 
5317     XF_CheckAlpha(gc->col, pd);
5318     if(R_OPAQUE(gc->col) && lty >= 0) {
5319 	fprintf(fp, "2 1 "); /* Polyline */
5320 	fprintf(fp, "%d %d ", lty, lwd>0?lwd:1); /* style, thickness */
5321 	fprintf(fp, "%d %d ", XF_SetColor(gc->col, pd), 7); /* pen colour fill colour */
5322 	fprintf(fp, "100 0 -1 "); /* depth, pen style, area fill */
5323 	fprintf(fp, "%.2f 0 0 -1 0 0 ", 4.0*lwd); /* style value, join .... */
5324 	fprintf(fp, "%d\n", n); /* number of points */
5325 	for(i = 0 ; i < n ; i++) {
5326 	    xx = x[i];
5327 	    yy = y[i];
5328 	    XFconvert(&xx, &yy, pd);
5329 	    fprintf(fp, "  %d %d\n", (int)xx, (int)yy);
5330 	}
5331     }
5332 }
5333 
5334 static const int styles[4] = {0,2,1,3};
5335 
XFig_Text(double x,double y,const char * str,double rot,double hadj,const pGEcontext gc,pDevDesc dd)5336 static void XFig_Text(double x, double y, const char *str,
5337 		      double rot, double hadj,
5338 		      const pGEcontext gc,
5339 		      pDevDesc dd)
5340 {
5341     XFigDesc *pd = (XFigDesc *) dd->deviceSpecific;
5342     FILE *fp = pd->tmpfp;
5343     int fontnum, style = gc->fontface;
5344     double size = floor(gc->cex * gc->ps + 0.5);
5345     const char *str1 = str;
5346     char *buf;
5347 
5348     if(style < 1 || style > 5) {
5349 	warning(_("attempt to use invalid font %d replaced by font 1"), style);
5350 	style = 1;
5351     }
5352     if(style == 5) fontnum = 32;
5353     else fontnum = pd->fontnum + styles[style-1];
5354 
5355     /*
5356      * xfig -international hoge.fig
5357      * mapping multibyte(EUC only) string Times{Romani,Bold} font Only
5358      */
5359     if ( mbcslocale && style != 5 )
5360 	if (!strncmp("EUC", locale2charset(NULL), 3))
5361 	    fontnum = ((style & 1) ^ 1 ) << 1 ;
5362 
5363     XFconvert(&x, &y, pd);
5364     XF_CheckAlpha(gc->col, pd);
5365     if(R_OPAQUE(gc->col)) {
5366 	fprintf(fp, "4 %d ", (int)floor(2*hadj)); /* Text, how justified */
5367 	fprintf(fp, "%d 100 0 ", XF_SetColor(gc->col, pd));
5368 	/* color, depth, pen_style */
5369 	fprintf(fp, "%d %d %.4f %d ", pd->defaultfont?-1:fontnum, (int)size, rot * DEG2RAD,pd->textspecial?6:4);
5370 	/* font pointsize angle flags (Postscript font) */
5371 	fprintf(fp, "%d %d ", (int)(size*12),
5372 		(int)(16.667*XFig_StrWidth(str, gc, dd) +0.5));
5373 	fprintf(fp, "%d %d ", (int)x, (int)y);
5374 	if(strcmp(pd->encoding, "none") != 0) {
5375 	    /* reencode the text */
5376 	    void *cd;
5377 	    const char *i_buf; char *o_buf;
5378 	    size_t i_len, o_len, status;
5379 	    size_t buflen = MB_LEN_MAX*strlen(str) + 1;
5380 
5381 	    cd = (void*)Riconv_open(pd->encoding, "");
5382 	    if(cd == (void*)-1) {
5383 		warning(_("unable to use encoding '%s'"), pd->encoding);
5384 	    } else {
5385 		R_CheckStack2(buflen);
5386 		buf = (char *) alloca(buflen);
5387 		i_buf = (char *) str;
5388 		o_buf = buf;
5389 		i_len = strlen(str) + 1; /* including terminator */
5390 		o_len = buflen;
5391 		status = Riconv(cd, &i_buf, &i_len, &o_buf, &o_len);
5392 		Riconv_close(cd);
5393 		if(status == (size_t)-1)
5394 		    warning(_("failed in text conversion to encoding '%s'"),
5395 			    pd->encoding);
5396 		else str1 = buf;
5397 	    }
5398 	}
5399 	XF_WriteString(fp, str1);
5400 	fprintf(fp, "\\001\n");
5401     }
5402 }
5403 
XFig_StrWidth(const char * str,const pGEcontext gc,pDevDesc dd)5404 static double XFig_StrWidth(const char *str,
5405 			    const pGEcontext gc,
5406 			    pDevDesc dd)
5407 {
5408     XFigDesc *pd = (XFigDesc *) dd->deviceSpecific;
5409     int face = gc->fontface;
5410 
5411     if(face < 1 || face > 5) face = 1;
5412 
5413     return floor(gc->cex * gc->ps + 0.5) *
5414 	PostScriptStringWidth((const unsigned char *)str, CE_NATIVE,
5415 			      &(pd->fonts->family->fonts[face-1]->metrics),
5416 			      FALSE, face, "latin1");
5417 }
5418 
XFig_MetricInfo(int c,const pGEcontext gc,double * ascent,double * descent,double * width,pDevDesc dd)5419 static void XFig_MetricInfo(int c,
5420 			    const pGEcontext gc,
5421 			    double* ascent, double* descent,
5422 			    double* width, pDevDesc dd)
5423 {
5424     XFigDesc *pd = (XFigDesc *) dd->deviceSpecific;
5425     int face = gc->fontface;
5426 
5427     if(face < 1 || face > 5) face = 1;
5428 
5429     PostScriptMetricInfo(c, ascent, descent, width,
5430 			 &(pd->fonts->family->fonts[face-1]->metrics),
5431 			 face == 5, "");
5432     *ascent = floor(gc->cex * gc->ps + 0.5) * *ascent;
5433     *descent = floor(gc->cex * gc->ps + 0.5) * *descent;
5434     *width = floor(gc->cex * gc->ps + 0.5) * *width;
5435 }
5436 
XFig_setPattern(SEXP pattern,pDevDesc dd)5437 static SEXP XFig_setPattern(SEXP pattern, pDevDesc dd) {
5438     return R_NilValue;
5439 }
5440 
XFig_releasePattern(SEXP ref,pDevDesc dd)5441 static void XFig_releasePattern(SEXP ref, pDevDesc dd) {}
5442 
XFig_setClipPath(SEXP path,SEXP ref,pDevDesc dd)5443 static SEXP XFig_setClipPath(SEXP path, SEXP ref, pDevDesc dd) {
5444     return R_NilValue;
5445 }
5446 
XFig_releaseClipPath(SEXP ref,pDevDesc dd)5447 static void XFig_releaseClipPath(SEXP ref, pDevDesc dd) {}
5448 
XFig_setMask(SEXP path,SEXP ref,pDevDesc dd)5449 static SEXP XFig_setMask(SEXP path, SEXP ref, pDevDesc dd) {
5450     return R_NilValue;
5451 }
5452 
XFig_releaseMask(SEXP ref,pDevDesc dd)5453 static void XFig_releaseMask(SEXP ref, pDevDesc dd) {}
5454 
5455 
5456 
5457 /***********************************************************************
5458 
5459 		 PDF driver also shares font handling
5460 
5461 ************************************************************************/
5462 
5463 typedef struct {
5464     rcolorPtr raster;
5465     int w;
5466     int h;
5467     Rboolean interpolate;
5468     int nobj;     /* The object number when written out */
5469     int nmaskobj; /* The mask object number */
5470 } rasterImage;
5471 
5472 #define DEFBUFSIZE 8192
5473 
5474 #define PDFlinearGradient 0
5475 #define PDFstitchedFunction 1
5476 #define PDFexpFunction 2
5477 #define PDFshadingPattern 3
5478 #define PDFsoftMask 4
5479 #define PDFclipPath 5
5480 #define PDFcontent 6
5481 #define PDFtilingPattern 7
5482 
5483 typedef struct {
5484     int type;
5485     int nchar;
5486     char* str;
5487 } PDFdefn;
5488 
5489 typedef struct {
5490     char filename[PATH_MAX];
5491     int open_type;
5492     char cmd[PATH_MAX];
5493 
5494     char papername[64];	/* paper name */
5495     int paperwidth;	/* paper width in big points (1/72 in) */
5496     int paperheight;	/* paper height in big points */
5497     int pageno;		/* page number */
5498     int fileno;		/* file number */
5499 
5500     int maxpointsize;
5501 
5502     double width;	/* plot width in inches */
5503     double height;	/* plot height in inches */
5504     double pagewidth;	/* page width in inches */
5505     double pageheight;	/* page height in inches */
5506     Rboolean pagecentre;  /* centre image on page? */
5507     Rboolean onefile;	/* one file or one file per page? */
5508 
5509     FILE *pdffp;        /* output file */
5510     FILE *mainfp;
5511     FILE *pipefp;
5512 
5513     /* This group of variables track the current device status.
5514      * They should only be set by routines that emit PDF. */
5515     struct {
5516 	double lwd;		 /* line width */
5517 	int lty;		 /* line type */
5518 	R_GE_lineend lend;
5519 	R_GE_linejoin ljoin;
5520 	double lmitre;
5521 	int fontsize;	         /* font size in points */
5522 	rcolor col;		 /* color */
5523 	rcolor fill;	         /* fill color */
5524 	rcolor bg;		 /* color */
5525 	int srgb_fg, srgb_bg;    /* Are stroke and fill colorspaces set? */
5526         int patternfill;
5527         int mask;
5528     } current;
5529 
5530     /*
5531      * This is a record of the alpha transparency levels used during
5532      * drawing to the device.
5533      * Only allow 256 different alpha levels
5534      * (because R uses 8-bit alpha channel).
5535      * "alphas" is a record of alphas used so far (unused set to -1)
5536      * There are separate alpha levels for stroking and filling
5537      * (i.e., col and fill)
5538      */
5539     short colAlpha[256];
5540     short fillAlpha[256];
5541     Rboolean usedAlpha;
5542 
5543     /*
5544      * What version of PDF are we trying to work with?
5545      * This is used (so far) for implementing transparency and CID fonts
5546      * Alphas are only used if version is at least 1.4
5547      */
5548     int versionMajor;
5549     int versionMinor;
5550 
5551     int nobjs;  /* number of objects */
5552     int *pos; /* object positions */
5553     int max_nobjs; /* current allocation size */
5554     int *pageobj; /* page object numbers */
5555     int pagemax;
5556     int startstream; /* position of start of current stream */
5557     Rboolean inText;
5558     char title[1024];
5559     char colormodel[30];
5560     Rboolean dingbats, useKern;
5561     Rboolean fillOddEven; /* polygon fill mode */
5562     Rboolean useCompression;
5563     char tmpname[PATH_MAX]; /* used before compression */
5564 
5565     /*
5566      * Fonts and encodings used on the device
5567      */
5568     type1fontlist fonts;
5569     cidfontlist   cidfonts;
5570     encodinglist  encodings;
5571     /*
5572      * These next two just record the default device font
5573      */
5574     type1fontfamily defaultFont;
5575     cidfontfamily   defaultCIDFont;
5576     /* Record if fonts are used */
5577     Rboolean fontUsed[100];
5578 
5579     /* Raster images used on the device */
5580     rasterImage *rasters;
5581     int numRasters; /* number in use */
5582     int writtenRasters; /* number written out on this page */
5583     int fileRasters; /* number written out in this file */
5584     int maxRasters; /* size of array allocated */
5585     /* Soft masks for raster images */
5586     int *masks;
5587     int numMasks;
5588 
5589     PDFdefn *definitions;
5590     int numDefns;
5591     int maxDefns;
5592     Rboolean appendingClipPath; /* Are we defining a clipping path ? */
5593     int appendingMask; /* Are we defining a mask ? */
5594     int currentMask;
5595     int appendingPattern; /* Are we defining a (tiling) pattern ? */
5596 
5597     /* Is the device "offline" (does not write out to a file) */
5598     Rboolean offline;
5599 }
5600 PDFDesc;
5601 
5602 /* Called at the start of a page and when clipping is reset */
PDF_Invalidate(PDFDesc * pd)5603 static void PDF_Invalidate(PDFDesc *pd)
5604 {
5605     pd->current.fontsize = -1;
5606     pd->current.lwd = -1;
5607     pd->current.lty = -1;
5608     pd->current.lend = 0;
5609     pd->current.ljoin = 0;
5610     pd->current.lmitre = 0;
5611     /* page starts with black as the default fill and stroke colours */
5612     pd->current.col = INVALID_COL;
5613     pd->current.fill = INVALID_COL;
5614     pd->current.bg = INVALID_COL;
5615     pd->current.srgb_fg = pd->current.srgb_bg = 0;
5616     pd->current.patternfill = -1;
5617     pd->current.mask = -1;
5618 }
5619 
5620 /* Macro for driver actions to check for "offline" device and bail out */
5621 
5622 #define PDF_checkOffline() if (pd->offline) return
5623 
5624 /* Device Driver Actions */
5625 
5626 static Rboolean PDF_Open(pDevDesc, PDFDesc*);
5627 static void PDF_Circle(double x, double y, double r,
5628 		       const pGEcontext gc,
5629 		       pDevDesc dd);
5630 static void PDF_Clip(double x0, double x1, double y0, double y1,
5631 		     pDevDesc dd);
5632 static void PDF_Close(pDevDesc dd);
5633 static void PDF_Line(double x1, double y1, double x2, double y2,
5634 		     const pGEcontext gc,
5635 		     pDevDesc dd);
5636 void PDF_MetricInfo(int c,
5637                     const pGEcontext gc,
5638                     double* ascent, double* descent,
5639                     double* width, pDevDesc dd);
5640 static void PDF_NewPage(const pGEcontext gc, pDevDesc dd);
5641 static void PDF_Polygon(int n, double *x, double *y,
5642 			const pGEcontext gc,
5643 			pDevDesc dd);
5644 static void PDF_Polyline(int n, double *x, double *y,
5645 			 const pGEcontext gc,
5646 			 pDevDesc dd);
5647 static void PDF_Rect(double x0, double y0, double x1, double y1,
5648 		     const pGEcontext gc,
5649 		     pDevDesc dd);
5650 static void PDF_Path(double *x, double *y,
5651                      int npoly, int *nper,
5652                      Rboolean winding,
5653                      const pGEcontext gc,
5654                      pDevDesc dd);
5655 static void PDF_Raster(unsigned int *raster, int w, int h,
5656 		       double x, double y, double width, double height,
5657 		       double rot, Rboolean interpolate,
5658 		       const pGEcontext gc, pDevDesc dd);
5659 static void PDF_Size(double *left, double *right,
5660 		     double *bottom, double *top,
5661 		     pDevDesc dd);
5662 double PDF_StrWidth(const char *str,
5663                     const pGEcontext gc,
5664                     pDevDesc dd);
5665 static void PDF_Text(double x, double y, const char *str,
5666 		     double rot, double hadj,
5667 		     const pGEcontext gc,
5668 		     pDevDesc dd);
5669 static double PDF_StrWidthUTF8(const char *str,
5670 			       const pGEcontext gc,
5671 			       pDevDesc dd);
5672 static void PDF_TextUTF8(double x, double y, const char *str,
5673 			 double rot, double hadj,
5674 			 const pGEcontext gc,
5675 			 pDevDesc dd);
5676 static SEXP     PDF_setPattern(SEXP pattern, pDevDesc dd);
5677 static void     PDF_releasePattern(SEXP ref, pDevDesc dd);
5678 static SEXP     PDF_setClipPath(SEXP path, SEXP ref, pDevDesc dd);
5679 static void     PDF_releaseClipPath(SEXP ref, pDevDesc dd);
5680 static SEXP     PDF_setMask(SEXP path, SEXP ref, pDevDesc dd);
5681 static void     PDF_releaseMask(SEXP ref, pDevDesc dd);
5682 
5683 
5684 /***********************************************************************
5685  * Stuff for recording definitions
5686  */
5687 
initDefn(int i,int type,PDFDesc * pd)5688 static void initDefn(int i, int type, PDFDesc *pd)
5689 {
5690     pd->definitions[i].type = type;
5691     pd->definitions[i].nchar = DEFBUFSIZE;
5692     pd->definitions[i].str = malloc(DEFBUFSIZE*sizeof(char));
5693     pd->definitions[i].str[0] = '\0';
5694 }
5695 
catDefn(char * buf,int i,PDFDesc * pd)5696 static void catDefn(char* buf, int i, PDFDesc *pd)
5697 {
5698     size_t len = strlen(pd->definitions[i].str);
5699     size_t buflen = strlen(buf);
5700     /* Grow definition string if necessary) */
5701     if (len + buflen + 1 >= pd->definitions[i].nchar) {
5702 	void *tmp;
5703         pd->definitions[i].nchar = pd->definitions[i].nchar + DEFBUFSIZE;
5704 	tmp = realloc(pd->definitions[i].str,
5705                       (pd->definitions[i].nchar)*sizeof(char));
5706 	if (!tmp) error(_("failed to increase definition string (shut down PDF device)"));
5707 	pd->definitions[i].str = tmp;
5708     }
5709     strncat(pd->definitions[i].str, buf, buflen);
5710 }
5711 
copyDefn(int fromDefn,int toDefn,PDFDesc * pd)5712 static void copyDefn(int fromDefn, int toDefn, PDFDesc *pd)
5713 {
5714     catDefn(pd->definitions[fromDefn].str, toDefn, pd);
5715 }
5716 
trimDefn(int i,PDFDesc * pd)5717 static void trimDefn(int i, PDFDesc *pd)
5718 {
5719     size_t len = strlen(pd->definitions[i].str);
5720     pd->definitions[i].str = realloc(pd->definitions[i].str,
5721                                      (len + 1)*sizeof(char));
5722     pd->definitions[i].str[len] = '\0';
5723 }
5724 
killDefn(int i,PDFDesc * pd)5725 static void killDefn(int i, PDFDesc *pd)
5726 {
5727     if (pd->definitions[i].str != NULL) {
5728         free(pd->definitions[i].str);
5729     }
5730 }
5731 
initDefinitions(PDFDesc * pd)5732 static void initDefinitions(PDFDesc *pd)
5733 {
5734     int i;
5735     pd->definitions = malloc(pd->maxDefns*sizeof(PDFdefn));
5736     for (i = 0; i < pd->maxDefns; i++) {
5737         pd->definitions[i].str = NULL;
5738     }
5739 }
5740 
growDefinitions(PDFDesc * pd)5741 static int growDefinitions(PDFDesc *pd)
5742 {
5743     if (pd->numDefns == pd->maxDefns) {
5744 	int newMax = 2*pd->maxDefns;
5745 	void *tmp;
5746 	/* Do it this way so previous pointer is retained if it fails */
5747 	tmp = realloc(pd->definitions, newMax*sizeof(PDFdefn));
5748 	if(!tmp) error(_("failed to increase 'maxDefns'"));
5749 	pd->definitions = tmp;
5750 	for (int i = pd->maxDefns; i < newMax; i++) {
5751 	    pd->definitions[i].str = NULL;
5752 	}
5753 	pd->maxDefns = newMax;
5754     }
5755     return pd->numDefns++;
5756 }
5757 
shrinkDefinitions(PDFDesc * pd)5758 static void shrinkDefinitions(PDFDesc *pd)
5759 {
5760     pd->numDefns--;
5761 }
5762 
killDefinitions(PDFDesc * pd)5763 static void killDefinitions(PDFDesc *pd)
5764 {
5765     int i;
5766     for (i = 0; i < pd->numDefns; i++)
5767         killDefn(i, pd);
5768     free(pd->definitions);
5769 }
5770 
5771 /* For when end file and start a new one on new page */
resetDefinitions(PDFDesc * pd)5772 static void resetDefinitions(PDFDesc *pd)
5773 {
5774     int i;
5775     for (i = 0; i < pd->numDefns; i++)
5776         killDefn(i, pd);
5777     pd->numDefns = 0;
5778     /* Leave pd->maxDefns as is */
5779 }
5780 
5781 /***********************************************************************
5782  * Stuff for gradients
5783  */
5784 
addRGBExpGradientFunction(SEXP gradient,int i,double start,double end,int toDefn,PDFDesc * pd)5785 static void addRGBExpGradientFunction(SEXP gradient, int i,
5786                                       double start, double end,
5787                                       int toDefn,
5788                                       PDFDesc *pd)
5789 {
5790     char buf[300];
5791     rcolor col1 = 0, col2 = 0; // -Wall
5792     switch(R_GE_patternType(gradient)) {
5793     case R_GE_linearGradientPattern:
5794         col1 = R_GE_linearGradientColour(gradient, i);
5795         col2 = R_GE_linearGradientColour(gradient, i + 1);
5796         break;
5797     case R_GE_radialGradientPattern:
5798         col1 = R_GE_radialGradientColour(gradient, i);
5799         col2 = R_GE_radialGradientColour(gradient, i + 1);
5800         break;
5801     }
5802     snprintf(buf,
5803              300,
5804              "<<\n/FunctionType 2\n/Domain [%.4f %.4f]\n/C0 [%0.4f %0.4f %0.4f]\n/C1 [%0.4f %0.4f %0.4f]\n/N 1\n>>\n",
5805              start,
5806              end,
5807              R_RED(col1)/255.0,
5808              R_GREEN(col1)/255.0,
5809              R_BLUE(col1)/255.0,
5810              R_RED(col2)/255.0,
5811              R_GREEN(col2)/255.0,
5812              R_BLUE(col2)/255.0);
5813     catDefn(buf, toDefn, pd);
5814 }
5815 
addAlphaExpGradientFunction(SEXP gradient,int i,double start,double end,int toDefn,PDFDesc * pd)5816 static void addAlphaExpGradientFunction(SEXP gradient, int i,
5817                                         double start, double end,
5818                                         int toDefn,
5819                                         PDFDesc *pd)
5820 {
5821     char buf[300];
5822     rcolor col1 = 0, col2 = 0; // -Wall
5823     switch(R_GE_patternType(gradient)) {
5824     case R_GE_linearGradientPattern:
5825         col1 = R_GE_linearGradientColour(gradient, i);
5826         col2 = R_GE_linearGradientColour(gradient, i + 1);
5827         break;
5828     case R_GE_radialGradientPattern:
5829         col1 = R_GE_radialGradientColour(gradient, i);
5830         col2 = R_GE_radialGradientColour(gradient, i + 1);
5831         break;
5832     }
5833     snprintf(buf,
5834              300,
5835              "<<\n/FunctionType 2\n/Domain [%.4f %.4f]\n/C0 [%0.4f]\n/C1 [%0.4f]\n/N 1\n>>\n",
5836              start,
5837              end,
5838              R_ALPHA(col1)/255.0,
5839              R_ALPHA(col2)/255.0);
5840     catDefn(buf, toDefn, pd);
5841 }
5842 
addStitchedGradientFunction(SEXP gradient,int nStops,int toDefn,Rboolean alpha,PDFDesc * pd)5843 static void addStitchedGradientFunction(SEXP gradient, int nStops, int toDefn,
5844                                         Rboolean alpha, PDFDesc *pd)
5845 {
5846     int defNum = growDefinitions(pd);
5847     double firstStop = 0.0, lastStop = 0.0, stop = 0.0; // -Wall for gcc 9
5848     char buf[100];
5849     initDefn(defNum, PDFstitchedFunction, pd);
5850     switch(R_GE_patternType(gradient)) {
5851     case R_GE_linearGradientPattern:
5852         firstStop = R_GE_linearGradientStop(gradient, 0);
5853         lastStop = R_GE_linearGradientStop(gradient, nStops - 1);
5854         break;
5855     case R_GE_radialGradientPattern:
5856         firstStop = R_GE_radialGradientStop(gradient, 0);
5857         lastStop = R_GE_radialGradientStop(gradient, nStops - 1);
5858         break;
5859     }
5860     snprintf(buf,
5861              100,
5862              "<<\n/FunctionType 3\n/Domain [%0.4f %0.4f]\n/Functions [\n",
5863              firstStop,
5864              lastStop);
5865     catDefn(buf, defNum, pd);
5866     for (int i = 0; i < (nStops - 1); i++) {
5867         if (alpha) {
5868             addAlphaExpGradientFunction(gradient, i, 0.0, 1.0, defNum, pd);
5869         } else {
5870             addRGBExpGradientFunction(gradient, i, 0.0, 1.0, defNum, pd);
5871         }
5872     }
5873     catDefn("]\n/Bounds [", defNum, pd);
5874     for (int i = 1; i < (nStops - 1); i++) {
5875         switch(R_GE_patternType(gradient)) {
5876         case R_GE_linearGradientPattern:
5877             stop = R_GE_linearGradientStop(gradient, i);
5878             break;
5879         case R_GE_radialGradientPattern:
5880             stop = R_GE_radialGradientStop(gradient, i);
5881             break;
5882         }
5883         sprintf(buf,
5884                 "%0.4f ",
5885                 stop);
5886         catDefn(buf, defNum, pd);
5887     }
5888     catDefn("]\n/Encode [", defNum, pd);
5889     for (int i = 0; i < (nStops - 1); i++)
5890         catDefn("0 1 ", defNum, pd);
5891     catDefn("]\n>>\n", defNum, pd);
5892     /* Copy toDefn */
5893     copyDefn(defNum, toDefn, pd);
5894     /* Discard temporary definition */
5895     killDefn(defNum, pd);
5896     shrinkDefinitions(pd);
5897 }
5898 
addGradientFunction(SEXP gradient,int toDefn,Rboolean alpha,PDFDesc * pd)5899 static void addGradientFunction(SEXP gradient, int toDefn,
5900                                 Rboolean alpha, PDFDesc *pd)
5901 {
5902     int nStops = 0; // -Wall
5903     switch(R_GE_patternType(gradient)) {
5904     case R_GE_linearGradientPattern:
5905         nStops = R_GE_linearGradientNumStops(gradient);
5906         break;
5907     case R_GE_radialGradientPattern:
5908         nStops = R_GE_radialGradientNumStops(gradient);
5909         break;
5910     }
5911     if (nStops > 2) {
5912         addStitchedGradientFunction(gradient, nStops, toDefn, alpha, pd);
5913     } else {
5914         double start = 0.0, end = 0.0; // -Wall
5915         switch(R_GE_patternType(gradient)) {
5916         case R_GE_linearGradientPattern:
5917             start = R_GE_linearGradientStop(gradient, 0);
5918             end = R_GE_linearGradientStop(gradient, 1);
5919             break;
5920         case R_GE_radialGradientPattern:
5921             start = R_GE_radialGradientStop(gradient, 0);
5922             end = R_GE_radialGradientStop(gradient, 1);
5923             break;
5924         }
5925         if (alpha) {
5926             addAlphaExpGradientFunction(gradient, 0, start, end, toDefn, pd);
5927         } else {
5928             addRGBExpGradientFunction(gradient, 0, start, end, toDefn, pd);
5929         }
5930     }
5931 }
5932 
addLinearGradient(SEXP gradient,char * colormodel,int toDefn,PDFDesc * pd)5933 static void addLinearGradient(SEXP gradient, char* colormodel,
5934                               int toDefn, PDFDesc *pd)
5935 {
5936     int defNum = growDefinitions(pd);
5937     char buf[200];
5938     char colorspace[12];
5939     if (streql(colormodel, "gray"))
5940         strcpy(colorspace, "/DeviceGray");
5941     else if (streql(colormodel, "srgb"))
5942         strcpy(colorspace, "5 0 R");
5943     else
5944         strcpy(colorspace, "/DeviceRGB");
5945     initDefn(defNum, PDFlinearGradient, pd);
5946     snprintf(buf,
5947              200,
5948              "<<\n/ShadingType 2\n/ColorSpace %s\n/Coords [%.4f %.4f %.4f %.4f]\n/Function\n",
5949              colorspace,
5950              R_GE_linearGradientX1(gradient),
5951              R_GE_linearGradientY1(gradient),
5952              R_GE_linearGradientX2(gradient),
5953              R_GE_linearGradientY2(gradient));
5954     catDefn(buf, defNum, pd);
5955     if (streql(colormodel, "gray")) {
5956         addGradientFunction(gradient, defNum, TRUE, pd);
5957     } else {
5958         addGradientFunction(gradient, defNum, FALSE, pd);
5959     }
5960     char extend[6];
5961     switch(R_GE_linearGradientExtend(gradient)) {
5962     case R_GE_patternExtendPad:
5963         strcpy(extend, "true");
5964         break;
5965     case R_GE_patternExtendRepeat:
5966     case R_GE_patternExtendReflect:
5967         warning("Repeat or reflect pattern not supported on PDF device");
5968     case R_GE_patternExtendNone:
5969         strcpy(extend, "false");
5970     }
5971     snprintf(buf,
5972              200,
5973              "/Extend [%s %s]\n>>\n",
5974              extend,
5975              extend);
5976     catDefn(buf, defNum, pd);
5977     /* Copy toDefn */
5978     copyDefn(defNum, toDefn, pd);
5979     /* Discard temporary definition */
5980     killDefn(defNum, pd);
5981     shrinkDefinitions(pd);
5982 }
5983 
addRadialGradient(SEXP gradient,char * colormodel,int toDefn,PDFDesc * pd)5984 static void addRadialGradient(SEXP gradient, char* colormodel,
5985                               int toDefn, PDFDesc *pd)
5986 {
5987     int defNum = growDefinitions(pd);
5988     char buf[200];
5989     char colorspace[12];
5990     if (streql(colormodel, "gray"))
5991         strcpy(colorspace, "/DeviceGray");
5992     else if (streql(colormodel, "srgb"))
5993         strcpy(colorspace, "5 0 R");
5994     else
5995         strcpy(colorspace, "/DeviceRGB");
5996     initDefn(defNum, PDFlinearGradient, pd);
5997     snprintf(buf,
5998              200,
5999              "<<\n/ShadingType 3\n/ColorSpace %s\n/Coords [%.4f %.4f %.4f %.4f %.4f %.4f]\n/Function\n",
6000              colorspace,
6001              R_GE_radialGradientCX1(gradient),
6002              R_GE_radialGradientCY1(gradient),
6003              R_GE_radialGradientR1(gradient),
6004              R_GE_radialGradientCX2(gradient),
6005              R_GE_radialGradientCY2(gradient),
6006              R_GE_radialGradientR2(gradient));
6007     catDefn(buf, defNum, pd);
6008     if (streql(colormodel, "gray")) {
6009         addGradientFunction(gradient, defNum, TRUE, pd);
6010     } else {
6011         addGradientFunction(gradient, defNum, FALSE, pd);
6012     }
6013     char extend[6];
6014     switch(R_GE_radialGradientExtend(gradient)) {
6015     case R_GE_patternExtendPad:
6016         strcpy(extend, "true");
6017         break;
6018     case R_GE_patternExtendRepeat:
6019     case R_GE_patternExtendReflect:
6020         warning("Repeat or reflect pattern not supported on PDF device");
6021     case R_GE_patternExtendNone:
6022         strcpy(extend, "false");
6023     }
6024     snprintf(buf,
6025              200,
6026              "/Extend [%s %s]\n>>\n",
6027              extend,
6028              extend);
6029     catDefn(buf, defNum, pd);
6030     /* Copy toDefn */
6031     copyDefn(defNum, toDefn, pd);
6032     /* Discard temporary definition */
6033     killDefn(defNum, pd);
6034     shrinkDefinitions(pd);
6035 }
6036 
addShadingSoftMask(SEXP pattern,PDFDesc * pd)6037 static int addShadingSoftMask(SEXP pattern, PDFDesc *pd)
6038 {
6039     int defNum = growDefinitions(pd);
6040     initDefn(defNum, PDFsoftMask, pd);
6041     /* Object number will be determined when definition written
6042      * to file (PDF_endfile)
6043      */
6044     catDefn(" 0 obj\n<<\n/Type /ExtGState\n/AIS false\n/SMask\n<<\n",
6045             defNum, pd);
6046     catDefn("/Type /Mask\n/S /Luminosity\n/G\n<<\n",
6047             defNum, pd);
6048     catDefn("/Type /XObject\n/Subtype /Form\n/FormType 1\n/Group\n<<\n",
6049             defNum, pd);
6050     catDefn("/Type /Group\n/CS /DeviceGray\n/I true\n/S /Transparency\n",
6051             defNum, pd);
6052     catDefn(">>\n/Resources\n<<\n",
6053             defNum, pd);
6054     catDefn("/Shading\n<<\n/S0\n",
6055             defNum, pd);
6056     switch(R_GE_patternType(pattern)) {
6057     case R_GE_linearGradientPattern:
6058         addLinearGradient(pattern, "gray", defNum, pd);
6059         break;
6060     case R_GE_radialGradientPattern:
6061         addRadialGradient(pattern, "gray", defNum, pd);
6062         break;
6063     default:
6064         warning("Shading type not yet supported");
6065         return -1;
6066     }
6067     catDefn(">>\n/ExtGState << /G0 << /CA 1 /ca 1 >> >>\n",
6068             defNum, pd);
6069     char buf[30];
6070     snprintf(buf,
6071              30,
6072              ">>\n/BBox [0 0 %d %d]\n",
6073              (int) (0.5 + pd->paperwidth), (int) (0.5 + pd->paperheight));
6074     catDefn(buf, defNum, pd);
6075     /* Note the spaces before the >> just after the endstream;
6076      * ghostscript seems to need those to avoid error (!?) */
6077     catDefn("/Length 14\n>>\nstream\n/G0 gs /S0 sh\nendstream\n  >>\n",
6078             defNum, pd);
6079     catDefn(">>\nendobj\n", defNum, pd);
6080     trimDefn(defNum, pd);
6081     return defNum;
6082 }
6083 
6084 /*
6085  * Do we need to bother with semi-transparency?
6086  */
semiTransparent(int col)6087 static int semiTransparent(int col)
6088 {
6089     return !(R_OPAQUE(col) || R_TRANSPARENT(col));
6090 }
6091 
semiTransparentShading(SEXP pattern)6092 static Rboolean semiTransparentShading(SEXP pattern)
6093 {
6094     int i, nStops = 0; // -Wall
6095     switch(R_GE_patternType(pattern)) {
6096     case R_GE_linearGradientPattern:
6097         nStops = R_GE_linearGradientNumStops(pattern);
6098         break;
6099     case R_GE_radialGradientPattern:
6100         nStops = R_GE_radialGradientNumStops(pattern);
6101         break;
6102     }
6103     rcolor col = 0; // -Wall
6104     Rboolean anyOpaque = FALSE;
6105     Rboolean anyTransparent = FALSE;
6106     for (i = 0; i < nStops; i++) {
6107         switch(R_GE_patternType(pattern)) {
6108         case R_GE_linearGradientPattern:
6109             col = R_GE_linearGradientColour(pattern, i);
6110             break;
6111         case R_GE_radialGradientPattern:
6112             col = R_GE_radialGradientColour(pattern, i);
6113             break;
6114         }
6115         if (semiTransparent(col))
6116             return TRUE;
6117         if (R_OPAQUE(col)) anyOpaque = TRUE;
6118         if (R_TRANSPARENT(col)) anyTransparent = TRUE;
6119         if (anyOpaque && anyTransparent)
6120             return TRUE;
6121     }
6122     return FALSE;
6123 }
6124 
addShading(SEXP pattern,PDFDesc * pd)6125 static SEXP addShading(SEXP pattern, PDFDesc *pd)
6126 {
6127     SEXP ref = R_NilValue;
6128     int defNum = growDefinitions(pd);
6129     /* Object number will be determined when definition written
6130      * to file (PDF_endfile)
6131      */
6132     initDefn(defNum, PDFshadingPattern, pd);
6133     catDefn(" 0 obj\n<<\n/Type Pattern\n/PatternType 2\n/Shading\n",
6134             defNum, pd);
6135     switch(R_GE_patternType(pattern)) {
6136     case R_GE_linearGradientPattern:
6137         addLinearGradient(pattern, pd->colormodel, defNum, pd);
6138         break;
6139     case R_GE_radialGradientPattern:
6140         addRadialGradient(pattern, pd->colormodel, defNum, pd);
6141         break;
6142     default:
6143         warning("Shading type not yet supported");
6144         return R_NilValue;
6145     }
6146     catDefn(">>\nendobj\n", defNum, pd);
6147     trimDefn(defNum, pd);
6148     if (defNum >= 0) {
6149         if (semiTransparentShading(pattern)) {
6150             int maskNum = addShadingSoftMask(pattern, pd);
6151             if (maskNum >= 0) {
6152                 PROTECT(ref = allocVector(INTSXP, 2));
6153                 INTEGER(ref)[0] = defNum;
6154                 INTEGER(ref)[1] = maskNum;
6155                 UNPROTECT(1);
6156             }
6157         } else {
6158             PROTECT(ref = allocVector(INTSXP, 1));
6159             INTEGER(ref)[0] = defNum;
6160             UNPROTECT(1);
6161         }
6162     }
6163     return ref;
6164 }
6165 
6166 /***********************************************************************
6167  * Stuff for tiling patterns
6168  */
6169 
newTiling(SEXP pattern,PDFDesc * pd)6170 static int newTiling(SEXP pattern, PDFDesc *pd)
6171 {
6172     SEXP R_fcall;
6173     int mainPattern;
6174     char buf[100];
6175     int defNum = growDefinitions(pd);
6176     initDefn(defNum, PDFtilingPattern, pd);
6177 
6178     /* Use separate definition to store the pattern content
6179      * so we can determine length of the content
6180      */
6181     int contentDefn = growDefinitions(pd);
6182     initDefn(contentDefn, PDFcontent, pd);
6183     /* Some initialisation that newpage does
6184      * (expected by other captured output)
6185      */
6186     catDefn("1 J 1 j q\n", contentDefn, pd);
6187 
6188     mainPattern = pd->appendingPattern;
6189     pd->appendingPattern = contentDefn;
6190 
6191     /* Invalidate current settings so pattern enforces its settings */
6192     PDF_Invalidate(pd);
6193 
6194     /* Evaluate the pattern function to generate the pattern */
6195     R_fcall = PROTECT(lang1(R_GE_tilingPatternFunction(pattern)));
6196     eval(R_fcall, R_GlobalEnv);
6197     UNPROTECT(1);
6198 
6199     /* Invalidate current settings so normal drawing enforces its settings */
6200     PDF_Invalidate(pd);
6201 
6202     /* Some finalisation that endpage does
6203      * (to match the newpage initilisation)
6204      */
6205     catDefn("Q\n", contentDefn, pd);
6206     /* Cannot discard temporary definition because there may have been
6207      * other definitions created during its creation (so it may no
6208      * longer be the topmost definition)
6209      */
6210     trimDefn(contentDefn, pd);
6211 
6212     pd->appendingPattern = mainPattern;
6213 
6214     /* Object number will be determined when definition written
6215      * to file (PDF_endfile)
6216      */
6217     catDefn(" 0 obj\n<<\n/Type /Pattern\n/PatternType 1\n/PaintType 1\n",
6218             defNum, pd);
6219     catDefn("/TilingType 1\n",
6220             defNum, pd);
6221     snprintf(buf,
6222              100,
6223              "/BBox [%d %d %d %d]\n",
6224              (int) (0.5 + R_GE_tilingPatternX(pattern)),
6225              (int) (0.5 + R_GE_tilingPatternY(pattern)),
6226              (int) (0.5 + R_GE_tilingPatternX(pattern) +
6227                     R_GE_tilingPatternWidth(pattern)),
6228              (int) (0.5 + R_GE_tilingPatternY(pattern) +
6229                     R_GE_tilingPatternHeight(pattern)));
6230     catDefn(buf, defNum, pd);
6231     snprintf(buf,
6232              100,
6233              "/XStep %d /YStep %d\n",
6234              (int) (0.5 + R_GE_tilingPatternWidth(pattern)),
6235              (int) (0.5 + R_GE_tilingPatternHeight(pattern)));
6236     catDefn(buf, defNum, pd);
6237 
6238     /* Tiling pattern will be completed at end of file with
6239      * call to completeTiling()
6240      */
6241 
6242     return defNum;
6243 }
6244 
6245 static int PDFwriteResourceDictionary(int objOffset, Rboolean endpage,
6246                                       int excludeDef, PDFDesc *pd);
6247 
completeTiling(int defNum,int resourceDictOffset,PDFDesc * pd)6248 static void completeTiling(int defNum, int resourceDictOffset, PDFDesc *pd)
6249 {
6250     char buf[100];
6251     /* (strong) assumption here that tiling pattern content is
6252      * very next definition
6253      */
6254     int contentDefn = defNum + 1;
6255 
6256     catDefn("/Resources\n",
6257             defNum, pd);
6258 
6259     /* Write the resource dictionary for the tiling pattern.
6260      * This is just a copy of the Resource Dictionary for the page,
6261      * WITHOUT the tiling pattern itself.
6262      * This will be AT LEAST as much as the tiling pattern needs,
6263      * but it MUST exclude itself to avoid infinite loop in PDF viewer.
6264      */
6265     /* Redirect PDFwriteResourceDictionary() output to pattern */
6266     pd->appendingPattern = defNum;
6267     PDFwriteResourceDictionary(resourceDictOffset, FALSE, defNum, pd);
6268 
6269     /* Note the spaces before the >> just after the endstream;
6270      * ghostscript seems to need those to avoid error (!?) */
6271     snprintf(buf,
6272              100,
6273              "/Length %d\n",
6274              (int) strlen(pd->definitions[contentDefn].str));
6275     catDefn(buf, defNum, pd);
6276     catDefn(">>\nstream\n", defNum, pd);
6277     /* Copy pattern content */
6278     copyDefn(contentDefn, defNum, pd);
6279     catDefn("endstream\nendobj\n", defNum, pd);
6280 
6281     trimDefn(defNum, pd);
6282 }
6283 
addTiling(SEXP pattern,PDFDesc * pd)6284 static SEXP addTiling(SEXP pattern, PDFDesc *pd)
6285 {
6286     SEXP ref = R_NilValue;
6287     int defNum = newTiling(pattern, pd);
6288 
6289     if (defNum >= 0) {
6290         PROTECT(ref = allocVector(INTSXP, 1));
6291         INTEGER(ref)[0] = defNum;
6292         UNPROTECT(1);
6293     }
6294 
6295     return ref;
6296 }
6297 
addToPattern(char * str,PDFDesc * pd)6298 static void addToPattern(char* str, PDFDesc *pd)
6299 {
6300     /* append to a tiling pattern content definition */
6301     catDefn(str, pd->appendingPattern, pd);
6302 }
6303 
6304 /***********************************************************************
6305  * Stuff for patterns
6306  */
6307 
addPattern(SEXP pattern,PDFDesc * pd)6308 static SEXP addPattern(SEXP pattern, PDFDesc *pd)
6309 {
6310     SEXP ref = R_NilValue;
6311     switch(R_GE_patternType(pattern)) {
6312     case R_GE_linearGradientPattern:
6313     case R_GE_radialGradientPattern:
6314         ref = addShading(pattern, pd);
6315         break;
6316     case R_GE_tilingPattern:
6317         ref = addTiling(pattern, pd);
6318         break;
6319     }
6320     return ref;
6321 }
6322 
countPatterns(PDFDesc * pd)6323 static int countPatterns(PDFDesc *pd)
6324 {
6325     int i, count = 0;
6326     for (i = 0; i < pd->numDefns; i++) {
6327         if (pd->definitions[i].type == PDFshadingPattern ||
6328             pd->definitions[i].type == PDFtilingPattern) {
6329             count++;
6330         }
6331     }
6332     return count;
6333 }
6334 
6335 /***********************************************************************
6336  * Stuff for clipping paths
6337  */
6338 
addToClipPath(char * str,PDFDesc * pd)6339 static void addToClipPath(char* str, PDFDesc *pd)
6340 {
6341     /* Just append to the "current" definition */
6342     catDefn(str, pd->numDefns - 1, pd);
6343 }
6344 
newClipPath(SEXP path,PDFDesc * pd)6345 static int newClipPath(SEXP path, PDFDesc *pd)
6346 {
6347     SEXP R_fcall;
6348     int defNum = growDefinitions(pd);
6349     initDefn(defNum, PDFclipPath, pd);
6350     catDefn("Q q\n", defNum, pd);
6351 
6352     /* Put device in "append mode" */
6353     pd->appendingClipPath = TRUE;
6354 
6355     /* Evaluate the path function to generate the clipping path */
6356     R_fcall = PROTECT(lang1(path));
6357     eval(R_fcall, R_GlobalEnv);
6358     UNPROTECT(1);
6359 
6360     trimDefn(defNum, pd);
6361     /* Exit "append mode" */
6362     pd->appendingClipPath = FALSE;
6363 
6364     return defNum;
6365 }
6366 
6367 /***********************************************************************
6368  * Stuff for masks
6369  */
6370 
addToMask(char * str,PDFDesc * pd)6371 static void addToMask(char* str, PDFDesc *pd)
6372 {
6373     /* append to the a mask content definition */
6374     catDefn(str, pd->appendingMask, pd);
6375 }
6376 
newMask(SEXP path,PDFDesc * pd)6377 static int newMask(SEXP path, PDFDesc *pd)
6378 {
6379     SEXP R_fcall;
6380     int mainMask;
6381     char buf[100];
6382     int defNum = growDefinitions(pd);
6383     initDefn(defNum, PDFsoftMask, pd);
6384 
6385     /* Use temporary definition to store the mask content
6386      * so we can determine length of the content
6387      */
6388     int tempDefn = growDefinitions(pd);
6389     initDefn(tempDefn, PDFcontent, pd);
6390     /* Some initialisation that newpage does
6391      * (expected by other captured output)
6392      */
6393     catDefn("1 J 1 j q\n", tempDefn, pd);
6394 
6395     mainMask = pd->appendingMask;
6396     pd->appendingMask = tempDefn;
6397 
6398     /* Invalidate current settings so mask enforces its settings */
6399     PDF_Invalidate(pd);
6400 
6401     /* Evaluate the path function to generate the mask */
6402     R_fcall = PROTECT(lang1(path));
6403     eval(R_fcall, R_GlobalEnv);
6404     UNPROTECT(1);
6405 
6406     /* Invalidate current settings so normal drawing enforces its settings */
6407     PDF_Invalidate(pd);
6408 
6409     /* Some finalisation that endpage does
6410      * (to match the newpage initilisation)
6411      */
6412     catDefn("Q\n", tempDefn, pd);
6413     /* Cannot discard temporary definition because there may have been
6414      * other definitions created during its creation (so it may no
6415      * longer be the topmost definition)
6416      */
6417     trimDefn(tempDefn, pd);
6418 
6419     pd->appendingMask = mainMask;
6420 
6421     /* Object number will be determined when definition written
6422      * to file (PDF_endfile)
6423      */
6424     catDefn(" 0 obj\n<<\n/Type /ExtGState\n/AIS false\n/SMask\n<<\n",
6425             defNum, pd);
6426     catDefn("/Type /Mask\n/S /Alpha\n/G\n<<\n",
6427             defNum, pd);
6428     catDefn("/Type /XObject\n/Subtype /Form\n/FormType 1\n/Group\n<<\n",
6429             defNum, pd);
6430     char colorspace[12];
6431     if (streql(pd->colormodel, "gray"))
6432         strcpy(colorspace, "/DeviceGray");
6433     else if (streql(pd->colormodel, "srgb"))
6434         strcpy(colorspace, "5 0 R");
6435     else
6436         strcpy(colorspace, "/DeviceRGB");
6437     snprintf(buf,
6438              100,
6439              "/Type /Group\n/CS %s\n/I true\n/S /Transparency\n",
6440              colorspace);
6441     catDefn(buf, defNum, pd);
6442     snprintf(buf,
6443              100,
6444              ">>\n/BBox [0 0 %d %d]\n",
6445              (int) (0.5 + pd->paperwidth), (int) (0.5 + pd->paperheight));
6446     catDefn(buf, defNum, pd);
6447 
6448     /* Note the spaces before the >> just after the endstream;
6449      * ghostscript seems to need those to avoid error (!?) */
6450     snprintf(buf,
6451              100,
6452              "/Length %d\n",
6453              (int) strlen(pd->definitions[tempDefn].str));
6454     catDefn(buf, defNum, pd);
6455     catDefn(">>\nstream\n", defNum, pd);
6456     /* Copy mask content */
6457     copyDefn(tempDefn, defNum, pd);
6458     catDefn("endstream\n  >>\n", defNum, pd);
6459     catDefn(">>\nendobj\n", defNum, pd);
6460 
6461     trimDefn(defNum, pd);
6462     return defNum;
6463 }
6464 
addMask(SEXP mask,SEXP ref,PDFDesc * pd)6465 static SEXP addMask(SEXP mask, SEXP ref, PDFDesc *pd)
6466 {
6467     SEXP newref = R_NilValue;
6468     int index;
6469 
6470     if (isNull(mask)) {
6471         /* Set NO mask */
6472         index = -1;
6473     } else {
6474         if (isNull(ref)) {
6475             /* Generate new mask */
6476             index = newMask(mask, pd);
6477             if (index >= 0) {
6478                 PROTECT(newref = allocVector(INTSXP, 1));
6479                 INTEGER(newref)[0] = index;
6480                 UNPROTECT(1);
6481             }
6482         } else {
6483             /* Reuse existing clipping path */
6484             index = INTEGER(ref)[0];
6485             newref = ref;
6486         }
6487     }
6488     pd->currentMask = index;
6489 
6490     return newref;
6491 }
6492 
6493 /***********************************************************************
6494  * Stuff for writing out PDF code
6495  */
6496 
6497 /* Write output to a variety of destinations
6498  * (buf must be preallocated)
6499  *
6500  * Check for clip path first
6501  * (because clippaths cannot be nested and
6502  *  because patterns and masks cannot be used in clippaths)
6503  *
6504  * Check for mask next
6505  * (and capture all output to mask in that case)
6506  *
6507  * Otherwise, write directly to the PDF file
6508  */
PDFwrite(char * buf,size_t size,const char * fmt,PDFDesc * pd,...)6509 static int PDFwrite(char *buf, size_t size, const char *fmt, PDFDesc *pd, ...)
6510 {
6511     int val;
6512     va_list ap;
6513 
6514     va_start(ap, pd);
6515     val = vsnprintf(buf, size, fmt, ap);
6516     va_end(ap);
6517 
6518     if (pd->appendingClipPath) {
6519         addToClipPath(buf, pd);
6520     } else if (pd->appendingPattern >= 0 &&
6521                (pd->appendingPattern > pd->appendingMask)) {
6522         addToPattern(buf, pd);
6523     } else if (pd->appendingMask >= 0) {
6524         addToMask(buf, pd);
6525     } else {
6526         fputs(buf, pd->pdffp);
6527     }
6528 
6529     return val;
6530 }
6531 
PDFwritePatternDefs(int objoffset,int excludeDef,PDFDesc * pd)6532 static void PDFwritePatternDefs(int objoffset, int excludeDef, PDFDesc *pd)
6533 {
6534     int i;
6535     char buf[100];
6536     PDFwrite(buf, 100, "/Pattern\n<<\n", pd);
6537     for (i = 0; i < pd->numDefns; i++) {
6538         if ((pd->definitions[i].type == PDFshadingPattern ||
6539              pd->definitions[i].type == PDFtilingPattern) &&
6540             i != excludeDef) {
6541             PDFwrite(buf, 100, "/Def%d %d 0 R\n", pd,
6542                      i, i + objoffset);
6543         }
6544     }
6545     PDFwrite(buf, 100, ">>\n", pd);
6546 }
6547 
PDFwriteSoftMaskDefs(int objoffset,PDFDesc * pd)6548 static void PDFwriteSoftMaskDefs(int objoffset, PDFDesc *pd)
6549 {
6550     int i;
6551     char buf[100];
6552     for (i = 0; i < pd->numDefns; i++) {
6553         if (pd->definitions[i].type == PDFsoftMask) {
6554             PDFwrite(buf, 100, "/Def%d %d 0 R\n", pd,
6555                      i, i + objoffset);
6556         }
6557     }
6558 }
6559 
PDFwriteClipPath(int i,PDFDesc * pd)6560 static void PDFwriteClipPath(int i, PDFDesc *pd)
6561 {
6562     char* buf1;
6563     char buf2[10];
6564     size_t len = strlen(pd->definitions[i].str);
6565     buf1 = malloc((len + 1)*sizeof(char));
6566 
6567     PDFwrite(buf1, len + 1, "%s", pd, pd->definitions[i].str);
6568     if (pd->fillOddEven) {
6569         PDFwrite(buf2, 10, " W* n\n", pd);
6570     } else {
6571         PDFwrite(buf2, 10, " W n\n", pd);
6572     }
6573 
6574     free(buf1);
6575 }
6576 
PDFwriteMask(int i,PDFDesc * pd)6577 static void PDFwriteMask(int i, PDFDesc *pd)
6578 {
6579     char buf[20];
6580     if (pd->current.mask != i) {
6581         PDFwrite(buf, 20, "/Def%d gs\n", pd, i);
6582         pd->current.mask = i;
6583     }
6584 }
6585 
PDFwriteDefinitions(int resourceDictOffset,PDFDesc * pd)6586 static void PDFwriteDefinitions(int resourceDictOffset, PDFDesc *pd)
6587 {
6588     for (int i = 0; i < pd->numDefns; i++) {
6589         /* All definitions written out, to keep the math somewhere near sane,
6590          * but some definitions are just empty here
6591          * (e.g., clipping paths are written inline every time
6592          *  they are used rather than here AND temporary mask content
6593          *  is still hanging around)
6594          */
6595         pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
6596         /* Definition object number */
6597         fprintf(pd->pdffp, "%d", pd->nobjs);
6598         if (pd->definitions[i].type == PDFclipPath ||
6599             pd->definitions[i].type == PDFcontent) {
6600             fprintf(pd->pdffp, " 0 obj << >>\n");
6601         } else if (pd->definitions[i].type == PDFtilingPattern) {
6602             /* Need to complete tiling pattern at end of file
6603              * to get its Resource Dictionary right
6604              */
6605             completeTiling(i, resourceDictOffset, pd);
6606             fputs(pd->definitions[i].str, pd->pdffp);
6607         } else {
6608             fputs(pd->definitions[i].str, pd->pdffp);
6609         }
6610     }
6611 }
6612 
6613 
6614 /***********************************************************************
6615  * Some stuff for recording raster images
6616  */
6617 /* Detect an image by non-NULL rasters[] */
initRasterArray(int numRasters)6618 static rasterImage* initRasterArray(int numRasters)
6619 {
6620     int i;
6621     /* why not use calloc? */
6622     rasterImage* rasters = malloc(numRasters*sizeof(rasterImage));
6623     if (rasters) {
6624 	for (i = 0; i < numRasters; i++) {
6625 	    rasters[i].raster = NULL;
6626 	}
6627     } /* else error thrown in PDFDeviceDriver */
6628     return rasters;
6629 }
6630 
6631 /* Add a raster (by making a copy)
6632  * Return value indicates whether the image is semi-transparent
6633  */
addRaster(rcolorPtr raster,int w,int h,Rboolean interpolate,PDFDesc * pd)6634 static int addRaster(rcolorPtr raster, int w, int h,
6635 		     Rboolean interpolate, PDFDesc *pd)
6636 {
6637     int i, alpha = 0;
6638     rcolorPtr newRaster;
6639 
6640     if (pd->numRasters == pd->maxRasters) {
6641 	int new = 2*pd->maxRasters;
6642 	void *tmp;
6643 	/* Do it this way so previous pointer is retained if it fails */
6644 	tmp = realloc(pd->masks, new*sizeof(int));
6645 	if(!tmp) error(_("failed to increase 'maxRaster'"));
6646 	pd->masks = tmp;
6647 	tmp = realloc(pd->rasters, new*sizeof(rasterImage));
6648 	if(!tmp) error(_("failed to increase 'maxRaster'"));
6649 	pd->rasters = tmp;
6650 	for (i = pd->maxRasters; i < new; i++) {
6651 	    pd->rasters[i].raster = NULL;
6652 	    pd->masks[i] = -1;
6653 	}
6654 	pd->maxRasters = new;
6655     }
6656 
6657     newRaster = malloc(w*h*sizeof(rcolor));
6658 
6659     if (!newRaster)
6660 	error(_("unable to allocate raster image"));
6661 
6662     for (i = 0; i < w*h; i++) {
6663 	newRaster[i] = raster[i];
6664 	if (!alpha && R_ALPHA(raster[i]) < 255) alpha = 1;
6665     }
6666     pd->rasters[pd->numRasters].raster = newRaster;
6667     pd->rasters[pd->numRasters].w = w;
6668     pd->rasters[pd->numRasters].h = h;
6669     pd->rasters[pd->numRasters].interpolate = interpolate;
6670     pd->rasters[pd->numRasters].nobj = -1; /* not yet written out */
6671     pd->rasters[pd->numRasters].nmaskobj = -1; /* not yet written out */
6672 
6673     /* If any of the pixels are not opaque, we need to add
6674      * a mask as well */
6675     if (alpha)
6676 	pd->masks[pd->numRasters] = pd->numMasks++;
6677 
6678     pd->numRasters++;
6679 
6680     return alpha;
6681 }
6682 
killRasterArray(rasterImage * rasters,int numRasters)6683 static void killRasterArray(rasterImage *rasters, int numRasters) {
6684     int i;
6685     for (i = 0; i < numRasters; i++)
6686 	if (rasters[i].raster != NULL) free(rasters[i].raster);
6687 }
6688 
6689 /* Detect a mask by masks[] >= 0 */
initMaskArray(int numRasters)6690 static int* initMaskArray(int numRasters) {
6691     int i;
6692     int* masks = malloc(numRasters*sizeof(int));
6693     if (masks) {
6694 	for (i = 0; i < numRasters; i++) masks[i] = -1;
6695     } /* else error thrown in PDFDeviceDriver */
6696     return masks;
6697 }
6698 
writeRasterXObject(rasterImage raster,int n,int mask,int maskObj,PDFDesc * pd)6699 static void writeRasterXObject(rasterImage raster, int n,
6700 			       int mask, int maskObj, PDFDesc *pd)
6701 {
6702     Bytef *buf, *buf2, *p;
6703     uLong inlen;
6704 
6705     if (streql(pd->colormodel, "gray")) {
6706 	inlen = raster.w * raster.h;
6707 	p = buf = Calloc(inlen, Bytef);
6708 	for(int i = 0; i < raster.w * raster.h; i++) {
6709 	    double r =  0.213 * R_RED(raster.raster[i])
6710 		+ 0.715 * R_GREEN(raster.raster[i])
6711 		+ 0.072 * R_BLUE(raster.raster[i]);
6712 	    *p++ = (Bytef)(r + 0.49);
6713 	}
6714     } else {
6715 	inlen = 3 * raster.w * raster.h;
6716 	p = buf = Calloc(inlen, Bytef);
6717 	for(int i = 0; i < raster.w * raster.h; i++) {
6718 	    *p++ = R_RED(raster.raster[i]);
6719 	    *p++ = R_GREEN(raster.raster[i]);
6720 	    *p++ = R_BLUE(raster.raster[i]);
6721 	}
6722     }
6723     uLong outlen = inlen;
6724     if (pd->useCompression) {
6725 	outlen = (int)(1.001*inlen + 20);
6726 	buf2 = Calloc(outlen, Bytef);
6727 	int res = compress(buf2, &outlen, buf, inlen);
6728 	if(res != Z_OK) error("internal error %d in writeRasterXObject", res);
6729 	Free(buf);
6730 	buf = buf2;
6731     }
6732     fprintf(pd->pdffp, "%d 0 obj <<\n", n);
6733     fprintf(pd->pdffp, "  /Type /XObject\n");
6734     fprintf(pd->pdffp, "  /Subtype /Image\n");
6735     fprintf(pd->pdffp, "  /Width %d\n", raster.w);
6736     fprintf(pd->pdffp, "  /Height %d\n", raster.h);
6737     if (streql(pd->colormodel, "gray"))
6738 	fprintf(pd->pdffp, "  /ColorSpace /DeviceGray\n");
6739     else if (streql(pd->colormodel, "srgb"))
6740 	fprintf(pd->pdffp, "  /ColorSpace 5 0 R\n"); /* sRGB */
6741     else
6742 	fprintf(pd->pdffp, "  /ColorSpace /DeviceRGB\n");
6743     fprintf(pd->pdffp, "  /BitsPerComponent 8\n");
6744     fprintf(pd->pdffp, "  /Length %u\n", (unsigned)
6745 	    (pd->useCompression ? outlen : 2 * outlen + 1));
6746     if (raster.interpolate)
6747 	fprintf(pd->pdffp, "  /Interpolate true\n");
6748     if (pd->useCompression)
6749 	fprintf(pd->pdffp, "  /Filter /FlateDecode\n");
6750     else
6751 	fprintf(pd->pdffp, "  /Filter /ASCIIHexDecode\n");
6752     if (mask >= 0)
6753 	fprintf(pd->pdffp, "  /SMask %d 0 R\n", maskObj);
6754     fprintf(pd->pdffp, "  >>\nstream\n");
6755     if (pd->useCompression) {
6756 	size_t res = fwrite(buf, 1, outlen, pd->pdffp);
6757 	if(res != outlen) error(_("write failed"));
6758     } else {
6759 	for(int i = 0; i < outlen; i++)
6760 	    fprintf(pd->pdffp, "%02x", buf[i]);
6761 	fprintf(pd->pdffp, ">\n");
6762     }
6763     Free(buf);
6764     fprintf(pd->pdffp, "endstream\nendobj\n");
6765 }
6766 
writeMaskXObject(rasterImage raster,int n,PDFDesc * pd)6767 static void writeMaskXObject(rasterImage raster, int n, PDFDesc *pd)
6768 {
6769     Bytef *buf, *buf2, *p;
6770     uLong inlen = raster.w * raster.h, outlen = inlen;
6771     p = buf = Calloc(outlen, Bytef);
6772     for(int i = 0; i < raster.w * raster.h; i++)
6773 	*p++ = R_ALPHA(raster.raster[i]);
6774     if (pd->useCompression) {
6775 	outlen = (uLong)(1.001*inlen + 20);
6776 	buf2 = Calloc(outlen, Bytef);
6777 	int res = compress(buf2, &outlen, buf, inlen);
6778 	if(res != Z_OK) error("internal error %d in writeRasterXObject", res);
6779 	Free(buf);
6780 	buf = buf2;
6781     }
6782     fprintf(pd->pdffp, "%d 0 obj <<\n", n);
6783     fprintf(pd->pdffp, "  /Type /XObject\n");
6784     fprintf(pd->pdffp, "  /Subtype /Image\n");
6785     fprintf(pd->pdffp, "  /Width %d\n", raster.w);
6786     fprintf(pd->pdffp, "  /Height %d\n", raster.h);
6787     /* This is not a mask but a 'soft mask' */
6788     fprintf(pd->pdffp, "  /ColorSpace /DeviceGray\n");
6789     fprintf(pd->pdffp, "  /BitsPerComponent 8\n");
6790     fprintf(pd->pdffp, "  /Length %u\n", (unsigned)
6791 	    (pd->useCompression ? outlen : 2 * outlen + 1));
6792     if (raster.interpolate)
6793 	fprintf(pd->pdffp, "  /Interpolate true\n");
6794     if (pd->useCompression)
6795 	fprintf(pd->pdffp, "  /Filter /FlateDecode\n");
6796     else
6797 	fprintf(pd->pdffp, "  /Filter /ASCIIHexDecode\n");
6798     fprintf(pd->pdffp, "  >>\nstream\n");
6799     if (pd->useCompression) {
6800 	size_t res = fwrite(buf, 1, outlen, pd->pdffp);
6801 	if(res != outlen) error(_("write failed"));
6802     } else {
6803 	for(int i = 0; i < outlen; i++)
6804 	    fprintf(pd->pdffp, "%02x", buf[i]);
6805 	fprintf(pd->pdffp, ">\n");
6806     }
6807     Free(buf);
6808     fprintf(pd->pdffp, "endstream\nendobj\n");
6809 }
6810 
6811 /***********************************************************************
6812  * Some stuff for fonts
6813  */
6814 /*
6815  * Add a graphics engine font family to the list of fonts used on a
6816  * PDF device ...
6817  *
6818  * ... AND add the font encoding to the list of encodings used on the
6819  * device (if necessary)
6820  */
6821 /*
6822  * Differs from addDeviceFont (used in PostScript device)
6823  * because we do not need to immediately write font
6824  * information to file.  In PDF, the font information is
6825  * all written at the end as part of the file footer.
6826  */
addPDFDeviceCIDfont(cidfontfamily family,PDFDesc * pd,int * fontIndex)6827 static Rboolean addPDFDeviceCIDfont(cidfontfamily family,
6828 				    PDFDesc *pd,
6829 				    int *fontIndex)
6830 {
6831     Rboolean result = FALSE;
6832     cidfontlist fontlist = addDeviceCIDFont(family, pd->cidfonts, fontIndex);
6833     if (fontlist) {
6834 	pd->cidfonts = fontlist;
6835 	result = TRUE;
6836     }
6837     return result;
6838 }
6839 
addPDFDevicefont(type1fontfamily family,PDFDesc * pd,int * fontIndex)6840 static Rboolean addPDFDevicefont(type1fontfamily family,
6841 				 PDFDesc *pd,
6842 				 int *fontIndex)
6843 {
6844     Rboolean result = FALSE;
6845     type1fontlist fontlist = addDeviceFont(family, pd->fonts, fontIndex);
6846     if (fontlist) {
6847 	int dontcare;
6848 	encodinginfo encoding =
6849 	    findDeviceEncoding(family->encoding->encpath,
6850 			       pd->encodings, &dontcare);
6851 	if (encoding) {
6852 	    pd->fonts = fontlist;
6853 	    result = TRUE;
6854 	} else {
6855 	    /*
6856 	     * The encoding should have been loaded when the font was loaded
6857 	     */
6858 	    encoding = findEncoding(family->encoding->encpath,
6859 				    pd->encodings, TRUE);
6860 	    if (!encoding) {
6861 		warning(_("corrupt loaded encodings;  font not added"));
6862 		/* NOTE: in fact the font was added */
6863 	    } else {
6864 		encodinglist enclist = addDeviceEncoding(encoding,
6865 							 pd->encodings);
6866 		if (enclist) {
6867 		    pd->fonts = fontlist;
6868 		    pd->encodings = enclist;
6869 		    result = TRUE;
6870 		} else
6871 		    warning(_("failed to record device encoding; font not added"));
6872 		    /* NOTE: in fact the font was added */
6873 	    }
6874 	}
6875     }
6876     return result;
6877 }
6878 
PDFcleanup(int stage,PDFDesc * pd)6879 static void PDFcleanup(int stage, PDFDesc *pd) {
6880     switch (stage) {
6881     case 7: /* Allocated defns */
6882         killDefinitions(pd);
6883     case 6: /* Allocated masks */
6884 	free(pd->masks);
6885     case 5: /* Allocated rasters */
6886 	free(pd->rasters);
6887     case 4: /* Allocated fonts */
6888 	freeDeviceFontList(pd->fonts);
6889 	freeDeviceCIDFontList(pd->cidfonts);
6890 	freeDeviceEncList(pd->encodings);
6891 	pd->fonts = NULL;
6892 	pd->cidfonts = NULL;
6893 	pd->encodings = NULL;
6894     case 3: /* Allocated pageobj */
6895 	free(pd->pageobj);
6896     case 2: /* Allocated pos */
6897 	free(pd->pos);
6898     case 1: /* Allocated PDFDesc */
6899 	free(pd);
6900     }
6901 }
6902 
6903 Rboolean
PDFDeviceDriver(pDevDesc dd,const char * file,const char * paper,const char * family,const char ** afmpaths,const char * encoding,const char * bg,const char * fg,double width,double height,double ps,int onefile,int pagecentre,const char * title,SEXP fonts,int versionMajor,int versionMinor,const char * colormodel,int dingbats,int useKern,Rboolean fillOddEven,Rboolean useCompression)6904 PDFDeviceDriver(pDevDesc dd, const char *file, const char *paper,
6905 		const char *family, const char **afmpaths,
6906 		const char *encoding,
6907 		const char *bg, const char *fg, double width, double height,
6908 		double ps, int onefile, int pagecentre,
6909 		const char *title, SEXP fonts,
6910 		int versionMajor, int versionMinor,
6911 		const char *colormodel, int dingbats, int useKern,
6912 		Rboolean fillOddEven, Rboolean useCompression)
6913 {
6914     /* If we need to bail out with some sort of "error" */
6915     /* then we must free(dd) */
6916 
6917     int i, gotFont;
6918     double xoff = 0.0, yoff = 0.0, pointsize;
6919     rcolor setbg, setfg;
6920     encodinginfo enc;
6921     encodinglist enclist;
6922     type1fontfamily font;
6923     cidfontfamily cidfont = NULL;
6924 
6925     PDFDesc *pd;
6926 
6927     /* Check and extract the device parameters */
6928 
6929     /* 'file' could be NULL */
6930     if(file && strlen(file) > PATH_MAX - 1) {
6931 	/* not yet created PDFcleanup(0, pd); */
6932 	free(dd);
6933 	error(_("filename too long in %s()"), "pdf");
6934     }
6935 
6936     /* allocate new PDF device description */
6937     if (!(pd = (PDFDesc *) malloc(sizeof(PDFDesc)))) {
6938 	free(dd);
6939 	error(_("memory allocation problem in %s()"), "pdf");
6940     }
6941     /* from here on, if need to bail out with "error", must also
6942        free(pd) */
6943 
6944     pd->versionMajor = versionMajor;
6945     pd->versionMinor = versionMinor;
6946     /* Precaution: should be initialized in PDF_newpage, but package
6947        PerformanceAnalytics manages to call PDF_Clip without.  */
6948     pd->inText = FALSE;
6949 
6950     /* This is checked at the start of every page.  We typically have
6951        three objects per page plus one or two for each raster image,
6952        so this is an ample initial allocation.
6953      */
6954     pd->max_nobjs = 2000;
6955     pd->pos = (int *) calloc(pd->max_nobjs, sizeof(int));
6956     if(!pd->pos) {
6957 	PDFcleanup(1, pd);
6958 	free(dd);
6959 	error("cannot allocate pd->pos");
6960     }
6961     /* This one is dynamic: initial allocation */
6962     pd->pagemax = 100;
6963     pd->pageobj = (int *) calloc(pd->pagemax, sizeof(int));
6964     if(!pd->pageobj) {
6965 	PDFcleanup(2, pd);
6966 	free(dd);
6967 	error("cannot allocate pd->pageobj");
6968     }
6969 
6970 
6971     /* initialize PDF device description */
6972     /* 'file' could be NULL */
6973     if (file)
6974         strcpy(pd->filename, file);
6975     else
6976         strcpy(pd->filename, "nullPDF");
6977     strcpy(pd->papername, paper);
6978     strncpy(pd->title, title, 1023);
6979     pd->title[1023] = '\0';
6980     memset(pd->fontUsed, 0, 100*sizeof(Rboolean));
6981     if (streql(colormodel, "grey")) strcpy(pd->colormodel, "gray");
6982     else {strncpy(pd->colormodel, colormodel, 29); pd->colormodel[29] = '\0';}
6983     pd->dingbats = (dingbats != 0);
6984     pd->useKern = (useKern != 0);
6985     pd->fillOddEven = fillOddEven;
6986     pd->useCompression = useCompression;
6987     if(useCompression && pd->versionMajor == 1 && pd->versionMinor < 2) {
6988 	pd->versionMinor = 2;
6989 	warning(_("increasing the PDF version to 1.2"));
6990     }
6991 
6992     pd->width = width;
6993     pd->height = height;
6994 
6995     if (file)
6996         pd->offline = FALSE;
6997     else
6998         pd->offline = TRUE;
6999 
7000     if(strlen(encoding) > PATH_MAX - 1) {
7001 	PDFcleanup(3, pd);
7002 	free(dd);
7003 	error(_("encoding path is too long in %s()"), "pdf");
7004     }
7005     /*
7006      * Load the default encoding AS THE FIRST ENCODING FOR THIS DEVICE.
7007      *
7008      * encpath MUST NOT BE "default"
7009      */
7010     pd->encodings = NULL;
7011     if (!(enc = findEncoding(encoding, pd->encodings, TRUE)))
7012 	enc = addEncoding(encoding, 1);
7013     if (enc && (enclist = addDeviceEncoding(enc,
7014 					    pd->encodings))) {
7015 	pd->encodings = enclist;
7016     } else {
7017 	PDFcleanup(3, pd);
7018 	free(dd);
7019 	error(_("failed to load default encoding"));
7020     }
7021 
7022     /*****************************
7023      * Load fonts
7024      *****************************/
7025     pd->fonts = NULL;
7026     pd->cidfonts = NULL;
7027 
7028     gotFont = 0;
7029     /*
7030      * If user specified afms then assume the font hasn't been loaded
7031      * Could lead to redundant extra loading of a font, but not often(?)
7032      */
7033     if (!strcmp(family, "User")) {
7034 	font = addDefaultFontFromAFMs(encoding, afmpaths, 0, pd->encodings);
7035     } else {
7036 	/*
7037 	 * Otherwise, family is a device-independent font family.
7038 	 * One of the elements of pdfFonts().
7039 	 * NOTE this is the first font loaded on this device!
7040 	 */
7041 	/*
7042 	 * Check first whether this font has been loaded
7043 	 * in this R session
7044 	 */
7045 	font = findLoadedFont(family, encoding, TRUE);
7046 	cidfont = findLoadedCIDFont(family, TRUE);
7047 	if (!(font || cidfont)) {
7048 	    /*
7049 	     * If the font has not been loaded yet, load it.
7050 	     *
7051 	     * The family SHOULD be in the font database to get this far.
7052 	     * (checked at R level in postscript() in postscript.R)
7053 	     */
7054 	    if (isType1Font(family, PDFFonts, NULL)) {
7055 		font = addFont(family, TRUE, pd->encodings);
7056 	    } else if (isCIDFont(family, PDFFonts, NULL)) {
7057 		cidfont = addCIDFont(family, TRUE);
7058 	    } else {
7059 		/*
7060 		 * Should NOT get here.
7061 		 */
7062 		error(_("invalid font type"));
7063 	    }
7064 	}
7065     }
7066     if (font || cidfont) {
7067 	/*
7068 	 * At this point the font is loaded, so add it to the
7069 	 * device's list of fonts.
7070 	 */
7071 	if (!strcmp(family, "User") ||
7072 	    isType1Font(family, PDFFonts, NULL)) {
7073 	    addPDFDevicefont(font, pd, &gotFont);
7074 	    /* NOTE: should check result, encoding may not have been found */
7075 	    pd->defaultFont = pd->fonts->family;
7076 	    pd->defaultCIDFont = NULL;
7077 	} else /* (isCIDFont(family, PDFFonts)) */ {
7078 	    addPDFDeviceCIDfont(cidfont, pd, &gotFont);
7079 	    pd->defaultFont = NULL;
7080 	    pd->defaultCIDFont = pd->cidfonts->cidfamily;
7081 	}
7082     }
7083     if (!gotFont) {
7084 	PDFcleanup(4, pd);
7085 	free(dd);
7086 	error(_("failed to initialise default PDF font"));
7087     }
7088 
7089     /*
7090      * Load the font names sent in via the fonts arg
7091      * NOTE that these are the font names specified at the
7092      * R-level, NOT the translated font names.
7093      */
7094     if (!isNull(fonts)) {
7095 	int i, dontcare, gotFonts = 0, nfonts = LENGTH(fonts);
7096 	for (i = 0; i < nfonts; i++) {
7097 	    int index, cidindex;
7098 	    const char *name = CHAR(STRING_ELT(fonts, i));
7099 	    if (findDeviceFont(name, pd->fonts, &index) ||
7100 		findDeviceCIDFont(name, pd->cidfonts, &cidindex))
7101 		gotFonts++;
7102 	    else {
7103 		/*
7104 		 * Check whether the font is loaded and, if not,
7105 		 * load it.
7106 		 */
7107 		font = findLoadedFont(name, encoding, TRUE);
7108 		cidfont = findLoadedCIDFont(name, TRUE);
7109 		if (!(font || cidfont)) {
7110 		    if (isType1Font(name, PDFFonts, NULL)) {
7111 			font = addFont(name, TRUE, pd->encodings);
7112 		    } else if (isCIDFont(name, PDFFonts, NULL)) {
7113 			cidfont = addCIDFont(name, TRUE);
7114 		    } else {
7115 			/*
7116 			 * Should NOT get here.
7117 			 */
7118 			error(_("invalid font type"));
7119 		    }
7120 		}
7121 		/*
7122 		 * Once the font is loaded, add it to the device's
7123 		 * list of fonts.
7124 		 */
7125 		if (font || cidfont) {
7126 		    if (isType1Font(name, PDFFonts, NULL)) {
7127 			if (addPDFDevicefont(font, pd, &dontcare)) {
7128 			    gotFonts++;
7129 			}
7130 		    } else /* (isCIDFont(family, PDFFonts)) */ {
7131 			if (addPDFDeviceCIDfont(cidfont, pd, &dontcare)) {
7132 			    gotFonts++;
7133 			}
7134 		    }
7135 		}
7136 	    }
7137 	}
7138 	if (gotFonts < nfonts) {
7139 	    PDFcleanup(4, pd);
7140 	    free(dd);
7141 	    error(_("failed to initialise additional PDF fonts"));
7142 	}
7143     }
7144     /*****************************
7145      * END Load fonts
7146      *****************************/
7147 
7148     pd->numRasters = pd->writtenRasters = pd->fileRasters = 0;
7149     pd->maxRasters = 64; /* dynamic */
7150     pd->rasters = initRasterArray(pd->maxRasters);
7151     if (!pd->rasters) {
7152 	PDFcleanup(4, pd);
7153 	free(dd);
7154 	error(_("failed to allocate rasters"));
7155     }
7156     pd->numMasks = 0;
7157     pd->masks = initMaskArray(pd->maxRasters);
7158     if (!pd->masks) {
7159 	PDFcleanup(5, pd);
7160 	free(dd);
7161 	error(_("failed to allocate masks"));
7162     }
7163 
7164     pd->numDefns = 0;
7165     pd->maxDefns = 64;
7166     initDefinitions(pd);
7167     if (!pd->definitions) {
7168         PDFcleanup(6, pd);
7169         free(dd);
7170 	error(_("failed to allocate definitions"));
7171     }
7172     pd->appendingClipPath = FALSE;
7173     pd->appendingMask = -1;
7174     pd->currentMask = -1;
7175     pd->appendingPattern = -1;
7176 
7177     setbg = R_GE_str2col(bg);
7178     setfg = R_GE_str2col(fg);
7179 
7180     /*
7181      * Initialise all alphas to -1
7182      */
7183     pd->usedAlpha = FALSE;
7184     for (i = 0; i < 256; i++) {
7185 	pd->colAlpha[i] = -1;
7186 	pd->fillAlpha[i] = -1;
7187     }
7188 
7189     /* Deal with paper and plot size and orientation */
7190 
7191     if(!strcmp(pd->papername, "Default") ||
7192        !strcmp(pd->papername, "default")) {
7193 	SEXP s = STRING_ELT(GetOption1(install("papersize")), 0);
7194 	if(s != NA_STRING && strlen(CHAR(s)) > 0)
7195 	    strcpy(pd->papername, CHAR(s));
7196 	else strcpy(pd->papername, "a4");
7197     }
7198     if(!strcmp(pd->papername, "A4") ||
7199        !strcmp(pd->papername, "a4")) {
7200 	pd->pagewidth  = 21.0 / 2.54;
7201 	pd->pageheight = 29.7  /2.54;
7202     }
7203     else if(!strcmp(pd->papername, "A4r") ||
7204        !strcmp(pd->papername, "a4r")) {
7205 	pd->pageheight = 21.0 / 2.54;
7206 	pd->pagewidth  = 29.7  /2.54;
7207     }
7208     else if(!strcmp(pd->papername, "Letter") ||
7209 	    !strcmp(pd->papername, "letter") ||
7210 	    !strcmp(pd->papername, "US") ||
7211 	    !strcmp(pd->papername, "us")) {
7212 	pd->pagewidth  =  8.5;
7213 	pd->pageheight = 11.0;
7214     }
7215     else if(!strcmp(pd->papername, "USr") ||
7216 	    !strcmp(pd->papername, "usr")) {
7217 	pd->pageheight =  8.5;
7218 	pd->pagewidth  = 11.0;
7219     }
7220     else if(!strcmp(pd->papername, "Legal") ||
7221 	    !strcmp(pd->papername, "legal")) {
7222 	pd->pagewidth  =  8.5;
7223 	pd->pageheight = 14.0;
7224     }
7225     else if(!strcmp(pd->papername, "Executive") ||
7226 	    !strcmp(pd->papername, "executive")) {
7227 	pd->pagewidth  =  7.25;
7228 	pd->pageheight = 10.5;
7229     }
7230     else if(!strcmp(pd->papername, "special")) {
7231       pd->pagewidth  =  width;
7232       pd->pageheight = height;
7233     }
7234     else {
7235 	char errbuf[strlen(pd->papername) + 1];
7236 	strcpy(errbuf, pd->papername);
7237 	PDFcleanup(7, pd);
7238 	free(dd);
7239 	error(_("invalid paper type '%s' (pdf)"), errbuf);
7240     }
7241     pd->pagecentre = pagecentre;
7242     pd->paperwidth = (int)(72 * pd->pagewidth);
7243     pd->paperheight = (int)(72 * pd->pageheight);
7244     if(strcmp(pd->papername, "special"))
7245     {
7246 	if(pd->width < 0.1 || pd->width > pd->pagewidth-0.5)
7247 	    pd->width = pd->pagewidth-0.5;
7248 	if(pd->height < 0.1 || pd->height > pd->pageheight-0.5)
7249 	    pd->height = pd->pageheight-0.5;
7250     }
7251     if(pagecentre)
7252     {
7253 	xoff = (pd->pagewidth - pd->width)/2.0;
7254 	yoff = (pd->pageheight - pd->height)/2.0;
7255     } else {
7256 	xoff = yoff = 0.0;
7257     }
7258 
7259     pointsize = floor(ps);
7260     if(R_TRANSPARENT(setbg) && R_TRANSPARENT(setfg)) {
7261 	PDFcleanup(7, pd);
7262 	free(dd);
7263 	error(_("invalid foreground/background color (pdf)"));
7264     }
7265 
7266     pd->onefile = onefile;
7267     pd->maxpointsize = (int)(72.0 * ((pd->pageheight > pd->pagewidth) ?
7268 				     pd->pageheight : pd->pagewidth));
7269     pd->pageno = pd->fileno = 0;
7270     /* Base Pointsize */
7271     /* Nominal Character Sizes in Pixels */
7272     /* Only right for 12 point font. */
7273     /* Max pointsize suggested by Peter Dalgaard */
7274 
7275     if(pointsize < 6.0) pointsize = 6.0;
7276     if(pointsize > pd->maxpointsize) pointsize = pd->maxpointsize;
7277     dd->startps = pointsize;
7278     dd->startlty = 0;
7279     dd->startfont = 1;
7280     dd->startfill = setbg;
7281     dd->startcol = setfg;
7282     dd->startgamma = 1;
7283 
7284     /* Set graphics parameters that must be set by device driver. */
7285     /* Page dimensions in points. */
7286 
7287     dd->left = 72 * xoff;			/* left */
7288     dd->right = 72 * (xoff + pd->width);	/* right */
7289     dd->bottom = 72 * yoff;			/* bottom */
7290     dd->top = 72 * (yoff + pd->height);	/* top */
7291     dd->clipLeft = dd->left; dd->clipRight = dd->right;
7292     dd->clipBottom = dd->bottom; dd->clipTop = dd->top;
7293 
7294     dd->cra[0] = 0.9 * pointsize;
7295     dd->cra[1] = 1.2 * pointsize;
7296 
7297     /* Character Addressing Offsets */
7298     /* These offsets should center a single */
7299     /* plotting character over the plotting point. */
7300     /* Pure guesswork and eyeballing ... */
7301 
7302     dd->xCharOffset =  0.4900;
7303     dd->yCharOffset =  0.3333;
7304     dd->yLineBias = 0.2;
7305 
7306     /* Inches per Raster Unit */
7307     /* 1200 dpi */
7308     dd->ipr[0] = 1.0/72.0;
7309     dd->ipr[1] = 1.0/72.0;
7310 
7311     dd->canClip = TRUE;
7312     dd->canHAdj = 0;
7313     dd->canChangeGamma = FALSE;
7314 
7315     /*	Start the driver */
7316     PDF_Open(dd, pd); /* errors on failure */
7317 
7318     dd->close      = PDF_Close;
7319     dd->size     = PDF_Size;
7320     dd->newPage    = PDF_NewPage;
7321     dd->clip	      = PDF_Clip;
7322     dd->text	      = PDF_Text;
7323     dd->strWidth   = PDF_StrWidth;
7324     dd->metricInfo = PDF_MetricInfo;
7325     dd->rect	      = PDF_Rect;
7326     dd->path	      = PDF_Path;
7327     dd->raster	      = PDF_Raster;
7328     dd->circle     = PDF_Circle;
7329     dd->line	      = PDF_Line;
7330     dd->polygon    = PDF_Polygon;
7331     dd->polyline   = PDF_Polyline;
7332     /* dd->locator    = PDF_Locator;
7333        dd->mode	      = PDF_Mode; */
7334     dd->hasTextUTF8   = TRUE;
7335     dd->textUTF8       = PDF_TextUTF8;
7336     dd->strWidthUTF8   = PDF_StrWidthUTF8;
7337     dd->useRotatedTextInContour = TRUE;
7338     dd->haveTransparency = 2;
7339     dd->haveTransparentBg = 3;
7340     dd->haveRaster = 2;
7341     dd->setPattern      = PDF_setPattern;
7342     dd->releasePattern  = PDF_releasePattern;
7343     dd->setClipPath     = PDF_setClipPath;
7344     dd->releaseClipPath = PDF_releaseClipPath;
7345     dd->setMask         = PDF_setMask;
7346     dd->releaseMask     = PDF_releaseMask;
7347 
7348     dd->deviceSpecific = (void *) pd;
7349     dd->displayListOn = FALSE;
7350     dd->deviceVersion = R_GE_definitions;
7351     return TRUE;
7352 }
7353 
7354 
7355 /*
7356  * Search through the alphas used so far and return
7357  * existing index if there is one.
7358  * Otherwise, add alpha to the list and return new index
7359  */
alphaIndex(int alpha,short * alphas)7360 static int alphaIndex(int alpha, short *alphas) {
7361     int i, found = 0;
7362     for (i = 0; i < 256 && !found; i++) {
7363 	if (alphas[i] < 0) {
7364 	    alphas[i] = (short) alpha;
7365 	    found = 1;
7366 	}
7367 	else if (alpha == alphas[i])
7368 	    found = 1;
7369     }
7370     if (!found)
7371 	error(_("invalid 'alpha' value in PDF"));
7372     return i;
7373 }
7374 
7375 /*
7376  * colAlpha graphics state parameter dictionaries are named
7377  * /GS1 to /GS256
7378  * fillAlpha graphics state parameter dictionaries are named
7379  * /GS257 to /GS512
7380  */
colAlphaIndex(int alpha,PDFDesc * pd)7381 static int colAlphaIndex(int alpha, PDFDesc *pd) {
7382     return alphaIndex(alpha, pd->colAlpha);
7383 }
7384 
fillAlphaIndex(int alpha,PDFDesc * pd)7385 static int fillAlphaIndex(int alpha, PDFDesc *pd) {
7386     return alphaIndex(alpha, pd->fillAlpha) + 256;
7387 }
7388 
7389 /*
7390  * Does the version support alpha transparency?
7391  * As from R 2.4.0 bump the version number so it does.
7392  */
alphaVersion(PDFDesc * pd)7393 static void alphaVersion(PDFDesc *pd) {
7394     if(pd->versionMajor == 1 && pd->versionMinor < 4) {
7395 	pd->versionMinor  = 4;
7396 	warning(_("increasing the PDF version to 1.4"));
7397     }
7398     pd->usedAlpha = TRUE;
7399 }
7400 
PDF_SetLineColor(int color,pDevDesc dd)7401 static void PDF_SetLineColor(int color, pDevDesc dd)
7402 {
7403     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
7404     char buf[100];
7405 
7406     if(color != pd->current.col) {
7407 	unsigned int alpha = R_ALPHA(color);
7408 	if (0 < alpha && alpha < 255) alphaVersion(pd);
7409 	if (pd->usedAlpha) {
7410 	    /*
7411 	     * Apply graphics state parameter dictionary
7412 	     * to set alpha
7413 	     */
7414 	    PDFwrite(buf, 100, "/GS%i gs\n", pd, colAlphaIndex(alpha, pd));
7415 	}
7416 	if(streql(pd->colormodel, "gray")) {
7417 	    double r = R_RED(color)/255.0, g = R_GREEN(color)/255.0,
7418 		b = R_BLUE(color)/255.0;
7419 	    /* weights from C-9 of
7420 	       http://www.faqs.org/faqs/graphics/colorspace-faq/
7421 	       Those from C-11 might be more appropriate.
7422 	    */
7423 	    PDFwrite(buf, 100, "%.3f G\n", pd, (0.213*r+0.715*g+0.072*b));
7424 	} else if(streql(pd->colormodel, "cmyk")) {
7425 	    double r = R_RED(color)/255.0, g = R_GREEN(color)/255.0,
7426 		b = R_BLUE(color)/255.0;
7427 	    double c = 1.0-r, m = 1.0-g, y = 1.0-b, k = c;
7428 	    k = fmin2(k, m);
7429 	    k = fmin2(k, y);
7430 	    if(k == 1.0) c = m = y = 0.0;
7431 	    else { c = (c-k)/(1-k); m = (m-k)/(1-k); y = (y-k)/(1-k); }
7432 	    PDFwrite(buf, 100, "%.3f %.3f %.3f %.3f K\n", pd, c, m, y, k);
7433 	} else if(streql(pd->colormodel, "rgb")) {
7434 	    PDFwrite(buf, 100, "%.3f %.3f %.3f RG\n", pd,
7435 		    R_RED(color)/255.0,
7436 		    R_GREEN(color)/255.0,
7437 		    R_BLUE(color)/255.0);
7438 	} else {
7439 	    if (!streql(pd->colormodel, "srgb"))
7440 		warning(_("unknown 'colormodel', using 'srgb'"));
7441 	    if (!pd->current.srgb_bg) {
7442 		PDFwrite(buf, 100, "/sRGB CS\n", pd);
7443 		pd->current.srgb_bg = 1;
7444 	    }
7445 	    PDFwrite(buf, 100, "%.3f %.3f %.3f SCN\n", pd,
7446 		    R_RED(color)/255.0,
7447 		    R_GREEN(color)/255.0,
7448 		    R_BLUE(color)/255.0);
7449 	}
7450 	pd->current.col = color;
7451     }
7452 }
7453 
PDF_SetFill(int color,pDevDesc dd)7454 static void PDF_SetFill(int color, pDevDesc dd)
7455 {
7456     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
7457     char buf[100];
7458     if(color != pd->current.fill) {
7459 	unsigned int alpha = R_ALPHA(color);
7460 	if (0 < alpha && alpha < 255) alphaVersion(pd);
7461 	if (pd->usedAlpha) {
7462 	    /*
7463 	     * Apply graphics state parameter dictionary
7464 	     * to set alpha
7465 	     */
7466 	    PDFwrite(buf, 100, "/GS%i gs\n", pd, fillAlphaIndex(alpha, pd));
7467 	}
7468 	if(streql(pd->colormodel, "gray")) {
7469 	    double r = R_RED(color)/255.0, g = R_GREEN(color)/255.0,
7470 		b = R_BLUE(color)/255.0;
7471 	    PDFwrite(buf, 100, "%.3f g\n", pd, (0.213*r+0.715*g+0.072*b));
7472 	} else if(streql(pd->colormodel, "cmyk")) {
7473 	    double r = R_RED(color)/255.0, g = R_GREEN(color)/255.0,
7474 		b = R_BLUE(color)/255.0;
7475 	    double c = 1.0-r, m = 1.0-g, y = 1.0-b, k = c;
7476 	    k = fmin2(k, m);
7477 	    k = fmin2(k, y);
7478 	    if(k == 1.0) c = m = y = 0.0;
7479 	    else { c = (c-k)/(1-k); m = (m-k)/(1-k); y = (y-k)/(1-k); }
7480 	    PDFwrite(buf, 100, "%.3f %.3f %.3f %.3f k\n", pd, c, m, y, k);
7481 	} else if(streql(pd->colormodel, "rgb")) {
7482 	    PDFwrite(buf, 100, "%.3f %.3f %.3f rg\n", pd,
7483 		    R_RED(color)/255.0,
7484 		    R_GREEN(color)/255.0,
7485 		    R_BLUE(color)/255.0);
7486 	} else {
7487 	    if (!streql(pd->colormodel, "srgb"))
7488 		warning(_("unknown 'colormodel', using 'srgb'"));
7489 	    if (!pd->current.srgb_fg) {
7490 		PDFwrite(buf, 100, "/sRGB cs\n", pd);
7491 		pd->current.srgb_fg = 1;
7492 	    }
7493 	    PDFwrite(buf, 100, "%.3f %.3f %.3f scn\n", pd,
7494 		    R_RED(color)/255.0,
7495 		    R_GREEN(color)/255.0,
7496 		    R_BLUE(color)/255.0);
7497 	}
7498 
7499 	pd->current.fill = color;
7500     }
7501     /* Fill set means pattern fill not set */
7502     pd->current.patternfill = -1;
7503 }
7504 
PDF_SetPatternFill(SEXP ref,pDevDesc dd)7505 static void PDF_SetPatternFill(SEXP ref, pDevDesc dd)
7506 {
7507     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
7508     int patternIndex = INTEGER(ref)[0];
7509 
7510     if (pd->current.patternfill != patternIndex) {
7511         char buf[100];
7512         if (length(ref) > 1) {
7513             /* Define soft mask as well as pattern */
7514             int maskIndex = INTEGER(ref)[1];
7515             PDFwrite(buf, 100,
7516                      "/Def%d gs /Pattern cs /Def%d scn\n",
7517                      pd,
7518                      maskIndex,
7519                      patternIndex);
7520         } else {
7521             PDFwrite(buf, 100,
7522                      "/Pattern cs /Def%d scn\n",
7523                      pd,
7524                      patternIndex);
7525         }
7526         pd->current.patternfill = patternIndex;
7527     }
7528     /* Pattern fill set means fill not set */
7529     pd->current.fill = INVALID_COL;
7530 }
7531 
PDFSetLineEnd(PDFDesc * pd,R_GE_lineend lend)7532 static void PDFSetLineEnd(PDFDesc *pd, R_GE_lineend lend)
7533 {
7534     char buf[10];
7535     int lineend = 1; /* -Wall */
7536     switch (lend) {
7537     case GE_ROUND_CAP:
7538 	lineend = 1;
7539 	break;
7540     case GE_BUTT_CAP:
7541 	lineend = 0;
7542 	break;
7543     case GE_SQUARE_CAP:
7544 	lineend = 2;
7545 	break;
7546     default:
7547 	error(_("invalid line end"));
7548     }
7549     PDFwrite(buf, 10, "%1d J\n", pd, lineend);
7550 }
7551 
PDFSetLineJoin(PDFDesc * pd,R_GE_linejoin ljoin)7552 static void PDFSetLineJoin(PDFDesc *pd, R_GE_linejoin ljoin)
7553 {
7554     char buf[10];
7555     int linejoin = 1; /* -Wall */
7556     switch (ljoin) {
7557     case GE_ROUND_JOIN:
7558 	linejoin = 1;
7559 	break;
7560     case GE_MITRE_JOIN:
7561 	linejoin = 0;
7562 	break;
7563     case GE_BEVEL_JOIN:
7564 	linejoin = 2;
7565 	break;
7566     default:
7567 	error(_("invalid line join"));
7568     }
7569     PDFwrite(buf, 10, "%1d j\n", pd, linejoin);
7570 }
7571 
7572 /* Note that the line texture is scaled by the line width.*/
PDFSetLineTexture(PDFDesc * pd,const char * dashlist,int nlty,double lwd,int lend)7573 static void PDFSetLineTexture(PDFDesc *pd, const char *dashlist, int nlty,
7574 			      double lwd, int lend)
7575 {
7576     double dash[8], a = (lend == GE_BUTT_CAP) ? 0. : 1.;
7577     int i;
7578     Rboolean allzero = TRUE;
7579     char buf[10];
7580     for (i = 0; i < nlty; i++) {
7581 	dash[i] = lwd *
7582 	    ((i % 2) ? (dashlist[i] + a)
7583 	     : ((nlty == 1 && dashlist[i] == 1.) ? 1. : dashlist[i] - a) );
7584 	if (dash[i] < 0) dash[i] = 0;
7585         if (dash[i] > .01) allzero = FALSE;
7586     }
7587     PDFwrite(buf, 10, "[", pd);
7588     if (!allzero) {
7589         for (i = 0; i < nlty; i++) {
7590             PDFwrite(buf, 10," %.2f", pd, dash[i]);
7591         }
7592     }
7593     PDFwrite(buf, 10, "] 0 d\n", pd);
7594 }
7595 
PDF_SetLineStyle(const pGEcontext gc,pDevDesc dd)7596 static void PDF_SetLineStyle(const pGEcontext gc, pDevDesc dd)
7597 {
7598     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
7599     char dashlist[8];
7600     int i;
7601     int newlty = gc->lty;
7602     double linewidth;
7603     double newlwd = gc->lwd;
7604     R_GE_lineend newlend = gc->lend;
7605     R_GE_linejoin newljoin = gc->ljoin;
7606     double newlmitre = gc->lmitre;
7607     char buf[100];
7608 
7609     if (pd->current.lty != newlty || pd->current.lwd != newlwd ||
7610 	pd->current.lend != newlend) {
7611 	pd->current.lwd = newlwd;
7612 	pd->current.lty = newlty;
7613         linewidth = newlwd * 0.75;
7614         /* Must not allow line width to be zero */
7615         if (linewidth < .01)
7616             linewidth = .01;
7617 	PDFwrite(buf, 100, "%.2f w\n", pd, linewidth);
7618 	/* process lty : */
7619 	for(i = 0; i < 8 && newlty & 15 ; i++) {
7620 	    dashlist[i] = newlty & 15;
7621 	    newlty = newlty >> 4;
7622 	}
7623 	PDFSetLineTexture(pd, dashlist, i, newlwd * 0.75, newlend);
7624     }
7625     if (pd->current.lend != newlend) {
7626 	pd->current.lend = newlend;
7627 	PDFSetLineEnd(pd, newlend);
7628     }
7629     if (pd->current.ljoin != newljoin) {
7630 	pd->current.ljoin = newljoin;
7631 	PDFSetLineJoin(pd, newljoin);
7632     }
7633     if (pd->current.lmitre != newlmitre) {
7634 	pd->current.lmitre = newlmitre;
7635 	PDFwrite(buf, 100, "%.2f M\n", pd, newlmitre);
7636     }
7637 }
7638 
7639 /* This was an optimization that has effectively been disabled in
7640    2.8.0, to avoid repeatedly going in and out of text mode.  Howver,
7641    Acrobat puts all text rendering calls in BT...ET into a single
7642    transparency group, and other viewers do not.  So for consistent
7643    rendering we put each text() call into a separate group.
7644 */
texton(PDFDesc * pd)7645 static void texton(PDFDesc *pd)
7646 {
7647     char buf[10];
7648     PDFwrite(buf, 10, "BT\n", pd);
7649     pd->inText = TRUE;
7650 }
7651 
textoff(PDFDesc * pd)7652 static void textoff(PDFDesc *pd)
7653 {
7654     char buf[10];
7655     PDFwrite(buf, 10, "ET\n", pd);
7656     pd->inText = FALSE;
7657 }
7658 
PDF_Encodings(PDFDesc * pd)7659 static void PDF_Encodings(PDFDesc *pd)
7660 {
7661     encodinglist enclist = pd->encodings;
7662 
7663     while (enclist) {
7664 	encodinginfo encoding = enclist->encoding;
7665 	pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
7666 
7667 	fprintf(pd->pdffp, "%d 0 obj\n<<\n/Type /Encoding ", pd->nobjs);
7668 	if (strcmp(encoding->name, "WinAnsiEncoding") == 0 ||
7669 	    strcmp(encoding->name, "MacRomanEncoding") == 0 ||
7670 	    strcmp(encoding->name, "PDFDocEncoding") == 0) {
7671 	    fprintf(pd->pdffp, "/BaseEncoding /%s\n", encoding->name);
7672 	    fprintf(pd->pdffp, "/Differences [ 45/minus ]\n");
7673 	} else if (strcmp(encoding->name, "ISOLatin1Encoding") == 0) {
7674 	    fprintf(pd->pdffp, "/BaseEncoding /WinAnsiEncoding\n");
7675 	    fprintf(pd->pdffp, "/Differences [ 45/minus 96/quoteleft\n144/dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent\n/dieresis /.notdef /ring /cedilla /.notdef /hungarumlaut /ogonek /caron /space]\n");
7676 	} else {
7677 	    int enc_first;
7678 	    int c = 0;
7679 	    int len;
7680 	    char buf[128];
7681 	    for(enc_first=0;encoding->enccode[enc_first]!='['   &&
7682 			    encoding->enccode[enc_first]!='\0' ;enc_first++);
7683 	    if (enc_first >= strlen(encoding->enccode))
7684 		enc_first=0;
7685 	    fprintf(pd->pdffp, "/BaseEncoding /PDFDocEncoding\n");
7686 	    fprintf(pd->pdffp, "/Differences [\n");
7687 	    while(encoding->enccode[enc_first]) {
7688 		switch (encoding->enccode[enc_first]) {
7689 		  case ' ':
7690 		  case '\t':
7691 		  case '\n':
7692 		  case '[':
7693 		  case ']':
7694 		    enc_first++;
7695 		    continue;
7696 		}
7697 		for(len=0;
7698 		    (encoding->enccode[enc_first+len]!=' ')   &&
7699 		    (encoding->enccode[enc_first+len]!=']')   &&
7700 		    (encoding->enccode[enc_first+len]!='\t')   &&
7701 		    (encoding->enccode[enc_first+len]!='\0')   &&
7702 		    (encoding->enccode[enc_first+len]!='\n') ;
7703 		    len++);
7704 		memcpy(buf,encoding->enccode + enc_first , len);
7705 		buf[len]='\0';
7706 		fprintf(pd->pdffp, " %d%s", c, buf);
7707 		if ( (c+1) % 8 == 0 ) fprintf(pd->pdffp, "\n");
7708 		c++;
7709 		enc_first+=len;
7710 	    }
7711 	    fprintf(pd->pdffp, "\n]\n");
7712 	}
7713 	fprintf(pd->pdffp, ">>\nendobj\n");
7714 
7715 	enclist = enclist->next;
7716     }
7717 }
7718 
7719 /* Read sRGB profile from icc/srgb.flate
7720  * HexCode original from
7721  * http://code.google.com/p/ghostscript/source/browse/trunk/gs/iccprofiles/srgb.icc
7722  */
7723 #define BUFSIZE2 10000
PDFwritesRGBcolorspace(PDFDesc * pd)7724 static void PDFwritesRGBcolorspace(PDFDesc *pd)
7725 {
7726     char buf[BUFSIZE2];
7727     FILE *fp;
7728 
7729     snprintf(buf, BUFSIZE2, "%s%slibrary%sgrDevices%sicc%s%s",
7730              R_Home, FILESEP, FILESEP, FILESEP, FILESEP,
7731 	     pd->useCompression ? "srgb.flate" : "srgb");
7732     if (!(fp = R_fopen(R_ExpandFileName(buf), "rb")))
7733         error(_("failed to load sRGB colorspace file"));
7734     size_t res = fread(buf, 1, BUFSIZE2, fp);
7735     res = fwrite(buf, 1, res, pd->pdffp);
7736     fclose(fp);
7737 }
7738 
7739 #include <time.h>  // for time_t, time, localtime
7740 #include <Rversion.h>
7741 
PDF_startfile(PDFDesc * pd)7742 static void PDF_startfile(PDFDesc *pd)
7743 {
7744     struct tm *ltm;
7745     time_t ct;
7746 
7747     pd->nobjs = 0;
7748     pd->pageno = 0;
7749     /*
7750      * I destroy it when I open in Japanese environment carelessly
7751      */
7752     fprintf(pd->pdffp, "%%PDF-%i.%i\n%%\x81\xe2\x81\xe3\x81\xcf\x81\xd3\x5c\x72\n",
7753 	    pd->versionMajor, pd->versionMinor);
7754     pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
7755 
7756     /* Object 1 is Info node. Date format is from the PDF manual */
7757 
7758     ct = time(NULL);
7759     ltm = localtime(&ct);
7760     fprintf(pd->pdffp,
7761 	    "1 0 obj\n<<\n/CreationDate (D:%04d%02d%02d%02d%02d%02d)\n",
7762 	    1900 + ltm->tm_year, ltm->tm_mon+1, ltm->tm_mday,
7763 	    ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
7764     fprintf(pd->pdffp,
7765 	    "/ModDate (D:%04d%02d%02d%02d%02d%02d)\n",
7766 	    1900 + ltm->tm_year, ltm->tm_mon+1, ltm->tm_mday,
7767 	    ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
7768     fprintf(pd->pdffp, "/Title (%s)\n", pd->title);
7769     fprintf(pd->pdffp, "/Producer (R %s.%s)\n/Creator (R)\n>>\nendobj\n",
7770 	    R_MAJOR, R_MINOR);
7771 
7772     /* Object 2 is the Catalog, pointing to pages list in object 3 (at end) */
7773 
7774     pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
7775     fprintf(pd->pdffp, "2 0 obj\n<< /Type /Catalog /Pages 3 0 R >>\nendobj\n");
7776 
7777     /* Objects at the end */
7778     pd->nobjs += 2;
7779     if (streql(pd->colormodel, "srgb")) pd->nobjs += 2;
7780 }
7781 
7782 static const char *Base14[] =
7783 {
7784     "Courier", "Courier-Oblique", "Courier-Bold", "Courier-BoldOblique",
7785     "Helvetica", "Helvetica-Oblique", "Helvetica-Bold",
7786     "Helvetica-BoldOblique", "Symbol", "Times-Roman", "Times-Italic",
7787     "Times-Bold", "Times-BoldItalic", "ZapfDingbats"
7788 };
7789 
isBase14(const char * name)7790 static int isBase14(const char *name)
7791 {
7792     int i;
7793     for(i = 0; i < 14; i++)
7794 	if(strcmp(name, Base14[i]) == 0) return 1;
7795     return 0;
7796 }
7797 
7798 static const char *KnownSanSerif[] =
7799 {
7800     "AvantGarde", "Helvetica-Narrow", "URWGothic", "NimbusSan"
7801 };
7802 
7803 
isSans(const char * name)7804 static int isSans(const char *name)
7805 {
7806     int i;
7807     for(i = 0; i < 4; i++)
7808 	if(strncmp(name, KnownSanSerif[i], strlen(KnownSanSerif[i])) == 0)
7809 	    return 1;
7810     return 0;
7811 }
7812 
7813 #define boldslant(x) ((x==3)?",BoldItalic":((x==2)?",Italic":((x==1)?",Bold":"")))
7814 
7815 #if defined(BUFSIZ) && (BUFSIZ > 512)
7816 /* OS's buffer size in stdio.h, probably.
7817    Windows has 512, Solaris 1024, glibc 8192
7818  */
7819 # define APPENDBUFSIZE BUFSIZ
7820 #else
7821 # define APPENDBUFSIZE 512
7822 #endif
7823 
7824 /* Write out the resources for a page OR for a tiling pattern.
7825  * Return the number of objects in the dictionary
7826  */
PDFwriteResourceDictionary(int objOffset,Rboolean endpage,int excludeDef,PDFDesc * pd)7827 static int PDFwriteResourceDictionary(int objOffset, Rboolean endpage,
7828                                       int excludeDef, PDFDesc *pd)
7829 {
7830     char buf[100];
7831     int i, objCount, nenc, nfonts, cidnfonts, nraster, nmask;
7832 
7833 
7834     nraster = pd->numRasters;
7835     nmask = pd->numMasks;
7836 
7837     /* ProcSet is regarded as obsolete as from PDF 1.4 */
7838     if (nraster > 0) {
7839 	if (nmask > 0) {
7840 	    PDFwrite(buf,
7841                      100,
7842                      "<<\n/ProcSet [/PDF /Text /ImageC /ImageB]\n/Font <<",
7843                      pd);
7844 
7845 	} else {
7846 	    PDFwrite(buf,
7847                      100,
7848 		    "<<\n/ProcSet [/PDF /Text /ImageC]\n/Font <<",
7849                      pd);
7850 	}
7851     } else {
7852 	/* fonts */
7853         PDFwrite(buf,
7854                  100,
7855                  "<<\n/ProcSet [/PDF /Text]\n/Font <<",
7856                  pd);
7857     }
7858 
7859     /* Count how many encodings will be included:
7860      * fonts come after encodings */
7861     nenc = 0;
7862     if (pd->encodings) {
7863 	encodinglist enclist = pd->encodings;
7864 	while (enclist) {
7865 	    nenc++;
7866 	    enclist = enclist->next;
7867 	}
7868     }
7869     /* Should be a default text font at least, plus possibly others */
7870     objCount = objOffset + nenc;
7871 
7872     /* Dingbats always F1 */
7873     if (pd->fontUsed[1])
7874         PDFwrite(buf, 100, " /F1 %d 0 R ", pd, ++objCount);
7875 
7876     nfonts = 2;
7877     if (pd->fonts) {
7878 	type1fontlist fontlist = pd->fonts;
7879 	while (fontlist) {
7880 	    for (i = 0; i < 5; i++) {
7881 		if(nfonts >= 100 || pd->fontUsed[nfonts]) {
7882                     PDFwrite(buf, 100, "/F%d %d 0 R ", pd, nfonts, ++objCount);
7883 		    /* Allow for the font descriptor object, if present */
7884 		    if(!isBase14(fontlist->family->fonts[i]->name)) objCount++;
7885 		}
7886 		nfonts++;
7887 	    }
7888 	    fontlist = fontlist->next;
7889 	}
7890     }
7891     cidnfonts = 0;
7892     if (pd->cidfonts) {
7893 	cidfontlist fontlist = pd->cidfonts;
7894 	while (fontlist) {
7895 	    for (i = 0; i < 5; i++) {
7896 		PDFwrite(buf, 100, "/F%d %d 0 R ", pd,
7897 			1000 + cidnfonts + 1, ++objCount);
7898 		cidnfonts++;
7899 	    }
7900 	    fontlist = fontlist->next;
7901 	}
7902     }
7903     PDFwrite(buf, 100, ">>\n", pd);
7904 
7905     if (nraster > 0) {
7906 	/* image XObjects */
7907 	PDFwrite(buf, 100, "/XObject <<\n", pd);
7908 	for (i = pd->fileRasters; i < nraster; i++) {
7909 	    PDFwrite(buf, 100, "  /Im%d %d 0 R\n", pd,
7910                      i, pd->rasters[i].nobj);
7911 		if (pd->masks[i] >= 0)
7912 		    PDFwrite(buf, 100, "  /Mask%d %d 0 R\n", pd,
7913                              pd->masks[i], pd->rasters[i].nmaskobj);
7914 	}
7915 	PDFwrite(buf, 100, ">>\n", pd);
7916         if (endpage) {
7917             pd->fileRasters = nraster;
7918         }
7919     }
7920 
7921     /* graphics state parameter dictionaries */
7922     PDFwrite(buf, 100, "/ExtGState << ", pd);
7923     for (i = 0; i < 256 && pd->colAlpha[i] >= 0; i++)
7924 	PDFwrite(buf, 100, "/GS%i %d 0 R ", pd, i + 1, ++objCount);
7925     for (i = 0; i < 256 && pd->fillAlpha[i] >= 0; i++)
7926 	PDFwrite(buf, 100, "/GS%i %d 0 R ", pd, i + 257, ++objCount);
7927     /* Special state to set AIS if we have soft masks */
7928     if (nmask > 0)
7929 	PDFwrite(buf, 100, "/GSais %d 0 R ", pd, ++objCount);
7930     /* Soft mask definitions */
7931     int defnOffset = ++objCount;
7932     if (pd->numDefns > 0) {
7933         PDFwriteSoftMaskDefs(defnOffset, pd);
7934     }
7935     PDFwrite(buf, 100, ">>\n", pd);
7936 
7937     /* patterns */
7938     if (pd->numDefns > 0) {
7939         PDFwritePatternDefs(defnOffset, excludeDef, pd);
7940     }
7941 
7942     if (streql(pd->colormodel, "srgb")) {
7943 	/* Ojects 5 and 6 are the sRGB color space, if required */
7944 	PDFwrite(buf, 100, "/ColorSpace << /sRGB 5 0 R >>\n", pd);
7945     }
7946     PDFwrite(buf, 100, ">>\n", pd);
7947 
7948     return objCount;
7949 }
7950 
PDF_endfile(PDFDesc * pd)7951 static void PDF_endfile(PDFDesc *pd)
7952 {
7953     int i, startxref, tempnobj, nfonts, cidnfonts, firstencobj;
7954     int nraster, nmask, npattern;
7955 
7956     /* object 3 lists all the pages */
7957 
7958     pd->pos[3] = (int) ftell(pd->pdffp);
7959     fprintf(pd->pdffp, "3 0 obj\n<< /Type /Pages /Kids [ ");
7960     for(i = 0; i < pd->pageno; i++)
7961 	fprintf(pd->pdffp, "%d 0 R ", pd->pageobj[i]);
7962 
7963     fprintf(pd->pdffp,
7964 	    "] /Count %d /MediaBox [0 0 %d %d] >>\nendobj\n",
7965 	    pd->pageno,
7966 	    (int) (0.5 + pd->paperwidth), (int) (0.5 + pd->paperheight));
7967 
7968     /* Object 4 is the standard resources dict for each page */
7969 
7970     /* Count how many images and masks and patterns */
7971     nraster = pd->numRasters;
7972     nmask = pd->numMasks;
7973     npattern = countPatterns(pd);
7974 
7975     if(pd->nobjs + nraster + nmask + npattern + 500 >= pd->max_nobjs) {
7976 	int new =  pd->nobjs + nraster + nmask + npattern + 500;
7977 	void *tmp = realloc(pd->pos, new * sizeof(int));
7978 	if(!tmp)
7979 	    error("unable to increase object limit: please shutdown the pdf device");
7980 	pd->pos = (int *) tmp;
7981 	pd->max_nobjs = new;
7982     }
7983 
7984     int resourceDictOffset = pd->nobjs;
7985     pd->pos[4] = (int) ftell(pd->pdffp);
7986     fprintf(pd->pdffp, "4 0 obj\n");
7987     /* The resource dictionary for the page */
7988     tempnobj = PDFwriteResourceDictionary(resourceDictOffset, TRUE, -1, pd);
7989     fprintf(pd->pdffp, "endobj\n");
7990 
7991     if (streql(pd->colormodel, "srgb")) {
7992 	pd->pos[5] = (int) ftell(pd->pdffp);
7993 	fprintf(pd->pdffp, "5 0 obj\n[/ICCBased 6 0 R]\nendobj\n");
7994 	pd->pos[6] = (int) ftell(pd->pdffp);
7995 	fprintf(pd->pdffp, "6 0 obj\n");
7996 	PDFwritesRGBcolorspace(pd);
7997 	fprintf(pd->pdffp, "endobj\n");
7998     }
7999 
8000     if(tempnobj >= pd->max_nobjs) {
8001 	int new = tempnobj + 500;
8002 	void *tmp = realloc(pd->pos, new * sizeof(int));
8003 	if(!tmp)
8004 	    error("unable to increase object limit: please shutdown the pdf device");
8005 	pd->pos = (int *) tmp;
8006 	pd->max_nobjs = new;
8007     }
8008 
8009    /*
8010      * Write out objects representing the encodings
8011      */
8012 
8013     firstencobj = pd->nobjs;
8014     PDF_Encodings(pd);
8015 
8016     /*
8017      * Write out objects representing the fonts
8018      */
8019 
8020     if (pd->fontUsed[1]) {
8021 	pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8022 	fprintf(pd->pdffp, "%d 0 obj\n<< /Type /Font /Subtype /Type1 /Name /F1 /BaseFont /ZapfDingbats >>\nendobj\n", pd->nobjs);
8023     }
8024 
8025 
8026     nfonts = 2;
8027     if (pd->fonts) {
8028 	type1fontlist fontlist = pd->fonts;
8029 	while (fontlist) {
8030 	    FontMetricInfo *metrics;
8031 	    /*
8032 	     * Find the index of the device encoding
8033 	     * This really should be there
8034 	     */
8035 	    int encIndex;
8036 	    encodinginfo encoding =
8037 		findDeviceEncoding(fontlist->family->encoding->encpath,
8038 				   pd->encodings, &encIndex);
8039 	    if (!encoding)
8040 		error(_("corrupt encodings in PDF device"));
8041 	    for (i = 0; i < 5; i++) {
8042 		if (nfonts >= 100 || pd->fontUsed[nfonts]) {
8043 		    type1fontinfo fn = fontlist->family->fonts[i];
8044 		    int base = isBase14(fn->name);
8045 		    metrics = &fn->metrics;
8046 		    pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8047 		    fprintf(pd->pdffp, "%d 0 obj\n<< /Type /Font /Subtype /Type1 /Name /F%d /BaseFont /%s\n",
8048 			    pd->nobjs,
8049 			    nfonts,
8050 			    fn->name);
8051 		    if (!base) {
8052 			int ii, first, last, tmp;
8053 			for(first = 1, ii = 0; ii < 255; ii++)
8054 			    if(metrics->CharInfo[ii].WX != NA_SHORT) {
8055 				first = ii;
8056 				break;
8057 			    }
8058 			for(last = 255, ii = 254; ii >= 0; ii--)
8059 			    if(metrics->CharInfo[ii].WX != NA_SHORT) {
8060 				last = ii + 1;
8061 				break;
8062 			    }
8063 			fprintf(pd->pdffp,
8064 				"/FirstChar %d /LastChar %d /Widths [\n",
8065 				first, last);
8066 			for (ii = first; ii <= last; ii++) {
8067 			    tmp = metrics->CharInfo[ii].WX;
8068 			    fprintf(pd->pdffp, " %d", tmp==NA_SHORT ? 0 : tmp);
8069 			    if ((ii + 1) % 15 == 0) fprintf(pd->pdffp, "\n");
8070 			}
8071 			fprintf(pd->pdffp, "]\n");
8072 			fprintf(pd->pdffp, "/FontDescriptor %d 0 R\n",
8073 				pd->nobjs + 1);
8074 		    }
8075 		    if(i < 4)
8076 			fprintf(pd->pdffp, "/Encoding %d 0 R ",
8077 				/* Encodings come after dingbats font which is
8078 				 * object 5 */
8079 				encIndex + firstencobj);
8080 		    fprintf(pd->pdffp, ">>\nendobj\n");
8081 		    if(!base) {
8082 			/* write font descriptor */
8083 			int flags = 32 /*bit 6, non-symbolic*/ +
8084 			    ((i==2 || i==3) ? 64/* italic */: 0) +
8085 			    (metrics->IsFixedPitch > 0 ? 1 : 0) +
8086 			    (isSans(fn->name) ? 0 : 2);
8087 			/* <FIXME> we have no real way to know
8088 			   if this is serif or not */
8089 			pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8090 			fprintf(pd->pdffp,
8091 				"%d 0 obj <<\n"
8092 				" /Type /FontDescriptor\n"
8093 				" /FontName /%s\n"
8094 				" /Flags %d\n"
8095 				" /FontBBox [%d %d %d %d]\n"
8096 				" /CapHeight %d\n /Ascent %d\n /Descent %d\n"
8097 				" /ItalicAngle %d\n /XHeight %d\n /StemV %d\n"
8098 				">>\nendobj\n",
8099 				pd->nobjs,
8100 				fn->name,
8101 				(i == 4) ? 4 : flags,
8102 				metrics->FontBBox[0], metrics->FontBBox[1],
8103 				metrics->FontBBox[2], metrics->FontBBox[3],
8104 				metrics->CapHeight, metrics->Ascender,
8105 				metrics->Descender,
8106 				metrics->ItalicAngle, metrics->XHeight,
8107 				(metrics->StemV != NA_SHORT) ? metrics->StemV :
8108 				(i==2 || i==3) ? 140 : 83);
8109 		    }
8110 		}
8111 		nfonts++;
8112 	    }
8113 	    fontlist = fontlist->next;
8114 	}
8115     }
8116     cidnfonts = 0;
8117     if (pd->cidfonts) {
8118 	cidfontlist fontlist = pd->cidfonts;
8119 	if(pd->versionMajor == 1 && pd->versionMinor < 3) {
8120 	    pd->versionMinor  = 3;
8121 	    warning(_("increasing the PDF version to 1.3"));
8122 	}
8123 	while (fontlist) {
8124 	    for (i = 0; i < 4; i++) {
8125 		pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8126 		fprintf(pd->pdffp,
8127 			/** format **/
8128 			"%d 0 obj\n"
8129 			"<<\n"
8130 			"  /Type /Font\n"
8131 			"  /Subtype /Type0\n"
8132 			"  /Name /F%d\n"
8133 			"  /BaseFont /%s%s\n"
8134 			"  /DescendantFonts [\n"
8135 			"    <<\n"
8136 			"      /Type /Font\n"
8137 			"      /Subtype /CIDFontType0\n"
8138 			"      /BaseFont /%s%s\n"
8139 			"      %s"
8140 			"    >>\n"
8141 			"  ]\n"
8142 			"  /Encoding /%s\n"
8143 			">>\n"
8144 			"endobj\n",
8145 			/** vararg **/
8146 			pd->nobjs,                          /* pdf objnum  */
8147 			1000 + cidnfonts + 1,               /* - face      */
8148 			fontlist->cidfamily->cidfonts[i]->name,/* /BaseFont*/
8149 			boldslant(i),                       /* - boldslant */
8150 			fontlist->cidfamily->cidfonts[i]->name,/* /BaseFont*/
8151 			boldslant(i),                       /* - boldslant */
8152 							    /* Resource    */
8153 			/*
8154 			 * Pull the resource out of R object
8155 			 * Hopefully one day this will be unnecessary
8156 			 */
8157 			getCIDFontPDFResource(fontlist->cidfamily->fxname),
8158 			fontlist->cidfamily->cmap           /* /Encoding   */
8159 			);
8160 		cidnfonts++;
8161 	    }
8162 	    /* Symbol face does not use encoding */
8163 	    pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8164 	    fprintf(pd->pdffp, "%d 0 obj\n<<\n/Type /Font\n/Subtype /Type1\n/Name /F%d\n/BaseFont /%s\n>>\nendobj\n",
8165 		    pd->nobjs,
8166 		    1000 + cidnfonts + 1,
8167 		    fontlist->cidfamily->symfont->name);
8168 	    cidnfonts++;
8169 	    fontlist = fontlist->next;
8170 	}
8171     }
8172 
8173     /*
8174      * Write out objects representing the graphics state parameter
8175      * dictionaries for alpha transparency
8176      */
8177     for (i = 0; i < 256 && pd->colAlpha[i] >= 0; i++) {
8178 	pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8179 	fprintf(pd->pdffp,
8180 		"%d 0 obj\n<<\n/Type /ExtGState\n/CA %1.3f >>\nendobj\n",
8181 		pd->nobjs, pd->colAlpha[i]/255.0);
8182     }
8183     for (i = 0; i < 256 && pd->fillAlpha[i] >= 0; i++) {
8184 	pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8185 	fprintf(pd->pdffp,
8186 		"%d 0 obj\n<<\n/Type /ExtGState\n/ca %1.3f\n>>\nendobj\n",
8187 		pd->nobjs, pd->fillAlpha[i]/255.0);
8188     }
8189 
8190     if (nmask > 0) {
8191 	pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8192 	fprintf(pd->pdffp,
8193 		"%d 0 obj\n<<\n/Type /ExtGState\n/AIS false\n>>\nendobj\n",
8194 		pd->nobjs);
8195     }
8196 
8197     /* Write out definitions */
8198     PDFwriteDefinitions(resourceDictOffset, pd);
8199 
8200     /* write out xref table */
8201 
8202     startxref = (int) ftell(pd->pdffp);
8203     /* items here must be exactly 20 bytes including terminator */
8204     fprintf(pd->pdffp, "xref\n0 %d\n", pd->nobjs+1);
8205     fprintf(pd->pdffp, "0000000000 65535 f \n");
8206     for(i = 1; i <= pd->nobjs; i++) {
8207 	fprintf(pd->pdffp, "%010d 00000 n \n", pd->pos[i]);
8208     }
8209     fprintf(pd->pdffp,
8210 	    "trailer\n<< /Size %d /Info 1 0 R /Root 2 0 R >>\nstartxref\n%d\n",
8211 	    pd->nobjs+1, startxref);
8212     fprintf(pd->pdffp, "%%%%EOF\n");
8213 
8214     /* now seek back and update the header */
8215     rewind(pd->pdffp);
8216     fprintf(pd->pdffp, "%%PDF-%i.%i\n", pd->versionMajor, pd->versionMinor);
8217     fclose(pd->pdffp);
8218     if (pd->open_type == 1) {
8219 	char buf[APPENDBUFSIZE];
8220 	size_t nc;
8221 	pd->pdffp = R_fopen(pd->filename, "rb");
8222 	while((nc = fread(buf, 1, APPENDBUFSIZE, pd->pdffp))) {
8223 	    if(nc != fwrite(buf, 1, nc, pd->pipefp))
8224 		error("write error");
8225 	    if (nc < APPENDBUFSIZE) break;
8226 	}
8227 	fclose(pd->pdffp);
8228 	pclose(pd->pipefp);
8229 	unlink(pd->filename);
8230     }
8231 }
8232 
8233 
PDF_Open(pDevDesc dd,PDFDesc * pd)8234 static Rboolean PDF_Open(pDevDesc dd, PDFDesc *pd)
8235 {
8236     char buf[512];
8237 
8238     if (pd->offline)
8239         return TRUE;
8240 
8241     if (pd->filename[0] == '|') {
8242 	strncpy(pd->cmd, pd->filename + 1, PATH_MAX - 1);
8243 	pd->cmd[PATH_MAX - 1] = '\0';
8244 	char *tmp = R_tmpnam("Rpdf", R_TempDir);
8245 	strncpy(pd->filename, tmp, PATH_MAX - 1);
8246 	pd->filename[PATH_MAX - 1] = '\0';
8247 	free(tmp);
8248 	errno = 0;
8249 	pd->pipefp = R_popen(pd->cmd, "w");
8250 	if (!pd->pipefp || errno != 0) {
8251 	    char errbuf[strlen(pd->cmd) + 1];
8252 	    strcpy(errbuf, pd->cmd);
8253 	    PDFcleanup(7, pd);
8254 	    error(_("cannot open 'pdf' pipe to '%s'"), errbuf);
8255 	    return FALSE;
8256 	}
8257 	pd->open_type = 1;
8258 	if (!pd->onefile) {
8259 	    pd->onefile = TRUE;
8260 	    warning(_("file = \"|cmd\" implies 'onefile = TRUE'"));
8261 	}
8262     } else pd->open_type = 0;
8263     snprintf(buf, 512, pd->filename, pd->fileno + 1); /* file 1 to start */
8264     /* NB: this must be binary to get tell positions and line endings right,
8265        as well as allowing binary streams */
8266     pd->mainfp = R_fopen(R_ExpandFileName(buf), "wb");
8267     if (!pd->mainfp) {
8268 	PDFcleanup(7, pd);
8269 	free(dd);
8270 	error(_("cannot open file '%s'"), buf);
8271     }
8272     pd->pdffp = pd->mainfp;
8273 
8274     PDF_startfile(pd);
8275     return TRUE;
8276 }
8277 
pdfClip(double x0,double x1,double y0,double y1,PDFDesc * pd)8278 static void pdfClip(double x0, double x1, double y0, double y1, PDFDesc *pd)
8279 {
8280     char buf[100];
8281     if(x0 != 0.0 || y0 != 0.0 || x1 != 72*pd->width || y1 != 72*pd->height)
8282 	PDFwrite(buf, 100, "Q q %.2f %.2f %.2f %.2f re W n\n", pd,
8283                  x0, y0, x1 - x0, y1 - y0);
8284     else PDFwrite(buf, 100, "Q q\n", pd);
8285 }
8286 
PDF_Clip(double x0,double x1,double y0,double y1,pDevDesc dd)8287 static void PDF_Clip(double x0, double x1, double y0, double y1, pDevDesc dd)
8288 {
8289     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
8290 
8291     PDF_checkOffline();
8292 
8293     if(pd->inText) textoff(pd);
8294     pdfClip(x0, x1, y0, y1, pd);
8295     PDF_Invalidate(pd);
8296 }
8297 
PDF_Size(double * left,double * right,double * bottom,double * top,pDevDesc dd)8298 static void PDF_Size(double *left, double *right,
8299 		     double *bottom, double *top,
8300 		     pDevDesc dd)
8301 {
8302     *left = dd->left;
8303     *right = dd->right;
8304     *bottom = dd->bottom;
8305     *top = dd->top;
8306 }
8307 
PDF_endpage(PDFDesc * pd)8308 static void PDF_endpage(PDFDesc *pd)
8309 {
8310     if(pd->inText) textoff(pd);
8311     fprintf(pd->pdffp, "Q\n");
8312     if (pd->useCompression) {
8313 	fflush(pd->pdffp);
8314 	fseek(pd->pdffp, 0, SEEK_END);
8315 	unsigned int len = (unsigned int) ftell(pd->pdffp);
8316 	fseek(pd->pdffp, 0, SEEK_SET);
8317 	Bytef *buf = Calloc(len, Bytef);
8318 	uLong outlen = (uLong)(1.001*len + 20);
8319 	Bytef *buf2 = Calloc(outlen, Bytef);
8320 	size_t res = fread(buf, 1, len, pd->pdffp);
8321 	if (res < len) error("internal read error in PDF_endpage");
8322 	fclose(pd->pdffp);
8323 	unlink(pd->tmpname);
8324 	pd->pdffp = pd->mainfp;
8325 	int res2 = compress(buf2, &outlen, buf, len);
8326 	if(res2 != Z_OK)
8327 	    error("internal compression error %d in PDF_endpage", res2);
8328 	fprintf(pd->pdffp, "%d 0 obj\n<<\n/Length %d /Filter /FlateDecode\n>>\nstream\n",
8329 		pd->nobjs, (int) outlen);
8330 	size_t nwrite = fwrite(buf2, 1, outlen, pd->pdffp);
8331 	if(nwrite != outlen) error(_("write failed"));
8332 	Free(buf); Free(buf2);
8333 	fprintf(pd->pdffp, "endstream\nendobj\n");
8334     } else {
8335 	int here = (int) ftell(pd->pdffp);
8336 	fprintf(pd->pdffp, "endstream\nendobj\n");
8337 	pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8338 	fprintf(pd->pdffp, "%d 0 obj\n%d\nendobj\n", pd->nobjs,
8339 		here - pd->startstream);
8340     }
8341 
8342     if(pd->nobjs + 2*(pd->numRasters - pd->writtenRasters) + 500
8343        >= pd->max_nobjs) {
8344 	int new =  pd->nobjs + 2*(pd->numRasters - pd->writtenRasters) + 2000;
8345 	void *tmp = realloc(pd->pos, new * sizeof(int));
8346 	if(!tmp)
8347 	    error("unable to increase object limit: please shutdown the pdf device");
8348 	pd->pos = (int *) tmp;
8349 	pd->max_nobjs = new;
8350     }
8351 
8352     /* Write out any new rasters */
8353     for (int i = pd->writtenRasters; i < pd->numRasters; i++) {
8354 	pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8355 	pd->rasters[i].nobj = pd->nobjs;
8356 	writeRasterXObject(pd->rasters[i], pd->nobjs,
8357 			   pd->masks[i], pd->nobjs+1, pd);
8358  	if (pd->masks[i] >= 0) {
8359 	    pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8360 	    pd->rasters[i].nmaskobj = pd->nobjs;
8361 	    writeMaskXObject(pd->rasters[i], pd->nobjs, pd);
8362 	}
8363 	free(pd->rasters[i].raster);
8364 	pd->rasters[i].raster = NULL;
8365 	pd->writtenRasters = pd->numRasters;
8366     }
8367 }
8368 
8369 #define R_VIS(col) (R_ALPHA(col) > 0)
8370 
PDF_NewPage(const pGEcontext gc,pDevDesc dd)8371 static void PDF_NewPage(const pGEcontext gc,
8372 			pDevDesc dd)
8373 {
8374     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
8375     char buf[512];
8376 
8377     PDF_checkOffline();
8378 
8379     if(pd->pageno >= pd->pagemax) {
8380 	void * tmp = realloc(pd->pageobj, 2*pd->pagemax * sizeof(int));
8381 	if(!tmp)
8382 	    error("unable to increase page limit: please shutdown the pdf device");
8383 	pd->pageobj = (int *) tmp;
8384 	pd->pagemax *= 2;
8385     }
8386     if(pd->nobjs + 500 >= pd->max_nobjs) {
8387 	int new = pd->max_nobjs + 2000;
8388 	void *tmp = realloc(pd->pos, new * sizeof(int));
8389 	if(!tmp)
8390 	    error("unable to increase object limit: please shutdown the pdf device");
8391 	pd->pos = (int *) tmp;
8392 	pd->max_nobjs = new;
8393     }
8394 
8395 
8396     if(pd->pageno > 0) {
8397 	PDF_endpage(pd);
8398 	if(!pd->onefile) {
8399 	    PDF_endfile(pd);
8400 	    pd->fileno++;
8401 	    snprintf(buf, 512, pd->filename, pd->fileno + 1); /* file 1 to start */
8402 	    pd->mainfp = R_fopen(R_ExpandFileName(buf), "wb");
8403 	    if (!pd->mainfp)
8404 		error(_("cannot open 'pdf' file argument '%s'\n  please shut down the PDF device"), buf);
8405 	    pd->pdffp = pd->mainfp;
8406             resetDefinitions(pd);
8407 	    PDF_startfile(pd);
8408 	}
8409     }
8410 
8411     pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8412     pd->pageobj[pd->pageno++] = pd->nobjs;
8413     fprintf(pd->pdffp, "%d 0 obj\n<< /Type /Page /Parent 3 0 R /Contents %d 0 R /Resources 4 0 R >>\nendobj\n",
8414 	    pd->nobjs, pd->nobjs+1);
8415     pd->pos[++pd->nobjs] = (int) ftell(pd->pdffp);
8416     if (pd->useCompression) {
8417 	char *tmp = R_tmpnam("pdf", R_TempDir);
8418 	/* assume tmpname is less than PATH_MAX */
8419 	strcpy(pd->tmpname, tmp);
8420 	pd->pdffp = fopen(tmp, "w+b");
8421 	if (! pd->pdffp) {
8422             pd->pdffp = pd->mainfp;
8423             pd->useCompression = 0;
8424             warning(_("Cannot open temporary file '%s' for compression (reason: %s); compression has been turned off for this device"),
8425                     tmp, strerror(errno));
8426         }
8427 	free(tmp);
8428     }
8429     /* May have turned compression off in previous block */
8430     if (!pd->useCompression) {
8431 	fprintf(pd->pdffp, "%d 0 obj\n<<\n/Length %d 0 R\n>>\nstream\n",
8432 		pd->nobjs, pd->nobjs + 1);
8433 	pd->startstream = (int) ftell(pd->pdffp);
8434     }
8435 
8436     /*
8437      * Line end/join/mitre now controlled by user
8438      * Same old defaults
8439      * .. but they are still needed because SetXXX produces the corresponding
8440      * command only if the value changes - so we have to define base defaults
8441      * according to the values reset by Invalidate. I'm pretty sure about j/J
8442      * but not so about M because Invalidate uses 0 yet the default used to be
8443      * 10.
8444      *
8445      * fprintf(pd->pdffp, "1 J 1 j 10 M q\n");
8446      */
8447     fprintf(pd->pdffp, "1 J 1 j q\n");
8448     PDF_Invalidate(pd);
8449     pd->appendingClipPath = FALSE;
8450     pd->appendingMask = -1;
8451     pd->currentMask = -1;
8452     pd->appendingPattern = -1;
8453     if(R_VIS(gc->fill)) {
8454 	PDF_SetFill(gc->fill, dd);
8455 	fprintf(pd->pdffp, "0 0 %.2f %.2f re f\n",
8456 		72.0 * pd->width, 72.0 * pd->height);
8457     }
8458     pd->inText = FALSE;
8459 }
8460 
PDF_Close(pDevDesc dd)8461 static void PDF_Close(pDevDesc dd)
8462 {
8463     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
8464 
8465     if (!pd->offline) {
8466         if(pd->pageno > 0) PDF_endpage(pd);
8467         PDF_endfile(pd);
8468         /* may no longer be needed */
8469         killRasterArray(pd->rasters, pd->maxRasters);
8470     }
8471     PDFcleanup(7, pd); /* which frees masks and rasters */
8472 }
8473 
PDF_Rect(double x0,double y0,double x1,double y1,const pGEcontext gc,pDevDesc dd)8474 static void PDF_Rect(double x0, double y0, double x1, double y1,
8475 		     const pGEcontext gc,
8476 		     pDevDesc dd)
8477 {
8478     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
8479     int code;
8480     char buf[100];
8481 
8482     PDF_checkOffline();
8483 
8484 
8485     if (gc->patternFill != R_NilValue) {
8486         if (R_VIS(gc->col)) {
8487             code = 3;
8488         } else {
8489             code = 2;
8490         }
8491     } else {
8492         code = 2 * (R_VIS(gc->fill)) + (R_VIS(gc->col));
8493     }
8494     if (code) {
8495         if(pd->inText) textoff(pd);
8496         /*
8497          * IF appending a clip path:
8498          *    Do NOT set graphical parameters
8499          *    Do NOT stroke or fill
8500          *
8501          * IF there is a pattern fill, use that instead of fill
8502          *
8503          * IF there is a mask apply that
8504          *
8505          * PDFwrite writes to ...
8506          *    clip path (if appending a clip path)
8507          *    mask (if appending a mask)
8508          *    file (otherwise)
8509          */
8510         if (!pd->appendingClipPath) {
8511             if (gc->patternFill != R_NilValue) {
8512                 PDF_SetPatternFill(gc->patternFill, dd);
8513             } else if(code & 2) {
8514                 PDF_SetFill(gc->fill, dd);
8515             }
8516             if(code & 1) {
8517                 PDF_SetLineColor(gc->col, dd);
8518                 PDF_SetLineStyle(gc, dd);
8519             }
8520         }
8521         if (pd->currentMask >= 0) {
8522             PDFwriteMask(pd->currentMask, pd);
8523         }
8524         PDFwrite(buf, 100, "%.2f %.2f %.2f %.2f re", pd, x0, y0, x1-x0, y1-y0);
8525         if (!pd->appendingClipPath) {
8526             switch(code) {
8527             case 1: PDFwrite(buf, 100, " S\n", pd); break;
8528             case 2: PDFwrite(buf, 100, " f\n", pd); break;
8529             case 3: PDFwrite(buf, 100, " B\n", pd); break;
8530             }
8531         }
8532     }
8533 }
8534 
8535 #ifdef SIMPLE_RASTER
8536 /* Maybe reincoporate this simpler approach as an alternative
8537  * (for opaque raster images) because it has the advantage of
8538  * NOT keeping the raster in memory until the PDF file is complete
8539  */
PDF_Raster(unsigned int * raster,int w,int h,double x,double y,double width,double height,double rot,Rboolean interpolate,const pGEcontext gc,pDevDesc dd)8540 static void PDF_Raster(unsigned int *raster,
8541 		       int w, int h,
8542 		       double x, double y,
8543 		       double width, double height,
8544 		       double rot, Rboolean interpolate,
8545 		       const pGEcontext gc, pDevDesc dd)
8546 {
8547     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
8548     double angle, cosa, sina;
8549 
8550     PDF_checkOffline();
8551 
8552     /* This takes the simple approach of creating an inline
8553      * image.  This is not recommended for larger images
8554      * because it makes more work for the PDF viewer.
8555      * It also does not allow for semitransparent images.
8556      */
8557     if(pd->inText) textoff(pd);
8558     /* Save graphics state */
8559     fprintf(pd->pdffp, "q\n");
8560     /* translate */
8561     fprintf(pd->pdffp,
8562 	    "1 0 0 1 %.2f %.2f cm\n",
8563 	    x, y);
8564     /* rotate */
8565     angle = rot*M_PI/180;
8566     cosa = cos(angle);
8567     sina = sin(angle);
8568     fprintf(pd->pdffp,
8569 	    "%.2f %.2f %.2f %.2f 0 0 cm\n",
8570 	    cosa, sina, -sina, cosa);
8571     /* scale */
8572     fprintf(pd->pdffp,
8573 	    "%.2f 0 0 %.2f 0 0 cm\n",
8574 	    width, height);
8575     /* Begin image */
8576     fprintf(pd->pdffp, "BI\n");
8577     /* Image characteristics */
8578     /* Use ASCIIHexDecode filter for now, just because
8579      * it's easier to implement */
8580     fprintf(pd->pdffp,
8581 	    "  /W %d\n  /H %d\n  /CS /RGB\n  /BPC 8\n  /F [/AHx]\n",
8582 	    w, h);
8583     if (interpolate) {
8584 	fprintf(pd->pdffp, "  /I true\n");
8585     }
8586     /* Begin image data */
8587     fprintf(pd->pdffp, "ID\n");
8588     /* The image stream */
8589     PDF_imagedata(raster, w, h, pd);
8590     /* End image */
8591     fprintf(pd->pdffp, "EI\n");
8592     /* Restore graphics state */
8593     fprintf(pd->pdffp, "Q\n");
8594 }
8595 #else
8596 
PDF_Raster(unsigned int * raster,int w,int h,double x,double y,double width,double height,double rot,Rboolean interpolate,const pGEcontext gc,pDevDesc dd)8597 static void PDF_Raster(unsigned int *raster,
8598 		       int w, int h,
8599 		       double x, double y,
8600 		       double width, double height,
8601 		       double rot, Rboolean interpolate,
8602 		       const pGEcontext gc, pDevDesc dd)
8603 {
8604     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
8605     double angle, cosa, sina;
8606     int alpha;
8607 
8608     PDF_checkOffline();
8609 
8610     /* A raster image adds nothing to a clipping path */
8611     if (pd->appendingClipPath)
8612         return;
8613 
8614     /* A raster image cannot be used in a pattern or mask either (for now) */
8615     if (pd->appendingMask >= 0 || pd->appendingPattern >= 0) {
8616         warning("Raster image within mask ignored");
8617         return;
8618     }
8619 
8620     /* Record the raster so can write it out when page is finished */
8621     alpha = addRaster(raster, w, h, interpolate, pd);
8622 
8623     if(pd->inText) textoff(pd);
8624     /* Save graphics state */
8625     fprintf(pd->pdffp, "q\n");
8626     /* Need to set AIS graphics state parameter ? */
8627     if (alpha) fprintf(pd->pdffp, "/GSais gs\n");
8628     /* translate */
8629     fprintf(pd->pdffp,
8630 	    "1 0 0 1 %.2f %.2f cm\n",
8631 	    x, y);
8632     /* rotate */
8633     angle = rot*M_PI/180;
8634     cosa = cos(angle);
8635     sina = sin(angle);
8636     fprintf(pd->pdffp,
8637 	    "%.2f %.2f %.2f %.2f 0 0 cm\n",
8638 	    cosa, sina, -sina, cosa);
8639     /* scale */
8640     fprintf(pd->pdffp,
8641 	    "%.2f 0 0 %.2f 0 0 cm\n",
8642 	    width, height);
8643     /* Refer to XObject which will be written to file when page is finished */
8644     fprintf(pd->pdffp, "/Im%d Do\n", pd->numRasters - 1);
8645     /* Restore graphics state */
8646     fprintf(pd->pdffp, "Q\n");
8647 }
8648 
8649 #endif
8650 
8651 /* r is in device coords */
PDF_Circle(double x,double y,double r,const pGEcontext gc,pDevDesc dd)8652 static void PDF_Circle(double x, double y, double r,
8653 		       const pGEcontext gc,
8654 		       pDevDesc dd)
8655 {
8656     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
8657     int code, tr;
8658     double xx, yy, a;
8659     char buf[100];
8660 
8661     PDF_checkOffline();
8662 
8663     if (r <= 0.0) return;  /* since PR#14797 use 0-sized pch=1, but now
8664 			      GECircle omits such circles */
8665 
8666     if (gc->patternFill != R_NilValue) {
8667         if (R_VIS(gc->col)) {
8668             code = 3;
8669         } else {
8670             code = 2;
8671         }
8672     } else {
8673         code = 2 * (R_VIS(gc->fill)) + (R_VIS(gc->col));
8674     }
8675     if (!pd->appendingClipPath) {
8676         if (gc->patternFill != R_NilValue) {
8677             PDF_SetPatternFill(gc->patternFill, dd);
8678         } else if(code & 2) {
8679             PDF_SetFill(gc->fill, dd);
8680         }
8681         if(code & 1) {
8682             PDF_SetLineColor(gc->col, dd);
8683             PDF_SetLineStyle(gc, dd);
8684         }
8685     }
8686     if (pd->currentMask >= 0) {
8687         PDFwriteMask(pd->currentMask, pd);
8688     }
8689     if (code) {
8690         if (semiTransparent(gc->col) || semiTransparent(gc->fill)
8691             || r > 10  || !pd->dingbats) {
8692             /*
8693              * Due to possible bug in Acrobat Reader for rendering
8694              * semi-transparent text, only ever draw Bezier curves
8695              * regardless of circle size.  Otherwise use font up to 20pt
8696              */
8697             {
8698                 /* Use four Bezier curves, hand-fitted to quadrants */
8699                 double s = 0.55 * r;
8700                 if(pd->inText) textoff(pd);
8701                 PDFwrite(buf, 100, "  %.2f %.2f m\n", pd, x - r, y);
8702                 PDFwrite(buf, 100,
8703                          "  %.2f %.2f %.2f %.2f %.2f %.2f c\n", pd,
8704                          x - r, y + s, x - s, y + r, x, y + r);
8705                 PDFwrite(buf, 100,
8706                          "  %.2f %.2f %.2f %.2f %.2f %.2f c\n", pd,
8707                          x + s, y + r, x + r, y + s, x + r, y);
8708                 PDFwrite(buf, 100,
8709                          "  %.2f %.2f %.2f %.2f %.2f %.2f c\n", pd,
8710                          x + r, y - s, x + s, y - r, x, y - r);
8711                 PDFwrite(buf, 100,
8712                          "  %.2f %.2f %.2f %.2f %.2f %.2f c\n", pd,
8713                          x - s, y - r, x - r, y - s, x - r, y);
8714                 if (!pd->appendingClipPath) {
8715                     switch(code) {
8716                     case 1: PDFwrite(buf, 100, "S\n", pd); break;
8717                     case 2: PDFwrite(buf, 100, "f\n", pd); break;
8718                     case 3: PDFwrite(buf, 100, "B\n", pd); break;
8719                     }
8720                 }
8721             }
8722         } else {
8723             pd->fontUsed[1] = TRUE;
8724             /* Use char 108 in Dingbats, which is a solid disc
8725                afm is C 108 ; WX 791 ; N a71 ; B 35 -14 757 708 ;
8726                so diameter = 0.722 * size
8727                centre = (0.396, 0.347) * size
8728             */
8729             a = 2./0.722 * r;
8730             if (a < 0.01) return; // avoid 0 dims below.
8731             xx = x - 0.396*a;
8732             yy = y - 0.347*a;
8733             if (pd->appendingClipPath) {
8734                 tr = 7;
8735             } else {
8736                 tr = (R_OPAQUE(gc->fill)) +
8737                     2 * (R_OPAQUE(gc->col)) - 1;
8738             }
8739             if(!pd->inText) texton(pd);
8740             PDFwrite(buf, 100,
8741                      "/F1 1 Tf %d Tr %.2f 0 0 %.2f %.2f %.2f Tm", pd,
8742                      tr, a, a, xx, yy);
8743             PDFwrite(buf, 100, " (l) Tj 0 Tr\n", pd);
8744             textoff(pd); /* added in 2.8.0 */
8745         }
8746     }
8747 }
8748 
PDF_Line(double x1,double y1,double x2,double y2,const pGEcontext gc,pDevDesc dd)8749 static void PDF_Line(double x1, double y1, double x2, double y2,
8750 		     const pGEcontext gc,
8751 		     pDevDesc dd)
8752 {
8753     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
8754     char buf[100];
8755 
8756     PDF_checkOffline();
8757 
8758     if(!R_VIS(gc->col)) return;
8759 
8760     if (!pd->appendingClipPath) {
8761         PDF_SetLineColor(gc->col, dd);
8762         PDF_SetLineStyle(gc, dd);
8763     }
8764     if (pd->currentMask >= 0) {
8765         PDFwriteMask(pd->currentMask, pd);
8766     }
8767 
8768     if(pd->inText) textoff(pd);
8769     PDFwrite(buf, 100, "%.2f %.2f m %.2f %.2f l S\n", pd, x1, y1, x2, y2);
8770 }
8771 
PDF_Polygon(int n,double * x,double * y,const pGEcontext gc,pDevDesc dd)8772 static void PDF_Polygon(int n, double *x, double *y,
8773 			const pGEcontext gc,
8774 			pDevDesc dd)
8775 {
8776     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
8777     double xx, yy;
8778     int i, code;
8779     char buf[100];
8780 
8781     PDF_checkOffline();
8782 
8783     if (gc->patternFill != R_NilValue) {
8784         if (R_VIS(gc->col)) {
8785             code = 3;
8786         } else {
8787             code = 2;
8788         }
8789     } else {
8790         code = 2 * (R_VIS(gc->fill)) + (R_VIS(gc->col));
8791     }
8792     if (code) {
8793         if(pd->inText) textoff(pd);
8794         if (!pd->appendingClipPath) {
8795             if (gc->patternFill != R_NilValue) {
8796                 PDF_SetPatternFill(gc->patternFill, dd);
8797             } else if(code & 2) {
8798                 PDF_SetFill(gc->fill, dd);
8799             }
8800             if(code & 1) {
8801                 PDF_SetLineColor(gc->col, dd);
8802                 PDF_SetLineStyle(gc, dd);
8803             }
8804         }
8805         if (pd->currentMask >= 0) {
8806             PDFwriteMask(pd->currentMask, pd);
8807         }
8808         xx = x[0];
8809         yy = y[0];
8810         PDFwrite(buf, 100, "%.2f %.2f m\n", pd, xx, yy);
8811         for(i = 1 ; i < n ; i++) {
8812             xx = x[i];
8813             yy = y[i];
8814             PDFwrite(buf, 100, "%.2f %.2f l\n", pd, xx, yy);
8815         }
8816         PDFwrite(buf, 100, "h ", pd, xx, yy);
8817         if (!pd->appendingClipPath) {
8818             if (pd->fillOddEven) {
8819                 switch(code) {
8820                 case 1: PDFwrite(buf, 100, "S\n", pd); break;
8821                 case 2: PDFwrite(buf, 100, "f*\n", pd); break;
8822                 case 3: PDFwrite(buf, 100, "B*\n", pd); break;
8823                 }
8824             } else {
8825                 switch(code) {
8826                 case 1: PDFwrite(buf, 100, "S\n", pd); break;
8827                 case 2: PDFwrite(buf, 100, "f\n", pd); break;
8828                 case 3: PDFwrite(buf, 100, "B\n", pd); break;
8829                 }
8830             }
8831         }
8832     }
8833 }
8834 
PDF_Path(double * x,double * y,int npoly,int * nper,Rboolean winding,const pGEcontext gc,pDevDesc dd)8835 static void PDF_Path(double *x, double *y,
8836                      int npoly, int *nper,
8837                      Rboolean winding,
8838                      const pGEcontext gc,
8839                      pDevDesc dd)
8840 {
8841     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
8842     double xx, yy;
8843     int i, j, index, code;
8844     char buf[100];
8845 
8846     PDF_checkOffline();
8847 
8848     if (gc->patternFill != R_NilValue) {
8849         if (R_VIS(gc->col)) {
8850             code = 3;
8851         } else {
8852             code = 2;
8853         }
8854     } else {
8855         code = 2 * (R_VIS(gc->fill)) + (R_VIS(gc->col));
8856     }
8857     if (code) {
8858         if(pd->inText) textoff(pd);
8859         if (!pd->appendingClipPath) {
8860             if(code & 2)
8861                 PDF_SetFill(gc->fill, dd);
8862             if(code & 1) {
8863                 PDF_SetLineColor(gc->col, dd);
8864                 PDF_SetLineStyle(gc, dd);
8865             }
8866         }
8867         if (pd->currentMask >= 0) {
8868             PDFwriteMask(pd->currentMask, pd);
8869         }
8870         index = 0;
8871         for (i=0; i < npoly; i++) {
8872             xx = x[index];
8873             yy = y[index];
8874             index++;
8875             PDFwrite(buf, 100, "%.2f %.2f m\n", pd, xx, yy);
8876             for(j=1; j < nper[i]; j++) {
8877                 xx = x[index];
8878                 yy = y[index];
8879                 index++;
8880                 PDFwrite(buf, 100, "%.2f %.2f l\n", pd, xx, yy);
8881             }
8882             if (i < npoly - 1)
8883                 PDFwrite(buf, 100, "h\n", pd);
8884         }
8885         PDFwrite(buf, 100, "h\n", pd);
8886         if (!pd->appendingClipPath) {
8887             if (winding) {
8888             switch(code) {
8889                 case 1: PDFwrite(buf, 100, "S\n", pd); break;
8890                 case 2: PDFwrite(buf, 100, "f\n", pd); break;
8891                 case 3: PDFwrite(buf, 100, "B\n", pd); break;
8892                 }
8893             } else {
8894                 switch(code) {
8895                 case 1: PDFwrite(buf, 100, "S\n", pd); break;
8896                 case 2: PDFwrite(buf, 100, "f*\n", pd); break;
8897                 case 3: PDFwrite(buf, 100, "B*\n", pd); break;
8898                 }
8899             }
8900         }
8901     }
8902 }
8903 
PDF_Polyline(int n,double * x,double * y,const pGEcontext gc,pDevDesc dd)8904 static void PDF_Polyline(int n, double *x, double *y,
8905 			 const pGEcontext gc,
8906 			 pDevDesc dd)
8907 {
8908     PDFDesc *pd = (PDFDesc*) dd->deviceSpecific;
8909     double xx, yy;
8910     int i;
8911     char buf[100];
8912 
8913     PDF_checkOffline();
8914 
8915     if(pd->inText) textoff(pd);
8916     if(R_VIS(gc->col)) {
8917         if (!pd->appendingClipPath) {
8918             PDF_SetLineColor(gc->col, dd);
8919             PDF_SetLineStyle(gc, dd);
8920         }
8921         if (pd->currentMask >= 0) {
8922             PDFwriteMask(pd->currentMask, pd);
8923         }
8924 	xx = x[0];
8925 	yy = y[0];
8926 	PDFwrite(buf, 100, "%.2f %.2f m\n", pd, xx, yy);
8927 	for(i = 1 ; i < n ; i++) {
8928 	    xx = x[i];
8929 	    yy = y[i];
8930 	    PDFwrite(buf, 100, "%.2f %.2f l\n", pd, xx, yy);
8931 	}
8932 	PDFwrite(buf, 100, "S\n", pd);
8933     }
8934 }
8935 
PDFfontNumber(const char * family,int face,PDFDesc * pd)8936 static int PDFfontNumber(const char *family, int face, PDFDesc *pd)
8937 {
8938     /* DingBats is font 1 */
8939     int num = 1;
8940 
8941     if (strlen(family) > 0) {
8942 	int fontIndex, cidfontIndex;
8943 	/*
8944 	 * Try to find font in already loaded fonts
8945 	 */
8946 	type1fontfamily fontfamily = findDeviceFont(family, pd->fonts,
8947 						    &fontIndex);
8948 	cidfontfamily cidfontfamily = findDeviceCIDFont(family, pd->cidfonts,
8949 							&cidfontIndex);
8950 	if (fontfamily)
8951 	    num = (fontIndex - 1)*5 + 1 + face;
8952 	else if (cidfontfamily)
8953 	    /*
8954 	     * Use very high font number for CID fonts to avoid
8955 	     * Type 1 fonts
8956 	     */
8957 	    num = 1000 + (cidfontIndex - 1)*5 + face;
8958 	else {
8959 	    /*
8960 	     * Check whether the font is loaded and, if not,
8961 	     * load it.
8962 	     */
8963 	    fontfamily = findLoadedFont(family,
8964 					pd->encodings->encoding->encpath,
8965 					TRUE);
8966 	    cidfontfamily = findLoadedCIDFont(family, TRUE);
8967 	    if (!(fontfamily || cidfontfamily)) {
8968 		if (isType1Font(family, PDFFonts, NULL)) {
8969 		    fontfamily = addFont(family, TRUE, pd->encodings);
8970 		} else if (isCIDFont(family, PDFFonts, NULL)) {
8971 		    cidfontfamily = addCIDFont(family, TRUE);
8972 		} else {
8973 		    /*
8974 		     * Should NOT get here.
8975 		     */
8976 		    error(_("invalid font type"));
8977 		}
8978 	    }
8979 	    /*
8980 	     * Once the font is loaded, add it to the device's
8981 	     * list of fonts.
8982 	     */
8983 	    if (fontfamily || cidfontfamily) {
8984 		if (isType1Font(family, PDFFonts, NULL)) {
8985 		    if (addPDFDevicefont(fontfamily, pd, &fontIndex)) {
8986 			num = (fontIndex - 1)*5 + 1 + face;
8987 		    } else {
8988 			fontfamily = NULL;
8989 		    }
8990 		} else /* (isCIDFont(family, PDFFonts)) */ {
8991 		    if (addPDFDeviceCIDfont(cidfontfamily, pd,
8992 					    &cidfontIndex)) {
8993 			num = 1000 + (cidfontIndex - 1)*5 + face;
8994 		    } else {
8995 			cidfontfamily = NULL;
8996 		    }
8997 		}
8998 	    }
8999 	}
9000 	if (!(fontfamily || cidfontfamily))
9001 	    error(_("failed to find or load PDF font"));
9002     } else {
9003 	if (isType1Font(family, PDFFonts, pd->defaultFont))
9004 	    num = 1 + face;
9005 	else
9006 	    num = 1000 + face;
9007     }
9008     if(num < 100) pd->fontUsed[num] = TRUE;
9009     return num;
9010 }
9011 
9012 /* added for 2.9.0 (donated by Ei-ji Nakama) : */
PDFWriteT1KerningString(FILE * fp,const char * str,FontMetricInfo * metrics,const pGEcontext gc)9013 static void PDFWriteT1KerningString(FILE *fp, const char *str,
9014 				    FontMetricInfo *metrics,
9015 				    const pGEcontext gc)
9016 {
9017     unsigned char p1, p2;
9018     size_t i, n;
9019     int j, ary_buf[128], *ary;
9020     Rboolean haveKerning = FALSE;
9021 
9022     n = strlen(str);
9023     if (n < 1) return;
9024     if(n > sizeof(ary_buf)/sizeof(int))
9025 	ary = Calloc(n, int);
9026     else ary = ary_buf;
9027 
9028     for(i = 0; i < n-1; i++) {
9029 	ary[i] = 0.;
9030 	p1 = str[i];
9031 	p2 = str[i+1];
9032 #ifdef USE_HYPHEN
9033 	if (p1 == '-' && !isdigit((int)p2))
9034 	    p1 = (unsigned char)PS_hyphen;
9035 #endif
9036 	for (j = metrics->KPstart[p1]; j < metrics->KPend[p1]; j++)
9037 	    if(metrics->KernPairs[j].c2 == p2 &&
9038 	       metrics->KernPairs[j].c1 == p1) {
9039 		ary[i] += metrics->KernPairs[j].kern;
9040 		haveKerning = TRUE;
9041 		break;
9042 	    }
9043     }
9044     ary[i] = 0;
9045     if(haveKerning) {
9046 	fputc('[', fp); fputc('(', fp);
9047 	for(i =  0; str[i]; i++) {
9048 	    switch(str[i]) {
9049 	    case '\n':
9050 		fprintf(fp, "\\n");
9051 		break;
9052 	    case '\\':
9053 		fprintf(fp, "\\\\");
9054 		break;
9055 	    case '-':
9056 #ifdef USE_HYPHEN
9057 		if (!isdigit((int)str[i+1]))
9058 		    fputc(PS_hyphen, fp);
9059 		else
9060 #endif
9061 		    fputc(str[i], fp);
9062 		break;
9063 	    case '(':
9064 	    case ')':
9065 		fprintf(fp, "\\%c", str[i]);
9066 		break;
9067 	    default:
9068 		fputc(str[i], fp);
9069 		break;
9070 	    }
9071 	    if( ary[i] != 0 && str[i+1] ) fprintf(fp, ") %d (", -ary[i]);
9072 	}
9073 	fprintf(fp, ")] TJ\n");
9074     } else {
9075 	PostScriptWriteString(fp, str, strlen(str));
9076 	fprintf(fp, " Tj\n");
9077     }
9078 
9079     if(ary != ary_buf) Free(ary);
9080 }
9081 
9082 static FontMetricInfo *PDFmetricInfo(const char *, int, PDFDesc *);
PDFSimpleText(double x,double y,const char * str,double rot,double hadj,int font,const pGEcontext gc,pDevDesc dd)9083 static void PDFSimpleText(double x, double y, const char *str,
9084 			  double rot, double hadj,
9085 			  int font,
9086 			  const pGEcontext gc,
9087 			  pDevDesc dd) {
9088     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
9089     int size = (int)floor(gc->cex * gc->ps + 0.5);
9090     int face = gc->fontface;
9091     double a, b, bm, rot1;
9092 
9093     if(!R_VIS(gc->col) || size <= 0) return;
9094 
9095     if(face < 1 || face > 5) {
9096 	warning(_("attempt to use invalid font %d replaced by font 1"), face);
9097 	face = 1;
9098     }
9099     rot1 = rot * DEG2RAD;
9100     a = size * cos(rot1);
9101     b = size * sin(rot1);
9102     bm = -b;
9103     /* avoid printing -0.00 on rotated text */
9104     if(fabs(a) < 0.01) a = 0.0;
9105     if(fabs(b) < 0.01) {b = 0.0; bm = 0.0;}
9106     if(!pd->inText) texton(pd);
9107     PDF_SetFill(gc->col, dd);
9108     fprintf(pd->pdffp, "/F%d 1 Tf %.2f %.2f %.2f %.2f %.2f %.2f Tm ",
9109 	    font,
9110 	    a, b, bm, a, x, y);
9111     if (pd->useKern &&
9112 	isType1Font(gc->fontfamily, PDFFonts, pd->defaultFont)) {
9113 	PDFWriteT1KerningString(pd->pdffp, str,
9114 				PDFmetricInfo(gc->fontfamily, face, pd), gc);
9115     } else {
9116 	PostScriptWriteString(pd->pdffp, str, strlen(str));
9117 	fprintf(pd->pdffp, " Tj\n");
9118     }
9119     textoff(pd); /* added in 2.8.0 */
9120 }
9121 
9122 static char *PDFconvname(const char *family, PDFDesc *pd);
9123 
PDF_Text0(double x,double y,const char * str,int enc,double rot,double hadj,const pGEcontext gc,pDevDesc dd)9124 static void PDF_Text0(double x, double y, const char *str, int enc,
9125 		      double rot, double hadj,
9126 		      const pGEcontext gc,
9127 		      pDevDesc dd)
9128 {
9129     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
9130     int size = (int) floor(gc->cex * gc->ps + 0.5);
9131     int face = gc->fontface;
9132     double a, b, bm, rot1;
9133     char *buff;
9134     const char *str1;
9135 
9136     PDF_checkOffline();
9137 
9138     if(!R_VIS(gc->col) || size <= 0) return;
9139 
9140     if(face < 1 || face > 5) {
9141 	warning(_("attempt to use invalid font %d replaced by font 1"), face);
9142 	face = 1;
9143     }
9144     if (face == 5) {
9145 	PDFSimpleText(x, y, str, rot, hadj,
9146 		      PDFfontNumber(gc->fontfamily, face, pd),
9147 		      gc, dd);
9148 	return;
9149     }
9150 
9151     rot1 = rot * DEG2RAD;
9152     a = size * cos(rot1);
9153     b = size * sin(rot1);
9154     bm = -b;
9155     /* avoid printing -0.00 on rotated text */
9156     if(fabs(a) < 0.01) a = 0.0;
9157     if(fabs(b) < 0.01) {b = 0.0; bm = 0.0;}
9158     if(!pd->inText) texton(pd);
9159 
9160     if(isCIDFont(gc->fontfamily, PDFFonts, pd->defaultCIDFont) && face != 5) {
9161 	/* NB we could be in a SBCS here */
9162 	size_t ucslen;
9163 	unsigned char *p;
9164 	int fontIndex;
9165 
9166 	/*
9167 	 * CID convert optimize PDF encoding == locale encode case
9168 	 */
9169 	cidfontfamily cidfont = findDeviceCIDFont(gc->fontfamily,
9170 						  pd->cidfonts,
9171 						  &fontIndex);
9172 	if (!cidfont) {
9173 	    int dontcare;
9174 	    /*
9175 	     * Try to load the font
9176 	     */
9177 	    cidfont = addCIDFont(gc->fontfamily, 1);
9178 	    if (cidfont) {
9179 		if (!addPDFDeviceCIDfont(cidfont, pd, &dontcare)) {
9180 		    cidfont = NULL;
9181 		}
9182 	    }
9183 	}
9184 	if (!cidfont)
9185 	    error(_("failed to find or load PDF CID font"));
9186 	if(!dd->hasTextUTF8 &&
9187 	   !strcmp(locale2charset(NULL), cidfont->encoding)) {
9188 	    PDF_SetFill(gc->col, dd);
9189 	    fprintf(pd->pdffp,
9190 		    "/F%d 1 Tf %.2f %.2f %.2f %.2f %.2f %.2f Tm ",
9191 		    PDFfontNumber(gc->fontfamily, face, pd),
9192 		    a, b, bm, a, x, y);
9193 
9194 	    fprintf(pd->pdffp, "<");
9195 	    p = (unsigned char *) str;
9196 	    while(*p)
9197 		fprintf(pd->pdffp, "%02x", *p++);
9198 	    fprintf(pd->pdffp, ">");
9199 	    fprintf(pd->pdffp, " Tj\n");
9200 	    return;
9201 	}
9202 
9203 	/*
9204 	 * CID convert  PDF encoding != locale encode case
9205 	 */
9206 	ucslen = (dd->hasTextUTF8) ? Rf_utf8towcs(NULL, str, 0): mbstowcs(NULL, str, 0);
9207 	if (ucslen != (size_t)-1) {
9208 	    void *cd;
9209 	    const char *i_buf; char *o_buf;
9210 	    size_t i, nb, i_len,  o_len, buflen = ucslen*sizeof(R_ucs2_t);
9211 	    size_t status;
9212 
9213 	    cd = (void*)Riconv_open(cidfont->encoding,
9214 				    (enc == CE_UTF8) ? "UTF-8": "");
9215 	    if(cd  == (void*)-1) return;
9216 
9217 	    R_CheckStack2(buflen);
9218 	    unsigned char buf[buflen];
9219 
9220 	    i_buf = (char *)str;
9221 	    o_buf = (char *)buf;
9222 	    i_len = strlen(str); /* no terminator,
9223 				    as output a byte at a time */
9224 	    nb = o_len = buflen;
9225 
9226 	    status = Riconv(cd, &i_buf, (size_t *)&i_len,
9227 			    (char **)&o_buf, (size_t *)&o_len);
9228 
9229 	    Riconv_close(cd);
9230 	    if(status == (size_t)-1)
9231 		warning(_("failed in text conversion to encoding '%s'"),
9232 			cidfont->encoding);
9233 	    else {
9234 		unsigned char *p;
9235 		PDF_SetFill(gc->col, dd);
9236 		fprintf(pd->pdffp,
9237 			"/F%d 1 Tf %.2f %.2f %.2f %.2f %.2f %.2f Tm <",
9238 			PDFfontNumber(gc->fontfamily, face, pd),
9239 			a, b, bm, a, x, y);
9240 		for(i = 0, p = buf; i < nb - o_len; i++)
9241 		    fprintf(pd->pdffp, "%02x", *p++);
9242 		fprintf(pd->pdffp, "> Tj\n");
9243 	    }
9244 	    return;
9245 	} else {
9246 	    warning(_("invalid string in '%s'"), "PDF_Text");
9247 	    return;
9248 	}
9249     }
9250 
9251     PDF_SetFill(gc->col, dd);
9252     fprintf(pd->pdffp, "/F%d 1 Tf %.2f %.2f %.2f %.2f %.2f %.2f Tm ",
9253 	    PDFfontNumber(gc->fontfamily, face, pd),
9254 	    a, b, bm, a, x, y);
9255     if((enc == CE_UTF8 || mbcslocale) && !strIsASCII(str) && face < 5) {
9256 	/* face 5 handled above */
9257 	R_CheckStack2(strlen(str)+1);
9258 	buff = alloca(strlen(str)+1); /* Output string cannot be longer */
9259 	mbcsToSbcs(str, buff, PDFconvname(gc->fontfamily, pd), enc);
9260 	str1 = buff;
9261     } else str1 = str;
9262 
9263     if (pd->useKern &&
9264 	isType1Font(gc->fontfamily, PDFFonts, pd->defaultFont)) {
9265 	PDFWriteT1KerningString(pd->pdffp, str1,
9266 				PDFmetricInfo(gc->fontfamily, face, pd), gc);
9267     } else{
9268 	PostScriptWriteString(pd->pdffp, str1, strlen(str1));
9269 	fprintf(pd->pdffp, " Tj\n");
9270     }
9271     textoff(pd); /* added in 2.8.0 */
9272 }
9273 
PDF_Text(double x,double y,const char * str,double rot,double hadj,const pGEcontext gc,pDevDesc dd)9274 static void PDF_Text(double x, double y, const char *str,
9275 		      double rot, double hadj,
9276 		      const pGEcontext gc,
9277 		      pDevDesc dd)
9278 {
9279     PDF_Text0(x, y, str, CE_NATIVE, rot, hadj, gc, dd);
9280 }
9281 
PDF_TextUTF8(double x,double y,const char * str,double rot,double hadj,const pGEcontext gc,pDevDesc dd)9282 static void PDF_TextUTF8(double x, double y, const char *str,
9283 			 double rot, double hadj,
9284 			 const pGEcontext gc,
9285 			 pDevDesc dd)
9286 {
9287     PDF_Text0(x, y, str, CE_UTF8, rot, hadj, gc, dd);
9288 }
9289 
9290 static FontMetricInfo
PDFCIDsymbolmetricInfo(const char * family,PDFDesc * pd)9291 *PDFCIDsymbolmetricInfo(const char *family, PDFDesc *pd)
9292 {
9293     FontMetricInfo *result = NULL;
9294     if (strlen(family) > 0) {
9295 	int dontcare;
9296 	/*
9297 	 * Find the family in pd->cidfonts
9298 	 */
9299 	cidfontfamily fontfamily = findDeviceCIDFont(family,
9300 						     pd->cidfonts,
9301 						     &dontcare);
9302 	if (fontfamily)
9303 	    result = &(fontfamily->symfont->metrics);
9304 	else {
9305 	    /*
9306 	     * Try to load the font
9307 	     */
9308 	    fontfamily = addCIDFont(family, 1);
9309 	    if (fontfamily) {
9310 		if (addPDFDeviceCIDfont(fontfamily, pd, &dontcare)) {
9311 		    result = &(fontfamily->symfont->metrics);
9312 		} else {
9313 		    fontfamily = NULL;
9314 		}
9315 	    }
9316 	}
9317 	if (!fontfamily)
9318 	    error(_("failed to find or load PDF CID font"));
9319     } else {
9320 	result = &(pd->cidfonts->cidfamily->symfont->metrics);
9321     }
9322     return result;
9323 }
9324 
9325 static FontMetricInfo
PDFmetricInfo(const char * family,int face,PDFDesc * pd)9326 *PDFmetricInfo(const char *family, int face, PDFDesc *pd)
9327 {
9328     FontMetricInfo *result = NULL;
9329     if (strlen(family) > 0) {
9330 	int dontcare;
9331 	/*
9332 	 * Find the family in pd->fonts
9333 	 */
9334 	type1fontfamily fontfamily = findDeviceFont(family, pd->fonts,
9335 						    &dontcare);
9336 	if (fontfamily)
9337 	    result = &(fontfamily->fonts[face-1]->metrics);
9338 	else {
9339 	    /*
9340 	     * Check whether the font is loaded and, if not,
9341 	     * load it.
9342 	     */
9343 	    fontfamily = findLoadedFont(family,
9344 					pd->encodings->encoding->encpath,
9345 					TRUE);
9346 	    if (!fontfamily) {
9347 		fontfamily = addFont(family, TRUE, pd->encodings);
9348 	    }
9349 	    /*
9350 	     * Once the font is loaded, add it to the device's
9351 	     * list of fonts.
9352 	     */
9353 	    if (fontfamily) {
9354 		int dontcare;
9355 		if (addPDFDevicefont(fontfamily, pd, &dontcare)) {
9356 		    result = &(fontfamily->fonts[face-1]->metrics);
9357 		} else {
9358 		    fontfamily = NULL;
9359 		}
9360 	    }
9361 	}
9362 	if (!fontfamily)
9363 	    error(_("failed to find or load PDF font"));
9364     } else {
9365 	result = &(pd->fonts->family->fonts[face-1]->metrics);
9366     }
9367     return result;
9368 }
9369 
9370 static char
PDFconvname(const char * family,PDFDesc * pd)9371 *PDFconvname(const char *family, PDFDesc *pd)
9372 {
9373     char *result = (pd->fonts) ? pd->fonts->family->encoding->convname : "latin1";
9374     /* pd->fonts is NULL when CIDfonts are used */
9375 
9376     if (strlen(family) > 0) {
9377 	int dontcare;
9378 	/*
9379 	 * Find the family in pd->fonts
9380 	 */
9381 	type1fontfamily fontfamily = findDeviceFont(family, pd->fonts,
9382 						    &dontcare);
9383 	if (fontfamily)
9384 	    result = fontfamily->encoding->convname;
9385 	else {
9386 	    /*
9387 	     * Check whether the font is loaded and, if not,
9388 	     * load it.
9389 	     */
9390 	    fontfamily = findLoadedFont(family,
9391 					pd->encodings->encoding->encpath,
9392 					TRUE);
9393 	    if (!fontfamily) {
9394 		fontfamily = addFont(family, TRUE, pd->encodings);
9395 	    }
9396 	    /*
9397 	     * Once the font is loaded, add it to the device's
9398 	     * list of fonts.
9399 	     */
9400 	    if (fontfamily) {
9401 		int dontcare;
9402 		if (addPDFDevicefont(fontfamily, pd, &dontcare)) {
9403 		    result = fontfamily->encoding->convname;
9404 		} else {
9405 		    fontfamily = NULL;
9406 		}
9407 	    }
9408 	}
9409 	if (!fontfamily)
9410 	    error(_("failed to find or load PDF font"));
9411     }
9412     return result;
9413 }
9414 
PDF_StrWidth(const char * str,const pGEcontext gc,pDevDesc dd)9415 double PDF_StrWidth(const char *str,
9416                     const pGEcontext gc,
9417                     pDevDesc dd)
9418 {
9419     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
9420 
9421     if(gc->fontface < 1 || gc->fontface > 5) gc->fontface = 1;
9422     if (isType1Font(gc->fontfamily, PDFFonts, pd->defaultFont)) {
9423 	return floor(gc->cex * gc->ps + 0.5) *
9424 	    PostScriptStringWidth((const unsigned char *)str, CE_NATIVE,
9425 				  PDFmetricInfo(gc->fontfamily,
9426 						gc->fontface, pd),
9427 				  pd->useKern, gc->fontface,
9428 				  PDFconvname(gc->fontfamily, pd));
9429     } else { /* cidfont(gc->fontfamily) */
9430 	if (gc->fontface < 5) {
9431 	    return floor(gc->cex * gc->ps + 0.5) *
9432 		PostScriptStringWidth((const unsigned char *)str, CE_NATIVE,
9433 				      NULL, FALSE, gc->fontface, NULL);
9434 	} else {
9435 	    return floor(gc->cex * gc->ps + 0.5) *
9436 		PostScriptStringWidth((const unsigned char *)str, CE_NATIVE,
9437 				      PDFCIDsymbolmetricInfo(gc->fontfamily,
9438 							     pd),
9439 				      FALSE, gc->fontface, NULL);
9440 	}
9441     }
9442 }
9443 
PDF_StrWidthUTF8(const char * str,const pGEcontext gc,pDevDesc dd)9444 static double PDF_StrWidthUTF8(const char *str,
9445 			       const pGEcontext gc,
9446 			       pDevDesc dd)
9447 {
9448     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
9449     int face = gc->fontface;
9450 
9451     if(gc->fontface < 1 || gc->fontface > 5) gc->fontface = 1;
9452     if (isType1Font(gc->fontfamily, PDFFonts, pd->defaultFont)) {
9453 	return floor(gc->cex * gc->ps + 0.5) *
9454 	    PostScriptStringWidth((const unsigned char *)str, CE_UTF8,
9455 				  PDFmetricInfo(gc->fontfamily,
9456 						gc->fontface, pd),
9457 				  pd->useKern, gc->fontface,
9458 				  PDFconvname(gc->fontfamily, pd));
9459     } else { /* cidfont(gc->fontfamily) */
9460 	if (face < 5) {
9461 	    return floor(gc->cex * gc->ps + 0.5) *
9462 		PostScriptStringWidth((const unsigned char *)str, CE_UTF8,
9463 				      NULL, FALSE, gc->fontface, NULL);
9464 	} else {
9465 	    return floor(gc->cex * gc->ps + 0.5) *
9466 		PostScriptStringWidth((const unsigned char *)str, CE_UTF8,
9467 				      PDFCIDsymbolmetricInfo(gc->fontfamily,
9468 							     pd),
9469 				      FALSE, gc->fontface, NULL);
9470 	}
9471     }
9472 }
9473 
PDF_MetricInfo(int c,const pGEcontext gc,double * ascent,double * descent,double * width,pDevDesc dd)9474 void PDF_MetricInfo(int c,
9475                     const pGEcontext gc,
9476                     double* ascent, double* descent,
9477                     double* width, pDevDesc dd)
9478 {
9479     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
9480     int face = gc->fontface;
9481 
9482     if(gc->fontface < 1 || gc->fontface > 5) gc->fontface = 1;
9483     if (isType1Font(gc->fontfamily, PDFFonts, pd->defaultFont)) {
9484 	PostScriptMetricInfo(c, ascent, descent, width,
9485 			     PDFmetricInfo(gc->fontfamily,
9486 					   gc->fontface, pd),
9487 			     face == 5, PDFconvname(gc->fontfamily, pd));
9488     } else { /* cidfont(gc->fontfamily) */
9489 	if (face < 5) {
9490 	    PostScriptCIDMetricInfo(c, ascent, descent, width);
9491 	} else {
9492 	    PostScriptMetricInfo(c, ascent, descent, width,
9493 				 PDFCIDsymbolmetricInfo(gc->fontfamily, pd),
9494 				 TRUE, "");
9495 	}
9496     }
9497     *ascent = floor(gc->cex * gc->ps + 0.5) * *ascent;
9498     *descent = floor(gc->cex * gc->ps + 0.5) * *descent;
9499     *width = floor(gc->cex * gc->ps + 0.5) * *width;
9500 }
9501 
PDF_setPattern(SEXP pattern,pDevDesc dd)9502 static SEXP PDF_setPattern(SEXP pattern, pDevDesc dd) {
9503     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
9504     SEXP ref = R_NilValue;
9505     ref = addPattern(pattern, pd);
9506     return ref;
9507 }
9508 
PDF_releasePattern(SEXP ref,pDevDesc dd)9509 static void PDF_releasePattern(SEXP ref, pDevDesc dd) {}
9510 
PDF_setClipPath(SEXP path,SEXP ref,pDevDesc dd)9511 static SEXP PDF_setClipPath(SEXP path, SEXP ref, pDevDesc dd) {
9512     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
9513     SEXP newref = R_NilValue;
9514 
9515     if (isNull(ref)) {
9516         /* Generate new clipping path */
9517         int index = newClipPath(path, pd);
9518         if (index >= 0) {
9519             PDFwriteClipPath(index, pd);
9520             PROTECT(newref = allocVector(INTSXP, 1));
9521             INTEGER(newref)[0] = index;
9522             UNPROTECT(1);
9523         }
9524     } else {
9525         /* Reuse existing clipping path */
9526         int index = INTEGER(ref)[0];
9527         PDFwriteClipPath(index, pd);
9528         newref = ref;
9529     }
9530 
9531     PDF_Invalidate(pd);
9532     return newref;
9533 
9534 }
9535 
PDF_releaseClipPath(SEXP ref,pDevDesc dd)9536 static void PDF_releaseClipPath(SEXP ref, pDevDesc dd) {}
9537 
PDF_setMask(SEXP path,SEXP ref,pDevDesc dd)9538 static SEXP PDF_setMask(SEXP path, SEXP ref, pDevDesc dd) {
9539     PDFDesc *pd = (PDFDesc *) dd->deviceSpecific;
9540     ref = addMask(path, ref, pd);
9541     return ref;
9542 }
9543 
PDF_releaseMask(SEXP ref,pDevDesc dd)9544 static void PDF_releaseMask(SEXP ref, pDevDesc dd) {}
9545 
9546 
9547 /*  PostScript Device Driver Parameters:
9548  *  ------------------------
9549  *  file	= output filename
9550  *  paper	= paper type
9551  *  family	= typeface = "family"
9552  *  encoding	= char encoding file name
9553  *  cidfamily	= char encoding file name for CID fonts
9554  *  bg		= background color
9555  *  fg		= foreground color
9556  *  width	= width in inches
9557  *  height	= height in inches
9558  *  horizontal	= {TRUE: landscape; FALSE: portrait}
9559  *  ps		= pointsize
9560  *  onefile     = {TRUE: normal; FALSE: single EPSF page}
9561  *  pagecentre  = centre plot region on paper?
9562  *  printit     = 'print' after closing device?
9563  *  command     = 'print' command
9564  *  title       = character string
9565  *  fonts
9566  *  colorModel
9567  *  useKerning
9568  *  fillOddEven
9569  */
9570 
PostScript(SEXP args)9571 SEXP PostScript(SEXP args)
9572 {
9573     pGEDevDesc gdd;
9574     const void *vmax;
9575     const char *file, *paper, *family=NULL, *bg, *fg, *cmd;
9576     const char *afms[5];
9577     const char *encoding, *title, call[] = "postscript", *colormodel;
9578     int i, horizontal, onefile, pagecentre, printit, useKern;
9579     double height, width, ps;
9580     SEXP fam, fonts;
9581     Rboolean fillOddEven;
9582 
9583     vmax = vmaxget();
9584     args = CDR(args); /* skip entry point name */
9585     file = translateCharFP(asChar(CAR(args)));  args = CDR(args);
9586     paper = CHAR(asChar(CAR(args))); args = CDR(args);
9587 
9588     /* 'family' can be either one string or a 5-vector of afmpaths. */
9589     fam = CAR(args); args = CDR(args);
9590     if(length(fam) == 1)
9591 	family = CHAR(asChar(fam));
9592     else if(length(fam) == 5) {
9593 	if(!isString(fam)) error(_("invalid 'family' parameter in %s"), call);
9594 	family = "User";
9595 	for(i = 0; i < 5; i++) afms[i] = CHAR(STRING_ELT(fam, i));
9596     } else
9597 	error(_("invalid 'family' parameter in %s"), call);
9598 
9599     encoding = CHAR(asChar(CAR(args)));    args = CDR(args);
9600     bg = CHAR(asChar(CAR(args)));    args = CDR(args);
9601     fg = CHAR(asChar(CAR(args)));    args = CDR(args);
9602     width = asReal(CAR(args));	      args = CDR(args);
9603     height = asReal(CAR(args));	      args = CDR(args);
9604     horizontal = asLogical(CAR(args));args = CDR(args);
9605     if(horizontal == NA_LOGICAL)
9606 	horizontal = 1;
9607     ps = asReal(CAR(args));	      args = CDR(args);
9608     onefile = asLogical(CAR(args));   args = CDR(args);
9609     pagecentre = asLogical(CAR(args));args = CDR(args);
9610     printit = asLogical(CAR(args));   args = CDR(args);
9611     cmd = CHAR(asChar(CAR(args)));    args = CDR(args);
9612     title = translateChar(asChar(CAR(args)));  args = CDR(args);
9613     fonts = CAR(args);		      args = CDR(args);
9614     if (!isNull(fonts) && !isString(fonts))
9615 	error(_("invalid 'fonts' parameter in %s"), call);
9616     colormodel = CHAR(asChar(CAR(args)));  args = CDR(args);
9617     useKern = asLogical(CAR(args));   args = CDR(args);
9618     if (useKern == NA_LOGICAL) useKern = 1;
9619     fillOddEven = asLogical(CAR(args));
9620     if (fillOddEven == NA_LOGICAL)
9621 	error(_("invalid value of '%s'"), "fillOddEven");
9622 
9623     R_GE_checkVersionOrDie(R_GE_version);
9624     R_CheckDeviceAvailable();
9625     BEGIN_SUSPEND_INTERRUPTS {
9626 	pDevDesc dev;
9627 	if (!(dev = (pDevDesc) calloc(1, sizeof(DevDesc))))
9628 	    return 0;
9629 	if(!PSDeviceDriver(dev, file, paper, family, afms, encoding, bg, fg,
9630 			   width, height, (double)horizontal, ps, onefile,
9631 			   pagecentre, printit, cmd, title, fonts,
9632 			   colormodel, useKern, fillOddEven)) {
9633 	    /* we no longer get here: error is thrown in PSDeviceDriver */
9634 	    error(_("unable to start %s() device"), "postscript");
9635 	}
9636 	gdd = GEcreateDevDesc(dev);
9637 	GEaddDevice2f(gdd, "postscript", file);
9638     } END_SUSPEND_INTERRUPTS;
9639     vmaxset(vmax);
9640     return R_NilValue;
9641 }
9642 
9643 
9644 
9645 /*  XFig Device Driver Parameters:
9646  *  ------------------------
9647  *  file	= output filename
9648  *  paper	= paper type
9649  *  family	= typeface = "family"
9650  *  bg		= background color
9651  *  fg		= foreground color
9652  *  width	= width in inches
9653  *  height	= height in inches
9654  *  horizontal	= {TRUE: landscape; FALSE: portrait}
9655  *  ps		= pointsize
9656  *  onefile     = {TRUE: normal; FALSE: single EPSF page}
9657  *  pagecentre  = centre plot region on paper?
9658  *  defaultfont = {TRUE: use xfig default font; FALSE: use R font}
9659  *  textspecial = {TRUE: use textspecial; FALSE: use standard font}
9660  *
9661  *  encoding
9662  */
9663 
XFig(SEXP args)9664 SEXP XFig(SEXP args)
9665 {
9666     pGEDevDesc gdd;
9667     const void *vmax;
9668     const char *file, *paper, *family, *bg, *fg, *encoding;
9669     int horizontal, onefile, pagecentre, defaultfont, textspecial;
9670     double height, width, ps;
9671 
9672     vmax = vmaxget();
9673     args = CDR(args); /* skip entry point name */
9674     file = translateCharFP(asChar(CAR(args)));  args = CDR(args);
9675     paper = CHAR(asChar(CAR(args))); args = CDR(args);
9676     family = CHAR(asChar(CAR(args)));  args = CDR(args);
9677     bg = CHAR(asChar(CAR(args)));    args = CDR(args);
9678     fg = CHAR(asChar(CAR(args)));    args = CDR(args);
9679     width = asReal(CAR(args));	      args = CDR(args);
9680     height = asReal(CAR(args));	      args = CDR(args);
9681     horizontal = asLogical(CAR(args));args = CDR(args);
9682     if(horizontal == NA_LOGICAL)
9683 	horizontal = 1;
9684     ps = asReal(CAR(args));	      args = CDR(args);
9685     onefile = asLogical(CAR(args));   args = CDR(args);
9686     pagecentre = asLogical(CAR(args));args = CDR(args);
9687     defaultfont = asLogical(CAR(args)); args = CDR(args);
9688     textspecial = asLogical(CAR(args)); args = CDR(args);
9689     encoding = CHAR(asChar(CAR(args)));
9690 
9691     R_GE_checkVersionOrDie(R_GE_version);
9692     R_CheckDeviceAvailable();
9693     BEGIN_SUSPEND_INTERRUPTS {
9694 	pDevDesc dev;
9695 	if (!(dev = (pDevDesc) calloc(1, sizeof(DevDesc))))
9696 	    return 0;
9697 	if(!XFigDeviceDriver(dev, file, paper, family, bg, fg, width, height,
9698 			     (double) horizontal, ps, onefile, pagecentre, defaultfont, textspecial,
9699 			     encoding)) {
9700 	    /* we no longer get here: error is thrown in XFigDeviceDriver */
9701 	    error(_("unable to start %s() device"), "xfig");
9702 	}
9703 	gdd = GEcreateDevDesc(dev);
9704 	GEaddDevice2f(gdd, "xfig", file);
9705     } END_SUSPEND_INTERRUPTS;
9706     vmaxset(vmax);
9707     return R_NilValue;
9708 }
9709 
9710 
9711 /*  PDF Device Driver Parameters:
9712  *  ------------------------
9713  *  file	= output filename
9714  *  paper       = paper type
9715  *  family	= typeface = "family"
9716  *  encoding	= char encoding file name
9717  *  cidfamily	= char encoding file name for CID fonts
9718  *  bg		= background color
9719  *  fg		= foreground color
9720  *  width	= width in inches
9721  *  height	= height in inches
9722  *  ps		= pointsize
9723  *  onefile     = {TRUE: normal; FALSE: single page per file}
9724  *  title
9725  *  fonts
9726  *  versionMajor
9727  *  versionMinor
9728  *  colormodel
9729  *  useDingbats
9730  *  forceLetterSpacing
9731  *  fillOddEven
9732  */
9733 
PDF(SEXP args)9734 SEXP PDF(SEXP args)
9735 {
9736     pGEDevDesc gdd;
9737     const void *vmax;
9738     const char *file, *paper, *encoding, *family = NULL /* -Wall */,
9739 	*bg, *fg, *title, call[] = "PDF", *colormodel;
9740     const char *afms[5];
9741     double height, width, ps;
9742     int i, onefile, pagecentre, major, minor, dingbats, useKern, useCompression;
9743     SEXP fam, fonts;
9744     Rboolean fillOddEven;
9745 
9746     vmax = vmaxget();
9747     args = CDR(args); /* skip entry point name */
9748     if (isNull(CAR(args)))
9749         file = NULL;
9750     else
9751         file = translateCharFP(asChar(CAR(args)));
9752     args = CDR(args);
9753     paper = CHAR(asChar(CAR(args))); args = CDR(args);
9754     fam = CAR(args); args = CDR(args);
9755     if(length(fam) == 1)
9756 	family = CHAR(asChar(fam));
9757     else if(length(fam) == 5) {
9758 	if(!isString(fam)) error(_("invalid 'family' parameter in %s"), call);
9759 	family = "User";
9760 	for(i = 0; i < 5; i++) afms[i] = CHAR(STRING_ELT(fam, i));
9761     } else
9762 	error(_("invalid 'family' parameter in %s"), call);
9763     encoding = CHAR(asChar(CAR(args)));  args = CDR(args);
9764     bg = CHAR(asChar(CAR(args)));    args = CDR(args);
9765     fg = CHAR(asChar(CAR(args)));    args = CDR(args);
9766     width = asReal(CAR(args));	      args = CDR(args);
9767     height = asReal(CAR(args));	      args = CDR(args);
9768     ps = asReal(CAR(args));           args = CDR(args);
9769     onefile = asLogical(CAR(args)); args = CDR(args);
9770     pagecentre = asLogical(CAR(args));args = CDR(args);
9771     title = translateChar(asChar(CAR(args))); args = CDR(args);
9772     fonts = CAR(args); args = CDR(args);
9773     if (!isNull(fonts) && !isString(fonts))
9774 	error(_("invalid 'fonts' parameter in %s"), call);
9775     major = asInteger(CAR(args)); args = CDR(args);
9776     minor = asInteger(CAR(args)); args = CDR(args);
9777     colormodel = CHAR(asChar(CAR(args))); args = CDR(args);
9778     dingbats = asLogical(CAR(args)); args = CDR(args);
9779     if (dingbats == NA_LOGICAL) dingbats = 1;
9780     useKern = asLogical(CAR(args)); args = CDR(args);
9781     if (useKern == NA_LOGICAL) useKern = 1;
9782     fillOddEven = asLogical(CAR(args)); args = CDR(args);
9783     if (fillOddEven == NA_LOGICAL)
9784 	error(_("invalid value of '%s'"), "fillOddEven");
9785     useCompression = asLogical(CAR(args)); args = CDR(args);
9786     if (useCompression == NA_LOGICAL)
9787 	error(_("invalid value of '%s'"), "useCompression");
9788 
9789     R_GE_checkVersionOrDie(R_GE_version);
9790     R_CheckDeviceAvailable();
9791     BEGIN_SUSPEND_INTERRUPTS {
9792 	pDevDesc dev;
9793 	if (!(dev = (pDevDesc) calloc(1, sizeof(DevDesc))))
9794 	    return 0;
9795 	if(!PDFDeviceDriver(dev, file, paper, family, afms, encoding, bg, fg,
9796 			    width, height, ps, onefile, pagecentre,
9797 			    title, fonts, major, minor, colormodel,
9798 			    dingbats, useKern, fillOddEven,
9799 			    useCompression)) {
9800 	    /* we no longer get here: error is thrown in PDFDeviceDriver */
9801 	    error(_("unable to start %s() device"), "pdf");
9802 	}
9803 	gdd = GEcreateDevDesc(dev);
9804 	GEaddDevice2f(gdd, "pdf", file);
9805     } END_SUSPEND_INTERRUPTS;
9806     vmaxset(vmax);
9807     return R_NilValue;
9808 }
9809 
9810