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