1 /* This file is part of the GNU plotutils package.  Copyright (C) 1995,
2    1996, 1997, 1998, 1999, 2000, 2005, 2008, 2009, Free Software
3    Foundation, Inc.
4 
5    The GNU plotutils package is free software.  You may redistribute it
6    and/or modify it under the terms of the GNU General Public License as
7    published by the Free Software foundation; either version 2, or (at your
8    option) any later version.
9 
10    The GNU plotutils package is distributed in the hope that it will be
11    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License along
16    with the GNU plotutils package; see the file COPYING.  If not, write to
17    the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
18    Boston, MA 02110-1301, USA. */
19 
20 /* This file defines the initialization for any PSPlotter object, including
21    both private data and public methods.  There is a one-to-one
22    correspondence between public methods and user-callable functions in the
23    C API. */
24 
25 #include "sys-defines.h"
26 #include "extern.h"
27 
28 /* ctime_r() is currently not used, because there is apparently _no_
29    universal way of ensuring that it is declared.  On some systems
30    (e.g. Red Hat Linux), `#define _POSIX_SOURCE' will do it.  But on other
31    systems, doing `#define _POSIX_SOURCE' **removes** the declaration! */
32 #ifdef HAVE_CTIME_R
33 #undef HAVE_CTIME_R
34 #endif
35 
36 #ifdef MSDOS
37 #include <unistd.h>		/* for fsync() */
38 #endif
39 
40 /* song and dance to define time_t, and declare both time() and ctime() */
41 #ifdef HAVE_SYS_TYPES_H
42 #include <sys/types.h>		/* for time_t on some pre-ANSI Unix systems */
43 #endif
44 #ifdef TIME_WITH_SYS_TIME
45 #include <sys/time.h>		/* for time() on some pre-ANSI Unix systems */
46 #include <time.h>		/* for ctime() */
47 #else  /* not TIME_WITH_SYS_TIME, include only one (prefer <sys/time.h>) */
48 #ifdef HAVE_SYS_TIME_H
49 #include <sys/time.h>
50 #else  /* not HAVE_SYS_TIME_H */
51 #include <time.h>
52 #endif /* not HAVE_SYS_TIME_H */
53 #endif /* not TIME_WITH_SYS_TIME */
54 
55 #include "p_header.h"		/* idraw Postscript header (from InterViews) */
56 
57 #ifndef LIBPLOTTER
58 /* In libplot, this is the initialization for the function-pointer part of
59    a PSPlotter struct. */
60 const Plotter _pl_p_default_plotter =
61 {
62   /* initialization (after creation) and termination (before deletion) */
63   _pl_p_initialize, _pl_p_terminate,
64   /* page manipulation */
65   _pl_p_begin_page, _pl_p_erase_page, _pl_p_end_page,
66   /* drawing state manipulation */
67   _pl_g_push_state, _pl_g_pop_state,
68   /* internal path-painting methods (endpath() is a wrapper for the first) */
69   _pl_p_paint_path, _pl_p_paint_paths, _pl_g_path_is_flushable, _pl_g_maybe_prepaint_segments,
70   /* internal methods for drawing of markers and points */
71   _pl_g_paint_marker, _pl_p_paint_point,
72   /* internal methods that plot strings in Hershey, non-Hershey fonts */
73   _pl_g_paint_text_string_with_escapes, _pl_p_paint_text_string,
74   _pl_g_get_text_width,
75   /* private low-level `retrieve font' method */
76   _pl_g_retrieve_font,
77   /* `flush output' method, called only if Plotter handles its own output */
78   _pl_g_flush_output,
79   /* error handlers */
80   _pl_g_warning,
81   _pl_g_error,
82 };
83 #endif /* not LIBPLOTTER */
84 
85 /* The private `initialize' method, which is invoked when a Plotter is
86    created.  It is used for such things as initializing capability flags
87    from the values of class variables, allocating storage, etc.  When this
88    is invoked, _plotter points to the Plotter that has just been
89    created. */
90 
91 /* For PS Plotter objects, we initialize the used-font array(s).  We also
92    determine the page size and the location on the page of the graphics
93    display, so that we'll be able to work out the map from user coordinates
94    to device coordinates in space.c. */
95 
96 void
_pl_p_initialize(S___ (Plotter * _plotter))97 _pl_p_initialize (S___(Plotter *_plotter))
98 {
99 #ifndef LIBPLOTTER
100   /* in libplot, manually invoke superclass initialization method */
101   _pl_g_initialize (S___(_plotter));
102 #endif
103 
104   /* override generic initializations (which are appropriate to the base
105      Plotter class), as necessary */
106 
107 #ifndef LIBPLOTTER
108   /* tag field, differs in derived classes */
109   _plotter->data->type = PL_PS;
110 #endif
111 
112   /* output model */
113   _plotter->data->output_model = PL_OUTPUT_PAGES_ALL_AT_ONCE;
114 
115   /* user-queryable capabilities: 0/1/2 = no/yes/maybe */
116   _plotter->data->have_wide_lines = 1;
117   _plotter->data->have_dash_array = 1;
118   _plotter->data->have_solid_fill = 1;
119   _plotter->data->have_odd_winding_fill = 1;
120   _plotter->data->have_nonzero_winding_fill = 1;
121   _plotter->data->have_settable_bg = 0;
122   _plotter->data->have_escaped_string_support = 0;
123   _plotter->data->have_ps_fonts = 1;
124 #ifdef USE_LJ_FONTS_IN_PS
125   _plotter->data->have_pcl_fonts = 1;
126 #else
127   _plotter->data->have_pcl_fonts = 0;
128 #endif
129   _plotter->data->have_stick_fonts = 0;
130   _plotter->data->have_extra_stick_fonts = 0;
131   _plotter->data->have_other_fonts = 0;
132 
133   /* text and font-related parameters (internal, not queryable by user);
134      note that we don't set kern_stick_fonts, because it was set by the
135      superclass initialization (and it's irrelevant for this Plotter type,
136      anyway) */
137   _plotter->data->default_font_type = PL_F_POSTSCRIPT;
138   _plotter->data->pcl_before_ps = false;
139   _plotter->data->have_horizontal_justification = false;
140   _plotter->data->have_vertical_justification = false;
141   _plotter->data->issue_font_warning = true;
142 
143   /* path-related parameters (also internal); note that we
144      don't set max_unfilled_path_length, because it was set by the
145      superclass initialization */
146   _plotter->data->have_mixed_paths = false;
147   _plotter->data->allowed_arc_scaling = AS_NONE;
148   _plotter->data->allowed_ellarc_scaling = AS_NONE;
149   _plotter->data->allowed_quad_scaling = AS_NONE;
150   _plotter->data->allowed_cubic_scaling = AS_NONE;
151   _plotter->data->allowed_box_scaling = AS_ANY;
152   _plotter->data->allowed_circle_scaling = AS_ANY;
153   _plotter->data->allowed_ellipse_scaling = AS_ANY;
154 
155   /* color-related parameters (also internal) */
156   _plotter->data->emulate_color = false;
157 
158   /* dimensions */
159   _plotter->data->display_model_type = (int)DISP_MODEL_PHYSICAL;
160   _plotter->data->display_coors_type = (int)DISP_DEVICE_COORS_REAL;
161   _plotter->data->flipped_y = false;
162   _plotter->data->imin = 0;
163   _plotter->data->imax = 0;
164   _plotter->data->jmin = 0;
165   _plotter->data->jmax = 0;
166   _plotter->data->xmin = 0.0;
167   _plotter->data->xmax = 0.0;
168   _plotter->data->ymin = 0.0;
169   _plotter->data->ymax = 0.0;
170   _plotter->data->page_data = (plPageData *)NULL;
171 
172   /* initialize certain data members from device driver parameters */
173 
174   /* Determine range of device coordinates over which the viewport will
175      extend (and hence the transformation from user to device coordinates;
176      see g_space.c). */
177   {
178     /* determine page type, viewport size and location */
179     _set_page_type (_plotter->data);
180 
181     /* convert viewport size-and-location data (in terms of inches) to
182        device coordinates (i.e. points) */
183     _plotter->data->xmin = 72 * (_plotter->data->viewport_xorigin
184 				 + _plotter->data->viewport_xoffset);
185     _plotter->data->xmax = 72 * (_plotter->data->viewport_xorigin
186 				 + _plotter->data->viewport_xoffset
187 				 + _plotter->data->viewport_xsize);
188 
189     _plotter->data->ymin = 72 * (_plotter->data->viewport_yorigin
190 				 + _plotter->data->viewport_yoffset);
191     _plotter->data->ymax = 72 * (_plotter->data->viewport_yorigin
192 				 + _plotter->data->viewport_yoffset
193 				 + _plotter->data->viewport_ysize);
194   }
195 
196   /* compute the NDC to device-frame affine map, set it in Plotter */
197   _compute_ndc_to_device_map (_plotter->data);
198 }
199 
200 /* The private `terminate' method, which is invoked when a Plotter is
201    deleted.  It may do such things as write to an output stream from
202    internal storage, deallocate storage, etc.  When this is invoked,
203    _plotter points to the Plotter that is about to be deleted. */
204 
205 /* This version is for Postscript Plotters.  It writes out the
206    idraw-derived PS prologue to the output stream, and copies each stored
207    page of graphics to it too, making sure to include the proper DSC
208    [Document Structuring Convention] comment lines at the beginning and the
209    end of the document, and at the beginning and end of each page.
210 
211    (PS Plotters differ from most other plotters that do not plot in real
212    time in that they emit output only after all pages have pages have been
213    drawn, rather than at the end of each page.  This is necessary for DSC
214    compliance.)
215 
216    When this is called, the PS code for the body of each page is stored in
217    a plOutbuf, and the page plOutbufs form a linked list.  In this function
218    we write the document header, the document trailer, and the
219    header/trailer for each page, all to separate plOutbufs.  We then copy
220    the plOutbufs, one after another, to the output stream. */
221 
222 void
_pl_p_terminate(S___ (Plotter * _plotter))223 _pl_p_terminate (S___(Plotter *_plotter))
224 {
225   double x_min, x_max, y_min, y_max;
226   int i, n;
227   time_t clock;
228   plOutbuf *doc_header, *doc_trailer, *current_page;
229   bool ps_font_used_in_doc[PL_NUM_PS_FONTS];
230 #ifdef USE_LJ_FONTS_IN_PS
231   bool pcl_font_used_in_doc[PL_NUM_PCL_FONTS];
232 #endif
233   char *time_string, time_string_buffer[32];
234 
235 #ifdef LIBPLOTTER
236   if (_plotter->data->outfp || _plotter->data->outstream)
237 #else
238   if (_plotter->data->outfp)
239 #endif
240     /* have an output stream */
241     {
242       int num_pages = _plotter->data->page_number;
243 
244       /* First, prepare the document header (DSC lines, etc.), and write it
245          to a plOutbuf.  The header is very long: most of it is simply the
246          idraw header (see p_header.h). */
247       doc_header = _new_outbuf ();
248 
249       if (num_pages == 1)
250 	/* will plot an EPS file, not just a PS file */
251 	sprintf (doc_header->point, "\
252 %%!PS-Adobe-3.0 EPSF-3.0\n");
253       else
254 	sprintf (doc_header->point, "\
255 %%!PS-Adobe-3.0\n");
256       _update_buffer (doc_header);
257 
258       /* Compute an ASCII representation of the current time, in a
259 	 reentrant way if we're supporting pthreads (i.e. by using ctime_r
260 	 if it's available). */
261       time (&clock);
262 #ifdef PTHREAD_SUPPORT
263 #ifdef HAVE_PTHREAD_H
264 #ifdef HAVE_CTIME_R
265       ctime_r (&clock, time_string_buffer);
266       time_string = time_string_buffer;
267 #else
268       time_string = ctime (&clock);
269 #endif
270 #else
271       time_string = ctime (&clock);
272 #endif
273 #else
274       time_string = ctime (&clock);
275 #endif
276 
277       sprintf (doc_header->point, "\
278 %%%%Creator: GNU libplot drawing library %s\n\
279 %%%%Title: PostScript plot\n\
280 %%%%CreationDate: %s\
281 %%%%DocumentData: Clean7Bit\n\
282 %%%%LanguageLevel: 1\n\
283 %%%%Pages: %d\n\
284 %%%%PageOrder: Ascend\n\
285 %%%%Orientation: Portrait\n",
286 	       PL_LIBPLOT_VER_STRING, time_string, num_pages);
287       _update_buffer (doc_header);
288 
289       /* emit the bounding box for the document */
290       _bbox_of_outbufs (_plotter->data->first_page, &x_min, &x_max, &y_min, &y_max);
291       if (x_min > x_max || y_min > y_max)
292 	/* all pages empty */
293 	sprintf (doc_header->point, "\
294 %%%%BoundingBox: 0 0 0 0\n");
295       else
296 	sprintf (doc_header->point, "\
297 %%%%BoundingBox: %d %d %d %d\n",
298 		 IROUND(x_min - 0.5), IROUND(y_min - 0.5),
299 		 IROUND(x_max + 0.5), IROUND(y_max + 0.5));
300       _update_buffer (doc_header);
301 
302       /* determine fonts needed by document, by examining all pages */
303       {
304 	current_page = _plotter->data->first_page;
305 
306 	for (i = 0; i < PL_NUM_PS_FONTS; i++)
307 	  ps_font_used_in_doc[i] = false;
308 #ifdef USE_LJ_FONTS_IN_PS
309 	for (i = 0; i < PL_NUM_PCL_FONTS; i++)
310 	  pcl_font_used_in_doc[i] = false;
311 #endif
312 	while (current_page)
313 	  {
314 	    for (i = 0; i < PL_NUM_PS_FONTS; i++)
315 	      if (current_page->ps_font_used[i])
316 		ps_font_used_in_doc[i] = true;
317 #ifdef USE_LJ_FONTS_IN_PS
318 	    for (i = 0; i < PL_NUM_PCL_FONTS; i++)
319 	      if (current_page->pcl_font_used[i])
320 		pcl_font_used_in_doc[i] = true;
321 #endif
322 	    current_page = current_page->next;
323 	  }
324       }
325 
326       /* write out list of fonts needed by the document */
327       {
328 	bool first_font = true;
329 
330 	strcpy (doc_header->point, "\
331 %%DocumentNeededResources: ");
332 	_update_buffer (doc_header);
333 
334 	for (i = 0; i < PL_NUM_PS_FONTS; i++)
335 	  {
336 	    if (ps_font_used_in_doc[i])
337 	      {
338 		if (first_font == false)
339 		  {
340 		    strcpy (doc_header->point, "%%+ ");
341 		    _update_buffer (doc_header);
342 		  }
343 		strcpy (doc_header->point, "font ");
344 		_update_buffer (doc_header);
345 		strcpy (doc_header->point, _pl_g_ps_font_info[i].ps_name);
346 		_update_buffer (doc_header);
347 		strcpy (doc_header->point, "\n");
348 		_update_buffer (doc_header);
349 		first_font = false;
350 	      }
351 	  }
352 #ifdef USE_LJ_FONTS_IN_PS
353 	for (i = 0; i < PL_NUM_PCL_FONTS; i++)
354 	  {
355 	    if (pcl_font_used_in_doc[i])
356 	      {
357 		if (first_font == false)
358 		  {
359 		    strcpy (doc_header->point, "%%+ ");
360 		    _update_buffer (doc_header);
361 		  }
362 		strcpy (doc_header->point, "font ");
363 		_update_buffer (doc_header);
364 		/* use replacement font name if any (this is only to
365                    support the Tidbits-is-Wingdings botch) */
366 		if (_pl_g_pcl_font_info[i].substitute_ps_name)
367 		  strcpy (doc_header->point, _pl_g_pcl_font_info[i].substitute_ps_name);
368 		else
369 		  strcpy (doc_header->point, _pl_g_pcl_font_info[i].ps_name);
370 		_update_buffer (doc_header);
371 		strcpy (doc_header->point, "\n");
372 		_update_buffer (doc_header);
373 		first_font = false;
374 	      }
375 	  }
376 #endif
377 	if (first_font)		/* no fonts needed in document */
378 	  {
379 	    strcpy (doc_header->point, "\n");
380 	    _update_buffer (doc_header);
381 	  }
382       }
383 
384       /* emit final DSC lines in header */
385       if (num_pages > 0)
386 	{
387 	  sprintf (doc_header->point, "\
388 %%%%DocumentSuppliedResources: procset %s %s 0\n",
389 		   PS_PROCSET_NAME, PS_PROCSET_VERSION);
390 	  _update_buffer (doc_header);
391 	}
392       strcpy (doc_header->point, "\
393 %%EndComments\n\n");
394       _update_buffer (doc_header);
395 
396       /* write out list of fonts needed by the document, all over again;
397 	 this time it's interpreted as the default font list for each page */
398       {
399 	bool first_font = true;
400 
401 	strcpy (doc_header->point, "\
402 %%BeginDefaults\n");
403 	_update_buffer (doc_header);
404 	strcpy (doc_header->point, "\
405 %%PageResources: ");
406 	_update_buffer (doc_header);
407 	for (i = 0; i < PL_NUM_PS_FONTS; i++)
408 	  {
409 	    if (ps_font_used_in_doc[i])
410 	      {
411 		if (first_font == false)
412 		  {
413 		    strcpy (doc_header->point, "%%+ ");
414 		    _update_buffer (doc_header);
415 		  }
416 		strcpy (doc_header->point, "font ");
417 		_update_buffer (doc_header);
418 		strcpy (doc_header->point, _pl_g_ps_font_info[i].ps_name);
419 		_update_buffer (doc_header);
420 		strcpy (doc_header->point, "\n");
421 		_update_buffer (doc_header);
422 		first_font = false;
423 	      }
424 	  }
425 #ifdef USE_LJ_FONTS_IN_PS
426 	for (i = 0; i < PL_NUM_PCL_FONTS; i++)
427 	  {
428 	    if (pcl_font_used_in_doc[i])
429 	      {
430 		if (first_font == false)
431 		  {
432 		    strcpy (doc_header->point, "%%+ ");
433 		    _update_buffer (doc_header);
434 		  }
435 		strcpy (doc_header->point, "font ");
436 		_update_buffer (doc_header);
437 		if (_pl_g_pcl_font_info[i].substitute_ps_name)
438 		  /* this is to support the Tidbits-is-Wingdings botch */
439 		  strcpy (doc_header->point, _pl_g_pcl_font_info[i].substitute_ps_name);
440 		else
441 		  strcpy (doc_header->point, _pl_g_pcl_font_info[i].ps_name);
442 		_update_buffer (doc_header);
443 		strcpy (doc_header->point, "\n");
444 		_update_buffer (doc_header);
445 		first_font = false;
446 	      }
447 	  }
448 #endif
449 	if (first_font)		/* no fonts needed in document */
450 	  {
451 	    strcpy (doc_header->point, "\n");
452 	    _update_buffer (doc_header);
453 	  }
454 
455 	strcpy (doc_header->point, "\
456 %%EndDefaults\n\n");
457 	_update_buffer (doc_header);
458       }
459 
460       /* Document Prolog */
461       strcpy (doc_header->point, "\
462 %%BeginProlog\n");
463       _update_buffer (doc_header);
464       if (num_pages > 1)
465 	/* PS [not EPS] file, include procset in document prolog */
466 	{
467 	  sprintf (doc_header->point, "\
468 %%%%BeginResource: procset %s %s 0\n",
469 		   PS_PROCSET_NAME, PS_PROCSET_VERSION);
470 	  _update_buffer (doc_header);
471 	  /* write out idraw-derived PS prologue (makes many definitions) */
472 	  for (i=0; *_ps_procset[i]; i++)
473 	    {
474 	      strcpy (doc_header->point, _ps_procset[i]);
475 	      _update_buffer (doc_header);
476 	    }
477 	  strcpy (doc_header->point, "\
478 %%EndResource\n");
479 	  _update_buffer (doc_header);
480 	}
481       strcpy (doc_header->point, "\
482 %%EndProlog\n\n");
483       _update_buffer (doc_header);
484 
485       /* Document Setup */
486       strcpy (doc_header->point, "\
487 %%BeginSetup\n");
488       _update_buffer (doc_header);
489 
490       /* tell driver to include any PS [or PCL] fonts that are needed */
491       for (i = 0; i < PL_NUM_PS_FONTS; i++)
492 	  if (ps_font_used_in_doc[i])
493 	    {
494 	      sprintf (doc_header->point, "\
495 %%%%IncludeResource: font %s\n", _pl_g_ps_font_info[i].ps_name);
496 	      _update_buffer (doc_header);
497 	    }
498 #ifdef USE_LJ_FONTS_IN_PS
499       for (i = 0; i < PL_NUM_PCL_FONTS; i++)
500 	  if (pcl_font_used_in_doc[i])
501 	    {
502 	      /* this is to support the Tidbits-is-Wingdings botch */
503 	      if (_pl_g_pcl_font_info[i].substitute_ps_name)
504 		sprintf (doc_header->point, "\
505 %%%%IncludeResource: font %s\n", _pl_g_pcl_font_info[i].substitute_ps_name);
506 	      else
507 		sprintf (doc_header->point, "\
508 %%%%IncludeResource: font %s\n", _pl_g_pcl_font_info[i].ps_name);
509 	      _update_buffer (doc_header);
510 	    }
511 #endif
512 
513       /* push private dictionary on stack */
514       strcpy (doc_header->point, "\
515 /DrawDict 50 dict def\n\
516 DrawDict begin\n");
517       _update_buffer (doc_header);
518 
519       /* do ISO-Latin-1 reencoding for any fonts that need it */
520       {
521 	bool need_to_reencode = false;
522 
523 	for (i = 0; i < PL_NUM_PS_FONTS; i++)
524 	  if (ps_font_used_in_doc[i] && _pl_g_ps_font_info[i].iso8859_1)
525 	    {
526 	      need_to_reencode = true;
527 	      break;
528 	    }
529 #ifdef USE_LJ_FONTS_IN_PS
530 	for (i = 0; i < PL_NUM_PCL_FONTS; i++)
531 	  if (pcl_font_used_in_doc[i] && _pl_g_pcl_font_info[i].iso8859_1)
532 	    {
533 	      need_to_reencode = true;
534 	      break;
535 	    }
536 #endif
537 	if (need_to_reencode)
538 	  {
539 	    strcpy (doc_header->point, _ps_fontproc);
540 	    _update_buffer (doc_header);
541 
542 	    for (i = 0; i < PL_NUM_PS_FONTS; i++)
543 	      {
544 		if (ps_font_used_in_doc[i] && _pl_g_ps_font_info[i].iso8859_1)
545 		  {
546 		    sprintf (doc_header->point, "\
547 /%s reencodeISO def\n",
548 			     _pl_g_ps_font_info[i].ps_name);
549 		    _update_buffer (doc_header);
550 		  }
551 	      }
552 #ifdef USE_LJ_FONTS_IN_PS
553 	    for (i = 0; i < PL_NUM_PCL_FONTS; i++)
554 	      {
555 		if (pcl_font_used_in_doc[i] && _pl_g_pcl_font_info[i].iso8859_1)
556 		  {
557 		    sprintf (doc_header->point, "\
558 /%s reencodeISO def\n",
559 			     _pl_g_pcl_font_info[i].ps_name);
560 		    _update_buffer (doc_header);
561 		  }
562 	      }
563 #endif
564 	  }
565       }
566 
567       if (num_pages == 1)
568 	/* EPS [not just PS] file, include procset in setup section,
569 	   so that it will modify only the private dictionary */
570 	{
571 	  sprintf (doc_header->point, "\
572 %%%%BeginResource: procset %s %s 0\n",
573 		   PS_PROCSET_NAME, PS_PROCSET_VERSION);
574 	  _update_buffer (doc_header);
575 
576 	  /* write out idraw-derived PS prologue in p_header.h (makes many
577              definitions) */
578 	  for (i=0; *_ps_procset[i]; i++)
579 	    {
580 	      strcpy (doc_header->point, _ps_procset[i]);
581 	      _update_buffer (doc_header);
582 	    }
583 	  strcpy (doc_header->point, "\
584 %%EndResource\n");
585 	  _update_buffer (doc_header);
586 	}
587 
588       strcpy (doc_header->point, "\
589 %%EndSetup\n\n");
590       _update_buffer (doc_header);
591 
592       /* Document header is now prepared, and stored in a plOutbuf.
593 	 Now do the same for the doc trailer (much shorter). */
594 
595       /* Document Trailer: just pop private dictionary off stack */
596       doc_trailer = _new_outbuf ();
597       strcpy (doc_trailer->point, "\
598 %%Trailer\n\
599 end\n\
600 %%EOF\n");
601       _update_buffer (doc_trailer);
602 
603       /* WRITE DOCUMENT HEADER (and free its plOutbuf) */
604       _write_string (_plotter->data, doc_header->base);
605       _delete_outbuf (doc_header);
606 
607       /* now loop through pages, emitting each in turn */
608       if (num_pages > 0)
609 	{
610 	  for (current_page = _plotter->data->first_page, n=1;
611 	       current_page;
612 	       current_page = current_page->next, n++)
613 	    {
614 	      plOutbuf *page_header, *page_trailer;
615 
616 	      /* prepare page header, and store it in a plOutbuf */
617 	      page_header = _new_outbuf ();
618 
619 	      sprintf (page_header->point, "\
620 %%%%Page: %d %d\n", n, n);
621 	      _update_buffer (page_header);
622 
623 	      /* write out list of fonts needed by the page */
624 	      {
625 		bool first_font = true;
626 
627 		strcpy (page_header->point, "\
628 %%PageResources: ");
629 		_update_buffer (page_header);
630 		for (i = 0; i < PL_NUM_PS_FONTS; i++)
631 		  {
632 		    if (current_page->ps_font_used[i])
633 		      {
634 			if (first_font == false)
635 			  {
636 			    strcpy (page_header->point, "%%+ ");
637 			    _update_buffer (page_header);
638 			  }
639 			strcpy (page_header->point, "font ");
640 			_update_buffer (page_header);
641 			strcpy (page_header->point, _pl_g_ps_font_info[i].ps_name);
642 			_update_buffer (page_header);
643 			strcpy (page_header->point, "\n");
644 			_update_buffer (page_header);
645 			first_font = false;
646 		      }
647 		  }
648 #ifdef USE_LJ_FONTS_IN_PS
649 		for (i = 0; i < PL_NUM_PCL_FONTS; i++)
650 		  {
651 		    if (current_page->pcl_font_used[i])
652 		      {
653 			if (first_font == false)
654 			  {
655 			    strcpy (page_header->point, "%%+ ");
656 			    _update_buffer (page_header);
657 			  }
658 			strcpy (page_header->point, "font ");
659 			_update_buffer (page_header);
660 			if (_pl_g_pcl_font_info[i].substitute_ps_name)
661 			  /* this is to support the Tidbits-is-Wingdings botch */
662 			  strcpy (page_header->point, _pl_g_pcl_font_info[i].substitute_ps_name);
663 			else
664 			  strcpy (page_header->point, _pl_g_pcl_font_info[i].ps_name);
665 			_update_buffer (page_header);
666 			strcpy (page_header->point, "\n");
667 			_update_buffer (page_header);
668 			first_font = false;
669 		      }
670 		  }
671 #endif
672 		if (first_font)	/* no fonts needed on page */
673 		  {
674 		    strcpy (page_header->point, "\n");
675 		    _update_buffer (page_header);
676 		  }
677 	      }
678 
679 	      /* emit the bounding box for the page */
680 	      _bbox_of_outbuf (current_page, &x_min, &x_max, &y_min, &y_max);
681 	      if (x_min > x_max || y_min > y_max)
682 		/* empty page */
683 		sprintf (page_header->point, "\
684 %%%%PageBoundingBox: 0 0 0 0\n");
685 	      else
686 		sprintf (page_header->point, "\
687 %%%%PageBoundingBox: %d %d %d %d\n",
688 			 IROUND(x_min - 0.5), IROUND(y_min - 0.5),
689 			 IROUND(x_max + 0.5), IROUND(y_max + 0.5));
690 	      _update_buffer (page_header);
691 	      /* Page Setup */
692 	      strcpy (page_header->point, "\
693 %%BeginPageSetup\n");
694 	      _update_buffer (page_header);
695 	      /* emit initialization code (including idraw, PS directives) */
696 	      /* N.B. `8' below is the version number of the idraw PS format
697 		 we're producing; see <Unidraw/Components/psformat.h> */
698 	      strcpy (page_header->point, "\
699 %I Idraw 8\n\n\
700 Begin\n\
701 %I b u\n\
702 %I cfg u\n\
703 %I cbg u\n\
704 %I f u\n\
705 %I p u\n\
706 %I t\n\
707 [ 1 0 0 1 0 0 ] concat\n\
708 /originalCTM matrix currentmatrix def\n\
709 /trueoriginalCTM matrix currentmatrix def\n");
710 	      _update_buffer (page_header);
711 	      strcpy (page_header->point, "\
712 %%EndPageSetup\n\n");
713 	      _update_buffer (page_header);
714 
715 	      /* Page header is now prepared, and stored in a plOutbuf.
716                  Do the same for the page trailer (much shorter). */
717 
718 	      page_trailer = _new_outbuf ();
719 	      /* Page Trailer: includes `showpage' */
720 	      strcpy (page_trailer->point, "\
721 %%PageTrailer\n\
722 End %I eop\n\
723 showpage\n\n");
724 	      _update_buffer (page_trailer);
725 	      /* Page trailer is now ready */
726 
727 	      /* WRITE PS CODE FOR THIS PAGE, including header, trailer */
728 	      _write_string (_plotter->data, page_header->base);
729 	      if (current_page->len > 0)
730 		_write_string (_plotter->data, current_page->base);
731 	      _write_string (_plotter->data, page_trailer->base);
732 
733 	      /* free header, trailer plOutbufs */
734 	      _delete_outbuf (page_trailer);
735 	      _delete_outbuf (page_header);
736 	    }
737 	}
738 
739       /* WRITE DOCUMENT TRAILER (and free its plOutbuf) */
740       _write_string (_plotter->data, doc_trailer->base);
741       _delete_outbuf (doc_trailer);
742     }
743 
744   /* delete all plOutbufs in which document pages are stored */
745   current_page = _plotter->data->first_page;
746   while (current_page)
747     {
748       plOutbuf *next_page;
749 
750       next_page = current_page->next;
751       _delete_outbuf (current_page);
752       current_page = next_page;
753     }
754 
755   /* flush output stream if any */
756   if (_plotter->data->outfp)
757     {
758       if (fflush(_plotter->data->outfp) < 0
759 #ifdef MSDOS
760 	  /* data can be caught in DOS buffers, so do an fsync() too */
761 	  || fsync (_plotter->data->outfp) < 0
762 #endif
763 	  )
764 	_plotter->error (R___(_plotter) "the output stream is jammed");
765     }
766 #ifdef LIBPLOTTER
767   else if (_plotter->data->outstream)
768     {
769       _plotter->data->outstream->flush ();
770       if (!(*(_plotter->data->outstream)))
771 	_plotter->error (R___(_plotter) "the output stream is jammed");
772     }
773 #endif
774 
775 #ifndef LIBPLOTTER
776   /* in libplot, manually invoke superclass termination method */
777   _pl_g_terminate (S___(_plotter));
778 #endif
779 }
780 
781 #ifdef LIBPLOTTER
PSPlotter(FILE * infile,FILE * outfile,FILE * errfile)782 PSPlotter::PSPlotter (FILE *infile, FILE *outfile, FILE *errfile)
783 	:Plotter (infile, outfile, errfile)
784 {
785   _pl_p_initialize ();
786 }
787 
PSPlotter(FILE * outfile)788 PSPlotter::PSPlotter (FILE *outfile)
789 	:Plotter (outfile)
790 {
791   _pl_p_initialize ();
792 }
793 
PSPlotter(istream & in,ostream & out,ostream & err)794 PSPlotter::PSPlotter (istream& in, ostream& out, ostream& err)
795 	: Plotter (in, out, err)
796 {
797   _pl_p_initialize ();
798 }
799 
PSPlotter(ostream & out)800 PSPlotter::PSPlotter (ostream& out)
801 	: Plotter (out)
802 {
803   _pl_p_initialize ();
804 }
805 
PSPlotter()806 PSPlotter::PSPlotter ()
807 {
808   _pl_p_initialize ();
809 }
810 
PSPlotter(FILE * infile,FILE * outfile,FILE * errfile,PlotterParams & parameters)811 PSPlotter::PSPlotter (FILE *infile, FILE *outfile, FILE *errfile, PlotterParams &parameters)
812 	:Plotter (infile, outfile, errfile, parameters)
813 {
814   _pl_p_initialize ();
815 }
816 
PSPlotter(FILE * outfile,PlotterParams & parameters)817 PSPlotter::PSPlotter (FILE *outfile, PlotterParams &parameters)
818 	:Plotter (outfile, parameters)
819 {
820   _pl_p_initialize ();
821 }
822 
PSPlotter(istream & in,ostream & out,ostream & err,PlotterParams & parameters)823 PSPlotter::PSPlotter (istream& in, ostream& out, ostream& err, PlotterParams &parameters)
824 	: Plotter (in, out, err, parameters)
825 {
826   _pl_p_initialize ();
827 }
828 
PSPlotter(ostream & out,PlotterParams & parameters)829 PSPlotter::PSPlotter (ostream& out, PlotterParams &parameters)
830 	: Plotter (out, parameters)
831 {
832   _pl_p_initialize ();
833 }
834 
PSPlotter(PlotterParams & parameters)835 PSPlotter::PSPlotter (PlotterParams &parameters)
836 	: Plotter (parameters)
837 {
838   _pl_p_initialize ();
839 }
840 
~PSPlotter()841 PSPlotter::~PSPlotter ()
842 {
843   /* if luser left the Plotter open, close it */
844   if (_plotter->data->open)
845     _API_closepl ();
846 
847   _pl_p_terminate ();
848 }
849 #endif
850