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