- Optification is done by auto builder now
[gnuplot] / term / pdf.trm
1 /* Hello, Emacs, this is -*-C-*-
2  * $Id: pdf.trm,v 1.68.2.9 2008/12/12 07:14:11 sfeam Exp $
3  */
4
5 /*------------------------------
6         GNUPLOT - pdf.trm
7
8         This file is included by ../term.c.
9
10         This driver uses PDFlib from www.pdflib.com
11
12         Author:
13
14                 Hans-Bernhard Br"oker
15                 broeker@physik.rwth-aachen.de
16
17         Licence: see the gnuplot copyright (to be merged into here...)
18
19         Options: can #define PDF_DONT_COMPRESS to avoid PDF output
20         generated being compressed (by the 'deflate' algorithm as used
21         in 'zip' or 'gzip'). That helps in debugging.
22
23 ------------------------------*/
24
25 /* CODEME: Add patterned lines (?). */
26
27 /* PM3D support by Johannes Zellner <johannes@zellner.org>, May-15-2002 */
28 /* set_color fixes by Petr Mikulik <mikulik@physics.muni.cz>, June-10-2002 */
29 /* image support by Ethan A Merritt <merritt@u.washington.edu>, March 2003 */
30
31 /* Text rotation 24-Jul-2002 Ethan A Merritt <merritt@u.washington.edu> */
32 /* Revised fill patterns 02-Apr-2003 Ethan A Merritt */
33 /* Enhanced text mode support November 2003 Ethan A Merritt */
34
35 #include "driver.h"
36
37 #ifdef TERM_REGISTER
38 register_term(pdf)
39 #endif
40
41 #ifdef TERM_PROTO
42 TERM_PUBLIC void PDF_options __PROTO ((void));
43 TERM_PUBLIC void PDF_init __PROTO ((void));
44 TERM_PUBLIC void PDF_graphics __PROTO ((void));
45 TERM_PUBLIC void PDF_text __PROTO ((void));
46 TERM_PUBLIC void PDF_linetype __PROTO ((int linetype));
47 TERM_PUBLIC void PDF_move __PROTO ((unsigned int x, unsigned int y));
48 TERM_PUBLIC void PDF_vector __PROTO ((unsigned int x, unsigned int y));
49 TERM_PUBLIC void PDF_put_text __PROTO ((unsigned int x, unsigned int y, const char *str));
50 TERM_PUBLIC void PDF_reset __PROTO ((void));
51 TERM_PUBLIC int PDF_justify_text __PROTO ((enum JUSTIFY mode));
52 TERM_PUBLIC int PDF_text_angle __PROTO ((int ang));
53 TERM_PUBLIC void PDF_point __PROTO ((unsigned int x, unsigned int y, int pointstyle));
54 TERM_PUBLIC int PDF_set_font __PROTO ((const char *font));
55 TERM_PUBLIC void PDF_boxfill __PROTO((int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height));
56 TERM_PUBLIC void PDF_linewidth __PROTO ((double linewidth));
57 TERM_PUBLIC int PDF_make_palette __PROTO((t_sm_palette *));
58 TERM_PUBLIC void PDF_previous_palette __PROTO((void));
59 TERM_PUBLIC void PDF_set_color __PROTO((t_colorspec *));
60 #ifdef WITH_IMAGE
61 TERM_PUBLIC void PDF_image __PROTO((unsigned, unsigned, coordval *, gpiPoint *, t_imagecolor));
62 #endif
63
64 TERM_PUBLIC void PDF_filled_polygon __PROTO((int, gpiPoint *));
65
66 /* To support "set term png enhanced" */
67 TERM_PUBLIC void ENHPDF_put_text __PROTO((unsigned int x, unsigned int y, const char *str));
68 TERM_PUBLIC void ENHPDF_OPEN __PROTO((char * fontname, double fontsize,
69                         double base, TBOOLEAN widthflag, TBOOLEAN showflag,
70                         int overprint));
71 TERM_PUBLIC void ENHPDF_FLUSH __PROTO((void));
72
73 #define PDF_NUM_POINTTYPES 75   /* number of point symbol types not counting the dot */
74
75 #define PDF_RESOLUTION  (20)    /* number of terminal pixels per pt */
76 #define PDF_XMAX        (5*72*PDF_RESOLUTION) /* 5 inches, 72 pt/inch */
77 #define PDF_YMAX        (3*72*PDF_RESOLUTION) /* 3 inches, 72 pt/inch */
78
79 static TBOOLEAN pdf_explicit_size = FALSE;
80 static size_units pdf_explicit_units = INCHES;
81
82 #endif /* TERM_PROTO */
83
84 #ifndef TERM_PROTO_ONLY
85 #ifdef TERM_BODY
86
87 #include <pdflib.h>
88
89 static PDF *myPDF = NULL;
90
91 static unsigned int PDF_xLast = UINT_MAX; /* current pen horizontal position*/
92 static unsigned int PDF_yLast = UINT_MAX; /* current pen vertical position*/
93
94 static int PDF_LineType = LT_UNDEFINED;         /* current line type*/
95 static int PDF_LineCap = 0;                     /* Butt ends */
96 static double PDF_LineWidth = 1.0;              /* current line width*/
97 static int PDF_TextAngle = 0;                   /* current text orientation*/
98 static enum JUSTIFY PDF_TextJust = LEFT;        /* current text justification*/
99 static double PDF_linewidth_factor = 1.0;       /* multiplier for line width */
100 static double PDF_dashlength_factor = 1.0;      /* multiplier for dash length */
101 static TBOOLEAN PDF_dashedlines = FALSE;        /* solid or dashed? */
102 static TBOOLEAN PDF_monochrome = FALSE;         /* default all linetypes to black */
103 static rgb_color PDF_current_rgb = {0.,0.,0.};  /* Last color set */
104 static double PDF_current_gray = 0.0;           /* Last color set (mono version) */
105
106 /* default text font family: */
107 static char PDF_fontNameDef[MAX_ID_LEN + 1] = "Helvetica";
108 static double PDF_fontSizeDef = 6;      /* default text size*/
109 /* current text font family: */
110 static char PDF_fontNameCur[MAX_ID_LEN + 1] = "Helvetica";
111 static double PDF_fontSizeCur = 6; /* current text size*/
112
113 static TBOOLEAN PDF_pageIsOpen = FALSE; /* already started a page ?? */
114 static TBOOLEAN PDF_pathIsOpen = FALSE; /* open path flag*/
115
116 static int PDF_fontAscent = 0;  /* estimated current font ascent*/
117 static int PDF_fontDescent = 0; /* estimated current font descent*/
118 static int PDF_fontLeading = 0; /* estimated current font leading*/
119 static int PDF_fontAvWidth = 0; /* estimated current font char average width*/
120 static int PDF_currentFontHandle; /* Needed for exhanced text mode */
121
122 static short PDF_Pen_RealID __PROTO ((int));
123 static void PDF_PathOpen __PROTO ((void));
124 static void PDF_PathClose __PROTO ((void));
125 static void PDF_SetFont __PROTO ((void));
126 static void PDF_DefinePatterns __PROTO((void));
127 enum { PDF_patterns = 7 };
128 static int PDF_patternHandles[PDF_patterns];
129
130 #ifndef HAVE_NODASH_LIBPDF
131 /* Description of dash patterns (same as those in post.trm) */
132 static int dash1[] = {8, 8};
133 static int dash2[] = {4, 6};
134 static int dash3[] = {2, 3};
135 static int dash4[] = {12, 4, 2, 4};
136 static int dash5[] = {6, 6, 2, 6};
137 static int dash6[] = {4, 4, 4, 12};
138 static int dash7[] = {1, 4, 12, 4, 1, 4};
139 #endif
140
141 /*------------------------ helper functions -------------------*/
142
143 static short
144 PDF_Pen_RealID (int inPenCode)
145 {
146     if (inPenCode >= 12)
147         inPenCode %= 12;        /* normalize pen code*/
148     if (inPenCode <= LT_NODRAW)
149         inPenCode = LT_NODRAW;
150
151     return (inPenCode + 2);
152 }
153
154 /* Functions to ensure that as many move() and vector() calls as
155  * possible get converted into a single long 'path', before closing it
156  * with a stroke or similar command. */
157 static void
158 PDF_PathOpen ()
159 {
160     PDF_pathIsOpen = TRUE;
161 }
162
163 static void
164 PDF_PathClose ()
165 {
166     if (PDF_pathIsOpen) {
167         PDF_stroke(myPDF);
168
169         PDF_pathIsOpen = FALSE;
170     }
171 }
172
173 /* Helper function to deal with switching over to a newly selected font.
174  * For now, this does not try to embed fonts into the PDF file.
175  * We would like to allow UTF-8 fonts via
176         font_handle = PDF_findfont(myPDF, PDF_fontNameCur, "unicode", 0);
177  * but this is not supported by the free-as-in-beer PDFlib Lite.
178  */
179 static void
180 PDF_SetFont ()
181 {
182     int font_handle;
183     const char *pdfenc = "host";
184
185     /* Allow graceful failure */
186     PDF_set_parameter(myPDF, "fontwarning", "false");
187
188     /* LCB : Symbol and ZapfDingbats should use "builtin" encoding */
189     if ( (strcmp(PDF_fontNameCur,"Symbol") == 0) ||
190          (strcmp(PDF_fontNameCur,"ZapfDingbats") == 0) ) {
191         pdfenc = "builtin";
192     } else if (encoding == S_ENC_ISO8859_1) {
193         pdfenc = "iso8859-1";
194     } else if (encoding == S_ENC_ISO8859_2) {
195         pdfenc = "iso8859-2";
196     } else if (encoding == S_ENC_ISO8859_15) {
197         pdfenc = "iso8859-15";
198     } else if (encoding == S_ENC_CP1250) {
199         pdfenc = "cp1250";
200     }
201         
202     font_handle = PDF_findfont(myPDF, PDF_fontNameCur, pdfenc, 0);
203     
204     if (font_handle == -1 && strcmp(pdfenc, "host")) {
205         fprintf(stderr,"Couldn't find font %s in encoding %s, trying \"host\"\n", 
206                 PDF_fontNameCur, pdfenc);
207         font_handle = PDF_findfont(myPDF, PDF_fontNameCur, "host", 0);
208     }
209
210     if (font_handle == -1) {
211         font_handle = PDF_findfont(myPDF, "Times-Roman", "host", 0);
212         fprintf(stderr,"Couldn't find font %s, falling back to Times-Roman\n", PDF_fontNameCur);
213     }
214
215     PDF_setfont(myPDF, font_handle, PDF_fontSizeCur * PDF_RESOLUTION);
216
217     /* Ask PDFlib for the actual numbers */
218     PDF_fontAscent = (int) (PDF_RESOLUTION * PDF_fontSizeCur * PDF_get_value(myPDF, "ascender", 0));
219     PDF_fontDescent = (int) (- PDF_RESOLUTION * PDF_fontSizeCur * PDF_get_value(myPDF, "descender", 0));
220     PDF_fontLeading = (int) (PDF_RESOLUTION * PDF_fontSizeCur * 0.25);
221
222     /* Assume this particular string is a somewhat reasonable typical
223      * output, for getting at the average character width */
224     PDF_fontAvWidth = (int)
225         (PDF_RESOLUTION * PDF_stringwidth(myPDF, "01234567890123456789",
226                                           font_handle, PDF_fontSizeCur)
227          / 20.0);
228     PDF_currentFontHandle = font_handle;
229
230 }
231
232 #if !HAVE_OLD_LIBPDF
233 static void
234 PDF_DefinePatterns()
235 {
236     int i;
237
238     /* EAM April 2003 - Rearrange patterns to maximize contrast in mono.
239      * Because of the finite linewidth, each pattern must include line
240      * fragments at the "empty" corners.
241      */
242     for (i=0; i<PDF_patterns; i++) {
243         PDF_patternHandles[i] = PDF_begin_pattern(myPDF, 8, 8, 8, 8, 2);
244         PDF_setlinewidth(myPDF, 0.25);
245         PDF_setlinecap(myPDF, 2); /* square ends */
246         switch (i) {
247         case 0: PDF_moveto(myPDF, 0, 8);
248                 PDF_lineto(myPDF, 8, 0);
249                 PDF_moveto(myPDF, 0, 0);
250                 PDF_lineto(myPDF, 8, 8);
251                 PDF_stroke(myPDF);
252                 break;
253         case 1: PDF_moveto(myPDF, 0, 8);
254                 PDF_lineto(myPDF, 8, 0);
255                 PDF_moveto(myPDF, 0, 0);
256                 PDF_lineto(myPDF, 8, 8);
257                 PDF_moveto(myPDF, 4, 0);
258                 PDF_lineto(myPDF, 8, 4);
259                 PDF_lineto(myPDF, 4, 8);
260                 PDF_lineto(myPDF, 0, 4);
261                 PDF_lineto(myPDF, 4, 0);
262                 PDF_stroke(myPDF);
263                 break;
264         case 2: PDF_moveto(myPDF, 0, 0);
265                 PDF_lineto(myPDF, 0, 8);
266                 PDF_lineto(myPDF, 8, 8);
267                 PDF_lineto(myPDF, 8, 0);
268                 PDF_lineto(myPDF, 0, 0);
269                 PDF_fill(myPDF);
270                 break;
271         case 3: PDF_moveto(myPDF, 0, 4);
272                 PDF_lineto(myPDF, 4, 0);
273                 PDF_moveto(myPDF, 4, 8);
274                 PDF_lineto(myPDF, 8, 4);
275                 PDF_stroke(myPDF);
276                 break;
277         case 4: PDF_moveto(myPDF, 0, 4);
278                 PDF_lineto(myPDF, 4, 8);
279                 PDF_moveto(myPDF, 4, 0);
280                 PDF_lineto(myPDF, 8, 4);
281                 PDF_stroke(myPDF);
282                 break;
283         case 5: PDF_moveto(myPDF, 0, 4);
284                 PDF_lineto(myPDF, 2, 0);
285                 PDF_moveto(myPDF, 2, 8);
286                 PDF_lineto(myPDF, 6, 0);
287                 PDF_moveto(myPDF, 6, 8);
288                 PDF_lineto(myPDF, 8, 4);
289                 PDF_stroke(myPDF);
290                 break;
291         case 6: PDF_moveto(myPDF, 0, 4);
292                 PDF_lineto(myPDF, 2, 8);
293                 PDF_moveto(myPDF, 2, 0);
294                 PDF_lineto(myPDF, 6, 8);
295                 PDF_moveto(myPDF, 6, 0);
296                 PDF_lineto(myPDF, 8, 4);
297                 PDF_stroke(myPDF);
298                 break;
299         case 7: /* not used */
300                 PDF_moveto(myPDF, 4, 0);
301                 PDF_lineto(myPDF, 0, 2);
302                 PDF_moveto(myPDF, 8, 2);
303                 PDF_lineto(myPDF, 0, 6);
304                 PDF_moveto(myPDF, 8, 6);
305                 PDF_lineto(myPDF, 4, 8);
306                 PDF_stroke(myPDF);
307                 break;
308         case 8: /* not used */
309                 PDF_moveto(myPDF, 4, 0);
310                 PDF_lineto(myPDF, 8, 2);
311                 PDF_moveto(myPDF, 0, 2);
312                 PDF_lineto(myPDF, 8, 6);
313                 PDF_moveto(myPDF, 0, 6);
314                 PDF_lineto(myPDF, 4, 8);
315                 PDF_stroke(myPDF);
316                 break;
317         }
318         PDF_end_pattern(myPDF);
319     }
320 }
321 #endif
322
323 /*------------------- the terminal entry functions --------------------*/
324
325
326 TERM_PUBLIC void
327 PDF_options ()
328 {
329     struct value a;
330
331     /* Annoying hack to handle the case of 'set termoption' after */
332     /* we have already initialized the terminal.                  */
333     if (c_token != 2)
334         pdf_explicit_size = FALSE;
335
336     while (!END_OF_COMMAND) {
337
338         if (almost_equals(c_token, "enh$anced")) {
339             c_token++;
340             term->put_text = ENHPDF_put_text;
341             term->flags |= TERM_ENHANCED_TEXT;
342             continue;
343         } else if (almost_equals(c_token, "noenh$anced")) {
344             c_token++;
345             term->put_text = PDF_put_text;
346             term->flags &= ~TERM_ENHANCED_TEXT;
347             continue;
348         }
349
350         if (almost_equals(c_token, "fn$ame") || almost_equals(c_token, "font"))  {
351             char *s, *comma;
352             c_token++;
353
354             if (!(s = try_to_get_string()))
355                 int_error(c_token,"fname: expecting font name");
356             comma = strrchr(s,',');
357             if (comma && (1 == sscanf(comma+1,"%lf",&PDF_fontSizeDef)))
358                 *comma = '\0';
359             if (*s)
360                 strncpy(PDF_fontNameDef, s, sizeof(PDF_fontNameDef));
361             free(s);
362             continue;
363         }
364
365         if (almost_equals(c_token, "fs$ize")) {
366             c_token++;
367
368             if (END_OF_COMMAND)
369                 int_error(c_token,"fsize: expecting font size");
370             PDF_fontSizeDef = real (const_express (&a));
371             continue;
372         }
373
374         if (equals(c_token, "lw") || almost_equals(c_token, "linew$idth")) {
375             c_token++;
376
377             if (END_OF_COMMAND)
378                 int_error(c_token, "expecting line width");
379             PDF_linewidth_factor = real (const_express (&a));
380             if (PDF_linewidth_factor <= 0)
381                 PDF_linewidth_factor = 0.1;
382             continue;
383         }
384
385         if (almost_equals(c_token, "rou$nded")) {
386             c_token++;
387             PDF_LineCap = 1;
388             continue;
389         }
390
391         if (equals(c_token, "butt")) {
392             PDF_LineCap = 0;
393             continue;
394         }
395
396         if (equals(c_token, "color") || almost_equals(c_token, "col$our")) {
397             c_token++;
398             PDF_monochrome = FALSE;
399             continue;
400         }   
401
402         if (almost_equals(c_token, "mono$chrome")) {
403             c_token++;
404             PDF_monochrome = TRUE;
405             continue;
406         }   
407
408         if (equals(c_token, "dl") || almost_equals(c_token, "dashl$ength")) {
409             c_token++;
410             if (END_OF_COMMAND)
411                 int_error(c_token, "expecting dashlength multiplier");
412             PDF_dashlength_factor = real(const_express(&a));
413             if (PDF_dashlength_factor < 0.0)
414                 PDF_dashlength_factor = 1.0;
415             continue;
416         }
417
418         if (equals(c_token, "solid")) {
419             c_token++;
420             PDF_dashedlines = FALSE;
421             continue;
422         }
423
424         if (equals(c_token, "size")) {
425             float xmax_t, ymax_t;
426             c_token++;
427             pdf_explicit_size = TRUE;
428             pdf_explicit_units = parse_term_size(&xmax_t, &ymax_t, INCHES);
429             term->xmax = xmax_t*PDF_RESOLUTION*72./gp_resolution;
430             term->ymax = ymax_t*PDF_RESOLUTION*72./gp_resolution;
431             break;
432         }
433
434 #ifdef HAVE_NODASH_LIBPDF
435         int_warn(NO_CARET,"gnuplot was linked against a version of pdflib with no dash or pattern support");
436 #else
437         if (almost_equals(c_token, "dash$ed")) {
438             c_token++;
439             PDF_dashedlines = TRUE;
440             continue;
441         }
442 #endif
443
444         int_error(c_token, "unexpected text at end of command");
445
446     }
447
448     /* Save options back into options string in normalized format */
449     sprintf(term_options, "%s%s fname '%s'  fsize %g linewidth %3.1f %s ",
450             PDF_monochrome ? "monochrome " : " ",
451             term->put_text == ENHPDF_put_text ? "enhanced" : "noenhanced",
452             PDF_fontNameDef, PDF_fontSizeDef, PDF_linewidth_factor,
453             PDF_LineCap == 1 ? "rounded" : "");
454     if (PDF_dashedlines)
455         sprintf(&(term_options[strlen(term_options)]), "dashed dl %3.1f",
456                 PDF_dashlength_factor);
457     if (pdf_explicit_size) {
458         if (pdf_explicit_units == CM)
459             sprintf(&(term_options[strlen(term_options)]), "size %.2fcm, %.2fcm ", 
460                 2.54*(float)term->xmax/(72.*PDF_RESOLUTION),
461                 2.54*(float)term->ymax/(72.*PDF_RESOLUTION));
462         else
463             sprintf(&(term_options[strlen(term_options)]), "size %.2fin, %.2fin ", 
464                 (float)term->xmax/(72.*PDF_RESOLUTION),
465                 (float)term->ymax/(72.*PDF_RESOLUTION));
466     }
467 }
468
469
470 TERM_PUBLIC void
471 PDF_init ()
472 {
473     static TBOOLEAN PDFlib_booted = FALSE;
474     char *gpversionstring;
475     char *username;
476     char *timedate;
477     time_t now;
478
479     if (!PDFlib_booted) {
480         PDF_boot();
481         PDFlib_booted = TRUE;
482     }
483
484     if (!myPDF)
485         myPDF = PDF_new();
486
487     /*open new PDF file */
488 #ifdef HAVE_LIBPDF_OPEN_FILE
489     if (PDF_open_file(myPDF, outstr) == -1)
490 #else
491     if (PDF_begin_document(myPDF, outstr?outstr:"-", 0,
492                            "compatibility=1.4") == -1)
493 #endif /* HAVE_LIBPDF_OPEN_FILE */
494         int_error(NO_CARET, "Error:cannot open PDF file .\n");
495
496 #ifdef PDF_DONT_COMPRESS
497     /* for easier debugging of the output, turn off PDF stream
498      * compression */
499     PDF_set_value(myPDF, "compress", 0);
500 #endif
501
502     gpversionstring = gp_alloc(20 + strlen(gnuplot_version) + 
503                                strlen(gnuplot_patchlevel) + 1, "PDF_init");
504     sprintf(gpversionstring,"gnuplot %s patchlevel %s",
505             gnuplot_version, gnuplot_patchlevel);
506
507     time(&now);
508     timedate=asctime(localtime(&now));
509     timedate[strlen(timedate)-1]='\0';
510
511     PDF_set_info(myPDF,"Creator",gpversionstring);
512
513     username=getusername();
514     if (username) {
515         PDF_set_info(myPDF,"Author",username);
516         free(username);
517     }
518
519     if (outstr)
520         PDF_set_info(myPDF,"Title",outstr); /* FIXME: use 'set title', if any? */
521     PDF_set_info(myPDF,"Subject","gnuplot plot");
522
523     if (gpversionstring)
524         free(gpversionstring);
525
526     PDF_LineType = LT_UNDEFINED;
527
528     /* set current font to default */
529     strcpy(PDF_fontNameCur, PDF_fontNameDef);
530     PDF_fontSizeCur = PDF_fontSizeDef;
531
532 #if !HAVE_OLD_LIBPDF
533     PDF_DefinePatterns();
534 #endif
535
536     /* Have to start the first page now, in order to know the actual
537      * size of the selected font */
538     PDF_graphics();
539
540     /* set h_char, v_char*/
541     term->h_char = PDF_fontAvWidth;
542     term->v_char = (PDF_fontAscent + PDF_fontDescent + PDF_fontLeading);
543
544     /* set h_tic, v_tic*/
545     term->h_tic = term->v_tic = 3 * PDF_RESOLUTION;
546
547     /* initialize terminal's pointsize from "set pointsize" value */
548     term_pointsize = pointsize;
549
550     /* Initialize other default settings */
551     PDF_setlinecap(myPDF, PDF_LineCap);
552     PDF_setlinejoin(myPDF, PDF_LineCap);        /* round+round or butt+mitre */
553 }
554
555
556 TERM_PUBLIC void
557 PDF_graphics ()
558 {
559     if (PDF_pageIsOpen)
560         return;                 /* already open --> nothing to do */
561
562     PDF_pathIsOpen = FALSE;
563     PDF_xLast = PDF_yLast = UINT_MAX;
564
565     /* set size of canvas */
566     if (!pdf_explicit_size) {
567         term->xmax = PDF_XMAX;
568         term->ymax = PDF_YMAX;
569     }
570
571     PDF_begin_page(myPDF, (double)term->xmax / PDF_RESOLUTION,
572                    (double)term->ymax / PDF_RESOLUTION);
573     PDF_scale(myPDF, 1.0/PDF_RESOLUTION, 1.0/PDF_RESOLUTION);
574     if (title.text && title.text[0])
575         /* a title has been set --> use it as the bookmark name, too */
576         PDF_add_bookmark(myPDF, title.text, 0, 1);
577     PDF_pageIsOpen = TRUE;
578
579     PDF_SetFont();
580 }
581
582
583 TERM_PUBLIC void
584 PDF_text ()
585 {
586     PDF_PathClose();
587     PDF_end_page(myPDF);
588     PDF_pageIsOpen = FALSE;
589 }
590
591
592 TERM_PUBLIC void
593 PDF_reset ()
594 {
595     assert(PDF_pageIsOpen == FALSE);
596 #ifdef HAVE_LIBPDF_OPEN_FILE
597     PDF_close(myPDF);
598 #else
599     PDF_end_document(myPDF, "");
600 #endif /* HAVE_LIBPDF_OPEN_FILE */
601     PDF_delete(myPDF);
602     myPDF = NULL;
603 }
604
605
606 TERM_PUBLIC void
607 PDF_linetype (int linetype)
608 {
609     int dash = linetype % 8;
610
611     linetype = PDF_Pen_RealID(linetype);
612     if (linetype == PDF_LineType)
613         return;
614         
615     PDF_PathClose ();
616     PDF_LineType = linetype;
617
618     if (PDF_monochrome) {
619         PDF_current_gray = 0.0;
620         PDF_setgray(myPDF, PDF_current_gray);
621     } else {
622         struct rgb *this_color = web_color_rgbs + 1 + linetype;
623         PDF_current_rgb.r = this_color->r / 255.0;
624         PDF_current_rgb.g = this_color->g / 255.0;
625         PDF_current_rgb.b = this_color->b / 255.0;
626         PDF_setrgbcolor(myPDF, PDF_current_rgb.r, PDF_current_rgb.g, PDF_current_rgb.b);
627     }
628
629 #ifndef HAVE_NODASH_LIBPDF
630         if (PDF_dashedlines) {
631             char dashtype[64];
632             float dl = 8.0 * PDF_dashlength_factor;
633
634             switch (dash) {
635             default:
636             case 0:     PDF_setdash(myPDF, 0.0, 0.0);
637                         return;
638             case 1:     sprintf(dashtype,"dasharray={%4.1f %4.1f}",
639                         dl*dash1[0],dl*dash1[1]);
640                         break;
641             case 2:     sprintf(dashtype,"dasharray={%4.1f %4.1f}",
642                         dl*dash2[0],dl*dash2[1]);
643                         break;
644             case 3:     sprintf(dashtype,"dasharray={%4.1f %4.1f}",
645                         dl*dash3[0],dl*dash3[1]);
646                         break;
647             case 4:     sprintf(dashtype,"dasharray={%4.1f %4.1f %4.1f %4.1f}",
648                         dl*dash4[0],dl*dash4[1],dl*dash4[2],dl*dash4[3]);
649                         break;
650             case 5:     sprintf(dashtype,"dasharray={%4.1f %4.1f %4.1f %4.1f}",
651                         dl*dash5[0],dl*dash5[1],dl*dash5[2],dl*dash5[3]);
652                         break;
653             case 6:     sprintf(dashtype,"dasharray={%4.1f %4.1f %4.1f %4.1f}",
654                         dl*dash6[0],dl*dash6[1],dl*dash6[2],dl*dash6[3]);
655                         break;
656             case 7:     sprintf(dashtype,"dasharray={%4.1f %4.1f %4.1f %4.1f %4.1f %4.1f}",
657                         dl*dash7[0],dl*dash7[1],dl*dash7[2],dl*dash7[3],dl*dash7[4],dl*dash7[5]);
658                         break;
659             }
660             PDF_setdashpattern(myPDF,dashtype);
661         }
662 #endif
663         
664 }
665
666
667 TERM_PUBLIC void
668 PDF_linewidth (double linewidth)
669 {
670     PDF_PathClose();
671     PDF_LineWidth = PDF_RESOLUTION * PDF_linewidth_factor * linewidth / 4.0;
672     if (PDF_LineWidth < 0.1)
673         PDF_LineWidth = 0.1;
674     PDF_setlinewidth(myPDF, PDF_LineWidth);
675 }
676
677
678 TERM_PUBLIC void
679 PDF_move (unsigned int x, unsigned int y)
680 {
681     if (PDF_pathIsOpen && x == PDF_xLast && y == PDF_yLast)
682         return;
683
684     PDF_PathOpen ();
685     PDF_moveto(myPDF, x, y);
686
687     PDF_xLast = x;
688     PDF_yLast = y;
689 }
690
691
692 TERM_PUBLIC void
693 PDF_vector (unsigned int x, unsigned int y)
694 {
695     if (PDF_pathIsOpen && x == PDF_xLast && y == PDF_yLast)
696         return;
697
698     PDF_PathOpen ();
699     PDF_lineto(myPDF, x, y);
700
701     PDF_xLast = x;
702     PDF_yLast = y;
703 }
704
705 /* Helper function. Many symbols have an additional dot in their
706  * center, so isolate its drawing into a separate function. */
707 static GP_INLINE void
708 PDF_dot (unsigned int x, unsigned int y)
709 {
710     /* Imitate PS's way of creating a small dot by a zero-length line
711      * segment with rounded endpoints */
712     PDF_setlinecap(myPDF, 1); /* rounded ends */
713     PDF_moveto(myPDF, x, y);
714     PDF_lineto(myPDF, x, y);
715     PDF_stroke(myPDF);
716     PDF_setlinecap(myPDF, PDF_LineCap); /* restore ends */
717 }
718
719
720 TERM_PUBLIC void
721 PDF_point (unsigned int x, unsigned int y, int number)
722 {
723     PDF_PathClose ();
724     PDF_save(myPDF);
725
726     if (number < 0) {
727         /* Treat all negative point sizes as  dots */
728         PDF_dot(x, y);
729     } else {
730         /* Change coordinate system so the point symbols themselves
731          * can be drawn without depending on position or size (-->
732          * better compression and less coding for gnuplot) */
733         /* NB: I use the do_pointsize() default implementation, which
734          * just stores the last set pointsize into `term_pointsize',
735          * to avoid introducing another static driver-local variable
736          * */
737         PDF_translate(myPDF, x, y);
738         PDF_scale(myPDF, term->h_tic / 2.0 * term_pointsize,
739                   term->v_tic / 2.0 * term_pointsize);
740         /* Correct linewidth to counter the scaling effect --- assume
741          * h_tic is usable, to avoid having to average h_ and v_tic */
742         PDF_setlinewidth(myPDF,
743                          PDF_LineWidth / (term->h_tic / 2.0 * term_pointsize));
744         switch (number %= PDF_NUM_POINTTYPES) {
745         case 0:                 /* Plus */
746             PDF_moveto(myPDF, -1, 0);
747             PDF_lineto(myPDF, 1, 0);
748             PDF_moveto(myPDF, 0, -1);
749             PDF_lineto(myPDF, 0, 1);
750             PDF_stroke(myPDF);
751             break;
752         case 2:                 /* Star */
753             PDF_moveto(myPDF, -1, 0);
754             PDF_lineto(myPDF, 1, 0);
755             PDF_moveto(myPDF, 0, -1);
756             PDF_lineto(myPDF, 0, 1);
757             /* FALLTHROUGH */
758         case 1:                 /* Cross */
759             PDF_moveto(myPDF, -1, -1);
760             PDF_lineto(myPDF, 1, 1);
761             PDF_moveto(myPDF, 1, -1);
762             PDF_lineto(myPDF, -1, 1);
763             PDF_stroke(myPDF);
764             break;
765
766 /* For each x = 0..5, 4 shapes are defined:
767  * 3 + 2*x --> hollow symbol with a dot at its center
768  * 4 + 2*x --> solid symbol filled in linetype's color
769  * 63 + x  --> hollow symbol without the center dot
770  * 69 + x  --> symbol filled with white --> opaque symbol */
771
772         case 63+0:              /* BoxEmpty */
773         case 3+2*0:             /* Box */
774             PDF_moveto(myPDF, -1, -1);
775             PDF_lineto(myPDF, 1, -1);
776             PDF_lineto(myPDF, 1, 1);
777             PDF_lineto(myPDF, -1, 1);
778             PDF_closepath_stroke(myPDF);
779             if (number == 3) PDF_dot(0,0);
780             break;
781         case 69+0:              /* BoxWhitefilled */
782             PDF_setgray_fill(myPDF, 1);
783             /* FALLTHROUGH */
784         case 4+2*0:             /* BoxFilled */
785             PDF_moveto(myPDF, -1, -1);
786             PDF_lineto(myPDF, 1, -1);
787             PDF_lineto(myPDF, 1, 1);
788             PDF_lineto(myPDF, -1, 1);
789             PDF_closepath_fill_stroke(myPDF);
790             break;
791
792         case 63+1:              /* CircleEmpty */
793         case 3+2*1:             /* Circle */
794             PDF_circle(myPDF, 0, 0, 1);
795             PDF_stroke(myPDF);
796             if (number == 5) PDF_dot(0,0);
797             break;
798         case 69+1:              /* CircleWhitefilled */
799             PDF_setgray_fill(myPDF, 1);
800             /* FALLTHROUGH */
801         case 4+2*1:             /* CircleFilled */
802             PDF_circle(myPDF, 0, 0, 1);
803             PDF_fill_stroke(myPDF);
804             break;
805
806         case 63+2:              /* TriangleUpEmpty */
807         case 3+2*2:             /* TriangleUp */
808             PDF_moveto(myPDF, 0, 1.12);
809             PDF_lineto(myPDF, -1, -0.5);
810             PDF_lineto(myPDF, 1, -0.5);
811             PDF_closepath_stroke(myPDF);
812             if (number == 7) PDF_dot(0,0);
813             break;
814         case 69+2:              /* TriangleUpWhitefilled */
815             PDF_setgray_fill(myPDF, 1);
816             /* FALLTHROUGH */
817         case 4+2*2:                     /* TriangleUpFilled */
818             PDF_moveto(myPDF, 0, 1.12);
819             PDF_lineto(myPDF, -1, -0.5);
820             PDF_lineto(myPDF, 1, -0.5);
821             PDF_closepath_fill_stroke(myPDF);
822             break;
823
824         case 63+3:              /* TriangleDownEmpty */
825         case 3+2*3:             /* TriangleDown */
826             PDF_moveto(myPDF, 0, -1.12);
827             PDF_lineto(myPDF, -1, 0.5);
828             PDF_lineto(myPDF, 1, 0.5);
829             PDF_closepath_stroke(myPDF);
830             if (number == 9) PDF_dot(0,0);
831             break;
832         case 69+3:              /* TriangleDownWhitefilled */
833             PDF_setgray_fill(myPDF, 1);
834             /* FALLTHROUGH */
835         case 4+2*3:             /* TriangleDownFilled */
836             PDF_moveto(myPDF, 0, -1.12);
837             PDF_lineto(myPDF, -1, 0.5);
838             PDF_lineto(myPDF, 1, 0.5);
839             PDF_closepath_fill_stroke(myPDF);
840             break;
841
842         case 63+4:              /* DiamondEmpty */
843         case 3+2*4:             /* Diamond */
844             PDF_moveto(myPDF, 0, -1);
845             PDF_lineto(myPDF, 1, 0);
846             PDF_lineto(myPDF, 0, 1);
847             PDF_lineto(myPDF, -1, 0);
848             PDF_closepath_stroke(myPDF);
849             if (number == 11) PDF_dot(0,0);
850             break;
851         case 69+4:              /* DiamondWhitefilled */
852             PDF_setgray_fill(myPDF, 1);
853             /* FALLTHROUGH */
854         case 4+2*4:             /* DiamondFilled */
855             PDF_moveto(myPDF, 0, -1);
856             PDF_lineto(myPDF, 1, 0);
857             PDF_lineto(myPDF, 0, 1);
858             PDF_lineto(myPDF, -1, 0);
859             PDF_closepath_fill_stroke(myPDF);
860             break;
861
862         case 63+5:              /* PentagonEmpty */
863         case 3+2*5:             /* Pentagon */
864             PDF_moveto(myPDF, 0, 1);
865             PDF_lineto(myPDF, -0.95, 0.31);
866             PDF_lineto(myPDF, -0.58, -0.81);
867             PDF_lineto(myPDF, +0.58, -0.81);
868             PDF_lineto(myPDF, +0.95, 0.31);
869             PDF_closepath_stroke(myPDF);
870             if (number == 13) PDF_dot(0,0);
871             break;
872         case 69+5:              /* PentagonWhitefilled */
873             PDF_setgray_fill(myPDF, 1);
874             /* FALLTHROUGH */
875         case 4+2*5:             /* PentagonFilled */
876             PDF_moveto(myPDF, 0, 1);
877             PDF_lineto(myPDF, -0.95, 0.31);
878             PDF_lineto(myPDF, -0.58, -0.81);
879             PDF_lineto(myPDF, +0.58, -0.81);
880             PDF_lineto(myPDF, +0.95, 0.31);
881             PDF_closepath_fill_stroke(myPDF);
882             break;
883
884 /* 15 + (0..15): circles with varying parts of'em filled. The added
885  * number is a bit-pattern of the 4 quadrants: 1 signals a quadrant
886  * filled */
887         case 15+0:
888             PDF_moveto(myPDF, 0, 0);
889             PDF_lineto(myPDF, 0, 1);
890             PDF_arc(myPDF, 0, 0, 1, 90, 360+90);
891             PDF_closepath_stroke(myPDF);
892             break;
893
894 /* Generalize common code into a macro... */
895 #define CIRCLE_SINGLE_PIESLICE(x, y, angle1, angle2)            \
896             PDF_moveto(myPDF, 0, 0);                            \
897             PDF_lineto(myPDF, (x), (y));                        \
898             PDF_arc(myPDF, 0, 0, 1, (angle1), (angle2));        \
899             PDF_lineto(myPDF, 0, 0);                            \
900             PDF_closepath(myPDF);                               \
901             PDF_fill_stroke(myPDF);                             \
902             PDF_arc(myPDF, 0, 0, 1, (angle2), (angle1) + 360);  \
903             PDF_stroke(myPDF);                                  \
904             break;
905
906 #define CIRCLE_SINGLE_QUADRANT(x, y, angle)                     \
907             CIRCLE_SINGLE_PIESLICE(x, y, angle, angle+90);
908         case 15+1:
909             CIRCLE_SINGLE_QUADRANT(1, 0, 0);
910         case 15+2:
911             CIRCLE_SINGLE_QUADRANT(0, 1, 90);
912         case 15+4:
913             CIRCLE_SINGLE_QUADRANT(-1, 0, 180);
914         case 15+8:
915             CIRCLE_SINGLE_QUADRANT(0, -1, 270);
916 #undef CIRCLE_SINGLE_QUADRANT
917
918 #define CIRCLE_TWO_NEIGHBOR_QUADRANTS(x, y, angle)              \
919             CIRCLE_SINGLE_PIESLICE(x, y, angle, angle+180)
920         case 15+3:
921             CIRCLE_TWO_NEIGHBOR_QUADRANTS(1, 0, 0);
922         case 15+6:
923             CIRCLE_TWO_NEIGHBOR_QUADRANTS(0, 1, 90);
924         case 15+12:
925             CIRCLE_TWO_NEIGHBOR_QUADRANTS(-1, 0, 180);
926         case 15+9:
927             CIRCLE_TWO_NEIGHBOR_QUADRANTS(0, -1, 270);
928 #undef CIRCLE_TWO_NEIGHBOR_QUADRANTS
929
930 #define CIRCLE_TWO_OPPOSING_QUADRANTS(x, y, angle)              \
931             PDF_moveto(myPDF, 0, 0);                            \
932             PDF_lineto(myPDF, x, y);                            \
933             PDF_arc(myPDF, 0, 0, 1, angle, angle + 90);         \
934             PDF_lineto(myPDF, 0, 0);                            \
935             PDF_fill_stroke(myPDF);                             \
936             PDF_moveto(myPDF, 0, 0);                            \
937             PDF_lineto(myPDF, -x, -y);                          \
938             PDF_arc(myPDF, 0, 0, 1, angle + 180, angle + 270);  \
939             PDF_lineto(myPDF, 0, 0);                            \
940             PDF_fill_stroke(myPDF);                             \
941             PDF_arc(myPDF, 0, 0, 1, angle + 90, angle + 360);   \
942             PDF_stroke(myPDF);                                  \
943             break;
944         case 15+5:
945             CIRCLE_TWO_OPPOSING_QUADRANTS(1, 0, 0);
946         case 15+10:
947             CIRCLE_TWO_OPPOSING_QUADRANTS(0, 1, 90);
948 #undef CIRCLE_TWO_OPPOSING_QUADRANTS
949
950 #define CIRCLE_THREE_QUADRANTS(x, y, angle)                     \
951             CIRCLE_SINGLE_PIESLICE(x, y, angle, angle+270)
952         case 15+7:
953             CIRCLE_THREE_QUADRANTS(1, 0, 0);
954         case 15+14:
955             CIRCLE_THREE_QUADRANTS(0, 1, 90);
956         case 15+13:
957             CIRCLE_THREE_QUADRANTS(-1, 0, 180);
958         case 15+11:
959             CIRCLE_THREE_QUADRANTS(0, -1, 270);
960 #undef CIRCLE_THREE_QUADRANTS
961 #undef CIRCLE_SINGLE_PIESLICE
962
963         case 15+15:
964             PDF_circle(myPDF, 0, 0, 1);
965             PDF_closepath_fill_stroke(myPDF);
966             break;
967
968
969 /*************************************************************************/
970 /* 31 + (0..15): squares with different quadrants of them filled in. */
971 /*************************************************************************/
972 /*************************************************************************/
973 /* 47 + (0..15): diamonds with filled quadrants as given by bit pattern  */
974 /*   Diamonds are drawn as squares rotated by 45 degrees, so can use
975  * fall-through from diamond to squares, and re-use some macros. */
976 /*************************************************************************/
977         case 47+0:
978             PDF_rotate(myPDF, 45);
979             /* FALLTHROUGH */
980         case 31+0:
981             PDF_moveto(myPDF, 0, 0);
982             PDF_lineto(myPDF, 0, 1);
983             PDF_lineto(myPDF, -1, 1);
984             PDF_lineto(myPDF, -1, -1);
985             PDF_lineto(myPDF, 1, -1);
986             PDF_lineto(myPDF, 1, 1);
987             PDF_lineto(myPDF, 0, 1);
988             PDF_stroke(myPDF);
989             break;
990
991         case 47+15:
992             PDF_rotate(myPDF, 45);
993             /* FALLTHROUGH */
994         case 31+15:
995             PDF_moveto(myPDF, -1, 1);
996             PDF_lineto(myPDF, -1, -1);
997             PDF_lineto(myPDF, 1, -1);
998             PDF_lineto(myPDF, 1, 1);
999             PDF_closepath_fill_stroke(myPDF);
1000             break;
1001
1002 /* macros defining shapes of the partly filled symbols. Done by
1003  * rotating the starting point (x0, y0) by 90 degrees or 45 degrees
1004  * (with length adjustment).  The rotations can be done without
1005  * trigonometric function calls, since their values are known:
1006  * cos(90)=0, sin(90)=1, cos(45)=sin(45)=1/sqrt(2).  A good compiler
1007  * should be able to optimize away all the local variables and
1008  * loops...  */
1009
1010 #define SQUARE_SINGLE_PIESLICE(x0, y0, quadrants)                       \
1011             {                                                           \
1012                 int quadrant = 0;                                       \
1013                 int x= x0, y=y0;                                        \
1014                 PDF_moveto(myPDF, 0, 0);                                \
1015                 PDF_lineto(myPDF, x, y);                                \
1016                 /* poor man's rotation by 45 and 90 degrees around the  \
1017                  * square's outline. */                                 \
1018                 while (quadrant++ < quadrants) {                        \
1019                     int dummy;                                          \
1020                     PDF_lineto(myPDF, x-y, x+y);                        \
1021                     dummy = x; x = -y; y = dummy;                       \
1022                 }                                                       \
1023                 PDF_lineto(myPDF, x, y);                                \
1024                 PDF_closepath_fill_stroke(myPDF);                       \
1025                 PDF_moveto(myPDF, x, y);                                \
1026                 while (quadrant++ <= 4) {                               \
1027                     int dummy;                                          \
1028                     PDF_lineto(myPDF, x-y, x+y);                        \
1029                     dummy = x; x = -y; y = dummy;                       \
1030                 }                                                       \
1031                 PDF_lineto(myPDF, x, y);                                \
1032                 PDF_stroke(myPDF);                                      \
1033             }                                                           \
1034             break;
1035
1036 #define SQUARE_TWO_OPPOSING_QUADRANTS(x0, y0, angle)    \
1037             {                                           \
1038                 int x = x0, y = y0, dummy;              \
1039                 int counter = 0;                        \
1040                                                         \
1041                 while (counter++ < 2) {                 \
1042                     PDF_moveto(myPDF, 0, 0);            \
1043                     PDF_lineto(myPDF, x, y);            \
1044                     PDF_lineto(myPDF, x-y, x+y);        \
1045                     dummy = x; x = -y; y = dummy;       \
1046                     PDF_lineto(myPDF, x, y);            \
1047                     PDF_closepath_fill_stroke(myPDF);   \
1048                                                         \
1049                     PDF_moveto(myPDF, x, y);            \
1050                     PDF_lineto(myPDF, x-y, x+y);        \
1051                     dummy = x; x = -y; y = dummy;       \
1052                     PDF_lineto(myPDF, x, y);            \
1053                     PDF_stroke(myPDF);                  \
1054                 }                                       \
1055                 break;                                  \
1056             }
1057
1058 /* Macros for diamonds just prepend the rotation and then call those
1059  * for squares: */
1060 #define DIAMOND_SINGLE_PIESLICE(x, y, quadrants)        \
1061             PDF_rotate(myPDF, 45);                      \
1062             SQUARE_SINGLE_PIESLICE(x, y, quadrants);
1063 #define DIAMOND_TWO_OPPOSING_QUADRANTS(x, y, angle)     \
1064             PDF_rotate(myPDF, 45);                      \
1065             SQUARE_TWO_OPPOSING_QUADRANTS(x, y, angle);
1066
1067 /* ... and now all the individual cases. The 'angle' arguments' are
1068  * purely for the sake of easing cut'n'paste with the circle case */
1069 #define SQUARE_SINGLE_QUADRANT(x, y, angle)                     \
1070             SQUARE_SINGLE_PIESLICE(x, y, 1);
1071         case 31+1:
1072             SQUARE_SINGLE_QUADRANT(1, 0, 0);
1073         case 31+2:
1074             SQUARE_SINGLE_QUADRANT(0, 1, 90);
1075         case 31+4:
1076             SQUARE_SINGLE_QUADRANT(-1, 0, 180);
1077         case 31+8:
1078             SQUARE_SINGLE_QUADRANT(0, -1, 270);
1079 #undef SQUARE_SINGLE_QUADRANT
1080
1081 #define SQUARE_TWO_NEIGHBOR_QUADRANTS(x, y, angle)              \
1082             SQUARE_SINGLE_PIESLICE(x, y, 2)
1083         case 31+3:
1084             SQUARE_TWO_NEIGHBOR_QUADRANTS(1, 0, 0);
1085         case 31+6:
1086             SQUARE_TWO_NEIGHBOR_QUADRANTS(0, 1, 90);
1087         case 31+12:
1088             SQUARE_TWO_NEIGHBOR_QUADRANTS(-1, 0, 180);
1089         case 31+9:
1090             SQUARE_TWO_NEIGHBOR_QUADRANTS(0, -1, 270);
1091 #undef SQUARE_TWO_NEIGHBOR_QUADRANTS
1092
1093         case 31+5:
1094             SQUARE_TWO_OPPOSING_QUADRANTS(1, 0, 0);
1095         case 31+10:
1096             SQUARE_TWO_OPPOSING_QUADRANTS(0, 1, 90);
1097
1098 #define SQUARE_THREE_QUADRANTS(x, y, angle)                     \
1099             SQUARE_SINGLE_PIESLICE(x, y, 3)
1100         case 31+7:
1101             SQUARE_THREE_QUADRANTS(1, 0, 0);
1102         case 31+14:
1103             SQUARE_THREE_QUADRANTS(0, 1, 90);
1104         case 31+13:
1105             SQUARE_THREE_QUADRANTS(-1, 0, 180);
1106         case 31+11:
1107             SQUARE_THREE_QUADRANTS(0, -1, 270);
1108 #undef SQUARE_THREE_QUADRANTS
1109
1110 #define DIAMOND_SINGLE_QUADRANT(x, y, angle)                    \
1111             DIAMOND_SINGLE_PIESLICE(x, y, 1)
1112         case 47+1:
1113             DIAMOND_SINGLE_QUADRANT(1, 0, 0);
1114         case 47+2:
1115             DIAMOND_SINGLE_QUADRANT(0, 1, 90);
1116         case 47+4:
1117             DIAMOND_SINGLE_QUADRANT(-1, 0, 180);
1118         case 47+8:
1119             DIAMOND_SINGLE_QUADRANT(0, -1, 270);
1120 #undef DIAMOND_SINGLE_QUADRANT
1121
1122 #define DIAMOND_TWO_NEIGHBOR_QUADRANTS(x, y, angle)             \
1123             DIAMOND_SINGLE_PIESLICE(x, y, 2)
1124         case 47+3:
1125             DIAMOND_TWO_NEIGHBOR_QUADRANTS(1, 0, 0);
1126         case 47+6:
1127             DIAMOND_TWO_NEIGHBOR_QUADRANTS(0, 1, 90);
1128         case 47+12:
1129             DIAMOND_TWO_NEIGHBOR_QUADRANTS(-1, 0, 180);
1130         case 47+9:
1131             DIAMOND_TWO_NEIGHBOR_QUADRANTS(0, -1, 270);
1132 #undef DIAMOND_TWO_NEIGHBOR_QUADRANTS
1133
1134
1135         case 47+5:
1136             DIAMOND_TWO_OPPOSING_QUADRANTS(1, 0, 0);
1137         case 47+10:
1138             DIAMOND_TWO_OPPOSING_QUADRANTS(0, 1, 90);
1139 #undef DIAMOND_TWO_OPPOSING_QUADRANTS
1140 #undef SQUARE_TWO_OPPOSING_QUADRANTS
1141
1142 #define DIAMOND_THREE_QUADRANTS(x, y, angle)                    \
1143             DIAMOND_SINGLE_PIESLICE(x, y, 3)
1144         case 47+7:
1145             DIAMOND_THREE_QUADRANTS(1, 0, 0);
1146         case 47+14:
1147             DIAMOND_THREE_QUADRANTS(0, 1, 90);
1148         case 47+13:
1149             DIAMOND_THREE_QUADRANTS(-1, 0, 180);
1150         case 47+11:
1151             DIAMOND_THREE_QUADRANTS(0, -1, 270);
1152 #undef DIAMOND_THREE_QUADRANTS
1153 #undef DIAMOND_SINGLE_PIESLICE
1154 #undef SQUARE_SINGLE_PIESLICE
1155
1156         default:
1157             int_warn(NO_CARET, "PDF: unknown point type number %d", number);
1158         }
1159     }
1160
1161     PDF_restore(myPDF);
1162     PDF_xLast = x;
1163     PDF_yLast = y;
1164 }
1165
1166
1167 TERM_PUBLIC int
1168 PDF_justify_text (enum JUSTIFY mode)
1169 {
1170     PDF_TextJust = mode;
1171     return (TRUE);
1172 }
1173
1174
1175 TERM_PUBLIC int
1176 PDF_text_angle (int ang)
1177 {
1178     PDF_TextAngle = ang;
1179     return (TRUE);
1180 }
1181
1182
1183 TERM_PUBLIC void
1184 PDF_put_text (unsigned int x, unsigned int y, const char *str)
1185 {
1186     char *alignment = NULL;
1187     double h = x, v = y;
1188
1189     PDF_PathClose ();
1190
1191     /* horizontal justification*/
1192     switch (PDF_TextJust) {
1193     case LEFT:
1194         alignment = "left";
1195         break;
1196     case CENTRE:
1197         alignment = "center";
1198         break;
1199     case RIGHT:
1200         alignment = "right";
1201         break;
1202     }
1203
1204     if (PDF_TextAngle) {
1205         PDF_save(myPDF);
1206         PDF_translate(myPDF, h, v);
1207         PDF_rotate(myPDF, PDF_TextAngle);
1208         /* vertical justification*/
1209         PDF_translate(myPDF, 0, -(PDF_fontAscent-PDF_fontDescent)/2);
1210         PDF_show_boxed(myPDF, str, 0,0, 0, 0, alignment, NULL);
1211         PDF_restore(myPDF);
1212     } else {
1213         /* vertical justification*/
1214         v -= (PDF_fontAscent - PDF_fontDescent) / 2;
1215         PDF_show_boxed(myPDF, str, h , v, 0, 0, alignment, NULL);
1216     }
1217
1218 }
1219
1220
1221 TERM_PUBLIC int
1222 PDF_set_font (const char *font)
1223 {
1224
1225     if (!font || !(*font)) {
1226         strcpy (PDF_fontNameCur, PDF_fontNameDef);
1227         PDF_fontSizeCur = PDF_fontSizeDef;
1228     } else {
1229         int sep = strcspn(font,",");
1230         if (sep > 0) {
1231             strncpy(PDF_fontNameCur,font,sep);
1232             PDF_fontNameCur[sep] = NUL;
1233         }
1234         if (font[sep] == ',')
1235             sscanf(&(font[sep+1]), "%lf", &PDF_fontSizeCur);
1236     }
1237
1238     PDF_PathClose();
1239     PDF_SetFont();
1240
1241     term->h_char = PDF_fontAvWidth;
1242     term->v_char = (PDF_fontAscent + PDF_fontDescent + PDF_fontLeading);
1243
1244     return (TRUE);
1245 }
1246
1247 TERM_PUBLIC void
1248 PDF_boxfill(int style, unsigned int x1, unsigned int y1,
1249             unsigned int width, unsigned int height)
1250 {
1251     gpiPoint corner[4];
1252
1253         corner[0].x = x1;        corner[0].y = y1;
1254         corner[1].x = x1+width;  corner[1].y = y1;
1255         corner[2].x = x1+width;  corner[2].y = y1+height;
1256         corner[3].x = x1;        corner[3].y = y1+height;
1257
1258         corner->style = style;
1259         PDF_filled_polygon(4, corner);
1260 }
1261
1262 TERM_PUBLIC void
1263 PDF_filled_polygon(int points, gpiPoint* corners)
1264 {
1265     int i;
1266     int fillpar = corners->style >> 4;
1267     int style = corners->style &= 0xf;
1268
1269     PDF_PathClose();
1270     PDF_save(myPDF);
1271
1272     switch (style) {
1273         case FS_EMPTY: /* fill with white */
1274             PDF_setgray(myPDF, 1);
1275             break;
1276         case FS_SOLID:
1277             {
1278                 double fact = (double)fillpar * 0.01;
1279                 double _fact = (double)(100-fillpar) * 0.01;
1280                 double red   = PDF_current_rgb.r * fact + _fact;
1281                 double green = PDF_current_rgb.g * fact + _fact;
1282                 double blue  = PDF_current_rgb.b * fact + _fact;
1283                 if (PDF_monochrome)
1284                     PDF_setgray_fill(myPDF, PDF_current_gray);
1285                 else
1286                     PDF_setrgbcolor_fill(myPDF, red, green, blue);
1287             }
1288             break;
1289 #if !HAVE_OLD_LIBPDF
1290         case FS_PATTERN:
1291             fillpar = fillpar % (PDF_patterns + 1) /* 0 == white */;
1292             switch (fillpar) {
1293                 case 0:
1294                     /* fill with white */
1295                     PDF_setcolor(myPDF, "fill", "rgb", 1, 1, 1, 0 /* unused */);
1296                     break;
1297                 default:
1298                     PDF_setcolor(myPDF, "fill", "pattern", PDF_patternHandles[fillpar - 1], 0, 0, 0);
1299             }
1300             break;
1301 #endif
1302         default:
1303             break;
1304     }
1305
1306     PDF_moveto(myPDF, corners[0].x, corners[0].y);
1307     for (i=1; i<points; i++)
1308         PDF_lineto(myPDF, corners[i].x, corners[i].y);
1309     PDF_lineto(myPDF, corners[0].x, corners[0].y);
1310     PDF_fill(myPDF);
1311     PDF_restore(myPDF);
1312 }
1313
1314 TERM_PUBLIC int
1315 PDF_make_palette(t_sm_palette *palette)
1316 {
1317     if (palette == NULL) {
1318         /* pdf can do continuous colors */
1319         return 0;
1320     }
1321
1322     return 0;
1323 }
1324
1325 TERM_PUBLIC void
1326 PDF_set_color(t_colorspec *colorspec)
1327 {
1328     if (colorspec->type == TC_LT) {
1329         struct rgb *this_color = web_color_rgbs + 1 + PDF_Pen_RealID(colorspec->lt);
1330         PDF_current_rgb.r = this_color->r / 255.0;
1331         PDF_current_rgb.g = this_color->g / 255.0;
1332         PDF_current_rgb.b = this_color->b / 255.0;
1333         PDF_current_gray = 0.0; /* monochrome mode only */
1334     } else if (colorspec->type == TC_FRAC) {
1335         rgb1maxcolors_from_gray( colorspec->value, &PDF_current_rgb);
1336         PDF_current_gray = colorspec->value; /* monochrome mode only */
1337     } else if (colorspec->type == TC_RGB) {
1338         PDF_current_rgb.r = (double)((colorspec->lt >> 16 ) & 255) / 255.;
1339         PDF_current_rgb.g = (double)((colorspec->lt >> 8 ) & 255) / 255.;
1340         PDF_current_rgb.b = (double)(colorspec->lt & 255) / 255.;
1341     } else
1342         return;
1343
1344     /* make sure that the path is stroked with the current color
1345      * before changing the color */
1346     PDF_PathClose();
1347
1348     if (PDF_monochrome && colorspec->type != TC_RGB)
1349         PDF_setgray(myPDF, PDF_current_gray);  /* FIXME - Should this be NTSC(current_rgb)? */
1350     else
1351         PDF_setrgbcolor(myPDF, PDF_current_rgb.r, PDF_current_rgb.g, PDF_current_rgb.b);
1352
1353     /* mark linetype invalid so that the color will be
1354      * set when PDF_linetype() is called next */
1355     PDF_LineType = LT_UNDEFINED;
1356 }
1357
1358 TERM_PUBLIC void
1359 PDF_previous_palette()
1360 {
1361 }
1362  
1363 #ifdef WITH_IMAGE
1364 TERM_PUBLIC void
1365 PDF_image (unsigned M, unsigned N, coordval * image, gpiPoint * corner, t_imagecolor color_mode)
1366 {
1367     unsigned char *pixel;
1368     float xscale, yscale;
1369     int i, im;
1370
1371     /* Allocate memory to hold a copy of the entire image in raw RGB format */
1372     unsigned char *rawrgb = gp_alloc( M*N*3, "Raw RGB image");
1373
1374     /* Convert the input image into raw RGB 24-bit color representation */
1375     if (color_mode == IC_RGB) {
1376         for (i=0, pixel=rawrgb; i<N*M*3;) {
1377             rgb_color rgb1;
1378             rgb255_color rgb255;
1379             rgb1.r = image[i++];
1380             rgb1.g = image[i++];
1381             rgb1.b = image[i++];
1382             rgb255_from_rgb1( rgb1, &rgb255 );
1383             *pixel++ = rgb255.r;
1384             *pixel++ = rgb255.g;
1385             *pixel++ = rgb255.b;
1386         }
1387     } else {
1388         for (i=0, pixel=rawrgb; i< N*M; i++) {
1389             rgb255_color rgb;
1390             rgb255maxcolors_from_gray(image[i], &rgb);
1391             *pixel++ = rgb.r;
1392             *pixel++ = rgb.g;
1393             *pixel++ = rgb.b;
1394         }
1395     }
1396       
1397     /* Describe this image to PDF library */
1398     im = PDF_open_image( myPDF, "raw", "memory", (char *)rawrgb,
1399                          (long)(M*N*3), (int)M, (int)N,
1400                          3, 8,                          /* 3 colors, 8 bits each */
1401                          "");
1402
1403     /* Clip to bounding box requested */
1404         PDF_save(myPDF);
1405         PDF_moveto(myPDF, corner[2].x, corner[2].y);
1406         PDF_lineto(myPDF, corner[2].x, corner[3].y);
1407         PDF_lineto(myPDF, corner[3].x, corner[3].y);
1408         PDF_lineto(myPDF, corner[3].x, corner[2].y);
1409         PDF_closepath(myPDF);
1410         PDF_clip(myPDF);
1411
1412 /* Scale and copy into the main PDF image */
1413         xscale = fabs((float)corner[1].x - (float)corner[0].x) / (float)M;
1414         yscale = fabs((float)corner[1].y - (float)corner[0].y) / (float)N;
1415         PDF_translate(myPDF, corner[0].x, corner[0].y);
1416         PDF_scale(myPDF, xscale, yscale);
1417         PDF_translate(myPDF, 0, -(float)N);
1418         PDF_place_image(myPDF, im, 0.0, 0.0, 1.0 );
1419         PDF_restore(myPDF);
1420
1421     /* Clean up */
1422         PDF_close_image(myPDF, im);
1423         free(rawrgb);
1424
1425 }
1426 #endif 
1427
1428
1429 /*
1430  * Ethan A Merritt November 2003
1431  *      - support for enhanced text mode
1432  * BUGS:
1433  *      - The baseline is not consistent if font size changes within a string.
1434  *      - Placement of overprinted characters is not correct.
1435  *      - libpdf exits if the requested font is not recognized.
1436  *      - I implement text-rotation by hand, but it may be possible to use
1437  *        a gsave/translate/rotate/.../grestore sequence instead.
1438  */
1439
1440 static TBOOLEAN ENHpdf_opened_string;
1441
1442 /* used in determining height of processed text */
1443 static float ENHpdf_base;
1444
1445 /* use these so that we don't over-write the current font settings in pdf_state */
1446 static double  ENHpdf_fontsize;
1447 static char   *ENHpdf_font;
1448
1449 /* A global flag that tells us this run is just to determine text size */
1450 static TBOOLEAN ENHpdf_sizeonly = FALSE;
1451
1452 static TBOOLEAN ENHpdf_show = TRUE;
1453 static int ENHpdf_overprint = 0;
1454 static TBOOLEAN ENHpdf_widthflag = FALSE;
1455 static unsigned int ENHpdf_xsave, ENHpdf_ysave;
1456
1457 /* Start a new string fragment */
1458 TERM_PUBLIC void
1459 ENHPDF_OPEN(
1460     char *fontname,
1461     double fontsize, double base,
1462     TBOOLEAN widthflag, TBOOLEAN showflag,
1463     int overprint)
1464 {
1465     /* If the overprint code requests a save or request, that's all we do */
1466     if (overprint == 3) {
1467         ENHpdf_xsave = PDF_xLast;
1468         ENHpdf_ysave = PDF_yLast;
1469         return;
1470     } else if (overprint == 4) {
1471         PDF_move(ENHpdf_xsave, ENHpdf_ysave);
1472         return;
1473     }
1474
1475     if (!ENHpdf_opened_string) {
1476         ENHpdf_opened_string = TRUE;
1477         enhanced_cur_text = &enhanced_text[0];
1478         ENHpdf_font = fontname;
1479         ENHpdf_fontsize = fontsize;
1480         ENHpdf_base = base * PDF_RESOLUTION;
1481         ENHpdf_show = showflag;
1482         ENHpdf_overprint = overprint;
1483         ENHpdf_widthflag = widthflag;
1484     }
1485 }
1486
1487 /* Write a string fragment and update the current position */
1488 TERM_PUBLIC void
1489 ENHPDF_FLUSH()
1490 {
1491     int x, y;
1492     float stringlength;
1493
1494         if (ENHpdf_opened_string) {
1495             ENHpdf_opened_string = FALSE;
1496             *enhanced_cur_text = '\0';
1497             x = PDF_xLast;
1498             y = PDF_yLast;
1499             x -= sin((double)PDF_TextAngle * M_PI_2/90.) * ENHpdf_base;
1500             y += cos((double)PDF_TextAngle * M_PI_2/90.) * ENHpdf_base;
1501             x += sin((double)PDF_TextAngle * M_PI_2/90.) * (double)PDF_fontAvWidth/2.;
1502             y -= cos((double)PDF_TextAngle * M_PI_2/90.) * (double)PDF_fontAvWidth/2.;
1503
1504             /* Select current font for enhanced text fragment, then restore context */
1505             if (1) {
1506                 char save_fontname[MAX_ID_LEN + 1];
1507                 double save_fontsize = PDF_fontSizeCur;
1508                     strcpy(save_fontname,PDF_fontNameCur);
1509                     PDF_fontSizeCur = ENHpdf_fontsize;
1510                     PDF_set_font(ENHpdf_font);
1511                     strcpy(PDF_fontNameCur,save_fontname);
1512                     PDF_fontSizeCur = save_fontsize;
1513             }
1514
1515             /* Find length of string in current font */
1516             stringlength = PDF_stringwidth(myPDF, enhanced_text,
1517                 PDF_currentFontHandle, ENHpdf_fontsize);
1518             stringlength *= PDF_RESOLUTION;
1519
1520             if (ENHpdf_show && !ENHpdf_sizeonly) {
1521                 if (PDF_TextAngle == 0 ) {
1522                     /* PDF_show(myPDF, enhanced_text); */
1523                     PDF_show_boxed(myPDF, enhanced_text, x, y, 0, 0, "left", NULL);
1524                 } else {
1525                     PDF_save(myPDF);
1526                     PDF_translate(myPDF, x, y);
1527                     PDF_rotate(myPDF, PDF_TextAngle);
1528                     /* vertical justification*/
1529                     PDF_translate(myPDF, 0, -(PDF_fontAscent-PDF_fontDescent)/2);
1530                     PDF_show_boxed(myPDF, enhanced_text, 0, 0, 0, 0, "left", NULL);
1531                     PDF_restore(myPDF);
1532                 }
1533             }
1534             if (ENHpdf_overprint == 1) {
1535                 PDF_xLast += stringlength * cos((double)PDF_TextAngle * M_PI_2/90.) / 2.;
1536                 PDF_yLast += stringlength * sin((double)PDF_TextAngle * M_PI_2/90.);
1537             } else if (ENHpdf_widthflag) {
1538                 PDF_xLast += stringlength * cos((double)PDF_TextAngle * M_PI_2/90.);
1539                 PDF_yLast += stringlength * sin((double)PDF_TextAngle * M_PI_2/90.);
1540             }
1541         }
1542 }
1543
1544 TERM_PUBLIC void
1545 ENHPDF_put_text(unsigned int x, unsigned int y, const char *str)
1546 {
1547     char *original_string = (char *)str;
1548
1549     if (ignore_enhanced_text) {
1550         PDF_put_text(x,y,str);
1551         return;
1552     }
1553
1554     if (!str || !strlen(str))
1555         return;
1556
1557     /* if there are no magic characters, we should just be able
1558      * punt the string to PDF_put_text()
1559      */
1560     if (!strpbrk(str, "{}^_@&~")) {
1561         /* do something to ensure default font is selected */
1562         PDF_put_text(x,y,str);
1563         return;
1564     }
1565
1566     PDF_move(x, y);
1567     PDF_PathClose();
1568     PDF_save(myPDF);
1569
1570     /* FIXME - Is this the way to do it?????
1571     if (PDF_TextAngle != 0)
1572         ENHPDF_DEBUG(("currentpoint gsave translate %d rotate 0 0 moveto\n", PDF_TextAngle));
1573      */
1574
1575     /* set up the global variables needed by enhanced_recursion() */
1576     enhanced_max_height = -1000;
1577     enhanced_min_height = 1000;
1578     enhanced_fontscale = 1.0;
1579     strncpy(enhanced_escape_format,"%c",sizeof(enhanced_escape_format));
1580
1581     ENHpdf_opened_string = FALSE;
1582
1583     /* EAM - Software text justification requires two passes */
1584     if (PDF_TextJust == RIGHT || PDF_TextJust == CENTRE)
1585         ENHpdf_sizeonly = TRUE;
1586
1587     /* Set the recursion going. We say to keep going until a
1588      * closing brace, but we don't really expect to find one.
1589      * If the return value is not the nul-terminator of the
1590      * string, that can only mean that we did find an unmatched
1591      * closing brace in the string. We increment past it (else
1592      * we get stuck in an infinite loop) and try again.
1593      */
1594     while (*(str = enhanced_recursion((char *)str, TRUE,
1595                         PDF_fontNameCur, PDF_fontSizeCur,
1596                         0.0, TRUE, TRUE, 0))) {
1597         (term->enhanced_flush)();
1598
1599         /* I think we can only get here if *str == '}' */
1600             enh_err_check(str);
1601
1602         if (!*++str)
1603             break; /* end of string */
1604
1605         /* else carry on and process the rest of the string */
1606     }
1607
1608     PDF_restore(myPDF);
1609
1610     enhanced_max_height += enhanced_min_height;
1611
1612     /* We can do text justification by running the entire top level string */
1613     /* through 2 times, with the ENHpdf_sizeonly flag set the first time.   */
1614     /* After seeing where the final position is, we then offset the start  */
1615     /* point accordingly and run it again.                                 */
1616     if (PDF_TextJust == RIGHT || PDF_TextJust == CENTRE) {
1617         int justification = PDF_TextJust;
1618         int x_offset = PDF_xLast - x;
1619         int y_offset = 0;
1620
1621         if (PDF_TextAngle != 0)
1622             y_offset = PDF_yLast - y;
1623         PDF_TextJust = LEFT;
1624         ENHpdf_sizeonly = FALSE;
1625
1626         if (justification == RIGHT) {
1627             ENHPDF_put_text(x - x_offset, y - y_offset, original_string);
1628         } else if (justification == CENTRE) {
1629             ENHPDF_put_text(x - x_offset/2, y - y_offset/2, original_string);
1630         }
1631         PDF_TextJust = justification;
1632     }
1633
1634 }
1635
1636 #endif /* TERM_BODY */
1637
1638 #ifdef TERM_TABLE
1639 TERM_TABLE_START (pdf_driver)
1640     "pdf", "PDF (Portable Document File) file driver",
1641     0 /* xmax */ , 0 /* ymax */ , 0 /* vchar */ , 0 /* hchar */ ,
1642     0 /* vtic */ , 0 /* htic */ ,
1643     PDF_options, PDF_init, PDF_reset, PDF_text, null_scale, PDF_graphics,
1644     PDF_move, PDF_vector, PDF_linetype, PDF_put_text, PDF_text_angle,
1645     PDF_justify_text, PDF_point, do_arrow, PDF_set_font, do_pointsize,
1646     TERM_BINARY,
1647     0 /* suspend */, 0 /* resume */ , PDF_boxfill, PDF_linewidth
1648 #   ifdef USE_MOUSE
1649    , 0, 0, 0, 0, 0 /* no mouse support for pdf */
1650 #   endif
1651    , PDF_make_palette,
1652    PDF_previous_palette,
1653    PDF_set_color,
1654    PDF_filled_polygon
1655 #ifdef WITH_IMAGE
1656    , PDF_image
1657 #endif
1658    , ENHPDF_OPEN, ENHPDF_FLUSH, do_enh_writec
1659 TERM_TABLE_END (pdf_driver)
1660 #undef LAST_TERM
1661 #define LAST_TERM pdf_driver
1662 #endif /* TERM_TABLE */
1663
1664 #endif /* TERM_PROTO_ONLY */
1665
1666 #ifdef TERM_HELP
1667 START_HELP(pdf)
1668 "1 pdf",
1669 "?commands set terminal pdf",
1670 "?set terminal pdf",
1671 "?set term pdf",
1672 "?terminal pdf",
1673 "?term pdf",
1674 "?pdf",
1675 " This terminal produces files in the Adobe Portable Document Format",
1676 " (PDF), useable for printing or display with tools like Acrobat Reader",
1677 "",
1678 " Syntax:",
1679 "       set terminal pdf {monochrome|color|colour}",
1680 "                        {{no}enhanced}",
1681 "                        {fname \"<font>\"} {fsize <fontsize>}",
1682 "                        {font \"<fontname>{,<fontsize>}\"}",
1683 "                        {linewidth <lw>} {rounded|butt}",
1684 "                        {solid|dashed} {dl <dashlength>}}",
1685 "                        {size <XX>{unit},<YY>{unit}}",
1686 "",
1687 " The default is to use a different color for each line type. Selecting",
1688 " `monochome` will use black for all linetypes, in which case you probably",
1689 " want to select `dashed` to distinguish line types. Even in in mono mode",
1690 " you can still use explicit colors for filled areas or linestyles.",
1691 "",
1692 " where <font> is the name of the default font to use (default Helvetica)",
1693 " and <fontsize> is the font size (in points, default 12).",
1694 " For help on which fonts are available or how to install new ones, please",
1695 " see the documentation for your local installation of pdflib.",
1696 "",
1697 " The `enhanced` option enables enhanced text processing features",
1698 " (subscripts, superscripts and mixed fonts). See `enhanced`.",
1699 "",
1700 " The width of all lines in the plot can be increased by the factor <n>",
1701 " specified in `linewidth`. Similarly `dashlength` is a multiplier for the",
1702 " default dash spacing.",
1703 "",
1704 " `rounded` sets line caps and line joins to be rounded; `butt` is the",
1705 " default, butt caps and mitered joins.",
1706 "",
1707 " The default size for PDF output is 5 inches by 3 inches. The `size` option",
1708 " changes this to whatever the user requests. By default the X and Y sizes",
1709 " are taken to be in inches, but other units are possible (currently only cm).",
1710 ""
1711 END_HELP(pdf)
1712 #endif