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