PLplot  5.11.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
ps.c
Go to the documentation of this file.
1 // PLplot PostScript device driver.
2 //
3 // Copyright (C) 1992, 2001 Geoffrey Furnish
4 // Copyright (C) 1992, 1993, 1994, 1995, 2001 Maurice LeBrun
5 // Copyright (C) 2000-2014 Alan W. Irwin
6 // Copyright (C) 2001, 2002 Joao Cardoso
7 // Copyright (C) 2001, 2003, 2004 Rafael Laboissiere
8 // Copyright (C) 2004, 2005 Thomas J. Duck
9 // Copyright (C) 2005 Andrew Ross
10 //
11 // This file is part of PLplot.
12 //
13 // PLplot is free software; you can redistribute it and/or modify
14 // it under the terms of the GNU Library General Public License as published
15 // by the Free Software Foundation; either version 2 of the License, or
16 // (at your option) any later version.
17 //
18 // PLplot is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU Library General Public License for more details.
22 //
23 // You should have received a copy of the GNU Library General Public License
24 // along with PLplot; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 //
27 //
28 
29 #include "plDevs.h"
30 
31 #define DEBUG
32 
33 #ifdef PLD_ps
34 #define NEED_PLDEBUG
35 #include "plplotP.h"
36 #include "drivers.h"
37 #include "ps.h"
38 
39 #include <string.h>
40 #include <time.h>
41 #include "plunicode-type1.h"
42 #include "plfci-type1.h"
43 
44 // Define macro to truncate small values to zero - prevents
45 // printf printing -0.000
46 #define TRMFLT( a ) ( ( fabs( a ) < 5.0e-4 ) ? 0.0 : ( a ) )
47 
48 // Device info
49 
50 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_ps =
51  "ps:PostScript File (monochrome):0:ps:29:psm\n"
52  "psc:PostScript File (color):0:ps:30:psc\n";
53 
54 
55 // Prototypes for functions in this file.
56 
59 
60 static char *ps_getdate( void );
61 static void ps_init( PLStream * );
62 static void fill_polygon( PLStream *pls );
63 static void proc_str( PLStream *, EscText * );
64 static void esc_purge( unsigned char *, unsigned char * );
65 
66 #define OUTBUF_LEN 128
67 static char outbuf[OUTBUF_LEN];
68 static int text = 1;
69 static int color;
70 static int hrshsym = 1;
71 
72 static DrvOpt ps_options[] = { { "text", DRV_INT, &text, "Use Postscript text (text=0|1)" },
73  { "color", DRV_INT, &color, "Use color (color=0|1)" },
74  { "hrshsym", DRV_INT, &hrshsym, "Use Hershey symbol set (hrshsym=0|1)" },
75  { NULL, DRV_INT, NULL, NULL } };
76 
77 static unsigned char
78 plunicode2type1( const PLUNICODE index,
79  const Unicode_to_Type1_table lookup[],
80  const int number_of_entries );
81 
82 static const char *
83 get_font( PSDev* dev, PLUNICODE fci );
84 
85 // text > 0 uses some postscript tricks, namely a transformation matrix
86 // that scales, rotates (with slanting) and offsets text strings.
87 // It has yet some bugs for 3d plots.
88 
89 
90 static void ps_dispatch_init_helper( PLDispatchTable *pdt,
91  const char *menustr, const char *devnam,
92  int type, int seq, plD_init_fp init )
93 {
94 #ifndef ENABLE_DYNDRIVERS
95  pdt->pl_MenuStr = (char *) menustr;
96  pdt->pl_DevName = (char *) devnam;
97 #else
98  (void) menustr; // Cast to void to silence compiler warnings about unused parameters
99  (void) devnam;
100 #endif
101  pdt->pl_type = type;
102  pdt->pl_seq = seq;
103  pdt->pl_init = init;
106  pdt->pl_eop = (plD_eop_fp) plD_eop_ps;
107  pdt->pl_bop = (plD_bop_fp) plD_bop_ps;
110  pdt->pl_esc = (plD_esc_fp) plD_esc_ps;
111 }
112 
114 {
115  ps_dispatch_init_helper( pdt,
116  "PostScript File (monochrome)", "ps",
119 }
120 
122 {
123  ps_dispatch_init_helper( pdt,
124  "PostScript File (color)", "psc",
127 }
128 
129 //--------------------------------------------------------------------------
130 // plD_init_ps()
131 //
132 // Initialize device.
133 //--------------------------------------------------------------------------
134 
135 void
137 {
138  color = 0;
139  pls->color = 0; // Not a color device
140 
141  plParseDrvOpts( ps_options );
142  if ( color )
143  pls->color = 1; // But user wants color
144  ps_init( pls );
145 }
146 
147 void
148 plD_init_psc( PLStream *pls )
149 {
150  color = 1;
151  pls->color = 1; // Is a color device
152  plParseDrvOpts( ps_options );
153 
154  if ( !color )
155  pls->color = 0; // But user does not want color
156  ps_init( pls );
157 }
158 
159 static void
160 ps_init( PLStream *pls )
161 {
162  PSDev *dev;
163 
164  PLFLT pxlx, pxly;
165 
166  // Set default values - 7.5 x 10 [inches] (72 points = 1 inch)
167  if ( pls->xlength <= 0 || pls->ylength <= 0 )
168  {
169  pls->xlength = 540;
170  pls->ylength = 720;
171  pls->xoffset = 32;
172  pls->yoffset = 32;
173  }
174  if ( pls->xdpi <= 0 )
175  pls->xdpi = 72.;
176  if ( pls->ydpi <= 0 )
177  pls->ydpi = 72.;
178 
179  pxlx = YPSSIZE / LPAGE_X;
180  pxly = XPSSIZE / LPAGE_Y;
181 
182  if ( text )
183  {
184  pls->dev_text = 1; // want to draw text
185  pls->dev_unicode = 1; // want unicode
186  if ( hrshsym )
187  pls->dev_hrshsym = 1; // want Hershey symbols
188  }
189 
190  pls->dev_fill0 = 1; // Can do solid fills
191 
192 // Initialize family file info
193 
194  plFamInit( pls );
195 
196 // Prompt for a file name if not already set
197 
198  plOpenFile( pls );
199 
200 // Allocate and initialize device-specific data
201 
202  if ( pls->dev != NULL )
203  free( (void *) pls->dev );
204 
205  pls->dev = calloc( 1, (size_t) sizeof ( PSDev ) );
206  if ( pls->dev == NULL )
207  plexit( "ps_init: Out of memory." );
208 
209  dev = (PSDev *) pls->dev;
210 
211  dev->xold = PL_UNDEFINED;
212  dev->yold = PL_UNDEFINED;
213 
214  plP_setpxl( pxlx, pxly );
215 
216  dev->llx = XPSSIZE;
217  dev->lly = YPSSIZE;
218  dev->urx = 0;
219  dev->ury = 0;
220  dev->ptcnt = 0;
221 
222 // Rotate by 90 degrees since portrait mode addressing is used
223 
224  dev->xmin = 0;
225  dev->ymin = 0;
226  dev->xmax = PSY;
227  dev->ymax = PSX;
228  dev->xlen = dev->xmax - dev->xmin;
229  dev->ylen = dev->ymax - dev->ymin;
230 
231  plP_setphy( dev->xmin, dev->xmax, dev->ymin, dev->ymax );
232 
233 // If portrait mode is specified, then set up an additional rotation
234 // transformation with aspect ratio allowed to adjust via freeaspect.
235 // Default orientation is landscape (ORIENTATION == 3 or 90 deg rotation
236 // counter-clockwise from portrait). (Legacy PLplot used seascape
237 // which was equivalent to ORIENTATION == 1 or 90 deg clockwise rotation
238 // from portrait.)
239 
240  if ( pls->portrait )
241  {
242  plsdiori( (PLFLT) ( 4 - ORIENTATION ) );
243  pls->freeaspect = 1;
244  }
245 
246 // Header comments into PostScript file
247 
248  fprintf( OF, "%%!PS-Adobe-2.0 EPSF-2.0\n" );
249  fprintf( OF, "%%%%BoundingBox: \n" );
250  fprintf( OF, "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" );
251 
252  fprintf( OF, "%%%%Title: PLplot Graph\n" );
253  fprintf( OF, "%%%%Creator: PLplot Version %s\n", PLPLOT_VERSION );
254  fprintf( OF, "%%%%CreationDate: %s\n", ps_getdate() );
255  fprintf( OF, "%%%%Pages: (atend)\n" );
256  fprintf( OF, "%%%%EndComments\n\n" );
257 
258 // Definitions
259 // Save VM state
260 
261  fprintf( OF, "/PSSave save def\n" );
262 
263 // Define a dictionary and start using it
264 
265  fprintf( OF, "/PSDict 200 dict def\n" );
266  fprintf( OF, "PSDict begin\n" );
267 
268  fprintf( OF, "/@restore /restore load def\n" );
269  fprintf( OF, "/restore\n" );
270  fprintf( OF, " {vmstatus pop\n" );
271  fprintf( OF, " dup @VMused lt {pop @VMused} if\n" );
272  fprintf( OF, " exch pop exch @restore /@VMused exch def\n" );
273  fprintf( OF, " } def\n" );
274  fprintf( OF, "/@pri\n" );
275  fprintf( OF, " {\n" );
276  fprintf( OF, " ( ) print\n" );
277  fprintf( OF, " ( ) cvs print\n" );
278  fprintf( OF, " } def\n" );
279 
280 // n @copies -
281 
282  fprintf( OF, "/@copies\n" );
283  fprintf( OF, " {\n" );
284  fprintf( OF, " /#copies exch def\n" );
285  fprintf( OF, " } def\n" );
286 
287 // - @start - -- start everything
288 
289  fprintf( OF, "/@start\n" );
290  fprintf( OF, " {\n" );
291  fprintf( OF, " vmstatus pop /@VMused exch def pop\n" );
292  fprintf( OF, " } def\n" );
293 
294 // - @end - -- finished
295 
296  fprintf( OF, "/@end\n" );
297  fprintf( OF, " {flush\n" );
298  fprintf( OF, " end\n" );
299  fprintf( OF, " PSSave restore\n" );
300  fprintf( OF, " } def\n" );
301 
302 // bop - -- begin a new page
303 // Only fill background if we are using color and if the bg isn't white
304 
305  fprintf( OF, "/bop\n" );
306  fprintf( OF, " {\n" );
307  fprintf( OF, " /SaveImage save def\n" );
308  fprintf( OF, " } def\n" );
309 
310 // - eop - -- end a page
311 
312  fprintf( OF, "/eop\n" );
313  fprintf( OF, " {\n" );
314  fprintf( OF, " showpage\n" );
315  fprintf( OF, " SaveImage restore\n" );
316  fprintf( OF, " } def\n" );
317 
318 // Set line parameters
319 
320  fprintf( OF, "/@line\n" );
321  fprintf( OF, " {0 setlinecap\n" );
322  fprintf( OF, " 0 setlinejoin\n" );
323  fprintf( OF, " 1 setmiterlimit\n" );
324  fprintf( OF, " } def\n" );
325 
326 // d @hsize - horizontal clipping dimension
327 
328  fprintf( OF, "/@hsize {/hs exch def} def\n" );
329  fprintf( OF, "/@vsize {/vs exch def} def\n" );
330 
331 // d @hoffset - shift for the plots
332 
333  fprintf( OF, "/@hoffset {/ho exch def} def\n" );
334  fprintf( OF, "/@voffset {/vo exch def} def\n" );
335 
336 // Set line width
337 
338  fprintf( OF, "/lw %d def\n", (int) (
339  ( pls->width < MIN_WIDTH ) ? DEF_WIDTH :
340  ( pls->width > MAX_WIDTH ) ? MAX_WIDTH : pls->width ) );
341 
342 // Setup user specified offsets, scales, sizes for clipping
343 
344  fprintf( OF, "/@SetPlot\n" );
345  fprintf( OF, " {\n" );
346  fprintf( OF, " ho vo translate\n" );
347  fprintf( OF, " XScale YScale scale\n" );
348  fprintf( OF, " lw setlinewidth\n" );
349  fprintf( OF, " } def\n" );
350 
351 // Setup x & y scales
352 
353  fprintf( OF, "/XScale\n" );
354  fprintf( OF, " {hs %d div} def\n", YPSSIZE );
355  fprintf( OF, "/YScale\n" );
356  fprintf( OF, " {vs %d div} def\n", XPSSIZE );
357 
358 // Macro definitions of common instructions, to keep output small
359 
360  fprintf( OF, "/M {moveto} def\n" );
361  fprintf( OF, "/D {lineto} def\n" );
362  fprintf( OF, "/A {0.5 0 360 arc} def\n" );
363  fprintf( OF, "/S {stroke} def\n" );
364  fprintf( OF, "/Z {stroke newpath} def\n" );
365  // Modify to use fill and stroke for better output with
366  // anti-aliasing
367  //fprintf(OF, "/F {fill} def\n");
368  if ( pls->dev_eofill )
369  fprintf( OF, "/F {closepath gsave eofill grestore stroke} def " );
370  else
371  fprintf( OF, "/F {closepath gsave fill grestore stroke} def " );
372  fprintf( OF, "/N {newpath} def" );
373  fprintf( OF, "/C {setrgbcolor} def\n" );
374  fprintf( OF, "/G {setgray} def\n" );
375  fprintf( OF, "/W {setlinewidth} def\n" );
376  fprintf( OF, "/SF {selectfont} def\n" );
377  fprintf( OF, "/R {rotate} def\n" );
378  fprintf( OF, "/SW {stringwidth 2 index mul exch 2 index mul exch rmoveto pop} bind def\n" );
379  fprintf( OF, "/B {Z %d %d M %d %d D %d %d D %d %d D %d %d closepath} def\n",
380  0, 0, 0, PSY, PSX, PSY, PSX, 0, 0, 0 );
381  fprintf( OF, "/CL {newpath M D D D closepath clip} def\n" );
382 
383 // End of dictionary definition
384 
385  fprintf( OF, "end\n\n" );
386 
387 // Set up the plots
388 
389  fprintf( OF, "PSDict begin\n" );
390  fprintf( OF, "@start\n" );
391  fprintf( OF, "%d @copies\n", COPIES );
392  fprintf( OF, "@line\n" );
393  fprintf( OF, "%d @hsize\n", YSIZE );
394  fprintf( OF, "%d @vsize\n", XSIZE );
395  fprintf( OF, "%d @hoffset\n", YOFFSET );
396  fprintf( OF, "%d @voffset\n", XOFFSET );
397 
398  fprintf( OF, "@SetPlot\n\n" );
399 }
400 
401 //--------------------------------------------------------------------------
402 // plD_line_ps()
403 //
404 // Draw a line in the current color from (x1,y1) to (x2,y2).
405 //--------------------------------------------------------------------------
406 
407 void
408 plD_line_ps( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
409 {
410  PSDev *dev = (PSDev *) pls->dev;
411  PLINT x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a;
412 
413 // Rotate by 90 degrees
414 
415  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x1, &y1 );
416  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x2, &y2 );
417 
418  if ( x1 == dev->xold && y1 == dev->yold && dev->ptcnt < 40 )
419  {
420  if ( pls->linepos + 12 > LINELENGTH )
421  {
422  putc( '\n', OF );
423  pls->linepos = 0;
424  }
425  else
426  putc( ' ', OF );
427 
428  snprintf( outbuf, OUTBUF_LEN, "%d %d D", x2, y2 );
429  dev->ptcnt++;
430  pls->linepos += 12;
431  }
432  else
433  {
434  fprintf( OF, " Z\n" );
435  pls->linepos = 0;
436 
437  if ( x1 == x2 && y1 == y2 ) // must be a single dot, draw a circle
438  snprintf( outbuf, OUTBUF_LEN, "%d %d A", x1, y1 );
439  else
440  snprintf( outbuf, OUTBUF_LEN, "%d %d M %d %d D", x1, y1, x2, y2 );
441  dev->llx = MIN( dev->llx, x1 );
442  dev->lly = MIN( dev->lly, y1 );
443  dev->urx = MAX( dev->urx, x1 );
444  dev->ury = MAX( dev->ury, y1 );
445  dev->ptcnt = 1;
446  pls->linepos += 24;
447  }
448  dev->llx = MIN( dev->llx, x2 );
449  dev->lly = MIN( dev->lly, y2 );
450  dev->urx = MAX( dev->urx, x2 );
451  dev->ury = MAX( dev->ury, y2 );
452 
453  fprintf( OF, "%s", outbuf );
454  pls->bytecnt += 1 + (PLINT) strlen( outbuf );
455  dev->xold = x2;
456  dev->yold = y2;
457 }
458 
459 //--------------------------------------------------------------------------
460 // plD_polyline_ps()
461 //
462 // Draw a polyline in the current color.
463 //--------------------------------------------------------------------------
464 
465 void
466 plD_polyline_ps( PLStream *pls, short *xa, short *ya, PLINT npts )
467 {
468  PLINT i;
469 
470  for ( i = 0; i < npts - 1; i++ )
471  plD_line_ps( pls, xa[i], ya[i], xa[i + 1], ya[i + 1] );
472 }
473 
474 //--------------------------------------------------------------------------
475 // plD_eop_ps()
476 //
477 // End of page.
478 //--------------------------------------------------------------------------
479 
480 void
481 plD_eop_ps( PLStream *pls )
482 {
483  fprintf( OF, " S\neop\n" );
484 }
485 
486 //--------------------------------------------------------------------------
487 // plD_bop_ps()
488 //
489 // Set up for the next page.
490 // Advance to next family file if necessary (file output).
491 //--------------------------------------------------------------------------
492 
493 void
494 plD_bop_ps( PLStream *pls )
495 {
496  PSDev *dev = (PSDev *) pls->dev;
497 
498  dev->xold = PL_UNDEFINED;
499  dev->yold = PL_UNDEFINED;
500 
501  if ( !pls->termin )
502  plGetFam( pls );
503 
504  pls->page++;
505 
506  if ( pls->family )
507  fprintf( OF, "%%%%Page: %d %d\n", (int) pls->page, 1 );
508  else
509  fprintf( OF, "%%%%Page: %d %d\n", (int) pls->page, (int) pls->page );
510 
511  fprintf( OF, "bop\n" );
512  if ( pls->color )
513  {
514  PLFLT r, g, b;
515  if ( pls->cmap0[0].r != 0xFF ||
516  pls->cmap0[0].g != 0xFF ||
517  pls->cmap0[0].b != 0xFF )
518  {
519  r = ( (PLFLT) pls->cmap0[0].r ) / 255.;
520  g = ( (PLFLT) pls->cmap0[0].g ) / 255.;
521  b = ( (PLFLT) pls->cmap0[0].b ) / 255.;
522 
523  fprintf( OF, "B %.4f %.4f %.4f C F\n", r, g, b );
524  }
525  }
526  pls->linepos = 0;
527 
528 // This ensures the color and line width are set correctly at the beginning of
529 // each page
530 
532  plD_state_ps( pls, PLSTATE_WIDTH );
533 }
534 
535 //--------------------------------------------------------------------------
536 // plD_tidy_ps()
537 //
538 // Close graphics file or otherwise clean up.
539 //--------------------------------------------------------------------------
540 
541 void
542 plD_tidy_ps( PLStream *pls )
543 {
544  PSDev *dev = (PSDev *) pls->dev;
545 
546  fprintf( OF, "\n%%%%Trailer\n" );
547 
548  dev->llx /= ENLARGE;
549  dev->lly /= ENLARGE;
550  dev->urx /= ENLARGE;
551  dev->ury /= ENLARGE;
552  dev->llx += YOFFSET;
553  dev->lly += XOFFSET;
554  dev->urx += YOFFSET;
555  dev->ury += XOFFSET;
556 
557 // changed for correct Bounding boundaries Jan Thorbecke okt 1993
558 // occurs from the integer truncation -- postscript uses fp arithmetic
559 
560  dev->urx += 1;
561  dev->ury += 1;
562 
563  if ( pls->family )
564  fprintf( OF, "%%%%Pages: %d\n", (int) 1 );
565  else
566  fprintf( OF, "%%%%Pages: %d\n", (int) pls->page );
567 
568  fprintf( OF, "@end\n" );
569  fprintf( OF, "%%%%EOF\n" );
570 
571 // Backtrack to write the BoundingBox at the beginning
572 // Some applications don't like it atend
573 
574  rewind( OF );
575  fprintf( OF, "%%!PS-Adobe-2.0 EPSF-2.0\n" );
576  fprintf( OF, "%%%%BoundingBox: %d %d %d %d\n",
577  dev->llx, dev->lly, dev->urx, dev->ury );
578  plCloseFile( pls );
579 }
580 
581 //--------------------------------------------------------------------------
582 // plD_state_ps()
583 //
584 // Handle change in PLStream state (color, pen width, fill attribute, etc).
585 //--------------------------------------------------------------------------
586 
587 void
588 plD_state_ps( PLStream *pls, PLINT op )
589 {
590  PSDev *dev = (PSDev *) pls->dev;
591 
592  switch ( op )
593  {
594  case PLSTATE_WIDTH: {
595  int width = (int) (
596  ( pls->width < MIN_WIDTH ) ? DEF_WIDTH :
597  ( pls->width > MAX_WIDTH ) ? MAX_WIDTH : pls->width );
598 
599  fprintf( OF, " S\n%d W", width );
600 
601  dev->xold = PL_UNDEFINED;
602  dev->yold = PL_UNDEFINED;
603  break;
604  }
605  case PLSTATE_COLOR0:
606  if ( !pls->color )
607  {
608  fprintf( OF, " S\n%.4f G", ( pls->icol0 ? 0.0 : 1.0 ) );
609  // Reinitialize current point location.
610  if ( dev->xold != PL_UNDEFINED && dev->yold != PL_UNDEFINED )
611  fprintf( OF, " %d %d M \n", (int) dev->xold, (int) dev->yold );
612  break;
613  }
614  // else fallthrough
615  case PLSTATE_COLOR1:
616  if ( pls->color )
617  {
618  PLFLT r = ( (PLFLT) pls->curcolor.r ) / 255.0;
619  PLFLT g = ( (PLFLT) pls->curcolor.g ) / 255.0;
620  PLFLT b = ( (PLFLT) pls->curcolor.b ) / 255.0;
621 
622  fprintf( OF, " S\n%.4f %.4f %.4f C", r, g, b );
623  }
624  else
625  {
626  PLFLT r = ( (PLFLT) pls->curcolor.r ) / 255.0;
627  fprintf( OF, " S\n%.4f G", 1.0 - r );
628  }
629  // Reinitialize current point location.
630  if ( dev->xold != PL_UNDEFINED && dev->yold != PL_UNDEFINED )
631  fprintf( OF, " %d %d M \n", (int) dev->xold, (int) dev->yold );
632  break;
633  }
634 }
635 
636 //--------------------------------------------------------------------------
637 // plD_esc_ps()
638 //
639 // Escape function.
640 //--------------------------------------------------------------------------
641 
642 void
643 plD_esc_ps( PLStream *pls, PLINT op, void *ptr )
644 {
645  switch ( op )
646  {
647  case PLESC_FILL:
648  fill_polygon( pls );
649  break;
650  case PLESC_HAS_TEXT:
651  proc_str( pls, (EscText *) ptr );
652  break;
653  }
654 }
655 
656 //--------------------------------------------------------------------------
657 // fill_polygon()
658 //
659 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
660 // Only solid color fill supported.
661 //--------------------------------------------------------------------------
662 
663 static void
664 fill_polygon( PLStream *pls )
665 {
666  PSDev *dev = (PSDev *) pls->dev;
667  PLINT n, ix = 0, iy = 0;
668  PLINT x, y;
669 
670  fprintf( OF, " Z\n" );
671 
672  for ( n = 0; n < pls->dev_npts; n++ )
673  {
674  x = pls->dev_x[ix++];
675  y = pls->dev_y[iy++];
676 
677 // Rotate by 90 degrees
678 
679  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x, &y );
680 
681 // First time through start with a x y moveto
682 
683  if ( n == 0 )
684  {
685  snprintf( outbuf, OUTBUF_LEN, "N %d %d M", x, y );
686  dev->llx = MIN( dev->llx, x );
687  dev->lly = MIN( dev->lly, y );
688  dev->urx = MAX( dev->urx, x );
689  dev->ury = MAX( dev->ury, y );
690  fprintf( OF, "%s", outbuf );
691  pls->bytecnt += (PLINT) strlen( outbuf );
692  continue;
693  }
694 
695  if ( pls->linepos + 21 > LINELENGTH )
696  {
697  putc( '\n', OF );
698  pls->linepos = 0;
699  }
700  else
701  putc( ' ', OF );
702 
703  pls->bytecnt++;
704 
705  snprintf( outbuf, OUTBUF_LEN, "%d %d D", x, y );
706  dev->llx = MIN( dev->llx, x );
707  dev->lly = MIN( dev->lly, y );
708  dev->urx = MAX( dev->urx, x );
709  dev->ury = MAX( dev->ury, y );
710 
711  fprintf( OF, "%s", outbuf );
712  pls->bytecnt += (PLINT) strlen( outbuf );
713  pls->linepos += 21;
714  }
715  dev->xold = PL_UNDEFINED;
716  dev->yold = PL_UNDEFINED;
717  fprintf( OF, " F " );
718 }
719 
720 //--------------------------------------------------------------------------
721 // ps_getdate()
722 //
723 // Get the date and time
724 //--------------------------------------------------------------------------
725 
726 static char *
727 ps_getdate( void )
728 {
729  int len;
730  time_t t;
731  char *p;
732 
733  t = time( (time_t *) 0 );
734  p = ctime( &t );
735  len = (int) strlen( p );
736  *( p + len - 1 ) = '\0'; // zap the newline character
737  return p;
738 }
739 
740 
741 // 0.8 should mimic the offset of first superscript/subscript level
742 // implemented in plstr (plsym.c) for Hershey fonts. However, when
743 // comparing with -dev xwin and -dev xcairo results changing this
744 // factor to 0.6 appears to offset the centers of the letters
745 // appropriately while 0.8 gives much poorer agreement with the
746 // other devices.
747 # define RISE_FACTOR 0.6
748 
749 //--------------------------------------------------------------------------
750 // proc_str()
751 //
752 // Prints postscript strings.
753 // N.B. Now unicode only, no string access!
754 //
755 //--------------------------------------------------------------------------
756 
757 void
758 proc_str( PLStream *pls, EscText *args )
759 {
760  PLFLT *t = args->xform, tt[4]; // Transform matrices
761  PLFLT theta, shear, stride; // Rotation angle and shear from the matrix
762  PLFLT ft_ht, offset; // Font height and offset
763  PLFLT cs, sn, l1, l2;
764  PSDev *dev = (PSDev *) pls->dev;
765  const char *font;
766  char esc;
767  // Be generous. Used to store lots of font changes which take
768  // 3 characters per change.
769  #define PROC_STR_STRING_LENGTH 1000
770  unsigned char *strp, str[PROC_STR_STRING_LENGTH], *cur_strp,
771  cur_str[PROC_STR_STRING_LENGTH];
772  float font_factor = 1.4f;
773  PLINT clxmin, clxmax, clymin, clymax; // Clip limits
774  PLINT clipx[4], clipy[4]; // Current clip limits
775 
776  PLFLT scale = 1., up = 0.; // Font scaling and shifting parameters
777 
778  int i = 0; // String index
779 
780  // unicode only! so test for it.
781  if ( args->unicode_array_len > 0 )
782  {
783  int j, s, f;
784  const char *fonts[PROC_STR_STRING_LENGTH];
785  const PLUNICODE *cur_text;
786  PLUNICODE fci, fci_save;
787  PLFLT old_sscale, sscale, old_soffset, soffset, ddup;
788  PLINT level = 0;
789  // translate from unicode into type 1 font index.
790  //
791  // Choose the font family, style, variant, and weight using
792  // the FCI (font characterization integer).
793  //
794 
795  plgesc( &esc );
796  plgfci( &fci );
797  fci_save = fci;
798  font = get_font( dev, fci );
799  cur_text = args->unicode_array;
800  for ( f = s = j = 0; j < args->unicode_array_len; j++ )
801  {
802  if ( cur_text[j] & PL_FCI_MARK )
803  {
804  // process an FCI by saving it and escaping cur_str
805  // with an escff to make it a 2-character escape
806  // that is not used in legacy Hershey code
807  //
808  if ( ( f < PROC_STR_STRING_LENGTH ) && ( s + 3 < PROC_STR_STRING_LENGTH ) )
809  {
810  fci_save = cur_text[j];
811  fonts[f++] = get_font( dev, fci_save );
812  cur_str[s++] = (unsigned char) esc;
813  cur_str[s++] = 'f';
814  cur_str[s++] = 'f';
815  }
816  }
817  else if ( s + 4 < PROC_STR_STRING_LENGTH )
818  {
819 #undef PL_TEST_TYPE1
820 #ifdef PL_TEST_TYPE1
821  // Use this test case only to conveniently view Type1 font
822  // possibilities (as in test_type1.py example).
823  // This functionality is useless other than for this test case.
824  PLINT ifamily, istyle, iweight;
825  plgfont( &ifamily, &istyle, &iweight );
826  if ( 0 <= cur_text[j] && cur_text[j] < 256 )
827  cur_str[s++] = cur_text[j];
828  else
829  cur_str[s++] = 32;
830  // Overwrite font just for this special case.
831  if ( ifamily == PL_FCI_SYMBOL )
832  font = get_font( dev, 0 );
833  else
834  font = get_font( dev, fci );
835 #else
836  cur_str[s] = plunicode2type1( cur_text[j], dev->lookup, dev->nlookup );
837  if ( cur_text[j] != ' ' && cur_str[s] == ' ' )
838  {
839  // failed lookup.
840  if ( !dev->if_symbol_font )
841  {
842  // failed standard font lookup. Use symbol
843  // font instead which will return a blank if
844  // that fails as well.
845  fonts[f++] = get_font( dev, 0 );
846  cur_str[s++] = (unsigned char) esc;
847  cur_str[s++] = 'f';
848  cur_str[s++] = 'f';
849  cur_str[s++] = plunicode2type1( cur_text[j], dev->lookup, dev->nlookup );
850  }
851  else
852  {
853  // failed symbol font lookup. Use last standard
854  // font instead which will return a blank if
855  // that fails as well.
856  fonts[f++] = get_font( dev, fci_save );
857  cur_str[s++] = (unsigned char) esc;
858  cur_str[s++] = 'f';
859  cur_str[s++] = 'f';
860  cur_str[s++] = plunicode2type1( cur_text[j], dev->lookup, dev->nlookup );
861  }
862  }
863  else
864  {
865  // lookup succeeded.
866  s++;
867  }
868 #endif
869  pldebug( "proc_str", "unicode = 0x%x, type 1 code = %d\n",
870  cur_text[j], cur_str[s - 1] );
871  }
872  }
873  cur_str[s] = '\0';
874 
875  // finish previous polyline
876 
877  dev->xold = PL_UNDEFINED;
878  dev->yold = PL_UNDEFINED;
879 
880  // Determine the font height
881  ft_ht = pls->chrht * 72.0 / 25.4; // ft_ht in points, ht is in mm
882 
883 
884  // The transform matrix has only rotations and shears; extract them
885  plRotationShear( t, &theta, &shear, &stride );
886  cs = cos( theta );
887  sn = sin( theta );
888  tt[0] = t[0] * cs + t[2] * sn;
889  tt[1] = t[1] * cs + t[3] * sn;
890  tt[2] = -t[0] * sn + t[2] * cs;
891  tt[3] = -t[1] * sn + t[3] * cs;
892 
893  //
894  // Reference point conventions:
895  // If base = 0, it is aligned with the center of the text box
896  // If base = 1, it is aligned with the baseline of the text box
897  // If base = 2, it is aligned with the top of the text box
898  //
899  // Currently plplot only uses base=0
900  // Postscript uses base=1
901  //
902  // We must calculate the difference between the two and apply the offset.
903  //
904 
905  if ( args->base == 2 ) // not supported by plplot
906  offset = ENLARGE * ft_ht / 2.; // half font height
907  else if ( args->base == 1 )
908  offset = 0.;
909  else
910  offset = -ENLARGE * ft_ht / 2.;
911 
912  // Determine the adjustment for page orientation
913  theta -= PI / 2. * pls->diorot;
914  args->y += (PLINT) ( offset * cos( theta ) );
915  args->x -= (PLINT) ( offset * sin( theta ) );
916 
917  // ps driver is rotated by default
918  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
919  &( args->x ), &( args->y ) );
920 
921  // Correct for the fact ps driver uses landscape by default
922  theta += PI / 2.;
923 
924  // Output
925  // Set clipping
926  clipx[0] = pls->clpxmi;
927  clipx[2] = pls->clpxma;
928  clipy[0] = pls->clpymi;
929  clipy[2] = pls->clpyma;
930  clipx[1] = clipx[2];
931  clipy[1] = clipy[0];
932  clipx[3] = clipx[0];
933  clipy[3] = clipy[2];
934  difilt( clipx, clipy, 4, &clxmin, &clxmax, &clymin, &clymax );
935  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
936  &clipx[0], &clipy[0] );
937  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
938  &clipx[1], &clipy[1] );
939  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
940  &clipx[2], &clipy[2] );
941  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
942  &clipx[3], &clipy[3] );
943  fprintf( OF, " gsave %d %d %d %d %d %d %d %d CL\n", clipx[0], clipy[0], clipx[1], clipy[1], clipx[2], clipy[2], clipx[3], clipy[3] );
944 
945  // move to string reference point
946  fprintf( OF, " %d %d M\n", args->x, args->y );
947 
948  // Save the current position and set the string rotation
949  fprintf( OF, "gsave %.3f R\n", TRMFLT( theta * 180. / PI ) );
950 
951  // Purge escape sequences from string, so that postscript can find it's
952  // length. The string length is computed with the current font, and can
953  // thus be wrong if there are font change escape sequences in the string
954  //
955 
956  esc_purge( str, cur_str );
957 
958  fprintf( OF, "/%s %.3f SF\n", font, TRMFLT( font_factor * ENLARGE * ft_ht ) );
959 
960  // Output string, while escaping the '(', ')' and '\' characters.
961  // this string is output for measurement purposes only.
962  //
963  fprintf( OF, "%.3f (", TRMFLT( -args->just ) );
964  while ( str[i] != '\0' )
965  {
966  if ( str[i] == '(' || str[i] == ')' || str[i] == '\\' )
967  fprintf( OF, "\\%c", str[i] );
968  else
969  fprintf( OF, "%c", str[i] );
970  i++;
971  }
972  fprintf( OF, ") SW\n" );
973 
974 
975  // Parse string for PLplot escape sequences and print everything out
976 
977  cur_strp = cur_str;
978  f = 0;
979  do
980  {
981  strp = str;
982 
983  if ( *cur_strp == esc )
984  {
985  cur_strp++;
986 
987  if ( *cur_strp == esc ) // <esc><esc>
988  {
989  *strp++ = *cur_strp++;
990  }
991  else if ( *cur_strp == 'f' )
992  {
993  cur_strp++;
994  if ( *cur_strp++ != 'f' )
995  {
996  // escff occurs because of logic above. But any suffix
997  // other than "f" should never happen.
998  plabort( "proc_str, internal PLplot logic error;"
999  "wrong escf escape sequence" );
1000  return;
1001  }
1002  font = fonts[f++];
1003  pldebug( "proc_str", "string-specified fci = 0x%x, font name = %s\n", fci, font );
1004  continue;
1005  }
1006  else
1007  switch ( *cur_strp++ )
1008  {
1009  case 'd': //subscript
1010  case 'D':
1011  plP_script_scale( FALSE, &level,
1012  &old_sscale, &sscale, &old_soffset, &soffset );
1013  scale = sscale;
1014  // The correction for the difference in magnitude
1015  // between the baseline and middle coordinate systems
1016  // for subscripts should be
1017  // -0.5*(base font size - superscript/subscript font size).
1018  ddup = -0.5 * ( 1.0 - sscale );
1019  up = -font_factor * ENLARGE * ft_ht * ( RISE_FACTOR * soffset + ddup );
1020  break;
1021 
1022  case 'u': //superscript
1023  case 'U':
1024  plP_script_scale( TRUE, &level,
1025  &old_sscale, &sscale, &old_soffset, &soffset );
1026  scale = sscale;
1027  // The correction for the difference in magnitude
1028  // between the baseline and middle coordinate systems
1029  // for superscripts should be
1030  // 0.5*(base font size - superscript/subscript font size).
1031  ddup = 0.5 * ( 1.0 - sscale );
1032  up = font_factor * ENLARGE * ft_ht * ( RISE_FACTOR * soffset + ddup );
1033  break;
1034 
1035  // ignore the next sequences
1036 
1037  case '+':
1038  case '-':
1039  case 'b':
1040  case 'B':
1041  plwarn( "'+', '-', and 'b/B' text escape sequences not processed." );
1042  break;
1043  }
1044  }
1045 
1046  // copy from current to next token, adding a postscript escape
1047  // char '\' if necessary
1048  //
1049  while ( *cur_strp && *cur_strp != esc )
1050  {
1051  if ( *cur_strp == '(' || *cur_strp == ')' || *cur_strp == '\\' )
1052  *strp++ = '\\';
1053  *strp++ = *cur_strp++;
1054  }
1055  *strp = '\0';
1056 
1057  if ( fabs( up ) < 0.001 )
1058  up = 0.; // Watch out for small differences
1059 
1060  // Apply the scaling and the shear
1061  fprintf( OF, "/%s [%.3f %.3f %.3f %.3f 0 0] SF\n",
1062  font,
1063  TRMFLT( tt[0] * font_factor * ENLARGE * ft_ht * scale ),
1064  TRMFLT( tt[2] * font_factor * ENLARGE * ft_ht * scale ),
1065  TRMFLT( tt[1] * font_factor * ENLARGE * ft_ht * scale ),
1066  TRMFLT( tt[3] * font_factor * ENLARGE * ft_ht * scale ) );
1067 
1068  // if up/down escape sequences, save current point and adjust baseline;
1069  // take the shear into account
1070  if ( up != 0. )
1071  fprintf( OF, "gsave %.3f %.3f rmoveto\n", TRMFLT( up * tt[1] ), TRMFLT( up * tt[3] ) );
1072 
1073  // print the string
1074  fprintf( OF, "(%s) show\n", str );
1075 
1076  // back to baseline
1077  if ( up != 0. )
1078  fprintf( OF, "grestore (%s) stringwidth rmoveto\n", str );
1079  } while ( *cur_strp );
1080 
1081  fprintf( OF, "grestore\n" );
1082  fprintf( OF, "grestore\n" );
1083 
1084  //
1085  // keep driver happy -- needed for background and orientation.
1086  // arghhh! can't calculate it, as I only have the string reference
1087  // point, not its extent!
1088  // Still a hack - but at least it takes into account the string
1089  // length and justification. Character width is assumed to be
1090  // 0.6 * character height. Add on an extra 1.5 * character height
1091  // for safety.
1092  //
1093  cs = cos( theta );
1094  sn = sin( theta );
1095  l1 = -i * args->just;
1096  l2 = i * ( 1. - args->just );
1097  // Factor of 0.6 is an empirical fudge to convert character
1098  // height to average character width
1099  l1 *= 0.6;
1100  l2 *= 0.6;
1101 
1102  dev->llx = (int) ( MIN( dev->llx, args->x + ( MIN( l1 * cs, l2 * cs ) - 1.5 ) * font_factor * ft_ht * ENLARGE ) );
1103  dev->lly = (int) ( MIN( dev->lly, args->y + ( MIN( l1 * sn, l2 * sn ) - 1.5 ) * font_factor * ft_ht * ENLARGE ) );
1104  dev->urx = (int) ( MAX( dev->urx, args->x + ( MAX( l1 * cs, l2 * cs ) + 1.5 ) * font_factor * ft_ht * ENLARGE ) );
1105  dev->ury = (int) ( MAX( dev->ury, args->y + ( MAX( l1 * sn, l2 * sn ) + 1.5 ) * font_factor * ft_ht * ENLARGE ) );
1106  }
1107 }
1108 
1109 static void
1110 esc_purge( unsigned char *dstr, unsigned char *sstr )
1111 {
1112  char esc;
1113 
1114  plgesc( &esc );
1115 
1116  while ( *sstr )
1117  {
1118  if ( *sstr != esc )
1119  {
1120  *dstr++ = *sstr++;
1121  continue;
1122  }
1123 
1124  sstr++;
1125  if ( *sstr == esc )
1126  {
1127  *dstr++ = *sstr++;
1128  continue;
1129  }
1130 
1131  else
1132  {
1133  switch ( *sstr++ )
1134  {
1135  case 'f':
1136  sstr++;
1137  break; // two chars sequence
1138 
1139  default:
1140  break; // single char escape
1141  }
1142  }
1143  }
1144  *dstr = '\0';
1145 }
1146 
1147 //--------------------------------------------------------------------------
1148 // unsigned char plunicode2type1 (const PLUNICODE index,
1149 // const Unicode_to_Type1_table lookup[], const int number_of_entries)
1150 //
1151 // Function takes an input unicode index, looks through the lookup
1152 // table (which must be sorted by PLUNICODE Unicode), then returns the
1153 // corresponding Type1 code in the lookup table. If the Unicode index is
1154 // not present the returned value is 32 (which is normally a blank
1155 // for Type 1 fonts).
1156 //--------------------------------------------------------------------------
1157 
1158 static unsigned char
1159 plunicode2type1( const PLUNICODE index,
1160  const Unicode_to_Type1_table lookup[],
1161  const int nlookup )
1162 {
1163  int jlo = -1, jmid, jhi = nlookup;
1164  while ( jhi - jlo > 1 )
1165  {
1166  // Note that although jlo or jhi can be just outside valid
1167  // range (see initialization above) because of while condition
1168  // jlo < jmid < jhi and jmid must be in valid range.
1169  //
1170  jmid = ( jlo + jhi ) / 2;
1171  if ( index > lookup[jmid].Unicode )
1172  jlo = jmid;
1173  else if ( index < lookup[jmid].Unicode )
1174  jhi = jmid;
1175  else
1176  // We have found it!
1177  // index == lookup[jmid].Unicode
1178  //
1179  return ( lookup[jmid].Type1 );
1180  }
1181  // jlo is invalid or it is valid and index > lookup[jlo].Unicode.
1182  // jhi is invalid or it is valid and index < lookup[jhi].Unicode.
1183  // All these conditions together imply index cannot be found in lookup.
1184  // Mark with ' ' (which is normally the index for blank in type 1 fonts).
1185  //
1186  return ( ' ' );
1187 }
1188 
1189 //--------------------------------------------------------------------------
1190 // get_font( PSDev* dev, PLUNICODE fci )
1191 //
1192 // Sets the Type1 font.
1193 //--------------------------------------------------------------------------
1194 static const char *
1195 get_font( PSDev* dev, PLUNICODE fci )
1196 {
1197  const char *font;
1198  // fci = 0 is a special value indicating the Type 1 Symbol font
1199  // is desired. This value cannot be confused with a normal FCI value
1200  // because it doesn't have the PL_FCI_MARK.
1201  if ( fci == 0 )
1202  {
1203  font = "Symbol";
1206  dev->if_symbol_font = 1;
1207  }
1208  else
1209  {
1210  // convert the fci to Base14/Type1 font information
1211  font = plP_FCI2FontName( fci, Type1Lookup, N_Type1Lookup );
1214  dev->if_symbol_font = 0;
1215  }
1216  pldebug( "set_font", "fci = 0x%x, font name = %s\n", fci, font );
1217  return ( font );
1218 }
1219 
1220 #else
1221 int
1223 {
1224  return 0;
1225 }
1226 
1227 #endif // PLD_ps