PLplot  5.10.0
 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-2011 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 " << XMIN << " " << YMIN << " M " << XMIN << " " << YMAX << " D " << XMAX << " " << YMAX << " D " << XMAX << " " << YMIN << " D " << XMIN << " " << YMIN << " 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 += XOFFSET;
616  dev->lly += YOFFSET;
617  dev->urx += XOFFSET;
618  dev->ury += YOFFSET;
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  break;
686  }
687  // else fallthrough
688  case PLSTATE_COLOR1:
689  if ( pls->color )
690  {
691  PLFLT r = ( (PLFLT) pls->curcolor.r ) / 255.0;
692  PLFLT g = ( (PLFLT) pls->curcolor.g ) / 255.0;
693  PLFLT b = ( (PLFLT) pls->curcolor.b ) / 255.0;
694 
695  doc->osBody() << " S\n" << r << " " << g << " " << b << " C";
696  }
697  else
698  {
699  PLFLT r = ( (PLFLT) pls->curcolor.r ) / 255.0;
700  doc->osBody() << " S\n" << 1.0 - r << " G";
701  }
702  break;
703  }
704 
705 // Reinitialize current point location.
706 
707  if ( dev->xold != PL_UNDEFINED && dev->yold != PL_UNDEFINED )
708  {
709  doc->osBody() << " " << (int) dev->xold << " " << (int) dev->yold << " M \n";
710  }
711 }
712 
713 //--------------------------------------------------------------------------
714 // plD_esc_psttf()
715 //
716 // Escape function.
717 //--------------------------------------------------------------------------
718 
719 void
720 plD_esc_psttf( PLStream *pls, PLINT op, void *ptr )
721 {
722  switch ( op )
723  {
724  case PLESC_FILL:
725  fill_polygon( pls );
726  break;
727  case PLESC_HAS_TEXT:
728  proc_str( pls, (EscText *) ptr );
729  break;
730  }
731 }
732 
733 //--------------------------------------------------------------------------
734 // fill_polygon()
735 //
736 // Fill polygon described in points pls->dev_x[] and pls->dev_y[].
737 // Only solid color fill supported.
738 //--------------------------------------------------------------------------
739 
740 static void
741 fill_polygon( PLStream *pls )
742 {
743  PSDev *dev = (PSDev *) pls->dev;
744  PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc;
745  PLINT n, ix = 0, iy = 0;
746  PLINT x, y;
747 
748  doc->osBody() << " Z\n";
749 
750  for ( n = 0; n < pls->dev_npts; n++ )
751  {
752  x = pls->dev_x[ix++];
753  y = pls->dev_y[iy++];
754 
755 // Rotate by 90 degrees
756 
757  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x, &y );
758 
759 // First time through start with a x y moveto
760 
761  if ( n == 0 )
762  {
763  snprintf( outbuf, OUTBUF_LEN, "N %d %d M", x, y );
764  dev->llx = MIN( dev->llx, x );
765  dev->lly = MIN( dev->lly, y );
766  dev->urx = MAX( dev->urx, x );
767  dev->ury = MAX( dev->ury, y );
768  doc->osBody() << outbuf;
769  pls->bytecnt += strlen( outbuf );
770  continue;
771  }
772 
773  if ( pls->linepos + 21 > LINELENGTH )
774  {
775  doc->osBody() << '\n';
776  pls->linepos = 0;
777  }
778  else
779  doc->osBody() << ' ';
780 
781  pls->bytecnt++;
782 
783  snprintf( outbuf, OUTBUF_LEN, "%d %d D", x, y );
784  dev->llx = MIN( dev->llx, x );
785  dev->lly = MIN( dev->lly, y );
786  dev->urx = MAX( dev->urx, x );
787  dev->ury = MAX( dev->ury, y );
788 
789  doc->osBody() << outbuf;
790  pls->bytecnt += strlen( outbuf );
791  pls->linepos += 21;
792  }
793  dev->xold = PL_UNDEFINED;
794  dev->yold = PL_UNDEFINED;
795  doc->osBody() << " F ";
796 }
797 
798 //--------------------------------------------------------------------------
799 // ps_getdate()
800 //
801 // Get the date and time
802 //--------------------------------------------------------------------------
803 
804 static char *
805 ps_getdate( void )
806 {
807  int len;
808  time_t t;
809  char *p;
810 
811  t = time( (time_t *) 0 );
812  p = ctime( &t );
813  len = strlen( p );
814  *( p + len - 1 ) = '\0'; // zap the newline character
815  return p;
816 }
817 
818 // 0.8 should mimic the offset of first superscript/subscript level
819 // implemented in plstr (plsym.c) for Hershey fonts. However, when
820 // comparing with -dev xwin and -dev xcairo results changing this
821 // factor to 0.6 appears to offset the centers of the letters
822 // appropriately while 0.8 gives much poorer agreement with the
823 // other devices.
824 # define RISE_FACTOR 0.6
825 
826 //--------------------------------------------------------------------------
827 // proc_str()
828 //
829 // Prints postscript strings.
830 // N.B. Now unicode only, no string access!
831 //
832 //--------------------------------------------------------------------------
833 
834 void
835 proc_str( PLStream *pls, EscText *args )
836 {
837  PLFLT *t = args->xform, tt[4]; // Transform matrices
838  PLFLT theta, shear, stride; // Rotation angle and shear from the matrix
839  PLFLT ft_ht, offset; // Font height and offset
840  PLFLT cs, sn;
841  PSDev *dev = (PSDev *) pls->dev;
842  PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc;
843  char *font, esc;
844  FontStyle style;
845  FontWeight weight;
846  // Be generous. Used to store lots of font changes which take
847  // 3 characters per change.
848  #define PROC_STR_STRING_LENGTH 1000
849  char *strp, str[PROC_STR_STRING_LENGTH], *cur_strp,
850  cur_str[PROC_STR_STRING_LENGTH];
851  float font_factor = 1.4;
852  PLINT clxmin, clxmax, clymin, clymax; // Clip limits
853  PLINT clipx[4], clipy[4]; // Current clip limits
854 
855  PLFLT scale = 1., up = 0.; // Font scaling and shifting parameters
856 
857  double lineSpacing, xAdvance, ymintmp, ymaxtmp, ymin, ymax, xmin, xmax;
858  PLINT xx[4], yy[4];
859 
860  // unicode only! so test for it.
861  if ( args->unicode_array_len > 0 )
862  {
863  int j, s, f;
864  char *fonts[PROC_STR_STRING_LENGTH];
865  FontStyle styles[PROC_STR_STRING_LENGTH];
866  FontWeight weights[PROC_STR_STRING_LENGTH];
867  const PLUNICODE *cur_text;
868  PLUNICODE fci;
869  unsigned char fontfamily, fontstyle, fontweight;
870  PLFLT old_sscale, sscale, old_soffset, soffset, dup;
871  PLINT level = 0;
872 
873  // translate from unicode into type 1 font index.
874  //
875  // Choose the font family, style, variant, and weight using
876  // the FCI (font characterization integer).
877  //
878 
879  plgesc( &esc );
880  plgfci( &fci );
881  plP_fci2hex( fci, &fontfamily, PL_FCI_FAMILY );
882  plP_fci2hex( fci, &fontstyle, PL_FCI_STYLE );
883  plP_fci2hex( fci, &fontweight, PL_FCI_WEIGHT );
884  font = (char *) FamilyLookup[fontfamily];
885  weight = WeightLookup[fontweight];
886  style = StyleLookup[fontstyle];
887  // Need to add some error checking here
888  if ( false )
889  {
890  fprintf( stderr, "fci = 0x%x, font name pointer = NULL \n", fci );
891  plabort( "proc_str: FCI inconsistent with TrueTypeLookup; "
892  "internal PLplot error" );
893  return;
894  }
895  //pldebug("proc_str", "fci = 0x%x, font name = %s\n", fci, font);
896  cur_text = args->unicode_array;
897  for ( f = s = j = 0; j < args->unicode_array_len; j++ )
898  {
899  if ( cur_text[j] & PL_FCI_MARK )
900  {
901  // process an FCI by saving it and escaping cur_str
902  // with an escff to make it a 2-character escape
903  // that is not used in legacy Hershey code
904  //
905  if ( ( f < PROC_STR_STRING_LENGTH ) && ( s + 3 < PROC_STR_STRING_LENGTH ) )
906  {
907  plP_fci2hex( cur_text[j], &fontfamily, PL_FCI_FAMILY );
908  plP_fci2hex( cur_text[j], &fontstyle, PL_FCI_STYLE );
909  plP_fci2hex( cur_text[j], &fontweight, PL_FCI_WEIGHT );
910  fonts[f] = (char *) FamilyLookup[fontfamily];
911  weights[f] = WeightLookup[fontweight];
912  styles[f] = StyleLookup[fontstyle];
913  if ( fonts[f] == NULL )
914  {
915  fprintf( stderr, "string-supplied FCI = 0x%x, font name pointer = NULL \n", cur_text[j] );
916  plabort( "proc_str: string-supplied FCI inconsistent with font lookup;" );
917  return;
918  }
919  //pldebug("proc_str", "string-supplied FCI = 0x%x, font name = %s\n", cur_text[j], fonts[f]);
920  cur_str[s++] = esc;
921  cur_str[s++] = 'f';
922  cur_str[s++] = 'f';
923  f++;
924  }
925  }
926  else if ( s + 1 < PROC_STR_STRING_LENGTH )
927  {
928  s += ucs4_to_utf8( cur_text[j], &cur_str[s] );
929  //pldebug("proc_str", "unicode = 0x%x, type 1 code = %d\n",
930 // cur_text[j], cur_str[j]);
931  }
932  }
933  cur_str[s] = '\0';
934 
935  // finish previous polyline
936 
937  dev->xold = PL_UNDEFINED;
938  dev->yold = PL_UNDEFINED;
939 
940  // Determine the font height
941  ft_ht = pls->chrht * 72.0 / 25.4; // ft_ht in points, ht is in mm
942 
943 
944  // The transform matrix has only rotations and shears; extract them
945  plRotationShear( t, &theta, &shear, &stride );
946  cs = cos( theta );
947  sn = sin( theta );
948  tt[0] = t[0] * cs + t[2] * sn;
949  tt[1] = t[1] * cs + t[3] * sn;
950  tt[2] = -t[0] * sn + t[2] * cs;
951  tt[3] = -t[1] * sn + t[3] * cs;
952 
953  //
954  // Reference point conventions:
955  // If base = 0, it is aligned with the center of the text box
956  // If base = 1, it is aligned with the baseline of the text box
957  // If base = 2, it is aligned with the top of the text box
958  //
959  // Currently plplot only uses base=0
960  // Postscript uses base=1
961  //
962  // We must calculate the difference between the two and apply the offset.
963  //
964 
965  if ( args->base == 2 ) // not supported by plplot
966  offset = ENLARGE * ft_ht / 2.; // half font height
967  else if ( args->base == 1 )
968  offset = 0.;
969  else
970  offset = -ENLARGE * ft_ht / 2.;
971 
972  // Determine the adjustment for page orientation
973  theta -= PI / 2. * pls->diorot;
974  args->y += (int) ( offset * cos( theta ) );
975  args->x -= (int) ( offset * sin( theta ) );
976 
977  // ps driver is rotated by default
978  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
979  &( args->x ), &( args->y ) );
980 
981  // Correct for the fact ps driver uses landscape by default
982  theta += PI / 2.;
983 
984  // Output
985  // Set clipping
986  clipx[0] = pls->clpxmi;
987  clipx[2] = pls->clpxma;
988  clipy[0] = pls->clpymi;
989  clipy[2] = pls->clpyma;
990  clipx[1] = clipx[2];
991  clipy[1] = clipy[0];
992  clipx[3] = clipx[0];
993  clipy[3] = clipy[2];
994  difilt( clipx, clipy, 4, &clxmin, &clxmax, &clymin, &clymax );
995  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
996  &clipx[0], &clipy[0] );
997  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
998  &clipx[1], &clipy[1] );
999  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
1000  &clipx[2], &clipy[2] );
1001  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
1002  &clipx[3], &clipy[3] );
1003  doc->osBody() << " gsave " << clipx[0] << " " << clipy[0] << " " <<
1004  clipx[1] << " " << clipy[1] << " " << clipx[2] << " " <<
1005  clipy[2] << " " << clipx[3] << " " << clipy[3] << " CL\n";
1006 
1007  // move to string reference point
1008  doc->osBody() << " " << args->x << " " << args->y << " M\n";
1009 
1010  // Save the current position and set the string rotation
1011  doc->osBody() << "gsave " << TRMFLT( theta * 180. / PI ) << " R\n";
1012 
1013  doc->osBody() << "[" << TRMFLT( tt[0] ) << " " << TRMFLT( tt[2] ) << " " << TRMFLT( tt[1] )
1014  << " " << TRMFLT( tt[3] ) << " 0 0] concat\n";
1015 
1016  xmax = 0;
1017  // Dummy run through the string first to work out the
1018  // length, including any font changes
1019  cur_strp = cur_str;
1020  f = 0;
1021  do
1022  {
1023  strp = str;
1024 
1025  if ( *cur_strp == esc )
1026  {
1027  cur_strp++;
1028 
1029  if ( *cur_strp == esc ) // <esc><esc>
1030  {
1031  *strp++ = *cur_strp++;
1032  }
1033  else if ( *cur_strp == 'f' )
1034  {
1035  cur_strp++;
1036  if ( *cur_strp++ != 'f' )
1037  {
1038  // escff occurs because of logic above. But any suffix
1039  // other than "f" should never happen.
1040  plabort( "proc_str, internal PLplot logic error;"
1041  "wrong escf escape sequence" );
1042  return;
1043  }
1044  font = fonts[f];
1045  style = styles[f];
1046  weight = weights[f];
1047  f++;
1048  continue;
1049  }
1050  else
1051  switch ( *cur_strp++ )
1052  {
1053  case 'd': //subscript
1054  case 'D':
1055  plP_script_scale( FALSE, &level,
1056  &old_sscale, &sscale, &old_soffset, &soffset );
1057  scale = sscale;
1058  // The correction for the difference in magnitude
1059  // between the baseline and middle coordinate systems
1060  // for subscripts should be
1061  // -0.5*(base font size - superscript/subscript font size).
1062  dup = -0.5 * ( 1.0 - sscale );
1063  up = -font_factor * ENLARGE * ft_ht * ( RISE_FACTOR * soffset + dup );
1064  break;
1065 
1066  case 'u': //superscript
1067  case 'U':
1068  plP_script_scale( TRUE, &level,
1069  &old_sscale, &sscale, &old_soffset, &soffset );
1070  scale = sscale;
1071  // The correction for the difference in magnitude
1072  // between the baseline and middle coordinate systems
1073  // for superscripts should be
1074  // 0.5*(base font size - superscript/subscript font size).
1075  dup = 0.5 * ( 1.0 - sscale );
1076  up = font_factor * ENLARGE * ft_ht * ( RISE_FACTOR * soffset + dup );
1077  break;
1078 
1079  // ignore the next sequences
1080 
1081  case '+':
1082  case '-':
1083  case 'b':
1084  case 'B':
1085  plwarn( "'+', '-', and 'b/B' text escape sequences not processed." );
1086  break;
1087  }
1088  }
1089 
1090  // copy from current to next token, adding a postscript escape
1091  // char '\' if necessary
1092  //
1093  while ( *cur_strp && *cur_strp != esc )
1094  {
1095  *strp++ = *cur_strp++;
1096  }
1097  *strp = '\0';
1098 
1099  // if(fabs(up)<0.001) up = 0.; /* Watch out for small differences */
1100 
1101  // Set the font size
1102  doc->setFont( font, style, weight );
1103  doc->setFontSize( font_factor * ENLARGE * ft_ht * scale );
1104  doc->get_dimensions( (const char *) str, &lineSpacing, &xAdvance, &ymintmp, &ymaxtmp );
1105  xmax += xAdvance;
1106  } while ( *cur_strp );
1107 
1108  // Use the length of the string to calculate offset
1109  // Also used later for bounding box
1110  xmin = -xmax * args->just;
1111  xmax = xmin;
1112  ymin = 0;
1113  ymax = 0;
1114 
1115  // Reset parameters
1116  level = 0;
1117  scale = 1.0;
1118  up = 0.0;
1119 
1120  // Move relative to position to account for justification
1121  doc->osBody() << " gsave " << TRMFLT( xmin * tt[0] ) << " " <<
1122  TRMFLT( xmin * tt[2] ) << " rmoveto\n";
1123 
1124  // Parse string for PLplot escape sequences and print everything out
1125 
1126  cur_strp = cur_str;
1127  f = 0;
1128  do
1129  {
1130  strp = str;
1131 
1132  if ( *cur_strp == esc )
1133  {
1134  cur_strp++;
1135 
1136  if ( *cur_strp == esc ) // <esc><esc>
1137  {
1138  *strp++ = *cur_strp++;
1139  }
1140  else if ( *cur_strp == 'f' )
1141  {
1142  cur_strp++;
1143  if ( *cur_strp++ != 'f' )
1144  {
1145  // escff occurs because of logic above. But any suffix
1146  // other than "f" should never happen.
1147  plabort( "proc_str, internal PLplot logic error;"
1148  "wrong escf escape sequence" );
1149  return;
1150  }
1151  font = fonts[f];
1152  style = styles[f];
1153  weight = weights[f];
1154  f++;
1155  //pldebug("proc_str", "string-specified fci = 0x%x, font name = %s\n", fci, font);
1156  continue;
1157  }
1158  else
1159  switch ( *cur_strp++ )
1160  {
1161  case 'd': //subscript
1162  case 'D':
1163  plP_script_scale( FALSE, &level,
1164  &old_sscale, &sscale, &old_soffset, &soffset );
1165  scale = sscale;
1166  // The correction for the difference in magnitude
1167  // between the baseline and middle coordinate systems
1168  // for subscripts should be
1169  // -0.5*(base font size - superscript/subscript font size).
1170  dup = -0.5 * ( 1.0 - sscale );
1171  up = -font_factor * ENLARGE * ft_ht * ( RISE_FACTOR * soffset + dup );
1172  break;
1173 
1174  case 'u': //superscript
1175  case 'U':
1176  plP_script_scale( TRUE, &level,
1177  &old_sscale, &sscale, &old_soffset, &soffset );
1178  scale = sscale;
1179  // The correction for the difference in magnitude
1180  // between the baseline and middle coordinate systems
1181  // for superscripts should be
1182  // 0.5*(base font size - superscript/subscript font size).
1183  dup = 0.5 * ( 1.0 - sscale );
1184  up = font_factor * ENLARGE * ft_ht * ( RISE_FACTOR * soffset + dup );
1185  break;
1186 
1187  // ignore the next sequences
1188 
1189  case '+':
1190  case '-':
1191  case 'b':
1192  case 'B':
1193  plwarn( "'+', '-', and 'b/B' text escape sequences not processed." );
1194  break;
1195  }
1196  }
1197 
1198  // copy from current to next token, adding a postscript escape
1199  // char '\' if necessary
1200  //
1201  while ( *cur_strp && *cur_strp != esc )
1202  {
1203  *strp++ = *cur_strp++;
1204  }
1205  *strp = '\0';
1206 
1207  // if(fabs(up)<0.001) up = 0.; /* Watch out for small differences */
1208 
1209  // Set the font size
1210  doc->setFont( font, style, weight );
1211  doc->setFontSize( font_factor * ENLARGE * ft_ht * scale );
1212  doc->get_dimensions( (const char *) str, &lineSpacing, &xAdvance, &ymintmp, &ymaxtmp );
1213  ymin = MIN( ymintmp + up, ymin );
1214  ymax = MAX( ymaxtmp + up, ymax );
1215  xmax += xAdvance;
1216 
1217  // if up/down escape sequences, save current point and adjust baseline;
1218  // take the shear into account
1219  if ( up != 0. )
1220  doc->osBody() << "gsave " << TRMFLT( up * tt[1] ) << " " << TRMFLT( up * tt[3] ) << " rmoveto\n";
1221 
1222  // print the string
1223  doc->osBody() << show( (const char *) str );
1224 
1225  // back to baseline
1226  if ( up != 0. )
1227  doc->osBody() << "grestore " << TRMFLT( xAdvance * tt[0] ) << " " << TRMFLT( xAdvance * tt[2] ) << " rmoveto\n";
1228  } while ( *cur_strp );
1229 
1230  doc->osBody() << "grestore\n";
1231  doc->osBody() << "grestore\n";
1232  doc->osBody() << "grestore\n";
1233 
1234  //
1235  // Estimate text bounding box from LASi get_dimensions function.
1236  // xmin, xmax are text left and right extents,
1237  // ymin, ymax are top and bottom extents.
1238  // These need to be rotated / transformed to get the correct values
1239  //
1240  xx[0] = (PLINT) ( t[0] * xmin + t[1] * ymin );
1241  yy[0] = (PLINT) ( t[2] * xmin + t[3] * ymin );
1242  xx[1] = (PLINT) ( t[0] * xmin + t[1] * ymax );
1243  yy[1] = (PLINT) ( t[2] * xmin + t[3] * ymax );
1244  xx[2] = (PLINT) ( t[0] * xmax + t[1] * ymin );
1245  yy[2] = (PLINT) ( t[2] * xmax + t[3] * ymin );
1246  xx[3] = (PLINT) ( t[0] * xmax + t[1] * ymax );
1247  yy[3] = (PLINT) ( t[2] * xmax + t[3] * ymax );
1248 
1249  plRotPhy( ORIENTATION, 0, 0, 0, 0, &xx[0], &yy[0] );
1250  plRotPhy( ORIENTATION, 0, 0, 0, 0, &xx[1], &yy[1] );
1251  plRotPhy( ORIENTATION, 0, 0, 0, 0, &xx[2], &yy[2] );
1252  plRotPhy( ORIENTATION, 0, 0, 0, 0, &xx[3], &yy[3] );
1253 
1254 
1255  xmin = MIN( MIN( MIN( xx[0], xx[1] ), xx[2] ), xx[3] ) + args->x;
1256  xmax = MAX( MAX( MAX( xx[0], xx[1] ), xx[2] ), xx[3] ) + args->x;
1257  ymin = MIN( MIN( MIN( yy[0], yy[1] ), yy[2] ), yy[3] ) + args->y;
1258  ymax = MAX( MAX( MAX( yy[0], yy[1] ), yy[2] ), yy[3] ) + args->y;
1259 
1260  dev->llx = (int) ( MIN( dev->llx, xmin ) );
1261  dev->lly = (int) ( MIN( dev->lly, ymin ) );
1262  dev->urx = (int) ( MAX( dev->urx, xmax ) );
1263  dev->ury = (int) ( MAX( dev->ury, ymax ) );
1264 // doc->osBody() << "Z " << xmin << " " << ymin << " M "
1265 // << xmin << " " << ymax << " D "
1266 // << xmax << " " << ymax << " D "
1267 // << xmax << " " << ymin << " D "
1268 // << xmin << " " << ymin << " closepath\n"
1269 // << "Z " << args->x << " " << args->y << " A closepath\n";
1270  }
1271 }
1272 
1273 //static void
1274 //esc_purge( char *dstr, char *sstr )
1275 //{
1276 // char esc;
1277 //
1278 // plgesc( &esc );
1279 //
1280 // while ( *sstr )
1281 // {
1282 // if ( *sstr != esc )
1283 // {
1284 // *dstr++ = *sstr++;
1285 // continue;
1286 // }
1287 //
1288 // sstr++;
1289 // if ( *sstr == esc )
1290 // {
1291 // *dstr++ = *sstr++;
1292 // continue;
1293 // }
1294 //
1295 // else
1296 // {
1297 // switch ( *sstr++ )
1298 // {
1299 // case 'f':
1300 // sstr++;
1301 // break; // two chars sequence
1302 //
1303 // default:
1304 // break; // single char escape
1305 // }
1306 // }
1307 // }
1308 // *dstr = '\0';
1309 //}
1310 
1311 #else
1312 int
1314 {
1315  return 0;
1316 }
1317 
1318 #endif // defined(PLD_psttf) || ....