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